TenantAtlas/specs/281-provider-connection-scope/data-model.md
ahmido 023274c46c feat: normalize provider connection scope contracts (#339)
## Summary
- normalize provider-neutral target-scope and identity contracts across provider connection resolution, operation-start gating, verification reporting, and boundary configuration
- align provider connection resource, onboarding, tenant summaries, and operation follow-up on the same shared scope contract while keeping Microsoft-specific profile details in provider-owned metadata
- add Spec 281 artifacts and focused feature/browser coverage for the new provider-scope contract
- move the tenant dashboard context-chip rail into Filament header widgets so the metadata row renders directly under the page subtitle

## Validation
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Providers/ProviderConnectionTargetScopeNeutralityTest.php tests/Feature/Providers/ProviderIdentityResolutionNeutralityTest.php tests/Feature/Providers/ProviderOperationStartGateTargetScopeContextTest.php tests/Feature/Filament/ProviderConnectionResourceScopeSummaryTest.php tests/Feature/Onboarding/ManagedTenantOnboardingProviderConnectionScopeTest.php tests/Feature/Guards/ProviderConnectionMicrosoftScopeLeakGuardTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Dashboard/TenantDashboardProductizationSummaryTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Dashboard/TenantDashboardProductizationSmokeTest.php`
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`

## Notes
- Filament remains on v5 with Livewire v4-compatible surfaces only.
- Provider registration location is unchanged; Laravel 11+ providers stay in `apps/platform/bootstrap/providers.php`.
- `ProviderConnectionResource` remains non-globally-searchable and still exposes View/Edit pages.
- No new asset registration was added; deploy-time `filament:assets` expectations are unchanged.
- No new destructive action path was introduced; existing server authorization and confirmation handling remain in place where applicable.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #339
2026-05-07 19:28:42 +00:00

232 lines
13 KiB
Markdown

# Data Model: Provider Connection Scope & Microsoft Profile Extraction
**Date**: 2026-05-07
**Branch**: `281-provider-connection-scope`
## Overview
This slice introduces no new persistence. It keeps the existing provider-connection, credential, and run records intact and instead standardizes the derived runtime contracts that platform-core seams expose to UI, audit, and provider-operation flows.
## Persisted Truth Unchanged
- `ProviderConnection` remains the workspace-owned, managed-environment-scoped binding record.
- `ProviderCredential` remains the optional credential record attached to one `ProviderConnection`.
- `OperationRun` remains execution truth and keeps its current identity and lifecycle ownership.
- `config/provider_boundaries.php` remains the single source for provider-owned versus platform-core seam classification.
- No new table, registry, provider-profile entity, enum family, or taxonomy is introduced.
## Derived Runtime Contracts
### 1. Provider Connection Record
**Persistence**: existing database row
**Owner**: `ProviderConnection`
| Field | Type | Required | Notes |
|---|---|---|---|
| `id` | int | yes | Existing record key |
| `workspace_id` | int | yes | Existing workspace boundary |
| `managed_environment_id` | int | yes | Existing managed-environment boundary; already the canonical scope anchor |
| `provider` | string | yes | Current provider key (`microsoft` today) |
| `display_name` | string | yes | Operator-visible connection label |
| `connection_type` | enum | yes | Existing `platform` or `dedicated` connection type |
| `is_default` | bool | yes | Existing default-binding flag |
| `is_enabled` | bool | yes | Existing enablement flag |
| `consent_status` | enum | yes | Existing consent state |
| `verification_status` | enum | yes | Existing verification state |
| `entra_tenant_id` | string | yes | Existing provider-owned persisted identifier; not the shared contract key after this slice |
| `metadata` | array | no | Existing legacy-identity and provider-owned metadata |
**Rules**:
- `managed_environment_id` remains the persisted scope anchor; the stale candidate move is not reopened.
- `entra_tenant_id` may remain a provider-owned stored value, but platform-core consumers must read the normalized `target_scope` contract instead of exposing this column name as shared truth.
- `metadata` remains derived/provider-owned detail and must not become a second canonical shared scope contract.
### 2. Shared Target Scope Descriptor
**Persistence**: derived
**Owner**: `ProviderConnectionTargetScopeDescriptor`
| Field | Type | Required | Notes |
|---|---|---|---|
| `provider` | string | yes | Shared provider key |
| `scope_kind` | string | yes | Current release supports `tenant` only |
| `scope_identifier` | string | yes | Neutral scope identifier used across platform-core seams |
| `scope_display_name` | string | yes | Operator-facing name for the scope |
| `shared_label` | string | yes | Current shared label, `Target scope` |
| `shared_help_text` | string | yes | Current shared help text |
**Rules**:
- This is the canonical shared `target_scope` object for connection resolution, identity resolution, audit metadata, surface summaries, onboarding readiness, and provider-operation start context.
- Shared `target_scope` payloads must not require `entra_tenant_id` as a top-level key.
- `scope_kind` remains the current `tenant` constant; this slice does not add new scope-state machinery.
### 3. Provider Context
**Persistence**: derived
**Owner**: `ProviderIdentityContextMetadata`
| Field | Type | Required | Notes |
|---|---|---|---|
| `provider` | string | yes | Provider key for the disclosed context |
| `details` | list<object> | yes | Ordered provider-owned detail items for profile, consent, audit, or troubleshooting disclosure |
**Nested `provider_context.details` item**
| Field | Type | Required | Notes |
|---|---|---|---|
| `detail_key` | string | yes | Stable provider-owned detail key such as `microsoft_tenant_id`, `admin_consent_url`, `required_permissions_url`, or `portal_domain` |
| `detail_label` | string | yes | Operator/support label |
| `detail_value` | string | yes | Current provider-owned value |
| `visibility` | string | yes | `contextual_only`, `audit_only`, or `troubleshooting_only` |
**Rules**:
- `provider_context` is the canonical nested provider-owned wrapper carried by shared identity, summary, onboarding, audit, and run-context contracts.
- Current Microsoft details include `microsoft_tenant_id`, `authority_tenant`, `redirect_uri`, consent links, required-permissions guidance, domains, and portal/profile links.
- The detail set is intentionally provider-owned and extensible; this slice does not freeze provider context to a three-key catalog.
- These values may appear in nested provider profile/context blocks or audit metadata, but they do not replace the shared `target_scope` descriptor.
### 4. Provider Identity Resolution Contract
**Persistence**: derived
**Owner**: `ProviderIdentityResolution`
| Field | Type | Required | Notes |
|---|---|---|---|
| `resolved` | bool | yes | Shared resolved/blocked status |
| `connection_type` | string | yes | Existing connection-type truth |
| `target_scope` | object | no | Canonical shared `target_scope` descriptor |
| `effective_client_identity.client_id` | string | no | Neutral shared client identity when resolved |
| `effective_client_identity.credential_source` | string | yes | Shared credential source (`platform_config`, dedicated source, legacy source) |
| `blocked_reason.reason_code` | string | no | Existing provider reason code when blocked |
| `blocked_reason.message` | string | no | Operator-facing blocked reason message |
| `provider_context` | object | yes | Nested provider-owned context wrapper with `provider` and ordered `details` |
**Rules**:
- The shared contract center is `target_scope`, effective client identity, credential source, and blocked reason.
- Legacy `tenantContext` is an implementation concern that should be absorbed into nested provider context or authority handling, not left as the primary shared contract field name.
- `clientSecret` remains runtime-only and is excluded from surface and audit contracts.
- Blocked results still return `target_scope` when it can be normalized, so surfaces keep one consistent summary even on failure.
### 5. Provider Connection Surface Summary
**Persistence**: derived
**Owner**: `ProviderConnectionSurfaceSummary`
| Field | Type | Required | Notes |
|---|---|---|---|
| `provider` | string | yes | Shared provider key |
| `target_scope` | object | yes | Canonical shared descriptor |
| `consent_state` | string | yes | Existing consent status value |
| `verification_state` | string | yes | Existing verification status value |
| `readiness_summary` | string | yes | Existing operator summary |
| `target_scope_summary` | string | yes | Shared rendered summary for UI surfaces |
| `provider_context` | object | yes | Nested provider-owned context wrapper with `provider` and ordered `details` |
| `contextual_identity_line` | string | no | Optional condensed display line derived from nested provider context |
| `is_enabled` | bool | yes | Existing enablement state for display logic |
**Rules**:
- `ProviderConnectionResource`, `ManagedTenantOnboardingWizard`, and managed-environment related-context summaries must all reuse this contract.
- Default-visible content remains summary-first: target scope, readiness, consent, and verification.
- Provider-specific detail is secondary and derived from nested provider context detail only.
- Invalid target scope falls back to an explicit review-needed summary instead of leaking raw provider fields back into shared UI.
### 6. Onboarding Provider-Connection Readiness View
**Persistence**: derived
**Owner**: `ManagedTenantOnboardingWizard`
| Field | Type | Required | Notes |
|---|---|---|---|
| `provider_connection_id` | int | yes | Selected or referenced connection |
| `provider_summary` | object | yes | Reused `ProviderConnectionSurfaceSummary` payload |
| `permission_overview` | object | yes | Existing required-permissions overview including nested provider-owned `required_permissions_url` guidance |
**Rules**:
- Onboarding must reuse the same `provider_summary.target_scope` and `target_scope_summary` contract as the provider-connections resource.
- Supporting verification and permission links remain secondary and stay nested under `permission_overview.required_permissions_url` or equivalent provider-owned guidance fields.
- No onboarding-only target-scope wording or fallback structure is introduced.
### 7. Provider Operation Run Context
**Persistence**: derived run context in existing `OperationRun` rows
**Owner**: `ProviderOperationStartGate`
| Field | Type | Required | Notes |
|---|---|---|---|
| `execution_authority_mode` | string | yes | Existing execution-authority contract |
| `required_capability` | string | no | Existing capability contract |
| `provider` | string | yes | Shared provider key |
| `module` | string | yes | Existing provider-operation module |
| `provider_binding` | object | yes | Existing registry binding metadata |
| `provider_connection_id` | int | no | Existing binding identity when present |
| `target_scope` | object | yes | Canonical shared descriptor |
| `provider_context` | object | no | Nested provider-owned details when required for follow-up |
**Rules**:
- Started and blocked runs must use the same neutral shared `target_scope` schema.
- Shared run context must stop writing `target_scope.entra_tenant_id` as the primary contract.
- Provider-specific fields needed for follow-up or troubleshooting move to nested `provider_context` or equivalent provider-owned metadata.
- Current dedupe identity remains `provider_connection_id` plus existing identity inputs; this slice does not redefine run identity semantics.
### 8. Credential Scope Validation Invariant
**Persistence**: derived runtime validation only
**Owner**: `CredentialManager`
| Field | Type | Required | Notes |
|---|---|---|---|
| `provider_connection_id` | int | yes | Existing credential owner |
| `payload.client_id` | string | yes | Existing credential field |
| `payload.client_secret` | string | yes | Existing credential field |
| `payload.scope_assertion` | mixed | no | Existing payload assertion if present today |
| `normalized_target_scope_identifier` | string | yes | Derived from canonical shared descriptor |
**Rules**:
- No provider-credential schema change is introduced.
- If a payload carries a scope assertion, validation should compare it to the normalized target-scope identifier rather than leaking raw provider-column names into the platform-core error contract.
- Neutral mismatch wording belongs in the shared seam; provider-specific values remain nested metadata only.
### 9. Provider Boundary Review Record
**Persistence**: config-driven
**Owner**: `config/provider_boundaries.php`
| Field | Type | Required | Notes |
|---|---|---|---|
| `seam_key` | string | yes | Boundary seam identifier |
| `owner` | string | yes | `platform_core` or `provider_owned` |
| `neutral_terms` | list<string> | yes | Shared vocabulary allowed at the seam |
| `retained_provider_semantics` | list<string> | yes | Documented provider-specific exceptions |
| `follow_up_action` | string | yes | Existing review follow-up rule |
**Rules**:
- `provider.connection_resolution`, `provider.identity_resolution`, and `provider.operation_start_gate` remain platform-core seams and must carry only the documented provider-specific exceptions.
- `provider.gateway_runtime` remains provider-owned.
- `config/provider_boundaries.php` stays the single review record for this classification; the slice does not create a new taxonomy.
## Contract Flow
1. `ProviderConnection` is loaded inside its current workspace plus managed-environment scope.
2. `ProviderConnectionTargetScopeNormalizer` derives the canonical shared `target_scope` descriptor and the nested `provider_context` wrapper.
3. `ProviderConnectionResolver` validates enablement, consent, and supported binding using the normalized `target_scope` contract.
4. `ProviderIdentityResolver` emits one `ProviderIdentityResolution` result centered on target scope, effective client identity, and nested provider context.
5. `ProviderConnectionSurfaceSummary` renders the same summary contract for the provider-connections resource, onboarding, and related-context surfaces.
6. `ProviderOperationStartGate` records the same neutral `target_scope` contract into `OperationRun` context while nesting any provider-only detail under provider context.
7. Provider-owned consumers such as admin-consent URL shaping and Graph runtime mapping read the nested provider context they need without re-promoting those values into shared platform-core vocabulary.
## Deferred Boundaries
- No new provider implementation is introduced.
- No provider-profile table, registry, package engine, or artifact taxonomy is introduced.
- No routing work from Spec `280` is absorbed.
- No RBAC redesign, copy-neutralization, or cutover quality-gate work from Specs `282` through `287` is introduced.