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;
}
}
1 Answer 1
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.
-
\$\begingroup\$ As I've not read up on bootstrap resources, does this just create those objects and variables within the bootstrap automagically? \$\endgroup\$Glen Solsberry– Glen Solsberry2011年06月05日 15:45:28 +00:00Commented Jun 5, 2011 at 15:45
-
\$\begingroup\$ @gms8994: Yes :) (In a more unified way) \$\endgroup\$Fge– Fge2011年06月05日 16:18:30 +00:00Commented 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\$Glen Solsberry– Glen Solsberry2011年06月05日 16:42:09 +00:00Commented 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\$Fge– Fge2011年06月05日 17:34:59 +00:00Commented Jun 5, 2011 at 17:34