Sunday, March 1, 2009

Re: ACL and Auth with additional checking

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+unsubscribe@googlegroups.com
For more options, visit this group at http://groups.google.com/group/cake-php?hl=en
-~----------~----~----~----~------~----~------~--~---

No comments: