Tenant Service — Security Model
Status: populated
Owner: TBD
Last updated: 2026-04-18
Companion: Service Template · 13 Security · 14 Compliance
1. RBAC matrix
| Role | Tenant lifecycle | Hierarchy | User profiles | Membership | Role assignments | Config |
|---|
SUPER_ADMIN | Full CRUD | Read + create root | Read all | Read | Read | Read + write |
TENANT_ADMIN | Read own + update profile | Full CRUD (own tenant) | Full CRUD (own) | Full CRUD (own) | Full CRUD (own) | Read + write (own) |
CLINICIAN | Read own tenant | Read | Read own | Read own | Read own | Read own |
NURSE | Read own tenant | Read | Read own | Read own | Read own | Read own |
PATIENT | — | — | Read own | Read own | — | — |
| Internal service | Read (via /internal/*) | Ancestors query | — | — | — | — |
2. ABAC (S2+)
ABAC policies (S2 / M2) extend RBAC with attribute conditions:
| Policy type | Example |
|---|
| Node-scoped resource access | CLINICIAN may read patient_chart WHERE node_id IN user_membership_nodes |
| Specialty-gated action | prescribe_medication REQUIRES clinician.specialty IN ['GP','Internal Medicine'] |
| Time-bounded access | emergency_access ALLOWED DURING shift.active_window |
Evaluate endpoint: POST /api/v1/tenants/:id/access/evaluate — returns { decision, reasons }.
3. Encryption classes
| Data | Classification | At-rest encryption | In-transit |
|---|
| Tenant legal/commercial data | Confidential | PostgreSQL TDE (AWS RDS AES-256) | TLS 1.3 |
| Tenant configuration (branding, MFA policy) | Internal | PostgreSQL TDE | TLS 1.3 |
| User profiles (name, specialty) | Confidential | PostgreSQL TDE | TLS 1.3 |
| Org membership / role data | Internal | PostgreSQL TDE | TLS 1.3 |
| Event payloads (NATS) | Internal | TLS in transit; at-rest encrypted per NATS JetStream storage | TLS 1.3 |
4. Audit events (emitted to audit-service)
| Event | Trigger |
|---|
tenant.tenant.activated.v1 | Activation (irrevocable step) |
tenant.tenant.suspended.v1 | Suspension |
tenant.tenant.terminated.v1 | Termination |
tenant.role_assignment.created.v1 | Role granted |
tenant.role_assignment.removed.v1 | Role revoked |
tenant.config.changed.v1 | Config key mutation |
tenant.org_membership.removed.v1 | Membership removed |
All audit events include actorId, tenantId, timestamp, requestId.
5. GDPR participation
| Action | Mechanism |
|---|
| User erasure | Consumes identity.user.deactivated.v1; anonymizes user_profiles (first_name, last_name → "ANONYMIZED"); removes org_memberships |
| Data export | /api/v1/tenants/:id/users/:userId/export returns user profile + membership + role data |
| Lawful basis | Healthcare treatment (Article 9 GDPR); tenant signs DPA |
6. Data residency
Tenant rows include country_code. Data residency enforcement:
- Afghan tenants (
AF): data stored in AF/UAE cluster region only.
- UAE tenants (
AE): data stored in UAE cluster region only.
- Cross-region replication prohibited for tenant PII.
- Module catalogue (no PII) replicated cross-region for availability.
7. Tenant isolation controls
| Control | Implementation |
|---|
| Row-level security | RLS on all tenant-scoped tables; SET app.tenant_id per transaction |
| Guard layer | TenantContextGuard validates JWT tid matches URL :tenantId |
| Internal API | /internal/tenant/* IP-restricted to cluster CIDR; no JWT required |
| Cross-tenant read prevention | RLS policy; test enforced by tenant-isolation.spec.ts (mandatory) |