6.2 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 04-rbac | 01 | rbac |
|
|
|
|
|
|
Phase 4 Plan 01: RBAC Foundation Summary
One-liner: 3-tier RBAC (platform_admin/customer_admin/customer_operator) with DB migration, FastAPI guard dependencies, HMAC invite tokens, and invite-only onboarding API.
What Was Built
Task 1: DB Migration + ORM Models + Config
Migration 006 (migrations/versions/006_rbac_roles.py) adds the RBAC schema to PostgreSQL:
- Adds
role TEXT + CHECKcolumn toportal_users, backfillsis_adminvalues, dropsis_admin - Creates
user_tenant_rolestable (user_id FK, tenant_id FK, UNIQUE constraint) - Creates
portal_invitationstable (token_hash UNIQUE, status, expires_at, all FKs)
packages/shared/shared/models/auth.py gains:
UserRolestring enum (PLATFORM_ADMIN, CUSTOMER_ADMIN, CUSTOMER_OPERATOR)UserTenantRoleORM model with CASCADE deletesPortalInvitationORM modelPortalUser.rolereplacesPortalUser.is_admin
packages/shared/shared/config.py gains: invite_secret, smtp_host, smtp_port, smtp_username, smtp_password, smtp_from_email.
Task 2: RBAC Guards + Invite Token + Email + Invitation API
packages/shared/shared/api/rbac.py — FastAPI dependency guards:
PortalCallerdataclass (user_id, role, tenant_id from request headers)get_portal_caller— parses X-Portal-User-Id/Role/Tenant-Id headers, raises 401 on bad UUIDrequire_platform_admin— raises 403 for non-platform_adminrequire_tenant_admin— platform_admin bypasses; customer_admin checked against UserTenantRole; operator always 403require_tenant_member— platform_admin bypasses; customer_admin/operator checked against UserTenantRole
packages/shared/shared/invite_token.py — HMAC token utilities:
generate_invite_token(invitation_id)— HMAC-SHA256, base64url-encoded, embeds{id}:{timestamp}validate_invite_token(token)— timing-safe compare_digest, 48h TTL check, returns invitation_idtoken_to_hash(token)— SHA-256 hex digest for DB storage
packages/shared/shared/email.py — SMTP email sender (sync, for Celery):
- Sends HTML+text multipart invite email
- Skips silently if smtp_host is empty (dev-friendly)
packages/shared/shared/api/invitations.py — Invitation CRUD router:
POST /api/portal/invitations— create invitation (requires tenant admin), returns raw tokenPOST /api/portal/invitations/accept— validate token, create PortalUser + UserTenantRole, mark acceptedPOST /api/portal/invitations/{id}/resend— regenerate token, extend expiryGET /api/portal/invitations— list pending invitations for caller's tenant
packages/shared/shared/api/portal.py — auth/verify updated:
AuthVerifyResponsenow returnsrole,tenant_ids,active_tenant_id(replacedis_admin)- platform_admin returns all tenant IDs; customer roles return their UserTenantRole tenant IDs
/auth/registergated behindrequire_platform_adminwith deprecation comment
packages/orchestrator/orchestrator/tasks.py — added send_invite_email_task Celery task.
packages/gateway/gateway/main.py — invitations_router mounted.
Task 3: Unit Tests (27 passing)
tests/unit/test_rbac_guards.py(11 tests): RBAC guard pass/reject scenarios, platform_admin bypasstests/unit/test_invitations.py(11 tests): HMAC token roundtrip, tamper/expiry, invitation CRUDtests/unit/test_portal_auth.py(7 tests): auth/verify returns role+tenant_ids+active_tenant_id
Deviations from Plan
None — plan executed exactly as written.
Commits
| Hash | Description |
|---|---|
f710c9c |
feat(04-rbac-01): DB migration 006 + RBAC ORM models + config fields |
d59f85c |
feat(04-rbac-01): RBAC guards + invite token + email + invitation API |
7b0594e |
test(04-rbac-01): unit tests for RBAC guards, invitation system, portal auth |
Self-Check: PASSED
All files created and verified before this summary was written.