Tags

, , ,

Let me just preface this post, by saying that this is indeed a very much simplified example.
The main purpose here is not to provide a solution that will fit any application, but rather give a decent foundation to further expand your own solution.

The example is based on Auth (you should know a little about the Auth component), the Security component and a simple method in your App Model.

Let’s say we have some journaling app with Articles Controller, which has a “view” action.
We only want to allow access to view the journal articles to their owners, i.e. the users who’ve actually wrote the article (or journal entry).

To illustrate this further, I am going to attempt to access a URL such as:
http://www.example.com/articles/view/8

The goal is to ensure that article with ID = 8, actually belongs to me (AKA the currently logged in user), and if I start tampering with URL by changing the ID, the access to “view” should be denied (unless I happen to pick another article ID, which belongs to me).

In a typical setup we probably have an “articles” table with a “user_id” field, i.e. User hasMany Article.

With that in mind we can create an easy method in our App Model to check for user access:

function canAccess($userId = null, $primaryKey = null) {

        if($this->find('first', array('conditions' => array($this->alias.'.user_id' => $userId, $this->primaryKey => $primaryKey),
                                           'recursive' => -1))) {
            return true;
        }

        return false;
}

Update (05/13/2009): Some users pointed out that recursive = -1, might cause problems with following find() operations. I’ve updated the code to pass ‘recursive’ as part of the find(), which resets the value back to the original in the latest versions of cake. Either way, you don’t want to pull associated models on this simple check.
If you decide to reset recursive later on, or better yet, use Contanable behavior, this solution should work perfectly well.

The above function simply looks for a combination of Article.user_id and Article.id to ensure that the Article actually belongs to the logged-in user.

So how do we use this in the Articles controller?
Perhaps something like this:

function beforeFilter() {
   if (in_array($this->action, array('view'))) {
      if(!$this->Article->canAccess($this->Auth->user('id'), $this->params['pass'][0])) {
         $this->Security->blackHole($this);
      }
   }
}

If the action is “view”, we pass the logged-in user ID $this->Auth->user(‘id’) and the article ID $this->params['pass'][0] to check the access.
If no record with both ID’s is found, we “blackhole” the request, otherwise the access is allowed.

And there you have it, a very simple way to control row-level access to any action in your controller.
As mentioned, I’m hoping that this post will inspire some ideas and will let you expand further in your app.

P.S. Do not forget to include your Auth and Security components, and if you need to get some more info on both of them you can take a look at the CakePHP manual or even do a quick search on this blog.