TenantAtlas/specs/081-provider-connection-cutover/tasks.md
ahmido 4db8030f2a Spec 081: Provider connection cutover (#98)
Implements Spec 081 provider-connection cutover.

Highlights:
- Adds provider connection resolution + gating for operations/verification.
- Adds provider credential observer wiring.
- Updates Filament tenant verify flow to block with next-steps when provider connection isn’t ready.
- Adds spec docs under specs/081-provider-connection-cutover/ and extensive Spec081 test coverage.

Tests:
- vendor/bin/sail artisan test --compact tests/Feature/Filament/TenantSetupTest.php
- Focused suites for ProviderConnections/Verification ran during implementation (see local logs).

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #98
2026-02-08 11:28:51 +00:00

147 lines
13 KiB
Markdown

# Tasks: Provider Connection Full Cutover (Spec 081)
**Input**: Design documents from `specs/081-provider-connection-cutover/`
**Prerequisites**: `plan.md` (required), `spec.md` (required), `research.md`, `data-model.md`, `contracts/`, `quickstart.md`
**Tests**: REQUIRED (Pest) — this feature changes runtime credential sourcing, operations behavior, and admin UI.
## Phase 1: Setup (Shared Infrastructure)
- [X] T001 Create new Spec 081 test files `tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php` and `tests/Feature/Guards/NoTenantCredentialRuntimeReadsSpec081Test.php`
- [X] T002 Define Spec 081 test naming/tagging conventions in `tests/Pest.php` so `vendor/bin/sail artisan test --compact --filter=Spec081` is a stable focused command (quickstart aligns to this command)
---
## Phase 2: Foundational (Blocking Prerequisites)
**⚠️ CRITICAL**: Complete this phase before starting any user story work.
- [X] T003 Implement default ProviderConnection resolver `app/Services/Providers/ProviderConnectionResolver.php` (resolve default per tenant/provider; detect missing/multiple/invalid defaults; return deterministic reason_code, including `provider_connection_invalid` for multi-default corruption)
- [X] T004 [P] Add reason-code constants (baseline) in `app/Support/Providers/ProviderReasonCodes.php` (mirror Appendix A)
- [X] T005 Implement link-only “next steps” registry `app/Support/Providers/ProviderNextStepsRegistry.php` (maps reason_code → label + URL generator; no secrets)
- [X] T006 Add `blocked` outcome support in `app/Support/OperationRunOutcome.php` and update casts/validation in `app/Models/OperationRun.php`
- [X] T007 Update centralized outcome badge mapping for new `blocked` value in `app/Support/Badges/Domains/OperationRunOutcomeBadge.php` and add mapping tests in `tests/Feature/Badges/OperationRunOutcomeBadgeBlockedTest.php`
- [X] T008 Add OperationRunService helper to finalize blocked runs with sanitized failures in `app/Services/OperationRunService.php` (store `reason_code` + next_steps links; set `status=completed`, `outcome=blocked`)
- [X] T009 Update provider operation start gate to use resolver + blocked-run helper in `app/Services/Providers/ProviderOperationStartGate.php` (create run even when blocked; dedupe identity uses provider_connection_id when present)
- [X] T010 [P] Update verification report normalization to accept registry-produced next steps in `app/Support/Verification/VerificationReportWriter.php` and sanitizer `app/Support/Verification/VerificationReportSanitizer.php` (no behavior drift, just centralize usage)
- [X] T011 Add guard tests (runtime + static scan assertions) preventing reads/writes of legacy tenant credentials (`->app_client_id` / `->app_client_secret`) outside allowed backfill tooling in `tests/Feature/Guards/NoTenantCredentialRuntimeReadsSpec081Test.php`
- [X] T042 Add regression coverage for default uniqueness invariant in `tests/Feature/ProviderConnections/ProviderConnectionDefaultInvariantSpec081Test.php` (assert partial unique index exists for `(tenant_id, provider)` where `is_default=true`, and `ProviderConnection::makeDefault()` preserves invariant)
**Checkpoint**: Foundation ready — default resolution, blocked outcomes, badge mapping, next steps registry, guard test framework, and default uniqueness invariant coverage exist.
---
## Phase 3: User Story 1 — Deterministic provider operations (Priority: P1) 🎯 MVP
**Goal**: All provider-backed operation starts deterministically use default ProviderConnection; missing/invalid configuration blocks with stable reason codes and observable runs.
**Independent Test**: Start a provider-backed operation with (a) default connection and (b) missing default; verify run outcome and context include `provider_connection_id` and stable `reason_code`.
- [X] T012 [P] [US1] Add provider-connection resolution tests for missing default → blocked run in `tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php`
- [X] T013 [P] [US1] Add provider-connection resolution tests for missing credential → blocked run in `tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php`
- [X] T043 [P] [US1] Add provider-connection resolution tests for invalid multi-default data → deterministic blocked/fail result with reason code `provider_connection_invalid` (extension detail allowed, e.g. `ext.multiple_defaults_detected`) in `tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php`
- [X] T014 [US1] Introduce a single entry point for “Graph options from connection” use in `app/Services/Providers/ProviderGateway.php` (ensure all call sites accept `ProviderConnection`)
- [X] T015 [US1] Refactor `app/Services/Inventory/InventorySyncService.php` to resolve ProviderConnection and build Graph options via ProviderGateway (remove `$tenant->app_client_*` runtime reads)
- [X] T016 [US1] Refactor `app/Services/Intune/PolicySyncService.php` to resolve ProviderConnection and build Graph options via ProviderGateway (remove `$tenant->app_client_*` runtime reads)
- [X] T017 [US1] Refactor `app/Services/Intune/PolicySnapshotService.php` to resolve ProviderConnection and build Graph options via ProviderGateway (remove `$tenant->app_client_*` runtime reads)
- [X] T018 [US1] Refactor `app/Services/Intune/RestoreService.php` to resolve ProviderConnection and build Graph options via ProviderGateway (remove `$tenant->app_client_*` runtime reads)
- [X] T019 [US1] Refactor `app/Services/Graph/ScopeTagResolver.php` to resolve ProviderConnection and build Graph options via ProviderGateway (remove `$tenant->app_client_*` runtime reads)
- [X] T020 [US1] Refactor `app/Services/Intune/RbacOnboardingService.php` to use ProviderConnection + Graph options (remove `$tenant->app_client_id` checks; gate via resolver)
- [X] T021 [US1] Refactor `app/Services/Intune/RbacHealthService.php` to use ProviderConnection + Graph options (remove `$tenant->app_client_id` filter usage)
- [X] T022 [US1] Refactor `app/Http/Controllers/AdminConsentCallbackController.php` to resolve/persist consent outcomes against `ProviderConnection` records and stop all reads/writes of `tenants.app_*` for callback handling
- [X] T023 [P] [US1] Deprecate `Tenant::graphOptions()` and add a test ensuring it is unused in runtime services/jobs in `app/Models/Tenant.php` and `tests/Feature/Guards/NoTenantCredentialRuntimeReadsSpec081Test.php`
- [X] T044 [US1] Audit all Graph call sites touched by Spec 081 refactors and ensure each is represented in `config/graph_contracts.php`; add/update regression coverage in `tests/Feature/Graph/GraphContractRegistryCoverageSpec081Test.php`
- [X] T045 [P] [US1] Add DB-only render regression test for Monitoring → Operations surfaces in `tests/Feature/Monitoring/OperationsDbOnlyRenderingSpec081Test.php` (assert no provider/network call path is invoked during page render)
### Backfill (Microsoft default provider connections) — Out of scope for Spec 081 (won't do)
- [X] T024 [US1] Implement idempotent backfill command `app/Console/Commands/TenantpilotBackfillMicrosoftDefaultProviderConnections.php` with signature `tenantpilot:provider-connections:backfill-microsoft-defaults` (won't do for 081: no legacy tenant credential data migration required)
- [X] T025 [US1] Implement backfill logic service `app/Services/Providers/ProviderConnectionBackfillService.php` (decision tree from FR-081-010; allowed to read legacy `tenants.app_*` once) (won't do for 081: no legacy tenant credential data migration required)
- [X] T026 [P] [US1] Add feature tests for backfill decision tree in `tests/Feature/Console/ProviderConnectionsBackfillMicrosoftDefaultsCommandTest.php` (won't do for 081: no legacy tenant credential data migration required)
- [X] T027 [US1] Add a regression test that backfill is idempotent (no duplicates) in `tests/Feature/Console/ProviderConnectionsBackfillMicrosoftDefaultsCommandTest.php` (won't do for 081: no legacy tenant credential data migration required)
---
## Phase 4: User Story 2 — Safe credential management with audit (Priority: P2)
**Goal**: Credentials are managed only via ProviderConnection UI/actions with explicit confirmation, correct authorization semantics, and audit logging (no secret leakage).
**Independent Test**: Rotate credentials and confirm (a) confirmation required, (b) audit event exists with redacted payload, (c) secrets never appear outside encrypted payload.
- [X] T028 [P] [US2] Remove tenant credential fields from registration form in `app/Filament/Pages/Tenancy/RegisterTenant.php` and replace with helper text linking to Provider Connections management
- [X] T029 [P] [US2] Remove tenant credential fields from tenant form/infolist in `app/Filament/Resources/TenantResource.php` (no `app_client_id` / `app_client_secret` inputs or copyable entries)
- [X] T030 [US2] Ensure ProviderConnection management is the single credential surface by adding/confirming a navigation action from tenant view to provider connections in `app/Filament/Resources/TenantResource/Pages/ViewTenant.php`
- [X] T031 [US2] Enforce confirmed, server-side authorized credential mutations for ProviderCredential updates in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php` using `Action::make(...)->action(...)->requiresConfirmation()`
- [X] T032 [US2] Extend the existing audit pipeline so credential create/update/rotate emits stable, redacted audit events via `app/Observers/ProviderCredentialObserver.php`
- [X] T033 [P] [US2] Add authorization tests for ProviderConnection management: non-member 404, member missing capability 403 in `tests/Feature/ProviderConnections/ProviderConnectionAuthorizationSpec081Test.php`
- [X] T034 [P] [US2] Add audit + redaction tests for credential rotation in `tests/Feature/Audit/ProviderCredentialAuditSpec081Test.php`
- [X] T046 [P] [US2] Add DB-only render regression tests for `TenantResource`/`ProviderConnectionResource` view pages in `tests/Feature/ProviderConnections/ProviderConnectionViewsDbOnlyRenderingSpec081Test.php` (no Graph/provider calls during render)
- [X] T047 [US2] Implement and test blocked-state guidance on operation start surfaces (reason code + primary “Manage Provider Connections” link) in `app/Filament/Resources/TenantResource/Pages/ViewTenant.php` and `tests/Feature/ProviderConnections/ProviderOperationBlockedGuidanceSpec081Test.php`
---
## Phase 5: User Story 3 — Troubleshoot failures using stable reason codes (Priority: P3)
**Goal**: Blocked/failed provider operations consistently expose stable `reason_code` and link-only next steps across OperationRuns and Verification reports, with zero secret leakage.
**Independent Test**: Trigger blocked outcomes and assert reason_code + next_steps shape is consistent and secrets are absent.
- [X] T035 [P] [US3] Add tests asserting blocked OperationRuns store `reason_code` + next_steps links and never include secrets in context/failure summaries in `tests/Feature/Monitoring/OperationRunBlockedSpec081Test.php`
- [X] T036 [US3] Update both provider health checks and permission check clusters to use the central registry for next steps in `app/Jobs/ProviderConnectionHealthCheckJob.php` and `app/Support/Verification/TenantPermissionCheckClusters.php`
- [X] T037 [US3] Normalize and document reason codes used by provider exceptions in `app/Support/RunFailureSanitizer.php` (map to Appendix A taxonomy where possible, document intentional extension/deviation mappings, unknown → `unknown_error`)
- [X] T038 [P] [US3] Add verification report schema tests for next_steps structure (label + url) in `tests/Feature/Verification/VerificationReportNextStepsSchemaSpec081Test.php`
---
## Phase 6: Polish & Cross-Cutting Concerns
- [X] T039 [P] Run Pint formatting for all changed files via `vendor/bin/sail bin pint --dirty`
- [X] T040 Run focused test pack for Spec 081 via `vendor/bin/sail artisan test --compact --filter=Spec081`
- [X] T041 Run impacted test suites: `tests/Feature/ProviderConnections`, `tests/Feature/Guards`, `tests/Feature/Monitoring`, `tests/Feature/Verification` via `vendor/bin/sail artisan test --compact`
---
## Dependencies & Execution Order
### Dependency Graph (User Stories)
- Phase 1 → Phase 2 → US1 → (US2, US3) → Polish
US2 and US3 can run in parallel after US1 (they reuse the foundational blocked-run + registry primitives).
### Parallel Execution Examples
**US1 (P1)**
- In parallel after Phase 2:
- T043 `tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php`
- T015 `app/Services/Inventory/InventorySyncService.php`
- T016 `app/Services/Intune/PolicySyncService.php`
- T017 `app/Services/Intune/PolicySnapshotService.php`
- T018 `app/Services/Intune/RestoreService.php`
- T019 `app/Services/Graph/ScopeTagResolver.php`
- T045 `tests/Feature/Monitoring/OperationsDbOnlyRenderingSpec081Test.php`
**US2 (P2)**
- In parallel:
- T028 `app/Filament/Pages/Tenancy/RegisterTenant.php`
- T029 `app/Filament/Resources/TenantResource.php`
- T033 `tests/Feature/ProviderConnections/ProviderConnectionAuthorizationSpec081Test.php`
- T046 `tests/Feature/ProviderConnections/ProviderConnectionViewsDbOnlyRenderingSpec081Test.php`
**US3 (P3)**
- In parallel:
- T035 `tests/Feature/Monitoring/OperationRunBlockedSpec081Test.php`
- T038 `tests/Feature/Verification/VerificationReportNextStepsSchemaSpec081Test.php`
---
## Implementation Strategy
**MVP scope**: Phase 1 + Phase 2 + US1.
- Deliver deterministic starts, blocked runs, backfill command, and remove runtime legacy reads first.
- Then deliver admin UX + audit (US2) and next-step consistency across verification/monitoring (US3).