TenantAtlas/specs/080-workspace-managed-tenant-admin/spec.md
ahmido 3f09fd50f6 feat(spec-080): workspace-managed tenant administration migration (#97)
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
2026-02-07 19:45:13 +00:00

239 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)*
<!--
IMPORTANT: User stories should be PRIORITIZED as user journeys ordered by importance.
Each user story/journey must be INDEPENDENTLY TESTABLE - meaning if you implement just ONE of them,
you should still have a viable MVP (Minimum Viable Product) that delivers value.
Assign priorities (P1, P2, P3, etc.) to each story, where P1 is the most critical.
Think of each story as a standalone slice of functionality that can be:
- Developed independently
- Tested independently
- Deployed independently
- Demonstrated to users independently
-->
### 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 Im 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.