Suspender un tenant sin echar a sus personas
Un tenant suspendido tiene que perder su panel: mientras dura el periodo de salida, nadie debería seguir registrando datos allí. Pero las personas que lo usaban no desaparecen, y muchas veces son también clientes del producto: el veterinario que atiende en la clínica tiene su propio perro dado de alta.
Rechazar el token cierra las dos puertas a la vez. El personal pierde el panel, que es lo que se quería, y también la aplicación de cliente, que no.
La alternativa obvia —comprobar “¿está activo el tenant?” en cada endpoint del panel— envejece mal: hay que acertar endpoint por endpoint, y el que se olvide no avisa.
Degradar en vez de rechazar
Al validar la petición, si quien llama es personal de un tenant suspendido, se le rebaja el rol efectivo al de cliente:
/** La misma persona, con los permisos de un cliente. */
asOwner(): User {
return new User({ ...this, role: Role.USER });
}
A partir de ahí no hace falta tocar nada más: la autorización que ya existe hace el trabajo. Los
endpoints con @Roles(STAFF, ADMIN) responden 403, los listados por rol devuelven lo del cliente, y
las comprobaciones por entidad —“¿esta mascota es tuya?”— le dejan justo lo suyo. El panel queda
cerrado sin haberlo cerrado a mano.
La lista corta de excepciones
Algunos endpoints necesitan el rol real aunque el tenant esté suspendido: saber quién eres para pintar la pantalla de “clínica suspendida”, y la exportación de los datos, que es justo lo que hay que poder hacer durante la salida. Se marcan con un decorador:
@Get("me")
@AllowWhileTenantInactive(Role.ADMIN, Role.STAFF)
Dos reglas para que esa lista no crezca sola: la decide el guard, que es quien ve los metadatos del endpoint —la validación del token solo marca la situación, porque no sabe a qué ruta se dirige—, y cada entrada nueva hay que poder justificarla en una frase.
Lo que esta técnica no arregla
Degradar el rol no inventa autorización: se apoya en la que haya. Un endpoint que no mira el rol sigue abierto, ahora para un cliente.
Nos pasó con el que edita el propio tenant: no tenía @Roles ni comprobación dentro, así que
cualquier cliente podía cambiar el nombre y los datos fiscales de su clínica — y, con la técnica de
arriba, el personal suspendido también. La suspensión no lo causó, solo lo puso a la vista.
Así que este cambio viene con una revisión: listar los endpoints que escriben y no comprueban
rol. Los que tenían @Roles ya estaban resueltos; los que confiaban en que “al panel solo entra
personal” no.
Y en el registro de auditoría
Las acciones quedan con el rol efectivo, no con el nominal. Es lo correcto: lo que ocurrió es que alguien actuó como cliente sobre sus propios datos, y así se lee después.
Qué probar
- Personal de un tenant suspendido → 403 en los endpoints del panel, 200 en lo suyo como cliente.
- El mismo en
users/me→ sigue viendo su rol real. - Cliente normal → nada cambia.
- Personal de un tenant activo → nada cambia.