065-tenant-rbac-v1 #79

Merged
ahmido merged 3 commits from 065-tenant-rbac-v1 into dev 2026-01-28 21:09:48 +00:00
5 changed files with 261 additions and 0 deletions
Showing only changes of commit aad55d1579 - Show all commits

View File

@ -0,0 +1,44 @@
# Capability Contracts: Tenant RBAC v1
This document defines the canonical set of capabilities for the Tenant RBAC system. Feature code MUST use these capability strings when checking permissions with Laravel Gates (e.g., `Gate::allows('tenant.members.manage')`).
## Naming Convention
Capabilities follow the format: `tenant.<domain>.<verb>`
## Capability List (v1)
### Core
- `tenant.core.view`: View the tenant dashboard and basic information.
### Membership
- `tenant.members.view`: View the list of members in a tenant.
- `tenant.members.manage`: Add, remove, or change the roles of members in a tenant. (Owner-only)
### Settings
- `tenant.settings.view`: View tenant settings.
- `tenant.settings.manage`: Modify tenant settings.
### Providers
- `tenant.providers.view`: View provider connections.
- `tenant.providers.manage`: Add, edit, or remove provider connections.
- `tenant.providers.credentials.rotate`: Rotate credentials for a provider connection.
- `tenant.providers.run_ops`: Execute operations using a provider.
### Operations & Monitoring
- `tenant.operations.view`: View tenant operations and monitoring data.
- `tenant.operations.start`: Start new tenant operations.
### Inventory & Drift
- `tenant.inventory.view`: View tenant inventory.
- `tenant.inventory.sync`: Trigger a synchronization of the tenant inventory.
- `tenant.drift.view`: View drift detection reports.
- `tenant.drift.ack`: Acknowledge drift alerts.
### Policies, Backups, & Restore
- `tenant.policies.view`: View policies.
- `tenant.policies.sync`: Synchronize policies.
- `tenant.policies.delete`: Delete policies.
- `tenant.backups.manage`: Manage backups.
- `tenant.restore.execute`: Execute a restore from a backup.
- `tenant.danger_zone`: Access to destructive "danger zone" actions. (Owner-only)

View File

@ -0,0 +1,32 @@
# Data Model: Tenant RBAC v1
This document outlines the data model for the Tenant RBAC feature, as defined in the feature specification.
## Tables
### `tenant_memberships` (New Table)
This table is the source of truth for user membership and roles within a tenant.
**Columns**:
| Name | Type | Description | Constraints |
|---|---|---|---|
| `id` | `bigint` or `uuid` | Primary key. Follows repository convention. | Primary Key |
| `tenant_id` | `bigint` | Foreign key to the `tenants` table. | Not Null, FK to `tenants.id` |
| `user_id` | `bigint` | Foreign key to the `users` table. | Not Null, FK to `users.id` |
| `role` | `string` | The user's role within the tenant. | Not Null, Enum: `owner`, `manager`, `operator`, `readonly` |
| `created_at` | `timestamp` | Timestamp of creation. | Not Null |
| `updated_at` | `timestamp` | Timestamp of last update. | Not Null |
**Indexes**:
- `tenant_memberships_tenant_id_user_id_unique`: Unique constraint on `(tenant_id, user_id)` to ensure a user has only one role per tenant.
- `tenant_memberships_tenant_id_role_index`: Index on `(tenant_id, role)` for efficient role-based queries within a tenant.
- `tenant_memberships_user_id_index`: Index on `(user_id)` for efficiently finding all tenant memberships for a user.
## Relationships
- A `Tenant` has many `TenantMembership` records.
- A `User` has many `TenantMembership` records.
- A `TenantMembership` belongs to one `Tenant` and one `User`.

View File

@ -0,0 +1,104 @@
# Implementation Plan: Tenant RBAC v1
**Branch**: `065-tenant-rbac-v1` | **Date**: 2026-01-27 | **Spec**: [spec.md](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:
1. Creating a `tenant_memberships` table to store user roles within each tenant.
2. Defining a central capability registry and mapping roles (`Owner`, `Manager`, `Operator`, `Readonly`) to specific capabilities.
3. Using Laravel Gates and Policies to enforce these capabilities throughout the application.
4. Building a Filament Relation Manager (`MembersRelationManager`) to provide a UI for Owners to manage tenant memberships.
5. Adding comprehensive feature and unit tests with Pest to ensure the RBAC system is secure and correct.
## 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.*
- [X] **Inventory-first**: N/A for this feature.
- [X] **Read/write separation**: All membership changes (write operations) are specified to require Owner-level privileges and will be implemented with confirmations and audit logs.
- [X] **Graph contract path**: N/A. The spec explicitly states no Graph calls for RBAC UI.
- [X] **Deterministic capabilities**: The role-to-capability mapping is deterministic and defined in a central place.
- [X] **RBAC Standard**: This feature implements the RBAC standard. It establishes the two-plane separation, capabilities-first authorization, and least-privilege roles.
- [X] **Tenant isolation**: All queries and actions are tenant-scoped through the `tenant_memberships` table.
- [X] **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.
- [X] **Automation**: N/A for this feature.
- [X] **Data minimization**: The `tenant_memberships` table stores only essential information (IDs and role).
- [X] **Badge semantics (BADGE-001)**: The role badge in the members list will use the `BadgeCatalog`.
## Project Structure
### Documentation (this feature)
```text
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:
```text
app/
├── Models/
│ ├── TenantMembership.php # New Eloquent model
│ └── User.php # Add relationship to TenantMembership
│ └── Tenant.php # Add relationship to TenantMembership
├── Policies/
│ └── TenantMembershipPolicy.php # New policy for managing memberships
├── Providers/
│ └── AuthServiceProvider.php # Register gates and policies
└── Filament/
└── Resources/
└── TenantResource/
└── RelationManagers/
└── MembersRelationManager.php # New Filament relation manager
database/
├── factories/
│ └── TenantMembershipFactory.php # New model factory
└── migrations/
└── [timestamp]_create_tenant_memberships_table.php # New migration
tests/
├── Feature/
│ └── Filament/
│ └── TenantMembersTest.php # New feature test for RBAC UI
└── Unit/
└── TenantRBACTest.php # New unit test for role-capability mapping
```
**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] |

View File

@ -0,0 +1,74 @@
# Quickstart: Tenant RBAC v1
This guide provides a quick overview for developers on how to use the Tenant RBAC v1 system.
## Checking Permissions
The core of the RBAC system is a set of defined capabilities. To check if the currently authenticated user has a specific capability, use Laravel's `Gate` facade.
**NEVER check for roles directly.** Always check for capabilities.
### In PHP (Controllers, Policies, Livewire Components)
```php
use Illuminate\Support\Facades\Gate;
// Check for a specific capability
if (Gate::allows('tenant.members.manage')) {
// User can manage members
}
// You can also deny access
if (Gate::denies('tenant.settings.manage')) {
abort(403);
}
```
### In Blade Views
You can use the `@can` and `@cannot` directives in your Blade templates to conditionally show UI elements.
```blade
@can('tenant.members.manage')
<button>Add Member</button>
@endcan
@cannot('tenant.danger_zone')
<p>You are not authorized to access the danger zone.</p>
@endcannot
```
### In Filament Resources
Filament actions and pages can be protected using the `can` method.
```php
use Filament\Actions\Action;
use Filament\Resources\Pages\ListRecords;
// Protecting an action
Action::make('delete')
->requiresConfirmation()
->action(fn ($record) => $record->delete())
->visible(fn ($record) => Gate::allows('tenant.policies.delete', $record));
// Protecting a page
class ListMembers extends ListRecords
{
// ...
public static function canView(): bool
{
return Gate::allows('tenant.members.view');
}
}
```
## Capability Reference
A full list of available capabilities is defined in `specs/065-tenant-rbac-v1/contracts/capabilities.md`.
## Key Principles
1. **Capabilities, Not Roles**: All authorization checks MUST be against capabilities, not roles (`owner`, `manager`, etc.). This decouples the application's logic from the role definitions.
2. **Server-Side Enforcement**: UI hiding is not security. Always enforce permissions on the server-side (in controllers, actions, or policies) in addition to hiding UI elements.
3. **Use Policies for Model-Specific Logic**: For authorization logic that depends on a specific model instance, use a Laravel Policy class.

View File

@ -0,0 +1,7 @@
# Research: Tenant RBAC v1
**Date**: 2026-01-27
No significant research was required for this feature. The specification is comprehensive and relies on established technologies and patterns within the project (Laravel, Filament, Pest).
The implementation will follow standard Laravel practices for Gates, Policies, and database migrations.