After submitting the initial example of how Zend_Auth and Zend_Acl could be implemented Gavin pointed out areas that weren't really addressed in my proof of concept and it could potentially confuse newcomers to the way MVC is utilised. I'd like to clarify that post to a) Address those concerns and b) see if there's any constructive criticism of the process that could benefit everyone.
Requirements:
-------------
Demonstrate a web environment where 'public' (i.e. non-authenticated) users and 'member' users have access restrictions, and to what context they may visit those resources. In a lot of ways this broad concept relates very well to small-medium sites of a lot of Zend developers (in my opinion). For purpose of clarity we will assume this is a SIG group for Mac Users to discuss all things Mac OS X-related. The site has 3 areas (home, news, tutorials) that are for the general public. Members can also view a discussion forum, community newsletter and support request area for members to share common problems.
Site layout
-------------
Expressed as :controller/:action notation:-
/home
/news/index
/view
/email
/tutorials/index
/view
/forum/index
/category
/view
/add
/update
/reply
/search
/report - report abuse, etc.
/support/index
/view
/search
/submit
/confirmation -
/comment - add comment
/login/index - handles form processing and auth processing
/logout/index - destroys current auth instance
/error/noroute - handles all 404s
/failure - handles 'Site error' messages
/privileges - handles 'You are not privileged...' messages
/admin - a cms to handle all site management
This loosely illustrates the site functionality and content - for the sake of brevity we'll assume that the general concepts and operations of these site functions are understood and familiar. What we're interested in is how to handle user authentication and then access, but at least this gives us some 'real world' understanding of what is required.
Access rules:
-------------
Three types of user 'roles' have been identified for the site:-
guest (not authenticated) - Guests can access 'home', 'news' and 'tutorials' only. Guests attempting to access member-only content will be asked to authenticate.
member (authenticated) - Access all top-level controllers. Can update forum posts but only those authored by themselves. Not allowed access to admin section. Access to 'admin' will result in 'privileges' error message.
The bootstrap takes care of the usual suspects - Db, View, Config, Log, Router - and stores them inside the Zend_Front_Controller so that they can be accessed via each controller using the getInvokeArg() method. This negates the need for an extra registry object and (hopefully) makes the dependencies somewhat easier to track.
To satisfy the needs of the Access rules, we create a subclassed instance of Zend_Acl like so:-
This is a pretty standard (IMO) bootstrap - the areas to note for the purpose of Authentication/Acl are the two first plugins:-
Auth.php
The purpose of this plugin is to first determine the 'role' of the current Auth identity. If Zend_Auth::getIdentity() returns false then we don't have a 'role' for the identity, so we assume 'guest'. If a user is authenticated, the Zend_Auth identity would be returned as an object and we would extract the role from this. For simplicity's sake, let's assume that the 'role' is stored in a MySQL database and is returned as a public property from the Identity object (i.e. 'member' or 'admin').
The 'role' is then a one-to-one match against the Acl rules. If we interrogate the Acl and we are allowed to view the current controller (maps to the 'resource' id given to each Acl resource) then the dispatcher continues on its merry way.
If the Acl denies the access, we then determine if the user has a valid identity. If not, we tell the request object that we want to redirect to a new controller (login) to perform a login. At this stage, no request data is required - this will be handled via a form in the LoginController.
If, however, the identity is valid then we know that access if definitely blocked for that user and we send the request to the 'error' controller to display the 'no privleges' error.
I've chosen this strategy as it means that none of the controllers need know anything about the ACL process - they can assume that access to the action has been already approved and need only check action-specific privilege checks (e.g. ensuring they view valid articles, forum threads, etc.)
However a developer could still choose to add further ACL rules if required and reduce the amount of ACL-related 'clutter' in the controllers themselves.
<?php
class My_Plugin_Auth extends Zend_Controller_Plugin_Abstract