Summary
Implements Spec 067 “RBAC Troubleshooting & Tenant UI Bugfix Pack v1” for the tenant admin plane (/admin) with strict RBAC UX semantics:
Non-member tenant scope ⇒ 404 (deny-as-not-found)
Member lacking capability ⇒ 403 server-side, while the UI stays visible-but-disabled with standardized tooltips
What changed
Tenant view header actions now use centralized UI enforcement (no “normal click → error page” for readonly members).
Archived tenants remain resolvable in tenant-scoped routes for entitled members; an “Archived” banner is shown.
Adds tenant-scoped diagnostics page (/admin/t/{tenant}/diagnostics) with safe repair actions (confirmation + authorization + audit log).
Adds/updates targeted Pest tests to lock the 404 vs 403 semantics and action UX.
Implementation notes
Livewire v4.0+ compliance: Uses Filament v5 + Livewire v4 conventions; widget Blade views render a single root element.
Provider registration: Laravel 11+ providers stay in providers.php (no changes required).
Global search: No global search behavior/resources changed in this PR.
Destructive actions:
Tenant archive/restore/force delete and diagnostics repairs execute via ->action(...) and include ->requiresConfirmation().
Server-side authorization is enforced (non-members 404, insufficient capability 403).
Assets: No new assets. No change to php artisan filament:assets expectations.
Tests
Ran:
vendor/bin/sail bin pint --dirty
vendor/bin/sail artisan test --compact (focused files for Spec 067)
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #84
65 lines
2.3 KiB
Markdown
65 lines
2.3 KiB
Markdown
# Data Model — RBAC Troubleshooting & Tenant UI Bugfix Pack v1
|
|
|
|
**Spec**: [spec.md](spec.md)
|
|
|
|
No new persistent tables are required for v1. Diagnostics findings are computed at runtime from existing tables.
|
|
|
|
## Entities
|
|
|
|
### Tenant (`tenants`)
|
|
|
|
**Purpose**: Tenant-plane scope boundary, lifecycle state, and tenant-scoped configuration root.
|
|
|
|
**Key fields (existing)**:
|
|
- `id` (bigint, PK)
|
|
- `tenant_id` (string GUID, external Microsoft Entra tenant identifier)
|
|
- `external_id` (string, used as Filament tenancy slug; often mirrors `tenant_id`)
|
|
- `status` (string, e.g. `active`, `archived`)
|
|
- `deleted_at` (nullable timestamp, soft delete)
|
|
- `name`, `environment`, `is_current`, `metadata` (assorted)
|
|
|
|
**Lifecycle rules (existing)**:
|
|
- Soft delete sets `status='archived'`.
|
|
- Restore sets `status='active'`.
|
|
|
|
### TenantMembership (`tenant_memberships`)
|
|
|
|
**Purpose**: Tenant membership boundary + role assignment.
|
|
|
|
**Key fields (existing)**:
|
|
- `id` (uuid, PK)
|
|
- `tenant_id` (bigint, FK → `tenants.id`)
|
|
- `user_id` (bigint, FK → `users.id`)
|
|
- `role` (enum: `owner`, `manager`, `operator`, `readonly`)
|
|
- `source` / `source_ref` (provenance)
|
|
- `created_by_user_id` (nullable FK)
|
|
|
|
**Constraints (existing)**:
|
|
- Unique `(tenant_id, user_id)`
|
|
- Index `(tenant_id, role)`
|
|
|
|
### DiagnosticsFinding (computed, not persisted)
|
|
|
|
**Purpose**: Represent a detected integrity/operational issue for the current tenant.
|
|
|
|
**Proposed shape (runtime DTO / array)**:
|
|
- `id` (string, stable key like `missing_owner`)
|
|
- `severity` (string, e.g. `warning`/`critical`)
|
|
- `title` (string)
|
|
- `description` (string)
|
|
- `repair_actions` (array of available actions given actor capabilities)
|
|
|
|
## Derived rules / invariants
|
|
|
|
- **Missing owner**: `tenant_memberships` has zero rows with `role='owner'` for the tenant.
|
|
- **Duplicate membership**: more than one membership row exists for a given `(tenant_id, user_id)` (should be prevented by DB uniqueness; diagnostics treats this as “historical/edge-case”).
|
|
- **Identifier misuse risk**: any query for membership/tenant scoping must use internal tenant key (`tenants.id`) and not the GUID.
|
|
|
|
## State transitions
|
|
|
|
- Tenant status:
|
|
- `active` → `archived` on soft delete
|
|
- `archived` → `active` on restore
|
|
- Membership role transitions:
|
|
- Allowed transitions are existing (`owner`, `manager`, `operator`, `readonly`) but last-owner demotion/removal is prohibited.
|