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[] |
findManypagina siempre. Sinlimit, la API da los 100 primeros y el resto no llega: una pantalla con más usuarios tiene que paginar conlimityoffset, o leerpagination.total. Los filtros sonis_active,username(contiene) yrole(lo tiene asignado), y el total cuenta con ellos.updateenvía solo lo que cambia.roleses la lista entera —se quitan los que no estén—; para tocar uno,assignRoleyremoveRole. 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.