|

CakePHP ACL and Auth: Record Level Protection

View full index of ACL Tutorial Articles

The most common initial question people have when learning about the CakePHP ACL component is: “how do I let a user edit her own profile BUT only that one profile”?

The short story is that the CakePHP ACL component does not, nor is meant to, automatically provide this kind of record level protection.

If you want to add protection to individual articles, you must do this on your own.

Most commonly, we want to:

  • let a user edit his own record, BUT only that one record
  • continue to let administrators edit all user records

Assume that we have a UsersController with the standard actions: index, view, edit, add.

Restricting Users to Only Edit Their Own Records

We want the typical users to be able to view and edit their own records. The easiest way to restrict this, is to create the following function in the UsersController:

function checkUsersOwnRecord($recordId = null)
{
    if( $this->Auth->user('id') == $recordId ){
        return TRUE;
    } else {
	return FALSE;
    }
}

Now, in the edit and view functions, we can make a call to the checkUsersOwnRecord() function using the record id requested in the URL. If the function returns TRUE, then we can continue displaying or processing the view logic which will let the user edit only her own record.

In order to allow this on the ACL side of things we have to set up the following:

  • the user needs to be associated with a particular row in the ARO table
  • this ARO needs to have access to the ACO associated with the UsersController
  • specifically, the ARO needs to have access to the edit and view functions

For this last point, if you are using ‘actions’ mode, then you will need to grant ALL users access to the UsersController::edit() and UsersController:view() actions.

If you are using crud mode, you will need to grant ALL USERS “UPDATE” and “READ” access to the UsersController.

The last sentence highlights one of the main pitfalls of working with the crud mode.

If we grant all users READ access to the UsersController, then all users will also have access to the UsersController::index() action, and this might not be desirable, for we might not want ALL users to be able to view a complete listing of users in the system.

There is a solution to this, but first, let’s get back to the initially stated requirement that we want to give administrators the ability to edit all user records.

Allowing An Admin Full Access to All Records

One really straightforward way to allow an admin access to all records would be to create some sort of exception in our checkUsersOwnRecord() function. Maybe we could check to see if the logged in user is part of the admin group, and if they are, then let them edit the record.

But this is going to lead to problems down the road if we start adding different levels of administration. In general, we don’t want to add this kind of hard-coded ACL check into our Controller code.

Under ‘actions’ Mode

The right solution under ‘actions’ mode is to create a new action in your UsersController called admin_edit.

This new action would provide edit access to all users. There would be a corresponding ACO node and anyone who was allowed to have full access to all user records can be granted access to this ACO node.

Your ACO tree might look like this:

Users
---- index [maybe only admins get access to this]
---- edit [this is the action restricted with a function]
---- admin_edit [this allows full access]

Under ‘crud’ Mode

The problem is slightly more difficult in crud mode. By granting a user access to “READ” rights, you let that user have access to all actions normally associated with READ. Also, by granting a user access to “UPDATE” rights, you are going to let that user have access to all actions associated with UPDATE.

One slightly ugly solution would be to remap the UsersController actions in the beforeFilter() function.

$this->Auth->actionMap['index'] = 'delete';
$this->Auth->actionMap['view'] = 'read';
$this->Auth->actionMap['edit'] = 'read';
$this->Auth->actionMap['admin_edit'] = 'delete';
$this->Auth->actionMap['delete'] = 'delete';

Here, we would create a new function called admin_edit which would allow edit access to any user record. The index of these records would be viewable with the index view. Access rights would be handled by granting admins “DELETE” access to UsersController().

The solution I prefer, however, is to create a brand new controller called, UserprofilesController. This new controller will have the standard actions and views.

We can associate it with the User model by putting the following at the top of the class:

public $uses = array('User');

Now you can grant users various levels of access to this controller and in doing so control who gets access to the various levels of user administration.

Meanwhile, on the UsersController dumb down your index function, so that it doesn’t return anything to the user. And you might as well just remove the delete() function entirely. To keep things simple, just grant everyone READ And UPDATE access to UsersController, and leave it at that.

Our ACO tree will look like this:

Users
Userprofiles

All users will have READ and UPDATE access to Users, but only admins will have access to Userprofiles.

Deciding Between ‘crud’ and ‘actions’ Mode

I’m not really sure which is the better option, but the issue of record level access control might be the biggest reason why you might want to go with ‘actions’ mode or ‘crud’ mode.

Under ‘actions’ mode, you will need to create special admin_ actions for each controller which is going to have record level access control. This might make your ACO node tree bigger than it needs to be if, for each controller, you have to also have an admin_index, admin_edit, and admin_create function.

Under ‘crud’ mode, you will wind up with more controllers. But those controllers will contain fewer actions. But overall, you will have fewer nodes in your ACO tree.

Sunday March 1, 2009

|

rss feed iconRSS / Atom

Aran World

My Amazon.com Wish List

I'm Aran Johnson and I make websites.

I primarily use: PHP, MySQL, SubVersion, CakePHP, TextPattern, Cream Text Editor, and Addi Turbo Needles

Contact Me

My website portfolio

Recently

Oakland Parking Violations and Fines

Tweet Design

CakePHP ACL and Auth: Record Level Protection

How To Play Hearts With Only Two Players

Facebook Privacy Settings

CakePHP Tutorials

CakePHP ACL and Auth: Record Level Protection

CakePHP ACL and Auth: Sample Website

CakePHP ACL and Auth Tutorial: Database Setup

CakePHP ACL Tutorial: Introduction

CakePHP ACL Tutorial: Usage With Auth Component

CakePHP ACL Tutorial: Initial Setup

CakePHP ACL Tutorial: Auth Component Example

CakePHP ACL Tutorial: How To Check Access

Cake PHP ACL Tutorial: The Database Tables

My Flickr

Advertisement:
Protect Your LCD or Plasma TV Screen From Accidents

All content © Aran Johnson