Zend_Acl rules based on both MVC and Propel models

165 Views Asked by At

I am having a web project where some access decisions are dependant on the page itself (e.g. /logout which shall only be visible to logged in users) and some are dependant on dynamic model objects (e.g. /article/delete/1234 where we have to check if 1234 was written by the logged in user or if he is an admin).

Now, I am facing the problem of how to bring both things together. No matter how I tried, I cannot rely on any of the two alone:

  • Some pages do not use any models, so I cannot setup any model rules there
  • On the other hand, I cannot create dynamic assertions for a modular approach, because Comment is just a comment and not a default/comment. A Comment is not restricted to the default module, it may also be used in the admin module.

With modular ACL I am trying to check for each page if a user is allowed to visit it, e.g.

if (!$acl->isAllowed($user, 'default/secrets', 'mysecrets')) {
    $this->forward('auth', 'login');
    $this->setDispatched(false);
}

And with dynamic assertions I am checking if somebody is allowed to edit a specific model object.

// $comment has a method getResourceId() returning 'comment'
if ($acl->isAllowed($user, $comment, 'delete')) {
    // display a link for deletion
}

Of course it would be nice if the check for

  • deletion of a specific comment, and
  • accessing the /comment/delete/???? page

would be the same, but I guess this is not possible and I would have to create two rules:

$acl->allow('member', 'default/comment', 'delete');
$acl->allow('member', 'comment', 'delete', new Acl_Assert_CommentAuthor());
$acl->allow('admin', 'comment', 'delete');

Now, this seems not perfect to me as this can lead to duplicate work in some cases.

Is there some better method to approach this problem? Or is the only method to at least create a coherent naming scheme like: mvc:default/comment, model:comment

1

There are 1 best solutions below

0
On

The way i did it, custom sql queries that restrict results, functions that check before insert/delete/modify sql

and then an ACL plugin i wrote that checks permission and uses these 5 tables

acl_groups
acl_permession
acl_permession_groups
acl_permession_risource
acl_risource

Code:

class Abc_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
{

    /**
     * Return whether a given request (module-controller-action) exists
     *
     * @param Zend_Controller_Request_Abstract $request Request to check
     * @return boolean Whether the action exists
     */
    private function _actionExists(Zend_Controller_Request_Abstract $request)
    {
        $dispatcher = Zend_Controller_Front::getInstance()->getDispatcher();

        // Check controller
        if (!$dispatcher->isDispatchable($request)) {
            return false;
        }

        $class  = $dispatcher->loadClass($dispatcher->getControllerClass($request));
        $method = $dispatcher->formatActionName($request->getActionName());

        return is_callable(array($class, $method));
    }



    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {

        // fetch the current user

        $controller = $request->controller;
        $action = $request->action;

        $logger = Zend_Registry::get('log');



        //prima controlla se sei autenticato e poi controlla se l'azione esiste, cosi non esponi il sito
        $auth = Zend_Auth::getInstance(); /* @var $auth Zend_Auth */
            if($auth->hasIdentity()) {


                if(! $this->_actionExists($request))//l'azione non esiste?
                {
                    $request->setControllerName('error');
                    $request->setActionName('pagenotfound');
                        $logger->notice( " IP: ". $_SERVER['REMOTE_ADDR']. " http://".$_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]. " ?" .http_build_query($_REQUEST));
                    return ;
                }

                $allowed = Abc_Action_Helper_CheckPermission::checkPermission($controller, $action);

                if ($allowed !== 1)
                {
                    $request->setControllerName('error');
                    $request->setActionName('noauth');
                }
                //fine azione esiste
            }else{
                                    $request->setControllerName('user');
                $request->setActionName('login');
                return ;
            }


    }//fine preDispatch

}

You can then add your code(which i ommited for shortness) to remember the request and redirect you there after login.