93 lines
3.5 KiB
Markdown
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.
|