Search code examples
phpaclzend-acl

How to pass custom data to a Zend Acl custom Assertion


The Zend Acl docs show an example on using a custom assertion:

$acl->allow(null, null, null, new MyCustomAssertion());

The problem is that the above code is executed while creating the rules not while checking them. In my controller I can only do something like:

 $acl->isAllowed('someUser', 'someResource') 

Unlike Zend Rbac the assertion class is already instantiated and I cannot pass it a user ID and post ID to check if the user has access to that post in particular.

Is checking if a user has access to a post from a controller achievable (in a maintainable way) with Zend Acl?

Note 1: I'm not using the Zend framework for this, just the Zend Acl component. Note 2: The reason I'm not using Rbac is because I need the "deny" feature that Acl has and Rbac doesn't.


Solution

  • One way is to create your own implementation of a role and a resource:

    class MyCustomAssertion implements Zend\Permissions\Acl\Assertion\AssertionInterface
    {
        public function assert(Zend\Permissions\Acl\Acl $acl,
            Zend\Permissions\Acl\Role\RoleInterface $role = null,
            Zend\Permissions\Acl\Resource\ResourceInterface $resource = null,
            $privilege = null)
        {
            if(is_a($role, UserRole::class) && is_a($resource, PostResource::class)) {
                $post_id = $resource->getResourceId();
                $user_id = $role->getId();
                // find out if the user has access to this post id(eg with a database query)
                // return true or false.
                return true;
            }
    
            return true;
        }
    
    }
    
    class PostResource implements Zend\Permissions\Acl\Resource\ResourceInterface
    {
        private $post_id;
    
        public function __construct($post_id)
        {
            $this->post_id = $post_id;
        }
    
        public function getId()
        {
            return$this->post_id;
        }
        public function getResourceId()
        {
            return 'post';
        }
    }
    
    class UserRole implements Zend\Permissions\Acl\Role\RoleInterface
    {
    
        private $id;
    
        public function __construct($id)
        {
            $this->id = $id;
        }
    
        public function getId()
        {
            return $this->id;
        }
        public function getRoleId()
        {
            return 'user';
        }
    }
    
    
    use Zend\Permissions\Acl\Acl;
    use Zend\Permissions\Acl\Role\GenericRole as Role;
    use Zend\Permissions\Acl\Resource\GenericResource as Resource;
    
    $acl = new Acl();
    
    $acl->addRole(new Role('user'));
    
    $acl->addResource(new Resource('post'));
    
    $acl->allow(null, null, null, new MyCustomAssertion());
    
    // lets check if user with id 11 has access to post with id 5.
    $acl->isAllowed(new UserRole(11), new PostResource(5));
    

    Yes, this way you can add this check in a controller using the last line above.