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
117 lines
7.1 KiB
Markdown
117 lines
7.1 KiB
Markdown
# Implementation Plan: Provider Connections (Tenantless UI + Tenant Transparency)
|
|
|
|
**Branch**: `089-provider-connections-tenantless-ui` | **Date**: 2026-02-12 | **Spec**: `specs/089-provider-connections-tenantless-ui/spec.md`
|
|
**Input**: Feature specification from `specs/089-provider-connections-tenantless-ui/spec.md`
|
|
|
|
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.
|
|
|
|
## Summary
|
|
|
|
Move Provider Connections to a canonical tenantless admin route (`/admin/provider-connections`) while enforcing workspace/tenant isolation (404 for non-members) and capability-first authorization (403 for members lacking capability). The list/details stay tenant-transparent (tenant column + deep links) and respect an active tenant context for default filtering, with a `tenant_id` querystring override.
|
|
|
|
## Technical Context
|
|
|
|
<!--
|
|
ACTION REQUIRED: Replace the content in this section with the technical details
|
|
for the project. The structure here is presented in advisory capacity to guide
|
|
the iteration process.
|
|
-->
|
|
|
|
**Language/Version**: PHP 8.4.15 (Laravel 12)
|
|
**Primary Dependencies**: Filament v5, Livewire v4, Laravel Sail, PostgreSQL
|
|
**Storage**: PostgreSQL (Sail locally; production via Dokploy)
|
|
**Testing**: Pest v4 (+ PHPUnit runner), Livewire component testing for Filament pages/actions
|
|
**Target Platform**: Web app (Laravel), admin panel under `/admin`
|
|
**Project Type**: Web application (single Laravel monolith)
|
|
**Performance Goals**: JOIN-based membership scoping (no large `whereIn` lists), eager-load tenant on list/detail, paginate safely for MSP-scale datasets
|
|
**Constraints**: Workspace non-members are 404; tenant non-members are 404; capability denial is 403 after membership is established; no plaintext secrets rendered; global search disabled for Provider Connections
|
|
**Scale/Scope**: MSP-style workspaces with many tenants and many provider connections
|
|
|
|
## Constitution Check
|
|
|
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
|
|
- Inventory-first: clarify what is “last observed” vs snapshots/backups
|
|
- Read/write separation: any writes require preview + confirmation + audit + tests
|
|
- Graph contract path: Graph calls only via `GraphClientInterface` + `config/graph_contracts.php`
|
|
- Deterministic capabilities: capability derivation is testable (snapshot/golden tests)
|
|
- RBAC-UX: two planes (/admin vs /system) remain separated; cross-plane is 404; non-member tenant access is 404; member-but-missing-capability is 403; authorization checks use Gates/Policies + capability registries (no raw strings, no role-string checks)
|
|
- Workspace isolation: non-member workspace access is 404; tenant-plane routes require an established workspace context; workspace context switching is separate from Filament Tenancy
|
|
- RBAC-UX: destructive-like actions require `->requiresConfirmation()` and clear warning text
|
|
- RBAC-UX: global search is tenant-scoped; non-members get no hints; inaccessible results are treated as not found (404 semantics)
|
|
- Tenant isolation: all reads/writes tenant-scoped; cross-tenant views are explicit and access-checked
|
|
- Run observability: long-running/remote/queued work creates/reuses `OperationRun`; start surfaces enqueue-only; Monitoring is DB-only; DB-only <2s actions may skip runs but security-relevant ones still audit-log; auth handshake exception OPS-EX-AUTH-001 allows synchronous outbound HTTP on `/auth/*` without `OperationRun`
|
|
- Automation: queued/scheduled ops use locks + idempotency; handle 429/503 with backoff+jitter
|
|
- Data minimization: Inventory stores metadata + whitelisted meta; logs contain no secrets/tokens
|
|
- Badge semantics (BADGE-001): status-like badges use `BadgeCatalog` / `BadgeRenderer`; no ad-hoc mappings; new values include tests
|
|
- Filament UI Action Surface Contract: for any new/modified Filament Resource/RelationManager/Page, define Header/Row/Bulk/Empty-State actions, ensure every List/Table has a record inspection affordance (prefer `recordUrl()` clickable rows; do not render a lone View row action), keep max 2 visible row actions with the rest in “More”, group bulk actions, require confirmations for destructive actions (typed confirmation for large/bulk where applicable), write audit logs for mutations, enforce RBAC via central helpers (non-member 404, member missing capability 403), and ensure CI blocks merges if the contract is violated or not explicitly exempted
|
|
|
|
**Gate evaluation (pre-Phase 0)**
|
|
|
|
- Inventory-first: Not impacted (UI/routing + entitlement/scoping only).
|
|
- Read/write separation: Manage actions are state-changing and will be confirmed + audited; run/health uses OperationRun.
|
|
- Graph contract path: Any health check job must use existing Graph abstractions; no external calls at render time.
|
|
- Deterministic capabilities: Uses existing capability registry (`PROVIDER_VIEW`, `PROVIDER_MANAGE`, `PROVIDER_RUN`).
|
|
- Workspace/Tenant isolation: Enforced via existing membership middleware + policy deny-as-not-found patterns.
|
|
- Global search: Explicitly disabled for Provider Connections.
|
|
- Action surface contract: List/detail surfaces must keep inspection affordance and proper action grouping.
|
|
|
|
## Project Structure
|
|
|
|
### Documentation (this feature)
|
|
|
|
```text
|
|
specs/089-provider-connections-tenantless-ui/
|
|
├── plan.md # This file (/speckit.plan command output)
|
|
├── research.md # Phase 0 output (/speckit.plan command)
|
|
├── data-model.md # Phase 1 output (/speckit.plan command)
|
|
├── quickstart.md # Phase 1 output (/speckit.plan command)
|
|
├── contracts/ # Phase 1 output (/speckit.plan command)
|
|
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
|
|
```
|
|
|
|
### Source Code (repository root)
|
|
```text
|
|
app/
|
|
├── Filament/
|
|
│ └── Resources/
|
|
│ └── ProviderConnectionResource.php
|
|
│ └── ProviderConnectionResource/
|
|
│ └── Pages/
|
|
│ ├── ListProviderConnections.php
|
|
│ ├── CreateProviderConnection.php
|
|
│ └── EditProviderConnection.php
|
|
├── Policies/
|
|
│ └── ProviderConnectionPolicy.php
|
|
├── Support/
|
|
│ ├── Auth/Capabilities.php
|
|
│ ├── Rbac/UiEnforcement.php
|
|
│ └── Workspaces/WorkspaceContext.php
|
|
└── Http/
|
|
└── Middleware/
|
|
├── EnsureWorkspaceMember.php
|
|
└── EnsureWorkspaceSelected.php
|
|
|
|
routes/
|
|
└── web.php
|
|
|
|
tests/
|
|
├── Feature/
|
|
└── Unit/
|
|
├── Filament/
|
|
└── Policies/
|
|
```
|
|
|
|
**Structure Decision**: Implement as a Laravel/Filament change (resource slug, navigation placement, scoping, and legacy redirects) plus targeted Pest tests. No new standalone services or packages are required.
|
|
|
|
## Complexity Tracking
|
|
|
|
> **Fill ONLY if Constitution Check has violations that must be justified**
|
|
|
|
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
|
|-----------|------------|-------------------------------------|
|
|
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
|
|
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
|
|
|
|
No constitution violations are required for this feature.
|