A comprehensive Laravel package for dynamic role and permission management with caching, API support, and complete database-driven URL management. Perfect for applications with complex permission requirements that need to be managed without touching code.
Run on terminal:
composer require anwar/dynamic-roles
Or add it on your main composer.json
Then run:
composer install
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate
# Publish configuration
php artisan vendor:publish --tag=dynamic-roles-config
# Publish migrations
php artisan vendor:publish --tag=dynamic-roles-migrations
# Run migrations
php artisan migrate
Add to config/app.php:
'providers' => [
// ...
Anwar\DynamicRoles\DynamicRolesServiceProvider::class,
],
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasRoles;
// ... your existing code
}
Add these to your .env file:
# Cache Configuration
DYNAMIC_ROLES_CACHE_ENABLED=true
DYNAMIC_ROLES_CACHE_DRIVER=redis
DYNAMIC_ROLES_CACHE_PREFIX=dynamic_roles
DYNAMIC_ROLES_CACHE_TTL=3600
# API Configuration
DYNAMIC_ROLES_ENABLE_API=true
DYNAMIC_ROLES_API_PREFIX=api/dynamic-roles
# Auto-Discovery
DYNAMIC_ROLES_AUTO_DISCOVERY=true
DYNAMIC_ROLES_AUTO_REGISTER_URLS=true
# Security
DYNAMIC_ROLES_SUPER_ADMIN=super-admin
DYNAMIC_ROLES_BYPASS_PERMISSIONS=false
DYNAMIC_ROLES_LOG_CHECKS=false
The package publishes a configuration file at config/dynamic-roles.php. Key sections include:
use \DynamicRoles\Facades\DynamicRoles;
// Register a URL with specific permissions
DynamicRoles::registerUrl(
'/api/users',
'GET',
['users.view', 'users.list'],
[
'name' => 'users.index',
'description' => 'List all users',
'category' => 'api'
]
);
// Auto-discover all routes
DynamicRoles::autoDiscoverRoutes();
// In your routes/api.php
Route::middleware(['dynamic.permission'])->group(function () {
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);
});
// With specific permissions
Route::get('/admin/users', [UserController::class, 'adminIndex'])
->middleware('dynamic.permission:admin.users.view');
// With role-based access
Route::middleware(['dynamic.role:admin,manager'])->group(function () {
Route::get('/admin/dashboard', [AdminController::class, 'dashboard']);
});
use \DynamicRoles\Services\UrlPermissionService;
$urlPermissionService = app(UrlPermissionService::class);
// Check if user has permission for a specific URL
$hasPermission = $urlPermissionService->checkUrlPermission(
$user,
'/api/users',
'GET'
);
if ($hasPermission) {
// Allow access
} else {
// Deny access
}
The package provides a complete REST API for managing roles and permissions:
All API endpoints require authentication. Configure the middleware in the config file:
'api' => [
'middleware' => [
'api',
'auth:sanctum', // or 'auth:api', 'jwt.auth', etc.
],
],
URL Management:
# Get all URLs
GET /api/dynamic-roles/urls
# Create new URL
POST /api/dynamic-roles/urls
{
"url": "/api/products",
"method": "GET",
"permissions": ["products.view"],
"roles": ["user", "admin"]
}
# Update URL
PUT /api/dynamic-roles/urls/{id}
# Delete URL
DELETE /api/dynamic-roles/urls/{id}
# Check permission
POST /api/dynamic-roles/urls/check-permission
{
"url": "/api/products",
"method": "GET"
}
# Auto-discover routes
POST /api/dynamic-roles/urls/auto-discover
Role Management:
# Get all roles
GET /api/dynamic-roles/roles
# Create role
POST /api/dynamic-roles/roles
{
"name": "editor",
"permissions": ["posts.create", "posts.edit"]
}
# Assign permissions to role
POST /api/dynamic-roles/roles/{roleId}/permissions
{
"permissions": ["posts.create", "posts.edit", "posts.delete"]
}
# Get role permissions
GET /api/dynamic-roles/roles/{roleId}/permissions
User Management:
# Assign role to user
POST /api/dynamic-roles/users/assign-role
{
"user_id": 1,
"role": "editor"
}
# Get user permissions
GET /api/dynamic-roles/users/{userId}/permissions
// lib/permissions.ts
interface PermissionCheck {
url: string;
method: string;
}
export async function checkPermission({ url, method }: PermissionCheck): Promise<boolean> {
try {
const response = await fetch('/api/dynamic-roles/urls/check-permission', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getAuthToken()}`,
},
body: JSON.stringify({ url, method }),
});
const result = await response.json();
return result.data.has_permission;
} catch (error) {
console.error('Permission check failed:', error);
return false;
}
}
// Usage in React component
import { checkPermission } from '@/lib/permissions';
export default function ProductList() {
const [canEdit, setCanEdit] = useState(false);
useEffect(() => {
checkPermission({ url: '/api/products', method: 'PUT' })
.then(setCanEdit);
}, []);
return (
<div>
{canEdit && <button>Edit Product</button>}
</div>
);
}
Configure auto-discovery patterns in config/dynamic-roles.php:
'discovery' => [
'permission_patterns' => [
'create' => ['store', 'create', 'add'],
'read' => ['index', 'show', 'view', 'list'],
'update' => ['update', 'edit', 'modify'],
'delete' => ['destroy', 'delete', 'remove'],
'admin' => ['admin', 'manage'],
],
],
'cache' => [
'enabled' => true,
'driver' => 'redis', // redis, memcached, file, etc.
'ttl' => 3600,
'tags' => [
'permissions' => 'dynamic_roles_permissions',
'roles' => 'dynamic_roles_roles',
'urls' => 'dynamic_roles_urls',
],
],
use \DynamicRoles\Services\RolePermissionService;
$rolePermissionService = app(RolePermissionService::class);
// Bulk assign permissions to roles
$rolePermissionService->bulkAssignPermissions([
'admin' => ['users.create', 'users.edit', 'users.delete'],
'editor' => ['posts.create', 'posts.edit'],
'viewer' => ['posts.view'],
]);
// Bulk assign roles to users
$rolePermissionService->bulkAssignRoles([
1 => ['admin'],
2 => ['editor'],
3 => ['viewer'],
]);
// Export current configuration
$config = $rolePermissionService->exportConfiguration();
file_put_contents('permissions-backup.json', json_encode($config, JSON_PRETTY_PRINT));
// Import configuration
$config = json_decode(file_get_contents('permissions-backup.json'), true);
$rolePermissionService->importConfiguration($config);
The package provides several useful Artisan commands:
# Sync permissions and auto-discover routes
php artisan dynamic-roles:sync-permissions --auto-discover --clear-cache
# Clear specific caches
php artisan dynamic-roles:clear-cache --user=1
php artisan dynamic-roles:clear-cache --role=2
php artisan dynamic-roles:clear-cache --url=/api/users --method=GET
# Clear all permission cache
php artisan dynamic-roles:clear-cache
# Publish configuration (force overwrite)
php artisan dynamic-roles:publish-config
Create tests for your permissions:
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Anwar\DynamicRoles\Facades\DynamicRoles;
class PermissionTest extends TestCase
{
use RefreshDatabase;
public function test_user_can_access_allowed_url()
{
$user = User::factory()->create();
$user->assignRole('user');
DynamicRoles::registerUrl('/api/profile', 'GET', ['profile.view']);
$response = $this->actingAs($user)
->getJson('/api/profile');
$response->assertStatus(200);
}
public function test_user_cannot_access_forbidden_url()
{
$user = User::factory()->create();
DynamicRoles::registerUrl('/api/admin', 'GET', ['admin.access']);
$response = $this->actingAs($user)
->getJson('/api/admin');
$response->assertStatus(403);
}
}
The package uses intelligent caching:
// Enable preloading for better performance
'performance' => [
'preload_permissions' => true,
'eager_load_relationships' => true,
'batch_permission_checks' => true,
],
Configure a super admin role that bypasses all permission checks:
'security' => [
'super_admin_role' => 'super-admin',
'bypass_permissions' => false, // Never set to true in production
],
Enable detailed logging:
DYNAMIC_ROLES_LOG_CHECKS=true
Check logs at storage/logs/laravel.log for detailed permission check information.
This package is open-sourced software licensed under the MIT license.
For support, please check:
storage/logs/laravel.log)config/dynamic-roles.php)The package includes a complete menu management system that allows you to create hierarchical menu structures with role and permission-based access control.
use Anwar\DynamicRoles\Services\MenuService;
$menuService = app(MenuService::class);
// Create a parent menu
$parentMenu = $menuService->createMenu([
'name' => 'dashboard',
'label' => 'Dashboard',
'url' => '/dashboard',
'icon' => 'fas fa-tachometer-alt',
'is_active' => true,
'is_visible' => true,
'sort_order' => 1,
]);
// Create a child menu
$childMenu = $menuService->createMenu([
'name' => 'user_management',
'label' => 'User Management',
'url' => '/users',
'icon' => 'fas fa-users',
'parent_id' => $parentMenu->id,
'sort_order' => 1,
'permissions' => [1, 2], // Permission IDs
'roles' => [1], // Role IDs
]);
// Get menu tree for current user (filtered by permissions)
$userMenuTree = $menuService->getMenuTreeForUser(auth()->user());
// Get full menu tree (admin view)
$fullMenuTree = $menuService->getFullMenuTree();
All menu endpoints are available under /api/dynamic-roles/menus/:
GET /menus - List all menus with filteringGET /menus/tree - Get menu tree for current userPOST /menus - Create a new menuGET /menus/{id} - Get specific menuPUT /menus/{id} - Update menuDELETE /menus/{id} - Delete menuGET /menus/{id}/breadcrumbs - Get breadcrumbs for menuPOST /menus/reorder - Reorder menu itemsPOST /menus/{id}/assign-permissions - Assign permissions to menuPOST /menus/{id}/assign-roles - Assign roles to menuConfigure menu behavior in config/dynamic-roles.php:
'menu' => [
'enabled' => true,
'cache_enabled' => true,
'cache_ttl' => 1800, // 30 minutes
'max_depth' => 5,
'auto_permissions' => true, // Auto create permissions for menu items
'icons' => [
'supported_libraries' => ['fontawesome', 'feather', 'heroicons', 'material'],
'default_library' => 'fontawesome',
],
],