Tying the ACL to Controllers/Action in Zend Framework 1.5

Posted in Development, PHP, Zend Framework by Mark on the April 13th, 2008

I have been using the PHP application framework 'Zend Framework' a lot lately. The 1.5 release in particular is very feature packed.

Zend Framework has built in classes for creating Access Control Lists, or ACL's. These ACL's allow for roles (users / groups) and resources (any resource a role may want to access). They also have the concept of privileges, which are what action you want to take on the resource (e.g. add / delete / edit etc).

If you are making an MVC application your first thought is probably like mine. How can we make the ACL automatically apply to controllers and actions? The answer I have found is by writing a custom Controller Plugin. By using a Controller Plugin, we can automatically apply our ACL without having to add any ACL related code in our actual Controllers.

This allows us to do the following

  1. Assign our users to arbitrary groups
  2. Allow groups to access only specific Controller and Action combinations, e.g. /news/add
  3. Do all of this seamlessly. No code is needed in each controller class. Only one line is added to the boot strap

Here is my plugin class file.

PHP:
<?php

class Lsdev_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
{
    private function getAcl()
    {
        $acl = new Zend_Acl();
       
        // Define our possible user groups including who they inherit from
        $acl->addRole(new Zend_Acl_Role('guest'));
        $acl->addRole(new Zend_Acl_Role('customer'), array('guest'));
        $acl->addRole(new Zend_Acl_Role('staff'), array('guest'));
        $acl->addRole(new Zend_Acl_Role('super'), array('staff'));
        $acl->addRole(new Zend_Acl_Role('admin'));

        // Define our controllers as resources.
        $acl->add(new Zend_Acl_Resource('index'));    // IndexController

        // Define our group access rules. Syntax is $acl->allow('User Group', 'Controller', 'Action');
        $acl->allow('guest', 'index', 'index');
        $acl->allow('customer', 'index', 'add');
        $acl->allow('staff', 'index', 'edit');
        $acl->allow('super', 'index', 'delete');
        // The admin group is special. It can always access anything
        $acl->allow('admin');

        return $acl;
    }

    // Check to see if a user can access the Controller and Action they are trying to
    private function check()
    {
        $acl = $this->getAcl();
        $auth = Zend_Auth::getInstance();
        if ($auth->hasIdentity()) {
            $user = $auth->getIdentity();
            $acl->addRole(new Zend_Acl_Role($user->username, array($user->group)));
        } else {
            $user = new ArrayObject();
            $user->group = 'guest';
        }
       
        $request = $this->getRequest()->getParams();

        try {
            return $acl->isAllowed($user->group, $request['controller'], $request['action']);
        } catch (Zend_Acl_Exception $e) {
            return false;
        }
    }

    // Run this class during Zend_Controller_Front's routeShutdown event
    public function routeShutdown(Zend_Controller_Request_Abstract $request)
    {
        $allowed = $this->check();
        // Here is where you would redirect to an 'unauthorised' page. For now just display a message
        if ($allowed) $msg = "you are authorised";
        else $msg = "you are not authorised. Bad bad!";
        $this->getResponse()->appendBody($msg);
    }
}

To use it, all I have to do is add the following line to my boot strapper.

PHP:
Zend_Controller_Front::getInstance()->registerPlugin(new Lsdev_Controller_Plugin_Acl());

Obviously I also had to previously set up my Zend_Auth controllers. I have done this by using a database table called 'users'.

SQL:
CREATE TABLE `users` (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `username` char(32) NOT NULL,
  `password` char(32) NOT NULL,
  `group` varchar(20) NOT NULL,
  `fullname` char(32) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `username` (`username`)
);

You will notice the 'group' column. This directly applies to the ACL and can be 'guest', 'customer', 'staff', 'super', or 'admin'. If a user is logged in, that user is added to the ACL under the group that they belong to.

This is just a mock-up I put together yesterday as a proof of concept. I'm very happy with it though. The next thing I'm working on is a way to do the following.

  1. Define the ACL in the database, including caching the result for performance.
  2. Make a tool that will automatically populate the ACL database based on the currently defined controllers / actions. It should be able to also update the current list, defaulting access to false for any new controller / action.
  3. Make an admin panel to administrate the ACL.

Basically the getAcl() function above is just a place-holder for something a lot better, but at the moment it fully works.

3 Responses to 'Tying the ACL to Controllers/Action in Zend Framework 1.5'

Subscribe to comments with RSS or TrackBack to 'Tying the ACL to Controllers/Action in Zend Framework 1.5'.

  1. aqls said,

    on September 24th, 2008 at 1:09 am

    brilliant!

  2. Hari K T said,

    on August 24th, 2009 at 1:36 pm

    Pretty cool man . Logic is helpful to newbie .

  3. teleskopy said,

    on December 30th, 2009 at 1:40 am

    Very nice blog, your article is interesting, i have bookmarked it for future referrence

Leave a Reply