4.3. Plugins

These are the Controller Plugins included in the Xyster distribution. For information about Controller Plugins themselves, see the documentation for Controller Plugins.

4.3.1. Xyster_Controller_Plugin_Acl

The ACL plugin is used to grant roles access to MVC dispatch locations. Given a Zend_Acl or Xyster_Acl, you can use this plugin to allow or deny a role access to an entire module, just a controller, or a single action. If the authenticated user tries to access a location that is forbidden, the plugin forwards them to an error action.

4.3.1.1. Setting the Error and Unauthenticated Locations

Like Zend's ErrorHandler plugin, the default is ErrorController::errorAction() in the default module. We made the request variables the plugin makes available to that action identical to those set up by ErrorHandler to provide reuse. You can change the forward location if you desire.

<?php
$module = 'default';
$controller = 'index';
$action = 'error';
/* @var $plugin Xyster_Controller_Plugin_Acl */
$plugin->setAccessDenied($module, $controller, $action);

The plugin registers a preDispatch hook to verify access. If the authenticated user isn't allowed to access the action about to be dispatched, the plugin forwards them to the error location specified.

[Tip] Tip

This plugin assumes that the currently authenticated Zend_Auth identity has been added as a role to the ACL. Xyster_Controller_Plugin_Auth when used will add the current authenticated role to the ACL during the routeStartup event.

If an action is denied but the user isn't authenticated (meaning Zend_Auth doesn't have an identity), the plugin will forward them to a location where they should be prompted for credentials. By default, this is the 'index' action of the 'login' controller in the default module. This can be changed by using the setLogin method.

<?php
$module = 'default';
$controller = 'index';
$action = 'login';
/* @var $plugin Xyster_Controller_Plugin_Acl */
$plugin->setLogin($module, $controller, $action);

The login action should process the the user-supplied credentials and authenticate the user.

[Tip] Tip

If you use Xyster_Controller_Plugin_Auth, you can set the adapter it uses within your login action. Doing so will automatically authenticate the user and assign the role to the ACL.

4.3.1.2. Adding Rules

You can set up the access control rules using the allow, deny, and setRules methods on the plugin. The rules must be defined before the Front Controller dispatches the request. Remember that the role used in the rule must be defined in the ACL before you can add the rule. The following example allows the authenticated user access to all actions in the 'foo' controller of the 'default' module.

<?php
$acl = new Zend_Acl;
$identity = Zend_Auth::getInstance()->getIdentity();
$acl->addRole(new Zend_Acl_Role($identity);
$plugin = new Xyster_Controller_Plugin_Acl($acl);
$plugin->allow($identity, 'default', 'foo');
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin($plugin);
[Note] Note

The ACL Plugin stores the dispatch locations as Xyster_Controller_Request_Resource objects. Each one represents a module, controller, or action. They are added hierarchically to the ACL. Therefore, adding a rule for access to the "bar" action in the "foo" controller in the "default" module will add three resources to the ACL: /default, /default/foo, and /default/foo/bar.

You can add as many rules as you want using the fluent interface of the allow method.

<?php
$plugin->allow('admin', null) // admin can access everything
	->allow('moderators', 'forum', 'mod') // moderators can access the 'mod' controller
	->allow(null, 'forum', 'view') // all users can access the 'view' controller
	->deny('trolls'); // all forum trolls should be forbidden

You can also add multiple rules at once by passing an array to the setRules method. Each element in the array must be an array with several keys.

  1. "type" : either Zend_Acl::TYPE_ALLOW or Zend_Acl::TYPE_DENY

  2. "role" : the string role ID

  3. "module" : the module name

  4. "controller" : the controller name

  5. "action" : the action name

If 'type' is omitted or is null, Zend_Acl::TYPE_ALLOW is assumed. If any of the other keys are omitted, null is assumed.

<?php
$rules = array(
    array('role'=>'admin'),
    array('role'=>'moderators', 'module'=>'forum', 'controller'=>'mod'), 
    array('module'=>'forum', 'controller'=>'view'),
    array('type'=>Zend_Acl::TYPE_DENY, 'role'=>'trolls')
);
/* @var $plugin Xyster_Controller_Plugin_Acl */
$plugin->setRules($rules);

4.3.2. Xyster_Controller_Plugin_Auth

The main purpose of the authentication plugin is to authenticate the current user and register it with a supplied ACL. The plugin registers a routeStartup hook so authentication can take place before the MVC system does any real work.

The plugin can be given an auth adapter with the setAuthAdapter method that can be used to authenticate the user. The adapter will only be used if Zend_Auth doesn't have the identity of the user. If your adapter doesn't take any arguments or credentials from the user, it is acceptable to supply the adapter immediately after the plugin is constructed.

<?php
$adapter = new MyAuthAdapter;
$plugin = new Xyster_Controller_Plugin_Auth;
$plugin->setAuthAdapter($adapter);

If the user must supply credentials for authentication, the adapter can and should be created and supplied within a controller action that processes the user's credentials.

<?php
public function loginAction()
{
	if ( $this->getRequest()->isPost() ) {
		$username = $this->_getParam('username');
		$password = $this->_getParam('password');
		$adapter = new MyAuthAdapter($username, $password);
		$plugin = $this->getFrontController()->getPlugin('Xyster_Controller_Plugin_Auth');
		$plugin->setAuthAdapter($adapter);
		return;
	} else {
		// show login form
	}
}

4.3.2.1. The Success and Failure Actions

At the routeStartup event or when given an adapter, the plugin will try to authenticate the user if an identity has not been stored. If the authentication fails, the user will be forwarded to a login failure action. By default, this is the 'index' action of the 'login' controller in the default module. You can change this using the setFailure method.

<?php
$module = 'default';
$controller = 'index';
$action = 'login';
/* @var $plugin Xyster_Controller_Plugin_Auth */
$plugin->setFailure($module, $controller, $action);

A Zend_Auth_Result object will be passed to this action as the result parameter.

If the authentication succeeds, the user will be forwarded to a login success action. By default, this is the 'success' action of the 'login' controller in the default module. You can change this using the setSuccess method.

<?php
$module = 'default';
$controller = 'index';
$action = 'myAccount';
/* @var $plugin Xyster_Controller_Plugin_Auth */
$plugin->setSuccess($module, $controller, $action);

4.3.2.2. Getting the Identity's Role

Once the user is authenticated, the plugin will then retrieve and store a role that represents the authenticated user. By default, Xyster_Acl_Role_Provider will be used, but you can define your own provider using the setRoleProvider method. If an ACL object is supplied, the plugin will take the role it got from the provider and add it to the ACL.

<?php
$acl = new Zend_Acl;
$authAdapter = new MyAuthAdapter();
$plugin = new Xyster_Controller_Plugin_Auth($authAdapter, $acl);
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin($plugin);

Once authentication takes place, the role generated for the current user is available from the getRole method.

4.3.3. Xyster_Controller_Plugin_Cache

The Cache plugin is responsible for one thing: providing the correct cache control headers.

Common problem: During a started session, PHP will by default send several anti-caching headers along with the response. Simply including this plugin class turns off the configuration setting that enables this. This plugin should be included before the session is started or any output is sent to the browser.

The Cache plugin registers a dispatchLoopShutdown hook so that the correct cache headers can be sent. There are four scenarios:

  1. You haven't sent any cache-control headers; default anti-caching headers will be sent as well as the content-length of the response body.

  2. You've sent cache-control headers; only the content-length of the response body will be sent.

  3. You've sent a content-type header signifying a file transfer; no headers will be sent.

  4. The response is a redirect; no headers will be sent.