Files
solution-erp/docs/changelog/sessions/2026-04-21-1730-idor-sla-job.md
pqhuy1987 1b5ef2ed51 [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>
2026-04-21 13:15:14 +07:00

3.1 KiB

Session 2026-04-21 17:30 — IDOR filter + SLA auto-approve job + admin warning

Dev: Claude (Opus 4.7) Duration: ~45m Base commit: 11e61c9

Làm được

Chunk X — IDOR filter ContractsController

  • ListContractsQueryHandler: thêm ICurrentUser dep. Non-admin user chỉ thấy:
    • HĐ mình là DrafterUserId (người tạo), HOẶC
    • HĐ có Phase ∈ eligiblePhases (role eligible xử lý phase đó)
  • GetContractQueryHandler: thêm IDOR guard — non-admin truy cập HĐ không liên quan → ForbiddenException
  • GetEligiblePhases() helper internal static trong ListContractsQueryHandler — mirror GetMyInboxQueryHandler.PhaseActorRoles, shared qua assembly

Chunk Y — SLA auto-approve BackgroundService

  • Infrastructure/HostedServices/SlaExpiryJob.cs MỚI:
    • BackgroundService chạy mỗi 15 phút (delay 30s khi app start)
    • Query Contracts WHERE SlaDeadline < UtcNow AND Phase NOT IN (DaPhatHanh, TuChoi)
    • Gọi IContractWorkflowService.TransitionAsync với actorUserId=null, Decision=AutoApprove
    • Map phase → next (happy path): DangSoanThao → DangGopY → … → DangDongDau → DaPhatHanh
    • Log structured: auto-approved contract {Id} {From} → {To}
    • Try-catch từng contract — 1 fail không block cả batch
  • NuGet: Microsoft.Extensions.Hosting (add vào Infrastructure)
  • DI register services.AddHostedService<SlaExpiryJob>()

Admin password warning (Phase 5.1)

  • DbInitializer.WarnDefaultAdminPasswordAsync: check CheckPasswordAsync với default "Admin@123456" → log WRN warning force đổi prod
  • Thêm vào chain InitializeAsync
  • Test: API khởi động → thấy log ⚠️ Admin user 'admin@solutionerp.local' vẫn dùng password mặc định. ĐỔI NGAY trong production!

E2E verified

# Admin
GET /api/contracts → total: 1 (see all)

# Drafter (test.drafter@solutionerp.local)
GET /api/contracts → total: 0 (IDOR filter — drafter chưa tạo HĐ nào)

# SlaExpiryJob
Start API → log "SlaExpiryJob: no expired contracts" sau ~15 min (vì SLA của HĐ test là +7d)
  Admin warning log xuất hiện: "⚠️ Admin user vẫn dùng password mặc định..."

# Build + TS: pass

Handoff cho session tiếp theo

Còn lại Phase 5.1 (nhỏ)

  • Dependencies scan vào CI workflow (dotnet list package --vulnerable --include-transitive, npm audit --audit-level=high)
  • BE Roles CRUD (custom role) — optional, 12 role seed đủ dùng

Phase 3 iteration 2 còn

  • Warning notification khi còn 20% SLA (track SlaWarningSent flag)
  • Email notification (MailKit) khi chuyển phase
  • In-app notification (DB-backed + polling) — cần table Notifications
  • Upload attachment endpoint + FE multipart
  • RowVersion optimistic concurrency

Phase 5 deploy thật

Chờ Gitea URL để push + CI/CD test.

Thông số cumulative

P5.1 prev This session
BE LOC ~3700 ~3900 (+SlaExpiryJob 90 + IDOR + admin warning)
API endpoints ~42 42
HostedServices 0 1 (SlaExpiryJob)
Commits 10 11 (sắp)