roleId -> resourceId (array of privileges) * * @var array */ protected $_rules = array('allow' => array(), 'deny' => array()); public function __construct() { $this->_resources = new Phreamp_Map_Identity(array(), 'Phreamp_Acl_Resource'); $this->_roles = new Phreamp_Map_Identity(array(), 'Phreamp_Acl_Role'); } /** * Adds a resource to the ACL * * @throws Phreamp_Exception on duplicate IDs * @param Phreamp_Acl_Resource|string $resource * @return Phreamp_Acl */ public function addResource($resource) { if (!is_object($resource)) { $resource = new Phreamp_Acl_Resource((string) $resource); } if ($this->hasResource($resource->getId())) { throw new Phreamp_Exception("A resource with the ID '{$resource->getId()}' already exists in this ACL."); } $this->_resources->add($resource); return $this; } /** * Gets a resource by its ID * * @param string $id * @return Phreamp_Acl_Resource */ public function getResource($id) { if ($this->_resources->contains($id)) { return $this->_resources->get($id); } return false; } /** * Checks if a resource exists in this ACL * * @param string $id * @return boolean */ public function hasResource($id) { return $this->_resources->contains($id); } /** * Adds a role to the ACL * * @throws Phreamp_Exception on duplicate IDs * @param Phreamp_Acl_Role|string $role * @param Phreamp_Acl_Role|string $parent * @return Phreamp_Acl */ public function addRole($role, $parent = null) { if (!is_object($role)) { $role = new Phreamp_Acl_Role((string) $role); } if ($this->hasRole($role->getId())) { throw new Phreamp_Exception("A role with the ID '{$role->getId()}' already exists in this ACL."); } if (!is_null($parent) && !is_object($parent)) { if (!$parent = $this->getRole($parent)) { throw new Phreamp_Exception("The parent role '{$parent}' does not exist in this ACL."); } } $role->setAcl($this); if (!is_null($parent)) { $role->setParent($parent); } $this->_roles->add($role); return $this; } /** * Gets a role by its ID * * @param string $id * @return Phreamp_Acl */ public function getRole($id) { if ($this->_roles->contains($id)) { return $this->_roles->get($id); } return false; } /** * Checks if a role is registered with the ACL * * @param string $id * @return boolean */ public function hasRole($id) { return $this->_roles->contains($id); } /** * Add deny rule shortcut. * * @param Phreamp_Acl_Role|string $role * @param Phreamp_Acl_Resource|string $resource * @param string|array $permissions * @return Phreamp_Acl */ public function deny($role, $resource, $permissions) { return $this->addRule(self::RULE_DENY, $role, $resource, $permissions); } /** * Add allow rule shortcut. * * @param Phreamp_Acl_Role|string $role * @param Phreamp_Acl_Resource|string $resource * @param string|array $permissions * @return Phreamp_Acl */ public function allow($role, $resource, $permissions) { return $this->addRule(self::RULE_ALLOW, $role, $resource, $permissions); } /** * Adds a rule to the ACL. * * @throws Phreamp_Exception on invalid rule type * @throws Phreamp_Exception if role or resource are not registered in this ACL * @param string $type One of the Phreamp_Acl::RULE_* constants * @param Phreamp_Acl_Role|string $role * @param Phreamp_Acl_Resource|string $resource * @param string|array $permissions * @return Phreamp_Acl */ public function addRule($type, $role, $resource, $permissions) { if (!in_array($type, array(self::RULE_ALLOW, self::RULE_DENY))) { throw new Phreamp_Exception('Invalid rule type.'); } if ($role instanceof Phreamp_Acl_Role) { $role = $role->getId(); } if ($resource instanceof Phreamp_Acl_Resource) { $resource = $resource->getId(); } if (!$this->_roles->contains($role)) { throw new Phreamp_Exception("The role '{$role}' is not registered in this ACL."); } if (!$this->_resources->contains($resource)) { throw new Phreamp_Exception("The resource '{$resource}' is not registered in this ACL."); } switch ($type) { case self::RULE_ALLOW: $index = 'allow'; break; case self::RULE_DENY: $index = 'deny'; break; } if (!key_exists($role, $this->_rules[$index])) { $this->_rules[$index][$role] = array(); } if (!key_exists($resource, $this->_rules[$index][$role])) { $this->_rules[$index][$role][$resource] = array(); } if (is_array($permissions)) { $this->_rules[$index][$role][$resource] = array_merge($this->_rules[$index][$role][$resource], $permissions); } else { $this->_rules[$index][$role][$resource][] = (string) $permissions; } return $this; } /** * Checks if a role is allowed access to a privilege on a particular resource. * Deny rules have higher precedence than allow rules, so if a deny rule exists * for the role in the tree then it will be denied, regardless of any allow rules * that might be present. * * @param string $role * @param string $resource * @param string $privilege * @return boolean */ public function isAllowed($role, $resource, $privilege) { if ($this->_checkRule(self::RULE_DENY, $role, $resource, $privilege)) { return false; } else { return $this->_checkRule(self::RULE_ALLOW, $role, $resource, $privilege); } } /** * Checks if a role has a particular type of rule for a privilege of a resource * in the tree. * * @throws Phreamp_Exception on invalid rule type * @param string $rule One of the Phreamp_Acl::RULE_* constants * @param string $role * @param string $resource * @param string $privilege * @return boolean */ protected function _checkRule($rule, $role, $resource, $privilege) { switch ($rule) { case self::RULE_ALLOW: $rindex = 'allow'; break; case self::RULE_DENY: $rindex = 'deny'; break; default: throw new Phreamp_Exception('Invalid rule type.'); break; } $deny = false; if (isset($this->_rules[$rindex][$role][$resource]) && in_array($privilege, $this->_rules[$rindex][$role][$resource])) { return true; } $parent = $this->getRole($role)->getParent(); if (!is_null($parent)) { $deny = $this->_checkRule($rule, $parent->getId(), $resource, $privilege); } return $deny; } }