ACL with Groups

19 07 2007

In a previous article I explained how to use the AclBehavior to create models that can be used for ACL.

Now the situation is – I want to assign permission to groups and have Users be a member of a group. I thought this would be easy using the AclBehavior. Turns out I was right, but it took me while to realise I was right.

So we are going to use the following Group model. It should look familiar. The database has a parent_id field in the groups table. This model allows us to create a heirachy of group roles.

<?php
class Group extends AppModel {
  var $name = 'Group';
  var $actsAs = array('Acl');

  function parentNode(){
    if (!$this->id) {
      return null;
    }
		
    $data = $this->read();
		
    if (!$data['Group']['parent_id']){
      return null;
    } else {
      return $data['Group']['parent_id'];
    }
  }

}
?>

Now, for ACL to work the easy way, we need to have our users in the Aro table as well. In your users table you need a field called group_id, and then use the belongsTo association to relate the user to a group. We also need to use the AclBehavior to get our users into the aro table.

Thats all pretty simple and straight forward. The trick is making the user aros children of a group aro. The parentNode() function is where the magic happens. Instead of returning a single id, you return an array with a two keys: model and foreign_key. This will allow the AclBehavior to locate the appropriate parent Aro node.

<?php
class User extends AppModel {
  var $name = 'User';
  var $belongsTo = array('Group');
  var $actsAs = array('Acl');

  function parentNode(){
    if (!$this->id) {
      return null;
    }
		
    $data = $this->read();
		
    if (!$data['User']['group_id']){
      return null;
    } else {
      return array('model' => 'Group', 'foreign_key' => $data['User']['group_id']);
    }
  }

}
?>

Now your User Aros are children of Group Aros. Simply assign permissions to the Group Aros and the Users will automatically inherit them.

Advertisements

Actions

Information

18 responses

23 07 2007
Using AclBehavior in CakePHP 1.2 « Another Cake Baker

[…] article on the Auth Component. See my post Using AuthComponent and ACL in CakePHP 1.2 Also see Groups with ACL for using groups and users together In the meantime I highly recommend the article Access Control […]

29 08 2007
Mike

These articles you have written on Auth/ACL in 1.2 have been a godsend – don’t know where I’d have got started otherwise so, many thanks! A question though regarding the actual setup of the records in the groups db table. Given a group structure such as:
1 – Gods
2 – Demigods
3 – Titans
4 – Land
5 – Sea
6 – Air
7 – Mortals

I can see that the Land, Sea and Air Titans would have parent ID 3, but is it okay for Gods, Demigods and Mortals to have a parent ID of 0. Does zero equate to a top-level group or for the purposes of working well with Aro’s should it be set to something else?

29 08 2007
Geoff

Mike,

I think zero will work but it is better to pass null as the parent id, as seen with this condition

if (!$data[‘Group’][‘parent_id’]){
return null;
}

Geoff

4 11 2007
lemp

Hi Geoff,

I have implemented your method with success (with Cake 1.2.0.5875 Pre-Beta). I just realized that users ARO are not updated when a user changes group. I have found a discussion where you explained it was made by design and looking at the current source, it still is.

My question: do you have any suggestions on how to solve this issue? Implementing a custom aftersave in the model?

24 11 2007
darkangel

Hi Geoff,

Thanks for these articles.

Is the result of parentNode() supposed to be entered into the ‘parent_id’ field of the ACL table? It seems NULL is inserted into the table regardless of whether or not I specify a parent.

My function:


public function parentNode()
{
if (isset($this->data['Group']['parent_id'])) {
return $this->data['Group']['parent_id'];
} else {
return null;
}
}

Any ideas?

_da.

25 11 2007
darkangel

(ignore previous comment — solution found)

27 12 2007
Aran

I had a similar problem as darkangel. My solution was to modify the Group::parentNode() method from this tutorial.

Instead of:
return $this->data['Group']['parent_id'];

I had to borrow the code from the User model and make it:
return array('model' => 'Group', 'foreign_key' => $data['Group']['parent_id']);

The Acl behavior’s afterSave() method does a call to Group::parentNode(), and the result is then fed to the node() method. The node() method needs the array with ‘model’ and ‘foreign_key’ in it in order to work.

Now … when I add a new group and specify as the parent id, the id of another group in the Groups table, the corresponding id for the Group in the Aros table, will be set as the parent_id of the new Group in the Aros table.

15 02 2008
Brade

As lemp points out, all of this is useless if you can’t edit a user to change groups. The source code indicates parentNode only runs if you’re adding a new record, which to me seems totally retarded. That’s not your fault, of course, but obviously this Behavior stuff is not going to be nearly as easy as it could (and should) have been.

15 02 2008
Brade

Mmmkay, so I figured a workaround. Building on Aran’s solution (which is the only way I could get the parentNode to actually save properly), in my “edit” action, I substituted this code:

if ($this->Group->save($this->data['Group'])) {

with this:

if ($this->Group->save($this->data['Group'])) {
$this->Group->behaviors['Acl']->afterDelete($this->Group);
$this->Group->behaviors['Acl']->afterSave($this->Group, true);

I guess this is cheating a bit. I still don’t know why the Cake auths decided to run the Acl behavior’s afterSave logic only when adding a new record. But this solution seems to work for me right now.

15 02 2008
Brade

Okay, so don’t use my previous code when editing an item with children, because it will delete the children. >=[
I can probably make use of the fact that the Aro object in Group already “actsAs” the TreeBehavior, so I can use some TreeBehavior function to move the node instead of deleting and re-creating. Let’s find out…

16 02 2008
Brade

Indeed, this is the revised approach:
if ($this->Group->save($this->data['Group'])) {
$this->Group->Aro->set($this->Group->Aro->find("foreign_key = $id"));
$this->Group->Aro->saveField('alias', $this->data['Group']['name']);
$this->Group->Aro->setParent($this->Group->Aro->field('id', 'foreign_key = ' . $this->data['Group']['parent_id']));

…where $id is the parameter in the “edit” action.
The “saveField” line isn’t really necessary unless (like me) you want a copy of the group name as the alias, so it makes it easier to use “generatetreelist” on the index page, and have the names of your groups readily available.

Also in my “delete” action I do a check like this to see if a group has children:
} elseif ($children = $this->Group->Aro->children($this->Group->Aro->field('id', "foreign_key = $id"), true)) {
and set a flash message/redirect without deleting.

25 04 2008
21 01 2009
JM

I guess that a many to many relation between users and groups will break the hierarchical rule of “only one parent”, and that is not possible to implement such a solution with aclbehavior (i supose that from a strictly acl point of view it’s possible to have a user in more than one group).

Any input on subject will be welcome.

By the way, congrats for the artcile, it’s nice explained.

28 04 2009
coskunlar vinc

I think zero will work but it is better to pass null as the parent id, as seen with this condition

19 06 2011
parentNode function for ACL management. « krissp.net

[…] links : Baking MVCs ACL with groups Using ACL behavior Understanding how ACL works Cakephp v1.3 […]

1 07 2011
ParentNode function for ACL management – Cakephp. « krissp.net

[…] : Cakephp version 1.3 Useful links : Baking MVCs ACL with groups Using ACL behavior Understanding how ACL works Cakephp v1.3 manual ACL management based on groups […]

24 09 2011
CakePHP ACL and Groups « Ben Murden

[…] Ben on Sep.23, 2011, under Development There are many great tutorials out there to help you get started with the arcane ACL features of CakePHP, but none of […]

15 05 2013
Simpler role-based access control for CakePHP – jonisalonen.com

[…] to copy the solution from the ACL blog tutorial where users can have only one role (like Gilang, lemoncake, or realm3). That’s fine if in your application you never need more than one role per user, […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: