TenantAtlas/specs/061-provider-foundation/tasks.md
ahmido a0ed9e24c5 feat: unify provider connection actions and notifications (#73)
## Summary
- introduce the Provider Connection Filament resource (list/create/edit) with DB-only controls, grouped action dropdowns, and badge-driven status/health rendering
- wire up the provider foundation stack (migrations, models, policies, providers, operations, badges, and audits) plus the required spec docs/checklists
- standardize Inventory Sync notifications so the job no longer writes its own DB rows; terminal notifications now flow exclusively through OperationRunCompleted while the start surface still shows the queued toast

## Testing
- ./vendor/bin/sail php ./vendor/bin/pint --dirty
- ./vendor/bin/sail artisan test tests/Unit/Badges/ProviderConnectionBadgesTest.php
- ./vendor/bin/sail artisan test tests/Feature/ProviderConnections tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php
- ./vendor/bin/sail artisan test tests/Feature/Inventory/RunInventorySyncJobTest.php tests/Feature/Inventory/InventorySyncStartSurfaceTest.php

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #73
2026-01-25 01:01:37 +00:00

189 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
description: "Task list for Provider Foundation v1"
---
# Tasks: Provider Foundation v1 (Microsoft-first, Security-first)
**Input**: Design documents from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/`
**Prerequisites**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/plan.md` (required), `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/spec.md` (required), `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/research.md`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/data-model.md`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/contracts/`, `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/quickstart.md`
**Tests**: REQUIRED (Pest) for runtime behavior changes
**Operations**: Provider operations MUST create/reuse a canonical `OperationRun`, be enqueue-only, and keep Monitoring/ProviderConnections pages DB-only at render/poll time
**Badges**: If ProviderConnection status/health is rendered as a badge, use `BadgeCatalog` / `BadgeRenderer` (BADGE-001) and add mapping tests
**Organization**: Tasks are grouped by user story (US1, US2, US3) to enable independent delivery.
## Phase 1: Setup (Shared Infrastructure)
**Purpose**: Prepare local environment + validate baseline
- [X] T001 Start containers with `./vendor/bin/sail up -d` (script: `./vendor/bin/sail`)
- [X] T002 Run baseline Ops-UX DB-only tests with `./vendor/bin/sail artisan test tests/Feature/Monitoring/OperationsDbOnlyTest.php` (test: `tests/Feature/Monitoring/OperationsDbOnlyTest.php`)
- [X] T003 [P] Review existing idempotency + run tracking patterns in `app/Services/OperationRunService.php` and `app/Jobs/Middleware/TrackOperationRun.php`
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Core provider foundation that all stories depend on (schema, models, policies, ops primitives)
**Checkpoint**: Migrations + models + policies exist; provider operation types are registered; shared run-gating utilities exist
- [X] T004 Create provider connections migration in `database/migrations/2026_01_24_000001_create_provider_connections_table.php`
- [X] T005 Create provider credentials migration in `database/migrations/2026_01_24_000002_create_provider_credentials_table.php`
- [X] T005a Add DB-level invariant: partial unique index ensuring only one default per (tenant_id, provider) (example: `unique (tenant_id, provider) WHERE is_default = true`; ensure default flipping is atomic/transactional)
- [X] T006 [P] Create `App\Models\ProviderConnection` in `app/Models/ProviderConnection.php` (relations, casts, default-connection invariant)
- [X] T007 [P] Create `App\Models\ProviderCredential` in `app/Models/ProviderCredential.php` (encrypted payload cast, hidden attributes, 1:1 relation)
- [X] T008 [P] Add tenant relationship for connections in `app/Models/Tenant.php` (e.g., `providerConnections()` / `providerCredentials()` as needed)
- [X] T009 [P] Add factories in `database/factories/ProviderConnectionFactory.php` and `database/factories/ProviderCredentialFactory.php`
- [X] T010 Create authorization policy + gates for `provider.view`, `provider.manage`, `provider.run` (capabilities-first; tenant-scoped). Map roles to capabilities: Owner/Manager=view+manage+run; Operator=view+run; Readonly=view only. No `role == X` checks in feature code.
- [X] T010b [P] Add RBAC capability tests in `tests/Feature/ProviderConnections/ProviderRbacCapabilitiesTest.php` (Operator can start operations but cannot manage; Readonly is view-only)
- [X] T011 Register the policy + gates in `app/Providers/AuthServiceProvider.php` (create it and register in `bootstrap/providers.php` if missing; otherwise use the projects canonical policy registration provider)
- [X] T012 Add provider operation labels in `app/Support/OperationCatalog.php` using canonical operation types: `provider.connection.check`, `inventory.sync`, `compliance.snapshot` (ensure UI/actions/jobs use these exact strings; update `tests/Feature/OpsUx/OperationCatalogCoverageTest.php` to detect multi-dot types)
- [X] T013 Add provider related links in `app/Support/OperationRunLinks.php` (link provider runs to Provider Connections pages when `context.provider_connection_id` exists)
- [X] T014 Create provider operation registry in `app/Services/Providers/ProviderOperationRegistry.php` (central allowlist + metadata for v1 operations)
- [X] T014a [P] Define provider capability interfaces + DTOs in `app/Services/Providers/Contracts/ProviderHealthCheck.php`, `app/Services/Providers/Contracts/HealthResult.php`, `app/Services/Providers/Contracts/ProviderInventoryCollector.php`, `app/Services/Providers/Contracts/ProviderComplianceCollector.php`, `app/Services/Providers/Contracts/ProviderDirectoryCollector.php`, `app/Services/Providers/Contracts/ProviderScriptExecutor.php`
- [X] T014b [P] Implement credential retrieval/rotation service in `app/Services/Providers/CredentialManager.php` (reads `provider_credentials`, validates required keys, never logs decrypted payload)
- [X] T014c [P] Implement provider gateway in `app/Services/Providers/ProviderGateway.php` (build Graph request context from ProviderConnection + CredentialManager, centralize correlation IDs + failure mapping, call `GraphClientInterface`)
- [X] T014d Enable Graph client binding for per-request credentials in `config/graph.php` and `app/Providers/AppServiceProvider.php` (e.g., `GRAPH_ENABLED` override; do not require env client_secret for binding when ProviderGateway supplies request context)
- [X] T014e [P] Add unit tests for provider gateway + credential manager in `tests/Unit/Providers/CredentialManagerTest.php` and `tests/Unit/Providers/ProviderGatewayTest.php`
- [X] T015 Create run gating service in `app/Services/Providers/ProviderOperationStartGate.php` (DB transaction + `lockForUpdate()` on ProviderConnection; dedupe same-operation; block different-operation as “scope busy”; returns active-run link)
- [X] T016 [P] Extend failure sanitization in `app/Support/OpsUx/RunFailureSanitizer.php` (provider auth/throttling/outage reason codes + stronger secret redaction)
- [X] T017 [P] Add unit tests for failure sanitization in `tests/Unit/OpsUx/RunFailureSanitizerTest.php`
- [X] T018 [P] Add unit tests for run gating in `tests/Unit/Providers/ProviderOperationStartGateTest.php`
- [X] T019 Run foundational tests with `./vendor/bin/sail artisan test tests/Unit/OpsUx/RunFailureSanitizerTest.php` (test: `tests/Unit/OpsUx/RunFailureSanitizerTest.php`)
---
## Phase 3: User Story 1 — Set up a provider connection safely (Priority: P1) 🎯 MVP
**Goal**: Owner/Manager can create/manage Microsoft provider connections, attach credentials, set a default connection, and never expose secrets.
**Independent Test**: An Owner/Manager can create a connection + credentials, later view/edit it, and no secret values are displayed or leaked; pages remain DB-only at render/poll time.
### Tests for User Story 1
- [X] T020 [P] [US1] Add DB-only render test for Provider Connections in `tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php`
- [X] T021 [P] [US1] Add role authorization test in `tests/Feature/ProviderConnections/ProviderConnectionAuthorizationTest.php`
- [X] T022 [P] [US1] Add credential encryption/non-disclosure test in `tests/Feature/ProviderConnections/ProviderCredentialSecurityTest.php`
- [X] T022b [P] [US1] Add credential leak guard test in `tests/Feature/ProviderConnections/CredentialLeakGuardTest.php` (assert no secrets appear in OperationRun failure records or captured logs; forbidden substrings: `client_secret`, `Bearer `, `access_token`, `refresh_token`, `Authorization`)
### Implementation for User Story 1
- [X] T023 [US1] Create Filament resource skeleton in `app/Filament/Resources/ProviderConnectionResource.php`
- [X] T024 [P] [US1] Create pages in `app/Filament/Resources/ProviderConnectionResource/Pages/ListProviderConnections.php`, `app/Filament/Resources/ProviderConnectionResource/Pages/CreateProviderConnection.php`, `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php`
- [X] T025 [US1] Implement list/table + filters in `app/Filament/Resources/ProviderConnectionResource.php` (tenant-scoped; DB-only)
- [X] T026 [US1] Implement create/edit forms in `app/Filament/Resources/ProviderConnectionResource.php` (provider=microsoft, `entra_tenant_id`, `display_name`, `is_default`, status/health read-only fields)
- [X] T027 [US1] Implement credential upsert action (Owner/Manager only; secrets never shown) in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php`
- [X] T028 [US1] Implement disable connection action with confirmation + audit log in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php`
- [X] T029 [US1] Write audit log entries for connection + credential changes using `app/Services/Intune/AuditLogger.php` (called from `app/Filament/Resources/ProviderConnectionResource/Pages/CreateProviderConnection.php` and `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php`)
- [X] T030 [US1] Run story tests with `./vendor/bin/sail artisan test tests/Feature/ProviderConnections/ProviderConnectionAuthorizationTest.php` (test: `tests/Feature/ProviderConnections/ProviderConnectionAuthorizationTest.php`)
---
## Phase 4: User Story 2 — Verify connection health without blocking the UI (Priority: P2)
**Goal**: Owner/Manager/Operator can enqueue a health check that creates an `OperationRun`, updates health state, and shows stable reason codes/messages on failure.
**Independent Test**: Clicking “Check connection” enqueues exactly one run (dedupe), never calls Graph in the request cycle, and produces a visible run outcome + updates connection health.
### Tests for User Story 2
- [X] T031 [P] [US2] Add start-surface test (no Graph in request; job queued; run created) in `tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php`
- [X] T032 [P] [US2] Add job behavior test (success + categorized failure) in `tests/Feature/ProviderConnections/ProviderConnectionHealthCheckJobTest.php`
- [X] T032b [P] [US2] Assert OperationRun context contract for connection health checks in `tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php` (`context.provider`, `context.module`, `context.provider_connection_id`, `context.target_scope.entra_tenant_id`)
### Implementation for User Story 2
- [X] T033 [US2] Add “Check connection” Filament action (enqueue-only) in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php` (MUST call `ProviderOperationStartGate` first: same-operation returns existing active run; different-operation for same scope blocks “scope busy” + link to active run)
- [X] T034 [US2] Create queued job in `app/Jobs/ProviderConnectionHealthCheckJob.php` (uses `app/Jobs/Middleware/TrackOperationRun.php`, updates `provider_connections` health fields, updates `operation_runs`)
- [X] T035 [US2] Create health check module in `app/Services/Providers/MicrosoftProviderHealthCheck.php` (implements `app/Services/Providers/Contracts/ProviderHealthCheck.php`, uses `ProviderGateway`, maps failures to reason codes via `app/Support/OpsUx/RunFailureSanitizer.php`)
- [X] T035b [P] [US2] Ensure health check uses ProviderConnection context (`entra_tenant_id`) and obtains tokens via ProviderGateway/CredentialManager (ProviderGateway is the only decryptor; no secrets in config/env beyond bootstrap)
- [X] T036 [US2] Run story tests with `./vendor/bin/sail artisan test tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php` (test: `tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php`)
---
## Phase 5: User Story 3 — Run provider operations with safety and observability (Priority: P3)
**Goal**: Owner/Manager/Operator can run provider operations (inventory + compliance snapshot) as observable runs, with per-scope concurrency rules (dedupe vs scope-busy) and summary counts.
**Independent Test**: Starting inventory/compliance creates runs, obeys dedupe/scope-busy rules, never calls Graph in the request cycle, and writes summary counts + failures safely.
### Tests for User Story 3
- [X] T037 [P] [US3] Add scope-busy + dedupe behavior tests in `tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`
- [X] T038 [P] [US3] Add compliance snapshot summary_counts tests in `tests/Feature/ProviderConnections/ProviderComplianceSnapshotJobTest.php`
- [X] T038b [P] [US3] Assert OperationRun context contract for inventory/compliance runs in `tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php` and `tests/Feature/ProviderConnections/ProviderComplianceSnapshotJobTest.php` (`context.provider`, `context.module`, `context.provider_connection_id`, `context.target_scope.entra_tenant_id`)
### Implementation for User Story 3
- [X] T039 [US3] Add managed devices contract entry for compliance snapshot in `config/graph_contracts.php` (resource `deviceManagement/managedDevices`, select includes compliance state)
- [X] T040 [US3] Add allowed summary keys for compliance counts in `app/Support/OpsUx/OperationSummaryKeys.php` (add `compliant`, `noncompliant`, `unknown`)
- [X] T041 [US3] Create compliance snapshot collector in `app/Services/Providers/MicrosoftComplianceSnapshotService.php` (implements `app/Services/Providers/Contracts/ProviderComplianceCollector.php`, uses `ProviderGateway`, Graph list + count compliance states)
- [X] T042 [US3] Create queued job in `app/Jobs/ProviderComplianceSnapshotJob.php` (writes `OperationRun` context + summary_counts; updates failures with sanitized reason codes)
- [X] T043 [US3] Add “Compliance snapshot” Filament action (enqueue-only) in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php` (MUST call `ProviderOperationStartGate` first: same-operation returns existing active run; different-operation for same scope blocks “scope busy” + link to active run)
- [X] T044 [US3] Create minimal inventory collector in `app/Services/Providers/MicrosoftProviderInventoryCollector.php` (implements `app/Services/Providers/Contracts/ProviderInventoryCollector.php`, uses `ProviderGateway`, contract-backed listing + summary counts only)
- [X] T045 [US3] Create queued job in `app/Jobs/ProviderInventorySyncJob.php` (writes `OperationRun` context + summary_counts; obeys scope-busy rules via start gate)
- [X] T046 [US3] Add “Inventory sync” Filament action (enqueue-only) in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php` (MUST call `ProviderOperationStartGate` first: same-operation returns existing active run; different-operation for same scope blocks “scope busy” + link to active run)
- [X] T047 [US3] Run story tests with `./vendor/bin/sail artisan test tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php` (test: `tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`)
---
## Phase 6: Polish & Cross-Cutting Concerns
**Purpose**: Reduce regressions and improve consistency across stories
- [X] T048 [P] Ensure provider operations set `context.target_scope.entra_tenant_id` and `context.target_scope.entra_tenant_name` in `app/Jobs/ProviderConnectionHealthCheckJob.php`, `app/Jobs/ProviderComplianceSnapshotJob.php`, and `app/Jobs/ProviderInventorySyncJob.php`
- [X] T049 [P] If ProviderConnection status/health is shown as badges, add a badge mapper + tests in `app/Support/Badges/Domains/` and `tests/Unit/Badges/`
- [X] T050 Run formatting with `./vendor/bin/sail php ./vendor/bin/pint --dirty` (script: `./vendor/bin/pint`)
- [X] T051 Run focused feature tests with `./vendor/bin/sail artisan test tests/Feature/ProviderConnections` (folder: `tests/Feature/ProviderConnections`)
---
## Dependencies & Execution Order
### User Story Dependency Graph
```text
Phase 1 (Setup)
Phase 2 (Foundation: schema/models/policy + ops primitives)
US1 (Connections + credentials UI) ─┬─→ US2 (Health check run)
└─→ US3 (Inventory + compliance runs)
```
### Parallel Opportunities
- Phase 2 tasks marked `[P]` can be done in parallel (different files).
- Within each user story phase, `[P]` tests can be written in parallel.
- Caution: US2 and US3 both extend `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php` (avoid parallel edits to that file unless coordinated).
---
## Parallel Example: User Story 1
```bash
Task: "Add DB-only render test for Provider Connections in tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php"
Task: "Add role authorization test in tests/Feature/ProviderConnections/ProviderConnectionAuthorizationTest.php"
Task: "Add credential encryption/non-disclosure test in tests/Feature/ProviderConnections/ProviderCredentialSecurityTest.php"
```
---
## Implementation Strategy
### MVP First (User Story 1)
1. Complete Phase 1 + Phase 2
2. Complete US1 (Provider Connections + credentials management)
3. Validate with `tests/Feature/ProviderConnections/*` and DB-only render checks
### Incremental Delivery
1. US1 → demo/manage connections safely (no provider calls)
2. US2 → add health check (first real provider call, but enqueue-only + observable)
3. US3 → add inventory + compliance snapshot operations (summary counts + scope-busy rules)