noxomix / laravel-rollo
A context-based, polymorphic role and permission management system for Laravel
dev-main
2025-08-08 19:00 UTC
Requires
- php: ^8.2
- illuminate/database: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^10.0|^11.0
This package is not auto-updated.
Last update: 2025-09-05 19:24:59 UTC
README
Context-based, polymorphic role and permission management for Laravel.
Requirements
- PHP: ^8.2
- Laravel (Illuminate components): ^11.0 | ^12.0
Installation
composer require noxomix/laravel-rollo
What It Does
- Polymorphic: Any model can have roles and permissions
- Context-based: Assignments can be scoped to contexts (tenants, teams, projects)
- Recursive role inheritance: Roles can inherit from other roles
- No auto-enforcement: You call helpers to build your own RBAC checks
Getting Started
1) Setup (recommended)
php artisan rollo:setup
This publishes config/rollo.php
, migrations, and can optionally run them.
Alternatively (manual):
php artisan vendor:publish --tag=rollo-config php artisan vendor:publish --tag=rollo-migrations php artisan migrate
2) Add Traits to Models
use Noxomix\LaravelRollo\Traits\HasRolloRoles; use Noxomix\LaravelRollo\Traits\HasRolloPermissions; class User extends Model { use HasRolloRoles, HasRolloPermissions; }
3) Add Context Trait (optional)
use Noxomix\LaravelRollo\Traits\AsRolloContext; class Tenant extends Model { use AsRolloContext; }
Usage
Permissions
// Create permission $permission = RolloPermission::create(['name' => 'edit-posts']); // Assign to user (kontextfrei) $user->assignPermission('edit-posts'); // Assign multiple permissions $user->assignPermissions(['edit-posts', 'delete-posts', 'publish-posts']); // Assign with context $tenant = Tenant::find(1); $context = $tenant->becomeRolloContext(); $user->assignPermission('edit-posts', $context); // Check permission $user->hasPermission('edit-posts'); // kontextfrei (context_id = NULL) $user->hasPermission('edit-posts', $context); // in context // Remove $user->removePermission('edit-posts'); $user->removePermissions(['edit-posts', 'delete-posts']); $user->removeAllPermissions(); // remove all
Roles
// Create role $role = RolloRole::create(['name' => 'editor']); // Assign permissions to role $role->assignPermission('edit-posts'); // Assign role to user $user->assignRole('editor'); $user->assignRole('editor', $context); // with context // Assign multiple roles $user->assignRoles(['editor', 'moderator']); // Check role $user->hasRole('editor'); // Remove role $user->removeRole('editor'); $user->removeRoles(['editor', 'moderator']);
Role Inheritance
$adminRole = RolloRole::create(['name' => 'admin']); $editorRole = RolloRole::create(['name' => 'editor']); // Admin inherits all editor permissions $adminRole->assignChildRole($editorRole);
Context Management
// Create context $context = $tenant->becomeRolloContext(); // auto-creates if not exists $context = $tenant->becomeRolloContext(['name' => 'EU_Production']); // custom name // Update context $tenant->updateRolloContext(); // updates to default name $tenant->updateRolloContext(['name' => 'New_Name']); // custom name // Check if has context if ($tenant->hasRolloContext()) { // ... } // Delete context $tenant->deleteRolloContext();
Service Class
use Noxomix\LaravelRollo\Facades\Rollo; // Check permission if (Rollo::has($user, 'edit-posts')) { // ... } // Check in context if (Rollo::has($user, 'edit-posts', $context)) { // ... } // Get all permissions $permissions = Rollo::permissionsFor($user); $permissions = Rollo::permissionsFor($user, $context); // Get all roles $roles = Rollo::rolesFor($user); $roles = Rollo::rolesFor($user, $context); // Note: This package does not auto-enforce permissions; use these helpers in your app logic.
Service API
Rollo::has(Model $model, string $permission, mixed $context = null): bool
— checks direct permission and via roles (including inherited roles). If$context
is provided, only assignments for thatcontext_id
are considered.Rollo::permissionsFor(Model $model, mixed $context = null): Illuminate\Support\Collection
— returns all effective permissions (direct + via roles), optionally scoped to a context.Rollo::rolesFor(Model $model, mixed $context = null): Illuminate\Support\Collection
— returns all effective roles (direct + inherited), optionally scoped to a context.
Events
This package dispatches Laravel events for key operations so you can build auditing, logs, or side-effects in your app without coupling it to the package:
Noxomix\\LaravelRollo\\Events\\RoleCreated
— payload:RolloRole $role
Noxomix\\LaravelRollo\\Events\\RoleAssigned
— payload:Model $model, RolloRole $role
Noxomix\\LaravelRollo\\Events\\RoleRemoved
— payload:Model $model, RolloRole $role
Noxomix\\LaravelRollo\\Events\\PermissionAssigned
— payload:Model $model, RolloPermission $permission, ?int $contextId
Noxomix\\LaravelRollo\\Events\\PermissionRemoved
— payload:Model $model, RolloPermission $permission, ?int $contextId
Noxomix\\LaravelRollo\\Events\\ContextCreated
— payload:RolloContext $context
Noxomix\\LaravelRollo\\Events\\ContextUpdated
— payload:RolloContext $context
Noxomix\\LaravelRollo\\Events\\ContextDeleted
— payload:RolloContext $context
Noxomix\\LaravelRollo\\Events\\RolesSynced
— payload:Model $model, array $attached, array $detached, ?int $contextId
Noxomix\\LaravelRollo\\Events\\PermissionsSynced
— payload:Model $model, array $attached, array $detached, ?int $contextId
Noxomix\\LaravelRollo\\Events\\RoleChildAssigned
— payload:RolloRole $parent, RolloRole $child
Noxomix\\LaravelRollo\\Events\\RoleChildRemoved
— payload:RolloRole $parent, RolloRole $child
Noxomix\\LaravelRollo\\Events\\RolesAssignedBatch
— payload:Model $model, array $attachedIds, ?int $contextId
Noxomix\\LaravelRollo\\Events\\RolesRemovedBatch
— payload:Model $model, array $detachedIds, ?int $contextId
Noxomix\\LaravelRollo\\Events\\PermissionsAssignedBatch
— payload:Model $model, array $attachedIds, ?int $contextId
Noxomix\\LaravelRollo\\Events\\PermissionsRemovedBatch
— payload:Model $model, array $detachedIds, ?int $contextId
Example listener registration:
Event::listen(\Noxomix\LaravelRollo\Events\PermissionAssigned::class, function ($event) { // audit($event->model, 'permission_assigned', [ // 'permission' => $event->permission->name, // 'context_id' => $event->contextId, // ]); });
Advanced Usage
JSON Configuration
// Store additional config in roles/permissions $role = RolloRole::create([ 'name' => 'moderator', 'config' => [ 'max_posts_per_day' => 10, 'can_ban_users' => true ] ]); // Access config $config = $role->getConfig('max_posts_per_day'); // 10
Polymorphic Support
// Any model can have roles/permissions $team->assignPermission('manage-projects'); $bot->assignRole('data-processor'); $service->assignPermission('api-access'); // Batch operations $team->assignPermissions(['create-projects', 'edit-projects', 'delete-projects']); $bot->assignRoles(['data-processor', 'api-consumer']);
Permission Checking
// Direct permission check if ($user->hasPermission('edit-posts')) { // User has direct permission } // Check via roles and permissions if ($user->canPerform('edit-posts')) { // User can perform action (direct or via roles) } // Check role has permission if ($role->canPerform('publish-posts')) { // Role has this permission }
Context Helpers
// Get all roles in context $roles = $tenant->getContextRoles(); // Create role in context $role = $tenant->createRoleInContext('team-admin', ['order' => 1]); // Find role in context $role = $tenant->findRoleInContext('team-admin'); // Get models with permissions in context $users = $tenant->getModelsWithPermissionsInContext(User::class);
Database Schema
rollo_permissions
- Permission definitionsrollo_roles
- Role definitions with parent/child relationshipsrollo_contexts
- Polymorphic contexts (tenants, teams, etc.)rollo_model_has_roles
- Polymorphic role assignmentsrollo_model_has_permissions
- Polymorphic permission assignments
Configuration
- Allowed Models: The whitelist
config('rollo.allowed_models')
restricts which Eloquent models may be used in dynamic, string-based queries (e.g., context lookups). Package models may be listed when they use the traits (e.g.,Noxomix\\LaravelRollo\\Models\\RolloRole
). - Config Field Validation: The
config
attribute on roles/permissions is accepted as an array or null. Optional schema-based validation exists in code but is not active by default and carries no required schema; you can ignore it safely for core usage. - Context Semantics: When you pass a context to checks or queries, only assignments for that specific
context_id
are considered; kontextfreie assignments (withcontext_id = null
) are not included implicitly.
Architecture
- Context Resolution: Internally, a central resolver (
Noxomix\\LaravelRollo\\Support\\ContextResolver
) resolves a context ID from IDs,RolloContext
, or arbitrary Eloquent models. Traits and the service class use this resolver to keep behavior consistent and simple. - Facade Binding: The
Rollo
facade resolves the container singleton bound under the keyrollo
.
Compatibility
- MySQL-Friendly Pivot: The
rollo_model_has_permissions
table uses a surrogate primary key (id
) with a unique index over (permission_id
,model_type
,model_id
,context_id
). This allowscontext_id
to remain nullable while staying compatible with databases that do not allow NULL columns in primary keys.
Testing
composer test
License
MIT