Identity and Execution Model¶
The application separates UI access from storage execution. A UI role decides which workspace can be opened; the selected execution context decides which credentials perform S3, IAM, or RGW operations.
Separation of identities¶
| Identity | What it controls | Examples |
|---|---|---|
| UI identity | Workspace visibility, Admin settings, Manager tool access, and audit actor. | ui_none, ui_user, ui_admin, ui_superadmin, UI groups, can_access_storage_ops, can_access_ceph_admin. |
| Execution context | Credentials and account scope used by storage actions. | RGW account, S3 connection, S3 user, session context, Ceph Admin endpoint. |
| Portal grant model | User-facing Storage Space visibility and role. | Private Owner, team Viewer/Editor grants, project Manager, Portal account links. |
| Backend workflow identity | Explicit technical credential used for controlled orchestration. | Portal IAM provisioning, healthchecks, billing, quota, key rotation. |
UI sessions are represented by auth_sessions and single-use
refresh_tokens. external_identities binds an immutable provider subject to
a user; email collisions are held in external_identity_link_requests for
superadmin approval. users.auth_version is the immediate invalidation boundary
for password, role, activation, MFA, and external-identity changes.
Personal storage identity contract¶
- Every human operator uses a dedicated IAM identity or owned private S3 connection; credentials are never shared between people.
- One personal identity may temporarily have multiple keys during rotation. Every key must still identify that same person and be retired after rotation.
- Portal already executes S3 operations with the signed-in user's personal IAM identity. Portal external access is assigned to one named person and must preserve that attribution.
- Application audit identifies control-plane actors. Object-level attribution comes from provider S3 access logs. Without provider logging and retention, the application cannot reconstruct an exhaustive object audit trail.
Context and executor¶
/managerand/browserrely on execution context selection./portalrequires explicit Portal account access and uses Portal Storage Space metadata and grants as the source of truth.- One RGW account represents one Portal project. The account-local
portal-managerIAM group can therefore grant the fixed Manager data-plane action set once for all project Storage Spaces; technical buckets apply an explicit principal-scoped resource-policyDeny. /ceph-adminuses an endpoint-scoped Ceph Admin context./storage-opslists and operates only over contexts the user is authorized to use.- Backend services resolve the executor from the requested context and reject incompatible contexts instead of silently switching to another identity.
Account-scoped Portal routes receive AccountAccess for a persisted RGW S3Account after
the dependency has checked the explicit account selection and Portal role.
Connection, S3-user, and Ceph Admin selectors are rejected at that boundary.
Portal bucket statistics therefore read Manager snapshots using the account
ID directly; current Storage Space visibility and content-access checks still
determine which snapshots can be returned.
The transversal execution-context boundary uses the explicit, non-persistent
S3ExecutionContext. Persistent RGW accounts selected through this boundary
are copied before credentials are attached. Connections, S3 users,
direct sessions, Portal Browser identities, and Ceph Admin endpoints never
instantiate synthetic S3Account ORM records. context_id and context_kind
are authoritative; database-like negative IDs and dynamically attached private
attributes are not part of the contract.
Manager usage scans, integrity checks, purges, and bucket-deletion workflows
take their target identity and name from the resolved S3ExecutionContext,
never from the raw account_id query string. Usage snapshots and aggregates
share that canonical context_id as their Manager scope_id; purge workflow
audit metadata uses it too. A direct S3 session without a selector keeps its
resolved local-account ID or explicit session:<rgw-account-id> identity.
Revision 0124 normalizes historical Manager snapshot scopes and retains the
newest calculation for each canonical context and bucket, without a runtime
alias lookup or changes to historical audit rows.
/manager/context consumes that explicit execution context directly. Its
internal dispatch uses the execution kind, never the presence of optional
source IDs to infer a different kind. Source IDs identify local records for
the existing access checks and limit lookups. access_mode keeps its public
values admin, connection, s3_user, and session; Browser availability,
Ceph operations, and private-access provisioning remain governed by their
existing access services and session capabilities. Limits are loaded only
when include_limits=true, both over HTTP and in direct route calls.
Manager IAM, SNS, statistics, and bucket-operation guards share the explicit
S3ExecutionContext/AccountCapabilities boundary. Missing capabilities are
rejected, never interpreted as unrestricted access. Statistics check bucket
permission and the direct-session traffic capability before resolving a
connection's RGW identity. Only the explicit connection kind triggers that
lookup, and it requires a source connection matching the selected connection
ID. Incidental source metadata on another execution kind cannot switch the
identity. The statistics overview collects IAM counts only when
can_manage_iam is granted. Existing endpoint feature flags, supervision
credentials, workspace access, and native storage authorization remain
independent checks.
Browser listing/detail caches and the shared Manager/Storage Ops bucket cache
use the same s3_execution_cache_key fingerprint. It includes the explicit
context kind and ID, credentials, session token, endpoint, region, addressing
mode, and TLS verification option. Rotating credentials or changing execution
configuration must not reuse a previous cached response. Cache keys store the
digest, never raw credentials. Existing TTLs and mutation invalidation remain
independent from authorization; a cache hit does not authorize a context or
replace Portal's current Storage Space visibility checks.
The shared Manager/Storage Ops bucket cache invalidates every execution variant
of the selected context_id (or the persisted account ID), without inferring a
scope from source metadata or display names. Account, Portal, and bound-session
contexts retain the same account invalidation scope while their cached results
remain separated by execution fingerprint. Invalidation also detaches pending
loads: existing callers may finish with their original result, but subsequent
reads start a fresh load and an older result cannot repopulate the cache.
Ceph Admin bucket listings have two endpoint-scoped cache layers: raw RGW payloads and prepared listing snapshots. Refresh and mutation invalidation atomically discard both layers and detach their pending loads. Old loads may finish for existing callers but cannot restore invalidated entries or displace new loads. Raw requests remain serialized per endpoint within the current load generation: a payload with statistics can satisfy a request without them, while a waiter rechecks its needs after an incomplete or failed request. Coordination state is released when loads finish; no idle endpoint locks are retained. The 30-minute TTL and limits of 16 raw payloads and 64 snapshots remain unchanged.
Ceph Admin RGW account and user listings share EndpointListingCache, with a
separate instance for each entity type. It owns the raw and prepared cache
layers under one lock, with a 30-second TTL and limits of 16 raw payloads and
64 prepared listings. Invalidation clears both layers and detaches pending
loads for the selected endpoint (or all endpoints). An older load cannot
restore invalidated entries. Builders retain independent progress and
cancellation callbacks; cancelling one request does not cancel another.
Pending-load markers are released on success or failure. Account payload
loading uses the internal client's explicit list_accounts(include_details=False)
contract and never retries a TypeError through a different call signature.
Browser STS sessions reuse that execution fingerprint, together with the resolved STS endpoint and the caller's cache partition. Exported credentials remain isolated by authenticated UI session. The cache requests 900-second credentials, renews them two minutes before their provider expiration, purges expired entries across contexts, and retains at most 512 entries with least-recently-used eviction. Failure logs identify the explicit context, never its access key; the existing fallback to that context's original credentials is preserved. Provider responses must contain nonempty string credentials and an explicit timezone-aware expiration. SDK datetimes and ISO timestamps are normalized to UTC; missing, malformed, or already expired values produce a provider error, never a fabricated expiration or a cached invalid session.
Connection RGW identity lookups use a separate 60-second monotonic cache, bounded to 512 entries with expired-entry pruning and least-recently-used eviction. Identity and metrics eligibility have distinct cache scopes. Keys fingerprint the connection metadata and full endpoint lookup configuration, including exact update timestamps and credentials, without retaining raw credentials in cache keys. Endpoint or credential changes therefore do not reuse an older resolution even when updates occur within one second. Identical concurrent lookups share a single result or error, and pending coordination is released on completion.
Connection RGW identity lookups, endpoint S3 healthchecks, and Portal access-log requester enrichment share the endpoint read-credential selector. It chooses a complete supervision key pair first, then a complete administrator pair only when supervision credentials are incomplete. It never combines keys from different pairs or retries with administrator credentials after a provider denial. Without a complete pair, the existing unavailable result is preserved. Metrics and usage eligibility still require complete supervision credentials; this selector does not change native S3 execution identities or permissions.
Usage-history subjects are local RGW accounts or S3 users, scoped to their storage endpoint. Trend filters use the explicit execution kind and the corresponding local subject ID. A direct S3 session bound to a local account can read that account's history; an unregistered session has no local history and returns empty baselines or an unavailable trend response. Connections and Ceph Admin contexts must not be reinterpreted as local history subjects, and missing IDs must never broaden a query to other subjects.
Canonical UI roles¶
users.role stores exactly one role: ui_none, ui_user, ui_admin, or
ui_superadmin. The database constraint, backend request models, and
frontend API types share this contract. Historical aliases are
migrated by revision 0092; they are not accepted or normalized at runtime.
ui_none is the explicit no-workspace role. Unknown historical values migrate
to ui_none so canonicalization cannot grant access accidentally.
Account access axes¶
Account associations carry two independent, nullable rights:
manager_role:account_administratororNULL;portal_role:portal_user,portal_manager, orNULL.
At least one axis must be present. Direct and UI-group associations are combined separately: Manager access is the union of administrator grants, while Portal access takes the highest Portal role only. The effective-access response exposes separate direct and group provenance for both axes. There is no conversion between Manager and Portal roles.
Account-association API payloads require both fields, using null for an absent
right. The removed role and is_root fields, missing role fields, empty
associations, and Manager Browser access without manager_role are rejected.
Ceph's technical account-root identity and RGW account credentials remain
execution details; they do not grant a BucketReef Portal role.
Workspace authorization matrix¶
EffectiveAccessService is the authority for catalogue construction and
execution of a selected context.
Manager tool guards always resolve persisted direct and UI-group grants through
this service; there is no user-only fallback without a database session. A
migration API request reuses that resolution to build its complete
BucketMigrationAccessScope, including administrator account contexts, rather
than querying the grants independently for each subset. The migration worker
uses the same scope builder but performs a fresh resolution at every existing
creator-access checkpoint. No access snapshot is cached between requests or
across migration execution checks; group revocations and expired connections
remain effective before further work.
ManagerToolAccess defines the tool list. The shared manager_tool_access
module maps those fields to the same can_access_manager_ columns on users and
UI groups for persistence, public projections, effective-access aggregation,
and sensitive-change detection. Public direct grants remain separate from
group-derived effective grants. An omitted or null manager_tool_access
preserves direct grants, unless a user's role no longer supports Manager;
demotion to ui_none clears the user's direct tool grants. An explicit empty
object resets all tools to false. A real change to a user's direct tools still
requires the configured recent-authentication check, including revocation.
| Workspace | Allowed UI-user contexts |
|---|---|
| Manager | account_administrator accounts, assigned RGW users, assigned shared Manager connections, and the owner's active private Manager connections. |
| Manager Browser | The active Manager context only. Accounts require account_administrator and allow_manager_browser_data_access = true on the same direct or group link and execute as root. RGW users require the flag on any direct or group link and execute with that RGW user's credentials. Owned private connections require both Manager and Browser access. Shared connections are rejected. |
| Browser | The owner's active, unexpired private connections with access_browser = true, plus compatible Portal projects whose effective portal.browser_access_enabled setting is true. Portal project execution uses the personal Portal IAM identity and Portal profile. |
| Portal | Explicit portal_user or portal_manager account membership only. Execution always uses the user's personal Portal IAM identity. |
| Ceph Admin Browser | The explicit endpoint-wide Ceph Admin branch. |
| Direct S3 session | The explicit session principal and its session capabilities. |
Generic account contexts, RGW users, and shared connections are rejected by
standard Browser before credential resolution. An enabled Portal project is
published as the distinct portal_account context and resolved through the
Portal authorization branch, never through account administrator credentials.
The embedded Manager Browser has no catalogue or selection of its own. It
reuses the active Manager identity and ctx, and every /api/browser request
uses X-S3-Workspace: manager-browser. Account permission cannot be assembled
from separate links: the administrator role and data-access flag must coexist
on one direct or group association. Authorization is revalidated before any
credential resolution so forged and revoked contexts return 403.
GET /api/me/workspace-access returns availability, context counts, and the
backend-selected default workspace. Password login, LDAP, OIDC, redirects, and
the workspace selector consume this contract instead of reconstructing access
from a cached user profile.
Practical impact¶
A single UI user can have access to multiple accounts, connections, and endpoints while keeping execution explicit and attributable. Granting a menu item or Manager tool access does not grant native storage permission by itself; S3, IAM, RGW Admin Ops, or Portal grants still decide whether the storage action is allowed.
Multi-tab context contract¶
- The visible query parameter is the authority for each tab:
ctxfor Manager and standalone Browser,projectfor Portal, andepfor Ceph Admin./manager/browseruses the Managerctxand topbar selector. - Manager and standalone Browser keep distinct default-context preferences. If a Browser
preference or
ctxvalue is no longer authorized, the client removes both, shows a warning, and requires an explicit selection. It never falls back to the first available context. - Portal follows the same rule for project selection. Internal navigation that
omits
projectkeeps the current tab's project and restores it in the URL. - Browser bucket and prefix position, Bucket Ops row selections, and the Bucket
Ops configuration clipboard are operational tab state. They are not shared
through
localStorage; bucket/prefix position and the clipboard usesessionStorage, while row selections start empty after a remount. - Cookie refresh is serialized across tabs with Web Locks and a storage lease
fallback. After one tab rotates the single-use refresh cookie, waiting tabs
reuse the resulting server session and call
/api/auth/session; no access token is stored in browser storage.
These rules intentionally do not migrate former shared selector, selection, or Browser-position snapshots. Old values are ignored rather than kept through a compatibility layer.
Server-managed private access¶
Manager exposes two specialized provisioning branches that never return the new secret to the frontend:
- an authorized, IAM-capable RGW Account or S3 Connection creates a dedicated deterministic IAM user, applies validated groups and policies, creates the access key last, then stores it in an owned private S3 Connection;
- an assigned RGW User with Ceph key-management permission creates a distinct RGW key and immediately stores it in an owned private S3 Connection.
S3 Connection identity metadata uses only iam_user, account_user, or
s3_user as credential_owner_type. The durable provisioning saga may retain
the storage-side principal kind rgw_user, but the resulting connection always
uses the canonical s3_user API and database value. Migration 0073 converts
existing rows and removes the frontend fallback for unknown owner types.
The identity is independent of AccountIAMUser and every Portal identity. A
shared connection is only an administration context; its credentials,
associations, capabilities, and tags are never copied. Endpoint data is derived
server-side from the selected Manager context.
managed_private_accesses records the UI owner, immutable source context,
remote principal, access key ID, private connection, applied IAM resources, and
saga state. It deliberately contains no secret. A partial unique index permits
only one provisioning, active, deleting, or cleanup_pending row per UI
user and source context. Remote mutations are checkpointed, compensated in
reverse order on failure, and retained as cleanup_pending when compensation
cannot finish.
Connections created through this flow have server_managed = true. Generic
connection APIs may change only their name, tags, active state, and workspace
flags. Endpoint, credentials, principal, provenance, rotation, and deletion are
owned by the orchestrator. IAM/RGW key inventories expose the managed link and
reject direct status or delete operations.
Quick troubleshooting matrix¶
| Symptom | Check first |
|---|---|
| Workspace is missing | Global feature flag, UI role, user or group entitlement. |
| Context is missing | Explicit Manager or Portal account role, assigned RGW user/shared Manager connection, owned private connection flags/activity/expiry, or endpoint availability. |
| Menu item is hidden in Manager | Global Manager tool setting, user or group Manager tool access, endpoint capability, and selected context type. |
| Portal Storage Space is missing | Portal account link, Storage Space metadata, access mode, collaborator grant, and archived state. |
Action is visible but returns AccessDenied |
Storage-side IAM/S3/RGW permission and the selected execution identity. |