# 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**: 1. **Given** I am a workspace member, **When** I visit `/admin/tenants`, **Then** I can access the Tenants management list. 2. **Given** I am a workspace member, **When** I visit `/admin/tenants/{tenant}`, **Then** I can access the Tenant management overview. 3. **Given** I am not a workspace member, **When** I visit `/admin/tenants` or `/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**: 1. **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. 2. **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**: 1. **Given** any user, **When** I request `/admin/t/{tenant}/provider-connections`, **Then** I receive 404 because the route does not exist. 2. **Given** any user, **When** I request `/admin/t/{tenant}/required-permissions`, **Then** I receive 404 because the route does not exist. 3. **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**: 1. **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**: 1. **Given** I am a workspace member, **When** I use workspace global search, **Then** I can find tenants I can access. 2. **Given** I am in tenant panel, **When** I use global search, **Then** it does not expose tenant-management entities. 3. **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/operations` and `/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 identifier `Tenant.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}`. - 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.