ACL not working in Phalcon

i was following the tutorial by Jesse Boyer, but I am stuck on the ACL part of teh tutorial, which seems not to be working for some reason, when adding more public areas like 'posts', 'forum' etc. instead of just 'index' and 'signin'.

Background info:

I am running Windows 7 64-bit and WAMP SERVER 2.4 (32-bit) with PHP 5.4.16 and Apache is 2.4.4, if it helps. It's a strange combination, but the 64-bit versions of WAMP didn't work on my 64-bit machine for some reason.

Phalcon Windows dll version is 1.3.2 x86 TS ( the same problem occurs with 1.3.1. version as well )

I have this in my Permission.php file located in config/Permission.php

<?php

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

/**
 * Permission
 *
 * Prevents User Types from accessing areas they are not allowed in.
 */
class Permission extends \Phalcon\Mvc\User\Plugin {

    /**
     * Constants to prevent a typo
     */
    const GUEST = 'guest';
    const USER  = 'user';
    const ADMIN = 'admin';

    /**
     * Accessible to everyone
     * @var array
     */
    protected $_publicResources = [
        'index'  => ['*'],
        'signin' => ['*'],
        'posts'  => ['*'],
    ];

    /**
     * Accessible to Users (and Admins)
     * @var array
     */
    protected $_userResources = [
        'dashboard' => ['*']
    ];

    /**
     * Accessible to Admins
     * @var array
     */
    protected $_adminResources = [
        'admin' => ['*']
    ];


    // ------------------------------------------------------------------------

    /**
     * Triggers before a route is successfully executed
     *
     * @param  Event      $event
     * @param  Dispatcher $dispatcher
     *
     * @return boolean|void
     */

    public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher)
    {

        // Debug
        // $this->session->destroy();

        // Get the current role
        $role = $this->session->get('role');
        if (!$role) {
            $role = self::GUEST;
        }

        // Get the current Controller/Action from the dispatcher
        $controller = $dispatcher->getControllerName();
        $action     = $dispatcher->getActionName();

        // Get the ACL Rule List
        $acl = $this->_getAcl();

        // See if they have permission
        $allowed = $acl->isAllowed($role, $controller, $action);

        if ($allowed != Acl::ALLOW)
        {
            $this->flash->error('You do not have permission to access this area');
            $this->response->redirect('');

            // Stop the dispatcher at the current operation
            $this->view->disable();
            return false;
        }
    }

    // ------------------------------------------------------------------------

    /**
     * Build the Session ACL list one time if it's not set
     *
     * @return object
     */
    protected function _getACL()
    {
        if (!isset($this->persistent->acl))
        {
            $acl = new Acl\Adapter\Memory();
            $acl->setDefaultAction(Acl::DENY);

            $roles = [
                self::GUEST => new Acl\Role(self::GUEST),
                self::USER  => new Acl\Role(self::USER),
                self::ADMIN => new Acl\Role(self::ADMIN),
            ];

            // Place all the roles inside the ACL Object
            foreach ($roles as $role) {
                $acl->addRole($role);
            }

            // Public Resources
            foreach ($this->_publicResources as $resource => $action) {
                $acl->addResource(new Acl\Resource($resource), $action);
            }

            // User Resources
            foreach ($this->_userResources as $resource => $action) {
                $acl->addResource(new Acl\Resource($resource), $action);
            }

            // Admin Resources
            foreach ($this->_adminResources as $resource => $action) {
                $acl->addResource(new Acl\Resource($resource), $action);
            }

            // Allow ALL Roles to access the Public Resources
            foreach ($roles as $role) {
                foreach($this->_publicResources as $resource => $action) {
                    $acl->allow($role->getName(), $resource, '*');
                }
            }

            // Allow User & Admin to access the User Resources
            foreach ($this->_userResources as $resource => $actions) {
                foreach ($actions as $action) {
                    $acl->allow(self::USER, $resource, $action);
                    $acl->allow(self::ADMIN, $resource, $action);
                }
            }

            // Allow Admin to access the Admin Resources
            foreach ($this->_adminResources as $resource => $actions) {
                foreach ($actions as $action) {
                    $acl->allow(self::ADMIN, $resource, $action);
                }
            }

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

        return $this->persistent->acl;
    }

    // ------------------------------------------------------------------------

}

then I got this in my PostsController.php:

<?php

use \Phalcon\Tag;

class PostsController extends BaseController {

    public function onConstruct()
    {
        parent::initialize();
    }


    public function indexAction()
    {
        Tag::setTitle('Posts');
    }

}

and this in my index.php in the public root (bootstrap file):

<?php

require '../app/config/Config.php';

try {

    //Register an autoloader
    $loader = new \Phalcon\Loader();
    $loader->registerDirs([
        '../app/controllers/',
        '../app/models/',
        '../app/config/'
    ]);

    $loader->registerClasses([
        'Component\User'     => '../app/components/User.php',
        'Component\Helper'   => '../app/components/Helper.php',
        'Component\Security' => '../app/components/Security.php',
    ]);

    $loader->register();

    // Create a DI
    $di = new Phalcon\DI\FactoryDefault();

    // Return general config
    $di->setShared('config', function() use ($config) {
        return $config;
    });

    // Return API config
    $di->setShared('api', function() use ($api) {
        return $api;
    });

    // Return custom components
    $di->setShared('component', function() {
        $obj = new stdClass();
        $obj->helper = new \Component\Helper;
        $obj->user   = new \Component\User;
        return $obj;
    });

    // Database
    $di->set('db', function() use ($di) {
        $dbConfig = $di->get('config')->get('db')->toArray();
        $db = new \Phalcon\Db\Adapter\Pdo\Mysql($dbConfig);
        return $db;
    });

    // View
    // Register Volt as a service
    $di->set('voltService', function($view, $di) {
        $volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
        $volt->setOptions(array(
            "compiledPath"      => "../app/compiled-templates/",
            "compiledExtension" => ".compiled",
            "compileAlways"     => true
        ));
        return $volt;
    });

    // Register Volt as template engine
    $di->set('view', function() {
        $view = new \Phalcon\Mvc\View();
        $view->setViewsDir('../app/views/');
        $view->registerEngines(array(
            ".volt" => 'voltService'
        ));
        return $view;
    });

    // Router
    $di->set('router', function() {
        $router = new \Phalcon\Mvc\Router();
        $router->mount(new Routes());
        return $router;
    });

    // Session
    $di->setShared('session', function() {
        $session = new \Phalcon\Session\Adapter\Files();
        $session->start();
        return $session;
    });

    // Flash Data (Temporary Data)
    $di->set('flash', function() {
        $flash = new \Phalcon\Flash\Session([
            'error'   => 'alert alert-danger',
            'success' => 'alert alert-success',
            'notice'  => 'alert alert-info',
            'warning' => 'alert alert-warning',
        ]);
        return $flash;
    });

    // Base URI
    $di->set('url', function(){
        $url = new \Phalcon\Mvc\Url();
        $url->setBaseUri('/test/');
        return $url;
    });

    // Custom dispatcher including ACL (Overrides the default)
    $di->set('dispatcher', function() use ($di) {

        $eventsManager = $di->getShared('eventsManager');

        // Custom ACL Class
        $permission = new Permission();

        // Listen for events from the permission class

        $eventsManager->attach('dispatch', $permission);

        $dispatcher = new \Phalcon\Mvc\Dispatcher();
        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;

    });

    // Security - temporary b/c security->hash bug in 1.3.2
    $di->set('security', function(){
        $security = new \Component\Security();
        //Set the password hashing factor to 12 rounds
        $security->setWorkFactor(12);
        return $security;
    }, true);

    // Deploy the App
    $application = new \Phalcon\Mvc\Application($di);
    echo $application->handle()->getContent();

} catch(\Phalcon\Exception $e) {
     // !!! comment in production !!!
     echo "PhalconException: ", $e->getMessage();
}

And I can only access: http://localhost/test/index and http://localhost/test/signin but I am NOT able to access http://localhost/test/posts. When I hit http://localhost/test/posts I am automaticallty redirected to the homepage and the message You do not have permission to access this areapops out.

So, I think the problem is probably in the Permission.php file. Any idea what is wrong there?

Another thing that I don't understand is that even if I comment out the signin from the resources like:

 /**
     * Accessible to everyone
     * @var array
     */
    protected $_publicResources = [
        'index'  => ['*'],
        // 'signin' => ['*'],
        // 'posts'  => ['*'],
    ];

I can still access http://localhost/test/signin . How is it possible?

Could this part:

// Allow ALL Roles to access the Public Resources
            foreach ($roles as $role) {
                foreach($this->_publicResources as $resource => $action) {
                    $acl->allow($role->getName(), $resource, '*');
                }
            }

be causing the problem?



23.4k

if I comment out this:

if ($allowed != Acl::ALLOW)
        {
            // $this->flash->error('You do not have permission to access this area');
            // $this->response->redirect('');
            return false;
        }

Then I can access posts.

So, for some reason $allowed is not set to allow, why? Any idea what is wrong with the code?



23.4k
edited Jul '14

This code:

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

        print_r($allowed);
        die;

        if ($allowed != Acl::ALLOW)
        {
            $this->flash->error('You do not have permission to access this area');
            $this->response->redirect('');
            return false;
        }

Gives me 1 on this URIs:

http://localhost/test
http://localhost/test/signin

But nothing (blank page) on posts:

http://localhost/test/posts

Why is there just a blank white page? Not even 0 for http://localhost/test/posts?



23.4k
edited Jul '14

btw. I have noticed that the output of:

$acl is always the same, even if I change the resources like:

protected $_userResources = [
        'blabalbalbababab' => ['*']
    ];

I still see 'dashboard' in print_r($acl) when typing:

// Get the current Controller/Action from the dispatcher
        $controller = $dispatcher->getControllerName();
        $action     = $dispatcher->getActionName();

        // Get the ACL Rule List
        $acl = $this->_getAcl();

        print_r($acl);
        die;

        // See if they have permission
        $allowed = $acl->isAllowed($role, $controller, $action);

Could it be some problem with Phalcon\Acl\Adapter\Memory in Phalcon 1.3.2 x86 on Windows ?

If so, how can I refresh taht to reflect the current values? Or is it something different?



23.4k
edited Jul '14

object(Phalcon\Acl\Adapter\Memory) is always the same. Like it's cached or soemthing. Why is that? I tried to restart the WAMP server, but it's always the same ;(. Doesn't reflect the new added or changed resources for user or guest or admin. ;(



23.4k

I tried v1.3.1 but with the same results ;(.

Btw. I am running Windows 7 64-bit and WAMP SERVER 2.4 (32-bit) with PHP 5.4.16 and Apache is 2.4.4, if it helps. It's a strange combination, but the 64-bit versions of WAMP didn't work on my 64-bit machine for some reason.



2.6k

I have the same problem.

Is there any news about it?

me too have the same problem

edited Feb '15

the first solution that comes to my mind is to unset the persistent variable so there's no "cache" :

> > public function getAcl() > > { > > unset($this->persistent->acl); /* HERE */ > > if (!isset($this->persistent->acl)) {

    public function getAcl()
    {

        // invalidate the cache.  Without destroying the persistent storage, changes to acl won't be detected.      
        $this->persistent->destroy();
        if (!isset($this->persistent->acl)) {

You can try: $this->persistent->destroy();. For some reason unsetting persistent->acl just doesn't work for me.

You answer is great. It works!! Appreciated

>the first solution that comes to my mind is to unset the persistent variable so there's no "cache" : > >> >> public function getAcl() >> >> { >> >> unset($this->persistent->acl); /* HERE */ >> >> if (!isset($this->persistent->acl)) {