Getting Auth and Admin Routing to play nicely together

Here I’m going to show how to use CakePHP’s built-in Auth component and Admin Routing to easily build a protected, administrative area for your application.

I guess this post is somewhat on an intermediate level and I hope you know a little about Auth (or maybe even here) and Admin/Prefix Routing already…

Generally speaking, Auth is often used to allow one to create a web application with multiple user profiles (or accounts… registration, etc.).

However, sometimes, you could have a site, where no user registration is required, but a site owner (admin) needs to have their own control panel (or administrative area) where she can manage some aspects of the site.
This is not to be misunderstood that the two are mutually exclusive, but in this post I’m just going to focus on how to, as always, very quickly (thanks to cake ;)) to build an admin area.

Let’s imagine some web site where there is an Administrator who can add articles to her blog, browse a DB of contacts who’ve submitted questions or comments via some form on the site and do some other, generic, administrative duties.

Even though we won’t have any registered users on the site, to make life just a little bit easier we’ll still create users table, User Model, and Users Controller to handle our admin’s login, logout and homepage. (By default Auth works nicely with the convention of User/users model, controller, table, etc.)

Keeping in mind our Prefix/Admin routing… which we’ve already enabled in core.php Configure::write(‘Routing.admin’, ‘admin’);… we create a Users controller that looks something like this:

<?php
    class UsersController extends AppController {

        var $name ='Users';

        function admin_index() {
            // home page code... could be something as simple as 'Welcome Home' view.
        }

        function admin_login() {
            if($this->Auth->user()) {
                $this->redirect(array('controller'=>'users', 'action'=>'admin_index'));
            }
        }

        function admin_logout() {
           $this->Session->del('Auth.User');
           $this->redirect(array('controller'=>'pages', 'action'=>'display', 'home', 'admin'=>false));
        }
    }
?>

Looks rather simple…
Some might say where’s your beforeFilter() and Auth component? … we’ll handle that in the app_controller.php (just a little later).

Let’s take a look, first, at admin_login().
It could be an empty action, really, but I prefer to override default login() mechanism and basically say, that if the admin has been authenticated, aka $this->Auth->user() returns true, let’s direct her to the home page.

Now what about admin_logout()?
The most common way to build a logout() action is to do something like this:

function logout() {
   $this->redirect($this->Auth->logout());
}

Doing something like this is completely fine, but for my specific need and idea (plus an example of an alternative option to think about) I would rather redirect the admin back to the main page of the site.

Since we cannot rely on the automagic of $this->Auth->logout(), we need to “properly” log out the admin. In other words, we make sure that the session with the default key of ‘Auth.User’ is gone… hence: $this->Session->del(‘Auth.User’);. Then we simply redirect to the default homepage…
Quickly note the ‘admin’=>false in the redirect code… that simply gets rid of the /admin/ prefix in the URL (again one of the lovely cake’s prefix routing tricks).

Alright, so we’ve got the admin login, homepage and logout handled nicely…

Let’s do an amazingly simple Auth component setup in App Controller (app_controller.php)

<?php
class AppController extends Controller {

    var $components = array('Auth');

    function beforeFilter () {
        $this->Auth->autoRedirect = false;
    }

    function beforeRender () {
        $admin = Configure::read('Routing.admin');

        if (isset($this->params[$admin]) && $this->params[$admin]) {
            $this->layout = 'admin';
        }
    }
?>

Well, let’s see… we’ve included our Auth component and added a simple beforeFilter().
As mentioned before, I really didn’t want the admin to be redirected anywhere, except the actual administrator homepage upon login. Therefore, I override the default Auth behavior to redirect to the previously accessed page (aka $this->referer()) by using $this->Auth->autoRedirect = false;

Why?
Imagine a blog… and an admin decided to login from a link in the footer of the site from some random blog article. Obviously the admin wants to get to her admin area and not just get redirected back to the article. (Of course this is highly dependent on the application, but in my case that was a requirement and this apporach worked out quite nicely).

To be honest I’m not quite sure about the beforeRender() method, but it did the trick to switch the layout from ‘default’ to ‘admin’ based on the action name (again, as you see, ‘admin_’ is a built-in part of cake’s Prefix/Admin routing).
(Thanks for a nice solution provided by Mark Story for the layout switching).

Well, that’s all lovely, but what’s next?
As a matter of fact we’re pretty much done. Our app is ready to easily setup any administrative functions for any of your controllers.

Alright, let’s say we’ve got a Posts controller and we need to make one of our actions (let’s imagine /admin/posts/add) for administrator only…

All that needs to be done is the following:

<?php
   class PostsController extends AppController {

       var $name ='Posts';

       function beforeFilter() {
          parent::beforeFilter();

          $this->Auth->allow('index', 'view');
        }

       function index() {
         //something here
       }

       function view() {
       //something here too
       }

        function admin_add() {
            if(!empty($this->data)) {
                $this->Post->save($this->data);
            }
        }
    }
?>

First, we inherit our App Controller’s settings by using parent::beforeFilter();, then we ensure that regular/non-registered users can see (access) index() and view() actions.

Of course, our admin_add() action is nicely protected and would only be accessed if the admin had authenticated.

Now what do you do if you needed an administrative action in the Contacts Controller, that allows an admin to ‘browse’ contacts?…

Yep, you simply add admin_browse() to your Contacts Controller. Of course, as in the example above, any actions of this controller that need to be seen by regular users would need to be allowed in beforeFilter() as we just did.

(I guess there is no need to mention that all admin actions should be added to the admin layout or element as links for convenient navigation ;))

P.S. How do you add an admin account to the users table with a properly hashed password?
The easiest way, unless you wish to build an add_admin() action is to temporarily enable scaffolding, which will let you easily add new users (admins).

%d bloggers like this: