effinix/user-permission-bundle
Installation
composer require effinix/user-permission-bundle
Register the bundle in config/bundles.php:
return [
// ...
Effinix\UserPermissionBundle\EffinixUserPermissionBundle::class => ['all' => true],
];
Configure Permissions
Define permissions in config/packages/effinix_user_permission.yaml:
effinix_user_permission:
permission_store:
permissions:
- 'login'
- 'admin_dashboard'
- 'edit_profile'
First Use Case
Protect a route with @RequirePermission:
#[Route('/dashboard', name: 'dashboard')]
#[RequirePermission('admin_dashboard')]
public function dashboard(): Response
{
return new Response('Welcome to the dashboard!');
}
Implement UserInterface
Extend your User entity:
class User implements UserInterface
{
public function hasPermission(string $permission): bool
{
return in_array($permission, $this->getPermissions());
}
public function getPermissions(): array
{
return ['login', 'edit_profile']; // Example permissions
}
}
Test in Development
Enable caching for performance (default: true):
when@dev:
effinix_user_permission:
do_cache: true
Role-Based Permissions
Use hasPermission() to dynamically assign permissions:
if ($user->hasPermission('admin_dashboard')) {
return $this->renderDashboard();
}
Permission Groups Group permissions in config for reusability:
effinix_user_permission:
permission_store:
permissions:
- 'user.*' # Wildcard for user-related actions
- 'admin.*'
Invokable Controllers Protect invokable controllers (Symfony 6.3+):
#[AsController]
#[RequirePermission('edit_profile')]
class ProfileController
{
public function __invoke(): Response
{
return new Response('Profile updated!');
}
}
Middleware Integration Combine with Symfony’s middleware for global checks:
// src/Kernel.php
protected function build(RequestContext $requestContext): void
{
$requestContext->addListener(
new PermissionMiddleware($this->getPermissionStore())
);
}
API Resource Protection Use with API Platform or API Resource controllers:
#[ApiResource(operations: [
new Get(collection: false, requirements: ['id' => '\d+']),
new Post(denormalizationContext: ['groups' => ['admin']]),
new Put(denormalizationContext: ['groups' => ['admin']]),
])]
#[RequirePermission('admin_edit')]
class AdminResource
{
}
Doctrine Integration Fetch permissions from a database table:
public function getPermissions(): array
{
return $this->permissionRepository->findBy(['user' => $this]);
}
Symfony Security Component Bridge with Symfony’s security system:
// src/Security/Voter/PermissionVoter.php
class PermissionVoter extends Voter
{
public function voteOnAttribute(
string $attribute,
$subject,
UserInterface $user
): bool {
return $user->hasPermission($attribute);
}
}
Event Listeners Log permission checks:
// src/EventListener/PermissionListener.php
class PermissionListener implements KernelEventSubscriber
{
public function onKernelController(
ControllerEvent $event
): void {
$controller = $event->getController();
if (is_array($controller)) {
$permission = $controller[0]->getPermission();
$this->logger->info("Permission checked: {$permission}");
}
}
}
Caching Issues
php bin/console cache:clear
dev if permissions change dynamically:
when@dev:
effinix_user_permission:
do_cache: false
Permission Store Provider
provider key in config is required but undocumented. Use a custom provider if needed:
effinix_user_permission:
permission_store:
provider: 'custom_provider_id'
Attribute Conflicts
@RequirePermission is placed before @Route or @AsController to avoid conflicts.Wildcard Permissions
user.*) are not supported by default. Implement custom logic in hasPermission():
public function hasPermission(string $permission): bool
{
return str_starts_with($permission, 'user.') &&
$this->isUserRole();
}
Invokable Controllers
#[AsController]
#[RequirePermission('invokable_action')]
class InvokableController
{
public function __invoke() { ... }
}
Check Permissions Dump permissions in a controller:
dump($this->getUser()->getPermissions());
Enable Debugging Add a debug listener:
// src/EventListener/DebugPermissionListener.php
class DebugPermissionListener implements KernelEventSubscriber
{
public function onKernelRequest(RequestEvent $event): void
{
if ($event->isMainRequest()) {
$this->logger->debug(
'Permissions checked: ' . implode(', ', $this->getCheckedPermissions())
);
}
}
}
Custom Permission Store
Extend PermissionStoreInterface for database-backed permissions:
class DatabasePermissionStore implements PermissionStoreInterface
{
public function hasPermission(UserInterface $user, string $permission): bool
{
return $this->entityManager->getRepository(Permission::class)
->exists(['user' => $user->getId(), 'name' => $permission]);
}
}
Dynamic Permissions
Override getPermissions() to fetch from an external API:
public function getPermissions(): array
{
$response = $this->httpClient->request('GET', '/api/user/permissions');
return json_decode($response->getContent(), true);
}
Permission Events Dispatch events for permission changes:
// src/Event/PermissionChangedEvent.php
class PermissionChangedEvent extends Event
{
public function __construct(private string $permission) {}
public function getPermission(): string { return $this->permission; }
}
GUI for Permissions Create a CRUD interface for managing permissions:
// src/Controller/PermissionController.php
#[Route('/admin/permissions')]
class PermissionController extends AbstractController
{
#[Route('/new', name: 'permission_new')]
#[RequirePermission('admin_manage_permissions')]
public function new(Request $request): Response
{
// ...
}
}
How can I help you explore Laravel packages today?