189 lines
9.3 KiB
Markdown
189 lines
9.3 KiB
Markdown
# Implementation Plan: Spec 080 Workspace-Managed Tenant Administration Migration
|
|
|
|
**Branch**: `080-workspace-managed-tenant-admin` | **Date**: 2026-02-07 | **Spec**: [/specs/080-workspace-managed-tenant-admin/spec.md](spec.md)
|
|
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/spec.md`
|
|
|
|
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.
|
|
|
|
## Summary
|
|
|
|
Migrate tenant administration surfaces out of tenant scope (`/admin/t/{tenant}/*`) into workspace scope (`/admin/*`) and keep tenant scope strictly for operational modules.
|
|
|
|
Implementation strategy:
|
|
- Introduce a second Filament panel for tenant operations at `/admin/t/{tenant}`.
|
|
- Convert the existing admin panel at `/admin` into a tenantless workspace management panel.
|
|
- Register management resources/pages only in the workspace panel, ensuring tenant-scoped management routes are not registered (404).
|
|
- Rewire internal CTAs/links (onboarding, required permissions, provider connection edit) to the new canonical workspace routes.
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: PHP 8.4.15 (Laravel 12)
|
|
**Primary Dependencies**: Filament v5, Livewire v4, Tailwind v4
|
|
**Storage**: PostgreSQL (via Sail)
|
|
**Testing**: Pest v4 (PHPUnit v12 runner)
|
|
**Target Platform**: Web application (server-rendered Filament/Livewire)
|
|
**Project Type**: Laravel monolith
|
|
**Performance Goals**: No new performance targets; management viewers must remain DB-only at render time
|
|
**Constraints**:
|
|
- No external calls during render for management viewers (DB-only).
|
|
- Dev-stage removed routes must 404 (no redirects).
|
|
- 404 vs 403 semantics must follow RBAC-UX.
|
|
**Scale/Scope**: Enterprise SaaS IA separation across admin surfaces (route + navigation correctness)
|
|
|
|
## Constitution Check
|
|
|
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
|
|
Assessment (pre-Phase 0): PASS
|
|
- No new Graph calls are introduced by this migration.
|
|
- No new long-running operations are introduced.
|
|
- Authorization behavior is being tightened via panel separation and route registration.
|
|
- Monitoring/management viewers remain DB-only.
|
|
|
|
Notes:
|
|
- This feature changes how routes are registered (which affects discovery, global search, and navigation). It must include regression tests ensuring removed tenant-scoped management routes do not exist.
|
|
|
|
- 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)
|
|
- 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
|
|
|
|
## Project Structure
|
|
|
|
### Documentation (this feature)
|
|
|
|
```text
|
|
specs/[###-feature]/
|
|
├── 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/
|
|
│ ├── Pages/
|
|
│ │ ├── Workspaces/
|
|
│ │ ├── Monitoring/
|
|
│ │ └── …
|
|
│ ├── Resources/
|
|
│ └── Concerns/
|
|
├── Providers/
|
|
│ └── Filament/
|
|
│ ├── AdminPanelProvider.php
|
|
│ ├── TenantPanelProvider.php
|
|
│ └── SystemPanelProvider.php
|
|
├── Support/
|
|
│ └── Middleware/
|
|
routes/
|
|
└── web.php
|
|
|
|
tests/
|
|
├── Feature/
|
|
└── Unit/
|
|
|
|
bootstrap/
|
|
└── providers.php
|
|
```
|
|
|
|
**Structure Decision**: Laravel monolith. Panel providers define panel boundaries. Routes outside Filament are defined in `routes/web.php`.
|
|
|
|
## Complexity Tracking
|
|
|
|
No constitution violations requiring justification.
|
|
|
|
## Phase 0 — Outline & Research
|
|
|
|
Outputs (written during `/speckit.plan`):
|
|
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/research.md`
|
|
|
|
Research questions (all resolved):
|
|
- How to configure a tenancy panel whose URLs are `/admin/t/{tenant}` without duplicating `/t/`.
|
|
- How to enforce route removal: do not register resources/pages in the tenant panel.
|
|
- How to keep global search isolated by panel registration.
|
|
|
|
## Phase 1 — Design & Contracts
|
|
|
|
Outputs:
|
|
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/data-model.md`
|
|
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/contracts/`
|
|
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/080-workspace-managed-tenant-admin/quickstart.md`
|
|
|
|
### Panel Design
|
|
|
|
**Workspace panel (Manage)**
|
|
- ID: `admin` (keep existing ID to avoid breaking `panel:admin` middleware usage)
|
|
- Path: `/admin`
|
|
- Tenancy: disabled (no `->tenant(...)`)
|
|
- Registered artifacts: tenant management resources/pages, monitoring pages.
|
|
|
|
**Tenant panel (Operate)**
|
|
- ID: `tenant` (new)
|
|
- Path: `/admin/t`
|
|
- Tenancy: enabled via `Tenant::class` with `slugAttribute: 'external_id'`
|
|
- Tenant route prefix: blank/`null` so tenant routes become `/admin/t/{tenant}/...`
|
|
- Registered artifacts: operational resources/pages only (inventory, drift, backups, policies, directory, etc.).
|
|
- Route-shape verification is mandatory: automated regression checks must assert canonical tenant URLs are `/admin/t/{tenant}` and `/admin/t/{tenant}/...` (never `/admin/t/t/{tenant}`).
|
|
|
|
Laravel 11+ provider registration requirement:
|
|
- Register the new tenant panel provider in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/bootstrap/providers.php`.
|
|
|
|
### Routing/Removal Mechanism
|
|
|
|
Tenant-scoped management routes return 404 by construction:
|
|
- Management resources/pages are **not registered** in the tenant panel.
|
|
- No redirects (dev-stage).
|
|
|
|
### Authorization Design
|
|
|
|
- Workspace management pages: require selected workspace + membership; non-member → 404.
|
|
- Tenant operational routes: require workspace membership + tenant entitlement; non-entitled → 404.
|
|
- Mutations: capability missing → 403 (server-side policy/gate); destructive-like actions require `->requiresConfirmation()`.
|
|
|
|
### Global Search Design
|
|
|
|
- Workspace panel: managed tenants are searchable (ensure resource has Edit/View page).
|
|
- Tenant panel: tenant-management entities are not registered, so they cannot appear in global search.
|
|
|
|
## Phase 2 — Implementation Plan (Code + Tests)
|
|
|
|
Stop condition for `/speckit.plan`: this section outlines implementation, but actual task breakdown happens in `/speckit.tasks`.
|
|
|
|
Planned steps:
|
|
1. Add new panel provider for tenant operations (e.g., `App\Providers\Filament\TenantPanelProvider`).
|
|
2. Register provider in `bootstrap/providers.php` (Laravel 11+ pattern).
|
|
3. Refactor `AdminPanelProvider` into a tenantless workspace panel:
|
|
- remove tenancy configuration (`->tenant(...)`, tenant menu, tenant route prefix)
|
|
- remove tenant-only middleware from the workspace panel pipeline
|
|
4. Move/register management pages/resources into workspace panel:
|
|
- `TenantResource` (managed tenants CRUD / manage view)
|
|
- `ProviderConnectionResource` (workspace-managed connections by tenant)
|
|
- required permissions viewer page
|
|
- membership management surfaces
|
|
- onboarding/activation surfaces
|
|
5. Move/register operational pages/resources into the tenant panel.
|
|
6. Rewire internal links/CTAs that currently build tenant-scoped management URLs to the new workspace-managed canonical URLs.
|
|
7. Add regression tests (Pest) to cover:
|
|
- workspace member can access `/admin/tenants*`
|
|
- non-member gets 404
|
|
- tenant entitlement required for `/admin/t/{tenant}/...`
|
|
- canonical tenant panel route shape is `/admin/t/{tenant}/...` (no duplicated `/t`)
|
|
- tenant-scoped management routes are missing (404)
|
|
- link rewiring expectations (where feasible)
|
|
8. Run targeted test pack and Pint:
|
|
- `vendor/bin/sail artisan test --compact tests/Feature/...`
|
|
- `vendor/bin/sail bin pint --dirty`
|