I'm writing an ACL that needs to take namespaces into account.
Some of my controllers are named the same but are from different namespaces.
Example:
App\Controllers\Account\Tickets
App\Controllers\Admin\Tickets
How can I add this functionality to my ACL? My first thought was to add the namespace in the resourcename and let the beforeDispatch code do a namepace+controller search in all the available resources. This resulted in an Dispatcher has detected a cyclic routing causing stability problems
error.
Thanks in advance for any help!
Original code:
<?php
use Phalcon\Acl;
use Phalcon\Acl\Role;
use Phalcon\Acl\Adapter\Memory as AclList;
use Phalcon\Acl\Resource;
use Phalcon\Events\Event;
use Phalcon\Mvc\User\Plugin;
use Phalcon\Mvc\Dispatcher;
class SecurityPlugin extends Plugin
{
/**
* Returns an existing or new access control list
*
* @returns AclList
*/
public function getAcl()
{
if (!isset($this->persistent->acl)) {
$acl = new AclList();
$acl->setDefaultAction(Acl::DENY);
// Register roles
$roles = [
'admins' => new Role(
'admins',
'Website administrators'
),
'users' => new Role(
'users',
'Member privileges, granted after sign in.'
),
'guests' => new Role(
'guests',
'Anyone browsing the site who is not signed in is considered to be a "Guest".'
)
];
foreach ($roles as $role) {
$acl->addRole($role);
}
//Private area resources
$privateResources = array(
'accountdashboard' => array('*'),
'licensemanager' => array('*'),
'profile' => array('*'),
'tickets' => array('*')
);
$privateResourcesAdmin = array(
'admindashboard' => array('*'),
'tickets' => array('*'),
'licensemanager' => array('*')
);
//Public area resources
$publicResources = array(
//Public section
'index' => array('*'),
'register' => array('*'),
'errors' => array('show401', 'show404', 'show500'),
'register' => array('*'),
'login' => array('*'),
'logout' => array('*'),
'buy' => array('*'),
'license' => array('*'),
//API
'auth' => array('*'),
'user' => array('*'),
'courses' => array('*'),
'announcements' => array('*'),
);
foreach ($privateResources as $resource => $actions) {
$acl->addResource(new Resource($resource), $actions);
}
foreach ($privateResourcesAdmin as $resource => $actions) {
$acl->addResource(new Resource($resource), $actions);
}
foreach ($publicResources as $resource => $actions) {
$acl->addResource(new Resource($resource), $actions);
}
//Grant access to public areas to users, admins and guests
foreach ($roles as $role) {
foreach ($publicResources as $resource => $actions) {
foreach ($actions as $action){
$acl->allow($role->getName(), $resource, $action);
}
}
}
//Grant access to private area to role Users
foreach ($privateResources as $resource => $actions) {
foreach ($actions as $action){
$acl->allow('users', $resource, $action);
}
}
foreach ($privateResourcesAdmin as $resource => $actions) {
foreach ($actions as $action){
$acl->allow('admins', $resource, $action);
}
}
//The acl is stored in session, APC would be useful here too
$this->persistent->acl = $acl;
}
return $this->persistent->acl;
}
/**
* This action is executed before execute any action in the application
*
* @param Event $event
* @param Dispatcher $dispatcher
* @return bool
*/
public function beforeDispatch(Event $event, Dispatcher $dispatcher){
$auth = $this->session->has('auth');
if (!$auth){
$role = 'guests';
} else {
$authSession = $this->session->get("auth");
if($authSession['account_type'] == 'admin'){
$role = 'admins';
} else {
$role = 'users';
}
}
$namespace = $dispatcher->getNamespaceName();
$controller = $dispatcher->getControllerName();
$action = $dispatcher->getActionName();
$acl = $this->getAcl();
if (!$acl->isResource($controller)) {
$dispatcher->forward([
'namespace' => 'App\\Controllers',
'controller' => 'errors',
'action' => 'show404'
]);
return false;
}
$allowed = $acl->isAllowed($role, $controller, $action);
if (!$allowed) {
if($namespace == 'App\\Controllers\\Admin'){
$dispatcher->forward(array(
'namespace' => 'App\\Controllers',
'controller' => 'errors',
'action' => 'show404'
));
} else {
$dispatcher->forward(array(
'namespace' => 'App\\Controllers',
'controller' => 'errors',
'action' => 'show401'
));
}
return false;
}
}
}