« Return to Thread: Zend_Acl / Zend_Auth example scenario
In the boostrap, you are using "$auth = Zend_Auth::getInstance();", but as far as i know, zend_Auth does not implement the singleton design pattern, no ?Have you made such implementation on your own ?Hi there allAfter 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/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 managementThis 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.*admin* (authenticated) - Unrestricted access.*Application layout:*-------------------Using the 'Conventional' layout that Gavin outlines in http://framework.zend.com/wiki/display/ZFDEV/Choosing+Your+Application%27s+Directory+LayoutThe bootstrap is located inside /htdocs/index.php*Bootstrap:*----------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:-class MyAcl extends Zend_Acl{public function __construct(Zend_Auth $auth){parent::__construct();$roleGuest = new Zend_Acl_Role('guest');$this->add(new Zend_Acl_Resource('home'));$this->add(new Zend_Acl_Resource('news'));$this->add(new Zend_Acl_Resource('tutorials'));$this->add(new Zend_Acl_Resource('forum'));$this->add(new Zend_Acl_Resource('support'));$this->add(new Zend_Acl_Resource('admin'));$this->addRole($roleGuest);$this->addRole(new Zend_Acl_Role('member'), 'guest');$this->addRole(new Zend_Acl_Role('admin'), 'member');// Guest may only view content$this->allow('guest', 'home');$this->allow('guest', 'news');$this->allow('guest', 'tutorials');$this->allow('member', 'forum');$this->deny('member', 'forum', 'update'); // Remove specific privilege$this->allow('member', 'support');$this->allow('admin'); // unrestricted access// Add authoring ACL check$this->allow('member', 'forum', 'update', new MyAcl_Forum_Assertion($auth));// NOTE: Dependency on auth object to allow getIdentity() for authenticated user object}}...and then this is added to the bootstrap. The final index.php file looks something like:-*Index.php*<?php// Initialise configuration / environment$config = new Zend_Config(new Zend_Config_Ini('../application/config/config.ini', 'live'));// Create sitemap from .ini using structure from example$sitemap = new Zend_Config(new Zend_Config_Ini('../application/config/sitemap.ini', 'live'));// Create db object and enable/disable debugging$db = Zend_Db::factory($config->db->connection, $config->db->asArray());...etc...// Create auth object$auth = Zend_Auth::getInstance();// Create acl object$acl = new MyAcl($auth); // see// Create router and configure (LIFO order for routes)$router = new Zend_Controller_RewriteRouter;...add rules...// Create view and register objects$view = new My_View;...init view...$front = Zend_Controller_Front::getInstance();$front->throwExceptions(true);$front->setRouter($router)->setDispatcher(new Zend_Controller_ModuleDispatcher())->registerPlugin(new My_Plugin_Auth($auth, $acl))->registerPlugin(new My_Plugin_Agreement($auth))->registerPlugin(new My_Plugin_View($view))->setControllerDirectory(array('default' => realpath('../application/controllers/default'),'admin' => realpath('../application/controllers/admin')))->setParam('auth', $auth)->setParam('view', $view)->setParam('config', $config)->setParam('sitemap', $sitemap)->dispatch();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.<?phpclass My_Plugin_Auth extends Zend_Controller_Plugin_Abstract{private $_auth;private $_acl;private $_noauth = array('module' => 'default','controller' => 'login','action' => 'index');private $_noacl = array('module' => 'default','controller' => 'error','action' => 'privileges');public function __construct($auth, $acl){$this->_auth = $auth;$this->_acl = $acl;}public function preDispatch($request){if ($this->_auth->hasIdentity()) {$role = $this->_auth->getIdentity()->getUser()->role;} else {$role = 'guest';}$controller = $request->controller;$action = $request->action;$module = $request->module;$resource = $controller;if (!$this->_acl->has($resource)) {$resource = null;}if (!$this->_acl->isAllowed($role, $resource, $action)) {if (!$this->_auth->hasIdentity()) {$module = $this->_noauth['module'];$controller = $this->_noauth['controller'];$action = $this->_noauth['action'];} else {$module = $this->_noacl['module'];$controller = $this->_noacl['controller'];$action = $this->_noacl['action'];}}$request->setModuleName($module);$request->setControllerName($controller);$request->setActionName($action);}}*Agreement.php*Many sites choose to enforce a set of terms and conditions to access. This intercepting plugin simply checks the Zend_Auth identity method hasAgreement() (for the sake of demonstration lets just say this is a boolean property that has been set in the user table of the database). Again, this is only enacted if an identity exists, and the request is redirected to a specific agreement controller/action.<?phpclass MyPlugin_Agreement extends Zend_Controller_Plugin_Abstract{private $_auth;private $_noagreement = array('module' => 'default','controller' => 'login','action' => 'agreement');public function __construct($auth){$this->_auth = $auth;}public function preDispatch($request){if ($request->controller != 'logout' && $this->_auth->hasIdentity()) {« Return to Thread: Zend_Acl / Zend_Auth example scenario
Free embeddable forum powered by Nabble Forum Help