Implements RBAC-based write gating for Intune restore flows, UI affordances, and audit logging; adds tests and specs.
85 lines
2.8 KiB
Markdown
85 lines
2.8 KiB
Markdown
# 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.
|