Implements workspace-first enforcement and UX: - Workspace selected before tenant flows; /admin routes into choose-workspace/choose-tenant - Tenant lists and default tenant selection are scoped to current workspace - Workspaces UI is tenantless at /admin/workspaces Security hardening: - Workspaces can never have 0 owners (blocks last-owner removal/demotion) - Blocked attempts are audited with action_id=workspace_membership.last_owner_blocked + required metadata - Optional break-glass recovery page to re-assign workspace owner (audited) Tests: - Added/updated Pest feature tests covering redirects, scoping, tenantless workspaces, last-owner guards, and break-glass recovery. Notes: - Filament v5 strict Page property signatures respected in RepairWorkspaceOwners. Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box> Reviewed-on: #86
2.1 KiB
2.1 KiB
Spec — 072 Managed Tenants workspace context enforcement
Problem
Managed Tenant pages exist in an unscoped URL space (/admin/managed-tenants/*) while Managed Tenants are product-scoped to a Workspace (MSP portfolio). This makes workspace context feel optional and allows confusing / insecure navigation patterns where tenant context and workspace context can drift.
Mental model (source of truth)
- Managed Tenant = the Entra/Intune tenant. All policy/backup/drift/inventory features are always scoped to a Managed Tenant.
- In code: Filament tenancy (
/admin/t/{tenant_external_id}/...).
- In code: Filament tenancy (
- Workspace = portfolio container. Controls which Managed Tenants a user can see + portfolio-level settings.
- In code: session +
last_workspace_id+ middleware (not Filament tenancy).
- In code: session +
Goals
- Workspace becomes a real, enforced context for all tenant-scoped pages.
- Keep Filament tenancy URL space unchanged:
/admin/t/{tenant_external_id}/.... - Keep Workspaces UI tenantless:
/admin/workspaces/*(never under/admin/t/{tenant}/...). - Introduce / use a workspace-scoped landing space for portfolio UX:
/admin/w/{workspace}/.... - Eliminate or redirect legacy unscoped Managed Tenants routes under
/admin/managed-tenants/*.
Non-goals
- Redesigning all navigation IA or introducing a second Filament panel.
- Migrating existing tenant data beyond enforcing
tenants.workspace_idconsistency.
Hard rule (security / enterprise)
When accessing /admin/t/{tenant} routes:
current_workspace_idmust be set, andtenant.workspace_id == current_workspace_id, and- user must be a member of the workspace (and/or tenant, per current auth model). Otherwise: deny as not found (404).
Acceptance criteria
/admin/managed-tenants/*does not act as a primary UX entry point anymore (redirects to workspace-scoped UX)./admin/w/{workspace}/managed-tenantsexists as the primary portfolio landing for Managed Tenants.- Tenant switcher only shows tenants from the current workspace.
- Visiting
/admin/t/{tenant}with missing or mismatched workspace context results in 404. - Pest tests cover redirects + workspace/tenant mismatch denial.