Issues

ZF-5369: Zend_Acl::allow($role) does not behave as expected

Description

The following code does not behave as I do expect:


$acl = new Zend_Acl();

$acl->addRole(new Zend_Acl_Role('user'));
$acl->addRole(new Zend_Acl_Role('admin'), 'user');

$acl->add(new Zend_Acl_Resource('resource'));

$acl->deny('user', 'resource');
$acl->allow('admin');

echo "user -> resource = ".(($acl->isAllowed('user', 'resource')) ? 'yes' : 'no')."\n";
echo "admin -> resource = ".(($acl->isAllowed('admin', 'resource')) ? 'yes' : 'no')."\n";

outputs: {quote} user -> resource = no admin -> resource = no {quote} I would expect that 'admin' is allowed to access 'resource'. Is there a bug?

Same thing with a modified code from http://devzone.zend.com/article/… :


$acl->add(new Zend_Acl_Resource('home'));
$acl->add(new Zend_Acl_Resource('news'));
$acl->add(new Zend_Acl_Resource('tutorials'));
$acl->add(new Zend_Acl_Resource('forum'));
$acl->add(new Zend_Acl_Resource('support'));
$acl->add(new Zend_Acl_Resource('admin'));

$acl->addRole(new Zend_Acl_Role('guest'));
$acl->addRole(new Zend_Acl_Role('member'), 'guest');
$acl->addRole(new Zend_Acl_Role('admin'), 'member');

// Guest may only view content
$acl->allow('guest', 'home');
$acl->allow('guest', 'news');
$acl->allow('guest', 'tutorials');
$acl->allow('member', 'forum');
$acl->deny('member', 'forum', 'update'); // Remove specific privilege
$acl->allow('member', 'support');
$acl->allow('admin'); // unrestricted access

echo "member -> forum : read = ".(($acl->isAllowed('member', 'forum', 'read')) ? 'yes' : 'no')."\n";
echo "admin -> forum : read = ".(($acl->isAllowed('admin', 'forum', 'read')) ? 'yes' : 'no')."\n";
echo "member -> forum : update = ".(($acl->isAllowed('member', 'forum', 'update')) ? 'yes' : 'no')."\n";
echo "admin -> forum : update = ".(($acl->isAllowed('admin', 'forum', 'update')) ? 'yes' : 'no')."\n";

{quote} member -> forum : read = yes admin -> forum : read = yes member -> forum : update = no admin -> forum : update = no {quote}

After a closer look, to me it seems as if the 'allResources'-part of the rules is only checked when $resource is explicitly null instead of - for example - checking it whenever there is no explicit rule for the given resource (in Zend_Acl::_getRules).

Comments

further investigations

This issue is a blocker, will investigate it, although i am not an expert at Zend_Acl

Confirmed the use-case as not working correctly.

Assigning to Ralph to get closure on this issues.

This issue cannot be changed without either changing the architecture drastically or breaking BC.

Given the original usecase: {php}$acl = new Zend_Acl();

$acl->addRole(new Zend_Acl_Role('user')); $acl->addRole(new Zend_Acl_Role('admin'), 'user');

$acl->add(new Zend_Acl_Resource('resource'));

$acl->deny('user', 'resource'); $acl->allow('admin');{php}

The point is that though you can allow role 'admin' to have access to everything, role 'user' is still the parent of user and by default this prevails. You can however change this behaviour by applying the attached patch. Point is then that you would break BC... see for that the current unittests, testCMSExample(), line 991: $this->assertFalse($this->_acl->isAllowed('administrator', 'announcement', 'archive')); (r16407)

What in that test basically is done is exactly the opposite: {php} $acl->addRole(new Zend_Acl_Role('user')); $acl->addRole(new Zend_Acl_Role('admin'), 'user');

$acl->add(new Zend_Acl_Resource('resource')); $acl->allow('admin'); $acl->deny(null, 'resource'); // Deny all access to 'resource'{php}

A solution would be with a future fix (reorganization of zend_acl, if any) would be to keep track of when which permission was added.

If not clear yet; I propose to either postpone this issue until zend_acl is drastically worked on (2.0?), or close it as a wont-fix.

I think it would be better to "dratically" work on Zend_ACL (which surely means breaking BC) instead of just closing this issue.

My idea is (basically) that you should have tree of roles and resources (and maybe also privileges), each with a ROOT element. Furthermore you should have an explizit access matrix for every combination of roles and resources where the possible values are 'allow', 'deny' and 'inherit' while 'inherit' should be default and would not need to be set explizitly.

One more thing about the current implementation: to me, it is not logical why one role should inherit from multiple other roles. At least the way it is currently implemented is hard to follow / confusing to me. An expample is given on http://framework.zend.com/manual/en/zend.acl.html This is a modification of the given example with an additional level of inheritance:


$acl = new Zend_Acl();

$acl->addRole(new Zend_Acl_Role('a1'))
    ->addRole(new Zend_Acl_Role('a2'));

$acl->addRole(new Zend_Acl_Role('b1'), 'a1')
    ->addRole(new Zend_Acl_Role('b2'), 'a2');

$parents = array('b1', 'b2');
$acl->addRole(new Zend_Acl_Role('someUser'), $parents);

$acl->add(new Zend_Acl_Resource('someResource'));

$acl->deny();

$acl->allow('a2', 'someResource');

$acl->deny('b1', 'someResource');

echo $acl->isAllowed('someUser', 'someResource') ? 'allowed' : 'denied';

The output is {quote} allowed {quote}

So you check 'b2' first, then 'b2's parent which is 'a2' (allowed). Given the case that there is no explizit rule for 'a2', then you would check 'b1' and its parent 'a1'... wouldn't it be more consequent then to completely walk up the tree to the ROOT with the result 'deny' (with no multiple inheritance possible) or check 'b1' first and then check the parents of 'b2' and 'b1'? I hope you understand what I mean (as I said, to me its is confusing...).

However, I really like Zend Framework and its really nice coding style. I would really like to get involved in this a bit!

This issue is certainly recognized. If the problem cannot be solved without major BC-breaks, probably a next version of an acl component will be added to ZF. There are already several proposals for this, I propose you keep an eye on those.