Categories

Archives

Did You Know?

From '96 to '99 I spent a lot of time on IRC (DALnet) under the name SysRequest. I wrote a little remover utility to clean up the DMsetup.exe worm, which turned into a bit of a contest with its creator who tried to make his code more polymorphic.

Recent Comments

Tags

asp audio browser bug business coalesce code crash Database db debian extension framework imap internet legions linux metaverse mysql obscurity patch PHP postgresql properties release scp Second Life second life security session social media sound sql ssh subversion tables tortoisesvn tribes ubuntu virtual world web windows zend zend framework zf

Using Zend_Acl with your model

Zend_Acl is an excellent component that provides Access Control List (ACL) functionality. In most cases the goal is to manage user access to resources. access to to manage all things related to user access. In a nutshell, a role

to any kind of resource. But unfortunate it doesn't quite live up to its full potential just yet, due to a few implementation details as outlined in tickets and ZF-4460. The latter of the two also has comments that include a few examples for a workaround.

I'm using the following class which gives any custom Assert object access to the actual Resource passed to it.

/**
 * The current Zend_Acl design does not allow for
 * using a custom Role and Resource objects and expect that they'll make it through
 * to custom assertions.
 * See http://framework.zend.com/issues/browse/ZF-1722
 * and http://framework.zend.com/issues/browse/ZF-4460
 */
class My_Acl extends Zend_Acl
{

    /**
     * Returns the identified Resource
     *
     * The $resource parameter can either be a Resource or a Resource identifier.
     *
     * @param  Zend_Acl_Resource_Interface|string $resource
     * @throws Zend_Acl_Exception
     * @return Zend_Acl_Resource_Interface
     */
    public function get($resource)
    {
        if (!$this->has($resource)) {
            require_once 'Zend/Acl/Exception.php';
            throw new Zend_Acl_Exception("Resource '$resource' not found");
        }

        if ($resource instanceof Zend_Acl_Resource_Interface) {
            return $resource;
        }

        return $this->_resources[$resource]['instance'];
    }

}

Unfortunately, this doesn't fix the issue of the Role making it through to an assertion, but in most of my cases that's the acting user anyway, so I don't even try to grab the passed in $role and instead use the identity straight from Zend_Auth.

/**
 * Ensure the photo is owned by the user with $role
 *
 */
class PhotoOwnerAssertion 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 Photos_Row) {
            return false;
        }
        /* @var $resource Photos_Row */

        /*
         * Workaround; the current Zend_Acl design does not allow for
         * using a custom Role interface and expect that it'll make it through.
         * See http://framework.zend.com/issues/browse/ZF-1722 and
         * http://framework.zend.com/issues/browse/ZF-4460
         */
        $role = Zend_Auth::getInstance()->getIdentity();

        return $resource->getOwnerId() == $role;
    }

}

When setting up the ACL, I provide an instance of the custom assertion which will then provide the proper access control. It's fairly well encapsulated (other than the bug workarounds).

/**
 * Only allow owners to view, edit, and delete their photos
 */
$acl->allow('member', 'Photo', array('view', 'edit', 'delete'), new PhotoOwnerAssertion());

In this case, the Photos_Row class must also provide a getOwnerId() method.

class Photos_Row extends Zend_Db_Table_Row_Abstract
                 implements Zend_Acl_Resource_Interface
{

    /**
     * Resource type (for use with ACL)
     *
     * @see Zend_Acl_Resource_Interface
     *
     * @return string
     */
    public function getResourceId()
    {
        return 'Photo';
    }

    /**
     * Return the photo owner's UUID
     *
     * @see UgcItem
     *
     * @return string
     */
    public function getOwnerId()
    {
        return $this->avataruuid;
    }

}

A little more abstraction and the custom assertion can be used for models other than photos.

Write a comment