[CLAUDE] Phase5.1/3.2: IDOR filter + SLA auto-approve job + admin password warning

IDOR filter ContractsController:
- ListContractsQueryHandler + ICurrentUser: non-admin chi thay HD minh la Drafter hoac role eligible phase hien tai
- GetContractQueryHandler + ICurrentUser: throw ForbiddenException neu truy cap HD khong lien quan
- GetEligiblePhases() internal static trong ListContractsQueryHandler — mirror GetMyInboxQueryHandler.PhaseActorRoles (Drafter/DeptManager → DangSoanThao/DangDamPhan/DangInKy, ProjectManager+PRO+CCM+FIN+ACT+EQU → DangGopY, CostControl → DangKiemTraCCM, Director+AuthorizedSigner → DangTrinhKy, HrAdmin → DangDongDau)

SLA Expiry BackgroundService (Phase 3 iteration 2 partial):
- Infrastructure/HostedServices/SlaExpiryJob MOI: BackgroundService moi 15 phut (delay 30s startup)
- Query Contracts WHERE SlaDeadline < UtcNow AND Phase NOT IN (DaPhatHanh, TuChoi)
- Map phase → next (happy path). Goi IContractWorkflowService.TransitionAsync voi actorUserId=null + Decision=AutoApprove + comment 'AUTO: het SLA phase X (Nh qua han)'
- Try-catch tung contract, 1 fail khong block batch
- Log structured: 'SlaExpiryJob: auto-approved contract {Id} {From} → {To}'
- Package Microsoft.Extensions.Hosting added to Infrastructure
- DI register AddHostedService<SlaExpiryJob>

Admin password warning (Phase 5.1):
- DbInitializer.WarnDefaultAdminPasswordAsync: check CheckPasswordAsync voi AdminPassword default → log WRN '⚠️  Admin user vẫn dùng password mặc định. ĐỔI NGAY trong production!'
- Chain vao InitializeAsync sau cac seed

E2E verified:
- Admin GET /contracts → total 1 (see all)
- Drafter GET /contracts → total 0 (IDOR filter, chua tao HD nao)
- API startup log: '⚠️  Admin user admin@solutionerp.local vẫn dùng password mặc định'
- Build + TS check → pass

Docs:
- STATUS.md: Phase 5.1 hau nhu xong (IDOR + admin warning + SLA job tick), cumulative BE 3900 LOC
- migration-todos.md: tick Phase 5.1 IDOR + admin warning, Phase 3 iter 2 SlaExpiryJob + E2E non-admin + admin warning
- session log 2026-04-21-1730-idor-sla-job.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-21 13:15:14 +07:00
parent 11e61c9c39
commit 1b5ef2ed51
8 changed files with 267 additions and 19 deletions

View File

@ -141,20 +141,21 @@
- [x] PhaseBadge component + color map
- [x] E2E verified: tạo HĐ → chạy 9 phase → gen mã `FLOCK 01/HĐGK/SOL&PVL2026/01`
### Iteration 2 (polish — optional)
### Iteration 2 (polish)
- [ ] `Infrastructure/HostedServices/SlaExpiryJob` — check mỗi 15min, auto-approve quá hạn với Decision=AutoApprove
- [ ] Warning notification khi còn 20% SLA
- [x] `Infrastructure/HostedServices/SlaExpiryJob` — check mỗi 15min, auto-approve quá hạn với Decision=AutoApprove (+30s delay startup)
- [x] E2E test với non-admin user (Drafter role) — IDOR filter verified
- [x] Admin password warning log khi vẫn dùng default
- [ ] Warning notification khi còn 20% SLA (track `SlaWarningSent` flag đã có)
- [ ] `Infrastructure/Services/NotificationService` — email (MailKit) + in-app
- [ ] SignalR hub cho real-time notification badge
- [ ] MediatR `AuditBehavior` — log mọi command (ngoài ContractApprovals)
- [ ] Upload attachment endpoint (multipart) + FE upload UI (`wwwroot/uploads/contracts/{id}/`)
- [ ] RowVersion optimistic concurrency (2 user race → 409)
- [ ] Render HĐ docx lúc tạo (merge TemplateId + DraftData + ContractClause appendix)
- [ ] E2E test với non-admin user (Drafter/CCM/BOD role)
- [ ] Filter Inbox theo phase ở FE
- [ ] E2E test: reject → quay về DangSoanThao
- [ ] E2E test: SLA expired → auto-approve + log
- [ ] E2E test: SLA expired → auto-approve + log (test thật qua set SlaDeadline past)
## Phase 4 — Reporting + Polish (T10-11)
@ -219,10 +220,10 @@
- [x] LoginCommand: check IsLockedOutAsync + AccessFailedAsync + reset on success
- [x] BE Users management: CQRS 8 feature + UsersController 7 endpoint (Users.Read/Create/Update policies)
- [x] FE admin `/system/users`: list + create + assign roles + reset password + unlock + toggle active
- [ ] IDOR check ContractsController — user Drafter chỉ xem HĐ mình tạo hoặc role giữ phase
- [x] IDOR check ContractsController — user Drafter chỉ xem HĐ mình tạo hoặc role eligible phase (`ListContractsQueryHandler` + `GetContractQueryHandler`)
- [x] Admin mặc định warning log (`DbInitializer.WarnDefaultAdminPasswordAsync`)
- [ ] Dependencies scan vào CI (`dotnet list package --vulnerable --include-transitive`, `npm audit --audit-level=high`)
- [ ] Admin mặc định: warning log force đổi password
- [ ] BE Roles CRUD (Create/Rename/Delete custom role) + FE `/system/roles`
- [ ] BE Roles CRUD (Create/Rename/Delete custom role) + FE `/system/roles` — optional, 12 role seed đủ dùng
## Post-launch (Phase 6+ — future)