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
8.3 KiB
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.mddocs/product/spec-candidates.mdspecs/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.phpapps/platform/app/Models/ManagedEnvironmentMembership.phpapps/platform/app/Models/User.phpapps/platform/app/Services/Auth/WorkspaceCapabilityResolver.phpapps/platform/app/Services/Auth/CapabilityResolver.phpapps/platform/app/Support/Workspaces/WorkspaceContext.phpapps/platform/app/Policies/ProviderConnectionPolicy.phpapps/platform/app/Policies/OperationRunPolicy.phpapps/platform/app/Policies/FindingPolicy.phpapps/platform/app/Policies/EvidenceSnapshotPolicy.phpapps/platform/app/Policies/ReviewPackPolicy.phpapps/platform/app/Policies/TenantReviewPolicy.phpapps/platform/app/Filament/Resources/TenantResource/Pages/ManageTenantMemberships.phpapps/platform/app/Filament/Resources/TenantResource/RelationManagers/TenantMembershipsRelationManager.php
Decision 1: Workspace membership is already the right canonical role-bearing seam
Findings
WorkspaceMembershipalready exists as a workspace-owned role-bearing pivot.WorkspaceCapabilityResolveralready resolves workspace membership, caches it per request, and exposesgetRole,can, andisMember.- 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
ManagedEnvironmentMembershipis still treated as a role-bearing pivot.CapabilityResolverstill resolves access from managed-environment membership andRoleCapabilityMap.User::canAccessTenant()still relies onCapabilityResolver::isMember().WorkspaceContextstill 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.
ManageTenantMembershipsandTenantMembershipsRelationManagerstill 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
ManagedEnvironmenttoworkspace_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
CapabilityResolveris 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(), andUser::canAccessTenant()currently shape Filament tenant access.WorkspaceContexttracks 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
ManageTenantMembershipscurrently titles the page around tenant memberships and says managed-environment access is managed there.TenantMembershipsRelationManagerstill 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
OperationRunPolicycurrently 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.