TenantAtlas/specs/061-provider-foundation/data-model.md

93 lines
3.5 KiB
Markdown

# Data Model: Provider Foundation v1
**Branch**: `061-provider-foundation`
**Date**: 2026-01-24
**Spec**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/spec.md`
## Entities
### ProviderConnection
Represents a tenant-scoped connection to an external provider (v1: Microsoft).
**Identity**
- Scoped to a Suite Tenant (`tenant_id`).
- Canonical target scope identifier for Microsoft: `entra_tenant_id` (GUID).
- Uniqueness: `(tenant_id, provider, entra_tenant_id)` must be unique.
**Fields (suggested)**
- `id` (UUID)
- `tenant_id` (FK → tenants)
- `provider` (string; v1: `microsoft`)
- `entra_tenant_id` (string; GUID)
- `display_name` (string)
- `is_default` (boolean; exactly one default per `(tenant_id, provider)`)
- `status` (string enum): `connected | needs_consent | error | disabled`
- `health_status` (string enum): `ok | degraded | down`
- `scopes_granted` (json/jsonb; optional; stores observed granted scopes/permissions metadata)
- `last_health_check_at` (timestamp nullable)
- `last_error_reason_code` (string nullable; stable reason code)
- `last_error_message` (string nullable; sanitized short message)
- `metadata` (json/jsonb nullable; optional non-sensitive provider metadata)
- timestamps
**Indexes (suggested)**
- Unique index: `(tenant_id, provider, entra_tenant_id)`
- Partial unique index: `(tenant_id, provider)` where `is_default = true`
- Indexes for filtering: `(tenant_id, provider, status)`, `(tenant_id, provider, health_status)`
**State transitions (v1)**
- `status`: typically `needs_consent`/`error` → `connected` after successful health check; `disabled` is an explicit admin action.
- `health_status`: `ok` on successful check; `degraded/down` based on categorized failures (throttling vs outage vs auth).
---
### ProviderCredential
Represents securely stored credentials for exactly one provider connection.
**Identity**
- 1:1 with ProviderConnection (`provider_connection_id` unique).
**Fields (suggested)**
- `id` (UUID)
- `provider_connection_id` (FK → provider_connections, unique)
- `type` (string; v1: `client_secret`)
- `payload` (encrypted JSON/array)
- required keys (v1): `client_id`, `client_secret`
- optional key: `tenant_id` (should match ProviderConnection `entra_tenant_id` if stored)
- timestamps
**Constraints (suggested)**
- `payload` must be encrypted at rest.
- `payload` must never be exposed via UI/API serialization.
- If `payload.tenant_id` is stored, validate it matches `provider_connections.entra_tenant_id`.
---
### DefaultProviderConnection (concept)
Not a separate table by default; represented by `provider_connections.is_default = true`.
Rules:
- Multiple Microsoft connections per Suite Tenant are allowed.
- Exactly one default connection exists per `(tenant_id, provider)` and is used when starting operations without an explicit connection selection.
---
### OperationRun (existing)
Provider operations are tracked as `operation_runs` with provider context stored in the `context` JSON.
**Provider context fields (suggested)**
- `provider`: `microsoft`
- `provider_connection_id`: UUID
- `target_scope`: `{ "entra_tenant_id": "<guid>" }`
- `module`: `health_check | inventory | compliance`
- `selection`: optional selectors/filters for the operation (DB-only)
- `idempotency`: fingerprint/hash inputs used for dedupe
**Concurrency rules (from spec clarifications)**
- Same operation type + same scope: dedupe → return the active run.
- Different operation type while any run is active for the same scope: block as “scope busy” and link to the active run.