# #25023 — Reducir `macrobase@%` y rotar su clave

Estado: **diseño, nada ejecutado.** Medido el 15-oct-2026 (solo lectura). Autoriza y decide MANUEL.

## Qué es `macrobase@%`
Cuenta de `prod.db.interno` (10.0.1.34:3306, MySQL 8.0.46). Permisos: casi todos los globales con `GRANT OPTION`
(incluye `DROP`, `CREATE USER`, `REPLICATION SLAVE`) y `ALL` con `GRANT OPTION` en `mbinvmacrobase`, `mbinvbitacoramb`
y `_temporalesmacrobase`. Su clave está en una nota de #12693 y es la misma que hoy usa macrobase/POSTouch.

## Quién la usa (medido)
Conexiones en 17.5 días: app-4 173,447; oci-mbinv 8,119; ningún otro host. Todo contra `mbinvmacrobase`.
Seis sitios propios de Macrobase: `macrobase/mbinv`, `tiquetes/mbinv`, `macrobase/POSTouch` (dos conexiones),
`macrobase/bluedevsWoo`, `macrobase/mariaPruebas` (app-4) y `qa_produccion` (oci-mbinv).

**Ningún sitio de cliente la usa.** `vitalis`, `sainsa` y `restaurantecostagrande` dicen `credito_usuario: macrobase` en
parameters.yml, pero el sistema lee el valor de `parametros_backoffice`, donde dicen `Macrored`. De 151 sitios revisados
(`parametros_backoffice.credito_usuario`): 146 `Macrored`, 4 `macrobase` (`demorestaurante`, `demosalon`, `demoventas`,
`manuales`) y 1 `?`; los 4 tienen `credito_cliente = ?`, o sea que ni consultan, y apuntan a otro servidor (192.168.0.x).
El chequeo de crédito corre en apiMB (app-4, `/var/www/html/macroapps/apiOci`, tras el balanceador apps.sistemasmb.com)
con la cuenta que cada sitio le manda, y solo lee 5 tablas. Eso es asunto de `Macrored` (#23920), no de `macrobase`.

## Qué hace de verdad (statements desde el reinicio, `performance_schema`)
`select`, `update` (multi), `delete`, `create_table`/`drop_table` (913/8,128), `create_index`, `alter_table`, 79,283
llamadas a procedimientos y 2 `show databases`. **Cero** `create_db`, `drop_db`, `create_user`, `grant`, vistas, rutinas o
triggers. Las funciones administrativas del backoffice (`AdministracionController` crear base/usuario/GRANT) **no se usaron**
en ese período: las altas de bases en producción pasan hoy por el panel de despliegues (`panel_altas`).

## Diseño
1. **`mb_backoffice`** (hosts: app-4 y oci-mbinv): `ALL` solo en `mbinvmacrobase` y `_temporalesmacrobase`, y `SELECT` por tabla (no `ALL`) en `mbinvbitacoramb`:
   `cliente`, `estadotarea`, `tarea`, `tipotarea`, `usuario` y `usuariotarea` (decisión de MANUEL, 16-oct-2026; medido: el código de macrobase/tiquetes
   sólo lee esas 6 y no escribe; las escrituras del esquema son de `bitacoramb@%` y `Macrored@%`); sin permisos globales y sin `GRANT OPTION`. Los 6 sitios propios pasan a usarla (en POSTouch, `database_user` y `database_ucentral`).
2. **`mb_credito_ro`: no se crea.** No hay quien la necesite (ver arriba).
3. **`mb_provisiona`: no se crea.** Ver la respuesta técnica de abajo.
4. **`macrobase@%`**: bloquear → cambiar clave → borrar, en ese orden y con las condiciones de `cuentas.sql`.

## Respuestas técnicas sobre `mb_provisiona`
- **¿Necesita `GRANT OPTION`?** Si el código actual siguiera funcionando, sí: `Conexion::asignarPrivilegios` hace
  `GRANT ALL ... ON base.*` y un `GRANT ... WITH GRANT OPTION` al usuario de cada cliente, y MySQL solo deja otorgar lo que
  uno tiene con `GRANT OPTION`. Eso daría control total (incluido otorgarse permisos) sobre todo el alcance de la cuenta.
- **¿Se puede limitar a "las bases que debe provisionar"?** Por patrón de nombre **no**: `partial_revokes` está activo
  (`@@partial_revokes = 1`) y, según la documentación de MySQL, con esa opción `%` y `_` en un `GRANT` por base se toman como
  caracteres literales, no como comodín (sin probar en este servidor). Un `GRANT ... ON mbinv%.*` no cubriría los 135 esquemas;
  habría que otorgar esquema por esquema o usar un procedimiento almacenado (ver recomendación).
- **¿Un patrón `mbinv%` incluiría `mbinvbitacoramb`?** Si el patrón funcionara, sí, y también `mbinvmacrobase` y `mbinvinstalar`
  (135 esquemas `mbinv%` en total), sin forma de excluirlos: la cuenta podría borrar o modificar la base central.
- **Recomendación:** no crear la cuenta. Hoy nadie la usa; las altas van por el panel. Si algún día el backoffice debe provisionar,
  hacerlo con un procedimiento almacenado `SQL SECURITY DEFINER` que valide el nombre (`^[a-z0-9]{3,30}$` y fuera de una lista
  negra de bases del sistema); es un cambio de código, no de cuenta. Mientras tanto la función de "crear base" del backoffice
  fallará en producción tras el paso 4 (y está sin uso medido).

## Qué hay que cambiar junto con parameters.yml
**Nada más para `macrobase`.** `parametros_backoffice` no guarda `macrobase` en ningún sitio de cliente (ver arriba). Lo que sí
cambia: `database_user`/`database_password` en los 6 sitios (y `database_ucentral`/`database_pcentral` en macrobase/POSTouch), con
respaldo fechado del archivo, limpieza de caché y verificación HTTP del sitio. Las 2 copias de `back/` y `equivalenza` no se
tocan (ver pendientes).

## Orden de ejecución (cuando MANUEL autorice)
0. Respaldo: `SHOW GRANTS FOR 'macrobase'@'%'` y parameters.yml de los 6 sitios, todo fechado.
1. Crear `mb_backoffice` (no cambia nada existente); probar un `SELECT` y un `CREATE TEMPORARY TABLE` con la cuenta nueva.
2. Cambiar un sitio por vez: `qa_produccion` → `mariaPruebas` → `bluedevsWoo` → `tiquetes/mbinv` → `macrobase/POSTouch` → `macrobase/mbinv`.
   Después de cada uno: sitio responde 200, una operación que escribe, y conexiones nuevas de `macrobase` bajando.
3. 24 h sin conexiones nuevas de `macrobase`: `performance_schema.accounts` no guarda la hora de la última conexión, y `CURRENT_CONNECTIONS = 0`
   no detecta un sitio que conectó y salió. Se guarda una foto fechada de `TOTAL_CONNECTIONS` por host (`SELECT host,total_connections FROM
   performance_schema.accounts WHERE user='macrobase'`) y se exige que los valores no cambien durante 24 h.
4. **Compuerta:** repetir el inventario de DEFINER; si devuelve algo, esos objetos se redefinen ANTES de seguir (no basta con haberlo hecho).
   Después `ACCOUNT LOCK`, **cerrar las sesiones ya abiertas** (`KILL` de cada hilo de `macrobase` en `performance_schema.threads`; el
   bloqueo y el cambio de clave sólo frenan conexiones nuevas) y exigir `CURRENT_CONNECTIONS = 0`; luego clave nueva. La cuenta sigue
   valiendo como DEFINER. **No se borra ni se le quitan permisos globales** hasta redefinir las 21 vistas y la función de abajo y probar
   que responden (paso 4d, aparte).
5. Reversa: ver `cuentas.sql`. Orden: primero devolverle a `macrobase` la clave de los respaldos y desbloquearla (y si ya se borró,
   `CREATE USER` antes de reponer los `GRANT`); recién después restaurar los `parameters.yml`.

## Pendientes y límites de lo medido
- **DEFINER (medido el 15-oct-2026 con una cuenta que ve todo: 984 vistas, 453 rutinas, 3,163 triggers y 11 eventos en 143 esquemas).**
  Dependen de `macrobase@%` como DEFINER (todas `SQL SECURITY DEFINER`): **21 vistas** — 9 `ia_*` en `mbinvdisol` y 12 en `mbinvmacrobase`
  (las mismas 9 `ia_*`, más `vbarras_sql`, `vclientes`, `vvendedores`) — y **1 función**, `mbinvmacrobase.regex_replace`. Triggers y eventos: ninguno.
  Consecuencias: `ACCOUNT LOCK` y cambiar la clave **no** las rompen; `DROP USER`, o quitarle el `SELECT` global, **sí** (error 1449 o
  «acceso denegado» al consultar las vistas de `mbinvdisol`). Por eso `macrobase@%` queda bloqueada, no borrada, hasta redefinir esas vistas
  con `DEFINER = 'mb_backoffice'@'10.0.1.105'` (cuenta con host que existe; `mb_backoffice` sin host sería `@'%'`, que no se crea) — las vistas con `ALTER`,
  la función con `SHOW CREATE FUNCTION` + `DROP` + `CREATE` porque `ALTER FUNCTION` no cambia el DEFINER — y darle a esa misma cuenta `SELECT` en `mbinvdisol`, o hasta decidir que las vistas `ia_*` de `mbinvdisol` sobran.
  Quién consulta las vistas `ia_*`: no medido.
- **Función de errores entre esquemas (hallazgo de Codex, P1; DECISIÓN DE MANUEL).** `eventosErroresGlobales()`
  (`MensajitosController.php:2245`) hace `SHOW DATABASES LIKE '%mbinv%'` y lee tablas de cada esquema de cliente; es la causa de los
  2 `show databases` medidos. Con `mb_backoffice` (3 esquemas) vería sólo esos 3 y omitiría en silencio los errores de los demás.
  No se resuelve con comodín (ver arriba). Opciones: (a) aceptar la pérdida y avisarlo en la pantalla; (b) una cuenta aparte con los esquemas listados uno por uno: `SELECT` más `UPDATE` acotado a
  `mensajitos_auditoria` (la función marca `mensaje_servido` tras avisar por Telegram, `MensajitosController.php:2343-2348`; sin ese `UPDATE` el aviso
  se repetiría); (c) leer los errores por otra vía. Hasta que MANUEL elija, no se ejecuta nada.
- Tablas exactas que toca cada sitio propio: no medidas; el diseño da `ALL` en `mbinvmacrobase` y `_temporalesmacrobase`, y sólo `SELECT`
  en las 6 tablas de `mbinvbitacoramb`. Si en la prueba (paso 2) aparece `Access denied`: en una de esas dos bases se agrega esa base y se anota;
  en `mbinvbitacoramb` se agrega **sólo el `SELECT` de la tabla puntual** que falló (nunca `ALL` sobre el esquema) y se anota;
  si el error es por un esquema DISTINTO de esos tres, se agrega ese esquema puntual (con el permiso mínimo que haga falta) y se anota.
- ⚠️ **Otros flujos entre esquemas del sitio `macrobase/mbinv`: SIN inventariar, y bloquean la ejecución** (hallazgo de Codex, P1).
  No es sólo `eventosErroresGlobales()` (resuelto en #1342). `Apis::getDTEs()` arma la lista de clientes desde `INFORMATION_SCHEMA.TABLES`
  y lee cada esquema (`Apis.php:~934-1035`); `Instalacion.php:~2052-2064` registra otros reportes administrativos que pueden recorrer
  esquemas (`DTEs`, `Clientes Sin Actividad`, `Tamaño de Bases de Datos`, `Cuadre Transacciones`, `Detalle de BacksUps`, `Consolidado de Errores SAM`,
  `Reporte de Errores`). MySQL sólo muestra en `INFORMATION_SCHEMA` los objetos sobre los que la cuenta tiene permiso: con `mb_backoffice`
  esos reportes omitirían clientes o fallarían, sin avisar. Antes de ejecutar este SQL hay que inventariar cuáles recorren esquemas
  y migrarlos al patrón de llamada interna de Reportes Globales (#25131, mbinv#1341) o decidir cada uno con MANUEL. No medido todavía.
  `qa_produccion` (oci-mbinv, que apunta a producción) no se verificó contra `mbinvbitacoramb`.
- `equivalenza/POSTouch` dice `macrobase` en `database_umacrobase` pero con la clave de otra cuenta (falla o no se usa); se revisa aparte.
- Las otras credenciales de #12693 (usuario `actualizar-mb`, clave dentro de un script) y el borrado de las notas son un frente aparte.
