# Appendix C -- Identity / RBAC reference

Authoritative reference for the cloud's identity model: which keystone roles and personas exist,
what each is for, and which accounts should receive which assignments. Governed by D-051 (SCS
Domain Manager persona) as reconciled by D-064 (scs-0302 alignment + create-op templating fix).

The persona is delivered by a keystone policy override (`policies/domain-manager-policy.yaml`)
attached as a `policyd-override` resource. Provenance: SCS standard scs-0302-w1 (Domain Manager).
This is a TRANSITIONAL policy for pre-2024.2 keystone; on any upgrade to 2024.2+ the override MUST
be removed in favour of the native domain-manager persona (see "Removal" below).

Do NOT hand-assign roles ad hoc. Provision accounts per the tables below so the model stays
auditable and the tenant isolation guarantees hold.

---

## C.1 Role / persona catalog

| Role / persona | Keystone rule | What it is for | Typical scope | Notes |
|----------------|---------------|----------------|---------------|-------|
| Admin / admin | `admin_required` (role:Admin); becomes `cloud_admin` when scoped to the admin domain/project | Full cloud authority (as cloud_admin), or domain-scoped admin over a single service domain | admin domain + admin project (cloud_admin); or a service domain (e.g. magnum) | "Admin" and "admin" are the same role (case-insensitive alias). After D-064 an admin-on-a-domain can create users/projects in THAT domain. NOT given to tenants -- an admin role is not safely domain-confinable; tenants get `manager` instead. |
| manager (SCS Domain Manager) | `is_domain_manager` = role:manager, plus the D-051/D-064 override | Domain-confined IAM self-service: create/manage users, projects, groups, and role assignments WITHIN the manager's own domain | a tenant's domain | Requires the policy override attached. Can assign ONLY the roles in `is_domain_managed_role` (below); cannot grant `admin` or `manager` (anti-escalation). |
| member | standard project role | Operate project resources (compute, network, volumes, images) | a project | The baseline tenant working role. |
| load-balancer_member | Octavia project role | Create / manage Octavia load balancers | a project | Required for tenant LBs and for Magnum (the cluster apiserver LB). Must be held by the Magnum trustor on capi-mgmt (D-039). |
| reader | standard read role | Read-only visibility | project or domain | Optional. Part of the Magnum trustor role set on capi-mgmt (D-039). |

Manager-assignable roles (`is_domain_managed_role`): **member** and **load-balancer_member** only.
This is the anti-escalation boundary -- a compromised or careless domain manager cannot mint an
admin or another manager, and cannot reach outside its own domain.

---

## C.2 Account -> role assignment

| Account | Role(s) | Scope | Why / reference |
|---------|---------|-------|-----------------|
| admin (operator super-admin) | Admin | admin domain + admin project (= cloud_admin) | Cloud operator; full authority. Bootstrap identity. |
| admin (as Magnum trustor) | member + load-balancer_member + reader | capi-mgmt project | So the app-cred Magnum mints per cluster carries Octavia authority for the apiserver LB (D-039). These are the frozen trustor roles delegated into each cluster trust. |
| magnum_domain_admin | Admin | magnum domain | Magnum trustee domain admin; creates the per-cluster trustee USER at cluster-create (D-046; Magnum docs). Works via the D-064 create-op fix -- no extra grant needed. Recreated by the `domain-setup` charm action after every teardown/redeploy (D-046). |
| <tenant>-domain-admin | manager | the tenant's domain | SCS Domain Manager persona (D-051/D-064). Operator provisions the domain + this one account; the tenant self-services users, projects, and member/load-balancer_member grants from there. |
| <tenant> human users | member (+ load-balancer_member if they use LBs or Magnum) | the tenant's project(s) | Created and assigned by the tenant's own domain-manager via Horizon/CLI. Operator is not in the loop. |
| <tenant>-ci / service accounts | member + load-balancer_member | the tenant's project | Backing identity for the application credential that CI/automation authenticates with. load-balancer_member so tenant CI can drive Magnum/LBs. |
| per-cluster trustee | (delegated via trust -- not a direct grant) | -- | Magnum mints this at cluster-create and deletes it at cluster-delete. It carries the trustor's frozen roles through the trust (D-039). Never assign roles to it by hand. |

Provisioning direction: the operator creates a tenant's DOMAIN and its single `manager` account,
then hands off. Everything below the domain (users, projects, member/LB grants) is tenant
self-service. This is the whole point of the persona -- it removes the operator from routine
tenant IAM while keeping a hard domain boundary.

---

## C.3 Attaching the policy override

Prerequisite: keystone deployed with `use-policyd-override=true` (already set in the bundle).
Note D-050: `use-policyd-override=true` with NO resource attached is a silent no-op -- the zip
must actually be supplied, or the override does nothing while reporting healthy.

Pre-attach re-check on the jumphost (from the repo working copy), then attach:

```
# 1) validate the file before attaching (YAML + ASCII + connector lint)
python3 -c 'import yaml,sys; d=yaml.safe_load(open("policies/domain-manager-policy.yaml")); print("YAML OK:",len(d),"rules")'
LC_ALL=C grep -nP "[^\x00-\x7F]" policies/domain-manager-policy.yaml && { echo "NON-ASCII -- STOP"; exit 1; } || echo "ASCII clean"
grep -nE "\)\s+(rule:|role:|token|project_id|domain_id|user_id|None:)" policies/domain-manager-policy.yaml && { echo "MALFORMED connector -- STOP"; exit 1; } || echo "connector lint clean"

# 2) package and attach (the zip's top-level file name is what keystone reads)
cd policies && zip -j /tmp/overrides.zip domain-manager-policy.yaml && cd -
juju attach-resource -m openstack keystone policyd-override=/tmp/overrides.zip
```

Gate: `juju status -m openstack keystone` must move from `PO (broken)` to `PO:` and settle
active/idle, with no other charm disturbed. If the charm rejects the file it stays `PO (broken)`.

Rollback (immediate): `juju config -m openstack keystone use-policyd-override=false`
(the override stops applying; keystone reverts to shipped defaults on the next hook).

The charm validates YAML only. It does NOT parse the oslo.policy rule grammar, so a syntactically
malformed rule can pass validation and silently no-op. Always run the oslo.policy parse in the
sandbox before delivering a change to this file.

---

## C.4 Tenant self-service validation (G3)

Run after the override is active. Confirms the persona works AND is properly bounded.

PASS cases (a `manager`-on-domain account, scoped to its own domain, must succeed):
- create a user in its own domain
- create a project in its own domain
- grant `member` and `load-balancer_member` to a user on a project in its own domain

DENY cases (the same account must be refused):
- grant `admin` or `manager` to anyone (anti-escalation)
- create/read/modify anything in a DIFFERENT domain (cross-domain isolation)

Unaffected:
- cloud_admin (the operator admin) retains full authority everywhere

Only when all three groups hold is the persona accepted for that release.

---

## C.5 Known limitations (carried from scs-0302)

- A domain manager can enumerate ALL domain names/ids (`list_domains`) and ALL role names
  (`list_roles`) cloud-wide. This is names/ids only -- no access to other domains' resources --
  and is required for the manager to resolve domains/roles by name. It is inherent to the
  pre-2024.2 transitional policy; upstream RBAC-scoping of domain listing is a pending fix.
- The persona relies on `enforce_scope=False` (old-style policy). It is a bridge, not the
  destination.

## C.6 Removal (on upgrade to keystone 2024.2+)

2024.2 ships a native domain-manager persona and secure-RBAC scope enforcement. On that upgrade:
detach the `policyd-override` resource (or set `use-policyd-override=false`), adopt the native
persona, and retire this file. Leaving the old-style override in place on a secure-RBAC keystone
is unsupported and will conflict. Tracked by D-051/D-064.
