composer require alexdpy/simple-acl-bundle
config/bundles.php (Symfony 4+) or AppKernel.php (Symfony <4):
AlexDpy\AclBundle\AlexDpyAclBundle::class => ['all' => true],
# config/services.yaml
services:
app.acl.database_provider:
class: AlexDpy\Acl\Database\Provider\DoctrineDbalProvider
arguments: ['@doctrine.dbal.default_connection']
# config/packages/alex_dpy_simple_acl.yaml
alex_dpy_simple_acl:
database_provider: app.acl.database_provider
use AlexDpy\AclBundle\Service\AclService;
class MyController extends AbstractController
{
public function __construct(private AclService $acl)
{}
public function index()
{
$canEdit = $this->acl->isGranted('edit', 'resource_id', 'user_id');
// ...
}
}
Defining Permissions:
Use the AclService to dynamically assign permissions:
$this->acl->allow('edit', 'post:1', 'user:1'); // Allow user:1 to edit post:1
$this->acl->deny('delete', 'post:*', 'user:*'); // Deny all users from deleting any post
Checking Permissions:
if ($this->acl->isGranted('view', 'profile:123', 'user:456')) {
// Render profile
}
Bulk Operations:
// Grant all permissions for a role
$this->acl->allow('*', 'article:*', 'role:admin');
Resource Hierarchies:
Leverage wildcards (*) for hierarchical resources (e.g., post:1/comments/*).
Symfony Security Integration: Use the ACL service in a Voter:
public function vote(AuthenticatedEntityInterface $user, $subject, array $attributes)
{
$acl = $this->container->get('alex_dpy_simple_acl.acl');
return $acl->isGranted($attributes[0], $subject->getId(), $user->getId());
}
Form-Level Permissions: Dynamically disable form fields based on ACL checks:
{% if app.acl.isGranted('edit', 'post.id', app.user.id) %}
{{ form_row(form.title) }}
{% endif %}
API Gateways: Use middleware to validate ACLs for incoming requests:
public function handle(Request $request, Closure $next)
{
if (!$this->acl->isGranted('access', $request->get('resource'), $request->get('user'))) {
throw new AccessDeniedException();
}
return $next($request);
}
Event-Driven Permissions:
Trigger ACL updates on entity events (e.g., post.updated):
public function onPostUpdated(PostUpdatedEvent $event)
{
$this->acl->allow('edit', 'post:'.$event->getPost()->getId(), $event->getUser()->getId());
}
Schema Mismatches:
php bin/console doctrine:schema:drop --force
php bin/console doctrine:schema:update --force
Caching Issues:
simple_acl by default).php bin/console cache:clear
Wildcard Overuse:
*:*) can bypass security.$this->acl->getPermissions('user:1'); // List all permissions for a user
Case Sensitivity:
strtolower()) if case-insensitive matching is needed.MaskBuilder Conflicts:
MaskBuilder classes must implement AlexDpy\Acl\MaskBuilderInterface.class CustomMaskBuilder extends \AlexDpy\Acl\MaskBuilder
{
public function build($permissions) { /* ... */ }
}
Enable Logging: Configure the ACL service to log decisions:
alex_dpy_simple_acl:
database_provider: app.acl.database_provider
debug: true # Logs all permission checks to Symfony's logger
Query Inspection: Use Doctrine’s query logging to verify ACL queries:
doctrine:
dbal:
logging: true
profiling: true
Common Errors:
database_provider service ID matches config.yml.schema.table_name config.1, 2, 4).Custom Providers:
Extend AlexDpy\Acl\Database\Provider\AbstractProvider for non-DBAL storage (e.g., Redis):
class RedisAclProvider extends AbstractProvider
{
public function __construct(Connection $redis)
{
$this->connection = $redis;
}
// Implement required methods
}
Dynamic Permission Generation:
Use the AclService to generate permissions on-the-fly:
$this->acl->generatePermissions('role:admin', [
'create' => 'post:*',
'edit' => 'post:*',
'delete' => 'post:*',
]);
Permission Inheritance: Implement a trait to inherit permissions from parent roles:
trait RoleInheritanceTrait
{
public function allow($permission, $resource, $requester)
{
if (str_starts_with($requester, 'role:')) {
$parentRoles = $this->getParentRoles($requester);
foreach ($parentRoles as $role) {
$this->allow($permission, $resource, $role);
}
}
parent::allow($permission, $resource, $requester);
}
}
Audit Trails: Log ACL changes to a separate table:
public function allow($permission, $resource, $requester)
{
$result = parent::allow($permission, $resource, $requester);
$this->logAclChange($permission, $resource, $requester, 'allow');
return $result;
}
Performance Optimization:
$this->acl->beginTransaction();
try {
$this->acl->allow('view', 'post:*', 'user:1');
$this->acl->allow('edit', 'post:*', 'user:1');
$this->acl->commit();
} catch (\Exception $e) {
$this->acl->rollBack();
throw $e;
}
$this->acl->invalidateCache('post:123');
How can I help you explore Laravel packages today?