0036: Permissions Multi-Tenancy & Scoping Strategy
Date: 2025-11-08
Status: Proposed
Context
The permissions-service uses a single, shared ReBAC engine instance (SpiceDB) for all tenants. A naive implementation where object identifiers are simple strings (e.g., document:123) would create a massive security hole, as there would be no way to distinguish document:123 for tenant-A from document:123 for tenant-B.
We need a robust strategy to enforce data isolation at the application layer, as the underlying engine is not tenant-aware by default. The permissions-service itself must act as the gatekeeper.
Decision
We will use Namespaced Object Identifiers for all resources managed by the permissions-service.
-
Format: The standard format for an object identifier will be
object_type:tenant_id/object_id. For example:document:acme-corp/report-xyz. -
API Enforcement: The
permissions-serviceAPI layer is responsible for enforcing this strategy. When a request comes in (e.g., to write a relationship), the service will: a. Extract thetenant_idfrom the caller's authenticated JWT. b. For every object identifier in the request payload, parse it and verify that itstenant_idmatches thetenant_idfrom the JWT. c. If there is any mismatch, the request will be rejected with a403 Forbiddenerror. -
System-Level Objects: For objects that are not tenant-specific (e.g., a global
role_template), a special reserved namespace likesystemwill be used. For example,role_template:system/super_admin. Only trusted internal services (identified via S2S auth) will be permitted to write relationships involving thesystemnamespace.
Consequences
Positive
- Strong Data Isolation: This application-layer enforcement provides a strong guarantee of multi-tenancy, preventing tenants from accessing or modifying each other's permissions.
- Engine Agnostic: This strategy works regardless of the underlying ReBAC engine, as the logic resides in our adapter layer.
- Clarity: The object identifiers become self-describing, making them easier to debug and audit.
Negative
- Increased Identifier Length: Object IDs are longer and more complex.
- Application Layer Responsibility: The responsibility for tenancy enforcement lies entirely within our
permissions-servicecode. Any bug in this parsing and validation logic could compromise data isolation. This is an accepted responsibility. - Client Complexity: All client services must be aware of this namespacing convention when constructing object IDs to send to the
permissions-service. This logic should be encapsulated in a shared library where possible.