You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replace project:admin role hierarchy with project:credentials permission
per Mark's feedback — any role can carry the permission without requiring
a new hierarchy level. Loosely coupled permissions > rigid hierarchies.
Also addresses CodeRabbit review:
- Clarify agent-level auth targets binding's project_id (not agent's owner)
- Upgrade duplicate binding rejection from SHOULD to SHALL
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: specs/security/credential-binding-enforcement.spec.md
+12-12Lines changed: 12 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ This spec defines the resolver algorithm, authorization rules for creating bindi
8
8
9
9
### Dependencies
10
10
11
-
-**`project:admin` role**: This spec requires a new `project:admin` role (level 2) between `project:owner` (level 1) and `project:editor` (level 3). Credential binding authorization requires `project:admin` or higher. The role definition and hierarchy amendment belong in `rbac-enforcement.spec.md`; this spec assumes the role exists.
11
+
-**`project:credentials` permission**: This spec requires a new `project:credentials` permission that gates credential binding operations on a project. Any role can carry this permission — `project:owner` includes it by default, `project:editor` does not. The permission definition belongs in `rbac-enforcement.spec.md`; this spec assumes it exists.
12
12
-**Global credential binding pattern**: The `scope=credential` binding with both `project_id=NULL` and `agent_id=NULL` is a new pattern. `ambient-model.spec.md` SHALL be amended to document this as valid for credential scope.
13
13
-**Session service identity**: The `credential:token-reader` lifecycle uses `user_id` in RoleBindings to represent the session's OIDC service account (e.g., `service-account-ambient-e2e`). This is the same identity the control plane already provisions via OIDC client_credentials grant — no new identity mechanism is required.
14
14
@@ -25,7 +25,7 @@ For each credential provider (github, jira, kubeconfig, google, gitlab, vertex):
25
25
3. Otherwise, if a `scope=credential` binding exists where `credential_id` references a credential of this provider, `project_id` is NULL, AND `agent_id` is NULL — use that credential (**global binding**).
26
26
4. Otherwise, no credential is injected for this provider.
27
27
28
-
The API server SHOULD reject creation of duplicate bindings at the same scope level for the same provider (same `credential.provider`, same `project_id`, same `agent_id`). If duplicates exist despite this, the binding with the earliest `created_at` timestamp wins.
28
+
The API server SHALL reject creation of duplicate bindings at the same scope level for the same provider (same `credential.provider`, same `project_id`, same `agent_id`). If duplicates exist despite this (e.g., from prior data), the binding with the earliest `created_at` timestamp wins.
**All credential bindings** require the caller to hold `credential:owner` on the target credential.
79
79
80
-
**Project-level bindings** (`project_id` set, `agent_id` NULL) additionally require the caller to hold `project:admin` or higher (level ≤ 2) on the target project.
80
+
**Project-level bindings** (`project_id` set, `agent_id` NULL) additionally require the caller to hold the `project:credentials` permission on the target project.
#### Scenario: User without project:credentials cannot bind credentials
105
105
106
106
- GIVEN user A holds `credential:owner` on credential C
107
-
- AND user A holds `project:editor` on project P
107
+
- AND user A holds `project:editor` on project P (which does NOT include `project:credentials`)
108
108
- WHEN user A creates a RoleBinding with `scope=credential`, `credential_id=C`, `project_id=P`
109
109
- THEN the request returns 403 Forbidden
110
110
@@ -199,7 +199,7 @@ Deleting a `scope=credential` RoleBinding SHALL NOT terminate running sessions t
199
199
| Consumer | Current behavior | Required change |
200
200
|----------|-----------------|-----------------|
201
201
| Control plane `resolveCredentialIDs`| Lists all credentials via `sdk.Credentials().ListAll()`, picks first per provider | Query `scope=credential` RoleBindings filtered by `project_id` and `agent_id`, implement hierarchical resolution |
202
-
| RBAC middleware (credential binding creation) | Validates `credential:owner` + `project:owner` for project-level bindings |Add validation for agent-level bindings (verify agent belongs to project, caller has `project:admin`+), global bindings (require `platform:admin`), and reject `agent_id` without `project_id`|
202
+
| RBAC middleware (credential binding creation) | Validates `credential:owner` + `project:owner` for project-level bindings |Check caller holds `project:credentials` permission (not just `project:owner`), add agent-level validation (verify agent belongs to project), global bindings (require `platform:admin`), and reject `agent_id` without `project_id`|
203
203
| Credential sidecar entrypoint | Fetches token via bearer token from CP token exchange | No change — consumes `CREDENTIAL_IDS` produced by CP |
204
204
| Runner `populate_runtime_credentials`| Fetches tokens from `CREDENTIAL_IDS` env var | No change — consumes `CREDENTIAL_IDS` produced by CP |
@@ -208,5 +208,5 @@ Deleting a `scope=credential` RoleBinding SHALL NOT terminate running sessions t
208
208
209
209
| Spec | Amendment |
210
210
|------|-----------|
211
-
|`rbac-enforcement.spec.md`| Add `project:admin` role at level 2; update credential binding authorization to require `project:admin`+ instead of `project:owner`|
211
+
|`rbac-enforcement.spec.md`| Add `project:credentials` permission; assign it to `project:owner` by default; update credential binding authorization to check permission instead of role level|
212
212
|`ambient-model.spec.md`| Document global credential binding pattern (`scope=credential` with `project_id=NULL`, `agent_id=NULL`); add credential binding scope terms (agent-level, project-level, global) |
0 commit comments