Installation:
composer require albegali/acl-bundle
Enable the bundle in config/bundles.php:
Albegali\AclBundle\AlbegaliAclBundle::class => ['all' => true],
Configuration:
Add ACL configuration to config/packages/albegali_acl.yaml:
albegali_acl:
driver: doctrine # or 'orm' (alias for doctrine)
remove_orphans: false
First Use Case: Define an ACL entry for a route or entity in a controller or service:
use Albegali\AclBundle\Manager\AclManager;
class SomeController extends AbstractController
{
public function __construct(private AclManager $aclManager) {}
public function index()
{
$this->aclManager->createMask('ROLE_USER', 'OBJECT_IDENTIFIER', 'MASK');
// OR for routes:
$this->aclManager->createRouteMask('ROLE_USER', 'ROUTE_NAME', 'MASK');
}
}
Entity-Level ACLs:
// In a service or controller:
$this->aclManager->createMask(
'ROLE_ADMIN', // Role
'OBJECT_IDENTIFIER', // e.g., 'POST_123' or 'USER_456'
'UPDATE' // Mask (e.g., 'CREATE', 'READ', 'UPDATE', 'DELETE')
);
// Check access:
if (!$this->aclManager->isGranted('OBJECT_IDENTIFIER', 'UPDATE')) {
throw $this->createAccessDeniedException();
}
Route-Level ACLs:
// In a controller or event subscriber:
$this->aclManager->createRouteMask(
'ROLE_SUPER_ADMIN',
'app_admin_dashboard',
'ACCESS'
);
// Check in controller:
if (!$this->aclManager->isGrantedRoute('app_admin_dashboard')) {
return $this->redirectToRoute('home');
}
Dynamic ACLs: Use Symfony’s ExpressionLanguage for dynamic rules:
# config/packages/albegali_acl.yaml
albegali_acl:
expression_language:
providers:
- 'security.token_provider'
$this->aclManager->createMask(
'is_granted("ROLE_USER") and object.getOwner() == user',
'POST_123',
'DELETE'
);
Doctrine Entities: Annotate entities to auto-generate ACL identifiers:
use Albegali\AclBundle\Model\AclAwareInterface;
class Post implements AclAwareInterface
{
public function getAclObjectIdentifier(): string
{
return 'POST_' . $this->id;
}
}
Fixtures: Load ACLs via DoctrineFixturesBundle:
use Albegali\AclBundle\Manager\AclManager;
public function load(ObjectManager $manager)
{
$aclManager = $this->container->get('albegali_acl.manager');
$aclManager->createMask('ROLE_USER', 'POST_1', 'READ');
}
Symfony Events: Attach ACL checks to kernel events:
use Albegali\AclBundle\EventListener\AclListener;
public function onKernelRequest(GetResponseEvent $event)
{
$aclListener = new AclListener($this->aclManager);
$aclListener->onKernelRequest($event);
}
Object Identifiers:
getAclObjectIdentifier() returns inconsistent values (e.g., due to lazy-loading).USER_{$this->id} instead of USER_{$this->getUser()->id}).Doctrine ORM vs. ODM:
driver: odm in config.albegali_acl:
driver: odm
Circular Dependencies:
AclManager::isGranted() sparingly in listeners; prefer role-based checks.Performance:
is_granted() in masks) can slow down ACL checks.Enable Logging:
albegali_acl:
debug: true
Logs ACL operations to var/log/dev.log.
Dump ACLs: Use the CLI command to inspect ACLs:
php bin/console albegali:acl:dump
Common Errors:
Symfony\Security\Acl is installed (composer require symfony/security-acl).MASK instead of mask).Custom Drivers:
Extend Albegali\AclBundle\Driver\DriverInterface for non-Doctrine storage (e.g., Redis):
class RedisAclDriver implements DriverInterface
{
public function createMask($role, $objectIdentifier, $mask) { /* ... */ }
// Implement other methods...
}
Register in config:
albegali_acl:
driver: your_bundle.redis_acl_driver
Custom Masks:
Extend the mask system by overriding Albegali\AclBundle\Security\Acl\MaskBuilder.
Event Subscribers:
Listen to albegali.acl.pre_mask_create or albegali.acl.post_mask_update events for pre/post-processing:
use Albegali\AclBundle\Event\AclEvent;
public function onPreMaskCreate(AclEvent $event)
{
if ($event->getMask() === 'DELETE') {
$event->setMask('UPDATE'); // Override
}
}
remove_orphans:
Set to true to auto-delete ACL entries when their referenced entity is deleted (default: false).
albegali_acl:
remove_orphans: true
Expression Language:
Requires symfony/expression-language (≥3.0). For complex expressions, use:
$this->aclManager->createMask(
'(attribute(user) == "admin") and (object.getCategory() == "premium")',
'POST_123',
'DELETE'
);
How can I help you explore Laravel packages today?