TenantAtlas/specs/068-workspace-foundation-v1/spec.md
2026-02-01 00:50:44 +01:00

219 lines
16 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 Foundation & Managed Tenant Onboarding Unification (v1)
**Feature Branch**: `068-workspace-foundation-v1`
**Created**: 2026-01-31
**Status**: Draft
**Input**: Consolidate Managed Tenant onboarding and routing into a single admin “front door”, remove tenant-in-tenant patterns, and standardize RBAC UX semantics for Managed Tenant flows.
## Clarifications
### Session 2026-01-31
- Q: Should `tenant_managed_tenants.open` be a separate capability? → A: No. “Open” is allowed whenever `tenant_managed_tenants.view` is allowed.
- Q: What is the canonical destination for “Open” (for an active managed tenant) in v1? → A: A stable tenantless landing page in the admin area (e.g. `/admin/managed-tenants/current`) that shows the selected managed tenant, with the selection stored in session.
- Q: Where should the “current managed tenant” selection be stored in v1? → A: Session-only.
- Q: What should be the canonical onboarding URL/path in v1? → A: `/admin/managed-tenants/onboarding`.
- Q: For archived/deactivated tenants, should “View” always be allowed for viewers? → A: Yes. Archived tenants remain viewable for users with `tenant_managed_tenants.view`.
## 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 - Add managed tenant via single front door (Priority: P1)
As an admin user, I can add a new managed tenant through one clearly labeled entry point (“Add managed tenant”), so there is no ambiguity about where onboarding begins.
**Why this priority**: This removes the current confusion and is the prerequisite for a future wizard-based onboarding experience.
**Independent Test**: Can be fully tested by verifying only one visible “Add managed tenant” entry exists, and legacy direct URLs redirect to the canonical onboarding screen.
**Acceptance Scenarios**:
1. **Given** I can access the admin panel, **When** I look for a way to add a managed tenant, **Then** there is exactly one visible entry point labeled “Add managed tenant”.
2. **Given** I open a legacy onboarding URL directly (e.g., the previously used “/admin/new”), **When** the page loads, **Then** I am redirected to the canonical onboarding screen.
---
### User Story 2 - Manage tenants without tenant-in-tenant navigation (Priority: P2)
As an admin user, I can list, view, and manage managed tenants in a tenantless admin area, so the system does not require an already-selected tenant context to manage tenants.
**Why this priority**: Eliminates tenant-in-tenant logic and removes a large source of inconsistent access/404 semantics.
**Independent Test**: Can be fully tested by verifying there is a tenantless managed-tenant listing and that links/routes do not require any “current tenant” parameter.
**Acceptance Scenarios**:
1. **Given** I have access to the admin panel, **When** I navigate to the managed tenants list, **Then** the list loads without requiring a pre-selected tenant context.
2. **Given** I open any managed-tenant detail screen via URL, **When** the page loads, **Then** the URL does not include a “current tenant” path prefix.
---
### User Story 3 - Open an active tenant context and handle archived tenants safely (Priority: P3)
As an admin user, I can “Open” a managed tenant to work within its context, and if a managed tenant is archived/deactivated I get a clear status screen instead of a broken experience.
**Why this priority**: Prevents confusing 404s and ensures consistent, safe operations as tenants change lifecycle state.
**Independent Test**: Can be fully tested by verifying “Open” is deterministic for active tenants, and for archived tenants it shows a status screen with only allowed actions.
**Acceptance Scenarios**:
1. **Given** a managed tenant is active and I am allowed to open it, **When** I click “Open”, **Then** I land on a stable tenantless screen that clearly indicates which managed tenant is selected.
2. **Given** a managed tenant is archived/deactivated, **When** I click “Open”, **Then** I see a status screen explaining the tenant is archived (not a 404).
3. **Given** a managed tenant is archived/deactivated and I do not have permission to restore or force delete it, **When** I view the status screen, **Then** those actions are not executable.
---
### Edge Cases
- Attempting to access managed-tenant screens as a non-member of the tenant-plane scope.
- Attempting to execute a mutation (create/edit/archive/restore/delete) without the necessary capability.
- A managed tenant transitions to archived/deactivated while a user has an open browser tab.
- Legacy deep links to the old tenant-scoped managed-tenant routes.
- A user opens the admin panel with no “current managed tenant” selected yet.
## 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.
<!--
ACTION REQUIRED: The content in this section represents placeholders.
Fill them out with the right functional requirements.
-->
### Functional Requirements
#### Scope, Definitions, Non-goals
- **FR-001 (Scope / Plane)**: The feature MUST apply only to the admin tenant-plane area and MUST NOT change platform/system-plane behavior.
- **FR-002 (Terminology)**: The UI MUST use the term “Managed tenant” (or an equivalent unambiguous label) in places where “Tenant” could be confused with a workspace or platform tenant concept.
- **FR-003 (Non-goals enforced)**: This version MUST NOT introduce a wizard/stepper onboarding UI, a new workspace membership model, or a provider/credentials architecture refactor.
#### Single Front Door
- **FR-004 (Canonical onboarding entry)**: The system MUST provide exactly one visible UI entry point to start managed-tenant onboarding (“Add managed tenant”), leading to a canonical onboarding screen.
- **FR-004a (Canonical onboarding path)**: The canonical onboarding screen MUST be reachable at `/admin/managed-tenants/onboarding`.
- **FR-005 (Legacy entry points)**: Legacy onboarding entry points MUST be removed from navigation/buttons, or MUST redirect to the canonical onboarding screen.
- **FR-006 (Direct URL behavior)**: Direct requests to the legacy onboarding URL (previously used “/admin/new”) MUST redirect to the canonical onboarding screen.
#### Tenantless Managed Tenants Area (No tenant-in-tenant)
- **FR-007 (Tenantless routes)**: Listing, viewing, and editing managed tenants MUST be available without requiring a “current tenant” context in the URL.
- **FR-008 (No tenant-in-tenant)**: Managed-tenant management MUST NOT appear under a tenant-scoped URL structure (e.g., MUST NOT require “/t/{currentTenant}/…” style path segments).
#### “Open” vs “Switch” semantics
- **FR-009 (Open semantics)**: “Open” on a managed tenant MUST mean “enter this managed tenants context within the admin area”, not “switch workspaces”.
- **FR-010 (Deterministic destination)**: “Open” MUST take the user to a stable, deterministic destination that clearly indicates the selected managed tenant.
- **FR-011 (No tenant-in-tenant link generation)**: Links used for “Open” MUST NOT rely on a tenant-scoped routing prefix.
- **FR-011a (Open authorization)**: “Open” MUST be authorized by `tenant_managed_tenants.view` in v1 (no separate “open” capability).
- **FR-011b (Open destination)**: In v1, “Open” MUST navigate to a stable tenantless destination in the admin area (e.g. `/admin/managed-tenants/current`), using a session-based “current managed tenant” selection.
- **FR-011c (Selection storage)**: In v1, the “current managed tenant” selection MUST be stored in server-side session (no required DB persistence).
#### Archived / Deactivated Managed Tenants UX
- **FR-012 (Archived view access)**: Archived/deactivated managed tenants MUST remain viewable (read-only) for users authorized by `tenant_managed_tenants.view`.
- **FR-012a (No 404 for viewers)**: Archived/deactivated managed tenants MUST NOT present a “not found” experience solely due to being archived/deactivated when the user is authorized to view managed tenants.
- **FR-013 (Archived open behavior)**: “Open” on an archived/deactivated managed tenant MUST NOT result in a “not found” experience; it MUST show a dedicated status screen explaining the tenant is archived/deactivated.
- **FR-014 (Archived actions)**: The archived/deactivated status screen MUST present only actions that are allowed for the current user (e.g., restore, force delete), and MUST prevent execution when not allowed.
#### RBAC UX and Enforcement
- **FR-015 (404 vs 403 semantics)**: Authorization behavior MUST follow these rules:
- Non-member / not entitled to tenant-plane scope → treated as “not found” in user experience.
- Member but missing capability → forbidden (action visible but disabled where appropriate; execution prevented).
- **FR-016 (Consistent UI affordances)**: In managed-tenant flows, actions MUST consistently follow these UX rules:
- Non-member: action not shown and not executable.
- Member without capability: action visible but disabled with an explanatory tooltip; not executable.
- Member with capability: action enabled.
- **FR-017 (Server-side defense-in-depth)**: All mutations and operation-start actions in the managed-tenant area MUST be rejected server-side when the user lacks authorization.
#### Capabilities (Canonical)
- **FR-018 (Capability registry)**: Managed-tenant functionality MUST be controlled via canonical capabilities (no ad-hoc permission checks).
- **FR-019 (Minimal capabilities set)**: The system MUST define, at minimum, the following capabilities (names are canonical):
- `tenant_managed_tenants.view`
- `tenant_managed_tenants.create`
- `tenant_managed_tenants.manage`
- `tenant_managed_tenants.archive`
- `tenant_managed_tenants.restore`
- `tenant_managed_tenants.force_delete`
- **FR-020 (Default role mapping)**: Default role-to-capability mapping MUST align with:
- Owner/Manager: all managed-tenant capabilities.
- Operator: view (includes “Open”) and tenant-context operations where permitted; not create/manage/archive/force delete.
- Readonly: view only.
#### Backwards Compatibility
- **FR-021 (Redirect compatibility)**: Legacy managed-tenant entry points and legacy deep links MUST redirect to the new tenantless managed-tenant area when reachable.
- **FR-022 (No data loss)**: The feature MUST NOT delete or irreversibly modify existing managed-tenant data.
#### Acceptance Criteria Summary
- Exactly one visible “Add managed tenant” entry exists in the admin UI.
- Direct requests to the legacy onboarding URL redirect to the canonical onboarding screen.
- Managed-tenant management is available without any “current tenant” path prefix.
- “Open” leads to a stable tenant-context destination for active managed tenants.
- “Open” for archived/deactivated managed tenants shows a dedicated status screen (not “not found”).
- Authorization outcomes match the defined 404-vs-403 semantics and actions are never executable without the required capability.
- The canonical managed-tenant capabilities exist and role mapping matches the default expectations.
- No managed-tenant data is deleted or irreversibly changed by this feature.
#### Assumptions & Dependencies
- A managed tenant entity already exists with a lifecycle indicator (active vs archived/deactivated) that can be used to drive UX decisions.
- The admin interface can represent a “current managed tenant” selection (even if it is not persisted long-term in v1).
- In v1, the selection is session-only (not stored in the database).
- A canonical capability registry exists (or can be extended) to hold the managed-tenant capabilities.
- Legacy deep links may exist in bookmarks and documentation and therefore must remain reachable via redirects.
### Key Entities *(include if feature involves data)*
- **Workspace (v1 minimal)**: An organizational container in the admin interface used to structure managed-tenant management. In v1 it is a routing/UI concept and does not introduce a new membership model.
- **Managed Tenant**: A Microsoft Entra/Intune tenant managed by the product, including identity, consent/connection state, and lifecycle state (active vs archived/deactivated).
- **Tenant Context (admin)**: The “current selection” of a managed tenant that determines what data/actions the admin interface displays.
- **Capability**: A permission unit used to allow/deny actions and UI affordances for managed-tenant flows.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001 (Single entry point)**: 100% of visible “add managed tenant” journeys originate from the single canonical entry point (no second visible onboarding CTA in the admin UI).
- **SC-002 (No tenant-in-tenant)**: 0 managed-tenant CRUD routes require a “current tenant” path prefix.
- **SC-003 (Archived UX)**: 100% of attempts to “Open” an archived/deactivated managed tenant result in a defined status screen (never a “not found” experience for authorized viewers).
- **SC-004 (RBAC semantics)**: For managed-tenant flows, authorization outcomes are consistent and testable: non-members receive “not found” experience, members without capability are blocked from execution.
- **SC-005 (Task completion)**: A user with the appropriate capability can start managed-tenant onboarding and reach the onboarding screen successfully on the first attempt in under 30 seconds.