Installation:
composer require problematic/acl-manager-bundle:dev-master
Register the bundle in AppKernel.php:
new Problematic\AclManagerBundle\ProblematicAclManagerBundle(),
Enable ACL in security.yml:
security:
acl:
connection: default
Initialize ACL:
php app/console init:acl
Grant a user ownership of a persisted entity:
$comment = new Comment();
$em->persist($comment);
$em->flush();
$aclManager = $this->get('problematic.acl_manager');
$aclManager->addObjectPermission($comment, MaskBuilder::MASK_OWNER);
Entity-Level Permissions:
addObjectPermission()/setObjectPermission() for granular control.$aclManager->revokePermission($comment, MaskBuilder::MASK_DELETE, $user);
Class-Level Permissions:
$aclManager->addClassPermission(Comment::class, MaskBuilder::MASK_EDIT, $user);
Field-Level Permissions:
$aclManager->addObjectFieldPermission($comment, 'title', MaskBuilder::MASK_EDIT);
Bulk Operations:
$aclManager->preloadAcls($comments);
prePersist/preUpdate to auto-apply permissions:
$entity->addLifecycleCallback(function($entity) {
$aclManager->addObjectPermission($entity, MaskBuilder::MASK_OWNER);
});
onFlush to batch permission updates:
$em->getEventManager()->addEventListener(
Doctrine\ORM\Events::onFlush,
function($event) use ($aclManager) {
$uow = $event->getEntityManager()->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() as $entity) {
$aclManager->addObjectPermission($entity, MaskBuilder::MASK_OWNER);
}
}
);
Persistence Requirement:
AclManager fails silently if the entity lacks an ID.flush() before granting permissions.User Context:
$userEntity defaults to the current session user (not the entity owner).$aclManager->addObjectPermission($comment, MaskBuilder::MASK_OWNER, $ownerUser);
Permission Mask Conflicts:
setObjectPermission() overwrites all permissions, not just the specified mask.revokeAllObjectPermissions() first if partial updates are needed.Field Permissions:
title) are case-sensitive and must match the entity property name exactly.$acl = $aclManager->findAcl($comment);
dump($acl->getObjectIdentity()->getIdentifier());
config.yml:
monolog:
handlers:
main:
level: DEBUG
channels: ["security"]
Custom Masks:
Extend MaskBuilder to define domain-specific permissions:
class CustomMaskBuilder extends MaskBuilder {
const MASK_PUBLISH = 0x8000;
}
Register as a service:
services:
problematic.acl_manager.mask_builder:
class: AppBundle\Security\CustomMaskBuilder
tags: ['problematic.acl_manager.mask_builder']
Auditing:
Subscribe to acl.update events to log permission changes:
$dispatcher->addListener('acl.update', function($event) {
$logger->info('Permission updated', [
'entity' => $event->getEntity(),
'mask' => $event->getMask(),
]);
});
Performance:
For large datasets, use preloadAcls() with a Doctrine\ORM\QueryBuilder to limit loaded entities:
$qb = $repo->createQueryBuilder('c')
->where('c.createdAt > :date')
->setParameter('date', new \DateTime('-1 month'));
$aclManager->preloadAcls($qb->getQuery()->getResult());
How can I help you explore Laravel packages today?