Tasty Bytes Coding, Design, etc972-679-1963
kelley.brianp@gmail.com

Simple ACL Class for CodeIgniter

Hey guys, I was originally using an ACL Class that was posted on net tuts. After scouring the internet for an ACL plugin that was similar to the net tuts one I opted to just adapt the net tuts one since I couldn’t find one similar. I tried out the Zend ACL library but it didnt (that i saw) have the same functionality

- User by user permissions
- Multiple Roles
- Database configured
- Etc.

Here is the net.tutsplus.com walk through if anyone needs it.
http://net.tutsplus.com/tutorials/php/a-better-login-system/

Database
- Same as the net tuts, just renamed the tables for my own preference.
- USER_DATA can be basically anything you want, just needs to have “id

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

--
-- Table structure for table `perm_data`
-- 

CREATE TABLE `perm_data` (
  `ID` bigint(20) unsigned NOT NULL auto_increment,
  `permKey` varchar(30) NOT NULL,
  `permName` varchar(30) NOT NULL,
  PRIMARY KEY  (`ID`),
  UNIQUE KEY `permKey` (`permKey`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=39 ;

-- --------------------------------------------------------

--
-- Table structure for table `role_data`
-- 

CREATE TABLE `role_data` (
  `ID` bigint(20) unsigned NOT NULL auto_increment,
  `roleName` varchar(20) NOT NULL,
  PRIMARY KEY  (`ID`),
  UNIQUE KEY `roleName` (`roleName`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;

-- --------------------------------------------------------

--
-- Table structure for table `role_perms`
-- 

CREATE TABLE `role_perms` (
  `ID` bigint(20) unsigned NOT NULL auto_increment,
  `roleID` bigint(20) NOT NULL,
  `permID` bigint(20) NOT NULL,
  `value` tinyint(1) NOT NULL default '0',
  `addDate` datetime NOT NULL,
  PRIMARY KEY  (`ID`),
  UNIQUE KEY `roleID_2` (`roleID`,`permID`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

-- --------------------------------------------------------

--
-- Table structure for table `user_data`
-- 

CREATE TABLE `user_data` (
  `ID` int(10) unsigned NOT NULL auto_increment,
  `username` varchar(20) NOT NULL,
  `password` text NOT NULL,
  `name` tinytext NOT NULL,
  `address` tinytext,
  `address2` tinytext,
  `city` tinytext,
  `state` tinytext,
  `zip` tinytext,
  `cellphone` tinytext,
  `telephone` tinytext,
  `email` tinytext,
  `aim` tinytext,
  `yahoo` tinytext,
  `icq` tinytext,
  `other` tinytext,
  `dateAdded` timestamp NOT NULL default CURRENT_TIMESTAMP,
  PRIMARY KEY  (`ID`),
  KEY `Username` (`username`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

-- --------------------------------------------------------

--
-- Table structure for table `user_perms`
-- 

CREATE TABLE `user_perms` (
  `ID` bigint(20) unsigned NOT NULL auto_increment,
  `userID` bigint(20) NOT NULL,
  `permID` bigint(20) NOT NULL,
  `value` tinyint(1) NOT NULL default '0',
  `addDate` datetime NOT NULL,
  PRIMARY KEY  (`ID`),
  UNIQUE KEY `userID` (`userID`,`permID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

-- --------------------------------------------------------

--
-- Table structure for table `user_roles`
-- 

CREATE TABLE `user_roles` (
  `userID` bigint(20) NOT NULL,
  `roleID` bigint(20) NOT NULL,
  `addDate` datetime NOT NULL,
  UNIQUE KEY `userID` (`userID`,`roleID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

The Class File
- Just a converted version of the net tuts class.

	class acl
	{
		var $perms = array();		//Array : Stores the permissions for the user
		var $userID;			//Integer : Stores the ID of the current user
		var $userRoles = array();	//Array : Stores the roles of the current user
		var $ci;
		function __construct($config=array()) {
			$this->ci = &get_instance();

			$this->userID = floatval($config['userID']);
			$this->userRoles = $this->getUserRoles();
			$this->buildACL();
		}

		function buildACL() {
			//first, get the rules for the user's role
			if (count($this->userRoles) > 0)
			{
				$this->perms = array_merge($this->perms,$this->getRolePerms($this->userRoles));
			}
			//then, get the individual user permissions
			$this->perms = array_merge($this->perms,$this->getUserPerms($this->userID));
		}

		function getPermKeyFromID($permID) {
			//$strSQL = "SELECT `permKey` FROM `".DB_PREFIX."permissions` WHERE `ID` = " . floatval($permID) . " LIMIT 1";
			$this->ci->db->select('permKey');
			$this->ci->db->where('id',floatval($permID));
			$sql = $this->ci->db->get('perm_data',1);
			$data = $sql->result();
			return $data[0]->permKey;
		}

		function getPermNameFromID($permID) {
			//$strSQL = "SELECT `permName` FROM `".DB_PREFIX."permissions` WHERE `ID` = " . floatval($permID) . " LIMIT 1";
			$this->ci->db->select('permName');
			$this->ci->db->where('id',floatval($permID));
			$sql = $this->ci->db->get('perm_data',1);
			$data = $sql->result();
			return $data[0]->permName;
		}

		function getRoleNameFromID($roleID) {
			//$strSQL = "SELECT `roleName` FROM `".DB_PREFIX."roles` WHERE `ID` = " . floatval($roleID) . " LIMIT 1";
			$this->ci->db->select('roleName');
			$this->ci->db->where('id',floatval($roleID),1);
			$sql = $this->ci->db->get('role_data');
			$data = $sql->result();
			return $data[0]->roleName;
		}

		function getUserRoles() {
			//$strSQL = "SELECT * FROM `".DB_PREFIX."user_roles` WHERE `userID` = " . floatval($this->userID) . " ORDER BY `addDate` ASC";

			$this->ci->db->where(array('userID'=>floatval($this->userID)));
			$this->ci->db->order_by('addDate','asc');
			$sql = $this->ci->db->get('user_roles');
			$data = $sql->result();

			$resp = array();
			foreach( $data as $row )
			{
				$resp[] = $row->roleID;
			}
			return $resp;
		}

		function getAllRoles($format='ids') {
			$format = strtolower($format);
			//$strSQL = "SELECT * FROM `".DB_PREFIX."roles` ORDER BY `roleName` ASC";
			$this->ci->db->order_by('roleName','asc');
			$sql = $this->ci->db->get('role_data');
			$data = $sql->result();

			$resp = array();
			foreach( $data as $row )
			{
				if ($format == 'full')
				{
					$resp[] = array("id" => $row->ID,"name" => $row->roleName);
				} else {
					$resp[] = $row->ID;
				}
			}
			return $resp;
		}

		function getAllPerms($format='ids') {
			$format = strtolower($format);
			//$strSQL = "SELECT * FROM `".DB_PREFIX."permissions` ORDER BY `permKey` ASC";

			$this->ci->db->order_by('permKey','asc');
			$sql = $this->ci->db->get('perm_data');
			$data = $sql->result();

			$resp = array();
			foreach( $data as $row )
			{
				if ($format == 'full')
				{
					$resp[$row->permKey] = array('id' => $row->ID, 'name' => $row->permName, 'key' => $row->permKey);
				} else {
					$resp[] = $row->ID;
				}
			}
			return $resp;
		}

		function getRolePerms($role) {
			if (is_array($role))
			{
				//$roleSQL = "SELECT * FROM `".DB_PREFIX."role_perms` WHERE `roleID` IN (" . implode(",",$role) . ") ORDER BY `ID` ASC";
				$this->ci->db->where_in('roleID',$role);
			} else {
				//$roleSQL = "SELECT * FROM `".DB_PREFIX."role_perms` WHERE `roleID` = " . floatval($role) . " ORDER BY `ID` ASC";
				$this->ci->db->where(array('roleID'=>floatval($role)));

			}
			$this->ci->db->order_by('id','asc');
			$sql = $this->ci->db->get('role_perms'); //$this->db->select($roleSQL);
			$data = $sql->result();
			$perms = array();
			foreach( $data as $row )
			{
				$pK = strtolower($this->getPermKeyFromID($row->permID));
				if ($pK == '') { continue; }
				if ($row->value === '1') {
					$hP = true;
				} else {
					$hP = false;
				}
				$perms[$pK] = array('perm' => $pK,'inheritted' => true,'value' => $hP,'name' => $this->getPermNameFromID($row->permID),'id' => $row->permID);
			}
			return $perms;
		}

		function getUserPerms($userID) {
			//$strSQL = "SELECT * FROM `".DB_PREFIX."user_perms` WHERE `userID` = " . floatval($userID) . " ORDER BY `addDate` ASC";

			$this->ci->db->where('userID',floatval($userID));
			$this->ci->db->order_by('addDate','asc');
			$sql = $this->ci->db->get('user_perms');
			$data = $sql->result();

			$perms = array();
			foreach( $data as $row )
			{
				$pK = strtolower($this->getPermKeyFromID($row->permID));
				if ($pK == '') { continue; }
				if ($row->value == '1') {
					$hP = true;
				} else {
					$hP = false;
				}
				$perms[$pK] = array('perm' => $pK,'inheritted' => false,'value' => $hP,'name' => $this->getPermNameFromID($row->permID),'id' => $row->permID);
			}
			return $perms;
		}

		function hasRole($roleID) {
			foreach($this->userRoles as $k => $v)
			{
				if (floatval($v) === floatval($roleID))
				{
					return true;
				}
			}
			return false;
		}

		function hasPermission($permKey) {
			$permKey = strtolower($permKey);
			if (array_key_exists($permKey,$this->perms))
			{
				if ($this->perms[$permKey]['value'] === '1' || $this->perms[$permKey]['value'] === true)
				{
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		}
	}

Example Usage

// Use whatever user script you would like, just make sure it has an ID field to tie into the ACL with
$this->load->library('user',array('username'=>'admin','password'=>'abc123') );	

// Get the user's ID and add it to the config array
$config = array('userID'=>$this->user->getUserID());

// Load the ACL library and pas it the config array
$this->load->library('acl',$config);

// Get the perm key
// I'm using the URI to keep this pretty simple ( http://www.example.com/test/this ) would be 'test_this'
$acl_test = $this->uri->segment(1).'_';
$acl_test .= ($this->uri->segment(2)!="")?$this->uri->segment(2):'view';

// If the user does not have permission either in 'user_perms' or 'role_perms' redirect to login, or restricted, etc
if ( !$this->acl->hasPermission($acl_test) ) {
	redirect('/login/');
}

6 Responses Subscribe to comments


  1. Boy george

    23 queries?!

    Nov 10, 2009 @ 6:23 pm


  2. admin

    I’m not sure how you came to 23, but its only 8 queries, and on an average check there should only be 2 queries, one for the Role Permissions, and another for the separate User Permissions

    Nov 11, 2009 @ 9:24 am


  3. Piotr Polak

    You might consider using cache for efficiency. You could store user rights in a session variable that is refreshed every 5 minutes or store serialized result sets on filesystem.

    Jun 08, 2010 @ 1:43 pm


  4. Sergio

    This note is to thank you for sharing your port to CI. I just downloaded it and made some minor adjustments. You saved me quite a bit of work.

    Seeing the code work put a smile on my face.

    Aug 20, 2010 @ 10:43 pm


  5. admin

    Agreed, I may adapt the file cache script I recently posted to do just this. Thanks for the tip.

    Aug 23, 2010 @ 7:38 am


  6. admin

    Your welcome, I figured someone could use some of the stuff I’ve been coding up.

    Aug 27, 2010 @ 7:30 pm

Reply