Implements Spec 080: split Filament into workspace-managed `/admin/*` (manage) vs tenant operations `/admin/t/{tenant}/*` (operate).
Highlights:
- Adds tenant operations panel (`tenant`) at `/admin/t` with tenancy by `Tenant.external_id`
- Keeps management resources in workspace panel (`admin`) under `/admin/tenants/*`
- Moves Provider Connections to workspace-managed routes: `/admin/tenants/{tenant}/provider-connections`
- Adds discoverability CTA on tenant view (Actions → Provider connections)
- Adds/updates Pest regression tests for routing boundaries, 404/403 RBAC-UX semantics, and global search isolation
- Includes full Spec Kit artifacts under `specs/080-workspace-managed-tenant-admin/`
Validation:
- `vendor/bin/sail bin pint --dirty`
- `vendor/bin/sail artisan test --compact tests/Feature/Spec080WorkspaceManagedTenantAdminMigrationTest.php`
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #97
15 KiB
Feature Specification: Workspace-Managed Tenant Administration Migration
Feature Branch: 080-workspace-managed-tenant-admin
Created: 2026-02-07
Status: Draft (implementation-ready)
Input: User description: "Make Manage workspace-scoped (/admin) and Operate tenant-scoped (/admin/t/{tenant}). Eliminate management CRUD from /admin/t/*"
This feature migrates all tenant administration surfaces out of the Filament tenant scope (/admin/t/{tenant}/*) into workspace-scoped routes (/admin/*). Tenant scope is reserved strictly for operational modules.
Separation rule (normative):
- Workspace panel (
/admin/*) = Manage (tenants, memberships, provider connections, required permissions, onboarding/activation, monitoring). - Tenant panel (
/admin/t/{tenant}/*) = Operate (inventory, drift, policies, backups, directory, etc.).
Out of scope for this feature: introducing a new tenant panel “Home” page at /admin/t/{tenant}.
Clarifications
Session 2026-02-07
- Q: Should management pages be viewable for workspace members without manage capabilities? → A: Yes. Management pages are viewable for workspace members; only mutations are capability-gated (403).
- Q: Should this feature include a dedicated tenant panel “Home” page at
/admin/t/{tenant}? → A: No. Do not add a new tenant home page in this feature. - Q: For workspace-managed routes like
/admin/tenants/{tenant}, what should{tenant}be? → A:Tenant.external_id(Entra tenant GUID), same identifier used by Filament tenancy under/admin/t/{tenant}. - Q: Should “Managed Tenants” appear in Filament Global Search? → A: Yes, in the workspace panel only; tenant panel must not expose tenant-management entities in search.
- Q: For workspace-managed tenant management routes, what is the canonical URL shape? → A:
/admin/tenants*(workspace is selected in context/session; middleware enforces it), not/admin/w/{workspace}/tenants*.
User Scenarios & Testing (mandatory)
User Story 1 - Manage tenants from workspace scope (Priority: P1)
As a workspace member, I can manage (view/create/configure) managed tenants under /admin/tenants* without needing to enter tenant scope.
Why this priority: It removes the “henne-ei” context issue and makes /admin the canonical management surface.
Independent Test: Fully testable via HTTP requests asserting 200/404 and basic page visibility under /admin/tenants*.
Acceptance Scenarios:
- Given I am a workspace member, When I visit
/admin/tenants, Then I can access the Tenants management list. - Given I am a workspace member, When I visit
/admin/tenants/{tenant}, Then I can access the Tenant management overview. - Given I am not a workspace member, When I visit
/admin/tenantsor/admin/tenants/{tenant}, Then I receive a 404 (deny-as-not-found).
User Story 2 - Operate tenant modules only when entitled (Priority: P2)
As a user, I can operate inside a managed tenant under /admin/t/{tenant}/* only when I’m entitled to that tenant.
Why this priority: It preserves tenant isolation and makes 404/403 semantics predictable.
Independent Test: Fully testable via an operational page route asserting 200 for entitled users and 404 for non-entitled users.
Acceptance Scenarios:
- Given I am a workspace member and entitled to the selected tenant, When I visit an operational route under
/admin/t/{tenant}/*, Then I can access it. - Given I am a workspace member but not entitled to the selected tenant, When I visit an operational route under
/admin/t/{tenant}/*, Then I receive a 404 (deny-as-not-found).
User Story 3 - Tenant-scoped management routes are removed (Priority: P3)
As a user, I cannot access tenant-scoped management CRUD routes anymore; they are removed and should not resolve.
Why this priority: It enforces IA separation via route registration (not just UI hiding).
Independent Test: Fully testable via HTTP 404 assertions for a set of removed routes.
Acceptance Scenarios:
- Given any user, When I request
/admin/t/{tenant}/provider-connections, Then I receive 404 because the route does not exist. - Given any user, When I request
/admin/t/{tenant}/required-permissions, Then I receive 404 because the route does not exist. - Given any user, When I request a tenant-scoped tenant-management route (e.g.
/admin/t/{tenant}/tenants/*), Then I receive 404 because the route does not exist.
User Story 4 - Management actions enforce capability semantics (Priority: P2)
As a workspace member, I can see management pages, but mutations are forbidden unless I have the required capability.
Why this priority: It preserves enterprise RBAC semantics (404 for non-membership; 403 for missing capability on mutation).
Independent Test: Test a representative management mutation and assert 403 when capability is missing.
Acceptance Scenarios:
- Given I am a workspace member without the relevant manage capability, When I attempt a management mutation (e.g., change role, set default connection), Then the server responds 403.
User Story 5 - Global search isolation (Priority: P2)
As a user, global search does not leak tenant management entities across scopes.
Why this priority: It prevents discovery leaks and aligns with deny-as-not-found semantics.
Independent Test: Test that workspace global search can find allowed tenants; tenant panel search does not expose tenant-management entities; non-members discover nothing.
Acceptance Scenarios:
- Given I am a workspace member, When I use workspace global search, Then I can find tenants I can access.
- Given I am in tenant panel, When I use global search, Then it does not expose tenant-management entities.
- Given I am not a workspace member, When I use global search, Then I do not discover tenant existence.
Edge Cases
- Direct navigation to removed tenant-scoped management URLs.
- Stale internal CTAs/links that previously pointed at
/admin/t/{tenant}management screens. - Tenant slug/identifier mismatch (requesting a tenant not belonging to the active workspace context).
- Cross-scope leakage via global search results or navigation items.
- Non-member users attempting to infer tenant/workspace existence.
Requirements (mandatory)
Constitution alignment (required): If this feature introduces any Microsoft Graph calls, any write/change behavior,
or any long-running/queued/scheduled work, the spec MUST describe contract registry updates, safety gates
(preview/confirmation/audit), tenant isolation, run observability (OperationRun type/identity/visibility), and tests.
If security-relevant DB-only actions intentionally skip OperationRun, the spec MUST describe AuditLog entries.
Constitution alignment (RBAC-UX): If this feature introduces or changes authorization behavior, the spec MUST:
- state which authorization plane(s) are involved (tenant
/admin/t/{tenant}vs platform/system), - ensure any cross-plane access is deny-as-not-found (404),
- explicitly define 404 vs 403 semantics:
- non-member / not entitled to tenant scope → 404 (deny-as-not-found)
- member but missing capability → 403
- describe how authorization is enforced server-side (Gates/Policies) for every mutation/operation-start/credential change,
- reference the canonical capability registry (no raw capability strings; no role-string checks in feature code),
- ensure global search is tenant-scoped and non-member-safe (no hints; inaccessible results treated as 404 semantics),
- ensure destructive-like actions require confirmation (
->requiresConfirmation()), - include at least one positive and one negative authorization test, and note any RBAC regression tests added/updated.
Constitution alignment (OPS-EX-AUTH-001): OIDC/SAML login handshakes may perform synchronous outbound HTTP (e.g., token exchange)
on /auth/* endpoints without an OperationRun. This MUST NOT be used for Monitoring/Operations pages.
Constitution alignment (BADGE-001): If this feature changes status-like badges (status/outcome/severity/risk/availability/boolean), the spec MUST describe how badge semantics stay centralized (no ad-hoc mappings) and which tests cover any new/changed values.
Functional Requirements
Principles (normative):
- “Tenants” means the workspace-managed tenants list (CRUD under
/admin/tenants*). - “Switch tenant” is a context action in tenant scope; it must not behave like a CRUD list.
- Management must not depend on being in tenant scope; tenant scope must not expose tenant administration CRUD.
Routing / IA (normative):
- Workspace management is canonical under
/admin/*:/admin/tenants(list/create)/admin/tenants/{tenant}(manage overview)/admin/tenants/{tenant}/memberships/admin/tenants/{tenant}/provider-connections/admin/tenants/{tenant}/required-permissions/admin/tenants/{tenant}/onboarding(optional entry)/admin/operationsand/admin/operations/{run}(monitoring)
- Tenant scope is operate-only under
/admin/t/{tenant}/*(inventory, drift, policies, backups/restore, directory, etc.).
Workspace context:
- Workspace-scoped management routes MUST use the
/admin/tenants*shape and rely on the selected workspace context (e.g., middleware) rather than including{workspace}in every management URL.
Route parameter identity (normative):
{tenant}in both workspace-managed routes (/admin/tenants/{tenant}/*) and tenant-scoped routes (/admin/t/{tenant}/*) MUST refer to the managed tenant identifierTenant.external_id(Entra tenant GUID).
Authorization semantics (mandatory):
- Non-member / not entitled to scope: 404 (deny-as-not-found).
- Member lacking capability: 403 for management mutations.
- Workspace management pages are viewable for workspace members even without manage capabilities; only mutations are capability-gated.
- All checks are server-side via Policies/Gates, using the canonical capability registry (no raw strings).
Panel structure (native):
- Two Filament panels are used:
- Workspace panel is tenantless and mounted at
/admin. - Tenant panel is tenancy-mounted at
/admin/t/{tenant}.
- Workspace panel is tenantless and mounted at
- Each resource/page is registered only in the appropriate panel.
Global search isolation (mandatory):
- Global search behavior is defined normatively in FR-080-014; this section captures intent only (no cross-scope discovery leaks).
Route removal (dev-stage requirement):
- Tenant-scoped management routes must not exist after migration (expected 404):
/admin/t/{tenant}/provider-connections*/admin/t/{tenant}/required-permissions*/admin/t/{tenant}/memberships*/admin/t/{tenant}/tenants*(any management tenancy list/view/edit)
FR-080-001: The system MUST expose tenant administration surfaces only under workspace scope (/admin/tenants*).
FR-080-002: The system MUST expose tenant operations only under tenant scope (/admin/t/{tenant}/*).
FR-080-003: All workspace management pages MUST require active workspace context + workspace membership; non-member returns 404.
FR-080-004: All tenant operational routes MUST require workspace membership + tenant entitlement; non-entitled returns 404.
FR-080-005: Management mutations MUST return 403 when capability is missing (in addition to any UI disabling).
Management surfaces (workspace-scoped):
FR-080-006: Tenants list and tenant manage overview MUST exist under /admin/tenants and /admin/tenants/{tenant}.
FR-080-007: Provider Connections CRUD MUST exist only under /admin/tenants/{tenant}/provider-connections*.
FR-080-008: Required Permissions remediation UI MUST exist only under /admin/tenants/{tenant}/required-permissions and render DB-only.
FR-080-009: Tenant Memberships/Roles management MUST exist only under /admin/tenants/{tenant}/memberships and audit changes.
FR-080-010: Activation/onboarding controls MUST live under workspace-managed tenant pages/wizard entry (not in tenant scope).
Navigation (enterprise):
FR-080-011: Workspace navigation MUST include Tenants (manage) and Monitoring (Operations, Alerts, Audit Log).
FR-080-012: Tenant navigation MUST include only operational modules; no tenant CRUD entry appears in tenant sidebar.
FR-080-014: Workspace panel global search MAY return managed tenants only when the user can access them; tenant panel global search MUST NOT include tenant-management resources.
FR-080-015: Workspace-managed tenant management routes MUST be reachable under /admin/tenants* with a selected workspace context; /admin/w/{workspace} is not the canonical management route shape for this feature.
Filament constraint (hard rule): any globally searchable Resource MUST have an Edit or View page; otherwise global search will return no results.
Observability & Audit (minimal, mandatory): FR-080-013: The system MUST emit audit events for management mutations (tenant changes, role changes, provider connection CRUD/default selection, activation/onboarding state changes) with redacted fields only.
Key Entities (include if feature involves data)
- Workspace: Portfolio/customer context; primary security boundary for
/admin/*. - ManagedTenant: Workspace-owned representation of an Entra/Intune tenant (identified by Entra tenant GUID).
- TenantMembership: Assignment of a user to a managed tenant with a role (Owner/Manager/Operator/Readonly).
- ProviderConnection: Stored connection/config for provider integration, scoped to a managed tenant.
- Capability: Canonical capability registry entries used for authorization decisions.
- AuditLog entry: Append-only event record for management mutations (redacted).
Success Criteria (mandatory)
Measurable Outcomes
- SC-080-001: Tenant management surfaces are reachable only under
/admin/tenants*. - SC-080-002: Tenant operational surfaces are reachable only under
/admin/t/{tenant}/*. - SC-080-003: Tenant-scoped management routes are not registered and return 404.
- SC-080-004: Authorization semantics are consistent: non-member 404, missing capability yields 403 on mutations.
- SC-080-005: Internal CTAs/links to manage provider connections/required permissions point to workspace-managed routes.