Apologies: I sent my first message without proof-reading it... this one reads (marginally) better ;)

Hi,

If you've been reading the list of late you'll realise that I've been delving into the ACL system. I've come across a problem and I'm not sure if I'm doing something fundamentally stupid with my design.

Here is my approach:

I have classes defined in my system which implement Zend_Acl_Resource_Interface. In my case I always implement my getResourceId() as a very simple "return __CLASS__".

I'll go back to my earlier example which concerns Articles to try and put this into context.

Say I have following setup:

class Article implements Zend_Acl_Resource_Interface.
{
   public function getResourceId()
   {
     return __CLASS__;
   }


   public $author;

   public function __construct($articleId)
   {
     // Load article by id and populate $author.
   }
}


(Obviously it does other stuff too!!)



In order to restrict editing to only the author of the article, I could implement the following Acl_Assert:

class Own_Article_Assert implements Zend_Acl_Assert_Interface
{
   public function assert(
     Zend_Acl $acl,
     Zend_Acl_Role_Interface $role = null,
     Zend_Acl_Resource_Interface $resource = null,
     $privilege = null)
   {
     if (!($resource instanceof Article))
       return false;

     // We now know $resource is of the class "Article"

     $auth = Zend_Auth::getInstance();
     if (!$auth->hasIdentity())
       return false;

     return ($auth->getIdentity()->username == $resouce->author);
   }
}



Then I had an ACL something like...

$acl = new Zend_Acl;
$acl->addRole(new Zend_Acl_Role('User'));
$acl->add(new Zend_Acl_Resource('Article'));
$acl->allow('User', 'Article' 'view');
$acl->allow('User', 'Article' 'edit', new Own_Article_Assert);


This should allow a user to view any article, but only allow the original author to edit it.

I've used the generic Zend_Acl_Resource() object to add the resource into the Acl as I cannot configure my Acl with the real article object without instantiating it. This seems like a reasonable thing to do, but the current Zend_Acl implementation seems to forbid this.

Allow me to provide an example. Let's say  I then have some code:

$art1 = new Article(1); // An article by current user
$art2 = new Article(2); // An article by someone else

echo 'View 1: '.($acl->isAllowed('User', $art1, 'view') ? 'ACK' :
'NAK')."<br />";
echo 'View 2: '.($acl->isAllowed('User', $art2, 'view') ? 'ACK' :
'NAK')."<br />";
echo 'Edit 1: '.($acl->isAllowed('User', $art1, 'edit') ? 'ACK' :
'NAK')."<br />";
echo 'Edit 2: '.($acl->isAllowed('User', $art2, 'edit') ? 'ACK' :
'NAK')."<br />";

What you'd expect here is ACK, ACK, ACK, NAK. But what actually happens
is ACK, ACK, NAK, NAK.

This is because the article object itself never gets passed through to the assert method and thus it fails at the "instanceof" check. This is due to the fact that the object you pass in to isAllowed is actually internally replaced by a the generic object added during ACL construction. This is done in Acl.php's get() function. (see the variable instance storage: $this->_resources[$resourceId]['instance']).

I can fix this trivially with a small patch (attached) that preserves the object passed in if it is given and only uses the generic object if a string is used as the parameter. With this patch applied, my original Article object makes it through to the assert() and I can perform the necessary tests.




I really hope this is a bug as, to me, I really don't see the point in using "objects" in the Acl system if the objects themselves are not going to be preserved and passed about accordingly. If the objects just represent generic strings then why not just use generic strings in the first place. Being able to pass a specific object to an assert method is what makes it powerful/useful.


I hope this is understandable. If you would like me to provide code I can do.

I'd really appreciate a follow up on this as it will affect how I approach my ACL implementation.

Col



--

Colin Guthrie
gmane(at)colin.guthr.ie
http://colin.guthr.ie/

Day Job:
   Tribalogic Limited [http://www.tribalogic.net/]
Open Source:
   Mandriva Linux Contributor [http://www.mandriva.com/]
   PulseAudio Hacker [http://www.pulseaudio.org/]
   Trac Hacker [http://trac.edgewall.org/]

Index: Acl.php
===================================================================
--- Acl.php	(revision 30213)
+++ Acl.php	(working copy)
@@ -288,8 +288,10 @@
     {
         if ($resource instanceof Zend_Acl_Resource_Interface) {
             $resourceId = $resource->getResourceId();
+            $lookup = false;
         } else {
             $resourceId = (string) $resource;
+            $lookup = true;
         }
 
         if (!$this->has($resource)) {
@@ -297,7 +299,7 @@
             throw new Zend_Acl_Exception("Resource '$resourceId' not found");
         }
 
-        return $this->_resources[$resourceId]['instance'];
+        return $lookup ? $this->_resources[$resourceId]['instance'] : $resource;
     }
 
     /**

Reply via email to