views:

171

answers:

5

Hello,

I'm contemplating using Zend_ACL. However, to me it looks like you create roles and then give those roles permissions to controllers and actions they can or cannot access.

However, to me that seems fairly limited. I have created a user permission system in the past where I stored the user_id, the module, the controller and the action they can access, however I never gave them a group. Therefore it was a per user, per module, per controller, per action basic on what they could access.

So! I'm wondering if I wanted to be less limited by groups that I should give the user a group and set those group permissions by default. Then load my user specific roles and over write the roles set by the default group: is that how you guys would do it?

A: 

You're on the right track.

There is no concept of a "group" or a "user", strictly defined.

You just have roles. You might have a role like "editors", which can access certain resources (like a particular action, or even some "article" object). You might also have a role like user_1234, which could have additional rules.

You'd give both roles to the user with id 1234.

timdev
A: 

In reply to the above (my response is to long)

Yeah, I'm not a big fan of doing it like that. Id rather have an array loaded for that specific user:

$item['Movie'] = 1;
$item['Movie']['manage']['edit'] = 0;

Then do a plugin, that loads those permissions (from the db, into an array) looks at the current module/controller/action and checks.

if (isset($item[$module]) && $item[$module] == 1) {
   if (isset($item[$module][$controller][$action]) && $item[$module][$controller][$action] == 0) {
       return false;
   }
   return true;
} else { 
 return false;
}

Then in my bootstrap, if its false, throw an exception. (Bear in mind, this is for the admin area of my site, not the front end, so I'm not fussed about a general user access.

Also I'd pass that array onto the view and do simple if statements on the links:

<?php if ($this->userPermission['Movie']['manage']['edit'])) { ?>
<a href="/administration/Movie/manage/edit/id/1">Edit Movie</a><?php } ?>

The downside being that if that method ever changed controller, I'd have to edit the templates. However I've seen no Zend_ACL view implimentation from the tutorials I've read so I'm not sure if its much different to what I'd do there.

azz0r
A: 

I asked a very similar question when I started using Zend_Acl. My post lists my solution with code. It might help you. :)

Sonny
+1  A: 

You are by no way limited to using the role system to denote groups in Zend_Acl.

For example:

In one application, I have my base user object, implement Zend_Acl_Role_Interface which, in this case returns a string that is specific for the user. For simplicity sake, let's just say something like user-1, where the numeric portion is the user's unique ID.

When the user object is initialized, it adds itself to the ACL, with the user specific role inheriting from a more generic role (akin to a group.):

if(!$acl->hasRole($this)) {
    $acl->addRole($this, $this->role); // Let's say role == 'member' here
}

This allows you to set general rules for the parent role (or roles, you can pass an array as the second argument to addRole, to have multiple parents. Also, remember that roles have a lineage all the way up the tree, so the parent role in this case may also have parent roles itself.)

So, to illustrate the flexibility here, let's assume this basic ACL set up (This is all theoretical, I'm writing it solely for the purpose of this answer):

$acl->addRole('guest');
$acl->addRole('member', 'guest');

$acl->allow('guest', 'comments', 'read');
$acl->allow('member', 'comments', 'write');

$user = $My_User_Model->find(1);
$acl->allow($user, 'comments', 'moderate');



$acl->isAllowed($user, 'comments', 'read'); // true
$acl->isAllowed($user, 'comments', 'write'); // true
$acl->isAllowed($user, 'comments', 'moderate'); // true

$acl->isAllowed('member', 'comments', 'moderate'); // false

And so on...

The beauty of the Zend Framework components is that you're not at all limited to using them in one specific way. Yes, while this can be daunting at times, and often requires a little more time invested on initial planning and implementation, it's great in the long run.

Also, personally, I'm not a fan of the direct mapping of ACL privileges to a controller/action construct. But that's a whole different conversation.

jason
A: 

Thanks for your feedback guys, however I decided to create my own. In case anyone's interested:

public function verify($controller=NULL, $action='index', $module='administration') {

    if ((isset($this->object[$module]['all']) && is_string($this->object[$module]['all'])) || isset($this->object[$module][$controller][$action]) || (isset($this->object[$module][$controller]) && is_string($this->object[$module][$controller]))) {
        return true;
    }
}

public static function check($values) {

    $module         = $values['module']     ? $values['module']     : 'administration';
    $controller     = $values['controller'] ? $values['controller'] : 'index';
    $action         = $values['action']     ? $values['action']     : 'index';
    $user_id        = $values['user_id'];

    $db    = Zend_Registry::get('dbAdapter');
    $query = $db->prepare(" 
        SELECT * 
        FROM `".self::table_name."` 
        WHERE 
            (
                (`module` = :module AND `controller` = :controller AND `action` = :action) OR
                (`module` = :module_2 AND `controller` = :controller_2 AND `action` = '') OR 
                (`module` = :module_3 AND `controller` = '' AND `action` = '')
            )
        AND enabled = 1 
        AND user_id = :user_id      
        ");

    $query->bindValue('module',         $module);
    $query->bindValue('module_2',       $module);
    $query->bindValue('module_3',       $module);
    $query->bindValue('controller',     $controller);
    $query->bindValue('controller_2',   $controller);
    $query->bindValue('action',         $action);
    $query->bindValue('user_id',        $user_id);

    $query->execute();
    $item = $query->fetch(PDO::FETCH_OBJ);
    $query->closeCursor();

    if (is_object($item)) {
        return $item;
    } else {
        throw new exception("Could not load user permission for this page ($module, $controller, $action)");
    }
}

and in the view:

    <?php if ($this->user_permissions->verify('movie')) { ?>
        <li class="parent">
            <img src="/design/images/icon/dvd.png" /> <span class="highlighter"><a href="/administration/movie/index">Movie</a></span>
            <?php if ($this->user_permissions->verify('movie', 'add')) { ?>
                 | <a href="/administration/movie/add">Add</a>
            <?php } ?>
            <?php if ($this->user_permissions->verify('movie', 'featured')) { ?>
                <ul>
                    <li>
                        <img src="/design/images/icon/order.png" /> <a href="/administration/movie/featured">Order Featured</a>
                    </li>
                </ul>
            <?php } ?>
        </li>
    <?php } ?>
azz0r