Registration Service — Security Model
Status: populated
Owner: TBD
Last updated: 2026-04-17
Companion: Service Template · 13 security
1. RBAC/ABAC Matrix
| Role | Create Patient | Read Patient | Update Demographics | Vital Status | Merge/Unmerge | Break-glass Search | Portrait Write | Extension Schema Admin |
|---|
FRONT_DESK | ✓ | ✓ | ✓ | — | — | — | ✓ | — |
NURSE | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | — |
PHYSICIAN / DOCTOR | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | — |
CLINICIAN | ✓ | ✓ | ✓ | ✓ | — | ✓ | — | — |
SUPERVISOR | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — |
ADMIN / TENANT_ADMIN | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
LAB_TECH / VIEWER | — | ✓ (masked) | — | — | — | — | — | — |
patient (self) | — | Own record only | — | — | — | — | — | — |
ABAC additions:
- National identifier values are masked for roles below
FRONT_DESK / NURSE / CLINICIAN.
- Extension
x-privacyTier: restricted fields masked for VIEWER / LAB_TECH / portal roles.
- Break-glass header adds audit event regardless of role.
2. Encryption Classes
| Data | Classification | Encryption |
|---|
| Demographics (names, DOB, sex) | PHI Class II | AES-256-GCM at rest (PostgreSQL encrypted volume) |
| National ID values | PHI Class III (sensitive) | Masked in API responses; encrypted at rest |
| Portrait binary | PHI Class III | AES-256-GCM at rest; optional REGISTRATION_PORTRAIT_ENCRYPTION_KEY per-blob |
| Extension payload (sensitive tiers) | PHI Class II–III | Encrypted at rest; masked on read by role |
| NATS event payloads | PHI Class II | TLS in transit; minimal PII in events (IDs only) |
| Audit events | PHI Class II | Append-only; encrypted at rest in audit-service |
3. Audit Events
| Action | Audit code | Who triggers |
|---|
| Patient created | PATIENT_CREATED | RegisterPatientUseCase |
| Patient updated | PATIENT_UPDATED | UpdatePatientUseCase |
| Patient searched | PATIENT_SEARCH | SearchPatientsUseCase |
| Vital status recorded/corrected | PATIENT_VITAL_STATUS_UPDATED | RecordVitalStatusUseCase |
| Patient merged | PATIENT_MERGED | MergePatientUseCase |
| Patient unmerged | PATIENT_UNMERGED | UnmergePatientUseCase |
| Portrait accessed | PATIENT_PORTRAIT_ACCESSED | GetPortraitUseCase |
| CDS override | N/A (owned by orders-service) | — |
| Break-glass search | PATIENT_SEARCH + breakGlass.reason | Audit client on search use case |
All audit events include: tenantId, actorId (JWT sub), patientId, action, timestamp, detail object.
4. GDPR Participation
| Right | Implementation |
|---|
| Right to access | GET /api/v1/patients/:id returns all held data for the patient |
| Right to erasure | Logical deletion only; physical purge handled by data governance workflow (not in-service API) |
| Right to rectification | PUT /api/v1/patients/:id with version lock |
| Data minimization | Minimum-necessary patient search enforced by criteria rules |
| Consent capture | consentFlags[] per patient; PATCH /vital-status consent attestation in portrait |
5. Data Residency
| Jurisdiction | Policy |
|---|
| Afghanistan (MoPH) | All patient data must remain in-country; no cross-border replication |
| UAE | Data residency per customer agreement |
| Multi-country | Tenant-per-region deployment; no cross-tenant data leakage via RLS |
6. Module Entitlement
All routes are guarded by ModuleEntitlementGuard checking ehr.registration license key. A missing or expired license returns 403 with a stable module error code.