TenantAtlas/specs/089-provider-connections-tenantless-ui/data-model.md
2026-02-12 17:32:08 +01:00

3.6 KiB

Phase 1 Design — Data Model (Provider Connections)

Feature: Provider Connections (tenantless UI + tenant transparency)

This feature primarily re-frames existing data under a canonical tenantless UI while enforcing workspace/tenant isolation.

Entities

Workspace

Represents the top-level isolation boundary. Users must be workspace members to access any Provider Connections surface.

Key relationships

  • Workspace has many Tenants.
  • Workspace has many ProviderConnections.

Tenant

A managed tenant inside a Workspace.

Key relationships

  • Tenant belongs to Workspace (tenants.workspace_id).
  • Tenant has many ProviderConnections (provider_connections.tenant_id).

ProviderConnection

An integration record owned by exactly one Tenant (and by extension, one Workspace).

Storage: provider_connections (PostgreSQL)

Key columns (existing schema)

  • workspace_id (FK-ish, scoped to Workspace)
  • tenant_id (FK to Tenants)
  • provider (string; MVP: microsoft)
  • entra_tenant_id (string)
  • display_name (string)
  • is_default (bool)
  • status (string)
  • health_status (string)
  • scopes_granted (jsonb)
  • last_health_check_at (timestamp)
  • last_error_reason_code (string|null)
  • last_error_message (string|null; sanitized/truncated)
  • metadata (jsonb)

Constraints / indexes (existing)

  • Unique: (tenant_id, provider, entra_tenant_id)
  • Partial unique: one default per (tenant_id, provider) where is_default = true

Relationships

  • ProviderConnection belongs to Tenant.
  • ProviderConnection belongs to Workspace.

State / badges

  • status: expected to map through BadgeCatalog (domain: ProviderConnectionStatus)
  • health_status: expected to map through BadgeCatalog (domain: ProviderConnectionHealth)

TenantMembership (isolation boundary)

A user-to-tenant entitlement table (exact schema is implementation-defined in the app; referenced by existing auth services).

Purpose

  • Drives query-time scoping (JOIN-based) for list views.
  • Drives deny-as-not-found semantics for direct record access.

WorkspaceMembership (isolation boundary)

A user-to-workspace entitlement table.

Purpose

  • Gates the entire feature with 404 semantics for non-members.

AuditLog (governance)

Used for state-changing actions (manage actions).

Requirements

  • Stable action IDs.
  • Redacted/sanitized payloads (no secrets).

OperationRun (observability)

Used for run/health actions.

Requirements

  • Canonical “view run” link.
  • Reason codes + sanitized messages.

Query Scoping (JOIN-based)

Goal: No metadata leaks and MSP-scale performance.

List and global data pulls MUST:

  • Start from provider_connections.
  • JOIN to tenants and membership tables to enforce entitlement.
  • Optionally apply a tenant filter based on:
    1. tenant_id querystring (takes precedence)
    2. otherwise, active TenantContext-derived default

Direct record access MUST:

  • Resolve the owning tenant from the record.
  • If user is not entitled to that tenant (or not a workspace member), respond 404.
  • If entitled but lacking capability, respond 403.

Validation Rules (UI-level)

  • provider must be one of allowed providers (MVP: microsoft).
  • entra_tenant_id must be present and formatted as expected (string).
  • display_name required.
  • Actions that toggle is_default must preserve the partial unique invariant.
  • Any stored error messages must be sanitized and truncated.

Data Minimization / Secrets

  • No plaintext secrets are ever rendered.
  • No “copy secret” affordances.
  • Non-secret identifiers (e.g., Entra tenant ID) may be copyable.