Auth and Acl - Automatically updating user Aros

In my previous Auth and Acl tutorial I mentioned that I wasn’t including a way for users ARO to be automatically updated when a User’s group was updated. Well ‘hepper’ posted a patch to the AclBehavior in the comments to that article. And it seems like it will work, however I wanted a solution that didn’t involve hacking / replacing any core classes. I also don’t think the AclBehavior will be patched this late in 1.2’s development. So I’ve written up an afterSave() method for the User Model. This code could be easily placed into any AclBehavior model with minimal effort as well.

Show Plain Text
  1.  
  2. /**
  3.  * After save callback
  4.  *
  5.  * Update the aro for the user.
  6.  *
  7.  * @access public
  8.  * @return void
  9.  */
  10.     function afterSave($created) {
  11.         if (!$created) {
  12.             $parent = $this->parentNode();
  13.             $parent = $this->node($parent);
  14.             $node = $this->node();
  15.             $aro = $node[0];
  16.             $aro['Aro']['parent_id'] = $parent[0]['Aro']['id'];
  17.             $this->Aro->save($aro);
  18.         }
  19.     }
  20.  

Since a model’s afterSave() fires after behavior afterSave’s we can safely use the parent node to update our user’s ARO. I’ve been working on a Component to generate application menus based on the ACL as well. I hope to have it done soon, and when it is you’ll find it here.

Update: The original code I posted was incorrect, and didn’t take into account a non-perfect sync between your aros and group ids. The code above has been updated to reflect correct functionality. Instead of blindly using the Group.id it fetches the parent Aro node. I’ve tested it with the User and Group models I posted in my other Auth / Acl tutorials.

Comments

Greate. Better to not touch the core classes.

Looking forward to your menu component.

anonymous user on 8/9/08

Hey, great and thanks. I’ve been looking for this for the past 3 days.

But actually, this doesn’t work for me. The parentNode() is returning the new ‘foreign_key’ (the ‘group_id’) and not the ‘parent_id’, and I suppose this is the one that needs to be updated.

anonymous user on 8/9/08

Mark, I’m using a “last_login” field in my User model which I update upon a successful login. When I use this little callback no parent_id is set which starts causing problems if it’s not corrected. Would it be possible to make this a normal function within the model and call it from within the User::Add and User::Edit actions? I know doing it this way removes the “automagic” nature of it all but would I be missing any other functionality?

anonymous user on 9/9/08

Jason: As long as you remember to call it when needed it shouldn’t function any differently. Strange that it is complaining about a missing parent_id field. It doesn’t rely on any data in the Model to do its work.

mark story on 10/9/08

The only place I think I need to make a call to this function is after a user has been edited; is this correct? I’m also under the impression that this only needs to be placed in the User model or does it need to be added to the Group model as well? Finally, I haven’t tried the new code you’ve posted so I’m not sure if the error I was getting still exists. Thanks for posting all of these resources; it’s making my app better.

anonymous user on 12/9/08

@Jason:
If you have a schema where your groups belongs to other groups (like a tree structure, where you have a field called parent_id in your groups table, and the Tree Behavior attached to the model) you also need to define this aftersave behavior but in the group model, with some changes.

But if you have the common schema where you user belongs to a group and your groups doesn’t belong any group (they are always in the root of your aro tree ‘parent_id = null’) then the code will just sit in the user model.

anonymous user on 17/9/08

Thank you very much for this code

anonymous user on 4/11/08

Have your say:

*
* You can use Textile markup, but be reasonable