I have a simple ACL configures in an acl.global.php like this:
return [
'acl' => [
'roles' => [
'guest' => null,
'member' => 'guest',
'admin' => 'member'
],
'resources' => [
'allow' => [
'Application\Controller\Index' => ['all' => 'member'],
'Application\Controller\Error' => ['all' => 'member'],
'Item\Controller\Process' => [
'index' => 'member',
'create' => 'member',
'showItem' => 'member', // website.tld/item/:id
'showList' => 'member' // website.tld/list-items
]
]
],
]
];
A parser iterates through the configuration and generates from the array elements calls to Zend\Permissions\Acl#allow(...) like $this->allow($role, $controller, $action);.
Now I need additionally to restrict the access of the users to the item's single view (mydomain.tld/item/:id). A user should only get the access, if its id equals to the item.user_id (means: the user is the author/owner).
The way I see to implement this requirement is to extend the config
'Item\Controller\Process' => [
'index' => 'member',
'create' => 'member',
'showItem' => [
'role' => 'member',
'assertion' => 'UserIsOwner'
]
'showList' => 'member'
]
and to inject the Assertion to Zend\Permissions\Acl#allow(...): $this->allow($role, $controller, $action, $assertion);.
namespace Authorization\Acl\Assertion;
use ...
class UserIsOwner implements AssertionInterface
{
protected $userId;
// To inject the $userId can be the job of the factory.
public function __construct(int $userId)
{
$this->userId = $userId;
}
public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null)
{
return return $this->userId === ???;
}
}
But now I have no idea, how the assertion should get the item.user_id injected. The example in the docu doesn't have this problem, since it assets against the $_SERVER['REMOTE_ADDR'].
I can inject the ItemService to find out the item.user_id:
public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null)
{
return $this->isUserOwner();
}
protected function isUserOwner()
{
$itemId = ???;
$item = $this->itemService->findOne($itemId);
$itemOwnerId = $item->getUser()->getId();
return $this->userId == $itemOwnerId;
}
Though then I still need external data -- the current item.id.
At what place can/should the variable item's data (in this case the item.user_id or item.id) be injected to an assertion?
Finally I resolved the problem by injecting the variable data via the
resource. Don't think, that it's the cleanest or a recommended solution. Anyway it works. But it would be nice to know, how to resolve it a clean / more elegant way.UserIsOwnerUserIsOwnerFactoryParametrizedResourceAclAclFactory