ADR-017: Scoped Authorization Service
Status
Accepted
Context
Admin and Organization need configurable resource/action grants, custom roles, per-member exceptions, and ownership-limited access. Both use the same grant semantics. An external authorization database is unnecessary for the current resource-type/ownership model. The previous unused shared policy packages were removed.
Decision
Implement one custom services/authorization module with shared scoped tables. A scope is platform administration or one organization. Admin and Organization retain their memberships and HTTP endpoints. Other services use curated public permission capabilities through the composition root.
Expose HasOrganizationPermission and HasAdminPermission returning (bool, error). Expose separate pure IsOrganizationResourceOwner and IsAdminResourceOwner functions using server-loaded facts. Grant access is own or any; callers select the actual relationship for record actions. Matching denies override all allows, and missing grants deny.
Keep the permission catalog in application code and seeded migrations. Users can compose custom roles and overrides from these supported permissions, but cannot create new application actions by inserting arbitrary permission names.
Owner-designated platform super admins and organization admins receive a protected bootstrap role. Ordinary memberships do not inherently confer customizable permissions. Store configurable roles and grants only in the Authorization-owned authz_* tables. The baseline no longer creates or imports the old Admin and Organization role/permission tables; this migration-history cleanup was explicitly requested by the user. Bind stored grants to membership IDs so deleted/recreated memberships cannot revive access.
Management validates actor authority and delegation for every mutation, including changes to assigned role bundles. Version checks reject lost updates. Mutations serialize within scopes and commit role/assignment changes and their audit entries atomically through the shared service in ADR-018.
The permission service evaluates grants and current membership eligibility. Resource-owning services retain ownership definitions and mandatory business rules. Contract-only business operations remain unimplemented until their own workflows are delivered.
Consequences
- Shared behavior, tests, and storage avoid duplicate admin/org permission engines.
- Explicit scope and database constraints enforce tenant/domain separation.
- Callers must use trusted identity, prove record ownership separately, handle both false results and errors, and keep lifecycle restrictions in use cases.
- The first repository reads scope snapshots without caching; very large scopes may later need targeted grant queries without changing public capabilities.
- Scope owners are deliberate protected authority; changing owner membership requires owner-service lifecycle controls.
- Doctor-affiliation persistence and lifecycle rules remain Organization-owned; Admin use cases enforce the explicit scoped permissions described in ADR-013.