We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

ACL: how to modify \Acl::ALLOW part to work with different roles

This is an example of how beforeExecuteRoute method is dealing with allowing users and guests to appropriate area resources:

 if($allowed != Phalcon\Acl::ALLOW) 
        {

        //If he doesn't have access forward him to the index controller
                $this->flashSession->warning("You don't have access to this module");
                $this->response->redirect('/index');

        //Returning "false" we tell to the dispatcher to stop the current operation
        return false;
        }

What if i have one more role - admins and don't want to have users end up in index page when they try to enter admin area(also want to send another message than to guests)?? Here is my Security.php code:

<?php

use Phalcon\Events\Event,
Phalcon\Mvc\Dispatcher,
Phalcon\Acl,
Phalcon\Acl\Adapter\Memory as AclList,
Phalcon\Mvc\User\Plugin;

class Security extends Plugin
{

    public function _getAcl()
    {
          $this->persistent->destroy();

        if (!isset($this->persistent->acl))
        {

        //Create the ACL
        $acl = new AclList();

        //The default action is DENY access
        $acl->setDefaultAction(Acl::DENY);

        //Register three roles, Users are registered users, 
        //Admin is the registered SuperUser
        //and Guests are users without a defined identity

        $roles = array(
            'users' => new Phalcon\Acl\Role('Users'),
            'guests' => new Phalcon\Acl\Role('Guests'),
            'admin' => new Phalcon\Acl\Role('Admin')
        );
            foreach ($roles as $role)
            {
                $acl->addRole($role);
            }

            //Admin area resources
        $adminResources = array(
            'users' => array('index', 'new', 'edit', 'delete'),
        );

            foreach ($adminResources as $resource => $actions)
            {
                $acl->addResource(new Phalcon\Acl\Resource($resource), $actions);
            } 

            // User area resources
        $userResources = array(
            'calendar' => array('index', 'update', 'book', 'done'),
        );
            foreach ($userResources as $resource => $actions)
            {
                $acl->addResource(new Phalcon\Acl\Resource($resource), $actions);
            } 

            //Public area resources (frontend)
        $publicResources = array(
            'session' => array('index', 'start', 'end'),
            'notfound' => array('route404')
        );
            foreach ($publicResources as $resource => $actions)
            {
                $acl->addResource(new Phalcon\Acl\Resource($resource), $actions);
            }

        //Grant access to public areas to all
        foreach ($roles as $role)
        {
            foreach ($publicResources as $resource => $actions)
            {
                $acl->allow($role->getName(), $resource, '*');
            }
        }

        //Grant access to calendar area to roles Users and Admin
        foreach ($userResources as $resource => $actions)
        {
            foreach ($actions as $action)
            {
                $acl->allow('Users', $resource, $action);
                $acl->allow('Admin', $resource, $action);
            }
        }
        //Grant access to admin area to role Admin only
        foreach ($adminResources as $resource => $actions)
        {
            foreach ($actions as $action)
            {
                $acl->allow('Admin', $resource, $action);
            }
        }
        return $acl;
        }
    }

    public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher)
    {
        //Check whether the "auth" variable exists in session to define the active role

        $auth = $this->session->get('auth');
        if(!$auth)
        {
            $role = 'Guests';
        }
        else if($auth['admin_role'] == 0)
        {
            $role = 'Users';
        }
        else
        {
            $role = 'Admin';
        }

        //Take the active controller/action from the dispatcher

        $controller = $dispatcher->getControllerName();
        $action = $dispatcher->getActionName();

        //Obtain the ACL list

        $acl = $this->_getAcl();

        //Check if the Role have access to the controller (resource)
        $allowed = $acl->isAllowed($role, $controller, $action);

        if($acl->isAllowed('Users', 'users', 'index'))
        {
                $this->flashSession->warning("You don't have access to admin area");

            //It does not work this way!!       
        }
        if($allowed != Phalcon\Acl::ALLOW) 
        {

        //If he doesn't have access forward him to the session/index controller
                $this->flashSession->warning("You don't have access to this module");
                $this->response->redirect('session/index');

        //Returning "false" we tell to the dispatcher to stop the current operation
        return false;
        }

    }

}


14.9k
edited May '15

well, i found myself the way, it can be done:

if($allowed != Phalcon\Acl::ALLOW) 
        {
            if($role == 'Users' && $controller == 'users')
            {
                $this->flashSession->warning("You don't have access to admin area");

            }
            else
            {
                    //If guests don't have access forward them to the session/start controller
                $this->flashSession->warning("You are not logged in!");
                $this->response->redirect('');
            }

            //Returning "false" we tell to the dispatcher to stop the current operation
        return false;
        }

i have another question, regarding notfound Cntroller i have mentioned it as a public resource,

 $publicResources = array(
        'session' => array('index', 'start', 'end'),
        'notfound' => array('route404')
    );

but still i cannot get 404 error page whatever incorrect URL i use.

What;s the proper way of ACL working with Notfound??



14.9k

I tried:

if($allowed != Phalcon\Acl::ALLOW) 
        {
            if($role == 'Users' && $controller == 'users')
            {
                $this->flashSession->warning("You don't have access to admin area");

            }
            else if($controller == 'NotFound')
            {
                $this->dispatcher->forward(array(
                    'controller' => 'NotFound',
                    'action' => 'route404'
                ));
            }
            else
            {
                    //If guests don't have access forward them to the session/start controller
                $this->flashSession->warning("You are not logged in!");
                $this->response->redirect('');
            }

            //Returning "false" we tell to the dispatcher to stop the current operation
        return false;
        }

But it giver an error:

Dispatcher has detected a cyclic routing causing stability problems

Please advise!



16.0k
Accepted
answer

Hello,

I would advise you to drop the not found from acl and register it in dispatcher as a event beforeException. What you did, if the page is not found it will redirect to not found which results in an infinite loop.

This is my approach and works wonderful for me

public function beforeException($event, $dispatcher, $exception) {
    if ($exception instanceof Exception) {
        $dispatcher->forward(
            [
                'module' => 'core',
                'namespace' => 'Core\Controllers',
                'controller' => 'error',
                'action' => 'show404'
            ]
        );
        return false;
    }
    // Handle other exceptions.
    $dispatcher->forward(
        [
            'module' => 'core',
            'namespace' => 'Core\Controllers',
            'controller' => 'error',
            'action' => 'show503'
        ]
    );
    return $event->isStopped();
}

https://github.com/magnxpyr/cms/blob/master/app/engine/Plugins/ErrorHandler.php

And then in bootstrap

    //Obtain the standard eventsManager from the DI
    $eventsManager = new \Phalcon\Events\Manager();

    //Registering a dispatcher
    $dispatcher = new \Phalcon\Mvc\Dispatcher();

    // Attach the Security plugin
    $eventsManager->attach('dispatch', new \Engine\Plugins\Security());
    // Attach the Error handler
    $eventsManager->attach('dispatch', new \Engine\Plugins\ErrorHandler());

    //Bind the EventsManager to the Dispatcher
    $dispatcher->setEventsManager($eventsManager);

    $di->set('dispatcher', $dispatcher);


14.9k

wow, it just works perfect!! Thanx Stefan!!!

However, since i denied access to all events except public Resources, i can't drop NotFound from acl completely. Will leave it in public resources