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 and routes: I'm a bit confused.

Hello everyone. This is my first post and question about my first project in PhalconPHP. So be nice and gentle with me :)

I've begun to make an MVC structured project. It's going to have a log in and the app will be merging CSV files of different formats into a database. Nothing too taxing. I have reached the point where I have now created a Security() plugin based on an example written in "Getting Started with Phalcon" by Stephan A. Miller (which has been very helpful in .. well getting started). It's all working to this point apart from one thing that has me confused and I'm presuming I've just hit a gap in knowledge here. So I am hoping that I can get an answer. I apologise if I am repeating something that's already been posted but my searches were unable to find anything.

In my routes.php file I have:

<?php
/**
 * Routes File
 */
    $di->set('router', function () {
        $router = new \Phalcon\Mvc\Router();
        $router->add("/", [
              'controller' => 'pages',
              'action' => 'index',
          ]);
        $router->notFound(['controller' => 'pages', 'action'=> 'index']);
        return $router;
    });

I intend to have a general info landing page here along with other useful information pages withing a pages controller. Before I implimented the ACL, https://MyProject.dev/ would render /pages/index/ fine. After ACL implimentation I can still navigate to /pages/index/ and see the page but navigating to root/ shows the log in page and my message saying I don't have permission. All other urls private and public work fine as they should because I've not started to add more routes.

Here is my Security.php file (which I plan on refactoring later to cut down the length of it, this is more from the book than anything but I have modified it slightly).

<?php

use Phalcon\Events\Event,
    Phalcon\Mvc\User\Plugin,
    Phalcon\Mvc\Dispatcher,
    Phalcon\Acl;

class Security extends Plugin {
    public function __construct($dependencyInjector) {
        $this->_dependencyInjector = $dependencyInjector;
    }

    public function getAcl() 
    {
        if(!isset($this->persistent->acl)) 
        {
            $acl = new Phalcon\Acl\Adapter\Memory();
            $acl->setDefaultAction(Phalcon\Acl::DENY);

            $roles = [
                'users' => new Phalcon\Acl\Role('Users'),
                //'admin' => new Phalcon\Acl\Role('Admin'), // comming back to this in a refactor
                'guests' => new Phalcon\Acl\Role('Guests')
            ];

            foreach ($roles as $role) 
            {
                $acl->addRole($role);
            }

            $private = [
                'dashboard' => ['index']
            ];

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

            $public = [
                'users' => ['*'], // will be moving to private
                'pages' => ['*']
            ];

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

            foreach ($roles as $role) 
            {
                foreach ($public as $resource => $actions) 
                {
                    foreach ($actions as $action) 
                    {
                        $acl->allow($role->getName(), $resource, $action);
                    }
                }
            }

            foreach ($private as $resource => $actions) 
            {
                foreach($actions as $action) 
                {
                    $acl->allow('Users', $resource, $action);
                }
            }

            $this->persistent->acl = $acl;
        }

        return $this->persistent->acl;
    }

    public function beforeDispatch(Event $event, Dispatcher $dispatcher) 
    {
        $user = $this->session->get('user_id');

        if(empty($user)) 
        {
            $role = "Guests";
        } else {
            $role = 'Users';
        }

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

        $user_is_allowed = ($acl->isAllowed($role, $controller, $action) == Acl::ALLOW ? true : false);

        if(!$user_is_allowed)
        {
            $this->flash->error("Please Log in to access this area.");
            $this->dispatcher->forward(['controller' => 'pages', 'action' => 'login']);
            return false;
        }
    }
}

$user_is_allowed is coming back as false for https://MyProject.dev/ and therefore it fires $this->dispatcher->forward(['controller' => 'pages', 'action' => 'login']); which is not what I want. Please help. Thanks. xx

Hi, it is bit hard for me to understand which controller/actions are not working out the way you want, but i see that you dont have to use this line :

$user_is_allowed = ($acl->isAllowed($role, $controller, $action) == Acl::ALLOW ? true : false);

this will do:

$user_is_allowed = $acl->isAllowed($role, $controller, $action);

since isAllowed already returns boolean value.

maybe this -> https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Acl/Factory component can help you with setting up ACL easier ... for sample configuration be sure to check out https://github.com/phalcon/incubator/blob/master/tests/Phalcon/Acl/Factory/\_fixtures/acl.ini



10.9k
edited Apr '14

$user_is_allowed = ($acl->isAllowed($role, $controller, $action) == Acl::ALLOW ? true : false); is just me playing around trying to force things so just ignore that I have left it there by accident. My problem isn't that controllers and actions are not working as expected, they are. It's that their routes are not. I have mapped '/' to '/pages/index/' in my code. Navigating to '/pages/index/' works fine bit navigating to '/' is denied and I am shown my log in page. I want '/' to display '/pages/index' but my ACL is preventing it from happening; unless I log in of course.

EDIT --

I have just discovered that I am being denied for all my routes by ACL regardless of whether I have logged in or not.



10.9k

That link you posted looks ace! Thanks!



10.9k

I suppose I could make a workaround by creating an indexController.php and placing the page I want within the indexAction() of course but all routes are just not working. I just tried creating the route '/home' but I can't access it whether I'm logged in or not.



10.9k

No I hadn't but I have now and no joy still.

hmmmm ... im out of ideas ... if you have time you might visit IRC channel for some more detailed debugging



6.9k
Accepted
answer

in

            $public = [
                'users' => ['*'], // will be moving to private
                'pages' => ['*']
            ];

You use wildcard to define all pages, but you need to actually define what those resources are. Wildcard will only work for assigning resources to a role, take the example from invo acl:

//Public area resources
            $publicResources = array(
                'index' => array('index'),
                'about' => array('index'),
                'session' => array('index', 'register', 'start', 'end'),
                'contact' => array('index', 'send')
            );
                        //Grant access to public areas to both users and guests
            foreach ($roles as $role) {
                foreach ($publicResources as $resource => $actions) {
                    $acl->allow($role->getName(), $resource, '*');
                }
            }

So in your case it should work if you just define your index controller as a public route

            $public = [
                `index` => `index`,
                'users' => ['*'], // this will not work
                'pages' => ['*'] //this will not work
            ];

to debug quicker it might be helpful if you use

var_dump($acl_object)

to actually see what the acl consists of



10.9k

So in the end I used the Invo security class and arranged my public and private variables as you said. It also turned out that my routes were not working at all which was compounding the problem also. I needed to do a quick $this->session->destroy(); to get everything to work normally. I seem to be out of the woods now with this one. Thanks for the help guys :) I'll probably be back in ten minutes with something else; steep learning curve this one.



10.9k
edited Jun '14

@digitronac I used that https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Acl/Factory (with an ini file) you showed me a few days a go and I'm much happier with it. Thanks for that.