219 lines
16 KiB
Markdown
219 lines
16 KiB
Markdown
# 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 tenant’s 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.
|