Skip to content

Commit 21529ea

Browse files
gabemonteroclaude
andcommitted
docs(augment): expand permissions scope and make admin fallback opt-in
Address second round of PR review feedback on fine-grained permissions proposal: - Add 5 infrastructure resource permissions (vectorstore, document, mcp, prompt, model) as basic permissions, expanding total from 16 to 21. These enable deployers to grant targeted access instead of all-or-nothing augment.admin. - Add corresponding route authorization requirements for all 5 infrastructure categories, including closing the gap where MCP tool creation was ungated. - Change augment.admin fallback from default-on to opt-in via permissions.legacyAdminFallback config flag. Dev preview means no backward compatibility guarantees — opt-in avoids the "temporary becomes permanent" problem where removing the fallback later becomes the breaking change it was meant to prevent. Existing deployments can enable the flag during migration. - Update authorization-middleware spec with fallback-disabled scenarios and config-gated fallback behavior in authorizeLifecycleAction and authorizeBasicWithFallback. - Update design.md context, goals, Decision 2, and risk mitigations to reflect broader scope and opt-in fallback rationale. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: gabemontero <gmontero@redhat.com>
1 parent 86cd312 commit 21529ea

4 files changed

Lines changed: 154 additions & 59 deletions

File tree

  • workspaces/augment/openspec/changes/fine-grained-backstage-permissions

workspaces/augment/openspec/changes/fine-grained-backstage-permissions/design.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Context
44

5-
The augment plugin currently enforces 12+ authorization decisions via custom inline guards in route handlers — `checkIsAdmin`, `createdBy` comparisons, lifecycle stage checks, and self-approval prevention. These bypass Backstage's permission framework entirely. Only two coarse permissions exist (`augment.access` for read, `augment.admin` for admin operations), making fine-grained RBAC impossible.
5+
The augment plugin currently enforces 12+ authorization decisions via custom inline guards in route handlers — `checkIsAdmin`, `createdBy` comparisons, lifecycle stage checks, and self-approval prevention. These bypass Backstage's permission framework entirely. Only two coarse permissions exist (`augment.access` for read, `augment.admin` for admin operations), making fine-grained RBAC impossible. Additionally, all infrastructure operations (vector stores, documents, MCP connections, prompts, models) are gated by the single `augment.admin` permission, preventing deployers from granting targeted access to specific resource categories.
66

77
Backstage provides a permission framework (`@backstage/plugin-permission-node`) supporting basic permissions, resource-based permissions with conditional rules, and policy evaluation via the permission backend. The `extensions-backend` plugin in this workspace already follows this pattern and serves as a reference implementation.
88

@@ -13,15 +13,15 @@ These permissions are independent of AgenticProvider authorization. Kagenti's pe
1313
**Goals:**
1414

1515
- Replace all 12+ inline authorization decisions with Backstage permission framework calls
16-
- Enable deployers to configure fine-grained RBAC policies for agent and tool lifecycle operations
17-
- Maintain full backward compatibility — existing `augment.access` + `augment.admin` policies continue to work unchanged
16+
- Enable deployers to configure fine-grained RBAC policies for agent and tool lifecycle operations and targeted access to infrastructure resources (vector stores, documents, MCP connections, prompts, models)
17+
- Provide an opt-in legacy fallback (`permissions.legacyAdminFallback`) so existing deployments using `augment.access` + `augment.admin` can migrate incrementally
1818
- Support conditional permission rules for ownership checks, self-approval prevention, and lifecycle stage gating
1919
- Keep self-approval prevention as defense-in-depth (permission rule supplements hard-coded check)
2020
- Emit structured audit log entries for all authorization decisions — recording the user, action, resource, outcome (allow/deny), and whether the `augment.admin` fallback was used. The authorization middleware is the single point through which all permission decisions flow, making it the natural place for audit logging.
2121

2222
**Non-Goals:**
2323

24-
- Modifying `augment.access` or `augment.admin` behavior or semantics
24+
- Modifying `augment.access` or `augment.admin` behavior or semantics (though `augment.admin` is no longer the default gate for lifecycle/infrastructure operations)
2525
- Adding permissions for AgenticProvider-level operations (Kagenti API calls, OpenAI API calls)
2626
- Frontend permission checks (backend-only enforcement)
2727
- Custom permission policy plugins (works with standard Backstage RBAC)
@@ -35,13 +35,13 @@ Define `augment-agent` and `augment-tool` as distinct resource types rather than
3535

3636
**Alternative considered:** Single `augment-resource` type with a discriminator field. Rejected because it conflates two domain objects that are independently routed and independently targetable by policy.
3737

38-
### Decision 2: Two-tier authorization with backward-compatible fallback
38+
### Decision 2: Opt-in legacy fallback to `augment.admin`
3939

40-
`authorizeLifecycleAction` checks the fine-grained permission first. If DENY (the default when no policy exists), it falls back to checking `augment.admin`. This means deployments that haven't configured fine-grained policies get identical behavior to today.
40+
When `permissions.legacyAdminFallback` is enabled in the plugin config, `authorizeLifecycleAction` checks the fine-grained permission first. If DENY, it falls back to checking `augment.admin`. When the config flag is absent or false (the default), only fine-grained permissions are evaluated — no fallback occurs.
4141

42-
**Why backward compatibility matters for a "new" plugin:** Although the augment plugin is still in active development, there are external consumers already running it with RBAC policies configured around `augment.access` + `augment.admin`. From their perspective, a new drop that requires policy reconfiguration to maintain existing access is a cross-release breaking change. The fallback ensures these deployments continue to work on upgrade, while giving them the option to adopt fine-grained policies incrementally.
42+
**Why opt-in rather than default-on:** The augment plugin is in dev preview, which means no backward compatibility guarantees. Making the fallback default-on risks the "temporary becomes permanent" problem — once deployments rely on `augment.admin` as a catch-all, removing the fallback later becomes the very breaking change it was meant to prevent. Opt-in gives existing consumers a migration path (enable the flag, then incrementally adopt fine-grained policies, then disable the flag) without baking the fallback into every deployment's baseline.
4343

44-
**Alternative considered:** Requiring all deployments to update their RBAC policies. Rejected because it would break existing augment deployments on upgrade — deployers would need to add fine-grained policy entries before the upgrade or lose access to lifecycle operations.
44+
**Alternative considered:** Default-on fallback for all deployments. Rejected because it creates invisible load-bearing behavior that becomes difficult to remove — every release the fallback ships as default makes removal harder, not easier. Dev preview is the right time to establish the clean model.
4545

4646
**Alternative considered:** OR-combining fine-grained and admin in a single policy evaluation. Rejected because Backstage's permission framework evaluates permissions individually — the fallback must be explicit in code.
4747

@@ -63,7 +63,7 @@ Permission rules (`IS_OWNER`, `IS_NOT_CREATOR`, `HAS_LIFECYCLE_STAGE`) follow th
6363

6464
## Risks / Trade-offs
6565

66-
- **Policy migration complexity** → Deployers who want fine-grained control must write new RBAC policies. Mitigated by the fallback mechanism — no action required to maintain current behavior.
66+
- **Policy migration complexity** → Deployers must configure fine-grained RBAC policies. Existing deployments can enable `permissions.legacyAdminFallback` to preserve current `augment.admin` behavior during migration.
6767
- **Conditional evaluation overhead** → Resource-based permissions require loading the resource to evaluate conditions. Mitigated by keeping list operations as basic permissions and only using resource-based permissions for mutation routes where the resource is already loaded.
6868
- **Rule mismatch on upgrade** → If a deployer configures a fine-grained policy with a `HAS_LIFECYCLE_STAGE` condition referencing a stage name that changes, the rule silently denies. Mitigated by documenting stage names as part of the permission contract.
6969
- **Defense-in-depth dual check** → The self-approval hard-coded check and `IS_NOT_CREATOR` rule are redundant by design. If one is relaxed without updating the other, behavior may be confusing. Mitigated by documenting this clearly.

workspaces/augment/openspec/changes/fine-grained-backstage-permissions/specs/authorization-middleware/spec.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,45 @@
22

33
Augment-specific authorization middleware that integrates Backstage's permission framework with the plugin's domain model (agents, tools, lifecycle stages, ownership). This is NOT a reimplementation of RBAC policy evaluation — the RBAC plugin handles that. This middleware provides:
44

5-
1. **Two-tier fallback logic** — check fine-grained permission first, fall back to `augment.admin` for backward compatibility during migration. This is augment-specific business logic that the RBAC plugin cannot provide, since the fallback semantics (which coarse permission to check, when to check it) are domain decisions.
5+
1. **Opt-in legacy fallback logic**when `permissions.legacyAdminFallback` is enabled, check fine-grained permission first, then fall back to `augment.admin` for backward compatibility during migration. When disabled (the default), only fine-grained permissions are evaluated. This is augment-specific business logic that the RBAC plugin cannot provide, since the fallback semantics (which coarse permission to check, when to check it) are domain decisions.
66
2. **Conditional evaluation against augment resource types**`matchesAgentConditions` and `matchesToolConditions` evaluate Backstage CONDITIONAL results against augment's domain objects (agents/tools with `createdBy`, lifecycle stages). The RBAC plugin evaluates policies; these utilities evaluate the resulting conditions against augment-specific resource shapes.
77
3. **RouteContext integration** — exposing authorization functions on the route context so handlers can call them without importing permission utilities directly.
88

99
### Relationship to existing permissions
1010

1111
- **`augment.access`** remains unchanged — it is a plugin-level visibility gate (can the user access the augment plugin at all?) and is orthogonal to fine-grained lifecycle permissions.
12-
- **`augment.admin`** continues to gate admin-only routes (config, documents, vector stores, models, evaluations, workflows, dev spaces) that are not covered by the fine-grained permissions. For agent/tool lifecycle operations, it serves as a backward-compatible fallback: existing deployments that only have `augment.admin` policies continue to work without reconfiguration. Deployers who adopt fine-grained permissions can remove the `augment.admin` grant for lifecycle operations — the fallback is a migration aid, not a permanent override.
12+
- **`augment.admin`** continues to gate admin-only routes (evaluations, workflows, dev spaces) that are not yet covered by fine-grained permissions. For agent/tool lifecycle operations and infrastructure resources (vector stores, documents, MCP, prompts, models), it can serve as a backward-compatible fallback when `permissions.legacyAdminFallback` is enabled in the plugin config. By default, only fine-grained permissions are evaluated — deployments must configure the appropriate fine-grained RBAC policies.
1313

1414
## ADDED Requirements
1515

1616
### Requirement: authorizeLifecycleAction function
1717

18-
The system SHALL provide an `authorizeLifecycleAction(req, permission, resource?)` function that implements two-tier authorization:
18+
The system SHALL provide an `authorizeLifecycleAction(req, permission, resource?)` function that implements authorization with opt-in legacy fallback:
1919

2020
1. Evaluate the fine-grained permission (with conditional rules if resource-based)
2121
2. If ALLOW, grant access
22-
3. If DENY, fall back to checking `augment.admin`
23-
4. If `augment.admin` is ALLOW, grant access
22+
3. If DENY and `permissions.legacyAdminFallback` is enabled, fall back to checking `augment.admin`
23+
4. If fallback is enabled and `augment.admin` is ALLOW, grant access
2424
5. Otherwise, deny access
2525

2626
#### Scenario: Fine-grained permission allows
2727

2828
- **WHEN** `authorizeLifecycleAction` is called and the fine-grained permission evaluates to ALLOW
2929
- **THEN** access SHALL be granted without checking `augment.admin`
3030

31-
#### Scenario: Fine-grained denies but admin allows
31+
#### Scenario: Fine-grained denies, fallback enabled, admin allows
3232

33-
- **WHEN** `authorizeLifecycleAction` is called, the fine-grained permission evaluates to DENY, and `augment.admin` evaluates to ALLOW
33+
- **WHEN** `authorizeLifecycleAction` is called, the fine-grained permission evaluates to DENY, `permissions.legacyAdminFallback` is enabled, and `augment.admin` evaluates to ALLOW
3434
- **THEN** access SHALL be granted via the fallback
3535

36-
#### Scenario: Both deny
36+
#### Scenario: Fine-grained denies, fallback disabled
3737

38-
- **WHEN** `authorizeLifecycleAction` is called and both the fine-grained permission and `augment.admin` evaluate to DENY
38+
- **WHEN** `authorizeLifecycleAction` is called, the fine-grained permission evaluates to DENY, and `permissions.legacyAdminFallback` is not enabled
39+
- **THEN** access SHALL be denied without checking `augment.admin`
40+
41+
#### Scenario: Both deny with fallback enabled
42+
43+
- **WHEN** `authorizeLifecycleAction` is called, `permissions.legacyAdminFallback` is enabled, and both the fine-grained permission and `augment.admin` evaluate to DENY
3944
- **THEN** access SHALL be denied
4045

4146
### Requirement: Conditional permission evaluation
@@ -50,21 +55,26 @@ When `authorizeLifecycleAction` receives a CONDITIONAL result from the permissio
5055
#### Scenario: Conditional result with non-matching resource
5156

5257
- **WHEN** the permission framework returns CONDITIONAL with an `IS_OWNER` condition, and the resource's `createdBy` does not match the requesting user
53-
- **THEN** `authorizeLifecycleAction` SHALL evaluate the condition to DENY and fall back to `augment.admin`
58+
- **THEN** `authorizeLifecycleAction` SHALL evaluate the condition to DENY and, if `permissions.legacyAdminFallback` is enabled, fall back to `augment.admin`
5459

5560
### Requirement: authorizeBasicWithFallback function
5661

57-
The system SHALL provide an `authorizeBasicWithFallback(req, permission)` function for basic (non-resource) permissions that checks the given permission first, then falls back to `augment.admin` on DENY.
62+
The system SHALL provide an `authorizeBasicWithFallback(req, permission)` function for basic (non-resource) permissions that checks the given permission first. If DENY and `permissions.legacyAdminFallback` is enabled, it falls back to `augment.admin`.
5863

5964
#### Scenario: Basic permission allows
6065

6166
- **WHEN** `authorizeBasicWithFallback` is called and the basic permission evaluates to ALLOW
6267
- **THEN** access SHALL be granted
6368

64-
#### Scenario: Basic permission denies with admin fallback
69+
#### Scenario: Basic permission denies, fallback enabled, admin allows
6570

66-
- **WHEN** `authorizeBasicWithFallback` is called, the basic permission evaluates to DENY, and `augment.admin` evaluates to ALLOW
67-
- **THEN** access SHALL be granted
71+
- **WHEN** `authorizeBasicWithFallback` is called, the basic permission evaluates to DENY, `permissions.legacyAdminFallback` is enabled, and `augment.admin` evaluates to ALLOW
72+
- **THEN** access SHALL be granted via the fallback
73+
74+
#### Scenario: Basic permission denies, fallback disabled
75+
76+
- **WHEN** `authorizeBasicWithFallback` is called, the basic permission evaluates to DENY, and `permissions.legacyAdminFallback` is not enabled
77+
- **THEN** access SHALL be denied without checking `augment.admin`
6878

6979
### Requirement: Authorization functions on RouteContext
7080

workspaces/augment/openspec/changes/fine-grained-backstage-permissions/specs/permission-definitions/spec.md

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
Fine-grained permission constants, resource types, and permission rule definitions for augment agent and tool governance.
44

5-
### Scope: Why agents and tools first
5+
### Scope
66

7-
This spec defines fine-grained permissions for **agent and tool lifecycle operations** because these are the authorization decisions with the most nuanced requirements — ownership scoping, self-approval prevention, lifecycle stage gating, and filtered visibility. These are currently enforced via inline checks (`checkIsAdmin`, `createdBy` comparisons) that bypass the Backstage permission framework entirely.
7+
This spec defines two tiers of fine-grained permissions:
88

9-
Other admin-only operations (config CRUD, document management, vector stores, models, evaluations, workflows, dev spaces) remain gated by the existing `augment.admin` permission. These are genuinely admin-only operations without ownership or conditional logic — the coarse gate is appropriate and adding fine-grained permissions for them today would increase surface area without clear demand. They are candidates for future fine-grained permissions if deployer requirements emerge (e.g., separating "can manage documents" from "can manage evaluations").
9+
1. **Resource-based permissions** for agents and tools — these have the most nuanced authorization requirements (ownership scoping, self-approval prevention, lifecycle stage gating, filtered visibility) and support conditional rules (`IS_OWNER`, `IS_NOT_CREATOR`, `HAS_LIFECYCLE_STAGE`).
10+
2. **Basic permissions** for infrastructure resources (vector stores, documents, MCP connections, prompts, models) — these are currently admin-only operations without ownership or lifecycle logic. Defining permissions for them now enables deployers to grant targeted access (e.g., "this team can manage vector stores but not MCP connections") instead of the all-or-nothing `augment.admin`. These can be upgraded to resource-based permissions later if ownership tracking is added.
1011

1112
## ADDED Requirements
1213

@@ -83,9 +84,33 @@ The system SHALL define `augment.kagenti.admin` as a basic permission with actio
8384
- **WHEN** the `augment-common` package is loaded
8485
- **THEN** `augment.kagenti.admin` SHALL be a basic permission with action `update`
8586

87+
### Requirement: Infrastructure resource permissions
88+
89+
The system SHALL define the following basic permissions (no resource type) for infrastructure operations that are currently gated by `augment.admin`:
90+
91+
| Permission ID | Action | Gates |
92+
| ---------------------------- | ------ | ----------------------------------------------------------- |
93+
| `augment.vectorstore.manage` | update | Vector store CRUD (create, connect, disconnect, delete) |
94+
| `augment.document.manage` | update | Document upload and deletion within vector stores |
95+
| `augment.mcp.manage` | update | MCP connection testing, tool creation, deletion, and builds |
96+
| `augment.prompt.manage` | update | System prompt generation and configuration |
97+
| `augment.model.manage` | update | Model listing, testing, and selection configuration |
98+
99+
These are basic permissions without conditional rules. Each gates a category of admin operations, enabling deployers to grant targeted access without granting full `augment.admin`.
100+
101+
#### Scenario: All infrastructure permissions defined
102+
103+
- **WHEN** the `augment-common` package is loaded
104+
- **THEN** all 5 infrastructure permissions SHALL be defined as basic permissions with action `update`
105+
106+
#### Scenario: Infrastructure permissions independent of augment.admin
107+
108+
- **WHEN** a user has `augment.vectorstore.manage` but not `augment.admin`
109+
- **THEN** the user SHALL be able to manage vector stores but not other admin operations
110+
86111
### Requirement: Existing permissions preserved
87112

88-
The existing `augmentAccessPermission` (`augment.access`) and `augmentAdminPermission` (`augment.admin`) SHALL remain unchanged. All 16 new permissions SHALL be added to the `augmentPermissions` array alongside the existing ones.
113+
The existing `augmentAccessPermission` (`augment.access`) and `augmentAdminPermission` (`augment.admin`) SHALL remain unchanged. All 21 new permissions SHALL be added to the `augmentPermissions` array alongside the existing ones. `augment.admin` continues to gate routes not yet covered by fine-grained permissions (evaluations, workflows, dev spaces) and serves as an opt-in fallback for fine-grained operations when `permissions.legacyAdminFallback` is enabled.
89114

90115
#### Scenario: Backward-compatible exports
91116

0 commit comments

Comments
 (0)