<?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>Ralph Schindler &#187; Zend_Acl</title>
	<atom:link href="http://ralphschindler.com/tag/zend_acl/feed" rel="self" type="application/rss+xml" />
	<link>http://ralphschindler.com</link>
	<description>Ralph Schindler</description>
	<lastBuildDate>Wed, 12 May 2010 21:58:33 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Dynamic Assertions for Zend_Acl in ZF</title>
		<link>http://ralphschindler.com/2009/08/13/dynamic-assertions-for-zend_acl-in-zf</link>
		<comments>http://ralphschindler.com/2009/08/13/dynamic-assertions-for-zend_acl-in-zf#comments</comments>
		<pubDate>Thu, 13 Aug 2009 14:19:10 +0000</pubDate>
		<dc:creator>Ralph Schindler</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend_Acl]]></category>

		<guid isPermaLink="false">http://ralphschindler.com/?p=34</guid>
		<description><![CDATA[In Zend Framework 1.9.1, Zend_Acl gets two major issues resolved and a simple API change that now make it possible to create a more robust,  more expressive ACL definition with less code.   ZF issues ZF-1721 and ZF-1722, each nearly two years old, have both been solved.  Over the last two years, [...]]]></description>
			<content:encoded><![CDATA[<p>In <a href="http://framework.zend.com/download/latest/">Zend Framework 1.9.1</a>, <tt>Zend_Acl</tt> gets two major issues resolved and a simple API change that now make it possible to create a more robust,  more expressive ACL definition with less code.   ZF issues <a href="http://framework.zend.com/issues/browse/ZF-1721">ZF-1721</a> and <a href="http://framework.zend.com/issues/browse/ZF-1722">ZF-1722</a>, each nearly two years old, have both been solved.  Over the last two years, I’ve seen a variety of duplicate issues come into the issue tracker, which stem from two fundamental flaws in Zend_Acl &#8211;  &#8220;Zend_Acl::isAllowed does not support Role/Resource Inheritance down to Assertions&#8221; and &#8220;Zend_Acl assertions breaks when inheritance is required (ie DepthFirstSearch)&#8221;.  In this article, we’ll explore the API changes that alleviate these two problems, and we&#8217;ll demonstrate how to leverage the <tt>Zend_Acl</tt> assertion system to create expressive, dynamic assertions that work with your applications models.</p>
<h3>Backwards Compatible API Changes</h3>
<p>Before discussing the issues, let&#8217;s go over the API change and how that affects the component.  Previously, the two methods for setting up an ACL that were used by a developer were <tt>add()</tt> and <tt>addRole()</tt>.  Interestingly, <tt>add()</tt> was intended to imply <tt>addResource()</tt>.  Since <tt>add()</tt> implied that you were adding a resource, its clear that this component was created from the perspective of resources as a primary actor, and then roles and assertions as secondary actors. </p>
<p>The new API allows for the creation of an ACL by using strings instead of having to use <tt>Zend_Acl_Role</tt> and <tt>Zend_Acl_Resource</tt> objects explicitly.  To me, this is a pretty important step towards what I&#8217;d like to see in 2.0.  In 2.0, I would ideally like to see <tt>addRole()</tt> and <tt>addResource()</tt> accept strings for <em>types</em> of roles and resources to query against, and <em>accept objects</em> for <em>explicit</em> role and resource objects to query against (even if they match an already registered type).  To put simply, I would expect <tt>addRole('user')</tt> and <tt>addRole($userObjectForRalph)</tt> to have different behaviors if different permissions were registered for each.  This would allow me to specify specific access for the user object &#8216;ralph&#8217; separately from the ACL&#8217;s for objects of role type &#8216;user&#8217;.  The behavior can be further defined to either inherit from the type, or override type ACL&#8217;s depending on the desired effect.  Ultimately, this would allow for a more dynamic experience with <tt>Zend_Acl</tt>.</p>
<h3>Dynamic Assertions Example</h3>
<p>In the following example, we&#8217;ll have a look at a common use case that is now possible in <tt>Zend_Acl</tt>.  In plain English, what developers want to be able to do is be able to design assertions that can accept application models that implement the Resource or Role interface, and be able to apply some dynamic or custom logic to assess whether or not the given role has access to the given resource.  As mentioned previously, this was not possible because in the process of checking the ACL tree, using a depth-first search, the calling resource and roles was lost, and only the original registered objects was being persisted into the assertions.  Well, that&#8217;s fixed now.</p>
<p>For the purposes of this example, we&#8217;ll take a simple concept: a user needs to be able to only edit their own blog post.  The user in this case, would be our applications model for users.  The actual class will implement the <tt>Zend_Acl_Role_Interface</tt>.  We will also have a <tt>BlogPost</tt> model which will serve as the resource in question, thus implementing the <tt>Zend_Acl_Resource_Interface</tt>.  Naturally, our system will be able to handle users of different role &#8216;types&#8217;, but our <tt>BlogPost</tt> will only be of a single resource type &#8216;blogPost&#8217;.</p>
<p>Note: the following code is demonstration only.  As such, some coding standards or conventions are not necessarily what you&#8217;d expect in proper object-oriented code or even a Zend Framework MVC based application.  Some of the code might contain rouge &#8216;echo&#8217; statements so that the demonstration below will be more expressive of what its actually doing.</p>
<pre class="brush: php;">
class User implements Zend_Acl_Role_Interface
{
    // using public members here for brevity in this article
	public $id = null;
    public $role = 'guest';

    public function getRoleId()
    {
        return $this-&gt;role;
    }
}

class BlogPost implements Zend_Acl_Resource_Interface
{
	public $id          = null;
    public $ownerUserId = null;

    public function getResourceId()
    {
        return 'blogPost';
    }
}
</pre>
<p>Next, we&#8217;ll create the dynamic assertion.  We generally would expect this assertion to be called when a <tt>User</tt> is requested to modify a <tt>BlogPost</tt>.  This assertion will ensure that the <tt>BlogPost</tt>&#8217;s owner id (the user id that owns said <tt>BlogPost</tt>), is the same as the provided <tt>User</tt> objects id.  If it is, pass, if not, fail.  Fairly common use case, right?  Here is what our assertion should look like, with a few inline comments:</p>
<pre class="brush: php;">
class UserCanModifyBlogPostAssertion implements Zend_Acl_Assert_Interface
{
    /**
     * This assertion should receive the actual User and BlogPost objects.
     *
     * @param Zend_Acl $acl
     * @param Zend_Acl_Role_Interface $user
     * @param Zend_Acl_Resource_Interface $blogPost
     * @param $privilege
     * @return bool
     */
    public function assert(Zend_Acl $acl, Zend_Acl_Role_Interface $user = null, Zend_Acl_Resource_Interface $blogPost = null, $privilege = null)
    {
    	echo ' == Checking the assertion ==' . PHP_EOL; // only here for the purposes of article

        if (!$user instanceof User) {
            throw new InvalidArgumentException(__CLASS__ . '::' . __METHOD__ . ' expects the role to be an instance of User');
        }

        if (!$blogPost instanceof BlogPost) {
            throw new InvalidArgumentException(__CLASS__ . '::' . __METHOD__ . ' expects the resource to be an instance of BlogPost');
        }

        // if role is publisher, he can always modify a post
        if ($user-&gt;getRoleId() == 'publisher') {
        	return true;
        }

        // check to ensure that everyone else is only modifying their own post
        if ($user-&gt;id != null &amp;&amp; $blogPost-&gt;ownerUserId == $user-&gt;id) {
        	return true;
        } else {
        	return false;
        }
    }
}
</pre>
<p>Note: Assertions, as with ACL&#8217;s can be treated, and most likely should be treated, as application models.  As such, if you are using the Zend Framework MVC application structure, you might want to name this one similarly to <tt>Default_Model_Acl_UserCanModifyBlogPostAssertion</tt>, and would live in <tt>application/models/Acl/UserCanModifyBlogPostAssertion.php</tt>.  Likewise, the <tt>User</tt> class would actually be <tt>Default_Model_User</tt>, and <tt>BlogPost</tt> might be <tt>Default_Model_BlogPost</tt>.</p>
<p>Now that we have our models setup for our ACL to interact with, its time to define the actual ACL definition itself.  For the purposes of this exercise, we&#8217;ll not assume that the ACL itself is a model, but our consuming script below will simply interact with it.  In a Zend Framework MVC application, one might find the ACL defined as a model within your application, depending on your needs.</p>
<pre class="brush: php;">
$acl = new Zend_Acl();

// setup the various roles in our system
$acl-&gt;addRole('guest');
$acl-&gt;addRole('contributor', 'guest');
$acl-&gt;addRole('publisher', 'contributor');

// add the resources
$acl-&gt;addResource('blogPost');

// add privileges to roles and resource combiniations
$acl-&gt;allow('guest', 'blogPost', 'view');
$acl-&gt;allow('contributor', 'blogPost', 'contribute');
$acl-&gt;allow('contributor', 'blogPost', 'modify', new UserCanModifyBlogPostAssertion());
$acl-&gt;allow('publisher', 'blogPost', 'publish');
</pre>
<p>The above code has produced a fully defined ACL object, at least for the purposes of this article, that we can now start interacting with.  In the follow examples, we&#8217;ll interact with this ACL object.  The <tt>User</tt> and <tt>BlogPost</tt> objects utilize public properties for brevity and illustrative purposes, but you can assume that these object properties might be populated and persisted via Zend_Db_Table row, a web service, or some other data source persistence layer.</p>
<pre class="brush: php;">
$user = new User();
$post = new BlogPost();

// some default values
$user-&gt;id = 1;
$post-&gt;ownerUserId = 1;

/**
 * Demonstrate guest Privileges
 */
echo 'Demonstrating ' . $user-&gt;role . ' privileges' . PHP_EOL
    . '------------------------------------------'
    . PHP_EOL . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') view?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'view') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') contribute?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'contribute') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') modify?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'modify') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') publish?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'publish') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

/**
 * Demonstrate contributor Privileges
 */

$user-&gt;role = 'contributor';

echo 'Demonstrating ' . $user-&gt;role . ' privileges' . PHP_EOL
    . '------------------------------------------'
    . PHP_EOL . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') view?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'view') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') contribute?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'contribute') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

$post-&gt;ownerUserId = 5;


// the following two examples should demonstrate the assertion being checked

echo 'Can user (' . $user-&gt;role . ') modify someone elses blogPost?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'modify') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

$post-&gt;ownerUserId = 1;

echo 'Can user (' . $user-&gt;role . ') modify own blogPost?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'modify') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') publish?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'publish') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

/**
 * Demonstrate publisher Privileges
 */

$user-&gt;role = 'publisher';

echo 'Demonstrating ' . $user-&gt;role . ' privileges' . PHP_EOL
    . '------------------------------------------'
    . PHP_EOL . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') view?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'view') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') contribute?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'contribute') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

$post-&gt;ownerUserId = 5;

echo 'Can user (' . $user-&gt;role . ') modify someone elses blogPost?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'modify') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

$post-&gt;ownerUserId = 1;

echo 'Can user (' . $user-&gt;role . ') modify own blogPost?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'modify') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;

echo 'Can user (' . $user-&gt;role . ') publish?' . PHP_EOL
    . ($acl-&gt;isAllowed($user, $post, 'publish') ? 'yes' : 'no') . PHP_EOL
    . PHP_EOL;
</pre>
<p>Once you have all of that in place, you can see a the run of such a script would produce these results:</p>
<pre class="brush: plain;">
/home/ralph/test-script/$ php acl-inheritance.php

Demonstrating guest privileges
------------------------------------------

Can user (guest) view?
yes

Can user (guest) contribute?
no

Can user (guest) modify?
no

Can user (guest) publish?
no

Demonstrating contributor privileges
------------------------------------------

Can user (contributor) view?
yes

Can user (contributor) contribute?
yes

 == Checking the assertion ==
Can user (contributor) modify someone elses blogPost?
no

 == Checking the assertion ==
Can user (contributor) modify own blogPost?
yes

Can user (contributor) publish?
no

Demonstrating publisher privileges
------------------------------------------

Can user (publisher) view?
yes

Can user (publisher) contribute?
yes

 == Checking the assertion ==
Can user (publisher) modify someone elses blogPost?
yes

 == Checking the assertion ==
Can user (publisher) modify own blogPost?
yes

Can user (publisher) publish?
yes
</pre>
<h3>Conclusion</h3>
<p>Zend_Acl can now be used to make concise, dynamic and expressive ACL systems.  The assertion system that is in place in <tt>Zend_Acl</tt> can be leveraged in ways never seen before out of the box.  While the User/BlogPost example is on the simple side, you can use this article to start thinking about the different ways such a system can be leveraged in your own projects where dynamic assertions would simplify controller or model code that is already in place.</p>
]]></content:encoded>
			<wfw:commentRss>http://ralphschindler.com/2009/08/13/dynamic-assertions-for-zend_acl-in-zf/feed</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
	</channel>
</rss>
