Zend_Acl / Zend_Auth example scenario
by Simon Mundy
Hi there all
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.-
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+Layout
The 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(new Zend_Acl_Role('guest'));
$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.
<?php
class 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'];

Comments
It's a good tutorial but we have some problems...
1- getinstance not exist in Zend_Auth, where is it ?
2- The good route controller is Zend_Controller_ModuleRewriteRouter and not Zend_Controller_RewriteRouter
Depending on the results of current design discussions, we may change
Zend_Auth to follow a Singleton pattern. The getInstance() method would be used to this, and Ralph's proof-of-concept code includes this approach.
Well, good news :)
The Singleton approach is a good idea. Will this approach be operational for 0.8 or 0.9?
The fw-auth mailing list archive:
http://www.nabble.com/Zend-Auth-f16181.html
To join fw-auth, send a blank email to:
fw-auth-subscribe@lists.zend.com
Your feedback would be highly appreciated - thank you!
this is my mail quannm0410l@yahoo.com. Thanks alot!
I'm also a bit vague on what the auth is actually looking at to check usernames and passwords.
Also, did your tutorial get cut off? It just sort of...ends...in the middle of your plugin code.
Thank you,
David Branco
<?php
class 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.
<?php
class 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()) {
if (!$this->_auth->getIdentity()->getUser()->hasAgreement()) {
$request->setModuleName($this->_noagreement['module']);
$request->setControllerName($this->_noagreement['controller']);
$request->setActionName($this->_noagreement['action']);
}
}
}
}
Login (Authentication)
The act of authentication - in my app - all happens within a domain model - MyForm_Login. Using Matt Zandstra's excellent reference on Observers/Observable at zend.com as a starting point I have created a form object that extends the PEAR HTML_Quickform component to allow one or more observers to be added to the form and activated upon validation.
The form is constructed (and auto-populated in my domain-specific instance with form elements like 'Username', 'Password' and a 'Remember Me' checkbox), then several observers are added to it.
When a form validates, the observers are all notified and given an instance of the form values and the Zend_Auth instance. From there, it is simply a matter of checking the sanitised form values (we've applied our form filters, right? :) and passing them to a domain-specific Zend_Auth_Identity object to query the database, perform a lookup and then either spit out an error message or start the login session.
The example below would also create a hypothetical log observer to record the login time, date, details, etc.
BTW, in case you're wondering why the $view->render() isn't called, it's because I generally have a View_Plugin that's registered in the the Front_Controller that kicks in during dispatch shutdown. It allows me to incrementally add components/properties to the view as the dispatcher loops through all the application actions.
LoginController.php
class LoginController extends Zend_Controller_Action
{
public function indexAction()
{
$auth = $this->getInvokeArg('auth');
$view = $this->getInvokeArg('view');
if ($auth->hasIdentity()) {
$this->_redirect('/home/index'); // Already authenticated? Navigate away
}
$form = new MyForm_Login(); // creates all fields, adds filters, etc...
$form->attach(new MyPlugin_Login_User($auth); // Perform login of user identity
$form->attach(new MyPlugin_Login_Log($auth); // Perhaps log the event?
if ($form->validate()) {
$this->_redirect('/home/index');
}
// Render page
$this->getInvokeArg('view')->title = 'Login';
$this->getInvokeArg('view')->template = 'login/index.tpl';
$this->getInvokeArg('view')->form = $form->render();
}
public function agreementAction()
{
$auth = $this->getInvokeArg('auth');
$view = $this->getInvokeArg('view');
$form = new MyForm_Agreement();
$form->attach(new MyPlugin_Agreement_User($auth));
if ($form->validate()) {
$this->_redirect('/home/index');
}
// Render page
$this->getInvokeArg('view')->title = 'Agreement';
$this->getInvokeArg('view')->template = 'login/agreement.tpl';
$this->getInvokeArg('view')->form = $form->render();
}
}
User.php
class MyPlugin_Login_User implements Observer
{
function notify($form)
{
$auth = $this->_auth;
$values = $form->exportValues();
$adapter = new MyAuth_Adapter();
$adapter->setUsername($values['username']);
$adapter->setPassword($values['password']);
try {
$auth->authenticate($adapter);
} catch (MyAuth_Adapter_Exception_Missing $e) {
// Let form know that login has failed...
} catch (MyAuth_Adapter_Exception_Locked $e) {
// Let form know that login has failed...
}
if (!$auth->isAuthenticated()) {
// Let form know that password was incorrect or your account is not active...
}
$identity = $auth->getIdentity();
// Retrieve row of user info and store inside Identity object (including role!)
$userTable = new MyUser_Table; // Instance of Zend_Db_Table or similar...
$identity->setUser($userTable->find($identity->getIdentifier()));
}
}
Conclusions:
-------------
This is obviously an over-simplified example that attempts to address the challenges of the Acl/Auth components in relation to the MVC components.
I believe this approach - though needing some further bulletproofing - demonstrates good practice and encourages the developer to think about logical and clean ways of separating the process of authentication. Some benefits are:-
* You could easily drop in ACL rules in the one point and have a more complex and rich set of rules without needing any update to your controllers, relieving a lot of maintenance issues.
* Processing user input happens only in a single point in an area of the application that makes it more natural for developers to understand and build upon. The filters and validation take place in a separate form model that can be replaced/updated without affecting any other portion of the process. Post-login business rules can be added without touching other plugins or the bootstrap.
I hope this is useful! Would very much appreciate feedback - even if to say I'm doing it all wrong! :-) I'm still learning a lot from listening to the discussions on this list and I'm keen to find out how others approach this kind of layer in their own applications.
I've tried to follow this "tutorial", but it doesn't work.
My Acl class begin like this :
class My_Acl extends Zend_Acl {
public function __construct(Zend_Auth $auth) {
parent::__construct();
$roleGuest = new Zend_Acl_Role('guest');
$this->add(new Zend_Acl_Resource('index'));
But when I do "$acl = new My_Acl($auth);" I get this error :
Fatal error: Can not call constructor in **the file** on line 4 (which is parent::__construct())
And if I comment this line, I get this error :
Fatal error: Class 'Zend_Acl_Role' not found in ...
Any help ?
(excuse me for my english)
Just wanted to post to let everyone know that calling parent::__construct() on a class that extends Zend_Acl will result in fatal error because Zend_Acl does not have a constructor.
So, for those of you following this old article verbatim, beware that much has changed for both Zend_Auth and Zend_Acl since this writing.
Best regards,
Darby
<p>I have to say that while I grasp the majority of the Zend Framework, Zend_Acl and Zend_Auth are really messing with my head.</p>
<p>As all examples I have seen use the Zend_Controller, I've abandoned adapting our legacy apps to use Zend_Auth (it was relatively straightforward to change these same applications to use the majority of the framework however).</p>
<p>Our primary project is being repurposed to use Zend Framework (and, indeed, Zend Platform) so now seems the opportune time to try and integrate Zend_Auth and Zend_Acl.</p>
<p>The main issue which I spent most of the week sorting out was a separate storage engine for Zend_Auth. Our applications allow for anonymous users to go all over our site, but we still need to know where they are and so forth. We therefore have a memory table which stores their current page, their pseudo session ID, and so forth. If they are anonymous, their user ID is -1. The storage engine interprets isEmpty() as:</p>
<p class="style1"> public function isEmpty()<br />
{<br />
$this->_init();<br />
return !(bool) $this->read();<br />
}<br />
<br />
public function read()<br />
{<br />
$this->_init();<br />
$id = $this->_sessionData->getUserId();<br />
return ($id == self::USER_ANONYMOUS) ? false : $id;<br />
}</p>
<p>This allows for hasIdentity() to correctly interpret them as being public users and not registered.</p>
<p>However, I have two points.</p>
<p>In reality, I really only want my storage engine to have the basic information - serializing objects in sessions isn't great. Secondly, as the majority of users will be anonymous, what I would ideally do is have User::factory($auth) method (which under Zend_Cache_Frontend_Class I could cache for performance). </p>
<p>This on the face of it seems simple enough, but no one seems to be doing this, so I'm wondering if I'm missing a trick.</p>
<p>Finally, I'm also having problems with the concept of Zend_Acl. Previously to reading up on Zend_Acl, I had wanted to use User::factory to return the relevant class of user to the needs of the page. It also had the advantage that there was <em>no way </em>to become, say, an administrator, unless the factory had chosen M4DC_User_Admin as the subclass to return. All dangerous functions weren't even in your base class. However, it strikes me this approach is incorrect, as a) you may be loading functions that a user doesn't need on particular pages, b) subclassing is not a way to protect an application.</p>
<p>So I'm thinking of other ways to look at the problem. One of the issues is that I don't particularly want to assign the Acl for all parts of the site directly - I'd like for a particular controller to decide its level of authority. Also it makes more sense to me to write $user->post($body) then $page->post($user, $body).</p>
<p>Any thoughts? <br />
Best regards Thomas
Also I suggest to use $request->getters to set the module/controller and action vars (it's just cleaner that way) and, more importantly, you have got to use setters after having modified these vars ($request->setAction()). Otherwise the request just won't be redirected ;-)
Hope did help ;-)
I am trying to wrap my mind around the ZF and this example helped a lot. I tried the code and works fine. Just one thing, it breaks the handling of invalid module or controller or action (HTTP 404).
I believe this is because of this plugin being a Zend_Controller_Plugin with a preDispatch hook which redirects to a login page or an error page if Acl->isAllowed() returns false (which is also true for all invalid paths).
So I used Zend_Controller_Action_Helper to implement this logic. So far my errorHandler seems to be working fine (redirecting to a 404 error page for invalid requests) and for valid requests the helper kicks in and checks the privileges.
Am I on the right track? Any comments on this?
As I already said, this code doesn't break anything as far as the zf logic is concerned and, normally, the errorHandler works just fine with the plugin... After all that's what the aop "plugin" pattern is supposed to do, using a helper for this kinda breaks the beauty of the idea.
Once again here's how it works...
1. the $front->throwExceptions(true) statement is off => no error will be thrown by the controller;
2. prior to the dispatch of the request, the plugin's method "preDispatch($request)" will be called. At this point, nothing will happen because the controller doesn't do anything yet! It's only when the auth_rights are determined, and the request forwarded to the right action (valid or not), that the controller will dispatch the request.
3. If the dispatched request contains an invalid url, an error will be thrown (but not shown on the screen!) and caught by the errorHandler which in turn will redispatch the request to the error action.
So if if it doesn't work, you probably missed something.
As I already said, this code doesn't break anything as far as the zf logic is concerned and, normally, the errorHandler works just fine with the plugin... After all that's what the aop "plugin" pattern is supposed to do, using a helper for this kinda breaks the beauty of the idea.
Once again here's how it works...
1. the $front->throwExceptions(true) statement is off => no error will be thrown by the controller;
2. prior to the dispatch of the request, the plugin's method "preDispatch($request)" will be called. At this point, nothing will happen because the controller doesn't do anything yet! It's only when the auth_rights are determined, and the request forwarded to the right action (valid or not), that the controller will dispatch the request.
3. If the dispatched request contains an invalid url, an error will be thrown (but not shown on the screen!) and caught by the errorHandler which in turn will redispatch the request to the error action.
So if if it doesn't work, you probably missed something.
As I already said, this code doesn't break anything as far as the zf logic is concerned and, normally, the errorHandler works just fine with the plugin... After all that's what the aop "plugin" pattern is supposed to do, using a helper for this kinda breaks the beauty of the idea.
Once again here's how it works...
1. the $front->throwExceptions(true) statement is off => no error will be thrown by the controller;
2. prior to the dispatch of the request, the plugin's method "preDispatch($request)" will be called. At this point, nothing will happen because the controller doesn't do anything yet! It's only when the auth_rights are determined, and the request forwarded to the right action (valid or not), that the controller will dispatch the request.
3. If the dispatched request contains an invalid url, an error will be thrown (but not shown on the screen!) and caught by the errorHandler which in turn will redispatch the request to the error action.
So if if it doesn't work, you probably missed something.
As I already said, this code doesn't break anything as far as the zf logic is concerned and, normally, the errorHandler works just fine with the plugin... After all that's what the aop "plugin" pattern is supposed to do, using a helper for this kinda breaks the beauty of the idea.
Once again here's how it works...
1. the $front->throwExceptions(true) statement is off => no error will be thrown by the controller;
2. prior to the dispatch of the request, the plugin's method "preDispatch($request)" will be called. At this point, nothing will happen because the controller doesn't do anything yet! It's only when the auth_rights are determined, and the request forwarded to the right action (valid or not), that the controller will dispatch the request.
3. If the dispatched request contains an invalid url, an error will be thrown (but not shown on the screen!) and caught by the errorHandler which in turn will redispatch the request to the error action.
So if if it doesn't work, you probably missed something.
Let me put it this way; as you mentioned:
2. prior to the dispatch of the request, the plugin's method "preDispatch($request)" will be called. At this point, nothing will happen because the controller doesn't do anything yet! It's only when the auth_rights are determined, and the request forwarded to the right action (valid or not), that the controller will dispatch the request.
Here is the problem. When the controller is invalid or not in the resource list (from the code):
if (!$this->_acl->has($resource)) {
$resource = null;
}
the $resource becomes null. Since Zend_Acl has a white list approach, unless you are 'admin' with unrestricted access to all resources:
if (!$this->_acl->isAllowed($role, $resource, $action))
becomes true (i.e. not authorized) redirecting to "privilege error" (or "login") page before errorHandler can do its job.
Implementing the same logic in a Zend_Controller_Action_Helper gives errorHandler the chance to do its job since it appears the action helper's preDispatch hook kicks in after the request is dispatched to a controller not before as in the case of a Zend_Controller_Plugin's preDispatch hook.
Also I don't see how action helpers break the beauty of the oop "plugin" pattern. They could very well be named as Zend_Controller_Action_Plugin since that is what they are; a plugin to an action.
Let me put it this way; as you mentioned:
2. prior to the dispatch of the request, the plugin's method "preDispatch($request)" will be called. At this point, nothing will happen because the controller doesn't do anything yet! It's only when the auth_rights are determined, and the request forwarded to the right action (valid or not), that the controller will dispatch the request.
Here is the problem. When the controller is invalid or not in the resource list (from the code):
<code>
if (!$this->_acl->has($resource)) {
$resource = null;
}
</code>
the $resource becomes null. Since Zend_Acl has a white list approach, unless you are 'admin' with unrestricted access to all resources:
<code>
if (!$this->_acl->isAllowed($role, $resource, $action))
</code>
becomes true (i.e. not authorized) redirecting to "privilege error" (or "login") page before errorHandler can do its job.
Implementing the same logic in a Zend_Controller_Action_Helper gives errorHandler the chance to do its job since it appears the action helper's preDispatch hook kicks in after the request is dispatched to a controller not before as in the case of a Zend_Controller_Plugin's preDispatch hook.
Also I don't see how action helpers break the beauty of the oop "plugin" pattern. They could very well be named as Zend_Controller_Action_Plugin since that is what they are; a plugin to an action.
Let me put it this way; as you mentioned:
2. prior to the dispatch of the request, the plugin's method "preDispatch($request)" will be called. At this point, nothing will happen because the controller doesn't do anything yet! It's only when the auth_rights are determined, and the request forwarded to the right action (valid or not), that the controller will dispatch the request.
Here is the problem. When the controller is invalid or not in the resource list the resource becomes null.
Since Zend_Acl has a white list approach, unless you are 'admin' with unrestricted access to all resources, you get "not allowed" redirecting to "privilege error" (or "login") page before errorHandler can do its job.
Implementing the same logic in a Zend_Controller_Action_Helper gives errorHandler the chance to do its job since it appears the action helper's preDispatch hook kicks in after the request is dispatched to a controller not before as in the case of a Zend_Controller_Plugin's preDispatch hook.
Also I don't see how action helpers break the beauty of the oop "plugin" pattern. They could very well be named as Zend_Controller_Action_Plugin since that is what they are; a plugin to an action.
I have also discovered that 404's are not picked up using the Plugin method so I am trying to sort out authorisation in an Action Helper class.
I'd rather instantiate the helper in the bootstrap so I don't have the embarrassment of forgetting to instantiate in an action controller which would cause a restricted area to be made publicly accessible.
As a newbie I'm confused as to how I can make use of the preDispatch() method in the action helper.
The docs (http://framework.zend.com/manual/en/zend.controller.actionhelpers.html#zend.controller.actionhelper.writingyourown)
say that predispatch happens prior to a dispatched action.
It would be handy if someone could show me an example of how to make use of the preDispatch method of an action helper or, just, how to make an action helper get instantiated by every action to check for authorisation.
I'm very confused! So thanks in advance.
(andrasz[@]gmail.com) Thanks.
I don't see a link to a tarball for the code. Maybe I missed it?
As a result it looks like you have the usual flurry of confused comments just trying to make it work. Sorry to whinge but what is the point?
This is the kind of thing that's needed here in my opinion:
http://akrabat.com/zend-auth-tutorial/
thanks.
but
!$this->_acl->isAllowed($role, $resource, $action)
always going to 'true' when we have invalid url.
So
else {
$module = $this->_noacl['module'];
$controller = $this->_noacl['controller'];
$action = $this->_noacl['action'];
}
changes the request to $this->_noacl['module']... before dispatching.
i did
if (!$this->_acl->isAllowed($role, $resource, $action)
&& Zend_Controller_Front::getDispatcher()->isDispatchable($request))
public function preDispatch($request)
{
if (!Zend_Controller_Front::getDispatcher()->isDispatchable($request))
{
return;
}
...
}
I would like to have an editbutton/link in my view. The view is a list of products. Only the 'editor' role may edit. The rest ('guest') my not see the editbutton at all. So, I want an $acl->isAllowed() somewhere in my MVC. But where?? Or is there another/better way?
kind regards,
Richard
I am currently looking for a way to implement Zend_Acl in my MVC impelementation. As I see this, article is already over a year old. So I can imagine that changes have already taken place. So this is also my question. Is there any updated information on this topic? What changes have been made to this in the release 1.5?
Better and safe is this method: $this->getRequest()->getActionName(), then you will get the correct action name, even when coming from an forward!
Where to put the ACL code, should i append it to the bootstrap.php file?
It is like most on ZF: http://framework.zend.com/manual/en/zend.acl.html
They hand out code, but never hint me on where to type it.
Or is there a more up to date version? Seems all examples lag behind the intended usage of current ZF versions.
I just want to authenticate a user and setup a session, but this example doesn't give me handlebars on the most-correct method in ZF.
Using the next three lines of code will fix the problem:
$controller = $request->getControllerName();
$action = $request->getActionName();
$module = $request->getModuleName();
Otherwise you'll get:
"Invalid controller specified (xxx)" or "Action "xxx" does not exist and was not trapped in __call()"
It took me a long time to figure out this problem, I just thought my ErrorHandler was broken.
I really think Zend should go through this entire website, review the examples, and build something that is actually useful
1. Most of the examples disagree with each other.
2. Searching on "ACL" brings up examples from ZendFrameworks as old as Ver 1.1, which obviously are of NO use today.
3. Almost none of the examples tell what file is being edited, just shows a "code snippet"
4. I have yet to find a single explanation of how the entire life-cycle of a request-process-response exists and fully explaining how filters, dispatchers etc might affect this cycle.
5. Not one single tutorial actually builds upon the previous tutorial, but instead builds upon a completely different codeset than was built in the previous example... and worse yet, the new codeset is of a completely different design pattern!
6. There are actually Zend Webinars that are recordings of a presentation wherein the slides stopped working, people in the audience can be heard complaining, and the presenter apologizes for the slide show not working. I am frankly astounded that a company like Zend would actually put presentations of this low quality and dubious value on their site instead of presenting a well-produced and well-though out series of interrelated tutorials.
so the solution is to inherit default dispatcher class and add a method to check if valid method
<?php
require_once 'Zend/Controller/Dispatcher/Standard.php';
/**
* Arabreader (RSS Aggregator)
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://arabreader.net/license/new-bsd
*
*
* @category Page
* @package AReader_Homepage
* @copyright Copyright (c) 2008-2009 Intpl Inc. (http://www.intpl.com)
* @license http://arabreader.net/license/new-bsd New BSD License
* @author islam el-naggar islamelnaggar@gmail.com | +20102706070
*/
/**
* solve Acl issue and check if valid action..
*
*/
class Intpl_Controller_Dispatcher_Custom extends Zend_Controller_Dispatcher_Standard{
public function isValidAction(Zend_Controller_Request_Abstract $request){
$className = $this->getControllerClass($request);
$classMethods = get_class_methods($this->loadClass($className));
if(!in_array($this->getActionMethod($request), $classMethods)){
return false;
}
return true;
}
}
then you can use this dispatcher into your front controller instead of the standard one by the following code
//set Fixed Dispatcher class which can check if Correct action
require_once 'Intpl/Controller/Dispatcher/Custom.php';
$this->front->setDispatcher(new Intpl_Controller_Dispatcher_Custom());
and in the acl plugin you need to check both if isDispatchable and isValidAction
if (!Zend_Controller_Front::getInstance()->getDispatcher()->isDispatchable($request)
|| !Zend_Controller_Front::getInstance()->getDispatcher()->isValidAction($request)
){
return;
}
Thank You!
which supports only
resources and roles
which means
i can add one role to such a resource
i.e
add member (role) to page(resource) width any privillage
but u can't assign if there are some members are allowed
or some not..
so i can add member role which have id = 1 to specific resource (such as page no.1 and 2 )
so i implements this method which offer those features to the current zend_acl
so u can check if the user 1 have a permission to page 1 and 2
you can find the source code
at googlecode
http://code.google.com/p/zendaclobjectpermission/
Thank you!