Authentication & Authorization with Scaffolding

Though scaffolding is not recommended for production sites, I’ve found it quite handy when just getting started. Unfortunately, it doesn’t appear that the authentication/authorization (auth^2) mechanism works with scaffolding. You can, however, get auth*2 working manually with just a few lines of code.

First, follow the steps of the Simple Acl controlled Application tutorial from the CakePHP cookbook up to the section on logging in.

Next, we need to insert the code that updates the ARO when a user is added or edited. Normally you would place this code in your add/edit action in the users controller, but for scaffolded actions we’ll use another callback function. Add the following code to your users controller:

function _afterScaffoldSave($action) {
  $aro =& $this->Acl->Aro;
  $user = $aro->findByForeignKeyAndModel($this->data['User']['id'], 'User');
  $group = $aro->findByForeignKeyAndModel($this->data['User']['group_id'], 'Group');
  $aro->id = $user['Aro']['id'];
  $aro->save(array('parent_id' => $group['Aro']['id']));
  return TRUE;
}

Note: for scaffold callbacks you must return TRUE; or the scaffold will not finish building the page.

The above code has been modified from the original in the tutorial, which included a conditional that checked for a change in the user’s group. As far as I can tell scaffolding causes CakePHP to return only the updated record (even when using the _beforeScaffold method) so you’re unable to compare the old and new values. As a result you have to update the ARO with every update, even if the user’s group is not updated.

Finally, for scaffolded actions we need a way to determine if the user is authorized. The AuthComponent has all the functionality we need. Add the following function to any controller using scaffolding that needs auth*2:

function _beforeScaffold($action) {
  if ($this->Auth->user() == NULL && !in_array('*', $this->Auth->allowedActions) && !in_array($action, $this->Auth->allowedActions)) {
    $this->Session->write('Auth.redirect', '/' . $this->name . '/' . $action);
    $this->Auth->loginRedirect = array('controller' => $this->name, 'action' => $action);
    $this->redirect($this->Auth->loginAction, NULL, TRUE);
    return FALSE;
  } else if (!in_array('*', $this->Auth->allowedActions) && !in_array($action, $this->Auth->allowedActions) && $this->Auth->user() !== NULL && !$this->Acl->check($this->Auth->user(),$this->Auth->action())) {
    $url = '/' . implode('/',$this->Auth->loginAction) == $this->referer() ? '/' : $this->referer();
    $this->Session->setFlash('You do not have permission to perform that action.');
    $this->redirect($url, NULL, TRUE);
  }
}

This function checks to see if the user is logged in when accessing restricted actions. If not, the user is redirected to the login page. If so, and if the user is attempting to access a page for which he has no permissions, then the user is bounced back to the referring page.

Of course, you can skip all this if you build a skeleton CRUD using cake bake and specify not to use scaffolding.

One thought on “Authentication & Authorization with Scaffolding”

  1. If you’re using a version of CakePHP prior to 1.2.1.8004 you’ll also need the following instructions:

    Set up your users controller so that it will hash passwords (this will not be done automatically like it would if you were not using scaffolding). Add the following callback method to your users controller:

    function _beforeScaffold($action) {
      if (($action == 'edit' || $action == 'add') && isset($this->data)) {
        $this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
      }
      return TRUE;
    }

    The above function is called every time a scaffolded action is executed. The password hashing will only be performed if the add or edit actions are POSTed.

Comments are closed.