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
13 KiB
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)
- T001 Create new Spec 081 test files
tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.phpandtests/Feature/Guards/NoTenantCredentialRuntimeReadsSpec081Test.php - T002 Define Spec 081 test naming/tagging conventions in
tests/Pest.phpsovendor/bin/sail artisan test --compact --filter=Spec081is a stable focused command (quickstart aligns to this command)
Phase 2: Foundational (Blocking Prerequisites)
⚠️ CRITICAL: Complete this phase before starting any user story work.
- T003 Implement default ProviderConnection resolver
app/Services/Providers/ProviderConnectionResolver.php(resolve default per tenant/provider; detect missing/multiple/invalid defaults; return deterministic reason_code, includingprovider_connection_invalidfor multi-default corruption) - T004 [P] Add reason-code constants (baseline) in
app/Support/Providers/ProviderReasonCodes.php(mirror Appendix A) - T005 Implement link-only “next steps” registry
app/Support/Providers/ProviderNextStepsRegistry.php(maps reason_code → label + URL generator; no secrets) - T006 Add
blockedoutcome support inapp/Support/OperationRunOutcome.phpand update casts/validation inapp/Models/OperationRun.php - T007 Update centralized outcome badge mapping for new
blockedvalue inapp/Support/Badges/Domains/OperationRunOutcomeBadge.phpand add mapping tests intests/Feature/Badges/OperationRunOutcomeBadgeBlockedTest.php - T008 Add OperationRunService helper to finalize blocked runs with sanitized failures in
app/Services/OperationRunService.php(storereason_code+ next_steps links; setstatus=completed,outcome=blocked) - 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) - T010 [P] Update verification report normalization to accept registry-produced next steps in
app/Support/Verification/VerificationReportWriter.phpand sanitizerapp/Support/Verification/VerificationReportSanitizer.php(no behavior drift, just centralize usage) - 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 intests/Feature/Guards/NoTenantCredentialRuntimeReadsSpec081Test.php - T042 Add regression coverage for default uniqueness invariant in
tests/Feature/ProviderConnections/ProviderConnectionDefaultInvariantSpec081Test.php(assert partial unique index exists for(tenant_id, provider)whereis_default=true, andProviderConnection::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.
- T012 [P] [US1] Add provider-connection resolution tests for missing default → blocked run in
tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php - T013 [P] [US1] Add provider-connection resolution tests for missing credential → blocked run in
tests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php - 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) intests/Feature/ProviderConnections/ProviderConnectionCutoverSpec081Test.php - T014 [US1] Introduce a single entry point for “Graph options from connection” use in
app/Services/Providers/ProviderGateway.php(ensure all call sites acceptProviderConnection) - T015 [US1] Refactor
app/Services/Inventory/InventorySyncService.phpto resolve ProviderConnection and build Graph options via ProviderGateway (remove$tenant->app_client_*runtime reads) - T016 [US1] Refactor
app/Services/Intune/PolicySyncService.phpto resolve ProviderConnection and build Graph options via ProviderGateway (remove$tenant->app_client_*runtime reads) - T017 [US1] Refactor
app/Services/Intune/PolicySnapshotService.phpto resolve ProviderConnection and build Graph options via ProviderGateway (remove$tenant->app_client_*runtime reads) - T018 [US1] Refactor
app/Services/Intune/RestoreService.phpto resolve ProviderConnection and build Graph options via ProviderGateway (remove$tenant->app_client_*runtime reads) - T019 [US1] Refactor
app/Services/Graph/ScopeTagResolver.phpto resolve ProviderConnection and build Graph options via ProviderGateway (remove$tenant->app_client_*runtime reads) - T020 [US1] Refactor
app/Services/Intune/RbacOnboardingService.phpto use ProviderConnection + Graph options (remove$tenant->app_client_idchecks; gate via resolver) - T021 [US1] Refactor
app/Services/Intune/RbacHealthService.phpto use ProviderConnection + Graph options (remove$tenant->app_client_idfilter usage) - T022 [US1] Refactor
app/Http/Controllers/AdminConsentCallbackController.phpto resolve/persist consent outcomes againstProviderConnectionrecords and stop all reads/writes oftenants.app_*for callback handling - T023 [P] [US1] Deprecate
Tenant::graphOptions()and add a test ensuring it is unused in runtime services/jobs inapp/Models/Tenant.phpandtests/Feature/Guards/NoTenantCredentialRuntimeReadsSpec081Test.php - 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 intests/Feature/Graph/GraphContractRegistryCoverageSpec081Test.php - 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)
- T024 [US1] Implement idempotent backfill command
app/Console/Commands/TenantpilotBackfillMicrosoftDefaultProviderConnections.phpwith signaturetenantpilot:provider-connections:backfill-microsoft-defaults(won't do for 081: no legacy tenant credential data migration required) - T025 [US1] Implement backfill logic service
app/Services/Providers/ProviderConnectionBackfillService.php(decision tree from FR-081-010; allowed to read legacytenants.app_*once) (won't do for 081: no legacy tenant credential data migration required) - 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) - 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.
- T028 [P] [US2] Remove tenant credential fields from registration form in
app/Filament/Pages/Tenancy/RegisterTenant.phpand replace with helper text linking to Provider Connections management - T029 [P] [US2] Remove tenant credential fields from tenant form/infolist in
app/Filament/Resources/TenantResource.php(noapp_client_id/app_client_secretinputs or copyable entries) - 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 - T031 [US2] Enforce confirmed, server-side authorized credential mutations for ProviderCredential updates in
app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.phpusingAction::make(...)->action(...)->requiresConfirmation() - T032 [US2] Extend the existing audit pipeline so credential create/update/rotate emits stable, redacted audit events via
app/Observers/ProviderCredentialObserver.php - T033 [P] [US2] Add authorization tests for ProviderConnection management: non-member 404, member missing capability 403 in
tests/Feature/ProviderConnections/ProviderConnectionAuthorizationSpec081Test.php - T034 [P] [US2] Add audit + redaction tests for credential rotation in
tests/Feature/Audit/ProviderCredentialAuditSpec081Test.php - T046 [P] [US2] Add DB-only render regression tests for
TenantResource/ProviderConnectionResourceview pages intests/Feature/ProviderConnections/ProviderConnectionViewsDbOnlyRenderingSpec081Test.php(no Graph/provider calls during render) - 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.phpandtests/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.
- T035 [P] [US3] Add tests asserting blocked OperationRuns store
reason_code+ next_steps links and never include secrets in context/failure summaries intests/Feature/Monitoring/OperationRunBlockedSpec081Test.php - T036 [US3] Update both provider health checks and permission check clusters to use the central registry for next steps in
app/Jobs/ProviderConnectionHealthCheckJob.phpandapp/Support/Verification/TenantPermissionCheckClusters.php - 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) - 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
- T039 [P] Run Pint formatting for all changed files via
vendor/bin/sail bin pint --dirty - T040 Run focused test pack for Spec 081 via
vendor/bin/sail artisan test --compact --filter=Spec081 - T041 Run impacted test suites:
tests/Feature/ProviderConnections,tests/Feature/Guards,tests/Feature/Monitoring,tests/Feature/Verificationviavendor/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
- T043
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
- T028
US3 (P3)
- In parallel:
- T035
tests/Feature/Monitoring/OperationRunBlockedSpec081Test.php - T038
tests/Feature/Verification/VerificationReportNextStepsSchemaSpec081Test.php
- T035
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).