# pp_cerbos

Cerbos policy data scraped from [`theorchard/ows-pdp/cerbos/policies/`](https://github.com/theorchard/ows-pdp/tree/master/cerbos/policies). The authoritative source for which `(resource_type, action, role)` triples are actually defined in policy.

## Files

| File                              | Rows | Source files | What it contains                                                                                          |
|-----------------------------------|------|--------------|------------------------------------------------------------------------------------------------------------|
| `cerbos_resource_actions.csv`     |  418 | 42 `resourcePolicy` files | One row per (resource × action × role). Includes `resource_schema`, `effect`, `has_condition`, `rule_name`, source `file:line`. |
| `cerbos_derived_roles.csv`        |   82 | 15 `derivedRoles` files   | One row per named derived role (`account_admin`, `content_nr_contribution_can_view`, …) with parent roles. |
| `cerbos_principal_policies.csv`   |   32 | 12 `principalPolicy` files in `policies/machines/**` | One row per (machine_principal × resource × action) — explicit allow/deny rules outside the per-resource policies. |

## Top resources by rule count

| Resource           | Rules | Notes                                              |
|--------------------|-------|----------------------------------------------------|
| `audience`         |   89  | Densest policy in the codebase                     |
| `account`          |   45  |                                                    |
| `identity`         |   35  |                                                    |
| `text_campaign`    |   25  |                                                    |
| `email_campaign`   |   25  |                                                    |
| `contribution`     |   18  | NR ownership                                       |
| `ad_account`       |   12  |                                                    |
| `contributor`      |   10  |                                                    |
| `ad_report`        |   10  |                                                    |
| `sound_recording`  |    9  | NR ownership                                       |

## Distinct `resource_schema` values

`tenant_owned_resource`, `identity_owned_resource`, `identity_and_tenant_owned_resource`, `shared_resource`, `fan_data_list` (snowflake — fansifter-team), `infra` (admin-only routes in `pdp/fastapi/routers/infra.py`), and empty (tenant-less, e.g. `abacus_schedule`).

## Column reference

### `cerbos_resource_actions.csv`

| Column                   | Meaning                                                                                                                                          |
|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| `resource`               | The cerbos resource type (`resourcePolicy.resource`), e.g. `contribution`, `audience`, `account`. Maps 1:1 to consumer code's `resource_type`.   |
| `resource_schema`        | Basename of `schemas.resourceSchema.ref` (e.g. `tenant_owned_resource`, `identity_owned_resource`). Empty when the policy declares no schema (tenant-less resources). |
| `action`                 | One action string from the rule's `actions:` list. Multi-action rules are exploded one row per action. **May be a [cerbos wildcard pattern](https://docs.cerbos.dev/cerbos/latest/policies/resource_policies.html)**: `*` matches any character sequence within a segment, so `view:*` matches `view:basic` / `view:nr` / `view:accounting`, `*:find` matches `audience:find` / `account:find` / etc., and a bare `*` matches every action. When cross-referencing a consumer's action against this column, check both exact and wildcard matches. |
| `role`                   | A single role allowed (or denied) by this rule. Multi-role rules are exploded one row per role.                                                  |
| `role_kind`              | `derived` if the role came from `rule.derivedRoles:`; `builtin` if it came from `rule.roles:` (cerbos built-in role like `user`). Only the demo policies use built-in roles. |
| `effect`                 | `EFFECT_ALLOW` or `EFFECT_DENY`.                                                                                                                  |
| `rule_name`              | The rule's `name` field (e.g. `contribution_view`). For traceability when reading the source file.                                               |
| `has_condition`          | `true` if the **rule** has a `condition:` CEL clause gating it. In our policies these are typically `identity_uuid` checks (e.g. `P.id != R.attr.identity_uuid` in `policies/identity.yml` blocks self-escalation — you can't grant yourself a role) or tenant-shape guards (e.g. `R.attr.tenant.tenant_type in C.seat_tenant_types`). Not feature flags — those aren't done in cerbos. Even when role + action match, a `false`-evaluating condition keeps the rule from firing, so don't trust the row in isolation; open the source file. |
| `imported_derived_roles` | Semicolon-separated list of `importDerivedRoles:` for this resource policy (e.g. `a360_roles;pde_teams`). Tells you which file in `derived_roles/` defines each `role`. |
| `source_file`            | `policies/<path>:<line>` of the rule definition.                                                                                                  |

### `cerbos_derived_roles.csv`

| Column          | Meaning                                                                                                                                                                                  |
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `derived_role`  | Name of the role as referenced from `cerbos_resource_actions.role` (e.g. `account_admin`, `content_nr_contribution_can_view`).                                                          |
| `role_set`      | The named export from the file — `derivedRoles.name` (e.g. `a360_roles`). Resource policies pull this name in via `importDerivedRoles:`.                                               |
| `parent_roles`  | Semicolon-separated list of cerbos built-in roles this derived role extends (typically just `user`).                                                                                   |
| `has_condition` | `true` if this **derived role definition** has a `condition:` CEL clause that must evaluate to true for the role to apply. Almost every derived role here has one — it's how cerbos checks whether the principal *actually has* the role on the relevant tenant (e.g. `P.attr.tenants[V.resource_tenant].roles['account_admin'] != null`). `false` means the role applies whenever its `parentRoles` match, with no extra check. |
| `source_file`   | Relative path to the `.yml` file that defines this role.                                                                                                                                |

### `cerbos_principal_policies.csv`

> **Background:** machines (lambdas, fargate tasks, etc. that act on behalf of a user) don't get derived roles — humans do. Instead, each machine gets an explicit principal policy that **enumerates the (resource, action) pairs it's allowed to perform**; anything not listed is implicitly denied. `condition:` clauses on those rules **further narrow which specific resources** the machine can touch (typically by tenant UUID). Example: `policies/machines/ddex_ingester/ddex_ingester_find_or_create_account.yml` allows `create` on `account`, but only when `V.resource_tenant == C.sme_company_brand_tenant_uuid` — so this lambda can only create accounts under the SME company brand tenant.

| Column          | Meaning                                                                                                                                                                                                                                                                                                                                                                                                             |
|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `principal`     | Machine identity the rule applies to (`principalPolicy.principal`) — typically a UUID for the lambda / fargate task.                                                                                                                                                                                                                                                                                                |
| `resource`      | Resource type this rule grants/denies access to.                                                                                                                                                                                                                                                                                                                                                                    |
| `action`        | The action the principal is allowed/denied. Each `actions[].action` becomes its own row.                                                                                                                                                                                                                                                                                                                            |
| `effect`        | `EFFECT_ALLOW` or `EFFECT_DENY`.                                                                                                                                                                                                                                                                                                                                                                                    |
| `rule_name`     | The rule's `name` field, when present (e.g. `can_create_account_only_under_sme_company_brand`).                                                                                                                                                                                                                                                                                                                     |
| `has_condition` | `true` if the rule has a `condition:` CEL clause **constraining which resources the machine can act on** (typically a tenant_uuid check like `V.resource_tenant == C.<some>_tenant_uuid`). `false` means the rule applies to every resource of `resource` type. Always open the source file when `true` — the row alone doesn't tell you the tenant scope. |
| `source_file`   | Relative path to the principalPolicy file in `cerbos/policies/machines/**`.                                                                                                                                                                                                                                                                                                                                         |

## Cross-references

- **`view:*` rows are cerbos wildcard patterns, not literal action strings.** `policies/neighbouring_rights/contribution.yml:17` declares `actions: ["view:*"]`, which matches any request action starting with `view:` (e.g. `view:basic`, `view:nr`, `view:accounting`). graphql-neighbouring-rights's `@ppCanPerform(action: "view:nr")` resolves to that wildcard rule.
- `frontend-content/modules/neighbouringRights/src/utils/identityPermissions.ts:15` sends the **literal string** `'view:*'` as its action. That likely matches by string equality against the policy pattern, but it's not a normal request action — worth confirming with PP whether the consumer should be sending a specific tier (e.g. `view:basic`) instead.
- Every `(resource_type, action)` pair found across the consumer CSVs in `pp_endpoints/`, `pp_graphql/`, and `pp_frontend/` is defined here — but the cross-check used **literal string matching** and didn't expand wildcards. So a consumer pair like `(contribution, view:nr)` looks "present" in this CSV only if a literal `view:nr` row exists; if only the wildcard `view:*` row exists, treat that as "addressable by a wildcard rule" rather than an exact policy entry.
- `policies/demo/demo_action_wildcard.yml` is the canonical example of cerbos action-wildcard usage.
