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
6.5 KiB
Data Model: Workspace-first RBAC & Environment Access Scoping
Overview
Feature 285 does not add a new persisted source of truth. It clarifies and narrows responsibility across the existing membership data.
Entities and responsibilities
1. WorkspaceMembership
Status: existing, canonical, role-bearing
Responsibility:
- defines whether a user belongs to a workspace
- defines the single role-bearing authority for capability resolution
- remains the source for last-owner protection and workspace membership audit
Core attributes:
workspace_iduser_idrolecreated_by_user_id- timestamps
Rules:
- every access decision starts here
- if no
WorkspaceMembershipexists, the user is out of scope for every workspace-owned and managed-environment-owned surface in that workspace
2. ManagedEnvironmentAccessScope
Status: logical successor to the current role-bearing ManagedEnvironmentMembership semantics
Responsibility:
- optionally narrows which managed environments a workspace member may open
- never grants capabilities by itself
- never stores a second role authority
Expected backing:
- the current
managed_environment_membershipspersistence reused or renamed in place - no dual-write and no compatibility read path
Required data meaning:
managed_environment_iduser_id- audit metadata such as creator and timestamps
Forbidden data meaning:
- role-bearing semantics
- capability-bearing semantics
- owner semantics separate from workspace ownership
Rules:
- scope rows are valid only for users who already have a
WorkspaceMembershipin the same workspace - if no scope rows exist for a workspace member, environment visibility is inherited across the currently selectable managed environments in that workspace
- if scope rows exist for a workspace member, they form an allowlist
3. ManagedEnvironment
Status: existing, workspace-owned child resource
Responsibility:
- anchors provider connections, runs, findings, review artifacts, and other environment-owned resources to
workspace_id - provides the workspace boundary that the scope overlay must match
Rules:
- every managed-environment access decision must confirm that the environment belongs to the current workspace
- scope rows may only reference environments within the member's workspace
4. Environment-owned Resources
Status: existing derived subjects of the access contract
In-scope examples:
ProviderConnectionOperationRunFindingEvidenceSnapshotReviewPackTenantReview- onboarding drafts and sessions tied to a managed environment
Responsibility:
- reuse the shared access contract instead of implementing local role logic
Derived access decision shapes
Workspace membership summary
Used by membership-management surfaces and policy helpers.
Fields:
workspace_iduser_idworkspace_memberbooleanworkspace_rolestring ornullowner_guardedboolean
Managed-environment access decision
Used by User::canAccessTenant(), WorkspaceContext, selection flows, and environment-owned policies.
Fields:
workspace_idmanaged_environment_iduser_idworkspace_rolestring ornullworkspace_memberbooleanexplicit_scope_rows_presentbooleanmanaged_environment_allowedbooleanfailed_boundarystring ornullrequired_capabilitystring ornullcapability_allowedbooleandenial_http_statusinteger ornullprovider_capability_contextstring ornull
Decision order:
- confirm workspace membership
- confirm the environment belongs to the workspace
- if explicit scope rows exist, require the environment to be in the allowlist
- evaluate required capability from the workspace role
- hand off to downstream provider-capability or operability gates only after local access passes
OperationRun access decision
Used by run drilldowns and monitoring surfaces.
Fields:
operation_run_idworkspace_idmanaged_environment_idnullableuser_idworkspace_rolestring ornullworkspace_memberbooleanmanaged_environment_allowedbooleanfailed_boundarystring ornullrequired_capabilitystring ornullcapability_allowedbooleandenial_http_statusinteger ornull
Decision order:
- if the run is workspace-bound, use workspace membership plus required capability
- if the run is managed-environment-bound, apply the managed-environment access decision first and then required capability
Invariants
WorkspaceMembershipis the only role-bearing source of truth.- no managed-environment scope record may grant access without workspace membership.
- no managed-environment scope record may point outside the workspace of the current member.
- deleting workspace membership removes or invalidates any managed-environment scope rows for that user in the same workspace.
- non-membership or out-of-scope access resolves to
404. - in-scope members missing the required capability resolve to
403. - provider capability and operability checks do not run until local access passes.
Audit expectations
- workspace role changes keep using the existing workspace membership audit path
- managed-environment scope mutations write audit records that describe scope narrowing or widening, not role reassignment
- last-owner protection remains anchored to workspace ownership only
Denied-access diagnostic context
Denied-access logging remains derived rather than persisted.
Expected diagnostic fields:
workspace_idmanaged_environment_idnullableuser_idfailed_boundaryas a descriptive string such asworkspace_membership,managed_environment_scope, orcapabilityrequired_capabilitynullable
Rules:
- diagnostics must be sufficient to explain whether the denial happened at workspace membership, environment scope, or capability evaluation
- diagnostics must not include raw provider payloads, secrets, or implementation-only provider evidence
- the shared access decision is the only source from which denial diagnostics are derived
Migration notes for implementation
- runtime implementation may repurpose the current
ManagedEnvironmentMembershipmodel or replace it in place with a clearer successor, but it must preserve the logical rules above - if the current table name is retained temporarily, the implementation must still remove the old role-bearing meaning in the same slice
- no parallel legacy projection is allowed beyond what is strictly required to land the cutover atomically