Zend_Acl part 1: Misconceptions and simple ACLs

February 6, 2009 – 7:35 am Tags: ,

I’m going to be writing a weekly series of posts on Zend_Acl. This first post will clear up some common misconceptions regarding Zend_Acl, introduce creating ACLs for simple applications, and give some examples on using the ACL in both non-Zend Framework and Zend Framework applications.

Later in the post series I’ll be talking about some more advanced ways of utilizing Zend_Acl, and topics such as database-backed ACLs.

A russian translation of this post is available here, courtesy of Rashad Surkin.

Zend_Acl misconceptions

Many people think that the ACL “resource” and “privilege” are the same as the controller and the action. This is not true!

The resource in Zend_Acl can be anything – a controller, a file, a model…

The privilege, just like the resource, can be anything related to the resource – for example, it could be an action if the resource is a controller, or it could be “read” or “write” if it’s a file or a model.

Later in the series, I’ll show some other ways of using acl resources, privileges and roles.

Building a simple ACL

As mentioned earlier, Zend_Acl constists of resources, privileges, and additionally, roles. Resources can be anything ranging from controllers to files. Privileges are different levels of access on the resource. Roles determine who can access a resource, and with what privileges. Roles can be users, user groups or anything you wish to associate such data with.

The very simplest way you can use Zend_Acl is building the ACL in code. This is often a useful approach for simple cases, where you only have a handful of resources and roles.

class My_Acl extends Zend_Acl {
  public function __construct() {
    //Add a new role called "guest"
    $this->addRole(new Zend_Acl_Role('guest'));
 
    //Add a role called user, which inherits from guest
    $this->addRole(new Zend_Acl_Role('user'), 'guest');
 
    //Add a resource called page
    $this->add(new Zend_Acl_Resource('page'));
 
    //Add a resource called news, which inherits page
    $this->add(new Zend_Acl_Resource('news'), 'page');
 
    //Finally, we want to allow guests to view pages
    $this->allow('guest', 'page', 'view');
 
    //and users can comment news
    $this->allow('user', 'news', 'comment');
  }
}

Now, by creating a new instance of My_Acl, we could perform simple checks against the acl. But what is allowed, and for who?

The guest role gets a privilege called view on the resource page. Since the user role inherits from guest, it also gets this privilege. The news resource inherits from page, so everyone who has privileges on the page resource, has the same privileges on news. In the last line of the constructor, we add a privilege comment for the user role on news. Only the user role has the privilege comment in the news resource – guest does not.

Note: The names used in the example for the roles and resources are just identifiers. They could be 1, 2, or whatever, but it’s often easier to understand identifiers that are human-readable. However, when generating ACLs from databases or other source, it may not be necessary to have human-readable identifiers – the primary key from a database row will do just fine. Again, later in the series I’ll show an example of this.

Using the simple ACL

Using an ACL class is quite simple. You just call the method isAllowed with a role, resource and a privilege. The main difficulty is in determining what is the role, resource and the privilege.

How do we determine, what is the role? This can be a very simple if-clause: if there is a logged-in user, then the role is user, otherwise it is guest.

What about the resource? Depending on the application, this could be guessed from the file name included. For example, with our simple ACL, we could have a file called page.php and a file called news.php. page would check against the page resource, and news would check against the news resource.

And lastly, the privilege? When simply loading the page, you could use the view privilege. The code could later on check for other privileges: for example, the code could check if you have access to the comment privilege and display a comment box.

Here’s a really simple example:

$role = 'guest';
if(isset($_SESSION['auth']))
  $role = 'user';
 
$acl = new My_Acl();
 
if($acl->isAllowed($role, 'news', 'comment')) {
  //Some code here to display a news box
}

As you can see, it’s quite easy to use the ACL even without Zend Framework, and you can easily speed up development of non-ZF apps by using the ACL component as it will save time when you won’t need to implement complex authorization checking code.

The above example may be very simple, but it shows you the basic way ACLs are used.

Using the simple ACL in Zend Framework applications

In Zend Framework applications, the resource and privilege can often be determined from the request object.

Typically, we would create a plugin which checks the acl for us:

class My_Plugin_Acl extends Zend_Controller_Plugin_Abstract {
  private $_acl = null;
 
  public function __construct(Zend_Acl $acl) {
    $this->_acl = $acl;
  }
 
  public function preDispatch(Zend_Controller_Request_Abstract $request) {
    //As in the earlier example, authed users will have the role user
    $role = (Zend_Auth::getInstance()->hasIdentity())
          ? 'user'
          : 'guest';
 
    //For this example, we will use the controller as the resource:
    $resource = $request->getControllerName();
 
    if(!$this->_acl->isAllowed($role, $resource, 'view')) {
      //If the user has no access we send him elsewhere by changing the request
      $request->setModuleName('auth')
              ->setControllerName('auth')
              ->setActionName('login');
    }
  }
}

We could have made the ACL static – instead of passing it in the constructor, we could have just created it in the preDispatch method. However, by passing it as a parameter, we make it easier to reuse this plugin. If we create a different ACL class, this plugin will still work with it without modifications.

Now that we have a plugin, we need to add it to the front controller in the boostrap:

$acl = new My_Acl();
 
//assuming $fc is the front controller
$fc->registerPlugin(new My_Plugin_Acl($acl));

Now every request will be checked against the acl, and anyone without the view privilege would not get through.

Note: if you use the My_Acl class as-is, you may get an infinite loop! The code in the plugin is sending the user to Auth module’s AuthController’s loginAction. It also uses the controller’s name as the resource ID… but My_Acl does not have a resource called auth! So if you want to make it work, you need to add a resource called auth to the ACL, and have it inherit from page, as we already gave guest the necessary privileges to page.

But how do we check for the comment privilege?

You could add some code to the controller, which checks for the required privilege, and then sets a variable in the view to true:

public function someAction() {
  $role = (Zend_Auth::getInstance()->hasIdentity())
        ? 'user'
        : 'guest';
 
  //assuming $this->_acl contains the acl
  $this->view->canComment = $this->_acl->isAllowed($role, 'news', 'comment');
}

It may be a good idea to modify the code so that you won’t need to duplicate the role check. For example, you could store the role in the auth identity, or you could create an action helper which is used to access the acl and contains all relevant data.

In closing

Armed with this knowledge, you should be able to get started with Zend_Acl!

Next week’s post is going to deal with some more advanced Zend_Acl usage scenarios:

  • Dealing with different resource types, such as controllers and files
  • Dealing with different role types, such as users and user groups

You can read the next post in the series here

Share this:
  1. 43 Responses to “Zend_Acl part 1: Misconceptions and simple ACLs”

  2. Great article Jani! It is exactly what i looking for during last few days, – A good example showing how to implement a nice ACL solution with Zend framework!
    Thank you, it is realy clear and helpful!

    Looking forward your next article on this topic!

    By Lukas on Feb 6, 2009

  3. Hi Jani.

    Though I don’t use ZF (Agavi rules!!! :P) it’s great article, as always :)
    Thanks to your blog posts I really consider to give second chance to that framework.

    Cheers, Alan

    By Alan on Feb 6, 2009

  4. Great initiative, Jani. I’m looking forward to reading the next few articles from you about this subject.

    Cheers, Christian.

    By Christian on Feb 9, 2009

  5. This is nice shortcut in understanding ACLs.

    But ACLs are not really useful in so simple cases, most of the time there is a super users, that manages permissions and resources, linking them to roles.

    Most of the time, resources are dynamic, they stay in a databases.

    Is there a SANE way of storing ACLs in databases as generic as they are, and not doing any hackish stufs like storing them in hackish values to be exploded thereafter in php ?

    Drawbacks observed:

    – always querying the databases
    – caching (until some superuser changes)
    etc …

    tell more on these real life situations for our mortal souls

    By iongion on Feb 10, 2009

  6. iongion,

    In some extremely simple cases ACLs aren’t necessary. For example, one of my projects involved requiring login for all users for everything.

    And for dynamic/database backed ACLs, that’s coming next week, so stay tuned!

    This weeks ACL post about handling different kinds of roles and resources etc. will go up tomorrow.

    By Jani Hartikainen on Feb 10, 2009

  7. That was great! Another thing people get confused about is the relationship of ACL and Directory (ex. LDAP). They are not on the same plane of utility. It would be good to enlighten the community on such a subject.

    By drydenmaker on Feb 11, 2009

  8. Hi Jani.

    This is a great article, and i want to translate it (and other articles in series) to Russian if you don’t mind.

    By Oleg Lobach on Feb 12, 2009

  9. Oleg, you are welcome to translate the articles. Just point out that the original was written by me and put a link back to this site :)

    By Jani Hartikainen on Feb 12, 2009

  10. Jani, no problem, make sure

    By Oleg Lobach on Feb 12, 2009

  11. Jani, I’m fairly new to ZF but it looks to me like you could make this a little more generic by getting the action too…

    $resource = $request->getControllerName();
    $action = $request->getActionName();

    if(!$this->_acl->isAllowed($role, $resource, $action)) {…

    Am I out to lunch on this?

    btw: good article and thanks!

    By Rick on Feb 13, 2009

  12. You are indeed correct there Rick. You could use the action as the privilege like that if you want

    By Jani Hartikainen on Feb 13, 2009

  13. I see this is where i need to be. I’ve been using ZF for around a year now and its great. It covers a ton of areas. But that also seems to be the drawback. I find myself doing things the long way for not knowing a particular method exists in ZF. Am I missing some books or websites that detail best practices or starting points of building applications? The reference is good for….a reference but you have to know exactly what it is you are looking for.

    This article really helped me with a mess I was beginning to make out of authentication schemes all because I had not run across the right parts of the ZF reference at the right time.

    Thank you sir for this article.

    By Phil on Feb 13, 2009

  14. Thanks for the article. Could you please mention MVC best practices on where in the filesystem you would put these new classes “My_Plugin_Acl” & “My_Acl”? Thanks.

    By Chaz Gilbert on Feb 24, 2009

  15. You’d usually put them to library/My/Plugin/Acl.php and library/My/Acl.php

    You may also want to consider using Zend Framework’s naming conventions for the plugin: My_Controller_Plugin_Acl

    By Jani Hartikainen on Feb 24, 2009

  16. Why do you create a new class extending Zend_Acl? You’re not modifying its behaviour, you’re not extending its functionality etc. it’s bad OOP. You can create a factory that configures and returns Zend_Acl object, this way your class has a distinct responsibility of just configuring the Acl and class hierarchy is simpler. The code should also be more testable this way.

    By foobar on Mar 2, 2009

  17. If you read part 3 you will notice it uses a factory =)

    The reason that it’s not used earlier is because simpler classes don’t necessarily need one, and it also helps to keep the post itself easier to digest as it focuses more on the actual task at hand instead of building architechture around it which isn’t really even required.

    By Jani Hartikainen on Mar 2, 2009

  18. This article (and the next 2) look very promising … I have read it globally, and I think it rocks!

    By Koen on Mar 3, 2009

  19. Many thanks for this article, it’s by far the best one I could find on the topic and it really helped me making sense of ACL. Keep up the good work!

    By Laurent on Mar 18, 2009

  20. hi am newbie to zend framework can u tell me where to add the following code
    $role = ‘guest';
    if(isset($_SESSION['auth']))
    $role = ‘user';

    $acl = new My_Acl();

    if($acl->isAllowed($role, ‘news’, ‘comment’)) {
    //Some code here to display a news box
    }
    and can send me the source code to my email id(anandhi.cse@gmail.com)??

    By Anandhi on Aug 31, 2009

  21. finally a tutorial that works!!! thanks

    By Pedro Bento on Apr 14, 2010

  22. most of the time resources are dynamic they stay in a databases!

    By mask3d on Jul 28, 2010

  23. Thanks for this tutorial. It is gentle introduction, which is really quite helpful.

    By kurt on Jun 6, 2011

  24. Great tutorial.

    By kurt on Jun 6, 2011

  25. Hi,

    Does anyone know if I can assign multiple roles to one user using ACL and get the navigation right?

    ACL is working fine with it (looping through each of the roles and if none of them is_allowed – then redirect to access denied happens),
    but this is screwing up my navigation:

    Error I get on line in bootstrap:

    $view->navigation($navContainer)->setAcl($this->_acl)->setRole($this->_role);
    

    $role must be a string, null, or an instance of Zend_Acl_Role_Interface; array given

    Thanks,
    Adam

    By Adam Napora on Sep 5, 2011

  26. Great tutorial, thanks man. If only there was something like this on the ZF documentation!

    By Rupert on Nov 9, 2011

  1. 18 Trackback(s)

  2. Feb 10, 2009: links for 2009-02-09 — Mior Muhammad Zaki: PHP & JavaScript Programmer
  3. Feb 11, 2009: Zend_Acl part 2: different roles and resources, more on access | CodeUtopia
  4. Feb 19, 2009: Zend_Acl part 3: creating and storing dynamic ACLs | CodeUtopia
  5. Feb 26, 2009: An idea for improving CakePHP’s ACL system - cakebaker
  6. Mar 10, 2009: Artículos interesantes sobre Zend Framework
  7. Apr 12, 2009: An idea for improving CakePHP’s ACL system | linkfeedr
  8. May 19, 2009: links for 2009-05-19 « sySolution
  9. May 26, 2009: Tims Blog » Blog Archive » In Training
  10. May 30, 2009: Zend_Acl part 1: Misconceptions and simple ACLs | CodeUtopia
  11. May 31, 2009: Zend Auth [1.8] With Bootstrap - Zend Framework Forum
  12. Dec 29, 2009: Best of 2009 | CodeUtopia - The blog of Jani Hartikainen
  13. Feb 26, 2010: Getting the information out there « Poolczar says
  14. Oct 18, 2010: links for 2010-10-17
  15. Jan 24, 2011: Hello 2011 | CodeUtopia - The blog of Jani Hartikainen
  16. Mar 15, 2011: ACL Question
  17. Nov 19, 2011: Zend Framework 加上使用者權限(Acl)設定 » 卡卡的程式部落格
  18. Oct 26, 2012: What resources mean in ACL in zend framework
  19. Oct 6, 2013: carlcarl's blog | Zend Framework add Acl setting

Post a Comment

You can use some HTML (a, em, strong, etc.). If you want to post code, use <pre lang="PHP">code here</pre> (you can replace PHP with the language you are posting)