Saltar a contenido

La gestión de usuarios

Con autenticación, el cliente trae api.rbac(realm): los usuarios, los roles y las reglas de RLS de un realm, que en la API son los endpoints de /rbac. Es lo que necesita la pantalla de administración de usuarios, sin un fetch ni un tipo escritos a mano.

const rbac = api.rbac('main')                        // un realm del proyecto: ver «El realm» en La sesión

const { data, pagination } = await rbac.users.findMany({ limit: 50, role: 'lector' })   // Page<RBACUser>
const ana = await rbac.users.find('ana')
await rbac.users.create({ username: 'ana', password: '…', roles: ['lector'] })
await rbac.users.update('ana', { is_active: false })
await rbac.users.delete('ana')
await rbac.users.assignRole('ana', 'editor')
await rbac.users.removeRole('ana', 'editor')

const roles = await rbac.roles.findMany()            // RBACRoleInfo[]: nombre, permisos y padre
const reglas = await rbac.rls.findMany()             // RBACRLS[]: las reglas que se pueden asignar
Función Endpoint Devuelve
users.findMany(params?) GET /rbac/{realm}/user Page<RBACUser>, por nombre
users.find(username) GET /rbac/{realm}/user/{username} RBACUser
users.create(values) POST /rbac/{realm}/user RBACUser
users.update(username, values) PATCH /rbac/{realm}/user/{username} RBACUser
users.delete(username) DELETE /rbac/{realm}/user/{username} el nombre del usuario
users.assignRole(username, role) POST /rbac/{realm}/user/{username}/role true
users.removeRole(username, role) DELETE /rbac/{realm}/user/{username}/role/{role} true
roles.findMany() GET /rbac/{realm}/role RBACRoleInfo[]
rls.findMany() GET /rbac/{realm}/rls RBACRLS[]
  • findMany pagina siempre. Sin limit, la API da los 100 primeros y el resto no llega: una pantalla con más usuarios tiene que paginar con limit y offset, o leer pagination.total. Los filtros son is_active, username (contiene) y role (lo tiene asignado), y el total cuenta con ellos.
  • update envía solo lo que cambia. roles es la lista entera —se quitan los que no estén—; para tocar uno, assignRole y removeRole. Cambiar la contraseña o desactivar al usuario cierra sus sesiones.
  • Los errores son un TaiApiError: un rol o un usuario que no existe, 404; un nombre repetido, 409.

Las reglas de RLS

Cada regla declarada con rls() en rbac/<realm>.py es un campo de RBACUser, RBACUserCreate y RBACUserUpdate, con el nombre con que viaja y el tipo de su columna:

await rbac.users.create({ username: 'ana', password: '…', 'rls.public.lote.centro_id': [1, 2] })
const centros: number[] | null = ana['rls.public.lote.centro_id']

null es que la regla no aplica (ve todo) y [], que aplica con cero valores (no ve nada). En update, omitirla no la toca y null la quita. Ver Filtrado por fila.

El permiso que exige

Toda la gestión exige administrar el realm: PERMISSIONS.<REALM>.ADMIN (main-admin para main), o sudo o allrealms-admin, que valen en cualquiera. Lo comprueba la API: sin él, cada llamada es un 403.

Un permiso de aplicación (gestion_usuarios-access) no abre /rbac: sirve para decidir quién ve la pantalla, pero si ese usuario no administra el realm, la pantalla se le enseña y la API le responde 403 en todo. Para ocultarla con la misma regla que la API:

import { canAdminRealm } from '@/lib/api'

canAdminRealm(session.user, 'main')                  // <realm>-admin, sudo o allrealms-admin

o, en la declaración de las pantallas, permission: PERMISSIONS.MAIN.ADMIN (que sudo también cumple). PERMISSIONS lleva siempre el ADMIN de cada realm del proyecto.

Con TanStack Query

Con "tanstack": true, rbac/query.ts trae las opciones, del mismo estilo que las de cada schema (ver TanStack Query), para un realm:

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { rbacMutations, rbacQueries } from '@/lib/api/rbac/query'

const usuarios = useQuery(rbacQueries(api, 'main').users.findMany({ limit: 50 }))
const roles = useQuery(rbacQueries(api, 'main').roles.findMany())
const alta = useMutation(rbacMutations(api, useQueryClient(), 'main').users.create({ onSuccess: (u) => cerrar(u.username) }))
<script lang="ts">
  import { createMutation, createQuery, useQueryClient } from '@tanstack/svelte-query'
  import { rbacMutations, rbacQueries } from '$lib/api/rbac/query'

  const usuarios = createQuery(rbacQueries(api, 'main').users.findMany({ limit: 50 }))
  const alta = createMutation(rbacMutations(api, useQueryClient(), 'main').users.create())
</script>

Las keys son ['tai', 'rbac', '<realm>', 'users' | 'roles' | 'rls', …], y rbacKeys(realm) las da (rbacKeys('main').users.find('ana')). Las mutaciones son factorías que suman los callbacks de la pantalla, como las de los schemas; cada una invalida los usuarios de su realm —no los roles ni las reglas—, create y update dejan además el usuario devuelto en la key de users.find, y delete la quita.

Lo que se borra al migrar

Una pantalla escrita a mano —un servicio con fetch a /rbac/main/…, sus tipos Usuario y Rol, sus claves de caché— se sustituye por lo de arriba: RBACUser es su Usuario, RBACRoleInfo su Rol, y RBACUserCreate/RBACUserUpdate sus cuerpos.