RBAC Model
Grant uses multi-tenant RBAC: roles, groups, and permissions can be combined flexibly. The classic path is User → Role → Group → Permission → Resource, but Grant also supports direct attachments — groups and permissions on users, and permissions on roles — and unions all sources when checking access.
Overview
Account-level: Personal Account Owner, Organization Account Owner (full control over that account).
Organization-level: Organization Owner, Admin, Dev, Viewer — full control down to read-only within the organization.
Scope of this doc
This page covers authorization (which actions need which permissions). These need only authentication (logged in), not authorization: login/register, session management, password reset, email verification, profile picture upload, session revocation, auth method management (create/delete/set-primary/change-password).
Resources and Actions
Actions that require authorization: read (single resource by ID), query (list/collection). Some resources expose only query (e.g. Project, Role, Tag, API Key); others support both read and query.
Which resources have read vs query only
- Both read + query: User, Account, Organization, Organization Member, Organization Invitation, Project User, User Session, User Authentication Method
- Query only: Project, Project App, Resource, Role, Group, Permission, Tag, API Key (list with optional
idsfilter)
Core Resources
| Resource | Actions | Description |
|---|---|---|
| User | create read update delete query export-data | Platform users |
| Account | read delete query | Personal or organization workspace |
| Organization | create read update delete query upload-picture | Container for projects and members |
| Project | create update delete query | Isolated environment (query only) |
| Resource | create update delete query | Domain entities (query only) |
| Role | create update delete query | Named permission sets (query only) |
| Group | create update delete query | Role–resource permission bundles |
| Permission | create update delete query | Action on a resource |
| Tag | create update delete query | Labels (query only) |
| API Key | create delete query revoke exchange | Programmatic credentials |
| App | create update query delete | Consent-flow applications |
Relationship & Session Resources
| Resource | Actions | Description |
|---|---|---|
| Organization Member | read update remove query | Users in an organization |
| Organization Invitation | create read query revoke resend-email renew | Join-org invitations |
| Project User | read query set-email set-password export-credentials | Users in a project |
| User Session | read query | Active sessions (own only) |
| User Authentication Method | read query | Auth methods (own only) |
Roles
Account-Level
Two account types: personal (one per user) and organization (owned by users). Each has a single role: Personal Account Owner or Organization Account Owner — full control over that account. Account ownership is independent of organization membership (you can be account owner and also Org Admin/Dev/Viewer elsewhere).
Organization-Level
Within an organization, four roles: Owner (full control), Admin (teams and permissions, no account ownership), Dev (create/manage resources, roles, permissions), Viewer (read-only).
Project-Level: Two Kinds of Users
- Platform users — People with a Grant account (personal or org member). They use the dashboard to design auth. Their project access is inherited: account owner → Owner on account projects; org role (Owner/Admin/Dev/Viewer) applies to org projects. The platform does not define project-level groups for them.
- Project users — Third-party identities in a project (Project User resource). They use the project's APIs/apps (API keys, OAuth), not the dashboard. Platform users define custom roles, groups, and permissions inside the project for these users; the platform supplies the primitives but not a fixed structure.
Groups
Groups bundle permissions (often named {Resource} {Role}, e.g. "Organization Owner", "User Dev"). Roles typically receive groups via role_groups, but that is not the only path: users may be assigned groups directly via user_groups (and project_user_groups in project scope), and both users and roles may receive permissions directly via user_permissions / role_permissions (plus the project-scoped variants).
Permission sources (unioned at check time):
User → Role → Group → Permission → Resource
User → Group → Permission → Resource
User → Role → Permission → Resource
User → Permission → ResourceFor CDM import/export field mapping, see CDM Import & Export → Permission assignment paths.
Standard account- and organization-level groups (reference)
Account-level: Only Owner groups — Personal Account Owner, Organization Account Owner (Role: Owner, Resource: Account). Organization-level: All four — Organization Owner, Organization Admin, Organization Dev, Organization Viewer. Project-level: No standard groups; platform users' project access is inherited from org role; custom project groups are user-defined.
Permission Mapping to Groups
Common groups — assigned to all roles (basic read/query, some create). Role-specific groups — extra permissions for Owner, Admin, Dev, or Viewer. Summary: Tags are Owner-only; API Key delete/revoke for Dev is own-keys only (condition). Expand below for full reference.
Full group → permission reference
Common Groups (all roles)
| Group Name | Resource | Permissions | Description |
|---|---|---|---|
Account Common | Account | read query | Basic account access for all authenticated users |
User Common | User | read query | Basic user viewing for all authenticated users |
Organization Common | Organization | create query | Basic organization access (can create and query organizations) |
Project Common | Project | query | Basic project viewing for all authenticated users |
Account Project Owner | Project | create update delete set-exportable-api-key-secrets | Project management for account owners |
Resource Common | Resource | query | Basic resource viewing for all authenticated users |
Role Common | Role | query | Basic role viewing for all authenticated users |
Group Common | Group | query | Basic group viewing for all authenticated users |
Permission Common | Permission | query | Basic permission viewing for all authenticated users |
Tag Common | Tag | query | Basic tag viewing for all authenticated users |
API Key Common | API Key | query exchange | Basic API key access for all authenticated users |
Organization Member Common | Organization Member | read query | Basic organization member viewing for all authenticated users |
Organization Invitation Common | Organization Invitation | query | Basic organization invitation access for all authenticated users |
Project User Common | Project User | read query | Basic project user viewing for all authenticated users |
User Session Common | User Session | read query | Basic user session viewing (own sessions only, enforced by condition) |
User Authentication Method Common | User Authentication Method | read query | Basic authentication method viewing (own methods only, enforced by condition) |
Role-Specific Groups
These groups contain permissions specific to certain roles:
Account Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Personal Account Owner | Account | delete | Personal Account Owner | Full personal account control |
Organization Account Owner | Account | delete | Organization Account Owner | Full organization account control |
User Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
User Owner | User | create update delete export-data | Organization Owner | Full user management |
User Admin | User | create update delete export-data | Organization Admin | Full user management |
User Dev | User | update (own user only) | Organization Dev | Limited user management (own profile only) |
User Viewer | User | (no additional permissions) | — | Viewer has same user access as common groups |
Organization Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Organization Owner | Organization | update delete | Organization Owner | Full organization management |
Organization Admin | Organization | update delete | Organization Admin | Full organization management |
Organization Dev | Organization | (no additional permissions) | — | Dev has same organization access as common groups |
Organization Viewer | Organization | (no additional permissions) | — | Viewer has same organization access as common groups |
Project Resource Groups
These groups control what organization-level roles can do with the Project resource (create, update, delete projects). They are not project-scope groups — see Project-Level: Two Kinds of Users.
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Project Owner | Project | create update delete set-exportable-api-key-secrets | Organization Owner | Full project management |
Project Admin | Project | create update delete set-exportable-api-key-secrets | Organization Admin | Full project management |
Project Dev | Project | create update delete | Organization Dev | Full project management |
Project Viewer | Project | (no additional permissions) | — | Viewer has same project access as common groups |
Resource Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Resource Owner | Resource | create update delete | Organization Owner | Full resource management |
Resource Admin | Resource | create update delete | Organization Admin | Full resource management |
Resource Dev | Resource | create update delete | Organization Dev | Full resource management |
Resource Viewer | Resource | (no additional permissions) | — | Viewer has same resource access as common groups |
Role Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Role Owner | Role | create update delete | Organization Owner | Full role management |
Role Admin | Role | create update delete | Organization Admin | Full role management |
Role Dev | Role | create update delete | Organization Dev | Full role management |
Role Viewer | Role | (no additional permissions) | — | Viewer has same role access as common groups |
Group Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Group Owner | Group | create update delete | Organization Owner | Full group management |
Group Admin | Group | create update delete | Organization Admin | Full group management |
Group Dev | Group | create update delete | Organization Dev | Full group management |
Group Viewer | Group | (no additional permissions) | — | Viewer has same group access as common groups |
Permission Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Permission Owner | Permission | create update delete | Organization Owner | Full permission management |
Permission Admin | Permission | create update delete | Organization Admin | Full permission management |
Permission Dev | Permission | create update delete | Organization Dev | Full permission management |
Permission Viewer | Permission | (no additional permissions) | — | Viewer has same permission access as common groups |
Tag Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Tag Owner | Tag | create update delete | Organization Owner | Full tag management (Owner only) |
Tag Admin | Tag | (no additional permissions) | — | Admin cannot manage tags |
Tag Dev | Tag | (no additional permissions) | — | Dev has same tag access as common groups |
Tag Viewer | Tag | (no additional permissions) | — | Viewer has same tag access as common groups |
Note: Only Organization Owner can manage tags. Admin, Dev, and Viewer roles have read-only access via common groups.
API Key Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
API Key Owner | API Key | create update delete revoke | Organization Owner | Full API key management |
API Key Admin | API Key | create update delete revoke | Organization Admin | Full API key management |
API Key Dev | API Key | create update delete* revoke* | Organization Dev | API key creation; delete/revoke own keys only |
API Key Viewer | API Key | (no additional permissions) | — | Viewer has same API key access as common groups |
INFO
Dev can only delete/revoke their own API keys (enforced by condition) *
ApiKey:Update is a permission of the ApiKey resource and of the three groups above (the account-project group already listed it). It gates setApiKeyPermissions and PUT /api/api-keys/{id}/permissions, which replace the permissions a project key carries (see API keys).
API keys are attenuated
A project API key (an accountProject or organizationProject scope) never carries more than its creator can do in the key's project scope. When a key is created, everything its role resolves to there (the role's groups and the role's direct permissions for that project) must be among the permissions its creator holds in the same scope. Equality is allowed: an Owner may create a key with the Owner role. The creator's permissions are collected the way a request is authorized (GrantService.collectPermissionIds) with the creator's own token type, so a key that creates a key is bound by its own permissions.
The same check decides who may rotate, revoke or delete a key: the caller must cover the key's permissions. A key with neither a role nor permissions is not manageable by anyone. The rule is the same for account and organization projects. A role key carries only a standard role (a standard organization role for an organization project, a standard account role for an account project); any other role is refused. Permissions are compared by row, so a row that carries a condition is covered only by the same row.
A machine-only permission may be carried by a key whose creator does not hold it, when the creator holds the permission its template names as the gate (attachableByHoldersOf). ProjectUser:ExportCredentials is the first: its gate is ProjectUser:SetPassword, held by the Owner, the Admin and the account owners and not by the Dev or the Viewer. The flag describes who may attach the permission to a key; it does not change who is authorized to use it.
A project key carries either a standard role or a set of permissions of its own (CreateApiKeyInput.permissionIds). A permission key is authorized, in its own project scope, by exactly those permissions: GrantService.collectPermissionIds adds the key's rows (read through the key's link to the project, so they resolve nowhere else) for an API-key token in an accountProject or organizationProject scope, and nothing from a role. Editing a key's permissions applies the same rule to the change: the editor must cover the key as it is, every permission the edit adds must be among the editor's own (a machine-only one through its gate), and removing is always allowed. Converting a role key to permissions is bounded by the editor's own permissions, not by the old role's. apiKeyAttachableGrants returns what the caller may attach. An edit drops the key's cached authorization results, so it applies to the key's next request, including through a token issued before the edit.
Member role changes and invitations are not API keys and keep the role-level comparison.
Project App Groups
Project App (Apps) are OAuth/consent applications scoped to a project. Account owners get full app management via Account Project App Owner; organization roles get app management via Project App Owner/Admin/Dev. There is no Project App Common group — only these role-specific groups grant access.
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Account Project App Owner | Project App | create update delete query | Personal/Organization Account Owner | Full app management for account projects |
Project App Owner | Project App | create update delete query | Organization Owner | Full project app management |
Project App Admin | Project App | create update delete query | Organization Admin | Full project app management |
Project App Dev | Project App | create update delete query | Organization Dev | Full project app management |
Project App Viewer | Project App | (no additional permissions) | — | Viewer has no project app access by default |
Organization Member Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Organization Member Owner | Organization Member | update remove | Organization Owner | Full organization member management |
Organization Member Admin | Organization Member | update remove | Organization Admin | Full organization member management |
Organization Member Dev | Organization Member | (no additional permissions) | — | Dev has same organization member access as common groups |
Organization Member Viewer | Organization Member | (no additional permissions) | — | Viewer has same organization member access as common groups |
Organization Invitation Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Organization Invitation Owner | Organization Invitation | create revoke resend-email renew | Organization Owner | Full organization invitation management |
Organization Invitation Admin | Organization Invitation | create revoke resend-email renew | Organization Admin | Full organization invitation management |
Organization Invitation Dev | Organization Invitation | (no additional permissions) | — | Dev has same organization invitation access as common groups |
Organization Invitation Viewer | Organization Invitation | (no additional permissions) | — | Viewer has same organization invitation access as common groups |
Project User Groups
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
Project User Owner | Project User | set-email set-password | Organization Owner, Personal Account Owner, Organization Account Owner | Set a project user's Email authentication method and password |
Project User Admin | Project User | set-email set-password | Organization Admin | Set a project user's Email authentication method and password |
Project User Dev | Project User | (no additional permissions) | — | Dev has same project user access as common groups |
Project User Viewer | Project User | (no additional permissions) | — | Viewer has same project user access as common groups |
ProjectUser:SetEmail stores the address unverified (isVerified: false), whether it adds the Email method or replaces the current one: an operator can type an address, not prove it belongs to the user. Only the platform (dashboard) sign-in enforces the flag. There the user can sign in with the address, but is held to the verification window and blocked from organization-context mutations until the address is verified. Project app sign-in and the project password-reset flow do not read it, so an operator-set address works there at once, and completing a project password reset (or an e-mail magic link) marks the method verified. The user clears the state with the dashboard's resend-verification action, which mails a code to the address; whoever cannot read that mailbox cannot clear it. The operation is recorded on the authentication-method audit log with metadata.operation set to operator-set-email (or operator-create-user when createUser supplied the address), and is rate-limited per operator and scope (SECURITY_RATE_LIMIT_SET_EMAIL_*, see Security).
ProjectUser:SetPassword is an ordinary extensible action: it is held by Owner and Admin by default, and an organization can add it to any other role's group exactly like SetEmail, with no code change.
Project:SetExportableApiKeySecrets follows the same precedent. It is the per-project opt-in for exportable API-key secrets (setProjectExportableApiKeySecrets, PUT /api/projects/{id}/exportable-api-key-secrets), and it is held by Owner and Admin only: the Project Owner and Project Admin groups (Organization Owner, Organization Admin) and the Account Project Owner group (Personal Account Owner, Organization Account Owner). The Project Dev group does not hold it, although it holds Project:Update, because the flag decides whether Grant keeps a credential in a reversible form. Like Project:Update, it applies to the project in the authorized project scope. An organization can add it to any other role's group, with no code change.
ProjectUser:ExportCredentials has no default holder. It reads project users' password hashes, so it belongs to no standard group and is attached deliberately to a mirror's API key as a machine-only permission (by a holder of ProjectUser:SetPassword, see API keys are attenuated), or granted to a group. It is seeded as a standalone permission (a permission row linked to no group), so the groups UI and API can grant it.
It gates two things. The exportProjectUserCredentials GraphQL query and POST /api/users/credentials/export return, for the requested project members, { algorithm: 'bcrypt', hash } (kind PASSWORD; the one place a project password hash leaves Grant) and the plaintext secrets of their API keys (kind API_KEY_SECRET; the one place an API-key secret can be read back, and only for keys of a project that opted in to exportable secrets, see ADR 0013). Both kinds sit behind this one permission, because a later credential type is a new kind and not a second export. Every successful call writes one export row on the project credential audit log (the caller, the number of users and credentials, the kinds; never a hash or a secret) and one export row per exported API-key secret on the API-key audit log (the key; never the secret), and a request naming a user who is not a member of the project fails as a whole. The User.passwordCredential field, resolved in the request's project scope (source, updatedAt and a SHA-256 fingerprint of the stored hash, a change detector rather than a secret), resolves to null, not an error, for any caller without the permission. ProjectUser:ExportCredentials also gates User.apiKeyCredentials, resolved the same way in the request's project scope: per project-user API key, its fingerprint (the SHA-256 of the secret string as issued, null for a key created before it existed) and whether an encrypted exportable copy exists, never a secret or a hash.
New standard permissions such as ProjectUser:SetEmail, ProjectUser:SetPassword and Project:SetExportableApiKeySecrets reach organizations bootstrapped earlier through seedAll (instance-wide, idempotent). bootstrapDatabase() runs that seed when DB_BOOTSTRAP_ON_BOOT is true (ADR 0001, the default); serverless deployments must run the migrate/seed step together with the flag.
User Session Groups
All roles have only common group permissions for User Session (read, query own sessions):
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
User Session Owner | User Session | (no additional permissions) | — | Owner has same user session access as common groups (own sessions) |
User Session Admin | User Session | (no additional permissions) | — | Admin has same user session access as common groups (own sessions) |
User Session Dev | User Session | (no additional permissions) | — | Dev has same user session access as common groups (own sessions) |
User Session Viewer | User Session | (no additional permissions) | — | Viewer has same user session access as common groups (own sessions) |
User Authentication Method Groups
All roles have only common group permissions for User Authentication Method (read, query own methods):
| Group Name | Resource | Permissions | Assigned To | Description |
|---|---|---|---|---|
User Authentication Method Owner | User Authentication Method | (no additional permissions) | — | Owner has same auth method access as common groups (own methods) |
User Authentication Method Admin | User Authentication Method | (no additional permissions) | — | Admin has same auth method access as common groups (own methods) |
User Authentication Method Dev | User Authentication Method | (no additional permissions) | — | Dev has same auth method access as common groups (own methods) |
User Authentication Method Viewer | User Authentication Method | (no additional permissions) | — | Viewer has same auth method access as common groups (own methods) |
The dedicated User Authentication Method resource is own-methods-only. Nested User.authenticationMethods on GraphQL users and REST GET /users is different: those lists authorize User:Query only, with no extra UserAuthenticationMethod:Read check. This is User:Query, not ProjectUserCommon. An OrganizationProject or AccountProject API key whose role includes User:Query (Owner and Viewer both have it via User Common) can therefore request authenticationMethods { provider providerId isVerified } for another member and receive the Email mailbox, including an operator-set unverified address. A ProjectUser-tenant key sees that nested field only when the bound user holds User:Query and the list is requested on a users-list tenant (projectUser itself is not one).
:::
Permission Mapping to Roles
Action × role matrices: Owner/Admin/Dev can do most writes; Viewer is read-only. Exceptions: Tags — Owner only; User update and API Key delete/revoke — Dev only for self (condition).
Full permission matrix (all resources)
Core Resource Permissions
User Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | user | ||||
read | user | ||||
update | user | * | |||
delete | user | ||||
query | user | ||||
export-data | user |
INFO
Dev can only update their own user record *
Account Permissions
| Action | Resource | Personal Account Owner | Organization Account Owner |
|---|---|---|---|
read | account | ||
delete | account | ||
query | account |
Organization Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | organization | ||||
update | organization | ||||
delete | organization | ||||
query | organization |
INFO
Organization read is enforced by scope (users can only read organizations they belong to).
Project Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | project | ||||
update | project | ||||
delete | project | ||||
query | project |
Resource Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | resource | ||||
update | resource | ||||
delete | resource | ||||
query | resource |
Role Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | role | ||||
update | role | ||||
delete | role | ||||
query | role |
Group Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | group | ||||
update | group | ||||
delete | group | ||||
query | group |
Permission Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | permission | ||||
update | permission | ||||
delete | permission | ||||
query | permission |
Tag Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | tag | ||||
update | tag | ||||
delete | tag | ||||
query | tag |
Note: Only Organization Owner can manage tags.
API Key Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | api-key | ||||
delete | api-key | * | |||
query | api-key | ||||
revoke | api-key | * | |||
exchange | api-key |
* Dev can only delete/revoke their own API keys
Relationship Permissions
Organization Member Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
read | organization-member | ||||
update | organization-member | ||||
remove | organization-member | ||||
query | organization-member |
Organization Invitation Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
create | organization-invitation | ||||
read | organization-invitation | ||||
query | organization-invitation | ||||
revoke | organization-invitation | ||||
resend-email | organization-invitation | ||||
renew | organization-invitation |
Project User Permissions
| Action | Resource | Organization Owner | Organization Admin | Organization Dev | Organization Viewer |
|---|---|---|---|---|---|
read | project-user | ||||
query | project-user | ||||
set-email | project-user | ||||
set-password | project-user | ||||
export-credentials | project-user |
export-credentials is denied to every standard role by default; an organization grants it explicitly (see Project User Groups above).
Session & Authentication Permissions (Authorization-Required Only)
User Session Permissions
| Action | Resource | All Roles |
|---|---|---|
read | user-session | * |
query | user-session | * |
* Users can only read/query their own sessions (enforced by condition)
User Authentication Method Permissions
| Action | Resource | All Roles |
|---|---|---|
read | user-authentication-method | * |
query | user-authentication-method | * |
* Users can only read/query their own authentication methods (enforced by condition)
:::
Naming and Scope
Permissions are stored as action (kebab-case: create, read, update, delete, query, export-data, remove, revoke, resend-email, renew, exchange) plus resourceId (references the resources table). Scopes: Account (account-level actions), Organization (org and members), Project (project resources). Access is limited to resources in the user's assigned scopes.
Implementation Notes
Role hierarchy (conceptual): Owner ⊃ Admin ⊃ Dev ⊃ Viewer; only Tags are Owner-only (Admin has no tag write). Self-management (conditions): Dev can update own user; all users can read own sessions and auth methods; Dev can delete/revoke own API keys. API keys are not bound by this hierarchy but by permission containment. See Permission Conditions for syntax. Auth-only (no RBAC): Login, register, session refresh, password reset, profile picture, session revocation, auth method CRUD — require only authentication.
Permission evaluation
Implemented by GrantService.getUserPermissions (API) and evaluated by PermissionChecker (@grantjs/core):
- Load the user’s roles in the scope (account / organization / project). Roles are optional — a user with only direct groups or permissions can still be authorized.
- Collect groups from those roles (
role_groups) and direct user groups (user_groups/project_user_groups). - Resolve permissions from those groups (
group_permissions). - Add direct role permissions (
role_permissions/project_role_permissions) and direct user permissions (user_permissions/project_user_permissions). - Union all permission IDs, then keep those matching the requested action + resource.
- If a matching permission has a condition, evaluate it with the execution context (role/group combinations when present).
- Enforce tenant isolation (resource in the user’s scope).
See also the diagram on Architecture Overview → Permission Evaluation.
Related: Data Model · Multi-Tenancy · Security