12 KiB
#+#+#+#+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
- Tenant-owned tables impacted (must become explicitly workspace-bound):
- 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:
- Given tenant-owned records exist without a workspace binding, When the staged rollout completes, Then all tenant-owned records are workspace-bound.
- 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:
- Given a table contains rows missing workspace bindings, When the backfill is executed, Then those rows are updated to the correct workspace.
- 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:
- Given an audit log entry references a tenant, When it is persisted, Then it must also reference a workspace.
- 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.