Commit Graph

91 Commits

Author SHA1 Message Date
1c79dfa0a4 feat(db): V010 audit infrastructure + temporal tables
Applied to SIGCM2 (dev) and SIGCM2_Test.

V010__audit_infrastructure.sql (idempotent, ~280 LoC):
- Filegroups AUDIT_HOT + AUDIT_COLD with physical files (per-DB logical names
  via DB_NAME() prefix to avoid collision in dev/test).
- pf/ps_AuditEvent_Monthly + pf/ps_SecurityEvent_Monthly (RANGE RIGHT,
  DATETIME2(3), 14 boundaries 2026-01..2027-02 → 15 partitions). Job extends
  forward monthly in B11.
- dbo.AuditEvent (partitioned, clustered PK on OccurredAt+Id) + 4 indexes
  (Actor/Target/Action/Correlation) with PAGE compression.
- dbo.SecurityEvent (partitioned) + 3 indexes (Actor/Action_Result/Ip_Failure).
- CHECK constraints: Action LIKE '%.%', ISJSON(Metadata), Result IN (success|failure).
- SYSTEM_VERSIONING ON in Usuario/Rol/Permiso/RolPermiso with 10 YEARS retention +
  PAGE compression in history tables.
- No hard FK on ActorUserId → Usuario.Id (soft FK — audit must survive user deletion).

V010_ROLLBACK.sql: emergency reversal (WARNING: destroys all audit history).

database/README.md: migration order + V010 prod-apply notes.

tests/SIGCM2.TestSupport/SqlTestFixture.cs:
- EnsureV010SchemaAsync() validates audit infra is applied (fails fast with
  clear message if not — migration itself requires ALTER DATABASE privileges
  and is applied manually via sqlcmd).
- Respawn TablesToIgnore extended with *_History (engine rejects direct DELETE
  on system-versioned history tables).

tests/SIGCM2.Api.Tests/Audit/V010MigrationTests.cs — 5 smoke tests:
- AuditEvent insert+roundtrip with CorrelationId.
- CK_AuditEvent_Action rejects Action without '.'.
- CK_AuditEvent_Metadata rejects non-JSON.
- CK_SecurityEvent_Result rejects invalid Result.
- Usuario SYSTEM_VERSIONING: temporal query FOR SYSTEM_TIME AS OF returns
  pre-update state + Usuario_History populated.

Suite: 130/130 passing (previous 124 + spike B0 + 5 new B1). No regressions.

Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-1,2, #REQ-SEC-1,
design#D-4, tasks}
2026-04-16 13:10:04 -03:00
2d1d187f6e chore(udt-010): bootstrap rama + spike anti-MSDTC
Validates design decision #D-1 (TransactionScope ambient over IUnitOfWork):
TransactionScope with TransactionScopeAsyncFlowOption.Enabled does NOT
escalate to MSDTC when multiple SqlConnections share the same connection
string. Test passes (DistributedIdentifier == Guid.Empty).

Unblocks UDT-010 batches B1-B14.

Refs: sdd/udt-010-auditoria-trazabilidad/{design,tasks}
2026-04-16 12:56:17 -03:00
47323302cc feat(api): GET /api/v1/users/{id}/permisos con CQRS handler [UDT-009] 2026-04-15 21:43:08 -03:00
5fd88b5a9d feat(infra): IUsuarioRepository.UpdatePermisosJsonAsync + impl Dapper [UDT-009] 2026-04-15 21:33:39 -03:00
bf64ffb35e feat(api): PermissionAuthorizationHandler resuelve overrides desde DB por request [UDT-009] 2026-04-15 21:32:35 -03:00
fb07a1139a feat(application): LoginCommandHandler usa PermisoResolver para permisos efectivos [UDT-009] 2026-04-15 21:29:33 -03:00
86310de286 feat(security): remover claim permisos del JWT post-UDT-009 [UDT-009] 2026-04-15 21:28:26 -03:00
54955231bf feat(infra): V009 migration + Usuario.WithPermisosJson + SqlTestFixture V009 schema [UDT-009] 2026-04-15 21:27:29 -03:00
da1eb83ac1 feat(application): PermisosOverride record + PermisoResolver static helper [UDT-009] 2026-04-15 21:25:09 -03:00
7d96d5ff18 feat(api): ResetPassword admin — TempPasswordGenerator, handler, endpoint POST /{id}/password/reset [UDT-008]
Batch 7: POST /api/v1/users/{id}/password/reset (admin only).
- TempPasswordGenerator: RandomNumberGenerator.Fill, 12-char min, full charset diversity, never logs result
- ResetUsuarioPasswordCommandHandler: self-reset guard, 404, hash, mustChangePassword=true, revoke all tokens
- ExceptionFilter: CannotSelfResetException → 400 {error: cannot-self-reset}
- Unit tests: TempPasswordGeneratorTests (8), ResetUsuarioPasswordCommandHandlerTests (5)
- Integration tests: ResetPasswordEndpointTests (6) — 200/length/self-reset/404/401/403
2026-04-15 17:55:45 -03:00
a3bd066f7b feat(api): ChangeMyPassword — validator, handler, endpoint PUT /me/password [UDT-008] 2026-04-15 17:52:15 -03:00
473566f255 feat(api): Deactivate + Reactivate usuarios — idempotentes, anti-lockout, revoke tokens [UDT-008] 2026-04-15 17:50:54 -03:00
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
8513e99554 test(api): assert count 21 permisos admin post-V007 [UDT-006] 2026-04-15 16:49:54 -03:00
0218d8d371 feat(api): migrar controllers admin a RequirePermission [UDT-006] 2026-04-15 16:34:32 -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
f6ad371de4 chore(tests): BATCH 0 - agregar Permiso y RolPermiso a TablesToIgnore [UDT-005] 2026-04-15 15:26:19 -03:00
57e4cdac01 chore(tests): limpia warning xUnit2012 en CreateUsuario_WithInactiveRol_Returns400
Reemplaza Assert.True(enumerable.Any(...)) por Assert.Contains idiomatico.
2026-04-15 13:03:18 -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
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
f1d4ea0047 fix(test): RefreshTokenRepository tests use Respawn pattern instead of transaction isolation
Transaction-scoped tests conflicted with the repository opening its own connection,
blocking on FK locks for the uncommitted seeded user and causing timeouts.
Switched to the Respawn pattern used by UsuarioRepositoryTests ([Collection("Database")])
which commits seed data and resets between test classes.
2026-04-14 13:45:53 -03:00
4e7b2690bd test(api): add Refresh and Logout endpoint integration tests RED 2026-04-14 13:28:44 -03:00
aed26e3de9 feat(infra): register RefreshTokenRepository, RefreshTokenGenerator, ClientContext and handlers in DI 2026-04-14 13:28:36 -03:00
e405c0453b test(infra): add RefreshTokenRepository integration tests RED 2026-04-14 13:28:28 -03:00
2806e8dfa6 test(infra): add RefreshTokenGenerator tests RED 2026-04-14 13:28:24 -03:00
a363e3658d test(infra): add GetPrincipalFromExpiredToken tests for JwtService RED 2026-04-14 13:28:20 -03:00
b79efc778a test(app): extend LoginCommandHandler tests with refresh token persistence cases RED 2026-04-14 13:28:15 -03:00
15a7687e4c test(app): add LogoutCommandHandler tests RED 2026-04-14 13:28:10 -03:00
25639398c2 test(app): add RefreshCommandHandler tests RED 2026-04-14 13:28:02 -03:00
22aff10330 test(domain): add TokenHasher tests RED 2026-04-14 13:16:43 -03:00
2efe4115c4 test(domain): add RefreshToken entity tests RED 2026-04-14 13:16:36 -03:00
b657dc0d2a test(udt-001): backend unit and integration tests (30 tests) 2026-04-13 21:36:09 -03:00