Zend_Acl + Zend_Auth + Zend_Controller_Plugin = HAPPY!
Before I start explaining the code let me just give it to you!
Users SQL
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(255) NOT NULL, `userpassword` varchar(40) NOT NULL, `firstname` varchar(255) NOT NULL, `lastname` varchar(255) NOT NULL, `role` varchar(30) NOT NULL DEFAULT 'user', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/application/configs/application.ini
autoloaderNamespaces[] = "Application_" resources.frontController.plugins.Acl = "Application_Controller_Plugin_Acl" acl.roles.guest = null acl.roles.user = guest acl.roles.admin = user acl.resources.allow.index.all = guest acl.resources.allow.error.all = guest acl.resources.allow.user.register = guest acl.resources.allow.user.login = guest acl.resources.allow.user.profile = user
/library/Application/Acl.php
<?php
class Application_Acl extends Zend_Acl {
public function __construct(Zend_Config $config){
$roles = $config->acl->roles;
$resources = $config->acl->resources;
$this->_addRoles($roles);
$this->_addResources($resources);
}
private function _addRoles($roles){
foreach($roles as $name => $parents){
if(!$this->hasRole($name)) {
if(empty($parents)){
$parents = array();
} else {
$parents = explode(',', $parents);
}
$this->addRole(new Zend_Acl_Role($name), $parents);
}
}
}
private function _addResources($resources){
foreach($resources as $permissions => $controllers){
foreach($controllers as $controller => $actions){
if('all' == $controller){
$controller = null;
} else {
if(!$this->has($controller)){
$this->add(new Zend_Acl_Resource($controller));
}
}
foreach($actions as $action => $role){
if($action == 'all') {
$action = null;
}
if($permissions = 'allow'){
$this->allow($role, $controller, $action);
}
if($permissions == 'deny'){
$this->deny($role, $controller, $action);
}
}
}
}
}
}
/library/Application/Controller/Plugin/Acl.php
<?php
class Application_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request){
// Load ACL config
$config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/application.ini', APPLICATION_ENV);
$acl = new Application_Acl($config);
// Begin authorisation
$auth = Zend_Auth::getInstance();
$role = 'guest';
if($auth->hasIdentity()){
$user = $auth->getIdentity();
print_r($user);
if(is_object($user)){
$role = $user->role;
}
}
$controller = $request->getControllerName();
$action = $request->getActionName();
$module = $request->getModuleName();
$resource = $controller;
$privellege = $action;
if(!$acl->has($resource)) {
throw new Exception('No resource found');
}
if(!$acl->isAllowed($role, $resource, $privellege)) {
$request->setModuleName('default')
->setControllerName('user')
->setActionName('login')
->setDispatched(false);
}
}
}
/application/controllers/UserController.php
<?php
class UserController extends Zend_Controller_Action
{
public function indexAction(){
// action body
}
public function logoutAction(){
$auth = Zend_Auth::getInstance();
$auth->clearIdentity();
}
public function loginAction() {
$form = $this->_helper->formLoader('login');
if ($this->getRequest()->isPost()) {
if (! $form->isValid($_POST)) {
$this->view->form = $form;
return;
} else {
$data = $form->getValues();
$authAdapter = new Zend_Auth_Adapter_DbTable(Zend_Db_Table::getDefaultAdapter());
$authAdapter->setTableName('user')->setCredentialColumn('userpassword')->setIdentityColumn('email')->setCredentialTreatment('MD5(?)');
$authAdapter->setIdentity($data['email']);
$authAdapter->setCredential($data['password']);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
$userInfo = $authAdapter->getResultRowObject(null, array('userpassword'));
$authStorage = $auth->getStorage();
$authStorage->write($userInfo);
} else {
}
}
}
$this->view->form = $form;
}
public function registerAction(){
$form = $this->_helper->formLoader('register');
if($this->getRequest()->isPost()){
if(!$form->isValid($_POST)){
$this->view->form = $form;
return;
}
}
$this->view->form = $form;
}
}
So that’s the code, cudos has to go to Joe Topjian for giving me the idea and the base code to work from…
Sorry for my bad English.
Init “$acl = new Application_Acl($config);” in method preDispatch bad good idea.
Because, when you call Zend_Controller_Action::_forward, you init your Acl several.
Init in the method __construct from class your Plugin
Just wanted to drop you a line to say thanks for helping me wrap my brain around this. I was struggling with exactly the same criteria as you (especially the piss easy bit), and this post helped me finally nail it.
good work!
Interesting article, however I found that this prevents my 404 page from working.
So if i type in /foo (a non-existing controller) it just redirects to the login page.
Any ideas?
Ben
Never mind I figured this out.
In the example code if a resource is not found the resource is set to null. In my circumstance this is the 404 condition.
So rather than setting the resource to null, i throw an exception which is handled by the error controller.
Anyhew, thanks for the above very useful.
It’s no problem, I’m glad this helped demystify this somewhat! I’ve amended the article so that it throws an exception now.
There is also an easier way to access your application.ini file through the bootstrap!
Right now I have the acl in it’s own config. I load the config and the model in the bootstrap then save the acl model to the registry so I can use it in my Access plugin.
I need to set up caching for this since I think parsing this file all the time might be a performance hit. Especially when it gets big.
The odd thing is, I did not think setting a model would work in the boostrap because it is invoked before the app knows about the models (at least that is what I thought).
protected function _initAcl() {
$aclConfig = new Zend_Config_Ini(APPLICATION_PATH . ‘/configs/acl.ini’, APPLICATION_ENV);
$aclModel = new Model_Acl($aclConfig);
Zend_Registry::set(‘aclModel’, $aclModel);
}
To be safe I think I will move the model instantiation and caching to my Access plugin. This will also keep all the acl stuff more centrally located.
Fun! fun!
You have a small typo in /library/Application/Acl.php on line 42, it should be if($permissions == ‘allow’) instead of if($permissions = ‘allow’)
He there
first of all thnx for this one it made several things clear
but i still have a question, how can you combine this with zend_navigation?
I need some links be hidden if the user is ‘guest’
thnx !
Thanks for this.
There is also another small typo. In /library/Application/Controller/Plugin/Acl.php you can remove the print_r on line 17.
KUTGW!
Hey.
Thanks for this great tut.
I’m just having a small issue.
Once the AclPlugin is registered to the front controller, I’m getting this error:
Fatal error: Cannot redeclare class Application_Acl in /Applications/MAMP/htdocs/…/library/Application/Acl.php on line 58
Any idea why?
Thanks.
Nevermind I found the solution.
What about redirecting a denied role to a denied action in the error controller instead of redirecting to the login page?
You must also add in your application.ini:
acl.resources.allow.user.logout = guest
Withou this, you can’t logout.
nail designs…
[...]Zend_Acl + Zend_Auth + Zend_Controller_Plugin = HAPPY![...]…