May 25, 2011

CakePHP Auth Conventions

CakePHP is something new that I'm picking up. My first exposure to web frameworks was Ruby on Rails. Like Rails, the Cake mantra is convention over configuration. It's an awesome way to program. You can save hours of work if you know what the conventions are and follow them. Even bypassing the conventions is not a superhuman effort, usually a few lines of configuration.

One issue I had with Cake was implementing controller authorization. As you might expect it's because I didn't know and follow convention.

I set up my AppController class to use the Auth module and set the mode to 'controller' in beforeFilter. That meant that each controller had to have isAuthorized function defined.

I knew that most of my controllers were going to be uniform in the rights granted to anonymous users, so I coded the isAuthorized method in the AppController too.

class AppController extends Controller {
    var $components = array('Auth', 'Session');
    var $uses = array('Role');

    function beforeFilter() {
        $this->Auth->authorize = 'controller';
    }

    function isAuthorized() {
        if (strcmp($this->action, "index") == 0 || strcmp($this->action, "view") == 0) {
            return true;
        }
        $user = $this->Auth->user('role_id');
        $admin = $this->Role->findByName('Admin');
        $admin = $admin['Role']['id'];
        if ($user == $admin) {
            return true;
        }
        else {
            return false;
        }
    }
}

I thought I'd allowed access to any "index" and "view" action program wide by always returning true from isAuthorized if those actions were requested. Admins had all CRUD rights and regular users had no additional rights over the anonymous users. Sub classes would further refine these rights if necessary.

The scheme didn't work as I expected though because the anonymous users seemed to be getting blocked from all actions, while regular users had the expected rights. Admins were also behaving as expected.

I had to modify my controller to this

class AppController extends Controller {
    var $components = array('Auth', 'Session');
    var $uses = array('Role');

    function beforeFilter() {
        $this->Auth->authorize = 'controller';
        $this->Auth->allow('view', 'index');
    }

    # defaulting all controllers to allow user access to view and index
    # anonymous access to be allowed by subclasses
    function isAuthorized() {
        $user = $this->Auth->user('role_id');
        $admin = $this->Role->findByName('Admin');
        $admin = $admin['Role']['id'];
        if ($user == $admin) {
            return true;
        }
        else {
            return false;
        }
    }
}

In the beforeFilter I had to specifically enable the actions I wanted open to anonymous use. I guess the Auth component will default to not authorize before checking the isAuthorized function if the session does not contain a user.

Spent a few hours tracking that down using pr($this->Auth->user()) calls and die to trace program flow.

No comments:

Post a Comment