Commit Graph

40 Commits

Author SHA1 Message Date
14c385fdb1 feat(api): UpdateUsuario — handler, validator, anti-lockout guard, revoke tokens [UDT-008] 2026-04-15 17:49:19 -03:00
2925336783 feat(api): List + GetById usuarios — handlers, repo, endpoints [UDT-008] 2026-04-15 17:46:23 -03:00
9dcd63543e feat(auth): extend LoginResponse with username + mustChangePassword + ultimoLogin [UDT-008] 2026-04-15 17:39:48 -03:00
d1f7b3805b feat(domain): V008 migration + Usuario with-methods + DomainException hierarchy [UDT-008] 2026-04-15 17:36:46 -03:00
0218d8d371 feat(api): migrar controllers admin a RequirePermission [UDT-006] 2026-04-15 16:34:32 -03:00
4866c4f21f feat(api): ForbiddenProblemDetailsHandler 403 shape [UDT-006] 2026-04-15 16:27:36 -03:00
58d0df601f feat(api): RequirePermissionAttribute + PermissionAuthorizationHandler [UDT-006] 2026-04-15 16:26:30 -03:00
cdb8dcd03c feat(api): login response permisos desde RolPermiso [UDT-006] 2026-04-15 16:24:21 -03:00
1a864e9f8b fix(app): validar formato codigo rol en GetRolPermisos [UDT-005]
Agrega GetRolPermisosQueryValidator con regex ^[a-z][a-z0-9_]*$ para
rechazar codigos invalidos con 400 en GET /api/v1/roles/{codigo}/permisos.
2026-04-15 15:56:49 -03:00
4913a35d06 feat(api): BATCH 5 - PermisosController + tests HTTP [UDT-005] 2026-04-15 15:42:03 -03:00
be2257a9bf feat(infra): BATCH 4 - Permiso/RolPermiso repos Dapper + tests integracion [UDT-005] 2026-04-15 15:39:25 -03:00
704794a2e2 feat(app): BATCH 3 - handlers permisos con TDD [UDT-005] 2026-04-15 15:31:26 -03:00
7ddb71c24c feat(domain): BATCH 2 - Permiso entity + catalogo const [UDT-005] 2026-04-15 15:31:20 -03:00
6f999b8fcd feat(api): UDT-004 controller de roles + refactor validator UDT-003 a lookup dinamico
- RolesController /api/v1/roles CRUD admin-only: GET list, GET {codigo}, POST, PUT, DELETE (soft-delete con guard 409)
- ExceptionFilter: mapea RolNotFound (404), RolAlreadyExists (409), RolInUse (409)
- DI: registra 5 handlers de Roles (Application) y IRolRepository/RolRepository (Infrastructure)
- CreateUsuarioCommandValidator: reemplaza whitelist hardcoded por IRolRepository.ExistsActiveByCodigoAsync via MustAsync; constructor recibe (AuthOptions, IRolRepository)
- Tests: 202 verdes (173 application + 29 api). Nuevas: RolesEndpointTests (13 integration), CreateUsuarioCommandValidatorTests reescrito con NSubstitute mock, CreateUsuario_WithInactiveRol_Returns400 en Api.Tests
- Fix: ApiIntegration pasa de IClassFixture (N factories) a ICollectionFixture (1 factory shared) — evitaba ObjectDisposedException sobre RSABCrypt al compartir coleccion con multiples test classes
- tests/tests.runsettings: MaxCpuCount=1 para evitar race entre assemblies sobre SIGCM2_Test
2026-04-15 12:50:24 -03:00
34b714750a feat(api): UDT-004 dominio + repositorio + application roles (tdd)
- Migraciones V003 (tabla Rol + 8 seeds canonicos) y V004 (drop CK + FK Usuario.Rol)
- Dominio: Rol entity + 3 excepciones (RolNotFound/AlreadyExists/InUse)
- Infraestructura: RolRepository (Dapper) con List/Get/ExistsActive/Add/Update/HasActiveUsuarios
- Application: CRUD queries y commands (List, Get, Create, Update, Deactivate) + validators (codigo regex ^[a-z][a-z0-9_]*$)
- Validator UDT-003: whitelist alineada a codigos canonicos (full IRolRepository lookup diferido a Phase 5.1)
- Tests: 169 application + 15 api (todos verdes). Respawn configurado para re-seedear Rol canonical post-reset.
- Estricto TDD: RED/GREEN/TRIANGULATE en todos los handlers nuevos.
2026-04-15 12:31:29 -03:00
bce591e63c fix(auth): preserve JWT claim names in bearer middleware
JwtBearerOptions.MapInboundClaims defaulted to true, which mapped the
'sub' claim to ClaimTypes.NameIdentifier in HttpContext.User. Logout
endpoint read User.FindFirst("sub") and got null, returning 401 for
any authenticated caller.

Fix: set MapInboundClaims=false and pin NameClaimType="name" so the
JWT claims land in the principal with their original names, aligning
with how JwtService.GetPrincipalFromExpiredToken (used by refresh)
already consumes them.

Unblocks Login_Refresh_Logout_FullFlow integration test (15/15 green).
2026-04-15 11:03:15 -03:00
3d598faffc feat(api): UDT-003 registro de usuarios — backend completo (Phases 1-6)
- Domain: Usuario.ForCreation factory, UsernameAlreadyExistsException, IUsuarioRepository extendido
- Application: CreateUsuarioCommand/Validator/Handler, UsuarioCreatedDto, AuthOptions password policy
- Infrastructure: UsuarioRepository.ExistsByUsernameAsync + AddAsync (INSERT OUTPUT INSERTED.Id), RoleClaimType="rol" en TokenValidationParameters
- Api: UsuariosController POST api/v1/users [Authorize(Roles="admin")], ExceptionFilter mapea UsernameAlreadyExistsException + SqlException 2627 → 409
- Tests (unit): 43 tests — 33 validator + 10 handler (107 total, green)
- Tests (integration): 7 tests CreateUsuarioEndpoint — 401/403/400/201/409/race/e2e (green)
- Fix: TestWebAppFactory.ConfigureTestServices reemplaza SqlConnectionFactory singleton con CS de test correcto
2026-04-15 10:47:48 -03:00
fd2ff8a802 feat(api): map InvalidRefreshTokenException and TokenReuseDetectedException to generic 401 2026-04-14 13:28:45 -03:00
8768067fdd feat(api): add /refresh [AllowAnonymous] and /logout [Authorize] endpoints to AuthController 2026-04-14 13:28:45 -03:00
aed26e3de9 feat(infra): register RefreshTokenRepository, RefreshTokenGenerator, ClientContext and handlers in DI 2026-04-14 13:28:36 -03:00
cb4250f7b3 feat(infra): implement ClientContext for IP and UserAgent from IHttpContextAccessor 2026-04-14 13:28:35 -03:00
19ac807500 feat(infra): add RefreshTokenDays to JwtOptions and AuthOptions config 2026-04-14 13:28:35 -03:00
0c809da633 feat(infra): implement RefreshTokenRepository with Dapper and add GetByIdAsync to UsuarioRepository 2026-04-14 13:28:29 -03:00
d326dd87e0 feat(infra): implement RefreshTokenGenerator with cryptographic random bytes 2026-04-14 13:28:24 -03:00
c910ff2fc5 feat(infra): implement GetPrincipalFromExpiredToken in JwtService 2026-04-14 13:28:20 -03:00
8bbd2b6f2a feat(app): update LoginCommandHandler to persist hashed refresh token on login 2026-04-14 13:28:16 -03:00
6c02197369 feat(app): implement LogoutCommand handler with idempotent revocation 2026-04-14 13:28:10 -03:00
f5e67b78a5 feat(app): implement RefreshCommand handler with token rotation and chain revocation 2026-04-14 13:28:06 -03:00
971f6f572f feat(app): add IClientContext abstraction for IP and UserAgent 2026-04-14 13:17:12 -03:00
84006776b6 feat(app): add IRefreshTokenGenerator abstraction 2026-04-14 13:17:12 -03:00
802c89ffe5 feat(app): add IRefreshTokenRepository abstraction 2026-04-14 13:17:11 -03:00
ba6dffb137 feat(app): extend IJwtService with GetPrincipalFromExpiredToken 2026-04-14 13:17:11 -03:00
83c6a95ee2 feat(domain): add InvalidRefreshTokenException and TokenReuseDetectedException 2026-04-14 13:16:44 -03:00
aacfd29673 feat(domain): add TokenHasher SHA-256 base64url helper 2026-04-14 13:16:43 -03:00
99bb3364c3 feat(domain): add RefreshToken entity with factory methods and IsActive logic 2026-04-14 13:16:38 -03:00
9891f96618 feat(udt-001): api layer with AuthController, Program.cs and Serilog 2026-04-13 21:36:08 -03:00
ca57ce33b5 feat(udt-001): infrastructure (Dapper, BCrypt, JWT RS256, dispatcher) 2026-04-13 21:36:02 -03:00
8c26cd3ac5 feat(udt-001): application layer with LoginCommandHandler and ports 2026-04-13 21:36:01 -03:00
2111070c77 feat(udt-001): domain layer with Usuario entity 2026-04-13 21:36:00 -03:00
88ecaa2c7f chore(udt-001): RSA key generation script 2026-04-13 21:35:56 -03:00