1

What is the best method in Zend Framework to provide restricted areas and redirect users to a login page? What I want to do is set a flag on my controllers for restricted pages:

class AdminController extends Zend_Controller_Action
{
    protected $_isRestricted = true;
    ....

and have a plugin check to see if the controller is restricted and if the user has authenticated, otherwise redirect them to the login page. If I do this directly in the controller's preDispatch I can use $this->_redirect(), but looking at Action Helpers they won't have access to that. It's also a lot of duplicate code to copy/paste the authentication check code in every controller that needs it.

Do I need an Action Controller linked to preDispatch, or a Front Controller plugin? How would I do the redirect and still preserve things like the base URL?

dragonmantank
  • 15,243
  • 20
  • 84
  • 92

3 Answers3

4

Use Zend_Acl (best combined with Zend_Auth)

See also Practical Zend_ACL + Zend_Auth implementation and best practices

Community
  • 1
  • 1
vartec
  • 131,205
  • 36
  • 218
  • 244
1

For one project, I've extended Zend_Controller_Action, and in that class's preDispatch put a check for logged-in-ness. I can override it on a per-action basis with an init() that checks the actionname and turns off the requirement (or preDispatch() that calls it's parent for the actual checks).

Alister Bulman
  • 34,482
  • 9
  • 71
  • 110
1

In a project I was working on, I had trouble with various users experiencing timeouts from their browsers. This meant the Zend_Auth no longer existed in the registry and users lost access to required pages/functions.

In order to stop this from occuring, I setup a Plugin (as you suggest) and have this plugin perform checks in the preDispatch(). An example is below:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    public function run()
    {
        $front  = Zend_Controller_Front::getInstance();
        $front->registerPlugin(new App_Controller_Plugin_Timeout());
        parent::run();
    }
}

with the timeout class implementing any Zend_Auth or Zend_Acl requirements, using a check via the function below.

class App_Controller_Plugin_Timeout extends Zend_Controller_Plugin_Abstract
{
    /**
     * Validate that the user session has not timed out.
     * @param Zend_Controller_Request_Abstract $request
     * @return void
     * @todo Validate the user has access to the requested page using Zend_Acl
     */
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        $frontController = Zend_Controller_Front::getInstance();
        $controllerName  = $frontController->getRequest()->getControllerName();
        $actionName      = $frontController->getRequest()->getActionName();
        $authInstance    = Zend_Auth::getInstance();

        /** If the controller is not the Auth or Error controller, then check for
         *  a valid authorized user and redirect to the login page if none found */
        if (($controllerName !== 'auth') && ($controllerName !== 'index') && ($controllerName !== 'error')) {
            if (!$authInstance->hasIdentity()) {
                $this->_response->setRedirect('/index/timeout')->sendResponse();
                exit;
            }
        } else if (($controllerName == 'index') || (($controllerName == 'auth') && ($actionName !== 'logout'))) {
            /** If running the Auth or Index (default) controller (and not the logout
             *  action), check if user already signed in and redirect to the welcome page */
            if ($authInstance->hasIdentity()) {
                $this->_response->setRedirect('/general/welcome')->sendResponse();
                exit;
            }
        }
    }
}

....

/**
 * Test that the input user belongs to a role based on the user input and
 * the values loaded into the Acl registry object setup when the site first
 * loads
 *
 * @param   mixed|Zend_Auth $userData
 * @param   string          $userRole
 * @return  boolean
 * @throws  Zend_Exception  When invalid input is provided
 */

public function isUserMemberOfRole($userData, $userRole)
{
    if (empty($userData)) {
        $auth = Zend_Auth::getInstance();
        if($auth->hasIdentity()) {
            $userData = $auth->getIdentity();
        } else {
            return FALSE;
        }
    }

    if (!is_string($userRole)){
        throw new Zend_Exception('Invalid input provided to ' . __METHOD__);
    }

    // Setup the required variables and access the registry for the Acl values
    $rolesTable = new App_Model_Internal_UsersToRoles();
    $registry   = Zend_Registry::getInstance();
    $acl        = $registry->get('acl');
    $roles      = $rolesTable->getUserRoles($userData); // returns an array of values

    foreach ($roles as $value) {
        if ($value['Name'] == $userRole) {
            return $acl->isAllowed($value['Name'], null, $userRole);
        }
    }
}

I had the user access implemented in a database table and then initialized as an "_init" function at Bootstrap->run() as follows:

protected function _initAclObjectForUserRoles()
{
    $userTable = new App_Model_Internal_Roles();
    $acl       = new Zend_Acl();

    $userRoles = $userTable->fetchAll();
    $roles     = $userRoles->toArray();

    // Cycle through each Role and set the allow status for each
    foreach($roles as $value) {
        $department = $value['Name'];
        $acl->addRole(new Zend_Acl_Role($department));
        $acl->allow($department, null, $department);
    }

    // Add the new Acl to the registry
    $registry = Zend_Registry::getInstance();
    $registry->set('acl', $acl);
}

So, using this method you could put access restrictions via the roles loaded via from a database into an Zend_Acl object, or you could load the controller class attribute via the Timeout plugin and check it's value. Although, I've found it's easier to maintain access policies in a database than spread them throughout your code base... :-)

nath77
  • 11
  • 3