Thanks for posting all this. I've just started--yesterday--trying to
work out how to go about something similar. I haven't used ACL at all
and I was really confused about row-level access. It looks like I'll
be able to adapt your code for my use. The one big difference is that
I'll need to control a model that implements TreeBehavior (think
folder hierarchy).

One thing I'd change is in su_index(): put the check on group_id at
the top to avoid all that logic.

function su_index()
{
        $conditions = null;

        if ($this->Auth->user('group_id') == 4)
        {
                $papers = array();
                $user_id = $this->Auth->user('id');
                $nodes = $this->Acl->Aro->findByForeignKeyAndModel($user_id,
                'User');
                
                foreach ($nodes['Aco'] as $node)
                {
                        if ($node['model'] === 'Paper')
                        {
                                $papers[] = $node['foreign_key'];
                        }
                        
                        // Get children from volumes
                        if ($node['model'] === 'Volume')
                        {
                                $children = 
$this->Acl->Aco->children($node['id']);
                                
                                foreach ($children as $child)
                                {
                                        $papers[] = 
$child['Aco']['foreign_key'];
                                }
                        }
                }
                $conditions = array('Paper.id' => $papers);
        }
        
        $this->set('papers', $this->paginate($conditions));
}


Although, if it were my project, I'd make the admin group ID 1. If you
later add more groups you'll run into problems.

Having said _that_, though, I have to say that I'm a bit confused
about how this is supposed to work. If it's an admin, you want all
files to show up, otherwise, check ACL for access. You're getting the
$user_id from the session, but it appears that this is an admin-only
method. (Is 'su' your admin prefix?) If you want that an admin can see
the allowed Papers for a particular user, I'd think you'd need to pass
the ID as a param.

Did I read that correctly?

On Sun, Mar 1, 2009 at 1:43 PM, Aidan Lister <aidanlis...@gmail.com> wrote:
>
> The final piece of the puzzle.
>
> The only thing remaining was displaying the list of papers and volumes
> that a user had access to, instead of all the papers/volumes.
>
> <?php
>    // papers_controller.php
>    function su_index()
>    {
>        $papers = array();
>        $user_id = $this->Auth->user('id');
>        $nodes = $this->Acl->Aro->findByForeignKeyAndModel($user_id,
> 'User');
>        foreach ($nodes['Aco'] as $node) {
>            if ($node['model'] === 'Paper') {
>                $papers[] = $node['foreign_key'];
>            }
>
>            // Get children from volumes
>            if ($node['model'] === 'Volume') {
>                $children = $this->Acl->Aco->children($node['id']);
>                foreach ($children as $child) {
>                    $papers[] = $child['Aco']['foreign_key'];
>                }
>            }
>        }
>        $conditions = array('Paper.id' => $papers);
>
>        if ($this->Auth->user('group_id') == 4) {
>            $conditions = null;
>        }
>
>        $this->set('papers', $this->paginate($conditions));
>    }
> ?>
>
> The same applies to the volumes controller, but a little simpler as
> you don't need the hierarchy.
>
> There must be an easier way to retrieve a set of Model records given
> an ARO and a parent ACO, but I couldn't find it.
>
> If anyone has any tips/suggestions, let me know.
>
> HTH,
> Aidan
>
>
> On Mar 2, 12:13 am, Aidan Lister <aidanlis...@gmail.com> wrote:
>> For the archives, this is a step-by-step on how I solved the problem:
>>
>> Rather than controllers/Papers/view/n which becomes unwieldy given you
>> have to create an ACO for each action, I instead created an ACO for
>> each row in my two models. Thanks to markstory for the suggestion.
>>
>> I created the following ACO heirachy:
>>     Papers/<volume id>/<paper id>
>>
>> This allowed me to give editors access to a volume, which
>> automatically gives access to the papers inside. This is the beauty of
>> ACLs.
>>
>> I created the ACO tree like so (using acltool, a custom cake shell
>> component):
>>
>> <?php
>>     // $ cake acltool aco_models
>>     function aco_models()
>>     {
>>         $this->out('Starting models sync');
>>         $Paper  = ClassRegistry::init('Paper');
>>         $Volume = ClassRegistry::init('Volume');
>>
>>         // Create the root node
>>         $root_alias = 'papers';
>>         $this->Aco->create();
>>         $this->Aco->save(array('parent_id' => null, 'model' => null,
>> 'alias' => $root_alias));
>>         $aco_root = $this->Aco->id;
>>
>>         // Iterate all the volumes
>>         $volumes = $Volume->findAll();
>>         foreach ($volumes as $volume) {
>>             // Create a node for the volume
>>             $this->out(sprintf('Created Aco node: %s/%s', $root_alias,
>> $volume['Volume']['number']));
>>             $this->Aco->create();
>>             $row = array('parent_id' => $aco_root, 'foreign_key' =>
>> $volume['Volume']['id'], 'model' => 'Volume', 'alias' => $volume
>> ['Volume']['number']);
>>             $this->Aco->save($row);
>>             $parent_id = $this->Aco->id;
>>
>>             // Iterate all the papers
>>             $papers = $Paper->find('all', array('conditions' => array
>> ('volume_id' => $volume['Volume']['id']), 'recursive' => -1));
>>             foreach ($papers as $paper) {
>>                 // Create a node for the paper
>>                 $this->out(sprintf('Created Aco node: %s/%s/%s',
>> $root_alias, $volume['Volume']['number'], $paper['Paper']['slug']));
>>                 $this->Acl->Aco->create();
>>                 $row = array('parent_id' => $parent_id, 'foreign_key'
>> => $paper['Paper']['id'], 'model' => 'Paper', 'alias' => $paper
>> ['Paper']['slug']);
>>                 $this->Acl->Aco->save($row);
>>             }
>>         }
>>     }
>> ?>
>>
>> Once all the ACOs are created, I gave access to my editors and authors
>> like so:
>>
>> <?php
>> // $ cake acltool vol_perms
>> function vol_perms()
>> {
>>         // Row level access for volumes
>>         $this->out('Creating row-level permissions for volumes');
>>         $Volume = ClassRegistry::init('Volume');
>>         $volumes = $Volume->findAll();
>>         foreach ($volumes as $vol) {
>>             $this->out(sprintf('- Entering volume number %s', $vol
>> ['Volume']['number']));
>>             $Volume->id = $vol['Volume']['id'];
>>             foreach ($vol['User'] as $user) {
>>                 $this->out(sprintf('-- Granting access to %s', $user
>> ['name']));
>>                 $User->id = $user['id'];
>>                 $this->Acl->allow($User, $Volume);
>>             }
>>         }}
>>
>> ?>
>>
>> Next we need to inform our models about our chosen ACO structure:
>>
>> <?php
>>     // volume.php
>>     function parentNode()
>>     {
>>         return null;
>>     }
>>
>>     // paper.php
>>     function parentNode()
>>     {
>>         if (!$this->id && empty($this->data)) {
>>             return null;
>>         }
>>         $data = $this->data;
>>         if (empty($this->data)) {
>>             $data = $this->read();
>>         }
>>         if (empty($data['Paper']['volume_id'])) {
>>             return null;
>>         } else {
>>             return array('Volume' => array('id' => $data['Paper']
>> ['volume_id']));
>>         }
>>     }
>> ?>
>>
>> Next, in our controllers that we wish to handle the row-level access
>> we do the following:
>>
>> In beforeFilter, we check that they're not an admin, then we apply our
>> Acl check. This relies on the fact that a) access is blocked to users
>> by the 'controllers' Aco tree and b) access is granted to editors/
>> volumes to this controller by the 'controllers' Aco tree. Both of
>> these constraints are enforced by Auth (with $this->Auth->authorize =
>> 'actions').
>>
>> <?php
>>         // Check row-level access
>>         if (isset($this->params['pass'][0]) && $this->Auth->user
>> ('group_id') < 4) {
>>             $aco = $this->Acl->Aco->findByModelAndForeignKey('Paper',
>> $this->params['pass'][0]);
>>             $aro = $this->Acl->Aro->findByModelAndForeignKey('User',
>> $this->Auth->user('id'));
>>             if (!$this->Acl->check($aro['Aro'], $aco['Aco'])) {
>>                 $this->Session->setFlash($this->Auth->authError);
>>                 $this->redirect(array('su' => true, 'controller' =>
>> 'papers', 'action' => 'index'));
>>             }
>>         }
>> ?>
>>
>> And that's it. If anyone has any improvements or suggestions I'd love
>> to here them.
>>
>> Cheers,
>> Aidan Lister
>>
>> On Mar 1, 4:36 pm, Aidan Lister <aidanlis...@gmail.com> wrote:
>>
>> > Hello,
>>
>> > I need to do some additional row level ACL access control for two of
>> > my models.
>>
>> > My system has the following groups: admins, editors, authors and
>> > users.
>>
>> > I'm restricting access to my controller actions using the Auth
>> > component, via $this->Auth->authorize = 'actions'.
>>
>> > At the moment, my authors have access to "controllers/Papers/view", I
>> > need to be able to limit their access to "controllers/Papers/view/n".
>> > Whether I use a custom query to check access to "n" or an ACL, I don't
>> > mind, both are feasible so whichever is easier.
>>
>> > Similarly, I need to control access to "controllers/Volumes/view/n"
>> > for editors.
>>
>> > Does anyone have any suggestions for achieving this?
>>
>> > Thanks,
>> > Aidan
> >
>

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"CakePHP" group.
To post to this group, send email to cake-php@googlegroups.com
To unsubscribe from this group, send email to 
cake-php+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/cake-php?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to