glueful / aegis
Aegis: Role-Based Access Control extension for Glueful
Installs: 10
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:glueful-extension
pkg:composer/glueful/aegis
Requires
- php: >=8.1
Requires (Dev)
- glueful/framework: dev-main
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
README
Overview
Aegis provides a comprehensive, modern Role-Based Access Control (RBAC) system for your Glueful application. It implements hierarchical roles, direct user permissions, resource-level filters, and optional audit logging.
Features
- Hierarchical roles: Create nested roles with inheritance
- Direct user permissions: Per-user grants that override role permissions
- Resource-level filters: Limit permissions to specific resources/types
- Temporal permissions: Expiry on roles and direct grants
- Scoped access: Multi-tenant friendly with scoping
- Audit service: Structured audit helpers + optional check logging
- Multi-layer caching: In-memory + distributed cache via CacheStore
- REST API: Full CRUD + assignment endpoints
- Flexible config: Tunable caching, inheritance, and logging
Installation
Installation (Recommended)
Install via Composer
composer require glueful/aegis
# Rebuild the extensions cache after adding new packages
php glueful extensions:cache
Glueful auto-discovers packages of type glueful-extension
and boots their service providers.
Enable/disable in development:
# Enable (adds provider to config/extensions.php) php glueful extensions:enable Aegis # Disable in dev php glueful extensions:disable Aegis
Run database migrations (if not auto-run by your workflow):
php glueful migrate:run
Local Development Installation
If you're working locally (without Composer), place the extension in extensions/Aegis
, ensure config/extensions.php
has local_path
pointing to extensions
(non‑prod).
Enable the provider for development (choose one):
-
CLI (recommended):
php glueful extensions:enable Aegis
-
Manual
config/extensions.php
edit:return [ 'enabled' => [ // ... other providers Glueful\\Extensions\\Aegis\\Services\\AegisServiceProvider::class, ], 'dev_only' => [ // Optionally keep Aegis dev-only ], 'local_path' => env('APP_ENV') === 'production' ? null : 'extensions', 'scan_composer' => true, ];
Run the migrations to create the necessary database tables:
php glueful migrate run
- Generate API documentation (optional, if your tooling supports it):
php glueful generate:json doc
- Restart your web server to apply the changes.
Verify Installation
Check status and details:
php glueful extensions:list php glueful extensions:info Aegis php glueful extensions:why Glueful\\Extensions\\Aegis\\Services\\AegisServiceProvider
Post-install checklist:
- Run migrations (if not auto-run):
php glueful migrate run
- Hit an endpoint to verify:
GET /rbac/roles
- Rebuild cache after Composer operations:
php glueful extensions:cache
- Check logs for initialization messages or errors
Quick Start
Create a role, assign it to a user, and verify via the API. Replace placeholders before running:
API_BASE
with your base URL (e.g., http://localhost:8000)TOKEN
with a valid bearer tokenUSER_UUID
with an existing user's UUID
API_BASE=http://localhost:8000 TOKEN="<YOUR_BEARER_TOKEN>" USER_UUID="<AN_EXISTING_USER_UUID>" # 1) Create a role create_resp=$(curl -s -X POST "$API_BASE/rbac/roles" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Editor", "slug": "editor", "description": "Can edit content" }') # Extract role UUID (requires jq). If jq is unavailable, inspect $create_resp ROLE_UUID=$(printf "%s" "$create_resp" | jq -r '.data.uuid') echo "Created role UUID: $ROLE_UUID" # 2) Assign the role to a user curl -s -X POST "$API_BASE/rbac/roles/$ROLE_UUID/assign" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{\n \"user_uuid\": \"$USER_UUID\",\n \"scope\": {\"tenant_id\": \"tenant_1\"}\n }" | jq . # 3a) Verify: list the user's roles curl -s "$API_BASE/rbac/users/$USER_UUID/roles" \ -H "Authorization: Bearer $TOKEN" | jq . # 3b) Verify: explicit role check by slug curl -s -X POST "$API_BASE/rbac/users/$USER_UUID/check-role" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "role_slug": "editor" }' | jq .
Quick Start (PHP)
Programmatic equivalent using the container and services:
<?php use Glueful\Extensions\Aegis\Services\RoleService; use Glueful\Extensions\Aegis\AegisPermissionProvider; // Resolve services from the container $roleService = container()->get(RoleService::class); $provider = container()->get(AegisPermissionProvider::class); $userUuid = '<AN_EXISTING_USER_UUID>'; // 1) Create a role $role = $roleService->createRole([ 'name' => 'Editor', 'slug' => 'editor', 'description' => 'Can edit content', ]); // 2) Assign the role to the user $roleService->assignRoleToUser($userUuid, $role->getUuid(), [ 'scope' => ['tenant_id' => 'tenant_1'], ]); // 3a) Verify via provider $hasRole = $provider->hasRole($userUuid, 'editor'); // 3b) Or verify via RoleService helper $hasRole2 = $roleService->userHasRole($userUuid, 'editor'); // Optional: check permissions using the permission manager $permissionManager = container()->get('permission.manager'); $canEdit = $permissionManager->can($userUuid, 'posts.edit', 'post:123'); var_dump([ 'role_uuid' => $role->getUuid(), 'has_editor_role' => $hasRole, 'has_editor_role_via_service' => $hasRole2, 'can_edit_post' => $canEdit, ]);
Database Schema
Aegis creates the following tables:
roles
: Role definitions with hierarchy supportpermissions
: Permission definitions with categories and resource typesrole_permissions
: Role-to-permission mappingsuser_roles
: User role assignments with scope and expiry supportuser_permissions
: Direct user permission assignmentspermission_audit
: Audit records for permission/role changes
Configuration
Configuration is loaded from Aegis/config/rbac.php
and merged by the service provider. Example:
<?php return [ 'roles' => [ // Inherit permissions up the role hierarchy 'inherit_permissions' => true, // Maximum depth for hierarchy resolution 'max_hierarchy_depth' => 10, ], 'permissions' => [ // Distributed caching for user permissions 'cache_enabled' => true, // TTL for user permissions (seconds) 'cache_ttl' => 3600, 'cache_prefix' => 'rbac:', // Enable role-permission inheritance 'inheritance_enabled' => true, ], 'logging' => [ // When true, logs each permission check (verbose) 'log_check_operations' => false, ], ];
Usage
Basic Permission Checking
use Glueful\Permissions\PermissionManager; $permissionManager = container()->get('permission.manager'); // Check if user has permission $canEdit = $permissionManager->can($userUuid, 'posts.edit', 'post:123'); // Check with additional context (e.g., tenant scope) $canDelete = $permissionManager->can($userUuid, 'posts.delete', 'post:123', [ 'scope' => ['tenant_id' => 'tenant_1'], ]);
Role Management
use Glueful\Extensions\Aegis\Services\RoleService; $roleService = container()->get(RoleService::class); // Create a new role $adminRole = $roleService->createRole([ 'name' => 'Administrator', 'slug' => 'admin', 'description' => 'System administrator with full access', ]); // Create a child role $moderatorRole = $roleService->createRole([ 'name' => 'Moderator', 'slug' => 'moderator', 'parent_uuid' => $adminRole->getUuid(), 'description' => 'Content moderator', ]); // Assign role to user $roleService->assignRoleToUser($userUuid, $adminRole->getUuid(), [ 'expires_at' => '2024-12-31 23:59:59', 'scope' => ['tenant_id' => 'tenant_1'], ]);
Permission Management
use Glueful\Extensions\Aegis\Services\PermissionAssignmentService; $permissionService = container()->get(PermissionAssignmentService::class); // Create a new permission $permission = $permissionService->createPermission([ 'name' => 'Edit Posts', 'slug' => 'posts.edit', 'description' => 'Ability to edit blog posts', 'category' => 'content', 'resource_type' => 'posts', ]); // Assign permission directly to user (overrides role permissions) $permissionService->assignPermissionToUser( $userUuid, 'posts.edit', 'post:123', // Specific resource [ 'expires_at' => '2024-06-30 23:59:59', 'constraints' => ['ip_range' => '192.168.1.0/24'], ] ); // Batch assign permissions $permissionService->batchAssignPermissions($userUuid, [ ['permission' => 'posts.read', 'resource' => '*'], ['permission' => 'posts.edit', 'resource' => 'post:*'], ['permission' => 'comments.moderate', 'resource' => '*'], ]);
Using the RBAC Permission Provider
The Aegis provider is auto-registered with the framework’s permission manager. You can either use the manager (recommended) or get the provider directly:
use Glueful\Extensions\Aegis\AegisPermissionProvider; // Recommended: use the permission manager for checks $permissionManager = container()->get('permission.manager'); $can = $permissionManager->can($userUuid, 'posts.edit', 'post:123'); // Direct access to provider when needed $rbacProvider = container()->get(AegisPermissionProvider::class); // Assign a role $rbacProvider->assignRole($userUuid, 'editor', [ 'scope' => ['tenant_id' => 'tenant_1'], 'expires_at' => '2024-12-31 23:59:59', ]); // Check if user has role $hasRole = $rbacProvider->hasRole($userUuid, 'admin'); // Get user's effective permissions $permissions = $rbacProvider->getUserPermissions($userUuid);
API Endpoints
All endpoints are prefixed with /rbac
and require authentication. Highlights include:
Roles
GET /rbac/roles
– List roles (with filters/pagination)POST /rbac/roles
– Create a roleGET /rbac/roles/{uuid}
– Get role detailsPUT /rbac/roles/{uuid}
– Update a roleDELETE /rbac/roles/{uuid}
– Delete a role- Extra:
GET /rbac/roles/stats
, bulk operations, assign/revoke users
Permissions
GET /rbac/permissions
– List permissions (with filters/pagination)POST /rbac/permissions
– Create a permissionGET /rbac/permissions/{uuid}
– Get permission detailsPUT /rbac/permissions/{uuid}
– Update a permissionDELETE /rbac/permissions/{uuid}
– Delete a permission- Extra:
GET /rbac/permissions/stats
,POST /rbac/permissions/cleanup-expired
, categories, resource-types
Users
GET /rbac/users/{user_uuid}/roles
– List a user’s rolesPOST /rbac/users/{user_uuid}/roles
– Assign roles to a userDELETE /rbac/users/{user_uuid}/roles/{role_uuid}
– Revoke a user’s roleGET /rbac/users/{user_uuid}/permissions
– List direct permissionsPOST /rbac/users/{user_uuid}/permissions
– Grant direct permissionsDELETE /rbac/users/{user_uuid}/permissions/{permission_uuid}
– Revoke direct permission
Hierarchical Roles
The RBAC system supports role hierarchy:
// Create role hierarchy: Admin -> Manager -> Employee $adminRole = $roleService->createRole([ 'name' => 'Administrator', 'slug' => 'admin', 'level' => 0 ]); $managerRole = $roleService->createRole([ 'name' => 'Manager', 'slug' => 'manager', 'parent_uuid' => $adminRole->getUuid(), 'level' => 1 ]); $employeeRole = $roleService->createRole([ 'name' => 'Employee', 'slug' => 'employee', 'parent_uuid' => $managerRole->getUuid(), 'level' => 2 ]); // Users with admin role automatically inherit manager and employee permissions
Scoped Permissions
Support multi-tenant environments with scoped permissions:
// Assign role with scope $roleService->assignRoleToUser($userUuid, $managerRole->getUuid(), [ 'scope' => [ 'tenant_id' => 'tenant_1', 'department' => 'marketing' ] ]); // Check permission with scope context $canAccess = $permissionManager->can($userUuid, 'reports.view', '*', [ 'scope' => ['tenant_id' => 'tenant_1'] ]);
Audit Logging
An audit service is provided for RBAC-related events, and permission-check logging can be enabled via config:
use Glueful\Extensions\Aegis\Services\AuditService; $audit = container()->get(AuditService::class); $audit->logSecurityEvent('unauthorized_access', ['path' => '/rbac/roles'], $userUuid ?? null); // In config (Aegis/config/rbac.php): // 'logging' => ['log_check_operations' => true] // When enabled, permission checks are logged to the 'rbac_audit' channel.
Caching
- Memory cache: In-process caching for the current request
- Distributed cache: Backed by
CacheStore
for cross-request caching - TTL: Permission checks cached for 15 minutes; user permissions use
permissions.cache_ttl
(default 3600s)
// Clear a user’s RBAC cache $rbacProvider->invalidateUserCache($userUuid); // Clear all RBAC cache entries $rbacProvider->invalidateAllCache();
Performance Considerations
- Permission checks cached ~15 minutes
- User permissions cached per
permissions.cache_ttl
- Batched lookups to minimize N+1 queries
- Prefer resource-specific permissions to improve cache effectiveness
Security Considerations
- System roles/permissions can be protected
- Circular hierarchies are prevented
- All endpoints require authentication and proper permissions
- Optional audit trails improve accountability
- Cache keys include security context
Migration from Legacy Systems
If migrating from an existing permission system:
- Export existing roles and permissions
- Use the batch assignment APIs to recreate the structure
- Test thoroughly with your existing codebase
- Update permission checks to use the new RBAC provider
Troubleshooting
Common Issues
- Permissions not working: Confirm Aegis is enabled and DB tables exist; use
permission.manager
for checks. - Cache issues: Clear RBAC cache via
invalidateUserCache()
/invalidateAllCache()
. - Performance issues: Enable caching and scope permissions/resources.
- Audit logs not appearing: Enable
logging.log_check_operations
and verify yourrbac_audit
log channel.
Debugging
Adjust Aegis/config/rbac.php
to disable caches or enable check logging as needed.
Requirements
- PHP 8.1 or higher
- Glueful 1.0.0 or higher
- MySQL, PostgreSQL, or SQLite database
- Redis or Memcached (optional, for distributed caching)
License
This extension is licensed under the same license as the Glueful framework.
Support
For issues, feature requests, or questions, please create an issue in the repository.