Multiple Admin Levels in CakePHP
Usually when building a website that has a content management system (CMS) you will need a control panel that will be accessed via a login page so that only administrators with a username and password can login and change the site. CakePHP comes equipped with "Admin Routing" that will automatically re route URLs such as http://cakephp/admin/posts/add to a controller action called admin_add this is extremely useful for admin panels.
The only drawback of using Admin Routing is that by default it can only accommodate a single admin user, recently I've been working on a project that requires multiple levels of users e.g. admins, members, and general visitors and I wanted a way of doing this in CakePHP. Luckily I found this solution in the bakery and I've slightly modified it to meet my needs.
Research
After a quick search on google I came across this article in the bakery which pretty much does the job. The initial function is quite basic but other CakePHP users have commented on the article to beef it up. The solution is a piece of code that's located in the bootstrap.php file and loaded every time a URL is requested.
The function works as follows:
- Takes an array of user types
- Process' and splits the URL of page
- If the first part of the URL is one of the usertypes it will dynamically assign it the 'CAKE_ADMIN'
- If no match is found a quick test is run to make sure the user isn't trying to access the action without the admin routing
Implementation
I've used a lot of the code from the bakery article however I've slightly modified the error checking process, initially the function was open to a few security concerns. If a user tried to access an admin function directly e.g. http://site/controller/admin_action without using the admin routing in the URL e.g. http://site/admin/controller/action then the action was processed as normal. My modified function takes this into account, users trying to access /controller/admin_action will be redirected to /admin/controller/action so that checks can be done to allow or deny access to the user.
// file: /app/config/bootstrap.php
/*
* adminHack()
* allows multiple admin users e.g. admin/posts/ and member/posts/
*/
function adminHack($admin_types)
{
// get the bits of the url
$bits = explode('/', $_GET['url'], 3);
// if a usertype is requested
if(in_array($bits[0], $admin_types)) {
// define usertype as an admin
define('CAKE_ADMIN', $bits[0]);
} else {
// init
$matches = array();
// explode the url that contains '_' e.g. /admin_index/
$morebits = explode('_', $bits[1]);
// check for this: url /controller/admin_action
if (preg_match('/^(' . implode('|', $admin_types) . ')\_/', $bits[1], $matches)) {
// redirect to this: /admin/controller/action
$url = $matches[1].'/'.$bits[0].'/'.$morebits[1];
header('Location: /'.$url);
}
}
}
// setup admin and member users
adminHack(array('admin', 'member'));
Controller Checks
Now that our system can accept multiple levels of users we can do a simple check in any controller by using the beforeFilter() method and checking the params array:
/*
* beforeFilter()
* before any action is done
*/
function beforeFilter() {
// if an admin route
if(isset($this->params['admin'])) {
// change the layout file
$this->layout = 'admin';
// check that the user has admin access
$this->checkAccess('admin');
}
// if a member route
if(isset($this->params['member'])) {
// change the layout file
$this->layout = 'member';
// check that the user has member access
$this->checkAccess('member');
}
}
For the project I'm working on I have created a simple function checkAccess() that will check user access depending on their user type and either allow them access to the action or redirect them to the login page with an error message, for more details on creating such a function check out my article on creating an admin section. If you are building a system with more user levels then you can just add another check of the params array like above.
Wrapping Up
So there you have it, multiple admin levels in CakePHP, just something that I came across for a project I'm currently working on and I thought I'd share how I implemented it in my code. If you have any problems or questions just let me know via a comment or by email and I'll try and help you out.
Comments
PandoO (16/05/2008 - 16:56)
Hello,
I know you're mainly working on v1.1. (i didn't know anything about that version). But i also know you're getting a closer look inside 1.2. I looked for "acl" in v1.2... and now that i read your article, i'm questionning myself : what is the best way for implementing administration rights ? should i better use acos/aros or transfer your vision of admin rights for 1.2 ?
(i hope you can understand my question, my english is sometimes not so good)
Thank's: o)
James (17/05/2008 - 05:20)
Hi PandoO, thanks for your comments. Generally the types of applications I'm working on only require a pretty simple user authentication so ACL would seem a bit overkill so I guess it depends on what your developing. If you have a complex application that has multiple users with different priviledges then I would go with ACL but otherwise just keep it simple.
PandoO (17/05/2008 - 13:57)
Thank's for reply ! Should read more on ACL ...
PS : i read your 1.2 tutos after posting yesterday, very nice, simple to understand, at least for someone that get inside cake few weeks ago and already has the basics. It gives a nice look on what and how to go through our applications. I am impatiently waiting for the next parts :o)
Cheers, Emmanuel.
Zoran (28/03/2009 - 15:29)
Hi James
At this line: $bits = explode('/', $_GET['url'], 3);
if $_GET['url'] is admin/controller/action, then:
$bits[0] is admin and $bits[1] is controller, i am confused cause later on you are using explode('_', $bits[1]) which should be the controller, but you say it is the action.
Where i am wrong?
James (29/03/2009 - 13:56)
@Zoran - hey thanks for commenting, yes $bits[0] does contain the 'admin' part of the url but an extra check is made on the action to ensure that the 'admin_' part is also being used i.e. 'admin_index'.
Zoran (30/03/2009 - 05:04)
Thank you for your response James, i think i get it, this is really helpful i think, cause it saves you from so much work with just one function. I also might use the phpGACL component, it looks much better than the built in ACL of cake.
abhikakade (26/04/2010 - 22:46)
Hi james,
First thanks for all your helpfull post .. im looking for some good tutorails on ACL (like dvd catalouge) ..
Thanks!
James (29/04/2010 - 04:46)
@abhikakade: Unfortunately I've not done much work with ACL, I'm sure a google search will point you in the right direction.
Prashant (25/06/2010 - 00:44)
This hack does not works in CakePHP 1.3. I tried it on 1.2 it works.