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)
Informative and useful, thanks.
Thank you very much.
A PHP Error was encountered
Severity: Warning
Message: require_once(Zend/Acl/Resource/Interface.php) [function.require-once]: failed to open stream: No such file or directory
Filename: Zend/Acl.php
Line Number: 26
I try to use function set_include_path point to BASEPATH .’libraries/ but still have this error
May be need something link Zend_Loader()
You shouldn’t need Zend_Loader(), i’ve had no need to use it.
Have you tried setting the include path in an .htaccess file?
Create a text file called .htaccess in your code igniter root (where you find the index.php and license.txt files) and add the following:
php_value include_path “/PATH/TO/CODE IGNITER/LIBRARIES”
For example, I use xampp and windows XP for my development machine so I would add:
php_value_include_path “C:\xampp\htdocs\system\libraries\”
Try that and let me know how you get on.
Cheers
Matt
maybe 9px font size in the comments is a little small
@matt: No need to set the include path through the .htaccess.
<?php
set_include_path(‘/path/to/code/igniter/libraries/’ . PATH_SEPARATOR . get_include_path());
And true, the Zend_Loader is not needed. Any autoload function that conforms to the underscore namespace separator plus each namespace as a folder will work. In fact you could easily use the function below:
<?php
function _my_autoload($classname)
{
$classfile = LIBRARY_BASE_DIRECTORY . str_replace(‘_’, ‘/’, $classname) . ‘.php’;
if( file_exists($classfile) ) include $classfile;
}
class Acl extends Zend_Acl {
….
$this->acl = new Zend_Acl();
}
WTF ?!?
Muito obrigado, por compartilhar seu conhecimento conosco!!!
Tudo de bom!!!
Valew
You should try to not get so excited while you code. You got $perms everywhere.
I install Acl but occur error :
A PHP Error was encountered
Severity: Notice
Message: Undefined property: Test::$db
Filename: libraries/Acl.php
Line Number: 9
Fatal error: Call to a member function order_by() on a non-object in E:\Projects\work\CodeIgniter171\system\application\libraries\Acl.php on line 9
———————————————————
This is error line:
9 – $CI->db->order_by(‘ParentId’, ‘ASC’); //Get the roles
Please help me
Many thanks
I fixed the db bug, because i didnt autoload database in config file yet
Parse error: syntax error, unexpected T_VARIABLE in C:\wamp\www\test_ci_connect\trunk\system\application\controllers\test.php on line 5
getting this error!! plz help
[...] Using the Zend Framework ACL … (tags: zend codeigniter webdev acl zend_acl zendframework) [...]
Excellent article, just what I was looking for.
i dont know what you have write in your articale i just know that im new user to codeigniter and when every im trying to get some data from database im having an error and i dont know how to solve it. it almost take 3 days but unable to solve it… please help me and please reply to my email.
the error is given bilow.
Fatal error: Call to a member function get() on a non-object in C:\wamp\www\codeigniter\system\application\controllers\blog.php on line 18
thanks in advance
I want to find good pop music. Help me please.
Thanks, this helped a lot. I was able to successfully get my ACL working.
I am very interested in this topic.
“Creating a custom Membership”
I am completely new to ASP.
its correct
Regards – thanks in advance
Hi am newbie to zend framework.. i just included the coding.. i am not getting any error but just getting the blank page.. can any1 tell me where i have gone wrong.. can give the complete source code??
Fatal error: Uncaught exception ‘Zend_Acl_Role_Registry_Exception’ with message ‘Parent Role id ’0′ does not exist’ in /Library/WebServer/chapeldesign/marques/system/libraries/Zend/Acl/Role/Registry.php:94 Stack trace: #0 /Library/WebServer/chapeldesign/marques/system/libraries/Zend/Acl.php(142): Zend_Acl_Role_Registry->add(Object(Zend_Acl_Role), ’0′) #1 /Library/WebServer/chapeldesign/marques/application/modules/admin/libraries/Acl.php(39): Zend_Acl->addRole(Object(Zend_Acl_Role), ’0′) #2 /Library/WebServer/chapeldesign/marques/application/libraries/Loader.php(635): Acl->__construct() #3 /Library/WebServer/chapeldesign/marques/application/libraries/Loader.php(540): CI_Loader->_ci_init_class(‘Acl’, ”, NULL, NULL, false) #4 /Library/WebServer/chapeldesign/marques/application/libraries/Loader.php(258): CI_Loader->_ci_load_class(‘Acl’, NULL, NULL, false) #5 /Library/WebServer/chapeldesign/marques/application/libraries/Loader.php(764) : eval()’d code(999): CI_Loader->library(‘Acl’) #6 /Library/WebServer/chapeldesign/marques/ in /Library/WebServer/chapeldesign/marques/system/libraries/Zend/Acl/Role/Registry.php on line 94
Seems odd?
Ah found it
parentId not null when imported into the DB
Nice Blogs
-Thanks-
really nice and useful article!
I read a little the comments and i feel to remember the following (i remember ‘cuse i find this is a very exellent work dear Matt!)
if you find any troubles about classes not found: don’t forget to set the right PATH in every require statament (i guess someone miss require statament, which is not only before classes declaration!!!!)
don’t forget to load CI databases!!! for a fast test add it as autoloaded
PS: im sorry about my bad english.
Thanks again!!!
nothing… i got a white page! when i do $this->load->library(‘Acl’); all seems to work but response with a blank page!
My constructor is:
function Site(){
parent::Controller();
$this->load->library(‘Acl’);
define(‘ROLE’, ’1′);
define(‘RESOURCE’, ’1′);
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!”;
}
Please help me!
Increddible. http://www.matttsone.me is the shit.
Niksdype say: It is remarkable, very amusing idea
_____________
lavetra
buyonline uk
7
@bluego78 I had the same problem, I fixed it by putting this line of code before the require_once for Zend/Acl.php
Here it is:
ini_set(‘include_path’,ini_get(‘include_path’).PATH_SEPARATOR.BASEPATH.’libraries/’);
Not sure if it will fix it for you, but worked for me.