Using the Zend Framework ACL Library in Codeigniter
To kick my blog off good and proper I thought a tutorial on implementing the Zend Framework ACL library into a Code Igniter application would be a nice start.
In this post I will explain how to build simple access control functionality for your Code Igniter application using the Zend Framework ACL library. To follow this tutorial you will need a working installation of Code Igniter and a database. You will also need to download the Zend Framework.
Code Igniter and Access Control Lists
A lot of Code Igniter users have complained that the framework doesn’t come bundled with an access control component unlike other heavyweight frameworks such as Symfony and CakePHP. Personally I don’t see this as a major issue as the developers behind Code Igniter have deliberately kept it lean with unnecessary libraries being left out in favour of a solid and very usable core and not every web app needs access control.
In my current job access control was an essential requirement and, being the sole person responsible for choosing code igniter over other frameworks, it’s down to me to implement some form of access control into our app.
I started by looking at rolling my own but decided that instead of reinventing the wheel it would be far better to go with a tried and trusted solution hence my reason for opting for the Zend Framework ACL implementation.
Zend ACL
The Zend ACL implementation provides a flexible approach to Access Control Lists with a simple querying interface supplying either true or false. It’s up to the developer to decide how to implement their permissions but I’ll give an example based on the application (a CMS) I’m currently developing.
Getting Started
To start with I’ll link to the blog post that got me started… A very good primer for getting your ACL up and running using the Zend Framework and a database can be found at Jani’s blog it helped me no end and formed the basis for this write up. Now on to the nitty gritty…
Download the Zend framework and unzip it to your code igniter install in /system/libraries. You can leave the whole framework there if you want but if it’s just the ACL stuff you need you can remove everything except Acl.php, Exception.php and the Acl folder.
After that create a file called Acl.php in /system/application/libraries, open it up in your favourite editor and pop this code in it…
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); require_once BASEPATH .'libraries/Zend/Acl.php'; class Acl extends Zend_Acl { function __construct() { $CI = &get_instance(); $this->acl = new Zend_Acl(); $CI->db->order_by('ParentId', 'ASC'); //Get the roles $query = $CI->db->get('user_roles'); $roles = $query->result(); $CI->db->order_by('parentId', 'ASC'); //Get the resources $query = $CI->db->get('user_resources'); $resources = $query->result(); $query = $CI->db->get('user_permissions'); //Get the permissions $permissions = $query->result(); foreach ($roles as $roles) { //Add the roles to the ACL $role = new Zend_Acl_Role($roles->id); $roles->parentId != null ? $this->acl->addRole($role,$roles->parentId): $this->acl->addRole($role); } foreach($resources as $resources) { //Add the resources to the ACL $resource = new Zend_Acl_Resource($resources->id); $resources->parentId != null ? $this->acl->add($resource, $resources->parentId): $this->acl->add($resource); } foreach($permissions as $perms) { //Add the permissions to the ACL $perms->read == '1' ? $this->acl->allow($perms->role, $perms->resource, 'read') : $this->acl->deny($perms->role, $perms->resource, 'read'); $perms->write == '1' ? $this->acl->allow($perms->role, $perms->resource, 'write') : $this->acl->deny($perms->role, $perms->resource, 'write'); $perms->modify == '1' ? $this->acl->allow($perms->role, $perms->resource, 'modify') : $this->acl->deny($perms->role, $perms->resource, 'modify'); $perms->publish == '1' ? $this->acl->allow($perms->role, $perms->resource, 'publish') : $this->acl->deny($perms->role, $perms->resource, 'publish'); $perms->delete == '1' ? $this->acl->allow($perms->role, $perms->resource, 'delete') : $this->acl->deny($perms->role, $perms->resource, 'delete'); } $this->acl->allow('3'); //Change this to whatever id your adminstrators group is } /* * Methods to query the ACL. */ function can_read($role, $resource) { return $this->acl->isAllowed($role, $resource, 'read')? TRUE : FALSE; } function can_write($role, $resource) { return $this->acl->isAllowed($role, $resource, 'write')? TRUE : FALSE; } function can_modify($role, $resource) { return $this->acl->isAllowed($role, $resource, 'modify')? TRUE : FALSE; } function can_delete($role, $resource) { return $this->acl->isAllowed($role, $resource, 'delete')? TRUE : FALSE; } function can_publish($role, $resource) { return $this->acl->isAllowed($role, $resource, 'publish')? TRUE : FALSE; } } |
IMPORTANT! The final line in the constructor allows all permissions for the role with an ID of 3. This is purely for demonstration purposes. I’m assuming you’ll create an admin role in your database before deploying your app so make sure this ID corresponds with your admin role.
The Database Tables
Next we need to create the database tables we’ll be using for the Acl. For that we only need 3 tables, user_permissions, user_resources and user_roles. The sql for those tables is as follows…
CREATE TABLE `user_resources` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(255) , `description` VARCHAR(255) , `parentId` INT DEFAULT NULL , PRIMARY KEY (`id`) ); CREATE TABLE `user_roles` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(255) NOT NULL , `description` VARCHAR(255) , `parentId` INT DEFAULT NULL , PRIMARY KEY (`id`) ); CREATE TABLE `user_permissions` ( `id` INT NOT NULL AUTO_INCREMENT , `role` INT , `resource` INT , `read` BOOLEAN DEFAULT FALSE , `write` BOOLEAN DEFAULT FALSE , `modify` BOOLEAN DEFAULT FALSE , `delete` BOOLEAN DEFAULT FALSE , `publish` BOOLEAN DEFAULT FALSE , `description` VARCHAR(255) , PRIMARY KEY (`id`) ); |
The user_resources table holds the data for the resources we want to protect through the ACL. Typically this will be admin controllers for any area of your site and perhaps restricted areas of your site such as user profiles or forums. Both the description and parentId fields are optional, the description is there purely for an admin backend to the ACL, the parentId allows you to inherit permissions from a resource, for example you could have separate CMS and media library controllers and you would like the media library to inherit permissions from the CMS so for the media library you would insert the id of the CMS resource into the parent field for the media library.
The user_roles table works in the same way as the resources table. Roles can inherit from other roles allowing you to build permissions with a minimal number of groups. For example you could have a CMS users role which has all permissions except for publish and delete, you could then create a CMS editors role which inherits from the CMS users role but adds extra permissions for deleting and publishing content.
The final table, user_permisssions, is what builds the permissions in the acl. If needs be you can add as many boolean columns for whatever permissions your application requires, just remember to add a method in your ACL library for querying that permission! The description field is, like the other tables, optional but for the purposes of an admin backend you’ll probably want to use it!
We can now start by populating our tables…
INSERT INTO `user_resources` (`id`, `name`, `description`, `parentId`) VALUES (1, 'test', 'Acl Test Controller', NULLL) INSERT INTO `user_roles` (`id`, `name`, `description`, `parentId`) VALUES (1, 'test', 'Acl Test Role', NULLL) INSERT INTO `user_permissions` (`id` ,`role` ,`resource` ,`read` ,`write` ,`modify` ,`delete` ,`publish`) VALUES (NULL , '1', '1', '1', '1', '0', '0', '0'); |
Testing the ACL
We’re now ready to test out the ACL. Create a controller test.php in /system/application/controllers with the following code.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Test extends Controller { function Test() { parent::Controller() $this->load->library('Acl'); define('ROLE', '1'); define('RESOURCE', '1'); } function index() { if (!$this->acl->can_read(ROLE, RESOURCE)) { die("You do not have permissions to read this resource"); } echo "You have permission to read this resource!"; } function write() { if (!$this->acl->can_write(ROLE, RESOURCE)) { die("You do not have permissions to write to this resource"); } echo "You have permission to write to this resource!"; } function modify() { if (!$this->acl->can_modify(ROLE, RESOURCE)) { die("You do not have permissions to modify this resource"); } echo "You have permission to modify this resource!"; } function delete() { if (!$this->acl->can_delete(ROLE, RESOURCE)) { die("You do not have permissions to delete this resource"); } echo "You have permission to delete this resource!"; } function publish() { if (!$this->acl->can_publish(ROLE, RESOURCE)) { die("You do not have permissions to publish this resource"); } echo "You have permission to publish this resource!"; } } |
For the purposes of this article I’m going to assume your working on your local machine, so browse to http://localhost/test. You should see the message “You have permission to read this resource”. Next try http://localhost/test/delete. You should see the message “You do not have permission to delete this resource”. Now go and try it on all the methods in your test controller.
Congratulations! You’ve successfully got Code Igniter and Zend ACL to play nicely together.
Note: If you get error messages about required files not being found in the include path you may have to modify the include paths in your Zend framework files. Open them up and, wherever you find an include or require, modify it to…
BASEPATH.'/libraries/REST OF THE FILE PATH'; |
Summary
While the above article explains how to get access control working in code igniter it doesn’t provide a model for obtaining roles and resources, the above example having the role and resource id hardcoded into the controller. This is an exercise I feel that is best left to the developer. Personally I use the excellent modular extensions for organising my application. I then have a separate module model which has a method get_resource_id which allows me to define the resource.
The users id is held in their session and I have a users model which, again, has to method to define their role from their session.
It’s also worth noting that if you try and check the ACL without either the role or resource being valid you’ll get an exception error so whichever way you decide to implement this it’s best to write some basic checks before querying the ACL.
The End
Ok, I hope some people have found this useful, let the comments commence!
Thank you for listening.


Comments(28)