Files
solution-erp/docs/changelog/sessions/2026-04-21-1630-phase5-1-security-users.md
pqhuy1987 11e61c9c39 [CLAUDE] Phase5.1: Security headers + account lockout + Users management
Security hardening:
- Api/Middleware/SecurityHeadersMiddleware MOI: remove server fingerprint (Server, X-Powered-By, ...), add X-Content-Type-Options:nosniff, X-Frame-Options:DENY, Referrer-Policy:strict-origin-when-cross-origin, Permissions-Policy (disable geolocation/mic/cam/payment), X-Permitted-Cross-Domain-Policies:none, CSP (default-src 'self' + img data: + style inline for Tailwind + frame-ancestors 'none'). Skip CSP tren /swagger (dung inline script).
- Program.cs wire UseMiddleware SecurityHeadersMiddleware first in pipeline
- Infrastructure/DependencyInjection Identity options:
  - Password.RequiredLength config-driven (Identity:Password:RequiredLength, default 8 dev, override 12+ prod)
  - Lockout: DefaultLockoutTimeSpan (15min), MaxFailedAccessAttempts (5), AllowedForNewUsers=true — all config-driven
- LoginCommandHandler: IsLockedOutAsync check truoc → throw voi deadline message, AccessFailedAsync khi sai password, ResetAccessFailedCountAsync khi login thanh cong

Users management:
- Application/Users/UserFeatures.cs: 8 CQRS (ListUsersQuery paging+search, GetUserQuery, CreateUserCommand + Validator, UpdateUserCommand voi self-disable protection, AssignRolesCommand voi self-demote protection (khong tu go Admin), ResetPasswordCommand (invalidate refresh token + unlock), UnlockUserCommand)
- UserDto: Id, Email, FullName, IsActive, IsLocked (computed tu LockoutEnd), CreatedAt, Roles
- Api/Controllers/UsersController: 7 endpoint (Users.Read/Create/Update policies):
  - GET / (list paged), GET /{id}, POST /, PUT /{id}, PUT /{id}/roles, POST /{id}/reset-password, POST /{id}/unlock
- using alias ValidationException = Application.Common.Exceptions.ValidationException (fix ambiguity voi FluentValidation)

Frontend fe-admin:
- types/users.ts MOI: User type + AVAILABLE_ROLES 12 role (match BE AppRoles.cs) + RoleLabel Vietnamese
- pages/system/UsersPage.tsx MOI:
  - DataTable columns: Email (mono), FullName, Roles (badge chips voi Vietnamese label), IsActive (CheckCircle/XCircle), IsLocked (KeyRound red), CreatedAt
  - Actions per row (PermissionGuard Users.Update wrap): Gan role (Shield icon → Dialog grid 12 checkbox), Reset password (KeyRound → Dialog voi warning user se bi logout), Unlock (Unlock icon, chi hien khi isLocked), Toggle active (XCircle/CheckCircle)
  - Create user dialog: email + fullName + password (min 8) + grid 12 role checkbox
- Route /system/users vao App.tsx

E2E verified:
- Security headers present tren moi response (check qua curl -I)
- POST /api/users voi roles: [Drafter] → 201 + id
- GET /api/users → paged voi 2 user (admin + new test.drafter)
- TS check fe-admin → pass
- dotnet build → 0 errors

Docs:
- docs/STATUS.md: Phase 5.1 xong, cumulative BE 3700 LOC, 42 endpoints, 17 FE pages
- docs/HANDOFF.md: phase table update row Phase 5.1, last updated timestamp
- docs/changelog/migration-todos.md: tick 6 items Phase 5.1 + 4 items remaining (IDOR, deps scan, admin warning, Roles CRUD)
- docs/changelog/sessions/2026-04-21-1630-phase5-1-security-users.md: session log

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
2026-04-21 13:06:46 +07:00

5.5 KiB

Session 2026-04-21 16:30 — Phase 5.1 Security + Users Management

Dev: Claude (Opus 4.7) Duration: ~1h Base commit: 46a2cab

Làm được

Chunk T — Phase 5.1 Security hardening

  • Api/Middleware/SecurityHeadersMiddleware.cs MỚI:

    • Remove server fingerprint: Server, X-Powered-By, X-AspNet-Version, X-AspNetMvc-Version
    • Security headers: X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, X-Permitted-Cross-Domain-Policies: none, Permissions-Policy (disable geolocation/mic/cam/payment)
    • CSP: default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self' — skip ở /swagger (dùng inline script/style)
  • Program.cs wire app.UseMiddleware<SecurityHeadersMiddleware>() đầu pipeline

  • Infrastructure/DependencyInjection.cs Identity options:

    • Password RequiredLength đọc config (Identity:Password:RequiredLength) — default 8 dev, override 12+ prod
    • Lockout: DefaultLockoutTimeSpan (default 15 phút), MaxFailedAccessAttempts (default 5), AllowedForNewUsers = true
    • Override qua appsettings.Production.json Identity section
  • LoginCommand handler update:

    • Check IsLockedOutAsync trước khi verify password → throw với deadline message
    • AccessFailedAsync khi sai password (tăng counter, auto-lock)
    • ResetAccessFailedCountAsync khi login thành công

Chunk U — BE Users management (8 features)

  • Application/Users/UserFeatures.cs:
    • UserDto (Id, Email, FullName, IsActive, IsLocked, CreatedAt, Roles)
    • ListUsersQuery với paging + search (email / fullName)
    • GetUserQuery
    • CreateUserCommand + Validator + Handler (check email unique, CreateAsync + AddToRoleAsync)
    • UpdateUserCommand (FullName + IsActive, với self-disable protection)
    • AssignRolesCommand (replace full set, diff add/remove, self-demote protection — không tự gỡ Admin)
    • ResetPasswordCommand (admin flow — Remove + Add password, invalidate refresh token, unlock)
    • UnlockUserCommand
  • Api/Controllers/UsersController.cs — 7 endpoint với policy Users.Read / Users.Create / Users.Update

Chunk V — FE admin Users page

  • types/users.ts: User type + AVAILABLE_ROLES (12 role từ BE AppRoles.cs) + RoleLabel tiếng Việt
  • pages/system/UsersPage.tsx:
    • DataTable list users với Email / Họ tên / Roles (badge chips) / Active / Locked / Created
    • Nút Gán role (icon Shield) → Dialog checkbox 12 role → PUT /roles
    • Nút Reset password (icon KeyRound) → Dialog với warning "user sẽ bị logout"
    • Nút Unlock (hiện khi isLocked=true)
    • Nút Toggle Active (XCircle / CheckCircle)
    • Dialog Thêm user mới: email / fullName / password / grid roles checkbox → POST /users
    • <PermissionGuard menuKey="Users" action="Create|Update"> wrap các button
  • Route /system/users vào App.tsx

E2E verified

# Security headers
GET /api/users (với Bearer)200
  Response headers:
    X-Content-Type-Options: nosniff ✅
    X-Frame-Options: DENY ✅
    Referrer-Policy: strict-origin-when-cross-origin ✅
    Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=() ✅
    Content-Security-Policy: default-src 'self'; ... ✅

# Users CRUD
POST /api/users {email: test.drafter@..., roles: ["Drafter"]}201 + id
GET /api/users → Paged[1 admin only initially, now 2]

# TS check fe-admin → pass

Bug gặp + fix

Bug Fix
ValidationException ambiguous FluentValidation vs Custom using ValidationException = ...Custom.ValidationException; alias
Edit tool "File not read" tiếp tục bị hit sau system-reminder Read lại → Edit — pattern đã quen

Tác động lên STATUS/HANDOFF/migration-todos/gotchas

  • migration-todos.md Phase 5.1: tick 5 items done (security headers, account lockout, password policy, — còn IDOR check + dependencies scan)
  • gotchas.md: không thêm mới (các issue lần này không có bẫy mới)
  • STATUS.md: cumulative BE LOC 3300 → 3700 (+UserFeatures + SecurityHeaders)
  • HANDOFF.md: thêm Phase 5.1 status row, FE pages 16 → 17 (+UsersPage)

Handoff cho session tiếp theo

Phase 5.1 còn

  • IDOR check ContractsController: user Drafter chỉ thấy HĐ mình tạo hoặc có role giữ phase hiện tại (cần add helper IsInvolvedInContract(userId, contractId))
  • Dependencies scan CI: dotnet list package --vulnerable --include-transitive + npm audit --audit-level=high
  • Admin mặc định: thêm warning log lần đầu start nếu admin password = Admin@123456 (force đổi)

Roles CRUD (quick win còn lại)

  • BE: CreateRoleCommand, UpdateRoleCommand (rename + description), DeleteRoleCommand (block nếu còn user/permission)
  • FE: /system/roles page

Phase 3 iter 2 (big — optional)

  • SlaExpiryJob BackgroundService
  • Email/in-app notification
  • Upload attachment endpoint + FE

Blocker

  • Gitea URL — user sẽ cấp

Thông số cumulative

Phase 5 prep Phase 5.1 Security + Users
BE LOC ~3300 ~3700 (+UserFeatures ~250 + SecurityHeaders ~40 + LoginHandler update)
DB tables 19 19
API endpoints ~35 ~42 (+7 users)
FE pages 16 17 (+UsersPage)
Middleware 2 3 (+SecurityHeaders)
Commits 9 10 (sắp)