<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Aviblock.com &#187; MVC</title>
	<atom:link href="http://www.aviblock.com/blog/tag/mvc/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.aviblock.com/blog</link>
	<description>My musings on web development</description>
	<lastBuildDate>Mon, 30 May 2011 15:07:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>ACL in Zend Framework</title>
		<link>http://www.aviblock.com/blog/2009/03/19/acl-in-zend-framework/</link>
		<comments>http://www.aviblock.com/blog/2009/03/19/acl-in-zend-framework/#comments</comments>
		<pubDate>Thu, 19 Mar 2009 20:21:23 +0000</pubDate>
		<dc:creator>Avi</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend_Acl]]></category>

		<guid isPermaLink="false">http://www.aviblock.com/blog/?p=22</guid>
		<description><![CDATA[When I first started using the Zend Framework, one of the most confusing things was the ACL component. Even after I succesfully implemented an ACL solution in my app, something seemed wrong with it. I would like to argue that the proper use for the Zend ACL is actually impossible, since there is a reported [...]]]></description>
			<content:encoded><![CDATA[<p>When I first started using the Zend Framework, one of the most confusing things was the ACL component. Even after I succesfully implemented an ACL solution in my app, something seemed wrong with it.</p>
<p>I would like to argue that the proper use for the Zend ACL is actually impossible, since there is a reported <a href="http://zendframework.com/issues/browse/ZF-1721">bug in the code</a> which, to my knowledge, has not been fixed.</p>
<p>The use case I will discuss is where you want to only authors to edit articles and only to articles authored by themselves. I think this is a fairly common case.<br />
<span id="more-22"></span></p>
<p>How does the ACL help us? The ACL allows the developer to setup access rules for arbitrary resources and arbitrary roles. The key word here is &#8220;arbitrary&#8221;, and I&#8217;ll get back to that in a moment.</p>
<p>My feeling is that when most people start out with the ACL, they assume that resources are controllers (or some combination of module and controller), and permissions are actions.  So, for example, let&#8217;s say you had a controller called &#8220;Article&#8221; with an action called &#8220;edit&#8221;.</p>
<pre lang="PHP">class ArticleController extends Zend_Controller_Action {
  public function editAction() {
     //do stuff
  }
}</pre>
<p>So an ACL would be set up with something like this:</p>
<pre lang="PHP">$acl = new Zend_Acl();

$acl->add(new Zend_Acl_Resource('article'));

$acl->addRole(new Zend_Acl_Role('author'));

$acl->deny();

$acl->allow('author', 'article', array('edit'));</pre>
<p>This looks farely straight forward. Some place before the action is dispatched, like in a plugin, action helper or whatever you want, you would do a check against this acl.</p>
<pre lang="PHP">$acl->isAllowed($role, $resource, $permission);</pre>
<p>There are two problems with this approach, one obvious and one not so obvious.</p>
<p>The obvious one is that, while this may be successful in blocking regular users from editing the articles, it doesn&#8217;t block another author from editing the article.</p>
<p>In order to do that, we need to add in an additional assertion:</p>
<pre lang="PHP">$acl->allow('author', 'article', array('edit'), new AssertionIsArticleAuthor());</pre>
<p>The problem then becomes: in this assertion, how do you know which article it is? How do you know who the current author is? These can be very tricky questions. You might have to somehow stick the request object in there, and access the Zend_Auth singleton. While these certaintly are viable solutions, they just &#8220;smell&#8221; bad.</p>
<p>The other not so obvious problem is seen by taking a few steps back and looking at the whole problem from the beginning. The first question to ask is, what exactly are we trying to solve here? We want to restrict access to an article. So what did we do instead? We restricted access to a controller? What kind of sense does that make? Additionally, we are now tying in our ACL implementation to the url structure of the site. That is primarily a function presentation. If we would want to change that, it would require changing our ACL implentation. While Zend_Acl is certainly a &#8220;Zend&#8221; component, it has nothing directly to do with Zend MVC, but this approach ties us down to Zend MVC. What if we decided one day that we did not like the MVC components of ZF and wanted to move to Symfony or <a href="http://www.apteno.net/AptitudeCMS/trac/wiki/MVCnPHPProject">MVCnPHP</a>?</p>
<p>Zend Framework is  primarily a presentation framework. It helps in providing a clear and organized way to bring the presentation layer to the user. And that&#8217;s what it excels at. That, I believe, is the number one reason why they don&#8217;t ship with a &#8220;model&#8221; component. The model layer must be provided by yourself, and this part is probably the hardest part of the whole game. But if you keep some simple principles in mind, and keep focused, you can reapproach this whole problem in a new light.</p>
<p>We&#8217;ll start with our model layer. This should be fully functional without ZF. Pretend you&#8217;re writing a desktop app. Or a shell script. Whether or not the model layer should be tied to the database or separated from that as well, is a different question. </p>
<p>My personal approach, one which is fairly common in the Java world, is to work with Business objects, which are just &#8220;POPOs&#8221;&#8230;Plain Old PHP Classes. These business objects represent all the entities in my application&#8230;but this is a post for later. Let&#8217;s not digress too much.</p>
<p>In our model layer, we have two objects, an Article and an Author.</p>
<pre lang="PHP">class Article {
    public $id;
    public $title;
    public $body;

   /**
   * @var Author
   */
   public $author;
}

class Author {
    public $id;
    public $name;
}</pre>
<p>Any business logic in your app should either take place within these objects, or within other objects that act as a service layer. Your controllers should either talk to these service layer objects or to the business objects themselves.</p>
<p>When you retrieve data from the database, instead of keeping it around as array, put it in one of these objects. You can also use a custom Zend_Db_Row object for this to if you want to make things really simple.</p>
<p>Now that we&#8217;re dealing with objects, we can see that we want to restrict access to an Article object to the Author object contained within it. So our resource becomes the Article and the role is the Author. How do you define these as resources and roles? All we have to do is have our objects implement the Zend_Acl_Resource and Zend_Acl_Role interfaces! So we will modify the previous code to something like this:</p>
<pre lang="PHP">class Article implements Zend_Acl_Resource_Interface {
    public $id;
    public $title;
    public $body;

   /**
   * @var Author
   */

   public $author;

   public function getResourceId() {
       return 'article';
  }
}

class Author implements Zend_Acl_Role_Interface {
    public $id;
    public $name;

    public function getRoleId() {
        return 'user';
   }
}</pre>
<p>Now we write our custom assertion:</p>
<pre lang="PHP">class Chintion_Acl_Assert_AssertIsArticleAuthor implements Zend_Acl_Assert_Interface
{
    public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface  $author = null,
    Zend_Acl_Resource_Interface $article = null, $privilege = null)
    {
          return $this->article->author == $author;
    }
}</pre>
<p>Now when you have your Article object, you can take your Author object (which you can either store in Zend_Auth or construct it from the data in Zend_Auth), and do a check like this:</p>
<pre lang="PHP">$acl->isAllowed($author, $article);</pre>
<p>Very nice, clean and simple.</p>
<p>Except&#8230;..</p>
<p>It won&#8217;t work. By the time the assertion gets a hold of the instance of Zend_Acl_Role_Interface, its been demoted to plain old Zend_Acl_Role. Ditto for Zend_Acl_Resource_Interface. It doesn&#8217;t have our fancy beefed up role or resource.</p>
<p>This I think is due to the bug report that I linked to at the beginning. Fortunately, for the moment, there&#8217;s a work around:</p>
<p>We have to override the method Zend_Acl::get(Zend_Acl_Resource_Interface $resource) and the method Zend_Acl_Role_Registry::get(Zend_Acl_Role_Interface $role);</p>
<p>To do this, you have to subclass Zend_Acl and overridde the get method as such:</p>
<pre lang="PHP">public function get($resource)
{
    if ($resource instanceof Zend_Acl_Resource_Interface) {
        return $resource;
    }
    return parent::get($resource);
}</pre>
<p>and similary for Zend_Acl_Role_Registry:</p>
<pre lang="PHP">class RoleRegistry extends Zend_Acl_Role_Registry {
    public function get($role)
    {
        if ($role instanceof Zend_Acl_Role_Interface) {
            return $role;
        }

        return parent::get($role);

    }
}</pre>
<p>and in your subclassed Zend_Acl:</p>
<pre lang="PHP">$this->_roleRegistry = new RoleRegistry();</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.aviblock.com/blog/2009/03/19/acl-in-zend-framework/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
	</channel>
</rss>

