6.4 KiB
Implementation Plan: Tenant RBAC v1
Branch: 065-tenant-rbac-v1 | Date: 2026-01-27 | Spec: spec.md
Input: Feature specification from /specs/[###-feature-name]/spec.md
Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.
Summary
This plan outlines the implementation of a capabilities-first Tenant RBAC system within the existing Laravel and Filament application. The primary requirement is to introduce granular, server-side enforced permissions for tenant users, managed through a new "Members" UI.
The technical approach involves:
- Verifying the existing
tenant_membershipstable matches the spec (role + minimal provenance fields). - Using the existing central capability registry (
App\Support\Auth\Capabilities) and role → capability mapping to enforce least privilege. - Ensuring Laravel Gates are defined per capability for all registry entries (no hand-maintained capability lists).
- Auditing / completing the Filament Relation Manager (
TenantMembershipsRelationManager) so only Owners can manage memberships. - Adding/expanding unit + feature tests with Pest to ensure the RBAC system is secure and correct.
Phases & Checkpoints
Phase 1 — Setup & Database
- Done when:
tenant_membershipsschema + relationships are verified and documented, and all related tests pass.
Phase 2 — Foundational RBAC Core
- Done when: capability registry + role mapping are aligned with least-privilege semantics and Gates are registered from
Capabilities::all(). - Done when: request-scope caching prevents repeated membership queries (query-count test).
Phase 3 — Membership Management UI
- Done when: Owners can add/change/remove members via Filament, last-owner rules are enforced, and all membership mutations require confirmation.
- Done when: members UI render/hydration is DB-only (no outbound HTTP and no jobs dispatched).
Phase 4 — Authorization Enforcement
- Done when: tenant route scoping is deny-as-not-found for non-members and global search is tenant-scoped.
- Done when: enforcement sweep removes role-ish authorization checks from tenant-plane feature code and replaces them with capability Gates/Policies.
Phase 5 — Polish & Finalization
- Done when: Pint passes for all changed files and the full test suite passes.
Technical Context
Language/Version: PHP 8.4+ Primary Dependencies: Laravel 12, Filament 5, Livewire 4, Pest 4 Storage: PostgreSQL Testing: Pest Target Platform: Web (Laravel application) Project Type: Web application Performance Goals: Membership/capability evaluation MUST be O(1) per request after initial load (NFR-001). Constraints: RBAC UI surfaces MUST be DB-only at render time (NFR-003). Scale/Scope: The system should be designed to handle a moderate number of tenants and users, with the potential to scale. Initial design should not be a bottleneck for future growth.
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
- Inventory-first: N/A for this feature.
- Read/write separation: All membership changes (write operations) are specified to require Owner-level privileges and will be implemented with confirmations and audit logs.
- Graph contract path: N/A. The spec explicitly states no Graph calls for RBAC UI.
- Deterministic capabilities: The role-to-capability mapping is deterministic and defined in a central place.
- RBAC Standard: This feature implements the RBAC standard. It establishes the two-plane separation, capabilities-first authorization, and least-privilege roles.
- Tenant isolation: All queries and actions are tenant-scoped through the
tenant_membershipstable. - Run observability: N/A for the core RBAC logic, as it's synchronous. Any long-running operations triggered by authorized users will follow this principle.
- Automation: N/A for this feature.
- Data minimization: The
tenant_membershipstable stores only essential information (IDs, role, and minimal provenance fields likesourceandcreated_by_user_id). - Badge semantics (BADGE-001): The role badge in the members list will use the
BadgeCatalog.
Project Structure
Documentation (this feature)
specs/065-tenant-rbac-v1/
├── plan.md # This file
├── research.md # Phase 0 output
├── data-model.md # Phase 1 output
├── quickstart.md # Phase 1 output
├── contracts/
│ └── capabilities.md # Phase 1 output
└── tasks.md # Phase 2 output (/speckit.tasks command)
Source Code (repository root)
The project follows a standard Laravel structure. Key files for this feature will be located in:
app/
├── Models/
│ ├── TenantMembership.php # Existing pivot model
│ └── User.php # Existing relationship to tenants/memberships
│ └── Tenant.php # Existing relationship to users/memberships
├── Policies/
│ └── (optional) TenantMembershipPolicy.php # Optional policy for membership mutations (currently Gate-driven)
├── Providers/
│ └── AuthServiceProvider.php # Register per-capability Gates
└── Filament/
└── Resources/
└── TenantResource/
└── RelationManagers/
└── TenantMembershipsRelationManager.php # New Filament relation manager
database/
└── migrations/
└── 2026_01_25_022729_create_tenant_memberships_table.php # Existing migration
tests/
├── Feature/
│ └── Filament/
│ └── TenantMembersTest.php # New feature test for RBAC UI
└── Unit/
└── Auth/ # Existing unit tests for capability registry + resolver
Structure Decision: The implementation will use the existing Laravel project structure. New classes and files will be created in their conventional locations. The primary UI for membership management will be a Filament Relation Manager on the TenantResource.
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] |