Creating an Admin Section with CakePHP

1st February 2008 CakePHP
Admin Section with CakePHP

UPDATE: This article has been updated, check out the new version right away

With most database driven websites you will normally need an admin section that requires a username and password that will give you access to a control panel where you can add/edit/delete information. With CakePHP this is quite simple with the help of “admin routing”, in the blog tutorial we can access the posts via “http://cakephp/posts/” with admin routing we can separate out all admin functions by adding “http://cakephp/admin/posts/” to the URL.

In this post we are going to re-visit the blog tutorial and implement an admin section that will require a username and password. Any request to an admin specific action will result in a login page being shown if you are not logged in, we will also be using the “Bake” command line script so please go over my previous post if you don’t know how to use it.

Enabling Admin Routing

The first thing that needs to be done is to enable admin routing in the “core.php” file located at “/cakephp/app/config/core.php”, uncomment the line:

define('CAKE_ADMIN', 'admin');

This will set up the proper URL routing for your applications and will be applied to any URL with ‘admin’ before the controller action e.g. ‘/admin/posts/add/’ or ‘/admin/posts/edit/’. This could be set to anything you want however for this example we will stick to default value.

To make use of admin routing there needs to be a completely separate function in the controller for all admin functions and these are created by prefixing ‘admin_’ to function names, for example ‘admin_index()’ will be accessed from ‘/cakephp/admin/posts/’ and ‘admin_add()’ from ‘/cakephp/admin/posts/add/’. This makes it easier to separate all your admin logic from your regular controller logic. Along with separate functions for your admin section you will also need separate views and these files will be the same name as your controller functions.

Re-Baking the Blog Application

The next step is to re-bake the blog application but this time we will include all the admin functions, fire up the “bake” script in the command line and go through the steps of creating a controller and views for the ‘Posts’ model. Choose ‘yes’ when it asks to create methods for admin routing in the controller and choose ‘yes’ when it asks to create views for admin routing in the views.

Baking the Posts Controller

Open up “posts_controller.php” and you will have twice as many functions, 5 for regular actions and 5 additional functions that are all prefixed with ‘admin_’, these are your admin functions.

function admin_index() {
	$this->Post->recursive = 0;
	$this->set('posts', $this->Post->findAll());
}

Also look in your posts views folder and you will see a file for each of the admin functions except ‘delete’ which doesn’t require a view. Open up your browser and visit ‘/cakephp/admin/posts/’ to double check that everything is working, we now need to create the functionality for logging in as an admin and to lock down all the admin functions.

Admin Views in CakePHP

Creating the Users Table

We are going to modifying the “simple user authentication” example from the CakePHP Manual so the first thing we need to create is a ‘users’ table in the database.

CREATE TABLE `users` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`username` VARCHAR( 255 ) NOT NULL ,
`password` VARCHAR( 255 ) NOT NULL ,
`last_login` DATETIME NOT NULL 
);

This table is going to store our username and password, along with the last time we logged in to the admin section. The easiest way to add this information is by using PhpMyAdmin to enter a sample username and password. In this example I am going to be using md5 to create a hashed version of the password for security reasons and I suggest doing this on all your PHP websites. To create your hashed password simply create a test file in your root and view the file in your browser:

echo md5('samplepassword');

Users Model

First create the Users model by creating a file called ‘user.php’ in the models folder. This is a very simple file and will enable you to connect to the ‘users’ table in the database.

class User extends AppModel {
	var $name = 'User';
}

Users Controller

Next create a file called ‘users_controller.php’ in the ‘controllers’ folder, we will need two functions ‘login’ and ‘logout’ which, funnily enough will deal with logging in and logging out an admin user. First the login controller logic, we check that the form has been submitted by inspecting the form data, in CakePHP all the form data is stored in “$this->data”. Next we use the ‘findByUsername’ magic method to get the details of the user from the database with the submitted username. If the user is not empty and the passwords match then the user is legitimate and we go ahead and log them in. At this point if the passwords don’t match or no-one was found with that username we set an error that can be used in the view. We are now going to save the user information in a session as a means to check if the admin is valid, then we save the login time and finally redirect to the posts admin index page.

function login() {
	// if the form was submitted
	if(!empty($this->data)) {
		// find the user in the database
		$dbuser = $this->User->findByUsername($this->data['User']['username']);
		// if found and passwords match
		if(!empty($dbuser) && ($dbuser['User']['password'] == md5($this->data['User']['password']))) {
			// write the username to a session
			$this->Session->write('User', $dbuser);
			// save the login time
			$dbuser['User']['last_login'] = date("Y-m-d H:i:s");
			$this->User->save($dbuser);
			// redirect the user
			$this->Session->setFlash('You have successfully logged in.');
			$this->redirect('/admin/posts/');
		} else {
			$this->set('error', 'Either your username or password is incorrect.');
		}
	}
}

The logout function is very simple, it will delete the session that was created when logging in and finally redirect the user to the main posts page. This function does not need a corresponding view file because we do not need to display anything to the user, we just call a redirect.

function logout() {
	// delete the user session
	$this->Session->delete('User');
	// redirect to posts index page
	$this->Session->setFlash('You have successfully logged out.');
	$this->redirect('/posts/');
}

The Login View

Create a folder named ‘users’ in the views section and inside that create the view for the login action (login.thtml). If the login error is set because of a wrong password then display the error, next display the form with the username and password. I’ve used form helpers provided by CakePHP to create the labels and form inputs and made sure that the form action is directed to the login action of the users controller (‘/users/login/’).

<h1>Login</h1>
<?php
// if an error occured display the error message
if(isset($error)) {
	echo "<div class='error'>" . $error . "</div>";
}
?>
<form action="< ?php echo $html->url('/users/login/'); ?>" method="post">
<fieldset>
	<legend>Please Login</legend>
    <p> 
        < ?php echo $form->labelTag('User/username', 'Username:');? >
        < ?php echo $html->input('User/username', array('size'=>'60'));? >
    </p>
    <p> 
        < ?php echo $form->labelTag('User/password', 'Password:');? >
        < ?php echo $html->password('User/password', array('size'=>'60'));? >
    </p>
    <p>
        < ?php echo $html->submit('Login');? >
    </p>
</fieldset>
</form>

When it comes to form data CakePHP uses the ‘Controller/fieldname’ convention, this makes it very easy to save data from a form and to access the data in the “$this->data[‘controller’][‘fieldname’]” array.

Protecting your Posts Admin Area

In order to protect the admin area I’m going to create a function that will check the session for the existence of the user data, if the session data is not found then do not allow access to the action by redirecting to the login page. Create a file called ‘app_controller.php’ in the ‘app’ folder and code a function called ‘checkAdminSession()’ any functions that you create in this file will be accessible by any controller by simply calling “$this->methodName()”. This works because your controllers all extend AppController and will therefore inherit any function in that file.

function checkAdminSession() {
	// if the admin session hasn't been set
	if (!$this->Session->check('User')) {
		// set flash message and redirect
		$this->Session->setFlash('You need to be logged in to access this area');
		$this->redirect('/users/login/');
		exit();
	}
}

Next we’re going to make a check on the session before any admin action in the posts controller, you could add “$this->checkAdminSession()” in every admin function however we can use the “beforeFilter” function that will make the call for us before any controller action is called. To only make this check for admin functions we can check the ‘params’ array for admin and this will ensure that only admin controller actions will check for a session.

// called before every single action
function beforeFilter() {
	// if admin pages are being requested
	if(isset($this->params['admin'])) {
		// check the admin is logged in
		// this method is in the app_controller.php file
		$this->checkAdminSession();
	}
}

Wrapping Up

Adding a simple admin section for your CakePHP application is generally pretty easy, and because of the joys of MVC (Model, View, Controller) you can easily copy and paste most of the files to other projects your working on. The process of coding something in CakePHP is usually the same, create the database table, then the model, then the controller and finally the views for each controller function.

The biggest hurdle in CakePHP that I found was all the specific methods and conventions that the framework uses but keep experimenting, keep reading the manual and things should hopefully click and fall in to place.

Source Code

Here's the source code for this article, its a zip file containing the app folder of the CakePHP installation. Click here to download code

UPDATE: This article has been updated, check out the new version right away

Back to Home Page

Comments

Wiras Adi (27/02/2008 - 00:57)

Thanks James,

This is the simplest CakePHP authentication article I could find around, and it works just fine.

Nhut (04/04/2008 - 19:22)

Thanhks James

I am Nhut, I living Vietname.

Thanh you very much !

Kyle Decot (11/04/2008 - 10:25)

Thanks a lot. This saved me a lot of time.

Costis (14/04/2008 - 18:48)

Thanks very much. Very well written tutorial

Hĺvard Fossli (27/04/2008 - 07:29)

is it possible to get it ready to download?? not to easy to follow. looks nice thou!!

James (28/04/2008 - 05:31)

Hi Håvard, I'll create a quick sample in the next few days and make the source code available for download.

James

CakePhp Guru (19/05/2008 - 16:34)

Is there a way in App Controller to determine which controller is being called ?

James (20/05/2008 - 10:45)

@CakePhp Guru: You can look in the $this->params['controller'] from the app controller to see which controller is being used.

bjudson (22/05/2008 - 09:29)

Would there be any problem with putting the beforeFilter() function into the app_controller so that *all* admin actions regardless of controller are protected? For sites with lots of models this seems like it would be easier.

James (22/05/2008 - 11:15)

@bjudson: Yeah you can put the beforeFilter() method in your app_controller so its called before any controller action. I' usually put it in every controller just in case I need to do other controller specific bits of logic. Thanks for pointing that one out.

JackBlade (23/05/2008 - 11:33)

Thank you, James! Very helpful and concise.

Manojo (13/07/2008 - 10:48)

Hi,
with cake 1.2, does this work ? I get an error saying that checkAdminSession() is not defined in my controller (of course, it is defined in app_controller). Anything I can do ?

James (14/07/2008 - 05:11)

@Manojo : Yeah it should be working in 1.2, if you'd like zip up your app folder and send it to me in an email and I'll have a look for you if you still haven't fixed the issue.

Derek (27/07/2008 - 07:33)

I can't seem to get the password through to the User controller.

In my controller I have:
$pass = $this->data['User']['password'];

But $pass is always empty...?


Also, when I print out $this->data I get:
Array
(
[User] => Array
(
[username] => derek
[password] =>
)

)

{Square brackets are my addition... :}

James (29/07/2008 - 07:03)

@Derek: hmmm very weird, I would double check the actual form and make sure you are setting the name of the field correctly, although the value is missing and not the field so that may not help. If your still having trouble email me your MVC for the login and I'll take a look, otherwise let me know how you went on and fixed it.

lowe (11/08/2008 - 13:58)

It's a nice tutorial but, I'm having a problem, how come my login view does not appear.. several messages appear.. what am supposed to write on my url in order 2 c the ouput correct output....? Please help, i badly need to solve it, im going to use this as the bases for my project....

James (14/08/2008 - 00:55)

@lowe: Hmmm not too sure whats going wrong with your setup, try going to "/users/login" in your browser and make sure that you've setup your Model and View files correctly, let me know if you still having trouble or email me your source code to let me have a more detailed look.

onokung (13/10/2008 - 12:48)

I'm thai, thanks a lot for a good articles : )

Christian Sueiras (24/12/2008 - 10:35)

Thank you very much, you helped me a lot.

Dan (28/12/2008 - 07:14)

This doesn't seem to work correctly for me. The set-up is okay but when I post my log-in form it returns to the form page saying: "Notice (8): Undefined index: User [APP\controllers\users_controller.php, line 10]"

It also seems to truncate the values in the username and password field to just their first character.

I'm brand new to cakephp, so I'm not sure how to debug this - any ideas?

Thanks folks...

udkl (29/12/2008 - 06:25)

James, I guess, using the inbuilt Auth component would be easier.

James (30/12/2008 - 02:12)

@Dan: Hey, the "Undefined index" your getting is probably because the way your saving and accessing the User session variable, try outputting the contents of the variable using "pr($variable)" and see what exactly is being stored. As for the form values I've no idea whats going on there. Try outputting the form values in the controller at different stages to see if they are being changed or whether its coming directly from the form. Let me know how you get on cos that's quite a weird error.

@udkl: yes, using the Auth component would probably be easier but at the time of writing this I don't think I was aware of but, I may write a little update on how to use the Auth component but its quite well documented on the official site. Thanks for pointing that one out.

DRE (02/01/2009 - 20:57)

Thanks!

This was supereasy. I modified slightly your script so that it would simply be automatic for every admin method.

In app_controller, I added(modified your checkAdminSession)

function beforeFilter(){
if (isset($this->params['admin']) AND !$this->Session->check('User')) {
$this->Session->setFlash('You need to be logged in to access this area');
$this->redirect('/users/login/');
}
}

paul (20/01/2009 - 20:08)

Hi,

I'm new to Cake. Is there a simpler way to protect ALL resources with a login page instead of just protecting the admin pages?

Thanks.

James (21/01/2009 - 04:33)

@paul: Hi there, to protect all your controller actions you can just get rid of the 'admin' check in the beforeFilter() function, this make sure you are logged in for everything. A simpler way would be to password protect your server directory using a .htaccess file. Search on Google and you should come up with something to help you out.

eric (23/01/2009 - 14:29)

this is very cool. however, unless I am missing something, it kind of defeats the purpose since in the end, a regular user that is not in the admin panel can still add edit and delete posts. Is there any way to make it so you can only access the add, edit, delete functionality if the admin parameter is passed?

James (26/01/2009 - 10:13)

@eric: If you only want specific controller actions to be admin protected then you can just add the check at the top of the action. Can get a bit messy if you have a lot of actions though.

Mike (23/03/2009 - 00:34)

Can you please clarify the steps while doing the baking? I am getting a lot of prompts that are not shown above, and it's very confusing to know what options I should choose during the bake process to ensure it's all correct.

James (23/03/2009 - 11:34)

@Mike: this article uses the old cakephp 1.1 so its quite different from the latest release. What I'll try and do is update it and post a second version online when I can find the time. Thanks for commenting.

ohcibi (06/04/2009 - 12:42)

<pre>
function beforeFilter() {
if (empty($this->params[Configure::read('Routing.admin')]) || !$this->params[Configure::read('Routing.admin')]) {
$this->Auth->allow($this->params['action']);
}
}
</pre>
you can do the protecting admin panel from users stuff with this. by default auth disallows any action. if we dont call a admin route we can allow the requested action.

Patrick (10/04/2009 - 11:02)

Hi James:

Great set of articles you have here. It will definitely be added into my bookmarks.

Quick question for you here:

Would it be possible to add a "user_role" field to the users table, so that one might be able to conditionally present different content to admin users with different role values? Do you see any security issues with implementing such a function?

Thanks and Cheers!

James (14/04/2009 - 11:38)

@Patrick: Hey thanks for commenting and apologies for taking so long to reply, I don't see any problem with adding a "user_role" field and using it to change the content and certain users get.

Osciel (13/05/2009 - 06:40)

Thank you James. It was the piece of code I was looking for. Very clear and precise.

Stepas (24/05/2009 - 10:15)

Thanks! Was a great help for me, who is just starting to play with Cake.

handsofaten (16/10/2009 - 12:13)

If you are just starting with Cake now, you should really look into the AuthComponent. This tutorial was great for CakePHP 1.1, but there are better ways of doing it now.

Johnny (23/10/2009 - 08:02)

I second all the responses saying Auth is the way to go. The users_controller ends up being about 6 lines of code with Auth. Very handy. Less likely that you'd make a goof and compromise your system.

Great article on how to set up Admin Routing though. 5 Minutes and I had it running just the way I like it :)

hans (05/05/2010 - 07:21)

Wow, works like a charm.
CakePHP is really great stuff!

Add Your Comment

Sites I've Created

DSP Chartered Surveyors Stoneylane Aberdeen Angus

Recently Watched Films

Mr Brooks Rec Pathology Diary of the Dead

TV Shows

The Shield Flight of the Conchords

Hosting

I've been using Dreamhost for my hosting for over 2 years and it's pretty good for what you pay. If you're signing up how about showing me some love and use this link so I get a referal bonus to help pay my server costs.

Site by James Fairhurst © 2008-2012, all rights reserved and all that malarky