3
\$\begingroup\$

I've been working on putting a new app up against Zend. In my admin section, I want the nav links to only show if the user has rights to see the page. So I set up some Acls. But this doesn't seem like the right way to do things. What's the "right" way to do this stuff?

Bootstrap.php

<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
 protected $_aclAdapter;
 protected function _initDoctype() {
 $this->bootstrap('view');
 $view = $this->getResource('view');
 $view->doctype('XHTML1_STRICT');
 }
 protected function _initPlugins() {
 // First, set up the Cache
 $frontendOptions = array(
 'automatic_serialization' => true
 );
 if (! is_dir("/tmp/cache")) mkdir("/tmp/cache");
 $backendOptions = array(
 'cache_dir' => '/tmp/cache'
 );
 $cache = Zend_Cache::factory('Core',
 'File',
 $frontendOptions,
 $backendOptions);
 Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);
 $this->_aclAdapter = new My_Plugin_Acl();
 }
 protected function _initRoute() {
 /*
 $this->bootstrap('FrontController');
 $front = $this->getResource('FrontController');
 $router = $front->getRouter();
 $restRoute = new Zend_Rest_Route($front, array(), array('rest'));
 $router->addRoute('rest', $restRoute);
 return $router;
 */
 }
 protected function _initAutoloadModuleAdmin() {
 $autoloader = new Zend_Application_Module_Autoloader(
 array(
 'namespace' => 'Admin',
 'basePath' => APPLICATION_PATH . '/modules',
 )
 );
 return $autoloader;
 }
 protected function _initDatabase() {
 $database = Zend_Db_Table::getDefaultAdapter();
 return $database;
 }
 protected function _initAuthChecker() {
 $this->bootstrap('view');
 $view = $this->getResource('view');
 /**
 * @todo Determine how to pass Acl information to the view
 * so that links can be determined properly
 */
 $auth = Zend_Auth::getInstance();
 if ($auth->hasIdentity()) {
 $view->isLoggedIn = true;
 $aclAdapter = $this->_aclAdapter;
 $container = new Zend_Navigation();
 $pages = array(
 new Zend_Navigation_Page_Mvc(array('module' => 'admin', 'action' => 'index', 'controller' => 'make', 'label' => 'Makes')),
 new Zend_Navigation_Page_Mvc(array('module' => 'admin', 'action' => 'index', 'controller' => 'model', 'label' => 'Models')),
 new Zend_Navigation_Page_Mvc(array('module' => 'admin', 'action' => 'index', 'controller' => 'year', 'label' => 'Years')),
 new Zend_Navigation_Page_Mvc(array('module' => 'admin', 'action' => 'index', 'controller' => 'user', 'label' => 'Users')),
 );
 foreach ($pages as $page) {
 $resources = $aclAdapter->getCurrentResources($page);
 $allowedToViewResource = true;
 foreach ($resources as $resource) {
 // access to the module
 if(!$aclAdapter->isAllowed($aclAdapter->getRolesForIdentity($auth->getIdentity()->nickname), $resource, 'view')) {
 $allowedToViewResource = false;
 }
 }
 if ($allowedToViewResource) {
 $container->addPage($page);
 }
 $view->navigation = $container;
 }
 } else {
 $view->isLoggedIn = false;
 }
 }
}

My/Plugin/Acl.php

<?php
/**
 * Acl functionality
 *
 * @category My
 * @package My_Plugin
 * @author Glen Solsberry <[email protected]>
 */
class My_Plugin_Acl extends Zend_Controller_Plugin_Abstract {
 /**
 * @var Zend_Acl
 */
 protected $_acl = null;
 public function __construct() {
 $args = func_get_args();
 if (count($args) > 0) {
 $this->_acl = $args[0];
 } else {
 $this->_acl = new Zend_Acl();
 }
 $this->_acl = $this->setupRoles($this->_acl);
 $this->_acl = $this->setupResources($this->_acl);
 $this->_acl = $this->setupAllowances($this->_acl);
 }
 public function isAllowed($role, $resource, $function) {
 return $this->_acl->isAllowed($role, $resource, $function);
 }
 /**
 * preDispatch
 *
 * @param Zend_Controller_Request_Abstract $request
 * @return none
 */
 public function preDispatch(Zend_Controller_Request_Abstract $request) {
 if (Zend_Auth::getInstance()->hasIdentity()) {
 $identity = Zend_Auth::getInstance()->getIdentity();
 $role = $this->getRolesForIdentity($identity->nickname);
 } else {
 $role = 'guest';
 }
 $resources = $this->getCurrentResources($request);
 $allowedToViewResource = true;
 foreach ($resources as $resource) {
 // access to the module
 if(!$this->_acl->isAllowed($role, $resource, 'view')) {
 $allowedToViewResource = false;
 }
 }
 if ($allowedToViewResource === false) {
 //If the user has no access we send him elsewhere by changing the request
 // print sprintf("I'm sorry, but your role '%s' doesn't have access to the resource named '%s'<br>", $role, $resource);
 $request->setModuleName('')->setControllerName('auth')->setActionName('login');
 return false;
 }
 return true;
 }
 public function getCurrentResources($request) {
 if ($request instanceOf Zend_Controller_Request_Http) {
 $resources = array(
 $request->getModuleName(),
 implode("-", array($request->getModuleName(), $request->getControllerName())),
 implode("-", array($request->getModuleName(), $request->getControllerName(), $request->getActionName())),
 );
 } else if ($request instanceOf Zend_Navigation_Page_Mvc) {
 $resources = array(
 $request->getModule(),
 implode("-", array($request->getModule(), $request->getController())),
 implode("-", array($request->getModule(), $request->getController(), $request->getAction())),
 );
 } else {
 throw new Zend_Exception('Unsupported request type');
 }
 return $resources;
 }
 /**
 * Gets available roles for an identity, by nickname
 * @param string $nickname
 */
 public function getRolesForIdentity($nickname) {
 if ($nickname === "") {
 return 'guest';
 }
 return 'admin';
 }
 private function setupRoles(Zend_Acl $acl) {
 $acl->addRole(new Zend_Acl_Role('guest'));
 // make-admin has all rights that guest has
 $acl->addRole(new Zend_Acl_Role('make-admin'), 'guest');
 $acl->addRole(new Zend_Acl_Role('model-admin'), 'guest');
 $acl->addRole(new Zend_Acl_Role('year-admin'), 'guest');
 $acl->addRole(new Zend_Acl_Role('user-admin'), 'guest');
 // admin has all rights that year-admin, make-admin, user-admin and model-admin have
 $acl->addRole(new Zend_Acl_Role('admin'), array('year-admin', 'make-admin', 'model-admin', 'user-admin'));
 return $acl;
 }
 private function setupResources(Zend_Acl $acl) {
 // setup admin resources
 $this->setupResourcesFromArrays(
 $acl,
 array('admin'),
 array('index','make','year','model','user'),
 array('index','update','delete')
 );
 $this->setupResourcesFromArrays(
 $acl,
 array('default'),
 array('auth','index','error'),
 array('index')
 );
 $this->setupResourcesFromArrays(
 $acl,
 array('default'),
 array('auth'),
 array('logout')
 );
 $this->setupResourcesFromArrays(
 $acl,
 array('default'),
 array('favicon.ico'),
 null
 );
 return $acl;
 }
 private function setupResourcesFromArrays($acl, $modules, $controllers, $actions) {
 foreach ($modules as $module) {
 $resource_name = $module;
 $this->addResource($acl, $resource_name);
 foreach ($controllers as $controller) {
 $resource_name = sprintf('%s-%s', $module, $controller);
 $this->addResource($acl, $resource_name);
 if (is_array($actions)) {
 foreach ($actions as $action) {
 $resource_name = sprintf('%s-%s-%s', $module, $controller, $action);
 $this->addResource($acl, $resource_name);
 }
 }
 }
 }
 }
 private function addResource($acl, $resource_name) {
 if (! $acl->has($resource_name)) {
 $acl->addResource(new Zend_Acl_Resource($resource_name));
 }
 }
 private function setupAllowances(Zend_Acl $acl) {
 // role, resource, privilege
 $acl->allow('admin', 'admin', 'view');
 $acl->allow('admin', 'admin-index', 'view');
 $acl->allow('admin', 'admin-index-index', 'view');
 /**
 SNIP
 */
 $acl->allow('guest', 'default', 'view');
 $acl->allow('guest', 'default-index', 'view');
 $acl->allow('guest', 'default-index-index', 'view');
 $acl->allow('guest', 'default-auth', 'view');
 $acl->allow('guest', 'default-auth-index', 'view');
 return $acl;
 }
}
asked Jun 3, 2011 at 20:58
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

You should use the available bootstrap resources where possible:

protected function _initPlugins() {
 // First, set up the Cache
 $frontendOptions = array(
 'automatic_serialization' => true
 );
 if (! is_dir("/tmp/cache")) mkdir("/tmp/cache");
 $backendOptions = array(
 'cache_dir' => '/tmp/cache'
 );
 $cache = Zend_Cache::factory('Core',
 'File',
 $frontendOptions,
 $backendOptions);

could be replaced by without any functions in your bootstrap:

resources.cachemanager.yourcachename.frontend.name = Core
resources.cachemanager.yourcachename.options.automatic_serialization = true
resources.cachemanager.yourcachename.backend.name = File
resources.cachemanager.yourcachename.backend.options.cache_dir = "/tmp/cache"

Finally, you activate the metadatacache for your database:

resources.db.defaultMetadataCache = "yourcachename"

Same can be done with your view:

protected function _initDoctype() {
 $this->bootstrap('view');
 $view = $this->getResource('view');
 $view->doctype('XHTML1_STRICT');
}
// can be replaced with:
resources.view.doctype = "XHTML1_STRICT"

Assuming you're setting up the database in your config, you probably don't need this line:

protected function _initDatabase() {
 $database = Zend_Db_Table::getDefaultAdapter();
 return $database;
}

You can do the same for your autoloader and routes as well.
This probably should be a plugin on it's own:

protected function _initAuthChecker() 
{
 /* .... */
}

So far on your bootstrap. I can see you're take your modules/controllers as resources. Usually I don't aggree with this: a controller handles a set of models, which are the resources. I know this doesn't make the navigation easier ;) You should have a look at the module-bootstraps too. They take module-specific bootstraping away from your main-bootstrap.

answered Jun 4, 2011 at 9:46
\$\endgroup\$
4
  • \$\begingroup\$ As I've not read up on bootstrap resources, does this just create those objects and variables within the bootstrap automagically? \$\endgroup\$ Commented Jun 5, 2011 at 15:45
  • \$\begingroup\$ @gms8994: Yes :) (In a more unified way) \$\endgroup\$ Commented Jun 5, 2011 at 16:18
  • \$\begingroup\$ "So far on your bootstrap. I can see you're take your modules/controllers as resources. Usually I don't aggree with this: a controller handles a set of models, which are the resources. I know this doesn't make the navigation easier ;) You should have a look at the module-bootstraps too. They take module-specific bootstraping away from your main-bootstrap." can you detail what you mean about this a little more? Thanks! \$\endgroup\$ Commented Jun 5, 2011 at 16:42
  • \$\begingroup\$ See Zend_Application_Resources_Modules and weierophinney.net/matthew/archives/… for module-bootstraping. For the controller/model stuff... this is way too much to explain it in a comment ;) survivethedeepend.com/zendframeworkbook/en/1.0 has a great explanation of both topics (you probably should read even more about it then :) \$\endgroup\$ Commented Jun 5, 2011 at 17:34

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.