#+#+#+#+markdown # Feature Specification: SCOPE-001 Workspace ID Isolation **Feature Branch**: `093-scope-001-workspace-id-isolation` **Created**: 2026-02-14 **Status**: Draft **Input**: Enforce workspace isolation by binding all tenant-owned records to a workspace, with a safe staged rollout and corrected audit invariants. ## Spec Scope Fields *(mandatory)* - **Scope**: workspace - **Primary Routes**: No new pages/routes. Affects all create/write paths that persist tenant-owned records in the tables listed below, plus operational tooling for a one-time backfill. - **Data Ownership**: - Tenant-owned tables impacted (must become explicitly workspace-bound): - policies - policy_versions - backup_sets - backup_items - restore_runs - backup_schedules - inventory_items - inventory_links - entra_groups - findings - entra_role_definitions - tenant_permissions - Audit trail invariants impacted: audit_logs - **RBAC**: No user-facing permissions change. Backfill execution is an operator-only workflow (platform/ops). ## Clarifications ### Session 2026-02-14 - Q: For the 12 tenant-owned tables, how strict should deterministic workspace binding be when a caller explicitly provides a workspace binding? → A: Reject any mismatch; if a record references a tenant, its workspace binding MUST equal the tenant’s workspace. - Q: During backfill, what should happen if a row’s tenant reference is invalid (tenant missing / cannot map tenant → workspace)? → A: Abort the backfill run and report the offending table plus sample identifiers for remediation. - Q: Should tenant_id be allowed to change on existing rows in the 12 tenant-owned tables? → A: No; tenant_id is immutable and updates are rejected. - Q: How should the backfill workflow be recorded for observability/audit? → A: Create/reuse an OperationRun for the backfill and also write an AuditLog summary for start/end/outcome. - Q: Should this feature include updating existing canonical/operational queries/views to scope by workspace binding? → A: No; query/view changes are out of scope for 093 (follow-up later). ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Enforce workspace ownership at the data layer (Priority: P1) As a platform owner, I want every tenant-owned record to be explicitly bound to a workspace so that workspace isolation does not depend on application-only guardrails. **Why this priority**: This closes a class of potential cross-workspace data leakage and makes operational reporting safer and simpler. **Independent Test**: Can be tested by creating a tenant-owned record with a tenant reference and asserting that `workspace_id` is derived from the tenant and that any explicit mismatched `workspace_id` is rejected; backfill behavior is validated in User Story 2. **Acceptance Scenarios**: 1. **Given** tenant-owned records exist without a workspace binding, **When** the staged rollout completes, **Then** all tenant-owned records are workspace-bound. 2. **Given** the rollout is in progress, **When** the system is used normally, **Then** there is no required downtime for administrators. --- ### User Story 2 - Safely backfill existing production data (Priority: P2) As an operator, I want a safe, resumable way to backfill missing workspace bindings so that large datasets can be migrated without risky one-shot operations. **Why this priority**: Without a safe backfill, enforcing data-level constraints risks outages and operational incidents. **Independent Test**: Can be tested by seeding rows with missing workspace bindings, running the backfill workflow twice, and confirming idempotent outcomes. **Acceptance Scenarios**: 1. **Given** a table contains rows missing workspace bindings, **When** the backfill is executed, **Then** those rows are updated to the correct workspace. 2. **Given** the backfill is interrupted and restarted, **When** it runs again, **Then** it resumes safely without corrupting already-correct rows. --- ### User Story 3 - Make audit logs unambiguous across scopes (Priority: P3) As an auditor, I want tenant-scoped audit events to always be workspace-bound so that workspace isolation semantics are preserved in audit trails. **Why this priority**: Audit trails are only reliable if their scope is structurally consistent and queryable. **Independent Test**: Can be tested by attempting to store a tenant-scoped audit entry without a workspace binding and verifying it is rejected, while allowing workspace-only and platform-only entries. **Acceptance Scenarios**: 1. **Given** an audit log entry references a tenant, **When** it is persisted, **Then** it must also reference a workspace. 2. **Given** an audit log entry is workspace-only or platform-only, **When** it is persisted, **Then** it remains valid according to the documented rules. --- ### Edge Cases - Tenant-owned row references a tenant that no longer exists (or is otherwise invalid). - Backfill is executed concurrently (must not produce conflicting outcomes). - New rows are created while the backfill is in progress (must not reintroduce missing bindings). - Partial completion: some tables are fully backfilled while others are pending. - Mixed-scope audit events: tenant-scoped vs workspace-only vs platform-only. - Attempt to change tenant_id on an existing tenant-owned record. ## 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 `/admin` + tenant-context `/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 workspace scope OR 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. **Constitution alignment (Filament Action Surfaces):** If this feature adds or modifies any Filament Resource / RelationManager / Page, the spec MUST include a “UI Action Matrix” (see below) and explicitly state whether the Action Surface Contract is satisfied. If the contract is not satisfied, the spec MUST include an explicit exemption with rationale. ### Functional Requirements - **FR-001 (Schema coverage)**: The system MUST represent a workspace binding for every tenant-owned record in the 12 listed tables. - **FR-002 (No missing bindings post-rollout)**: After completion of the rollout, the system MUST prevent creation or persistence of tenant-owned records without a workspace binding. - **FR-003 (Deterministic binding for new writes)**: For any new or updated tenant-owned record, the system MUST derive the workspace binding from the referenced tenant (not from user/session context alone). - **FR-003a (Mismatch handling)**: If a caller provides a workspace binding that does not match the referenced tenant’s workspace, the write MUST be rejected. - **FR-004 (Safe staged rollout)**: The system MUST support a staged rollout that allows introducing the new field, deploying write-path enforcement, backfilling existing data, and only then enforcing strict constraints. - **FR-005 (Idempotent backfill)**: Operators MUST be able to re-run the backfill safely; repeated runs MUST not regress already-correct data. - **FR-006 (Operational safety)**: The backfill workflow MUST be safe for large datasets (batching/resume) and MUST prevent concurrent executions. - **FR-006a (Invalid mapping handling)**: If the backfill workflow encounters a tenant-owned row that cannot be mapped from tenant → workspace, it MUST abort and report the offending table and sample identifiers for operator remediation. - **FR-007 (Validation)**: Operators MUST be able to validate that no tenant-owned records remain without a workspace binding before strict constraints are enforced. - **FR-011 (Tenant immutability)**: For tenant-owned records, tenant identity MUST be immutable after creation; attempts to change tenant_id MUST be rejected. - **FR-012 (Backfill observability and audit)**: The backfill workflow MUST be observable via an OperationRun (progress + outcome) and MUST write an audit log summary entry for start/end/outcome. - **FR-013 (Query/view scope)**: This feature MUST NOT require broad refactors of canonical/operational queries; it MUST focus on making workspace scoping structurally correct at the data layer. - **FR-008 (Audit log invariant)**: If an audit log entry references a tenant, it MUST also reference a workspace. - **FR-009 (Audit scope flexibility)**: The audit log MUST continue to support workspace-only events (workspace present, tenant absent) and platform-only events (both absent). - **FR-010 (Canonical view scoping readiness)**: After rollout, operational/canonical queries SHOULD be able to scope tenant-owned data by workspace without relying on implicit joins or assumptions. ### Assumptions & Dependencies - Each tenant belongs to exactly one workspace, and that mapping is the source of truth for deriving workspace bindings. - The 12 listed tables are “tenant-owned” per SCOPE-001 and are expected to remain tenant-owned. - Any existing tooling that creates tenant-owned records has enough information to reference the tenant (directly or indirectly) at write time. ### Key Entities *(include if feature involves data)* - **Workspace**: The top-level isolation boundary for data access and operations. - **Tenant**: A unit of configuration/data that is owned by exactly one workspace. - **Tenant-owned record**: Any record that must be both tenant-scoped and workspace-scoped. - **Audit event**: An immutable entry describing an action/event, which may be tenant-scoped, workspace-scoped, or platform-scoped. - **Backfill run**: A controlled operational execution that updates legacy data and reports progress/outcomes. ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-001 (Completeness)**: 100% of records in the 12 tenant-owned tables have a workspace binding after backfill and validation. - **SC-002 (Integrity)**: After strict constraints are enabled, creating a tenant-owned record without a workspace binding is rejected. - **SC-003 (Audit correctness)**: 100% of tenant-scoped audit events include a workspace binding; workspace-only and platform-only audit events remain valid. - **SC-004 (Operational safety)**: The rollout requires no planned downtime for administrators. - **SC-005 (Repeatability)**: Re-running the backfill after completion does not change already-correct records and can be used as a safety check.