ZF-5700: Warning thrown in Zend_Acl when using "allow all roles access to X" scenario

Description

Warning: Undefined index: byRoleId Line 201: Zend_Acl.php

Happens when we define rule like this:


$acl->allow(null,'SomeResource','Someaction');

... and the rule is the only rule for this resource.

The error is in this snippet:


        foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) {
            foreach ($visitor['byRoleId'] as $roleIdCurrent => $rules) {
                if ($roleId === $roleIdCurrent) {
                    unset($this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]);
                }
            }
        }

Comments

The error is in Zend_Acl::removeRole (on line 201 in Zend Framework 1.8.4).

If we have a resource that only has privileges for all users, then the $visitor array looks like:

$visitor    Array [1]   
    allRoles    Array [1]   
        byPrivilegeId   Array [1]   
            Index   Array [2]   
                type    (string:10) TYPE_ALLOW  
                assert  null    

I.e. there is no byRoleId key in the $visitor array and so the foreach generates the Undefined Index warning.

Note that removeRole() loops through rules for every resource looking for privileges granted to the role being removed. This means that if there is a Resource that only has privileges for all users, the error occurs regardless of which role is being removed.

This error can be fixed by checking for the byRoleId array key before looping through the roles. I.e. instead of:


        foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) {
            foreach ($visitor['byRoleId'] as $roleIdCurrent => $rules) {
                if ($roleId === $roleIdCurrent) {
                    unset($this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]);
                }
            }
        }

we have


        foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) {
            if (array_key_exists('byRoleId', $visitor)) {
                foreach ($visitor['byRoleId'] as $roleIdCurrent => $rules) {
                    if ($roleId === $roleIdCurrent) {
                        unset($this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]);
                    }
                }
            }
        }

Adding patch for this issue

Patch applied to trunk and 1.9 release branch -- thanks!