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
203 lines
9.5 KiB
Markdown
203 lines
9.5 KiB
Markdown
# HANDOFF — Brief 5 phút cho session tiếp theo
|
||
|
||
**Last updated:** 2026-04-21 16:30 (cuối Phase 5.1 Security + Users Mgmt)
|
||
|
||
## Ở đâu rồi?
|
||
|
||
| Phase | Trạng thái |
|
||
|---|---|
|
||
| 0 Draft | ✅ Done |
|
||
| 1 Alpha Core foundation | ✅ Done |
|
||
| 1 Alpha Core đợt 2 (CRUD + Permission) | ✅ Done |
|
||
| 2 Form Engine MVP | ✅ Done |
|
||
| 2 Form Engine iteration 2 | 📝 Optional |
|
||
| 3 Workflow MVP (9 phase + code gen) | ✅ Done |
|
||
| 3 Workflow iteration 2 (SLA + notify + attachment) | 📝 Optional |
|
||
| 4 Report MVP (Dashboard + Excel) | ✅ Done |
|
||
| 4 Report iteration 2 | 📝 Optional |
|
||
| 5 Prep (infra + scripts + guides + refresh token) | ✅ Done |
|
||
| **5.1 Security + Users Mgmt (headers, lockout, Users CRUD)** | ✅ Done (IDOR + deps scan còn) |
|
||
| 5 Deploy production (cần Gitea URL) | 📋 Next |
|
||
|
||
## Run nhanh
|
||
|
||
```powershell
|
||
# Terminal 1 — API (auto seed 8 template lần đầu)
|
||
dotnet run --project src\Backend\SolutionErp.Api
|
||
|
||
# Terminal 2 — Admin FE
|
||
cd fe-admin && npm run dev # → http://localhost:8082
|
||
|
||
# Terminal 3 — User FE
|
||
cd fe-user && npm run dev # → http://localhost:8080
|
||
```
|
||
|
||
Login: `admin@solutionerp.local` / `Admin@123456`
|
||
|
||
Điểm cần test ngay (Phase 4 MVP):
|
||
- **Admin `/dashboard`** → 5 KPI card + By Phase bar + Monthly chart + Top NCC/dự án
|
||
- **Admin `/reports`** → filter phase/date → Export Excel .xlsx 10 cột có formula SUM
|
||
- **fe-user `/contracts/new`** → tạo HĐ draft (Phase 2 DangSoanThao, SLA +7d)
|
||
- **fe-user `/inbox`** → xem HĐ chờ role mình xử lý
|
||
- **`/contracts/{id}`** → click "Duyệt → tiếp" chạy state machine. Phase 8 gen `MaHopDong` RG-001
|
||
- **`/forms`** → render template .docx
|
||
- **`/system/permissions`** → ma trận Role × MenuKey
|
||
- **`/master/suppliers|projects|departments`** → CRUD
|
||
|
||
## Cần làm kế tiếp (ưu tiên)
|
||
|
||
### A. Phase 5 — Production (tuần 12-13, item lớn nhất còn lại)
|
||
|
||
**Đọc trước:** `docs/changelog/migration-todos.md` section Phase 5.
|
||
|
||
- [ ] `.gitea/workflows/deploy.yml` CI/CD build + deploy IIS
|
||
- [ ] `scripts/deploy-iis.ps1` stop app pool → xcopy → start
|
||
- [ ] Windows Server IIS + URL Rewrite + ARR (reverse proxy FE → .NET)
|
||
- [ ] HTTPS cert via win-acme hoặc mua
|
||
- [ ] `appsettings.Production.json` + user secrets + JWT secret rotation
|
||
- [ ] Rate limiting middleware (auth endpoint 5 req/min/IP)
|
||
- [ ] Security audit OWASP top 10
|
||
- [ ] Health check endpoint `/health`
|
||
- [ ] Serilog → file rolling daily retention 30d
|
||
- [ ] SQL backup: daily full + 15min log
|
||
- [ ] Runbook: restart, rollback migration, restore backup
|
||
- [ ] UAT production 1 tuần với 2-3 user thật
|
||
- [ ] Go-live checklist
|
||
|
||
### B. Polish iterations (optional — khi rảnh)
|
||
|
||
**Phase 2 iter 2:** convert 3 .doc qua Word COM `DisplayAlerts=0` hoặc LibreOffice, field spec JSON + form builder FE dynamic, `{{#loop}}` block support, PDF convert, FE upload template multipart.
|
||
|
||
**Phase 3 iter 2:** `SlaExpiryJob` BackgroundService auto-approve, email (MailKit) + in-app (SignalR) notify, upload attachment endpoint + FE multipart (`wwwroot/uploads/contracts/{id}/`), RowVersion concurrency, render HĐ docx khi tạo (merge TemplateId + DraftData + ContractClause).
|
||
|
||
**Phase 4 iter 2:** SLA overdue report by role/phase, PDF HĐ export (LibreOffice), dashboard user-specific (role tôi).
|
||
|
||
### C. Phase 2 iteration 2 (form engine polish)
|
||
|
||
- Convert 3 file `.doc` qua Word COM `DisplayAlerts=0` + timeout, hoặc LibreOffice
|
||
- Field spec JSON per template + dynamic form builder FE
|
||
- `{{#loop}}...{{/loop}}` block support cho table lặp
|
||
- PDF convert via LibreOffice headless
|
||
- Admin upload template UI (multipart)
|
||
|
||
### D. Quick wins (không block phase)
|
||
|
||
- FE Users management + Roles CRUD (test permission với non-admin user thật)
|
||
- Filter Inbox theo phase FE
|
||
- Refresh token auto (FE axios interceptor retry 401)
|
||
|
||
## Lưu ý kỹ thuật quan trọng
|
||
|
||
**Đọc [`gotchas.md`](gotchas.md) trước khi:**
|
||
- Thêm package mới → check compat với .NET 10 (MediatR 14 fail → dùng 12)
|
||
- Debug 404 API → kiểm Program.cs có persist không (Dropbox issue)
|
||
- Expression tree error → tách switch ra ngoài LINQ
|
||
- TS enum error → dùng const-object pattern (`erasableSyntaxOnly`)
|
||
- Word COM stuck → kill + fallback LibreOffice
|
||
- Migration lỗi → check 3 file đầy đủ (Designer + Migration + Snapshot)
|
||
|
||
## File đang active
|
||
|
||
```
|
||
SOLUTION_ERP/
|
||
├── src/Backend/ (Clean Arch, 4 project, .NET 10)
|
||
│ ├── SolutionErp.Domain/
|
||
│ │ ├── Common/ BaseEntity, AuditableEntity
|
||
│ │ ├── Contracts/ ContractType, ContractPhase, ApprovalDecision + **Contract, ContractApproval, ContractComment, ContractAttachment, ContractCodeSequence** ← Phase 3
|
||
│ │ ├── Forms/ ContractTemplate, ContractClause ← Phase 2
|
||
│ │ ├── Identity/ User, Role, MenuItem, Permission, AppRoles, MenuKeys
|
||
│ │ └── Master/ Supplier, Project, Department, SupplierType
|
||
│ ├── SolutionErp.Application/
|
||
│ │ ├── Auth/ Login, Refresh, Me
|
||
│ │ ├── Common/ Exceptions, Behaviors, Interfaces, Models
|
||
│ │ ├── Contracts/ ContractFeatures (8 CQRS), IContractWorkflowService, IContractCodeGenerator, DTOs ← Phase 3
|
||
│ │ ├── Forms/ FormFeatures (List/Get/Render) ← Phase 2
|
||
│ │ ├── Master/ Suppliers, Projects, Departments CQRS
|
||
│ │ ├── Permissions/ GetMyMenuTree, matrix upsert
|
||
│ │ └── Reports/ **DashboardStats, ExportContractsToExcel, IContractExcelExporter** ← Phase 4
|
||
│ ├── SolutionErp.Infrastructure/
|
||
│ │ ├── Forms/ DocxRenderer, XlsxRenderer, FormRenderer ← Phase 2
|
||
│ │ ├── Identity/ JwtSettings, JwtTokenService
|
||
│ │ ├── Persistence/ DbContext, DbInitializer, Interceptors, Migrations (**5**)
|
||
│ │ ├── Reports/ **ContractExcelExporter** ← Phase 4
|
||
│ │ └── Services/ DateTimeService, ContractWorkflowService, ContractCodeGenerator ← Phase 3
|
||
│ └── SolutionErp.Api/
|
||
│ ├── Authorization/ MenuPermissionHandler + Requirement
|
||
│ ├── Controllers/ Auth, Suppliers, Projects, Departments, Menus, Roles, Permissions, Forms, Contracts, **Reports** (10 controller)
|
||
│ ├── Middleware/ GlobalExceptionMiddleware
|
||
│ ├── Services/ CurrentUserService, WebHostEnvironmentLocator
|
||
│ └── wwwroot/templates/ 5 file .docx/.xlsx ← Phase 2
|
||
├── fe-admin/ (11 page)
|
||
│ └── src/pages/
|
||
│ ├── LoginPage
|
||
│ ├── DashboardPage ← Phase 4 rewrite (KPI cards + BarChart)
|
||
│ ├── master/SuppliersPage, ProjectsPage, DepartmentsPage
|
||
│ ├── system/PermissionsPage
|
||
│ ├── forms/FormsPage ← Phase 2
|
||
│ ├── contracts/ContractsListPage, ContractDetailPage ← Phase 3
|
||
│ └── ReportsPage ← Phase 4
|
||
├── fe-user/ (5 page)
|
||
│ └── src/pages/
|
||
│ ├── LoginPage
|
||
│ ├── InboxPage ← Phase 3
|
||
│ └── contracts/ContractCreatePage, ContractDetailPage, MyContractsPage ← Phase 3
|
||
├── docs/ (35 file)
|
||
│ ├── STATUS.md, HANDOFF.md, rules.md, architecture.md
|
||
│ ├── CLAUDE.md, PROJECT-MAP.md
|
||
│ ├── workflow-contract.md, forms-spec.md
|
||
│ ├── database/{database-guide, schema-diagram}.md
|
||
│ ├── flows/ (7 file — README + 6 flow)
|
||
│ ├── guides/ (4 file) — deployment-iis, cicd, security-checklist, runbook ← Phase 5 prep
|
||
│ ├── changelog/migration-todos.md + sessions/ (7 session log)
|
||
│ └── gotchas.md
|
||
├── scripts/ (5 file PS + py)
|
||
│ ├── parse_forms.py, parse_workflow.py (Phase 0)
|
||
│ ├── convert-doc-to-docx.ps1 (Phase 2)
|
||
│ └── deploy-iis.ps1, backup-sql.ps1 ← Phase 5 prep
|
||
├── .gitea/workflows/deploy.yml ← Phase 5 prep CI/CD template
|
||
└── .claude/skills/ (3 skill — all full spec)
|
||
```
|
||
|
||
## Git state
|
||
|
||
```
|
||
(sẽ là commit 8) — Phase 5 Prep (infra + scripts + guides + refresh token)
|
||
fe7ad8e — Phase 4 Report MVP + docs consolidation
|
||
7e957a7 — Phase 3 Workflow MVP
|
||
5113e4c — Phase 2 Form Engine MVP
|
||
54d6c9b — Phase 1.2 CRUD + Permission
|
||
49a5f57 — Docs database-guide + flows
|
||
702411f — Phase 1 foundation
|
||
25dad7f — Phase 0 scaffold
|
||
|
||
Branch: main
|
||
Remote: chưa (Gitea URL CẦN NGAY để Phase 5 go-live)
|
||
```
|
||
|
||
## Credentials + URLs
|
||
|
||
```
|
||
admin@solutionerp.local / Admin@123456
|
||
```
|
||
|
||
- API: http://localhost:5443 (swagger `/swagger`)
|
||
- Admin FE: http://localhost:8082
|
||
- User FE: http://localhost:8080
|
||
- SQL LocalDB: `(localdb)\MSSQLLocalDB` / Database=`SolutionErp_Dev`
|
||
|
||
## Đánh giá nhanh
|
||
|
||
**Tốt:**
|
||
- Build pass 100% cả BE + FE
|
||
- E2E test full 9-phase workflow end-to-end — mã HĐ gen đúng format RG-001
|
||
- Docs đầy đủ: 26 file, session log mỗi chunk, gotchas tích lũy 17 pitfall
|
||
- Cả 2 FE đều có Contract detail page + timeline + comment thread + state machine action
|
||
|
||
**Rủi ro còn:**
|
||
- SLA chỉ set deadline — không có job auto-approve (Phase 3.2)
|
||
- Không có notification (email + in-app) — user phải F5 inbox
|
||
- Form render chỉ MVP — loop table + PDF chưa có
|
||
- Permission matrix chưa test thực với non-admin user
|
||
- 3 file .doc chưa convert (carryover Phase 2)
|
||
- Không có upload attachment endpoint (chỉ có entity + DTO)
|