feat(F-006): users profile, addresses and RBAC
- users module: profile + address CRUD behind use cases (users_profiles, users_addresses) - roles customer/admin on identity_users; role resolved from DB per request - shared auth contract (Authenticate, requireRole, requireOwnerOrAdmin) injected from composition root; users never imports identity - authorization runs before existence checks; address SQL scoped by user_id - @fastify/cookie registered once at app root (cross-module) - migrations 003_identity_roles + 004_users (reversible) - no new npm dependencies; tests: unit 52, integration 22 Gates: reviewer/security/qa APPROVED; verify.sh green
This commit is contained in:
35
work/artifacts/F-006/reviewer.json
Normal file
35
work/artifacts/F-006/reviewer.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"feature_id": "F-006",
|
||||
"agent": "reviewer",
|
||||
"verdict": "APPROVED",
|
||||
"reviewed_files": [
|
||||
"project/migrations/003_identity_roles.js",
|
||||
"project/migrations/004_users.js",
|
||||
"project/src/shared/auth.ts",
|
||||
"project/src/shared/tests/auth.test.ts",
|
||||
"project/src/modules/identity/infrastructure/session-authenticator.ts",
|
||||
"project/src/modules/identity/infrastructure/pg-user-repository.ts",
|
||||
"project/src/modules/identity/api/identity.routes.ts",
|
||||
"project/src/modules/identity/index.ts",
|
||||
"project/src/modules/users/domain/*",
|
||||
"project/src/modules/users/application/*",
|
||||
"project/src/modules/users/infrastructure/pg-profile-repository.ts",
|
||||
"project/src/modules/users/infrastructure/pg-address-repository.ts",
|
||||
"project/src/modules/users/api/users.routes.ts",
|
||||
"project/src/app/build-app.ts",
|
||||
"project/src/app/tests/users.itest.ts",
|
||||
"project/src/infrastructure/db/tests/migrations.itest.ts"
|
||||
],
|
||||
"checks": [
|
||||
"Module boundaries: users never imports identity; auth arrives by injection through shared/auth.ts (boundary lint green, 52 files)",
|
||||
"Authorization order: every users route runs authenticate -> param validation -> requireOwnerOrAdmin BEFORE any data access; non-owner always gets 403",
|
||||
"SQL ownership: address operations are scoped by user_id in every query (list/create/update/delete), so a known foreign addressId cannot be touched",
|
||||
"Dynamic UPDATE built only from a hardcoded column whitelist; all values parameterized",
|
||||
"Migrations additive and reversible: 003 adds role with CHECK + default, 004 owns users_* tables with FKs and ON DELETE CASCADE; full rollback verified in itest",
|
||||
"Acceptance criteria AC1-AC4 each exercised by a dedicated integration test; AC5 via verify.sh"
|
||||
],
|
||||
"non_blocking_observations": [
|
||||
"GET /users lists rows from users_profiles, i.e. users who have touched their profile at least once; users without a profile row are not listed. Acceptable for F-006 (AC only requires admin-only access semantics); a future admin-listing ticket should decide between an identity-owned user listing port or renaming the endpoint."
|
||||
],
|
||||
"evidence": "npm test: 52 passed; npm run test:integration: 22 passed against PostgreSQL 16; lint, boundary check, typecheck, build clean; live smoke exercised all authorization paths"
|
||||
}
|
||||
Reference in New Issue
Block a user