diff --git a/src/Acl.php b/src/Acl.php index c4179e4..c00f166 100644 --- a/src/Acl.php +++ b/src/Acl.php @@ -572,7 +572,9 @@ public function setRule($operation, $type, $roles = null, $resources = null, $resources = array(); foreach ($resourcesTemp as $resource) { if (null !== $resource) { - $resources[] = $this->getResource($resource); + $children = $this->getChildResources($this->getResource($resource)); + $resources = array_merge($resources, $children); + $resources[$resource] = $this->getResource($resource); } else { $resources[] = null; } @@ -659,6 +661,28 @@ public function setRule($operation, $type, $roles = null, $resources = null, return $this; } + /** + * Returns all child resources from the given resource. + * + * @param Resource\ResourceInterface|string $resource + * @return Resource\ResourceInterface[] + */ + protected function getChildResources(Resource\ResourceInterface $resource) + { + $return = array(); + $id = $resource->getResourceId(); + + $children = $this->resources[$id]['children']; + foreach($children as $child) { + $child_return = $this->getChildResources($child); + $child_return[$child->getResourceId()] = $child; + + $return = array_merge($return, $child_return); + } + + return $return; + } + /** * Returns true if and only if the Role has access to the Resource * @@ -747,7 +771,10 @@ public function isAllowed($role = null, $resource = null, $privilege = null) if (null !== ($ruleType = $this->getRuleType($resource, null, $privilege))) { return self::TYPE_ALLOW === $ruleType; } elseif (null !== ($ruleTypeAllPrivileges = $this->getRuleType($resource, null, null))) { - return self::TYPE_ALLOW === $ruleTypeAllPrivileges; + $result = self::TYPE_ALLOW === $ruleTypeAllPrivileges; + if ($result || null === $resource) { + return $result; + } } // try next Resource diff --git a/test/AclTest.php b/test/AclTest.php index ebe62d1..b8788f5 100644 --- a/test/AclTest.php +++ b/test/AclTest.php @@ -1303,4 +1303,29 @@ public function testRemoveDenyWithNullResourceAppliesToAllResources() $this->assertFalse($this->_acl->isAllowed('guest', 'newsletter', 'read')); } + /** + * @group ZF2-3454 + */ + public function testAclResourcePermissionsAreInheritedWithMultilevelResourcesAndDenyPolicy() + { + $this->_acl->addRole('guest'); + $this->_acl->addResource('blogposts'); + $this->_acl->addResource('feature', 'blogposts'); + $this->_acl->addResource('post_1', 'feature'); + $this->_acl->addResource('post_2', 'feature'); + + // Allow a guest to read feature posts and + // comment on everything except feature posts. + $this->_acl->deny(); + $this->_acl->allow('guest', 'feature', 'read'); + $this->_acl->allow('guest', null, 'comment'); + $this->_acl->deny('guest', 'feature', 'comment'); + + $this->assertFalse($this->_acl->isAllowed('guest', 'feature', 'write')); + $this->assertTrue($this->_acl->isAllowed('guest', 'post_1', 'read')); + $this->assertTrue($this->_acl->isAllowed('guest', 'post_2', 'read')); + + $this->assertFalse($this->_acl->isAllowed('guest', 'post_1', 'comment')); + $this->assertFalse($this->_acl->isAllowed('guest', 'post_2', 'comment')); + } }