ZF-1722: Zend_Acl assertions broken when inheritance is required (ie DepthFirstSearch)

Description

Reproduction code


webdeveloper@webdevelopment ~/testing $ cat test_aclwhoswho.php 
<?php

require_once 'Zend/Acl.php';
require_once 'Zend/Acl/Role.php';
require_once 'Zend/Acl/Resource.php';

class HasIQFor implements Zend_Acl_Assert_Interface
{
    public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $role = null,
                           Zend_Acl_Resource_Interface $resource = null, $privilege = null)
    {
        echo "digging deeper";
        echo var_dump($role);
    }
}

class Acl_Child implements Zend_Acl_Role_Interface { public function getRoleId() { return 'Child'; } }
class Acl_Parent implements Zend_Acl_Role_Interface { public function getRoleId() { return 'Parent'; } }

$acl = new Zend_Acl();
$acl->addRole(new Zend_Acl_Role('Child'))
    ->addRole(new Zend_Acl_Role('Parent'), 'Child')
    ->add(new Zend_Acl_Resource('Toy'))
    ->add(new Zend_Acl_Resource('PokerGame'))
    ->allow('Child', 'Toy', 'Play', new HasIQFor())
    ->allow('Parent', 'PokerGame', 'Play', new HasIQFor());

$child = new Acl_Child();
$mommy = new Acl_Parent();

echo "Children and toys?\n"; echo $acl->isAllowed($child, 'Toy', 'Play');
echo "\n\nChildren and poker?\n"; echo $acl->isAllowed($child, 'PokerGame', 'Play');
echo "\n\nParents and toys?\n"; echo $acl->isAllowed($mommy, 'Toy', 'Play');
echo "\n\nParents and poker?\n"; echo $acl->isAllowed($mommy, 'PokerGame', 'Play');

webdeveloper@webdevelopment ~/testing $ php test_aclwhoswho.php 
Children and toys?
digging deeperobject(Zend_Acl_Role)#2 (1) {
  ["_roleId:protected"]=>
  string(5) "Child"
}


Children and poker?


Parents and toys?
digging deeperobject(Zend_Acl_Role)#2 (1) {
  ["_roleId:protected"]=>
  string(5) "Child"
}


Parents and poker?
digging deeperobject(Zend_Acl_Role)#4 (1) {
  ["_roleId:protected"]=>
  string(6) "Parent"
}

you can notice the problem in the 3rd case where when trying to determine if the parent has the IQ to play with the toy, the parent object is not passed to the assertion.

Comments

Postponing to after 1.0.1.

We should resolve this for 1.1.0.

But why is this a problem? The rule is satisfied by the Child role and since there is no ACL specifically for Parents on the Toy then Zend_Acl returns true.

Wouldn't performance suffer if Zend_Acl iterates through the whole inheritance only to come to the same conclusion that it did in the first place? Or do you see a use case where a custom Assertion will need to query this hierarchy to supply a more qualitative answer?

Hey Simon, The problem i specifically notice (from above) is in test case number 3. If I am writing custom assertions, then my assertion (if applied) should always get the exact object that i proposed when i asked if it "isAllowed".


// IF I DO:
$acl->isAllowed($mommy, 'Toy', 'Play'); // where mommy is of type (class Acl_Parent)

I should expect any assertions that were triggered as a result of that exact query should indeed get teh exact object i passed into the query. The reason for that expectation is in case I want to call some Acl_Parent specific methods in order to assert that what is queried should be allowed or not.

I see no direct performance impact, its simply a matter of whats being passed to the assertion.

as far as this statement: > Or do you see a use case where a custom Assertion will need to query this hierarchy to supply a more qualitative answer?

Assertions don't query the inheritance chain, its actually the other way around, currently the inheritance chain is iterated in order to trigger the appropriate assertion.

make sense?

This is still not fixed in 1.5.2, and is still quite irritating when trying to write any non-trivial assertions.

One workaround: Inherit Zend_Acl, override isAllowed(), store the parameters in a private member and make them available through a new method, say, "getLastRequest()". Then, in an assertion, get them using something like: list($role, $resource, $privilege) = $acl->getLastRequest();

(It's a good idea to make sure getLastRequest() always returns objects, even if a string was passed to isAllowed().)

Fix in trunk as of r17317, please test.