diff --git a/app/library/Acl.php b/app/library/Acl.php index be6ed5d..80a3157 100644 --- a/app/library/Acl.php +++ b/app/library/Acl.php @@ -2,50 +2,93 @@ namespace Httpcb; -use Phalcon\Acl\Role, - Phalcon\Acl\Adapter\Memory as AclList; +use Phalcon\Config, + Phalcon\Acl\Role, + Phalcon\Acl\Adapter\Memory as Adapter; -class Acl extends AclList +class Acl { const ROLE_USER = 'user'; const ROLE_GUEST = 'guest'; - public function __construct() + /** + * @var Adapter + */ + protected $_adapter = null; + + public function __construct(Config $config) { + $this->_adapter = new Adapter(); + // Deny access to everything by default. - $this->setDefaultAction(\Phalcon\Acl::DENY); + $this->_adapter->setDefaultAction(\Phalcon\Acl::DENY); - // Roles - $guest = new Role(self::ROLE_GUEST); - $user = new Role(self::ROLE_USER); - - $this->addRole($guest); - $this->addRole($user, $guest); - - // Public Resources - $public = array( - 'index', - 'error', - 'auth', - 'api', - ); - - $this->_grant($guest, $public); - - // Protected Resources - $protected = array( - 'callback', - 'user', - ); - - $this->_grant($user, $protected); + $this->fromConfig($config); } - protected function _grant(Role $role, array $resources) + /** + * @param $role + * @param $resource + * @return bool + */ + public function isAllowed($role, $resource) { - foreach($resources as $resource) { - $this->addResource($resource, 'Read'); - $this->allow($role->getName(), $resource, 'Read'); + return $this->_adapter->isAllowed($role, $resource, 'All') == \Phalcon\Acl::ALLOW; + } + + /** + * @param string $resource + * @return bool + */ + public function hasResource($resource) + { + return $this->_adapter->isResource($resource); + } + + public function fromConfig(Config $config) + { + // Add roles. + foreach($config->roles as $name => $def) { + + $inherits = null; + $description = null; + + if ($def instanceof Config) { + $inherits = $def->get('inherits'); + $description = $def->get('description'); + + } + + $role = new Role($name, $description); + $this->_adapter->addRole($role, $inherits); + } + + // Zones + foreach($config->zones as $name => $resources) { + + if (!($resources instanceof Config)) { + $resources = new Config([ $resources ]); + } + + foreach($resources as $resource) { + $this->_adapter->addResource($resource, 'All'); + } + } + + // Grant access for roles and resources. + foreach($config->roles as $name => $def) { + + $zones = $def->get('allowed-zones', []); + + if (is_string($zones)) { + $zones = [ $zones ]; + } + + foreach($zones as $zone) { + foreach($config->zones->get($zone) as $resource) { + $this->_adapter->allow($name, $resource, 'All'); + } + } } } } diff --git a/app/listeners/AclListener.php b/app/listeners/AclListener.php index cd8b732..dd787fd 100644 --- a/app/listeners/AclListener.php +++ b/app/listeners/AclListener.php @@ -10,6 +10,11 @@ use Httpcb\Acl; class AclListener extends Plugin { + protected $_ignored_resources = [ + 'index', + 'error' + ]; + public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher) : bool { // We only have two roles for now, authenticated users and guests. @@ -19,24 +24,17 @@ class AclListener extends Plugin $role = Acl::ROLE_GUEST; } - // Support annotations for actions to define custom resources. - $controllerClass = $dispatcher->getControllerClass(); - $activeMethod = $dispatcher->getActiveMethod(); + // Get the resource from controller name. + $resource = $dispatcher->getControllerName(); - $annotation = $this->annotations->getMethod($controllerClass, $activeMethod); - - // ACL annotation found. use that. - if ($annotation->has('Acl')) { - $resource = $annotation->get('Acl')->getArgument('resource'); - } - // Otherwise, default to controller name. - else { - $resource = $dispatcher->getControllerName(); + // Ignore checks for error resource. + if (in_array($resource, $this->_ignored_resources)) { + return true; } // Now, check and redirect user to login page if // this role does not have access to this resource. - if ($this->acl->isAllowed($role, $resource, 'Read') == \Phalcon\Acl::DENY) { + if ($this->acl->isAllowed($role, $resource) === false) { // Forward to login page. $dispatcher->forward(array( @@ -47,7 +45,6 @@ class AclListener extends Plugin // Return false to stop the dispatch loop. return false; } - return true; } }