TenantAtlas/specs/089-provider-connections-tenantless-ui/spec.md
ahmido fb4de17c63 feat(spec-089): provider connections tenantless UI (#107)
Implements Spec 089: moves Provider Connections to canonical tenantless route under `/admin/provider-connections`, enforces 404/403 semantics (workspace/tenant membership vs capability), adds tenant transparency (tenant column + filter + deep links), adds legacy redirects for old tenant-scoped URLs without leaking Location for 404 cases, and adds regression test coverage (RBAC semantics, filters, UI enforcement tooltips, Microsoft-only MVP scope, navigation placement).

Notes:
- Filament v5 / Livewire v4 compatible.
- Global search remains disabled for Provider Connections.
- Destructive/manage actions require confirmation and are policy-gated.

Tests:
- `vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #107
2026-02-12 16:35:13 +00:00

16 KiB
Raw Blame History

Feature Specification: Provider Connections (Tenantless UI + Tenant Transparency)

Feature Branch: 089-provider-connections-tenantless-ui
Created: 2026-02-12
Status: Draft
Input: User description: "Provider Connections als workspace-weites Integrations-Asset (tenantless UI) + Tenant-Transparenz"

Clarifications

Session 2026-02-12

  • Q: Welche Provider sind im MVP-Scope dieser Spec? → A: Nur Microsoft (Graph) im MVP; UI/IA bleibt generisch „Provider Connections“.
  • Q: Wie wird „aktiver Tenant-Kontext“ für den Default-Filter bestimmt? → A: TenantContext kommt aus Session/Context-Switcher; tenant_id Querystring kann den Default überschreiben.
  • Q: Soll „Provider Connections“ in Filament Global Search erscheinen? → A: Nein, Global Search ist für diese Resource deaktiviert.
  • Q: Wie soll die Auditability konkret umgesetzt werden? → A: AuditLog + OperationRun.

User Scenarios & Testing (mandatory)

User Story 1 - Workspace-weite Übersicht (Priority: P1)

Als Operator/Admin im Workspace möchte ich Provider Connections zentral unter Settings → Integrations finden und über eine canonical Route ohne Tenant-Parameter aufrufen, damit Integrationen enterprise-typisch auffindbar sind und die UI nicht von tenant-scoped Routes abhängt.

Why this priority: Stellt Informationsarchitektur wieder her, reduziert Kontextbrüche und eliminiert fragiles tenant-scoped Routing als Voraussetzung.

Independent Test: Aufruf der canonical Liste und Verifikation von Scoping + Default Filter + Tenant-Spalte.

Acceptance Scenarios:

  1. Given ein User ist Mitglied im Workspace und Mitglied in Tenant A, aber nicht in Tenant B, When er die Provider-Connections-Liste öffnet, Then sieht er ausschließlich Connections aus Tenant A.
  2. Given ein aktiver Tenant-Kontext ist gesetzt (Tenant A), When die Liste geöffnet wird, Then ist initial ein Tenant-A Filter aktiv und kann vom User entfernt werden.
  3. Given ein aktiver Tenant-Kontext ist gesetzt (Tenant A) und der User ruft die Liste mit ?tenant_id=<tenant_external_id_von_TenantB> auf, When die Liste lädt, Then ist Tenant B als Filter aktiv (Querystring überschreibt den Context-Default), ohne dass nicht-berechtigte Tenants Metadaten leaken.
  4. Given ein User ist kein Workspace-Mitglied, When er die canonical Provider-Connections-Route aufruft, Then erhält er 404 (deny-as-not-found).

User Story 2 - Sicherer Detail-/Edit-Zugriff ohne Secrets (Priority: P2)

Als Tenant-Mitglied möchte ich eine Provider Connection ansehen und (mit entsprechender Berechtigung) verwalten, damit ich Integrationsprobleme diagnostizieren und beheben kann, ohne dass Secrets im Klartext sichtbar werden.

Why this priority: Detail-/Edit-Surfaces sind die riskantesten Stellen für Metadaten-Leaks und Secret-Exfiltration.

Independent Test: Direkte View/Edit Zugriffe mit unterschiedlichen Membership-/Capability-Kombinationen; UI zeigt keine Klartext-Secrets.

Acceptance Scenarios:

  1. Given ein User ist Workspace-Mitglied aber nicht Mitglied im owning Tenant einer Connection, When er die Detailseite direkt aufruft, Then erhält er 404.
  2. Given ein User ist Tenant-Mitglied, aber ihm fehlt die View-Capability, When er Liste oder Detailseite aufruft, Then erhält er 403.
  3. Given ein User hat View-, aber nicht Manage-Capability, When er die Detailseite öffnet, Then sind Manage-Aktionen sichtbar aber deaktiviert (mit Tooltip), und ein server-seitiger Mutationsversuch wird mit 403 abgewiesen.
  4. Given irgendein berechtigter User sieht Liste/Detail, When Credentials/Secrets dargestellt werden, Then werden niemals Klartext-Secrets angezeigt und es gibt keine Copy-Aktionen für Secrets.

User Story 3 - Tenant-Detailseite zeigt effektiven Provider-State + Deep Link (Priority: P3)

Als Operator im Tenant-Kontext möchte ich auf der Tenant-Detailseite den effektiven Default-Provider-Connection-Status sehen und über eine CTA direkt zur vorgefilterten Provider-Connections-Liste springen, damit ich Kontext behalte und trotzdem zentral arbeiten kann.

Why this priority: Reduziert Kontextbruch und macht den „effective state“ dort sichtbar, wo Operators ihn erwarten.

Independent Test: Tenant-Detailseite zeigt Card + CTA; CTA führt zur gefilterten canonical Liste.

Acceptance Scenarios:

  1. Given Tenant A hat eine aufgelöste Default-Connection, When der User die Tenant-Detailseite öffnet, Then sieht er Display Name + Status/Health + Last Check.
  2. Given Tenant A hat keine gültige Default-Connection, When der User die Tenant-Detailseite öffnet, Then sieht er einen klaren „Needs action“-State und eine CTA zur gefilterten Liste.

Edge Cases

  • User ist Workspace-Mitglied, aber in keinem Tenant Mitglied → Liste zeigt 0 Rows (ohne Tenant-Hinweise).
  • tenant_id Filter verweist auf einen nicht-mitgliedschaftlich berechtigten Tenant → Liste zeigt 0 Rows; direkte Record-Zugriffe bleiben 404.
  • Mitgliedschaft wird entzogen → direkte Zugriffe auf vormals sichtbare Records liefern 404.
  • Sehr viele Tenants/Connections (MSP) → Scoping bleibt performant (server-seitige, membership-basierte Filterung ohne große In-Memory-ID-Listen).
  • Health/Last Error enthält sensitive Inhalte → UI zeigt nur Reason Codes und gekürzte, nicht-sensitive Messages; keine Secrets.

Requirements (mandatory)

Functional Requirements

  • FR-001 (Canonical route): The system MUST expose Provider Connections at a canonical admin route without requiring a tenant route parameter (e.g., /admin/provider-connections).

  • FR-002 (Tenant filter): The canonical list MUST support optional filtering by owning tenant (e.g., ?tenant_id=<tenant_external_id>).

  • FR-003 (Tenant transparency): List and detail MUST clearly show the owning tenant (name + environment indicator where applicable) and provide a deep link to tenant detail.

  • FR-004 (Context-respecting default filter): If a tenant context is active, the list MUST default to filtering by the current tenant. The user MUST be able to remove the filter.

  • FR-004a (Default resolution precedence): The default tenant filter MUST be resolved from the active TenantContext (session/context switcher). If a tenant_id query parameter is present, it MUST take precedence over the TenantContext-derived default.

  • FR-005 (Zero-leak scoping): Index/List MUST only return connections for tenants where the user is a member. The UI MUST not reveal metadata for non-member tenants.

  • FR-006 (Direct access semantics): Direct record access (view/edit) MUST be deny-as-not-found (404) when the user is not a member of the owning tenant.

  • FR-007 (Workspace gating): If the user is not a workspace member, all Provider Connections routes MUST return 404.

  • FR-008 (Capability-first RBAC): Capabilities MUST gate: view (list/detail), manage (create/edit/enable/disable/set-default/credential updates), run (health checks / operation triggers). Missing capability MUST return 403.

  • FR-009 (UI enforcement behavior): When missing capability but otherwise eligible (member), the UI SHOULD render actions disabled with a tooltip describing the missing capability (not silently removed), while server-side enforcement stays authoritative.

  • FR-010 (No secrets in UI): The UI MUST never display plaintext secrets and MUST NOT provide copy actions for secrets. Non-secret identifiers (e.g., Entra tenant ID) MAY be copyable.

  • FR-011 (List columns minimum): The list MUST include at least: Tenant, Provider, Display name, Entra tenant ID, Default indicator, Status badge, Health badge, Last check timestamp, Last error (reason code + truncated message).

  • FR-012 (List filters minimum): The list MUST provide filters for: Tenant, Provider, Status, Health, Default-only.

  • FR-013 (Tenant detail card): Tenant detail MUST show an “effective provider connection state” for the tenant and provide a CTA to open the canonical Provider Connections list pre-filtered for that tenant.

  • FR-014 (Legacy redirect): Legacy tenant-scoped URLs for provider connections MUST behave according to the “Legacy URL Redirect Matrix” (302 redirects only for entitled members; no-leak 404 otherwise) and remain for at least two releases.

  • FR-015 (Scalability requirement): Tenant-visibility scoping MUST be performed at query time based on membership relationships and MUST NOT depend on loading large tenant-id lists into memory.

  • FR-016 (MVP provider scope): The MVP MUST support Microsoft (Graph) as the only provider. The navigation label and IA remain generic (“Provider Connections”).

  • FR-017 (Global Search): Provider Connections MUST NOT appear in global search.

  • FR-018 (Create tenant resolution): Create MUST resolve the target tenant to an entitled tenant via tenant_id query parameter or the active TenantContext default. If no entitled tenant can be resolved, the create surface MUST behave as not found (404).

Information Architecture

  • Provider Connections MUST be placed under Settings → Integrations → Provider Connections.
  • Provider Connections MUST feel “workspace-level” even when a tenant context is active (context affects default filtering, not canonical addressability).

Scope Boundaries

  • In scope: tenantless canonical navigation and routing; tenant transparency in list/detail; context-respecting default filter; deny-as-not-found membership rules; capability-first action gating; legacy redirect behavior.
  • Out of scope: shared connections across multiple tenants; redesign of default/override semantics; changing the meaning of “default”; large wizard refactors.

Assumptions & Dependencies

  • A "workspace" membership model exists and can be evaluated for every request.
  • A "tenant membership" model exists and is the source of truth for which tenants a user may access.
  • Provider Connections already belong to exactly one owning tenant and one workspace.
  • A tenant detail surface exists where the “Provider connection” card can be shown.
  • The system has, or can represent, a provider identifier for connections (even if only Microsoft exists in the MVP).
  • The system has an active TenantContext concept (e.g., chosen via a context switcher) that can be read when rendering admin pages.

Query Parameter Contract

  • QP-001: The canonical tenant filter query parameter name is tenant_id.
  • QP-002: The tenant_id value is the managed tenants external identifier (the same identifier used in /admin/tenants/{tenant} routes), not a database primary key.

Tenant Transparency Conventions

  • TT-001: “Environment indicator” means a human-readable label that distinguishes tenant environments when such labeling exists in the workspace (e.g., Production vs Staging). If no such label exists for a tenant, the UI MUST omit the environment indicator (not substitute guesses).

Default Filter Removal

  • DF-001: The default tenant filter is a usability default only. Users MAY remove it at any time.
  • DF-002: Removing the default filter MUST NOT change authorization boundaries: list and detail remain scoped to tenants the user is a member of.

Authorization Semantics (404 vs 403)

  • AS-001: Not a workspace member → 404 for list/detail/edit and any action endpoints.
  • AS-002: Workspace member but not a member of the owning tenant → list returns no rows for that tenant; direct record access → 404.
  • AS-003: Tenant member but missing required capability → 403 for the protected surface/action.

Audit & Observability

  • AO-001: User-initiated actions that change state (set default, enable/disable, credentials update/rotate) MUST be auditable.
  • AO-002: User-initiated run/health actions MUST be auditable, either via an audit event or a run record that is reachable via a canonical “view run” link.
  • AO-003 (Decision): Manage/state-change actions MUST write an AuditLog entry. Run/health actions MUST create an OperationRun (or equivalent run record) that is reachable via a canonical “view run” link.

Backward Compatibility

  • BC-001: Redirect MUST preserve intent (tenant filter) and MUST NOT leak tenant names/metadata for non-members.
  • BC-002: Deprecation window is at least two releases.

Legacy URL Redirect Matrix

  • LR-001 (Redirect, tenant-managed scope): Requests to legacy workspace-managed tenant routes MUST redirect (302) to the canonical route and preserve intent via the tenant filter:
    • /admin/tenants/{tenant_external_id}/provider-connections/admin/provider-connections?tenant_id={tenant_external_id}
    • /admin/tenants/{tenant_external_id}/provider-connections/create/admin/provider-connections/create?tenant_id={tenant_external_id}
    • /admin/tenants/{tenant_external_id}/provider-connections/{record}/edit/admin/provider-connections/{record}/edit?tenant_id={tenant_external_id}
  • LR-002 (No-leak): For non-workspace members or users who are not members of the target tenant, legacy URLs MUST behave as not found (404) and MUST NOT redirect.
  • LR-003 (Explicit exclusion): The previously removed tenant-panel management route shape /admin/t/{tenant_external_id}/provider-connections remains not found (404) and is not part of the redirect surface.

UI Action Matrix (mandatory when Filament is changed)

Surface Location Header Actions Inspect Affordance (List/Table) Row Actions (max 2 visible) Bulk Actions (grouped) Empty-State CTA(s) View Header Actions Create/Edit Save+Cancel Audit log? Notes / Exemptions
Provider Connections (List) Admin → Settings → Integrations → Provider Connections Create (manage) Tenant deep link + View action View (view), Edit (manage) None (not required) Create (manage) n/a n/a Yes Default tenant filter applies when tenant context active
Provider Connection (View) Provider Connections → View Enable/Disable (manage), Set default (manage), Health check (run), Credential update (manage) n/a Edit (manage) None n/a Same as header actions n/a Yes Non-membership is 404; missing capability is 403; destructive-like actions require confirmation
Provider Connection (Create/Edit) Provider Connections → Create/Edit None n/a None None n/a n/a Save (manage), Cancel Yes Secrets never displayed; credential updates require confirmation
Tenant detail “Provider connection” card Tenant detail page Open Provider Connections (view) n/a None None Optional: Create connection (manage) n/a n/a No CTA links to canonical list filtered by tenant

Key Entities (include if feature involves data)

  • Workspace: A membership-gated scope that owns managed tenants and integrations.
  • Managed Tenant: A tenant within a workspace; users are members of specific tenants.
  • Provider Connection: An integration record owned by exactly one managed tenant and one workspace; includes status/health metadata and non-secret identifiers.
  • Capabilities: Named permissions that gate view/manage/run behavior; UI enforcement reflects missing capabilities.
  • Audit Event / Run Record: Captures sensitive user-initiated actions for later review.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001 (IA discoverability): A workspace admin can reach Provider Connections from the admin sidebar in ≤ 2 clicks (Settings → Integrations → Provider Connections).
  • SC-002 (No metadata leaks): For non-members of a tenant, 100% of direct access attempts to that tenants Provider Connection detail/edit routes return 404 and do not reveal tenant/provider/status/health metadata.
  • SC-003 (RBAC correctness): For tenant members lacking a required capability, 100% of protected endpoints return 403; the UI consistently shows disabled actions with an explanatory tooltip.
  • SC-004 (Context-respecting UX): From a tenant detail page, the “Open Provider Connections” CTA lands on a pre-filtered list for that tenant with a first-attempt success rate ≥ 95% in acceptance testing.