TenantAtlas/specs/080-workspace-managed-tenant-admin/spec.md

15 KiB
Raw Blame History

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 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.