TenantAtlas/specs/108-provider-access-hardening/research.md
ahmido 0dc79520a4 feat: provider access hardening (RBAC write gate) (#132)
Implements provider access hardening for Intune write operations:

- RBAC-based write gate with configurable staleness thresholds
- Gate enforced at restore start and in jobs (execute + assignments)
- UI affordances: disabled rerun action, tenant RBAC status card, refresh RBAC action
- Audit logging for blocked writes
- Ops UX label: `rbac.health_check` now displays as “RBAC health check”
- Adds/updates Pest tests and SpecKit artifacts for feature 108

Notes:
- Filament v5 / Livewire v4 compliant.
- Destructive actions require confirmation.
- Assets: no new global assets.

Tested:
- `vendor/bin/sail artisan test --compact` (suite previously green) + focused OpsUx tests for OperationCatalog labels.
- `vendor/bin/sail bin pint --dirty`.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #132
2026-02-23 00:49:37 +00:00

68 lines
3.4 KiB
Markdown

# Research — Provider Access Hardening v1 (Intune Write Gate)
## Repository Reality Check (existing implementation surfaces)
### Write start surfaces
- Restore execution start (wizard submit / create path): `App\Filament\Resources\RestoreRunResource::createRestoreRun()`
- Creates a queued `RestoreRun` and a corresponding `OperationRun` of type `restore.execute`.
- Dispatches `App\Jobs\ExecuteRestoreRunJob` with the `OperationRun` instance.
- Restore rerun path (action path inside `RestoreRunResource`): also enqueues `restore.execute` and dispatches `ExecuteRestoreRunJob`.
### Job execution layer (defense-in-depth insertion points)
- `App\Jobs\ExecuteRestoreRunJob::handle()`
- Calls `RestoreService::executeForRun(...)` (this is the Graph-mutation path).
- `App\Jobs\RestoreAssignmentsJob::handle()`
- Calls `AssignmentRestoreService::restore(...)` (Graph-mutation path for assignments).
### Tenant view RBAC UX surfaces
- Tenant view page: `App\Filament\Resources\TenantResource::infolist()` currently renders a full "RBAC" section with many raw fields.
- Tenant view header actions: `App\Filament\Resources\TenantResource\Pages\ViewTenant::getHeaderActions()` includes `TenantResource::rbacAction()`.
- RBAC setup surface already exists: `App\Filament\Resources\TenantResource::rbacAction()` creates the "Setup Intune RBAC" action (no new wizard page required).
### Existing gating and observability primitives
- Operation dedupe + provider connection blocking: `App\Services\Providers\ProviderOperationStartGate` (existing pattern for start surfaces).
- Long-running work visibility: `OperationRun` is the canonical monitoring primitive.
- Audit logging: `App\Models\AuditLog` exists and `Tenant` already relates to it.
## Decisions (resolved clarifications)
### Gate evaluation during health check
- Decision: Gate evaluates persisted Tenant state only; an in-progress health check has no special effect.
- Rationale: Keeps the gate DB-only and deterministic; avoids introducing locks/sentinels.
- Alternatives considered:
- Block all writes while health check is running (safer but adds operational friction and requires tracking “running” state).
### "Setup Intune RBAC" CTA destination
- Decision: CTA deep-links to the existing Tenant view RBAC section / surfaces (no new wizard page in this feature).
- Rationale: Minimizes scope and leverages `TenantResource::rbacAction()` which already exists.
- Alternatives considered:
- Build a dedicated setup wizard page for RBAC (adds navigation, testing, and more UX requirements).
### Audit logging for blocked attempts
- Decision: Use existing `AuditLog` model for UI-level blocked write attempts (P3), in addition to OperationRun failures.
- Rationale: Meets compliance intent without introducing new schema.
- Alternatives considered:
- Create a new audit log table (unnecessary; model already exists).
### Config toggle behavior
- Decision: `tenantpilot.hardening.intune_write_gate.enabled=false` causes writes to proceed, but logs a warning per evaluation that the gate is bypassed.
- Rationale: Rollback safety without silent loss of protection.
- Alternatives considered:
- Silent bypass (bad observability).
- Startup-only warning (can be missed; per-evaluation warning is explicit).
## Key constraints reaffirmed
- No synchronous Graph calls in the gate.
- Start surface must block before job enqueue.
- Job-level must block before any Graph mutation call.
- Reason codes are stable and sanitized.