TenantAtlas/specs/067-rbac-troubleshooting/plan.md
ahmido 3490fb9e2c feat: RBAC troubleshooting & tenant UI bugfix pack (spec 067) (#84)
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
2026-01-31 20:09:25 +00:00

8.3 KiB

Implementation Plan: RBAC Troubleshooting & Tenant UI Bugfix Pack v1

Branch: 067-rbac-troubleshooting | Date: 2026-01-31 | Spec: spec.md Input: Feature specification from specs/067-rbac-troubleshooting/spec.md

Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.

Summary

Tighten tenant-plane (/admin) RBAC UX and eliminate sharp edges by:

  • Applying the existing UiEnforcement pattern consistently on Tenant admin surfaces (especially Tenant view header actions).
  • Ensuring archived (soft-deleted) tenants can still resolve in tenant-scoped routes for entitled members (404 only for non-members).
  • Adding a tenant-scoped diagnostics surface to detect and repair membership invariants (missing owner) and prevent GUID-vs-bigint mistakes.
  • Adding targeted Pest tests to lock in 404 vs 403 semantics and action disable/tooltip UX, and re-running existing last-owner invariant tests as regression coverage.

Technical Context

Language/Version: PHP 8.4 (per repo guidelines)
Primary Dependencies: Laravel 12, Filament v5, Livewire v4
Storage: PostgreSQL (via Laravel Sail)
Testing: Pest v4 (PHPUnit v12 runner)
Target Platform: Laravel Sail (Docker) on macOS/Linux
Project Type: Web application (Laravel monolith)
Performance Goals: N/A (admin UX + correctness)
Constraints: Tenant-plane only (/admin); no /system expansion; no new outbound integration calls for diagnostics; must preserve RBAC-UX semantics (404 vs 403)
Scale/Scope: Touches Tenant admin UI, tenancy binding for archived tenants, membership invariants + tests

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)
  • 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

Gate evaluation: PASS (no constitution violations intended).

  • No new Microsoft Graph call paths are introduced.
  • Mutations involved (archive/restore, membership repairs) keep explicit confirmation and server-side enforcement.
  • Auditing: membership repairs use existing audit logger patterns; tenant lifecycle actions already log.

Post-design re-check: PASS (design artifacts: research.md, data-model.md, contracts/).

Project Structure

Documentation (this feature)

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)

app/
├── Filament/
│   ├── Resources/
│   ├── Pages/
│   └── Widgets/
├── Models/
├── Policies/
├── Services/
└── Support/

database/
├── migrations/
└── factories/

resources/
├── views/
└── css/

routes/
└── web.php

tests/
├── Feature/
└── Unit/

Structure Decision: Laravel monolith. Changes will land in app/Filament/** (tenant UI), app/Models/** (tenant binding/invariants), app/Services/** (membership repairs), and tests/** (Pest feature tests).

Complexity Tracking

Fill ONLY if Constitution Check has violations that must be justified

N/A.

Phase 0 — Research (repo-backed)

Key findings (evidence)

Decisions

  • Archived tenant 404s for members: implement tenant route binding that includes soft-deleted tenants, then rely on membership middleware to enforce deny-as-not-found for non-members.
  • Replace ad-hoc tooltips on tenant actions with centralized UiTooltips constants (add a tenant-archived tooltip string).
  • Standardize tenant page header actions using UiEnforcement so member-without-capability sees disabled actions with tooltip (no normal-click 403).

Output: research.md

Phase 1 — Design (data model + contracts)

Design outline

  • Tenant status UX
    • Archived tenants remain viewable for entitled members.
    • Actions become status-aware (e.g., Deactivate disabled when already archived).
  • Diagnostics
    • Tenant-scoped, DB-only page that reports:
      • Missing owner (0 owners)
      • Identifier misuse risk (guardrails against using external GUID where an internal FK is expected)
    • Repair actions are capability-gated and audit-logged.

Artifacts

Phase 2 — Planning (for tasks.md)

Dependency-ordered implementation outline (will be expanded in tasks.md):

  1. Fix tenant view header actions (Edit/Deactivate) to use UI enforcement + tooltips.
  2. Add Restore icon consistency on tenant list row menu.
  3. Ensure archived tenant is resolvable in tenant-scoped routes for members (avoid false 404).
  4. Add tenant status banner on view page.
  5. Add diagnostics page + safe repair actions for “missing owner”.
  6. Add regression tests for:
    • Readonly UX (disabled + tooltip)
    • Readonly cannot deactivate
    • Archived tenant view access (member OK, non-member 404)
    • GUID vs bigint guard test
  7. Keep the no-ad-hoc authorization guard green.