Zend Framework Zend_Auth and a Plugin to Check Access to a Page

Posted on: Jan 02, 2008 by wenbert

I have written this simple user authentication and user access privilege plugin in one of my Web Apps.

First, in my AuthController, I have something like this:

    public function loginAction()
    {
        try {
            if ($this->_request->isPost()) { 
                // collect the data from the user 
                $f = new Zend_Filter_StripTags(); 
                $username = $f->filter($this->_request->getPost('username')); 
                $password = $f->filter($this->_request->getPost('password'));
                $password = md5($password); 
 
                if (empty($username)) { 
                    $this->_flashMessenger->addMessage('Provide a username.');
                    $this->_redirect('auth/');
                } else { 
                    // setup Zend_Auth adapter for a database table 
                    $dbAdapter = Zend_Registry::get('dbAdapter'); 
                    $authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter); 
                    $authAdapter->setTableName('users'); 
                    $authAdapter->setIdentityColumn('username'); 
                    $authAdapter->setCredentialColumn('password'); 
 
                    // Set the input credential values to authenticate against 
                    $authAdapter->setIdentity($username); 
                    $authAdapter->setCredential($password); 
 
                    // do the authentication  
                    $auth = Zend_Auth::getInstance(); 
                    $result = $auth->authenticate($authAdapter); 
                    if ($result->isValid()) { 
                        // success: store database row to auth's storage 
                        // system. (Not the password though!) 
                        $data = $authAdapter->getResultRowObject(null, 'password'); 
 
                        $auth->getStorage()->write($data); 
 
                        $user = Zend_Auth::getInstance()->getIdentity();
 
                        $dbAdapter = Zend_Registry::get('dbAdapter');
 
                        $sql = 'SELECT
                            ug.*,
                            g.*
                            FROM user_group ug
                            LEFT JOIN groups g ON ug.group_id = g.id
                            WHERE ug.user_id="'.$user->id.'"';
                        $groups = $dbAdapter->fetchAll($sql);
 
                        $mygroups = array();
                        foreach ($groups AS $group) {
 
                            //query permissions table
                            $sql = '
                                SELECT *
                                FROM permissions
                                WHERE group_id="'.$group['group_id'].'"
                            ';
                            $group_permission = $dbAdapter->fetchAll($sql);
 
                            $mygroups[]['group_name'] = $group['group_name'];
                            $mygroups['group_permission'] = $group_permission;
                        }
 
                        //get the Zend_Auth and then save the roles
                        $authNamespace = new Zend_Session_Namespace('Zend_Auth');
                        $authNamespace->storage->mygroups = $mygroups;
 
                        $this->_flashMessenger->addMessage('Login Successful.');
                        $this->_redirect('/');
                    } else { 
                        $this->_flashMessenger->addMessage('Login Failed. Please enter a valid username/password.');
                        $this->_redirect('/auth/');
                    } 
                }
            }
        } catch (Exception $e){
            echo $e->getMessage();
        }
        $this->view->messages = $this->_flashMessenger->getMessages();
        $this->_helper->viewRenderer->setNoRender(); //suppress auto-rendering

I know I should have done the queries in my User Model instead of inserting them in-line with the loginAction. Anyways, I did that do give you an idea of what I am querying.

Now, for the plugin. In my library folder, beside the Zend folder – which contains all the Zend Framework core files, I have a directory called Ekini.

../library/Ekini/Controller/Plugin/

Inside the Plugin directory, I have this file called: CheckHasAccess.php

The file contains the following code:

< ?php
class Ekini_Controller_Plugin_CheckHasAccess extends Zend_Controller_Plugin_Abstract {
 
    /**
     * Checks if a user is allowed to access the current controller
     * Returns true if allowed
     * redirects to the noaccess.phtml found in auth controller if user is not allowed
     *
     * @param Zend_Controller_Request_Abstract $request
     * @return unknown
     */
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {   
        try {
 
            $moduleName         = $this->getRequest()->getModuleName();
            $controllerName     = $this->getRequest()->getControllerName();
            $actionName         = $this->getRequest()->getActionName();
            $frontController    = Zend_Controller_Front::getInstance();
            $user = Zend_Auth::getInstance()->getIdentity();
 
 
            if($controllerName == 'admin' AND !isset($user)) {
                throw new Exception('You must login to access this page.');
            }
 
            //check $user->mygroups['group_permission'] array
            //echo $moduleName.' '.$controllerName.' '.$actionName;
            //Zend_Debug::dump($user->mygroups['group_permission']);
 
            if (isset($user)) {
                $permissions = $user->mygroups['group_permission'];
 
                //check for module ONLY - controller and action must be NULL
                for($i=0;$i<count ($permissions);$i++) {
                    if ($permissions[$i]['module_name'] == $moduleName AND 
                        $permissions[$i]['controller_name'] == NULL AND
                        $permissions[$i]['action_name'] == NULL AND 
                        $permissions[$i]['permission'] == 'deny') {
                        throw new Exception('You are not allowed to access this module.');    
                        }
                }
 
                //check for controller+module - action is blank
                for($i=0;$i<count($permissions);$i++) {
                    if ($permissions[$i]['module_name'] == $moduleName AND 
                        $permissions[$i]['controller_name'] == $controllerName AND
                        $permissions[$i]['action_name'] == NULL AND 
                        $permissions[$i]['permission'] == 'deny') {
                        throw new Exception('You are not allowed to access this controller.');    
                        }
                }
 
                //check for the module / controller / action - look for deny
                for($i=0;$i<count($permissions);$i++) {
                    //echo $permissions[$i]['module_name'].' '.$permissions[$i]['permission'].'<br>';
                    if ($permissions[$i]['module_name'] == $moduleName AND 
                        $permissions[$i]['controller_name'] == $controllerName AND 
                        $permissions[$i]['action_name'] == $actionName AND 
                        $permissions[$i]['permission'] == 'deny') {
 
                        throw new Exception('You are not allowed to access this page.');
                    }
                }
 
                //if guest session exists, unset it
                Zend_Session::sessionExists('Guest_Session')?Zend_Session::namespaceUnset('Guest_Session'):'';
 
            } elseif(Zend_Session::namespaceIsset('Guest_Session')) {
                //this part executes only when the Guest_Session is set
                //so that we only have to query the database once
                //querying the database is in the else
                //after querying, the Guest_Session is set.
 
                $guest = new Zend_Session_Namespace('Guest_Session');
 
                $permissions = $guest->guestRoles;
                //Zend_Debug::dump($guest);
                //echo 'guest session exists!';
                //check for module ONLY - controller and action must be NULL
                for($i=0;$i</count><count ($permissions);$i++) {
                    if ($permissions[$i]['module_name'] == $moduleName AND 
                        $permissions[$i]['controller_name'] == NULL AND
                        $permissions[$i]['action_name'] == NULL AND 
                        $permissions[$i]['permission'] == 'deny') {
                        throw new Exception('You are not allowed to access this module.');    
                        }
                }
 
                //check for controller+module - action is blank
                for($i=0;$i<count($permissions);$i++) {
                    if ($permissions[$i]['module_name'] == $moduleName AND 
                        $permissions[$i]['controller_name'] == $controllerName AND
                        $permissions[$i]['action_name'] == NULL AND 
                        $permissions[$i]['permission'] == 'deny') {
                        throw new Exception('You are not allowed to access this controller.');    
                        }
                }
 
                //check for the module / controller / action - look for deny
                for($i=0;$i<count($permissions);$i++) {
                    //echo $permissions[$i]['module_name'].' '.$permissions[$i]['permission'].'<br>';
                    if ($permissions[$i]['module_name'] == $moduleName AND 
                        $permissions[$i]['controller_name'] == $controllerName AND 
                        $permissions[$i]['action_name'] == $actionName AND 
                        $permissions[$i]['permission'] == 'deny') {
 
                        throw new Exception('You are not allowed to access this page.');
                    }
                }   
            } else {
                //query the database for permissions and then set Guest_Session
                $guestSession = new Zend_Session_Namespace('Guest_Session');
 
                //Not logged in, so check guest/unregistered user
                //query permissions table
                $dbAdapter = Zend_Registry::get('dbAdapter');
                $sql = '
                    SELECT *
                    FROM permissions
                    WHERE group_name="guest"
                ';
                $permissions = $dbAdapter->fetchAll($sql);
 
                //set this to session, so that we will not be querying the db for every guest that is logged in.
                $guestSession->guestRoles = $permissions;
 
                //check for module ONLY - controller and action must be NULL
                for($i=0;$i</count><count ($permissions);$i++) {
                    if ($permissions[$i]['module_name'] == $moduleName AND 
                        $permissions[$i]['controller_name'] == NULL AND
                        $permissions[$i]['action_name'] == NULL AND 
                        $permissions[$i]['permission'] == 'deny') {
                        throw new Exception('You are not allowed to access this module.');    
                        }
                }
 
                //check for controller+module - action is blank
                for($i=0;$i<count($permissions);$i++) {
                    if ($permissions[$i]['module_name'] == $moduleName AND 
                        $permissions[$i]['controller_name'] == $controllerName AND
                        $permissions[$i]['action_name'] == NULL AND 
                        $permissions[$i]['permission'] == 'deny') {
                        throw new Exception('You are not allowed to access this controller.');    
                        }
                }
 
                //check for the module / controller / action - look for deny
                for($i=0;$i<count($permissions);$i++) {
                    //echo $permissions[$i]['module_name'].' '.$permissions[$i]['permission'].'<br>';
                    if ($permissions[$i]['module_name'] == $moduleName AND 
                        $permissions[$i]['controller_name'] == $controllerName AND 
                        $permissions[$i]['action_name'] == $actionName AND 
                        $permissions[$i]['permission'] == 'deny') {
 
                        throw new Exception('You are not allowed to access this page.');
                    }
                }
            }
            return true;
 
        } catch (Exception $e) {
            $this->getResponse()->setHttpResponseCode(403);
            $request->setControllerName('error');
            $request->setActionName('noaccess');
        }
    }
 
}
</count>

I know, lots of repition :P But I will refactoring this part when I have time. But I hope you got the idea of the entire thing.

Well, if you didn’t lemme explain it a bit.

First of all, the loginAction – logs in the user. It get the user details including the groups where the user is under. I have stored these details in the Session.

The CheckHasAccess Plugin gets called everytime someone loads a page. Plugins behave like this. Here are table structures for your reference.


--
-- Table structure for table `permissions`
-- 

CREATE TABLE `permissions` (
  `id` int(11) NOT NULL auto_increment,
  `group_id` int(11) NOT NULL,
  `group_name` varchar(250) NOT NULL,
  `module_name` varchar(250) default 'default',
  `controller_name` varchar(250) default NULL,
  `action_name` varchar(250) default NULL,
  `permission` enum('allow','deny') NOT NULL default 'allow',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=19 ;

--
-- Dumping data for table `permissions`
-- 

INSERT INTO `permissions` VALUES (1, 1, 'admin', 'default', 'admin', NULL, 'allow');
INSERT INTO `permissions` VALUES (3, 3, 'registereduser', 'default', 'admin', NULL, 'deny');
INSERT INTO `permissions` VALUES (7, 1, 'admin', 'default', 'index', NULL, 'allow');
INSERT INTO `permissions` VALUES (4, 2, 'moderator', 'default', 'admin', 'createsitevar', 'deny');
INSERT INTO `permissions` VALUES (5, 2, 'moderator', 'default', 'admin', 'managesitevars', 'deny');
INSERT INTO `permissions` VALUES (6, 2, 'moderator', 'default', 'admin', 'deletesitevar', 'deny');

--
-- Table structure for table `groups`
-- 

CREATE TABLE `groups` (
  `id` int(11) NOT NULL auto_increment,
  `group_name` varchar(250) NOT NULL,
  `group_desc` varchar(250) NOT NULL,
  `group_parent` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `group_name` (`group_name`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;

--
-- Dumping data for table `groups`
-- 

INSERT INTO `groups` VALUES (1, 'admin', 'Administrators', 0);
INSERT INTO `groups` VALUES (2, 'moderators', 'Moderators', 0);
INSERT INTO `groups` VALUES (3, 'registereduser', 'Registered User / Member', 0);
INSERT INTO `groups` VALUES (4, 'guest', 'Guest', 0);

--
-- Table structure for table `user_group`
-- 

CREATE TABLE `user_group` (
  `id` bigint(20) NOT NULL auto_increment,
  `user_id` int(11) NOT NULL,
  `group_id` int(11) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

--
-- Dumping data for table `user_group`
-- 

INSERT INTO `user_group` VALUES (1, 1, 1);
INSERT INTO `user_group` VALUES (2, 2, 2);
INSERT INTO `user_group` VALUES (3, 3, 3);

Subscribe to comments Comment | Trackback |
Post Tags: ,

Browse Timeline


Comments ( 8 )

So.
Ugly.

ben added these pithy words on Oct 24 08 at 5:27 AM

Heh.

Wenbert added these pithy words on Oct 24 08 at 8:48 AM

Good! I like it.
But how i can call the plugin everytime??
I’m newbie..

I think that add this line to my bootstrat file…
$frontController->registerPlugin(new My_Controller_Plugin_CheckAccess());

but the argument?? I don’t now..รน

Tankyou

simon added these pithy words on Nov 20 08 at 11:22 PM

@simon you put something like this in your bootstrap file

$frontController->registerPlugin(new Ekini_Controller_Plugin_CheckHasAccess());

see http://framework.zend.com/manual/en/zend.controller.plugins.html for more info…

Wenbert added these pithy words on Nov 21 08 at 10:27 AM

Tanks, it work.

simon added these pithy words on Nov 21 08 at 7:13 PM

this has to be done by Zend_Auth/Zend_ACL.

Salman added these pithy words on Mar 05 09 at 1:25 PM

Hi,

Nice example. Thanks for sharing it.

I have more that one module like ‘admin’ , ‘technology’….

Now, how can I call this plugin in each module (all controllers)?

Is there a way to check HasAccess in bootstrap itself when we are in site.com/admin (or site.com/technology) like that?

Thanks in Advance.

PixelMaker added these pithy words on Mar 12 10 at 3:33 AM

@PixelMaker, I am not sure I understand your question. But I think you would want to check the module and check if it “admin”. If it is, then you would “restrict” the user.

Wenbert added these pithy words on Mar 12 10 at 4:49 AM

Add a Comment


XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">


© Copyright 2007 eKini Web Developer Blog . Thanks for visiting!