Some checks failed
Dependency Audit / dependency-audit (push) Has been cancelled
344 lines
9.7 KiB
Markdown
344 lines
9.7 KiB
Markdown
# GovOPlaN RBAC And Resource-Access Model
|
|
|
|
**Updated:** 2026-07-11
|
|
|
|
## Authorization Equation
|
|
|
|
An operation is permitted only when every applicable layer allows it:
|
|
|
|
```text
|
|
effective role/API-key capability
|
|
AND resource ownership/share access
|
|
AND workflow state
|
|
AND active governance/policy constraints
|
|
```
|
|
|
|
RBAC answers what an actor may do. ACLs answer which resource the actor may do
|
|
it to. Workflow state and policy decide whether the operation is currently
|
|
valid.
|
|
|
|
## Identity And Scope
|
|
|
|
```text
|
|
Account global login identity
|
|
+- User membership tenant-local identity
|
|
+- direct tenant roles
|
|
+- active group memberships
|
|
| +- inherited tenant roles
|
|
+- tenant-local API keys
|
|
|
|
Account
|
|
+- direct system-role assignments
|
|
```
|
|
|
|
A browser session has one active tenant membership. System privileges do not
|
|
silently grant tenant data access. API keys remain tenant-local and receive the
|
|
intersection of their configured scopes and their owner's live tenant scopes on
|
|
every request.
|
|
|
|
## Wildcards
|
|
|
|
```text
|
|
tenant:* every canonical tenant permission
|
|
system:* every canonical system permission
|
|
* legacy alias interpreted as tenant:* only
|
|
```
|
|
|
|
Tenant wildcards never grant system permissions.
|
|
|
|
## Canonical Tenant Permissions
|
|
|
|
Campaigns:
|
|
|
|
```text
|
|
campaign:read
|
|
campaign:create
|
|
campaign:update
|
|
campaign:copy
|
|
campaign:archive
|
|
campaign:delete
|
|
campaign:share
|
|
campaign:validate
|
|
campaign:build
|
|
campaign:review
|
|
campaign:send_test
|
|
campaign:queue
|
|
campaign:control
|
|
campaign:send
|
|
campaign:retry
|
|
campaign:reconcile
|
|
```
|
|
|
|
Recipients:
|
|
|
|
```text
|
|
recipients:read
|
|
recipients:write
|
|
recipients:import
|
|
recipients:export
|
|
```
|
|
|
|
Files:
|
|
|
|
```text
|
|
files:read
|
|
files:download
|
|
files:upload
|
|
files:organize
|
|
files:share
|
|
files:delete
|
|
files:admin
|
|
```
|
|
|
|
Reports and audit:
|
|
|
|
```text
|
|
reports:read
|
|
reports:export
|
|
reports:send
|
|
audit:read
|
|
```
|
|
|
|
Mail servers:
|
|
|
|
```text
|
|
mail_servers:read
|
|
mail_servers:use
|
|
mail_servers:test
|
|
mail_servers:write
|
|
mail_servers:manage_credentials
|
|
```
|
|
|
|
Tenant administration:
|
|
|
|
```text
|
|
admin:users:read
|
|
admin:users:create
|
|
admin:users:update
|
|
admin:users:suspend
|
|
|
|
admin:groups:read
|
|
admin:groups:write
|
|
admin:groups:manage_members
|
|
|
|
admin:roles:read
|
|
admin:roles:write
|
|
admin:roles:assign
|
|
|
|
admin:api_keys:read
|
|
admin:api_keys:create
|
|
admin:api_keys:revoke
|
|
|
|
admin:settings:read
|
|
admin:settings:write
|
|
admin:policies:read
|
|
admin:policies:write
|
|
```
|
|
|
|
## Canonical System Permissions
|
|
|
|
```text
|
|
system:tenants:read
|
|
system:tenants:create
|
|
system:tenants:update
|
|
system:tenants:suspend
|
|
|
|
system:accounts:read
|
|
system:accounts:create
|
|
system:accounts:update
|
|
system:accounts:suspend
|
|
|
|
system:roles:read
|
|
system:roles:write
|
|
system:roles:assign
|
|
|
|
system:access:read
|
|
system:access:assign
|
|
|
|
system:audit:read
|
|
system:settings:read
|
|
system:settings:write
|
|
system:governance:read
|
|
system:governance:write
|
|
```
|
|
|
|
`system:access:*` remains a read/assignment boundary for cross-tenant and
|
|
system access handling. It is not a separate primary UI area.
|
|
|
|
## Default Tenant Roles
|
|
|
|
- **Owner:** `tenant:*`. At least one active operational owner must remain.
|
|
- **Tenant administrator:** settings, policies, users, groups, roles, API keys,
|
|
and read access to campaigns/files/reports/audit. Real delivery remains
|
|
separately delegable.
|
|
- **Administrator:** all tenant permissions for upgraded installations.
|
|
- **Access administrator:** membership and assignment management within
|
|
delegation limits.
|
|
- **Campaign manager:** prepare, validate, and build campaigns; no review
|
|
approval or real delivery by default.
|
|
- **Reviewer:** inspect and approve prepared campaign messages.
|
|
- **Sender:** mock-test, queue, control, send, retry, and reconcile prepared
|
|
campaigns; can use/test approved mail profiles.
|
|
- **File manager:** managed file operations without campaign delivery rights.
|
|
- **Viewer:** read campaigns, recipients, files, and reports.
|
|
- **Auditor:** read campaigns, recipient evidence, reports, and audit records;
|
|
export detailed evidence.
|
|
|
|
## Default System Roles
|
|
|
|
- **System owner:** `system:*`, protected. At least one active account must
|
|
retain it.
|
|
- **System administrator:** all specific system permissions, editable and not
|
|
protected.
|
|
- **System auditor:** read-only system registry/settings/governance/audit role,
|
|
editable.
|
|
|
|
## Delegation Ceiling
|
|
|
|
For role definition, assignment, and API-key creation:
|
|
|
|
```text
|
|
requested scopes subset of actor delegateable scopes
|
|
```
|
|
|
|
Rules:
|
|
|
|
1. Tenant roles may contain tenant scopes only.
|
|
2. System roles may contain system scopes only.
|
|
3. Definition rights and assignment rights are separate.
|
|
4. Group definition and group membership management are separate.
|
|
5. API-key scopes are intersected with the owner's current effective scopes on
|
|
every request.
|
|
6. Suspended accounts, users, tenants, or groups stop contributing access
|
|
immediately.
|
|
7. Administrative updates are field-sensitive; a user with only status
|
|
authority cannot change role assignments.
|
|
|
|
## Campaign Ownership And ACLs
|
|
|
|
A campaign has exactly one owner:
|
|
|
|
```text
|
|
owner user OR owner group
|
|
```
|
|
|
|
Additional active shares may target users or groups with `read` or `write`.
|
|
|
|
Resolution:
|
|
|
|
- owner user: read and write;
|
|
- member of owner group: read and write;
|
|
- explicit read share: read;
|
|
- explicit write share: read and write;
|
|
- `tenant:*`: tenant-wide ACL bypass;
|
|
- ordinary campaign permission without ownership/share: no object access.
|
|
|
|
ACLs do not add capabilities. A write share still needs the specific permission
|
|
for update, validation, review, send, report, retry, or reconciliation.
|
|
|
|
## Files
|
|
|
|
The current file access model distinguishes tenant-level file capabilities from
|
|
space/folder/file ownership:
|
|
|
|
| Scope | Meaning |
|
|
| --- | --- |
|
|
| `files:read` | browse/read visible file spaces and metadata |
|
|
| `files:download` | download file content when ACL permits |
|
|
| `files:upload` | create files in writable spaces |
|
|
| `files:organize` | create folders, move files, and update metadata where ACL permits |
|
|
| `files:share` | share files/spaces according to owner and policy rules |
|
|
| `files:delete` | delete or retire files where ACL permits |
|
|
| `files:admin` | tenant-wide administration of user/group file spaces |
|
|
|
|
External file connections and spaces are additionally constrained by connector
|
|
policy and owner/group assignment in the files module.
|
|
|
|
## Resource Access Explanations
|
|
|
|
The access module exposes a diagnostic endpoint for explaining why a principal
|
|
can see or operate on a concrete resource:
|
|
|
|
```text
|
|
GET /api/v1/admin/access/resource-explanation
|
|
```
|
|
|
|
Required query values:
|
|
|
|
| Field | Meaning |
|
|
| --- | --- |
|
|
| `user_id` | Tenant membership to explain. The current module UIs pass the signed-in user. |
|
|
| `resource_type` | Module-owned type such as `file`, `folder`, or `campaign`. |
|
|
| `resource_id` | Stable module resource identifier. |
|
|
| `action` | Permission/action being explained, for example `files:file:read`. |
|
|
| `tenant_id` | Optional tenant override for system/admin contexts. |
|
|
|
|
The access module always contributes effective-scope provenance for the action.
|
|
Installed modules may add resource provenance by exposing a
|
|
`ResourceAccessExplanationProvider` through a capability consumed by access.
|
|
Providers should return only facts they own, using these provenance kinds:
|
|
|
|
| Kind | Meaning |
|
|
| --- | --- |
|
|
| `resource` | The concrete resource or an explicit not-found result. |
|
|
| `owner` | Matching user/group ownership. |
|
|
| `share` | Matching explicit user/group/tenant share. |
|
|
| `policy` | Administrative bypass or policy-derived grant. |
|
|
| `role` / `right` | Scope and role provenance from access itself. |
|
|
|
|
Files currently registers `files.access` and explains file assets plus folders.
|
|
Persisted folders use their database ID. Folder rows inferred from file paths use
|
|
a deterministic virtual ID:
|
|
|
|
```text
|
|
virtual-folder:v1:<tenant_id>:<owner_type>:<owner_id>:<base64url(normalized_path)>
|
|
```
|
|
|
|
The files provider validates virtual folder IDs before returning provenance: the
|
|
tenant and owner must match the resource ID, and at least one active file must
|
|
exist below the normalized folder path. This keeps virtual folder explanations
|
|
stable without forcing every inferred tree node to become a stored folder row.
|
|
|
|
Campaign currently registers `campaigns.access` and explains the campaign
|
|
ownership/sharing object itself. Finer-grained campaign sub-objects are tracked
|
|
separately in `govoplan-campaign#50` until their resource identifiers and access
|
|
rules are decided.
|
|
|
|
Cross-user resource explanation is a policy feature, not a module-local UI
|
|
detail. Until `govoplan-policy#6` is resolved, module UIs should default to
|
|
current-user explanation and avoid importing access-admin user-picking
|
|
components.
|
|
|
|
## Mail Servers
|
|
|
|
| Scope | Meaning |
|
|
| --- | --- |
|
|
| `mail_servers:read` | profile metadata and effective policy visibility |
|
|
| `mail_servers:use` | use an allowed profile for a campaign or message flow |
|
|
| `mail_servers:test` | run connectivity tests without revealing secrets |
|
|
| `mail_servers:write` | create/update profile metadata where policy allows |
|
|
| `mail_servers:manage_credentials` | create/replace SMTP/IMAP secrets or lower-level credentials where policy allows |
|
|
|
|
Reusable encrypted profiles exist. Effective usability is also constrained by
|
|
hierarchical mail-profile policy, ownership, allowed/forced profile sets,
|
|
credential inheritance mode, lower-level override switches, and allow/deny
|
|
patterns.
|
|
|
|
## Compatibility Aliases
|
|
|
|
Compatibility aliases may exist in backend code for upgraded installations, but
|
|
new UI and docs should use canonical scopes.
|
|
|
|
Current alias direction:
|
|
|
|
```text
|
|
* -> tenant:*
|
|
system:tenants:write -> create/update/suspend tenant scopes
|
|
system:access:write -> system access assignment/write scopes
|
|
```
|
|
|
|
A separate `retention:*` family is not currently canonical because retention is
|
|
managed through system settings and tenant policy scopes. Add it only if
|
|
retention operation duties need separation from general policy/settings
|
|
administration.
|