0

I'm trying to figure out how to limit access to specific resources in a PHP project I'm currently working on. I've looked for existing solutions, but none of them really fit what I need (for example, Zend_Acl).

Now I've come up with something like this: (Of course, this is very, very simplified. No exceptions or whatever. Just enough to get the point across)

class Access {
    protected $_context;
    protected $_handlers;
    public function __construct($context) {
        $this->_context = $context;
    }
    public static function registerHandler(Access_Handler $handler) {
        $key = $handler->getContextType().'/'.$handler->getResourceType();
        self::$_handlers[$key] = $handler;
    }
    public function isAllowed($resource) {
        return $this->getHandler($resource)->isAllowed($this->_context, $resource);
    }
    public function getHandler($resource) {
        // Look for and return the appropriate handler for the combination of 
        // $context and $resource
    }
}

abstract class Access_Handler {
    $_contextType;
    $_resourceType;
    abstract public function isAllowed();
}

class Access_Handler_UserInvoice extends Access_Handler {
    $_contextType = 'User';
    $_resourceType = 'Invoice';
    public function isAllowed($user, $invoice) {
        if($invoice->user_id === $user->id) {
            return true;
        }
        return false;
    }
}

I would then do something like this in my Application Bootstrap:

protected function $_initAccessHandlers() {
    Access::registerHandler(new Access_Handler_UserInvoice());
}

And in my controller (because I've heard that's where you should put your access control) I'd have something like this:

class InvoiceController {
    public function viewAction() {
        // $this->me is of type User
        $access = new Access($this->me);
        if($access->isAllowed($this->invoice)) {
            // ...
        }
    }
}

I haven't tested the code, so there might be typos or other errors, but I think you get the gist. Also, in reality I'd probably implement Access as a Singleton or a Multiton, but that's not what my question is about.

Is this the right way to do it? It seems so natural to me, but then I'm wondering why nobody else is doing it in this fashion.

My development stack is PHP/MySQL/Zend Framework/Doctrine.

Community
  • 1
  • 1
Rudolph Gottesheim
  • 1,671
  • 1
  • 17
  • 30
  • Everything you are trying to do here is already done in Zend_Acl with Dynamic Assertions, no? – Keyne Viana Feb 09 '12 at 19:24
  • It's a little bit more complicated in my case.. For example, I want a user to be able to access invoices from their colleagues (who belong to the same company) but not modify them. AFAIK, this is very hard if not impossible to do with Zend_Acl. (I haven't looked into Zend_Acl_Assert_Interface yet.. is this what I want?) – Rudolph Gottesheim Feb 09 '12 at 19:33

1 Answers1

1

With Zend_Acl you will perform the basic control, like:

$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('list'));

Then you can use assertions to do what you want:

$user = Zend_Auth::getInstance()->getIdentity();
$assertion = new My_Acl_Assertion_ArticleEditCheck($user);
$acl->allow('author', 'article', 'edit', $assertion);

You can instead of pass the user object to the assertion, implement it as a internal property and also work on the request parameters if necessary.

References:

http://framework.zend.com/manual/en/zend.acl.advanced.html

Dynamic custom ACL in zend framework?

For more advanced use of assertions, look at:

http://www.aviblock.com/blog/2009/03/19/acl-in-zend-framework/

http://ralphschindler.com/2009/08/13/dynamic-assertions-for-zend_acl-in-zf

Community
  • 1
  • 1
Keyne Viana
  • 6,194
  • 2
  • 24
  • 55