The creation of a secure and powerful ACL (Access control list) it’s one of the most delicate and important pieces for building a sturdy website. I’ll try to make this task easier sharing the code I used in one of my latest projects. This ACL system works with a MySQL database which grant us total flexibility creating users and roles.
Creating database tables
The first step is to create the necessary tables in database:
Table roles
For storing the roles or groups. Each role have is own privileges. This roles will be assigned to each user, and the users will inherit the role privileges.
CREATE TABLE `roles` ( `id` tinyint(1) NOT NULL AUTO_INCREMENT, `role` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
Now we add some roles to the table:
Anonymous: For non-registered visitors.
Registered: For registered visitors.
Admin: For super users.
INSERT INTO roles (id, role) VALUES (1, 'Anonymous'); INSERT INTO roles (id, role) VALUES (2, 'Registered'); INSERT INTO roles (id, role) VALUES (3, 'Admin');
Table acl
To store all the controllers/actions. Each row means a different action that can be performed by the application.
CREATE TABLE `acl` ( `id` int(10) NOT NULL AUTO_INCREMENT, `controller` varchar(100) NOT NULL, `action` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `controller` (`controller`,`action`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC;
Table acl_to_roles
Establish the relation between the roles and actions. In other words: Which actions can perform each role/group.
CREATE TABLE `acl_to_roles` ( `id` int(10) NOT NULL AUTO_INCREMENT, `acl_id` int(10) NOT NULL, `role_id` tinyint(10) NOT NULL, PRIMARY KEY (`id`), KEY `acl_id` (`acl_id`), KEY `role_id` (`role_id`), CONSTRAINT `acl_to_roles_ibfk_1` FOREIGN KEY (`acl_id`) REFERENCES `acl` (`id`) ON DELETE CASCADE, CONSTRAINT `acl_to_roles_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Table users
It contains the field role_id, which establish the privileges that each user will inherit from the roles, and the general information about the users: login, password, ETC.
CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `role_id` tinyint(1) DEFAULT '1', `login` varchar(50) DEFAULT NULL, `password` varchar(32) DEFAULT NULL, `salt` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`), KEY `login_index` (`login`), KEY `password_index` (`password`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC;
Create the ACL plugin
Now we need to create the ACL plugin. It will be located in the following path within our library folder: “/library/MyProject/Controller/Plugin/Acl.php”
<?php class MyProject_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract { public function preDispatch(Zend_Controller_Request_Abstract $request) { $auth = Zend_Auth::getInstance(); //var_dump($auth->getIdentity()); $authModel=new Application_Model_Auth(); if (!$auth->hasIdentity()){ //If user doesn't exist it will get the Guest account from "users" table Id=1 $authModel->authenticate(array('login'=>'Guest','password'=>'shocks')); } $request=$this->getRequest(); $aclResource=new Application_Model_AclResource(); //Check if the request is valid and controller an action exists. If not redirects to an error page. if( !$aclResource->resourceValid($request)){ $request->setControllerName('error'); $request->setActionName('error'); return; } $controller = $request->getControllerName(); $action = $request->getActionName(); //Check if the requested resource exists in database. If not it will add it if( !$aclResource->resourceExists($controller, $action)){ $aclResource->createResource($controller,$action); } //Get role_id $role_id=$auth->getIdentity()->role_id; $role=Application_Model_Role::getById($role_id); $role=$role[0]->role; // setup acl $acl = new Zend_Acl(); // add the role $acl->addRole(new Zend_Acl_Role($role)); if($role_id==3){//If role_id=3 "Admin" don't need to create the resources $acl->allow($role); }else{ //Create all the existing resources $resources=$aclResource->getAllResources(); // Add the existing resources to ACL foreach($resources as $resource){ $acl->add(new Zend_Acl_Resource($resource->getController())); } //Create user AllowedResources $userAllowedResources=$aclResource->getCurrentRoleAllowedResources($role_id); // Add the user permissions to ACL foreach($userAllowedResources as $controllerName =>$allowedActions){ $arrayAllowedActions=array(); foreach($allowedActions as $allowedAction){ $arrayAllowedActions[]=$allowedAction; } $acl->allow($role, $controllerName,$arrayAllowedActions); } } //Save ACL so it can be used later to check current user permissions Zend_Registry::set('acl', $acl); //Check if user is allowed to acces the url and redirect if needed if(!$acl->isAllowed($role,$controller,$action)){ $request->setControllerName('error'); $request->setActionName('access-denied'); return; } } }
Update config.ini and Bootstrap.php files
Add the following line with the path of your plugin to your config.ini or application.ini file:
resources.frontController.plugins.acl = “MyProject_Controller_Plugin_Acl”
Now we need to register the namespace, so we have to add the following function to the Bootstrap.php file.
/* Application/Bootstrap.php */ protected function _initAutoload() { // Add autoloader empty namespace $autoLoader = Zend_Loader_Autoloader::getInstance (); $autoLoader->registerNamespace('MyProject_'); // Return it so that it can be stored by the bootstrap return $autoLoader; }
Create the model AclResource
We need to create the model AclResource which have methods that are used by the plugin, like getting the Acl resources from database or checking whether resources exist and are valid.
<?php /* Application/models/AclResource.php */ class Application_Model_AclResource extends Application_Model_Mapper { protected $Model_DbTable='Application_Model_DbTable_Acl'; protected $Model_DbView=null; protected $id; protected $controller; protected $action; public function __set($name, $value) { $method = 'set' . $name; if (('mapper' == $name) || !method_exists($this, $method)) { throw new Exception('Error __set() function, Invalid property'); } $this->$method($value); } public function __get($name) { $method = 'get' . $name; if (('mapper' == $name) || !method_exists($this, $method)) { throw new Exception('Error __get() function, Invalid property'); } return $this->$method(); } public function setOptions(array $options) { $methods = get_class_methods($this); foreach ($options as $key => $value) { $method = 'set' . ucfirst($key); if (in_array($method, $methods)) { $this->$method($value); } } return $this; } public function getModel_DbTable(){ return $this->Model_DbTable; } public function getModel_DbView(){ return $this->Model_DbView; } public function getController(){ return $this->controller; } public function setController($text){ $this->controller = (string) $text; return $this; } public function setAction($text){ $this->action = (string) $text; return $this; } public function getAction(){ return $this->action; } public function setId($text){ $this->id = (string) $text; return $this; } public function getId(){ return $this->id; } public static function getByColumn($column = null,$value = null){ $mapper=new self(); $out=$mapper->MapperGetByColumn($column,$value); return $out; } public static function getBySQLCondition(array $conditions ){ $mapper=new self(); $out=$mapper->MapperGetBySQLCondition($conditions); return $out; } public static function select($sql=null, $params=null){ $mapper=new self(); $out=$mapper->MapperSelect($sql,$params); return $out; } public static function save(array $data){ $mapper=new self(); $out=$mapper->MapperSave($data); return $out; } public static function resourceExists($controller=null,$action=null){ if(!$controller || !$action) throw new Exception("Error resourceExists(), the controller/action is empty"); $result=self::getBySQLCondition(array('controller=?'=>$controller,'action=?'=>$action)); if(count($result)){ return true; } return false; } public static function resourceValid($request){ // Check if controller exists and is valid $dispatcher = Zend_Controller_Front::getInstance()->getDispatcher(); if (!$dispatcher->isDispatchable($request)) { return false; } // Check if action exist and is valid $front = Zend_Controller_Front::getInstance(); $dispatcher = $front->getDispatcher(); $controllerClass = $dispatcher->getControllerClass($request); $controllerclassName = $dispatcher->loadClass($controllerClass); $actionName = $dispatcher->getActionMethod($request); $controllerObject = new ReflectionClass($controllerclassName); if(!$controllerObject->hasMethod($actionName)){ return false; } return true; } public function createResource($controller=null,$action=null){ if(!$controller || !$action) throw new Exception("Error resourceExists(), the controller/action is empty"); $data=array('controller'=>$controller,'action'=>$action); return self::save($data); } public function getCurrentRoleAllowedResources($role_id=null){ if(!$role_id) throw new Exception("Error getCurrentUserPermissions(), the role_id is empty"); $db = Zend_Db_Table::getDefaultAdapter(); $sql='SELECT A.controller,A.action FROM acl_to_roles ATR INNER JOIN acl A ON A.id=ATR.acl_id WHERE role_id=? ORDER BY A.controller'; $stmt = $db->query($sql,$role_id); $out= $stmt->fetchAll(); $controller=''; $resources=array(); foreach ($out as $value){ if($value['controller']!=$controller){ $controller=$value['controller']; } $resources[$controller][]=$value['action']; } return $resources; } public static function getAll(){ $mapper=new self(); $out=$mapper->MapperFetchAll(); return $out; } public function getAllResources(){ $mapper=new self(); $sql='SELECT controller FROM acl GROUP BY controller'; $out=$mapper->select($sql); return $out; } }
Create the Authorization model (Zend_Auth)
Now we need to create a model to authenticate users. In this example I’m using a instance of Zend_Auth to perform that task and a Salt generator to improve the passwords security.
<?php class Application_Model_Auth extends Application_Model_Mapper { public static function authenticate(array $values) { if(!count($values)) throw new Exception('Exception trying to authenticate using empty values'); // Get our authentication adapter and check credentials $loginValue=$values['login']; $adapter = self::getAuthAdapter('login'); $adapter->setIdentity($loginValue); $adapter->setCredential($values['password']); $auth = Zend_Auth::getInstance(); $result = $auth->authenticate($adapter); if ($result->isValid()) { $user = $adapter->getResultRowObject(); $auth->getStorage()->write($user); return true; } return false; } /** * This function generates a password salt as a string of x (default = 15) characters * ranging from a-zA-Z0-9. * @param $max integer The number of characters in the string */ public static function generateSalt($max = 15) { $characterList = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $i = 0; $salt = ""; do { $salt .= $characterList{mt_rand(0,strlen($characterList)-1)}; $i++; } while ($i <= $max); return $salt; } public static function logOut(){ Zend_Auth::getInstance()->clearIdentity(); } private static function getAuthAdapter($loginField='login') { $dbAdapter = Zend_Db_Table::getDefaultAdapter(); $authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter); $authAdapter->setTableName('users') ->setIdentityColumn($loginField) ->setCredentialColumn('password') ->setCredentialTreatment('MD5(CONCAT(?,salt))'); return $authAdapter; } }
Create the Mapper (Application_Model_Mapper)
The Mapper is a cunning way to perform all the common database requests. If we need any model to have access to a certain database table we just have to create a class extending the Application_Model_Mapper and follow the steps detailed below.
<?php class Application_Model_Mapper { protected $_dbTable; public function setDbTable($dbTable) { if (is_string($dbTable)) { $dbTable = new $dbTable(); } if (!$dbTable instanceof Zend_Db_Table_Abstract) { throw new Exception('Invalid table data gateway provided'); } $this->_dbTable = $dbTable; return $this; } protected function getDbTable($useViewTable=true) { if (null === $this->_dbTable) { if(null === $this->Model_DbTable){ throw new Exception('There is no Model_DbTable. You must define the Model_DbTable variable on the model in order to get the correct database table.'); } $table=(null !== $this->Model_DbView && $useViewTable)?$this->Model_DbView:$this->Model_DbTable; $this->setDbTable($table); } return $this->_dbTable; } protected function MapperSave(array $data) { $currentModel=get_class($this);//Get the class that is extending the mapper foreach($data as $index=>$value){ if(!property_exists($currentModel,$index)){ unset($data[$index]);//Delete key if not exist on the object } } $id=(isset($data['id']))?$data['id']:null; if (null ===$id ) { unset($data['id']); $out= $this->getDbTable(false)->insert($data); } else { $this->getDbTable(false)->update($data, array('id = ?' => $id)); $out=$id; } return $out; } protected function MapperGetByColumn($column=null,$value=null,$order=array('id DESC')){ if(!$column || !$value)throw new Exception("Error -> MapperGetByColumn (The params are empty)"); $currentModel=get_class($this);//Get the class that extends the mapper if(!property_exists ($currentModel,$column))throw new Exception("Error -> getByColumn (The Column '_{$column}' doesn't exist on model '{$currentModel}')"); $result =$this->getDbTable()->select()->where("$column = ?", $value)->order($order); $resultSet=$this->getDbTable()->fetchAll($result); $currentModel=get_class($this);//Get the class that extends the mapper $entries=array(); foreach ($resultSet as $row) { $data=$row->toArray(); $entry = new $currentModel(); $entry->setOptions($data); $entries[] = $entry; } return $entries; } protected function MapperDeleteByColumn($column=null,$value=null){ if(!$column || !$value)throw new Exception("Error -> MapperDeleteByColumn (The params are empty)"); $currentModel=get_class($this);//Get the class that extends the mapper if(!property_exists ($currentModel,$column))throw new Exception("Error -> MapperDeleteByColumn (The Column '_{$column}' doesn't exist on model '{$currentModel}')"); $result =$this->getDbTable()->delete(array("$column=?"=>$value)); return $result; } protected function MapperSelect($sql=null, $params = null){ if(!$sql)throw new Exception("Error -> MapperSelect (The sql is empty)"); $db = Zend_Db_Table::getDefaultAdapter(); $stmt = $db->query($sql,$params); $resultSet= $stmt->fetchAll(); $currentModel=get_class($this);//Get the class that extends the mapper $entries=array(); foreach ($resultSet as $data) { $entry = new $currentModel(); $entry->setOptions($data); $entries[] = $entry; } return $entries; } /* * MapperGetBySQLCondition * @param $conditions must be an array with the WHERE conditions and values like the following: * array( * 'product_name = ?'=>'Wimax', * 'id <> ?'=>2 * ) * @param $OR if this is setted to true the conditions will be joined with an "OR" statement * ex: select * from example WHERE product=1 OR id=2 */ protected function MapperGetBySQLCondition(array $conditions,$use_OR=false,$order=array('id DESC')){ if(!count($conditions))throw new Exception("Error -> MapperGetBySQLCondition (The conditions are empty)"); $result =$this->getDbTable()->select(); $first=true; foreach($conditions as $sql=>$value){ $where=($use_OR && !$first)?'orWhere':'where'; $result=$result->$where($sql, $value); $first=false; } $result->order($order); $resultSet=$this->getDbTable()->fetchAll($result); $currentModel=get_class($this);//Get the class that extends the mapper $entries=array(); foreach ($resultSet as $row) { $data=$row->toArray(); $entry = new $currentModel(); $entry->setOptions($data); $entries[] = $entry; } return $entries; } protected function MapperGetById($id=null) { if(!$id)throw new Exception("Error -> MapperGetById (The ID is empty)"); $result = $this->getDbTable()->find($id); $entries = array(); if (0 == count($result)) { return; } $row = $result->current()->toArray(); $currentModel=get_class($this);//Get the class that extends the mapper $entry = new $currentModel(); $entry->setOptions($row); $entries[] = $entry; return $entries; } protected function MapperFetchAll($order=array('id DESC')) { $resultSet = $this->getDbTable()->fetchAll(null,$order); $entries = array(); $currentModel=get_class($this);//Get the class that extends the mapper foreach ($resultSet as $row) { $data=$row->toArray(); $entry = new $currentModel(); $entry->setOptions($data); $entries[] = $entry; } return $entries; } public function toArray(){ $class=get_class($this); $properties=get_class_vars ($class); $result=array(); foreach($properties['viewColumns'] as $viewProperty){ $properties[$viewProperty]=null; } unset($properties['viewColumns']); unset($properties['_dbTable']); foreach($properties as $key=> $value){ $result[$key]=$this->$key; } return $result; } }
Create the User and Role Models
Finally we need to create the “application/models/User.php” and “application/models/Role.php” models. All models which have access to a certain database table will need variable called “$Model_DbTable”. For security reasons this variable contains the name of the class which contains the real db-table name. All these db access classes are stored in “application/models/DbTable/”.
User model
<?php /* Application/models/User.php */ class Application_Model_User extends Application_Model_Mapper { protected $Model_DbTable='Application_Model_DbTable_User'; protected $Model_DbView; protected $id; protected $role_id; protected $login; protected $password; protected $salt; protected $viewColumns=array(); public function __set($name, $value) { if (('mapper' == $name) || !property_exists ($this, $name)) { throw new Exception('Error __set() function, Invalid property'); } $this->$name = (string) $value; } public function __get($name) { if (('mapper' == $name) || !property_exists($this, $name)) { if(array_key_exists ($name,$this->viewColumns)){ return $this->viewColumns[$name]; } throw new Exception("Error __get() function, Invalid property '$name'"); } return $this->$name; } public function setOptions(array $options) { foreach ($options as $key => $value) { if (property_exists($this, $key)) { $this->$key= (string) $value; }elseif(in_array($key,$this->viewColumns)){//Check if the property is a view $this->viewColumns[$key]=(string)$value; unset($this->viewColumns[array_search($key, $this->viewColumns)]); } } return $this; } public static function getByColumn($column = null,$value = null,$order=null){ $mapper=new self(); $out=$mapper->MapperGetByColumn($column,$value,$order); return $out; } public static function getBySQLCondition(array $conditions,$is_OR=false,$order=null ){ $mapper=new self(); $out=$mapper->MapperGetBySQLCondition($conditions,$is_OR,$order); return $out; } public static function getAll($order=null){ $mapper=new self(); $out=$mapper->MapperFetchAll($order); return $out; } /** * * Create and returns a new user * @param array $data: it requires at least three parameters: $data['role_id', $data['login'] and $data['password'] * @throws Exception */ public static function createUser(array $data){ if(!$data['role_id'] || !$data['login'] || !$data['password'])throw new Exception('role_id, login and password must be provided in the function createUser().'); $data['id']=self::save($data); $data['salt']=Application_Model_Auth::generateSalt(); $data['password']=md5($data['password'].$data['salt']); self::save($data); //Return created user: return self::getById($data['id']); } public static function select($sql=null, $params=null){ $mapper=new self(); $out=$mapper->MapperSelect($sql,$params); return $out; } public static function save(array $data){ $mapper=new self(); $out=$mapper->MapperSave($data); return $out; } public static function getById($id = null){ $mapper=new self(); $out=$mapper->MapperGetById($id); return $out; } public static function getCurrentUser(){ $auth = Zend_Auth::getInstance(); if (!$auth->hasIdentity()){ return 0; } $id= $auth->getIdentity()->id; $user=Application_Model_User::getById($id); return (count($user))? $user[0]->toArray() : 0; } public function getViewProperty($column=null){ return (in_array($column,$this->viewColumns))? $column:null; } /** * Check if the user is allowed to access the provided Controller->action **/ public static function isAllowed($controller, $action){ //Get Current user role $auth = Zend_Auth::getInstance(); $role=Application_Model_Role::getById($auth->getIdentity()->role_id); $acl = Zend_Registry::get('acl'); return ($acl->isAllowed($role[0]->role, $controller, $action))?true : false; }
Role model
<?php /* Application/models/Role.php */ class Application_Model_Role extends Application_Model_Mapper { protected $Model_DbTable='Application_Model_DbTable_Role'; protected $Model_DbView; protected $id; protected $role; protected $viewColumns=array(); public function __set($name, $value) { if (('mapper' == $name) || !property_exists ($this, $name)) { throw new Exception('Error __set() function, Invalid property'); } $this->$name = (string) $value; } public function __get($name) { if (('mapper' == $name) || !property_exists($this, $name)) { if(array_key_exists ($name,$this->viewColumns)){ return $this->viewColumns[$name]; } throw new Exception("Error __get() function, Invalid property '$name'"); } return $this->$name; } public function setOptions(array $options) { foreach ($options as $key => $value) { if (property_exists($this, $key)) { $this->$key= (string) $value; }elseif(in_array($key,$this->viewColumns)){//Check if the property is a view $this->viewColumns[$key]=(string)$value; unset($this->viewColumns[array_search($key, $this->viewColumns)]); } } return $this; } public static function getByColumn($column = null,$value = null,$order=null){ $mapper=new self(); $out=$mapper->MapperGetByColumn($column,$value,$order); return $out; } public static function getBySQLCondition(array $conditions,$is_OR=false,$order=null ){ $mapper=new self(); $out=$mapper->MapperGetBySQLCondition($conditions,$is_OR,$order); return $out; } public static function getAll($order=null){ $mapper=new self(); $out=$mapper->MapperFetchAll($order); return $out; } public static function select($sql=null, $params=null){ $mapper=new self(); $out=$mapper->MapperSelect($sql,$params); return $out; } public static function save(array $data){ $mapper=new self(); $out=$mapper->MapperSave($data); return $out; } public static function getById($id = null){ $mapper=new self(); $out=$mapper->MapperGetById($id); return $out; } }
The DB-Table Classes
As mentioned before, These are the needed db-table classes for the models User.php, Role.php and AclResource.php. All located in “Application/models/DbTable/”.
Application_Model_DbTable_Acl for model AclResource.php
<?php /* Application/models/DbTable/Acl.php */ class Application_Model_DbTable_Acl extends Zend_Db_Table_Abstract { protected $_name = 'acl'; }
Application_Model_DbTable_User for model User.php
<?php /* Application/models/DbTable/User.php */ class Application_Model_DbTable_User extends Zend_Db_Table_Abstract { protected $_name = 'users'; }
Application_Model_DbTable_Role for model Role.php
<?php /* Application/models/DbTable/Role.php */ class Application_Model_DbTable_Role extends Zend_Db_Table_Abstract { protected $_name = 'roles'; }
Create the user “Guest”
Last thing, you will need to add a default user for all the non-registered visitors. Run the following command in any script just once and check the “users” table to be sure that the user “Guest” has been created.
Application_Model_User::createUser( array('role_id' => 1, 'login' => 'Guest', 'password' => 'shocks'));
Comments 43
Thanks for the code/tutorial – it all seems nice and clear
Would it be possible for you to give some hints/code for Application_Model_Mapper?
Author
In this case I’m using a master mapper class which makes easier the database requests. Thus you can retrieve data just calling the following methods:
Application_Model_AclResource::select,
Application_Model_AclResource::getBySQLCondition,
Application_Model_AclResource::getByColumn,
Application_Model_AclResource::getById
But the implemmentation of this class is a little bit complex to explain in just a few lines. I would need to create another full post just to explain how it works.
But don’t worry actually you don’t need to extend the Application_Model_Mapper class. You can use your own database mapper like in the Zend Framework Quickstart example. And then, replace the database calls like:
Application_Model_Role::getById (Acl.php),
self::getBySQLCondition (AclResource.php)
for the calls to your own mapper.
Thanks Ander – I’ve got it now
Please send me code for Application_Model_Mapper?
Author
Hi Nirankar, I have added the Application_Model_Mapper, the User/Role models and the full db-table implementation. There are not missing parts now, hope this clarifies your confusion.
can your tutorial also work with Zend_Navigation?
Author
I’m afraid Marcus, I didn’t implement Zend_Navigation in my project. But as far as I know, the Zend_Navigation pages are generated depending on the Action -> Controller. The Access control system provided in this tutorial sets access rules for each Action/Controller. So my guess is both features would be a perfect combination.
hi Ander, are you doing this project,
This project does not have a login form, right
Author
This is not a project Dalibor, this is an implementation from scrach of a Zend Framework ACL (Access control list) for managing the users access and authentication into your Zend Framework project.
Login a user is as easy as calling the following function:
Application_Model_Auth::authenticate(array(‘login’=>user_name, ‘password’=>user_password));
You just need a form to get the user_name and user_password.
Hey Ander,
Thanks for taking the time to put this post up, Zend ACL can be a complex task for new comers to the framework and this has really helped. I made some modifications to enable it to work in a modular structure and it works great!
Hey Stephen,
Can you provide the steps you did for your modifications to enable it to work in a modular structure?
Thanks!
Hi, can you send me the database DATA?
Thank you!
you are expert developer
I am having an error with the code. It is saying that Fatal error: Uncaught exception ‘Exception’ with message ‘Error -> MapperGetById (The ID is empty)’. It is connected to the database because it created the to controllers in the ACL table. Not sure why it is not grabbing the Guest user Id. Any help would be greatly appreciated.
Author
That’s because you are calling from somewhere the method getById($id) with the variable $id empty. You should check why that variable is empty. I cannot say much more having that little information.
Hi Ander,
I’m totally new to php and Zend, but not to programming, so i’m using this code to see how it works. I’m having the same problem as Scott.
The cause seemed to be as simple as not having a quest entry in the users table. This way no role_id is returned because the $auth isn’t present .
So I changed the code in the Acl.php to:
//If user doesn’t exist it will get the Guest account from “users” table Id=1
//$authModel->authenticate(array(‘login’=>’Guest’,’password’=>’shocks’));
if (!$authModel->authenticate(array(‘login’=>’Guest’,’password’=>’shocks’))) {
//create user Guest…
$user = new User();
$user->createUser(array(‘login’=>’Guest’,’role_id’=>’1′,’password’=>’shocks’));
}
In the database I now have an entry: id=1, role_id=1, login=Guest, password=64fcd6bbb4f8b5e10b61f3fba96773f9, salt=gPkpvb7h6QiDyphk
But the error “Fatal error: Uncaught exception ‘Exception’ with message ‘Error -> MapperGetById (The ID is empty)” persists. The authentication still doesn’t work, at least the
$role_id = $auth->getIdentity()->role_id; is still empty.
Can anyone tell me what I’m missing?
Oooh… The part where I create a new user is now under comment of course. I only want one user “Guest” in the database….
Hi Ander,
I’ve found the “error”…
The createUser() function creates a password based on:
$data[‘password’]=md5($data[‘login’].$data[‘id’].$data[‘salt’]);
And doesn’t use the password “shocks”! So my Guest entry in the database has a different MD5 hashed password as I assumed.
Thanks for the great example. I’m learning a lot from it…
Author
Sorry for the delay Bazzline, I’m just back from my holidays. To be honest I’m glad to see that finally someone has been brave enough to go deeper in my implementation. And yes, you are completely right. I forgot telling that I was using a user called “Guest” for the anonymous visitors.
I have updated the function createUser() so now you can create the user “Guest” following this two steps:
Add this roles to your “roles” table:
Create the “Guest” user (remember to update the function first):
Hi Ander, Could be implement this whole functionality in zend modular application??
Author
Sure, I don’t see why not
Hello Ander,
Could you tell me for which version of Zend this tutorial is written? 1 or 2? 🙂
Author
It was written for Zend Framework 1.12
Sir it gives an error Class ‘MyProject_Controller_Plugin_Acl’ not found in E:\wamp\www\myZend\library\Zend\Application\Resource\Frontcontroller.php on line 117 .
in my application.ini i add this line resources.frontController.plugins.acl = “MyProject_Controller_Plugin_Acl” . Please help or give me zip it will be more helpful.
Author
Have you registered the namespace in the Application/Bootstrap.php file ?
/* Application/Bootstrap.php */
protected function _initAutoload() {
// Add autoloader empty namespace
$autoLoader = Zend_Loader_Autoloader::getInstance ();
$autoLoader->registerNamespace('MyProject_');
// Return it so that it can be stored by the bootstrap
return $autoLoader;
}
Thanks Ander
Estimated thank you very much for the code, the post is old but I am assailed by doubt,
which is the method to ask what if the user has permission to appeal or not? I hope you’re very well thank you very much in advance
greetings from chile
Author
Hi Grisuno,
This function checks if the current role of the visitor is allowed to access to the selected page:
if(!$acl->isAllowed($role,$controller,$action)){
$request->setControllerName('error');
$request->setActionName('access-denied');
return;
}
You can find this function in the file: “/library/MyProject/Controller/Plugin/Acl.php”
If the current user is allowed to see this page will depend on the content of the table acl_to_roles.
Thanks a lot bro !
Hi Ander,
Thanks fro this tutorial.
I have been checking your implementation of acl.
Its well implemented and straight to the point but I have only one issue.
How do I check if the current user has permission to access a particular action from the controller?
Thanks.
Author
Hi Lawrence, you can implement a method for that using the following query:
SELECT COUNT(*) FROM acl a JOIN acl_to_roles b ON a.id=b.acl_id WHERE a.controller='$controller' AND a.action='$action' AND b.role_id='$role_id'
And then replace the following variables:
If you don’t get results it means that the user is not allowed to access that resource
Author
I’ve updated the code so now you just need to call the following method:
Application_Model_User::isAllowed($controller, $action);
Hi, nice to meet you.
I am going to implement ACl in my local. But I don’t know how to do.
Can you help me?
Hello how may i use this ACL form my none Zend framework based application? I have a won made application where i need a ACL or RBAC….. could any one give me suggestion?
I read this tutorial as well http://www.sitepoint.com/role-based-access-control-in-php/ but i fail to install to my localhost….
I got below error
Fatal error: Class ‘MyProject_Controller_Plugin_Acl’ not found in /var/www/html/Website/library/Zend/Application/Resource/Frontcontroller.php on line 117
error in MapperGetBySQLCondition line number 111 because generate wrong query “SELECT `roles`.* FROM `roles` WHERE (controller=’index’) AND (action=’index’) ORDER BY `id` DESC” and here Application_Model_AclResource set model on here; please suggest how to execute
” Select * from roles inner join acl_to_roles on acl_to_roles.role_id=roles.id inner join acl on acl.id= acl_to_roles.acl_id WHERE (acl.controller=’index’) AND (acl.action=’index’) ORDER BY acl.id DESC”
because when set new model then show error I did code
” $this->modelUsers=new Application_Model_DbTable_Users();
$userList=$this->modelUsers->getList();”
hi, this is very good tutorial, I am looking for this acl model for long time, I will try it out, thanks
Hi Ander,
Very nice tutorial 🙂
I would like to mentioned a single problem here.
As per the code you are allowing access to all the resources to admin without creating the resources (you’re creating the resources in else{} part).
$acl->allow($role);
But without creating all the existing resources it is showing error for the admin that resource ‘XXX’ not found. Should it work for admin without creating the resources?
Author
As far as I remember it should work that way. I just had a look at the Zend Framework 1.12 documentation. Go to that link and look for the following lines:
As you see in the documentation it has been implemented in the same way. Maybe this is a problem with the lines 25-28 on file: “/library/MyProject/Controller/Plugin/Acl.php”:
These lines are creating the current resource (controller, action) only if it doesn’t exist already. So for example if you create a controller named “Test” and an action called “copy”, the first time you run it in your browser (ex: http://yourApplication/test/copy) these lines will create the resource on your database. I think you should first test if this part is working properly.
Yes, resource is already existing in my database table.
Author
I guess debugging is your way to go now Rupali, I added some improvements in the past but I couldn’t test it, matter of time…
Could you let me know if you find out what was the issue?
Thanks
please an exampl of form login