TenantAtlas/specs/108-provider-access-hardening/data-model.md
Ahmed Darrazi 12973248e7 feat: provider access hardening (RBAC write gate)
Implements RBAC-based write gating for Intune restore flows, UI affordances, and audit logging; adds tests and specs.
2026-02-23 01:20:28 +01:00

2.8 KiB

Data Model — Provider Access Hardening v1 (Intune Write Gate)

Entities (existing)

Tenant

Used as the persisted source of truth for the gate.

  • tenants.rbac_status (nullable string)
    • Expected values used by this feature: null, not_configured, ok, degraded, failed
  • tenants.rbac_status_reason (nullable string)
    • Human-readable/safe explanation from last check / setup.
  • tenants.rbac_last_checked_at (nullable timestamp)
    • Used for freshness evaluation.

Relationships:

  • Tenant → hasMany AuditLog
  • Tenant → hasMany OperationRun (tenant-scoped runs)

OperationRun

Used to record outcomes for queued/async work.

  • operation_runs.type
    • Existing types touched by this feature (no new types introduced):
      • restore.execute
      • assignments.restore
      • health checks: reuse existing verification/provider connection check type(s)
  • operation_runs.failures / failure metadata
    • Must include stable reason codes when gate blocks a queued job.

RestoreRun

Represents the restore workflow state and links to an OperationRun.

  • restore_runs.operation_run_id (FK to operation_runs)
  • restore_runs.status transitions drive UX and notifications.

AuditLog

Used to record UI-level blocked write attempts (P3 in spec).

  • Must store stable action (e.g., intune_rbac.write_blocked)
  • Must store tenant scope and sanitized metadata (no tokens, no raw Graph payloads)

Domain Objects (new / feature-scoped)

IntuneRbacWriteGate (service)

  • Inputs: Tenant, operation identifier (string), “write class” operation
  • Output: allowed or throws a domain exception containing:
    • reason_code: one of
      • intune_rbac.not_configured
      • intune_rbac.unhealthy
      • intune_rbac.stale
    • reason_message: sanitized, user-facing message

Freshness rule:

  • Evaluate staleness by comparing tenant.rbac_last_checked_at to now() - threshold.
  • Threshold is configurable (default 24 hours).

Config toggle:

  • If disabled, gate always allows but logs a warning per evaluation (observability).

ProviderAccessHardeningRequired (exception)

  • Carries tenant + operation + reason code + safe message.
  • Used by both start surfaces (to present UX) and jobs (to fail OperationRun safely).

State transitions (gate perspective)

  • Allowed: rbac_status = ok AND rbac_last_checked_at is within freshness threshold.
  • Blocked — Not configured: rbac_status is null OR rbac_status = not_configured.
  • Blocked — Unhealthy: rbac_status in {degraded, failed}.
  • Blocked — Stale: rbac_status = ok but rbac_last_checked_at is older than threshold.

Validation & safety rules

  • Gate evaluation must be DB-only (no Graph calls).
  • Blocked paths must ensure zero Graph write calls.
  • Failure metadata must be sanitized and stable.