Who is calling a control plane, and what they may do: the result of
Ankusa.Admin.Auth.authenticate/2, assigned to conn.assigns.principal
for every handler of the admin API, the route-management API and the
claim-check gateway.
id— a stable name for audit and logs (a token entry'sid, a JWT'ssub, a certificate's mapped subject).scopes— operation scopes, e.g."dlq:read".tenants—:all(thetenant:*scope) or the list of tenant ids the principal may act on (onetenant:<id>scope each).
Scopes
| Scope | Grants |
|---|---|
metrics:read | GET /metrics, GET /v1/wal |
config:read | GET /v1/config, GET /asyncapi.json |
config:write | PUT /v1/log-level and POST /v1/config/reload (with tenant:*: both are the node's, for every tenant) |
sources:read, sources:write | a tenant's sources |
dlq:read | GET /v1/dlq; dlq:write: starting a dlq replay |
replays:read, replays:write | replay jobs (dlq:write / quarantine:write as well for those kinds) |
quarantine:read, quarantine:write | the quarantine pen |
rate_limits:read, rate_limits:write | per-tenant rate limits |
tenants:write | DELETE /v1/tenants/:tenant (a tenant erase) and GET /v1/tenants/:tenant/erase; with tenant:* |
routes:read, routes:write | route definitions, IP rules, the dry run |
claims:read | claim-check gateway reads |
audit:read | reserved for the audit log; no route yet |
tenant:<id>, tenant:* | the tenants the other scopes apply to |
A resource that spans tenants is filtered by tenant, not refused. The DLQ,
the quarantine pen, replay jobs and the rate-limit listing take an optional
tenant filter: a principal limited to some tenants gets rows for those
tenants only (forced server-side) and 403 for a tenant it does not hold;
tenant:* is required to read or change them unfiltered. Route definitions
and IP rules belong to no tenant and still need tenant:*.
Summary
Functions
Does the principal hold tenant:*?
A principal from id, scope strings and an extra list of tenant ids (the
ankusa_tenants claim: "*" means every tenant). Entries that are not
scopes of this system are dropped: an identity provider's token carries
scopes of its own (openid, email) that mean nothing here.
The principal of an unauthenticated listener (mode: :none, loopback only):
every scope, every tenant.
Split scope strings into operation scopes and tenants. Returns
{scopes, tenants, unknown}: unknown is every entry that is neither a known
scope nor a tenant: scope, in order.
Does the principal hold scope?
Every operation scope (the tenant: scopes are separate).
May the principal act on tenant?
Types
Functions
Does the principal hold tenant:*?
A principal from id, scope strings and an extra list of tenant ids (the
ankusa_tenants claim: "*" means every tenant). Entries that are not
scopes of this system are dropped: an identity provider's token carries
scopes of its own (openid, email) that mean nothing here.
The principal of an unauthenticated listener (mode: :none, loopback only):
every scope, every tenant.
Split scope strings into operation scopes and tenants. Returns
{scopes, tenants, unknown}: unknown is every entry that is neither a known
scope nor a tenant: scope, in order.
Does the principal hold scope?
@spec scopes() :: [String.t()]
Every operation scope (the tenant: scopes are separate).
May the principal act on tenant?