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