Authorization cascading
There are 2 ways to cascade authorizations:
- via a hierarchy of resource: parent resources cascade their authorizations to sub-resources
- via a custom cascading strategy (if you have exotic needs)
The first solution is supported out of the box. Example: allowing a user to access a folder and all its sub-folders.
You have 2 solutions to define the hierarchical structure:
- implementing the
CascadingResource
interface - writing a
ResourceGraphTraverser
CascadingResource
This is a very simple solution, yet a bit limited, and it crowds your entity a bit.
Example:
class Category implements EntityResource, CascadingResource
{
/**
* @var Category[] Sub-categories
**/
private $children;
/**
* @var Category|null Parent category
**/
private $parent;
// ...
public function getParentResources(EntityManager $entityManager)
{
$parents = [ new ClassResource(get_class()) ];
if ($this->parent !== null) {
$parents[] = $this->parent;
}
return $parents;
}
public function getSubResources(EntityManager $entityManager)
{
return $this->children->toArray();
}
}
Note: if you want to give authorizations on the class-resource "All categories" (new ClassResource('Category')
)
don't forget to return it in getParentResources()
(as shown above). Else you can ignore it.
Just so you know, ClassResource
implements the CascadingResource
interface:
final class ClassResource implements ResourceInterface, CascadingResource
{
// ...
public function getSubResources(EntityManager $entityManager)
{
$repository = $entityManager->getRepository($this->class);
return $repository->findAll();
}
}
Important: with CascadingResource
, MyCLabs\ACL will assume each resource only returns its direct
children/parent resources. So they will be traversed recursively, which sometimes can be inefficient.
Have a look below for an alternative solution.
ResourceGraphTraverser
The ResourceGraphTraverser
is an object you write that must return the parent and sub-resources of a resource.
As explained, it must return all the sub/parent resources, which avoids MyCLabs\ACL recursively looking for sub/parent resources.
Example:
class FolderResourceGraphTraverser implements ResourceGraphTraverser
{
public function getAllParentResources(ResourceInterface $resource)
{
if (! $resource instanceof Folder) {
throw new \RuntimeException;
}
$parents = $resource->getAllParentFoldersRecursively();
$parents[] = new ClassResource(Folder::class);
return $parents;
}
public function getAllSubResources(ResourceInterface $resource)
{
if (! $resource instanceof Folder) {
throw new \RuntimeException;
}
return array_merge(
$resource->getAllSubFoldersRecursively(),
$resource->getAllFiles()
);
}
}
To register it:
$cascadeStrategy = new SimpleCascadeStrategy($entityManager);
$cascadeStrategy->setResourceGraphTraverser(
Folder::class,
$c->get(FolderResourceGraphTraverser::class)
);
$acl = new ACL($em, $cascadeStrategy);