Installation:
composer require at/resource-access
Add the bundle to config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 2/3):
AT\ResourceAccessBundle\ATResourceAccessBundle::class => ['all' => true],
Configure Doctrine:
Update config/packages/doctrine.yaml (Symfony 4+) or config.yml (Symfony 2/3):
doctrine:
orm:
resolve_target_entities:
AT\ResourceAccessBundle\Entity\Requester: App\Entity\User
Implement RequesterInterface:
Extend your User entity:
use AT\ResourceAccessBundle\Model\RequesterInterface;
class User implements RequesterInterface
{
// ...
}
Create a Resource Entity:
Implement ResourceInterface and embed the Resource entity:
use AT\ResourceAccessBundle\Model\ResourceInterface;
use AT\ResourceAccessBundle\Entity\Resource;
class MyResource implements ResourceInterface
{
/**
* @ORM\OneToOne(targetEntity="AT\ResourceAccessBundle\Entity\Resource", cascade={"persist"})
*/
private $resource;
public function __construct()
{
$this->resource = new Resource();
}
public function getResource(): Resource
{
return $this->resource;
}
}
Define Role Hierarchy:
Configure in config/packages/at_resource_access.yaml (Symfony 4+):
at_resource_access:
resources:
App\Entity\MyResource:
role_hierarchy:
ROLE_ADMIN: [ROLE_EDIT]
ROLE_EDIT: [ROLE_READ]
Update Schema:
php bin/console doctrine:schema:update --force
Grant access to a resource in a controller:
use AT\ResourceAccessBundle\Manager\ResourceAccessManagerInterface;
class MyResourceController extends AbstractController
{
public function grantAccess(ResourceAccessManagerInterface $manager, User $user, MyResource $resource)
{
$manager->grantAccess($user, $resource, ['ROLE_EDIT']);
return new Response('Access granted!');
}
}
Granting Access:
// Single role
$manager->grantAccess($user, $resource, ['ROLE_READ']);
// Multiple roles
$manager->grantAccess($user, $resource, ['ROLE_READ', 'ROLE_EDIT'], $grantedByUser);
Checking Access:
// For logged-in user (auto-resolves from security context)
$manager->isGranted('ROLE_READ', $resource);
// For specific user
$manager->isGranted('ROLE_EDIT', $resource, $user);
Bulk Updates:
// Replace all roles
$manager->updateAccessLevels($user, $resource, ['ROLE_ADMIN']);
// Remove specific roles
$manager->removeAccessLevels($user, $resource, ['ROLE_READ']);
Revoking All Access:
$manager->removeAccess($user, $resource);
Event Listeners: Trigger access checks in pre-persist/update listeners:
public function preUpdate(LifecycleEventArgs $args)
{
$resource = $args->getObject();
$user = $this->security->getUser();
if (!$this->manager->isGranted('ROLE_EDIT', $resource, $user)) {
throw new AccessDeniedException();
}
}
Voters:
Extend AbstractResourceVoter for custom logic:
use AT\ResourceAccessBundle\Security\Voter\AbstractResourceVoter;
class MyResourceVoter extends AbstractResourceVoter
{
protected function supports(string $attribute, $subject): bool
{
return $attribute === 'EDIT' && $subject instanceof MyResource;
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
return $this->manager->isGranted('ROLE_EDIT', $subject, $token->getUser());
}
}
Forms: Dynamically populate role selections based on hierarchy:
$form->add('roles', EntityType::class, [
'class' => Role::class,
'choices' => $this->getAllowedRoles($resource),
]);
APIs:
Use isGranted() in API controllers to validate requests:
public function update(MyResource $resource, Request $request)
{
if (!$this->manager->isGranted('ROLE_EDIT', $resource)) {
return $this->json(['error' => 'Unauthorized'], 403);
}
// ...
}
Role Hierarchy Conflicts:
config.yml. Example:
ROLE_ADMIN: [ROLE_EDIT, ROLE_READ]
ROLE_EDIT: [ROLE_READ]
bin/console debug:role-hierarchy (if available) or log the hierarchy:
$manager->getRoleHierarchy($resource);
Circular Dependencies:
Resource entity in your custom resource causes null references.$resource in the constructor and map it as @OneToOne.Doctrine Proxy Issues:
Resource entities fail in voters/listeners.Resource entity eagerly or use getResource()->getId() to trigger loading.Security Context:
isGranted() without a user parameter fails if no user is logged in.$user explicitly or handle null:
$user = $this->security->getUser() ?: new AnonymousUserToken();
Schema Updates:
doctrine:schema:update after adding new resources.composer.json:
"scripts": {
"post-install-cmd": [
"php bin/console doctrine:schema:update --force"
]
}
Log Access Decisions:
$manager->isGranted('ROLE_READ', $resource, $user, true); // Enable debug mode
Inspect Role Hierarchy:
$hierarchy = $manager->getRoleHierarchy($resource);
var_dump($hierarchy);
Check Granted Roles:
$grantedRoles = $manager->getAccessLevels($user, $resource);
var_dump($grantedRoles);
Symfony Profiler:
use AT\ResourceAccessBundle\DataCollector\ResourceAccessDataCollector;
services:
app.resource_access.data_collector:
class: AT\ResourceAccessBundle\DataCollector\ResourceAccessDataCollector
arguments: ['@resource_access.manager']
tags: ['data_collector']
Custom Storage:
ResourceAccessStorage interface to use a non-Doctrine backend (e.g., Redis):
use AT\ResourceAccessBundle\Storage\ResourceAccessStorageInterface;
class RedisResourceAccessStorage implements ResourceAccessStorageInterface
{
// Implement methods to use Redis
}
services:
resource_access.storage:
class: App\Storage\RedisResourceAccessStorage
Dynamic Role Hierarchies:
$hierarchy = $this->hierarchyRepository->findForResource($resource);
$manager->setRoleHierarchy($resource, $hierarchy);
Bulk Operations:
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class GrantAccessCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output)
{
$users = $this->userRepository->findBy(['role' => 'ROLE_ADMIN']);
$resource = $this->resourceRepository->find(1);
foreach ($users as $user) {
$this->
How can I help you explore Laravel packages today?