Using CAKE_ADMIN for multiple user types
A simple hack allowing to use the functionality of CAKE_ADMIN for more than one usertype.
Ever wanted to have pretty URLs for multiple separate types of users than just one, as core.php's CAKE_ADMIN allows? Try this little hack.
By default, Cake supports a way of making specific controller actions and views for a special class of users - generally intended for administrator views.
This is done by setting the CAKE_ADMIN define in /app/config/core.php with a string, such as "superuser". In that case, if a user accesses WEBROOT/superuser/book/add, BookController::superuser_add() will be called, rather than BookController::add() or maybe even SuperuserController::book('add') which is, of course, not what we intend to happen. This mechanism allows for prettier and more sensible URLs for these admin users.
But, Cake only supports one such admin view - if there are more types of administrators or access levels, each with their totally disjunct pages and functionality, how do we go about that? This little snippet will, albeit in a slightly ugly way, do the trick.
Put the following code in your /app/config/bootstrap.php:
Download code
In the above example, calling (for instance) /member/banana/peel/3 would cause BananaController::member_peel(3) to be invoked.
The situation
By default, Cake supports a way of making specific controller actions and views for a special class of users - generally intended for administrator views.
This is done by setting the CAKE_ADMIN define in /app/config/core.php with a string, such as "superuser". In that case, if a user accesses WEBROOT/superuser/book/add, BookController::superuser_add() will be called, rather than BookController::add() or maybe even SuperuserController::book('add') which is, of course, not what we intend to happen. This mechanism allows for prettier and more sensible URLs for these admin users.
But, Cake only supports one such admin view - if there are more types of administrators or access levels, each with their totally disjunct pages and functionality, how do we go about that? This little snippet will, albeit in a slightly ugly way, do the trick.
The solution
Put the following code in your /app/config/bootstrap.php:
Download code
<?php
/**
* We define CAKE_ADMIN differently for the different usertypes we
* want to support, acting on the currently called URL to decide
* which we tell Cake about.
*/
function adminHack($subdirs)
{
$firstSlash = strpos($_GET['url'],'/');
$firstSubdir = substr($_GET['url'], 0, $firstSlash);
if(in_array($firstSubdir, $subdirs))
{
define('CAKE_ADMIN', $firstSubdir);
}
}
//example usage:
adminHack(array('member', 'expert', 'superuser'));
?>
In the above example, calling (for instance) /member/banana/peel/3 would cause BananaController::member_peel(3) to be invoked.
Comments
Comment
1 Great
Small typo:
adminHack_hack(array('member', 'expert', 'superuser'));
should be:
adminHack(array('member', 'expert', 'superuser'));
Comment
2 Note regarding to security
So if someone with Your 'hack' in bootstrap will not test $this->action in access control system, or if it is depending on $this->params[CAKE_ADMIN] somehow, then he is in a big trouble.
Comment
3 Sollution
Comment
4 A basic patch for the security issue
Regarding the security issue mentioned above, try replacing the adminHack() function with something like the following code:
(It's not tested - just an idea.)
<?php
function adminHack($adminTypes) {
$bits = explode('/', $_GET['url'], 3);
if (in_array($bits[0], $adminTypes) {
// This admin route is being requested (officially)
define('CAKE_ADMIN', $bits[0]);
{
else {
$matches = array();
// Check if someone's trying to be naughty
if (preg_match('/^(' . implode('|', $adminTypes) . ')\_/', $bits[1], $matches)) {
define('CAKE_ADMIN', $matches[1]);
}
}
}
?>
This should stop URLs of the type example.com/banana/admin_peel/21.
It does this by detecting the /admin_ part of th URL and declaring that as the CAKE_ADMIN, which will convince CakePHP's default security bouncer to kick the user out - or whatever it usually does.
(Nice banana example btw - although according to the CakePHP bible the controller should be called 'BananasController').
Comment
5 Point to remember
If anybody tries this and it doesn't work - remember that you need to comment out the
define('CAKE_ADMIN', 'admin');line in app/config/core.phpAre multiple admin routes a feature in 1.2?
Comment
6 A basic patch for the security issue with 1.2 Compliance
<?php
function adminHack($adminTypes) {
$bits = explode('/', $_GET['url'], 3);
if (in_array($bits[0], $adminTypes)) {
//This admin route is being requested (officially)
Configure::write('Routing.admin', $bits[0]);
} else {
$matches = array();
// Check if someone's trying to be naughty
if (preg_match('/^(' . implode('|', $adminTypes) . ')\_/', $bits[1], $matches)) {
Configure::write('Routing.admin', $matches[1]);
}
}
}
?>
Rember to have Config:write('Routing.admin', '???'); commented in your core.php
happy hacking
Comment
7 Just in case...
<?php
function adminHack($adminTypes) {
$bits = explode('/', $_GET['url'], 3);
if (in_array($bits[0], $adminTypes) {
// This admin route is being requested (officially)
define('CAKE_ADMIN', $bits[0]);
{
else {
$matches = array();
// Check if someone's trying to be naughty
if (preg_match('/^(' . implode('|', array_map('preg_quote', $adminTypes)) . ')\_/', $bits[1], $matches)) {
define('CAKE_ADMIN', $matches[1]);
}
}
}
?>