Implements platform feature branch `285-workspace-rbac-environment-access`. Summary: - switch managed environment authorization to workspace-first role resolution with explicit environment-scope narrowing - rewire Filament pages, resources, policies, and user tenant access helpers to the shared access-scope resolver - add Spec 285 coverage across unit, feature, and browser tests plus full spec artifacts Validation: - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Auth/WorkspaceFirstCapabilityResolverTest.php tests/Unit/Auth/ManagedEnvironmentAccessScopeResolverTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Auth/WorkspaceFirstManagedEnvironmentAccessTest.php tests/Feature/Filament/ManagedEnvironmentAccessScopeManagementTest.php tests/Feature/Filament/WorkspaceMembershipRoleManagementTest.php tests/Feature/Rbac/GovernanceArtifactsWorkspaceFirstAuthorizationTest.php tests/Feature/Rbac/OperationRunWorkspaceFirstAuthorizationTest.php tests/Feature/Rbac/ProviderConnectionWorkspaceFirstPolicyTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Verification/ProviderExecutionReauthorizationTest.php tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php tests/Feature/Tenants/TenantProviderBackedActionStartTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Audit/TenantMembershipAuditLogTest.php tests/Feature/Filament/TenantMembersTest.php tests/Feature/TenantRBAC/TenantMembershipCrudTest.php tests/Feature/TenantRBAC/TenantSwitcherScopeTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` Target branch: `platform-dev`. Follow-up integration path after merge: - `platform-dev` -> `dev`. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #344
183 lines
8.3 KiB
Markdown
183 lines
8.3 KiB
Markdown
# Research: Workspace-first RBAC & Environment Access Scoping
|
|
|
|
## Scope of this research
|
|
|
|
This note records the repo-truth decisions behind Feature `285`. It stays prep-only and does not introduce runtime implementation.
|
|
|
|
## Sources reviewed
|
|
|
|
- `docs/product/roadmap.md`
|
|
- `docs/product/spec-candidates.md`
|
|
- `specs/280-workspace-tenancy-environment-routing/`
|
|
- `specs/281-provider-connection-scope/`
|
|
- `specs/283-provider-capability-registry/`
|
|
- `specs/062-tenant-rbac-v1/`
|
|
- `specs/065-tenant-rbac-v1/`
|
|
- `apps/platform/app/Models/WorkspaceMembership.php`
|
|
- `apps/platform/app/Models/ManagedEnvironmentMembership.php`
|
|
- `apps/platform/app/Models/User.php`
|
|
- `apps/platform/app/Services/Auth/WorkspaceCapabilityResolver.php`
|
|
- `apps/platform/app/Services/Auth/CapabilityResolver.php`
|
|
- `apps/platform/app/Support/Workspaces/WorkspaceContext.php`
|
|
- `apps/platform/app/Policies/ProviderConnectionPolicy.php`
|
|
- `apps/platform/app/Policies/OperationRunPolicy.php`
|
|
- `apps/platform/app/Policies/FindingPolicy.php`
|
|
- `apps/platform/app/Policies/EvidenceSnapshotPolicy.php`
|
|
- `apps/platform/app/Policies/ReviewPackPolicy.php`
|
|
- `apps/platform/app/Policies/TenantReviewPolicy.php`
|
|
- `apps/platform/app/Filament/Resources/TenantResource/Pages/ManageTenantMemberships.php`
|
|
- `apps/platform/app/Filament/Resources/TenantResource/RelationManagers/TenantMembershipsRelationManager.php`
|
|
|
|
## Decision 1: Workspace membership is already the right canonical role-bearing seam
|
|
|
|
### Findings
|
|
|
|
- `WorkspaceMembership` already exists as a workspace-owned role-bearing pivot.
|
|
- `WorkspaceCapabilityResolver` already resolves workspace membership, caches it per request, and exposes `getRole`, `can`, and `isMember`.
|
|
- Workspace-owned policies and management flows already rely on that seam.
|
|
|
|
### Decision
|
|
|
|
`WorkspaceMembership` remains the only role-bearing truth for Feature `285`.
|
|
|
|
### Why
|
|
|
|
The repo already contains the correct workspace-first substrate. Adding a third RBAC layer or preserving a second role-bearing environment path would increase drift instead of resolving it.
|
|
|
|
## Decision 2: The real repo problem is dual role-bearing truth, not missing RBAC from scratch
|
|
|
|
### Findings
|
|
|
|
- `ManagedEnvironmentMembership` is still treated as a role-bearing pivot.
|
|
- `CapabilityResolver` still resolves access from managed-environment membership and `RoleCapabilityMap`.
|
|
- `User::canAccessTenant()` still relies on `CapabilityResolver::isMember()`.
|
|
- `WorkspaceContext` still reaches managed-environment access through that tenant-first user path.
|
|
- Key policies for provider connections, runs, findings, evidence, review packs, and tenant reviews use different combinations of workspace membership and managed-environment membership.
|
|
- `ManageTenantMemberships` and `TenantMembershipsRelationManager` still expose operator-facing role editing for managed environments.
|
|
|
|
### Decision
|
|
|
|
Feature `285` is a consolidation cutover. It must replace the split role-bearing model rather than stack a new compatibility layer on top.
|
|
|
|
### Why
|
|
|
|
The raw candidate text talks about `TenantMembership`, but current repo truth uses `ManagedEnvironmentMembership`. The real cutover target is therefore the duplicated role-bearing access model itself.
|
|
|
|
## Decision 3: Managed-environment membership becomes a narrow access-scope overlay only
|
|
|
|
### Findings
|
|
|
|
- The roadmap still wants environment access scoping to remain possible after the workspace-first cutover.
|
|
- The repo has many environment-owned resources where full workspace inheritance may need narrowing.
|
|
- Replacing environment role authority does not require removing the concept of selective environment visibility.
|
|
|
|
### Decision
|
|
|
|
The current managed-environment membership persistence may survive only as a logical access-scope overlay or be replaced in place by an equivalent successor. It must not remain role-bearing.
|
|
|
|
### Why
|
|
|
|
This preserves the narrow product need for selective environment visibility without rebuilding a second ACL system.
|
|
|
|
## Decision 4: The default environment access rule is inheritance, not mandatory per-environment grants
|
|
|
|
### Findings
|
|
|
|
- Workspace-first tenancy is meant to reduce operator friction, not require duplicate allowlists for every member.
|
|
- The current product already anchors `ManagedEnvironment` to `workspace_id`.
|
|
|
|
### Decision
|
|
|
|
If a workspace member has no explicit scope rows, they inherit visibility to the managed environments in that workspace. If scope rows exist, they narrow visibility to an allowlist.
|
|
|
|
### Why
|
|
|
|
This is the smallest rule that preserves optional narrowing while keeping workspace membership authoritative.
|
|
|
|
## Decision 5: `CapabilityResolver` should be retargeted, not replaced by another resolver family
|
|
|
|
### Findings
|
|
|
|
- `CapabilityResolver` is already the shared entry point for many environment-owned capability checks.
|
|
- Policies, `User`, and other helpers already call into it.
|
|
|
|
### Decision
|
|
|
|
Retarget `CapabilityResolver` to derive the current role from workspace membership and use managed-environment scope only for visibility narrowing.
|
|
|
|
### Why
|
|
|
|
Replacing callers across the repo with a third resolver type would add avoidable migration spread.
|
|
|
|
## Decision 6: `User` and `WorkspaceContext` must share the same access contract
|
|
|
|
### Findings
|
|
|
|
- `User::getTenants()`, `User::getDefaultTenant()`, and `User::canAccessTenant()` currently shape Filament tenant access.
|
|
- `WorkspaceContext` tracks current workspace, remembered tenant context, and initial workspace resolution, but still delegates tenant access to the old user path.
|
|
|
|
### Decision
|
|
|
|
`User` and `WorkspaceContext` must consume the same workspace-first access contract so context selection, remembered tenant state, and page policy outcomes cannot drift.
|
|
|
|
### Why
|
|
|
|
If the shell and the policies resolve access differently, operators will continue to see allowed selection with denied pages or denied selection with allowed deep links.
|
|
|
|
## Decision 7: Membership-management UI must split role authority from visibility scope
|
|
|
|
### Findings
|
|
|
|
- `ManageTenantMemberships` currently titles the page around tenant memberships and says managed-environment access is managed there.
|
|
- `TenantMembershipsRelationManager` still offers add, change, and remove role actions using tenant capability checks.
|
|
- Workspace membership management already exists separately.
|
|
|
|
### Decision
|
|
|
|
The operator-facing role editor remains the workspace membership surface. The current managed-environment membership page is removed or retargeted into access-scope management with no second role selector.
|
|
|
|
### Why
|
|
|
|
Leaving the current UI intact would let operators recreate the duplicate role model even after backend changes land.
|
|
|
|
## Decision 8: OperationRun authorization stays shared but must follow the same workspace-first rule
|
|
|
|
### Findings
|
|
|
|
- `OperationRunPolicy` currently mixes workspace membership, managed-environment membership, and required capability logic.
|
|
- Some runs are workspace-bound; others are managed-environment-bound.
|
|
|
|
### Decision
|
|
|
|
Workspace-bound runs authorize from workspace membership only. Managed-environment-bound runs authorize from workspace membership, then environment scope, then required capability.
|
|
|
|
### Why
|
|
|
|
This preserves the existing distinction between workspace-wide and environment-bound operations without keeping role truth in two places.
|
|
|
|
## Rejected alternatives
|
|
|
|
### Keep both role-bearing membership models and synchronize them
|
|
|
|
Rejected because it adds more compatibility logic to a pre-production codebase and preserves the core ambiguity that `285` is meant to remove.
|
|
|
|
### Introduce a third access resolver beside the current two
|
|
|
|
Rejected because most callers already rely on `CapabilityResolver` or `WorkspaceCapabilityResolver`. A third resolver family would create a longer migration window and more drift.
|
|
|
|
### Remove all environment-level scoping entirely
|
|
|
|
Rejected because the roadmap explicitly keeps environment access scoping in scope after the workspace-first cutover.
|
|
|
|
### Turn environment scope into per-environment role overrides
|
|
|
|
Rejected because that would introduce a second ACL product and exceed the reserved scope of `285`.
|
|
|
|
## Resulting design constraints
|
|
|
|
- No compatibility shim or dual-write path.
|
|
- No new role family.
|
|
- No new persisted source of truth.
|
|
- No widening into provider capability, route-shell, source taxonomy, copy/localization, or cutover-guardrail work reserved for adjacent specs.
|
|
- The proof set stays bounded to unit, feature, and one browser smoke.
|