UDT-009: Overrides de PermisosJson por usuario — cierre módulo Auth #12

Merged
dmolinari merged 14 commits from feature/UDT-009 into main 2026-04-16 13:12:23 +00:00
Owner

Summary

Activación de Usuario.PermisosJson como overrides grant/deny por usuario. Cierre definitivo del módulo Auth (UDT-001 a UDT-009).

Backend (.NET 10)

  • Migración V009: nuevo DF_Usuario_Permisos con default {"grant":[],"deny":[]} + UPDATE de filas legacy ('[]' / '["*"]' / NULL) → shape canónico
  • PermisosOverride record + PermisoResolver static en Application.Common — helper único consumido por LoginHandler y PermissionAuthorizationHandler
  • JwtService ya no emite el claim permisos del token (era letra muerta). Token final: sub, jti, name, rol
  • LoginCommandHandler devuelve permisos efectivos (rol ∪ grant) \ deny)
  • PermissionAuthorizationHandler agrega dependencia IUsuarioRepository y resuelve efectivos por request (sin cache)
  • IUsuarioRepository.UpdatePermisosJsonAsync nuevo método Dapper
  • 2 endpoints nuevos:
    • GET /api/v1/users/{id}/permisos{ rolPermisos, overrides: {grant, deny}, effective }
    • PUT /api/v1/users/{id}/permisos/overrides body { grant, deny }
  • InvalidPermisoCodesException + GrantDenyOverlapException → 400 ProblemDetails

Frontend (React 19)

  • UserEditPage refactorizado con Tabs Perfil / Permisos
  • Tab Permisos deshabilitada en self-edit (evita auto-lockout accidental)
  • PermisosEditor component tri-state (Heredado / Concedido / Denegado) agrupado por módulo
  • tabs.tsx shadcn sobre @radix-ui/react-tabs (paquete nuevo)
  • useUserPermisos + useUpdateUserPermisosOverrides hooks
  • Errors 400 mapeados a Alert UI

SDD Artifacts (engram)

sdd/udt-009-permisos-overrides/* — explore / proposal / spec / design / tasks / apply-progress

Test plan

  • dotnet test tests/SIGCM2.Application.Tests — 325/325
  • dotnet test tests/SIGCM2.Api.Tests — 124/124
  • cd src/web && npx vitest run — 136/136
  • V009 aplicada en SIGCM2 dev — 3 filas migradas al nuevo shape
  • Smoke test manual: cajero + grant administracion:usuarios:gestionar → acceso a /usuarios. Admin + deny administracion:permisos:ver → 403 en endpoint correspondiente.

Close-out

Con UDT-009 el módulo Auth (UDT-001..006, 008, 009) queda 100% cerrado. RBAC completo con roles + overrides por usuario. Próximo critical path: ADM-001 (Medios y Secciones).

## Summary Activación de `Usuario.PermisosJson` como overrides grant/deny por usuario. **Cierre definitivo del módulo Auth** (UDT-001 a UDT-009). **Backend (.NET 10)** - Migración **V009**: nuevo `DF_Usuario_Permisos` con default `{"grant":[],"deny":[]}` + UPDATE de filas legacy (`'[]'` / `'["*"]'` / NULL) → shape canónico - `PermisosOverride` record + `PermisoResolver` static en `Application.Common` — helper único consumido por LoginHandler y PermissionAuthorizationHandler - `JwtService` **ya no emite** el claim `permisos` del token (era letra muerta). Token final: `sub`, `jti`, `name`, `rol` - `LoginCommandHandler` devuelve permisos **efectivos** (`rol ∪ grant) \ deny`) - `PermissionAuthorizationHandler` agrega dependencia `IUsuarioRepository` y resuelve efectivos por request (sin cache) - `IUsuarioRepository.UpdatePermisosJsonAsync` nuevo método Dapper - 2 endpoints nuevos: - `GET /api/v1/users/{id}/permisos` → `{ rolPermisos, overrides: {grant, deny}, effective }` - `PUT /api/v1/users/{id}/permisos/overrides` body `{ grant, deny }` - `InvalidPermisoCodesException` + `GrantDenyOverlapException` → 400 ProblemDetails **Frontend (React 19)** - `UserEditPage` refactorizado con Tabs **Perfil / Permisos** - Tab Permisos deshabilitada en self-edit (evita auto-lockout accidental) - `PermisosEditor` component tri-state (Heredado / Concedido / Denegado) agrupado por módulo - `tabs.tsx` shadcn sobre `@radix-ui/react-tabs` (paquete nuevo) - `useUserPermisos` + `useUpdateUserPermisosOverrides` hooks - Errors 400 mapeados a Alert UI ## SDD Artifacts (engram) `sdd/udt-009-permisos-overrides/*` — explore / proposal / spec / design / tasks / apply-progress ## Test plan - [x] `dotnet test tests/SIGCM2.Application.Tests` — 325/325 - [x] `dotnet test tests/SIGCM2.Api.Tests` — 124/124 - [x] `cd src/web && npx vitest run` — 136/136 - [x] **V009 aplicada en `SIGCM2` dev** — 3 filas migradas al nuevo shape - [x] Smoke test manual: cajero + grant `administracion:usuarios:gestionar` → acceso a `/usuarios`. Admin + deny `administracion:permisos:ver` → 403 en endpoint correspondiente. ## Close-out Con UDT-009 el módulo Auth (UDT-001..006, 008, 009) queda 100% cerrado. RBAC completo con roles + overrides por usuario. Próximo critical path: **ADM-001** (Medios y Secciones).
dmolinari added 12 commits 2026-04-16 00:53:48 +00:00
dmolinari added 1 commit 2026-04-16 13:06:46 +00:00
El backend devuelve { rolPermisos, overrides: {grant, deny}, effective }
(nested) segun spec, pero el frontend lo lee como {grant, deny} planos.
Causaba TypeError: permisoData.grant is not iterable al abrir tab Permisos.
Tests del frontend actualizados con el shape correcto.
dmolinari added 1 commit 2026-04-16 13:11:06 +00:00
Sonner estaba como dependencia pero el componente Toaster nunca se monto
en el arbol de la app. ChangeMyPasswordPage ya usaba toast() pero no
mostraba nada visualmente. Agregado <Toaster richColors closeButton /> en
App.tsx (top-right) y toasts de exito/error en PermisosEditor.handleSave
para confirmar al usuario que el cambio se persistio.
dmolinari merged commit 492705c076 into main 2026-04-16 13:12:23 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dmolinari/SIG-CM2.0#12