Using CAKE_ADMIN for multiple user types

By Egbert Teeselink (skrebbel)
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.

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 358

CakePHP Team Comments Author Comments
 

Comment

1 Great

I just asked about this a couple of days ago in the IRC channel. phpNut told me it would not be a feature for a future Cake version but this way I can still have it my way.

Small typo:
adminHack_hack(array('member', 'expert', 'superuser'));
should be:
adminHack(array('member', 'expert', 'superuser'));
Posted May 3, 2007 by TheVigilante
 

Comment

2 Note regarding to security

There is interesting 'feature' digged in Your code: imagine url example.com/controller/member_index/ - because no 'admin' prefix is used, then Your 'hack' will not define constant CAKE_ADMIN, and that will cause potentionally unsafe thing: none of Your 'admin' methods will be refused at dispatcher level.

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.
Posted May 3, 2007 by Jitka Koukalova
 

Comment

3 Sollution

So best add an else block behind the if block that contains a define('CAKE_ADMIN', 'admin'); ?
Posted May 4, 2007 by TheVigilante
 

Comment

4 A basic patch for the security issue

I think this is a really good idea, multiple admin types are commonplace and some solution for integrating them cleanly into CakePHP would be great.

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').
Posted May 11, 2007 by JP Mortiboys
 

Comment

5 Point to remember

This is a great idea, thanks. I come across a problem, have a think, google it - BANG - there is an answer straight away.

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.php

Are multiple admin routes a feature in 1.2?

Posted Oct 30, 2007 by John Elliott
 

Comment

6 A basic patch for the security issue with 1.2 Compliance

I think this is a really good idea, multiple admin types are commonplace and some solution for integrating them cleanly into CakePHP would be great.

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').



<?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
Posted Feb 4, 2008 by Nich
 

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]);
    }
  }
}
?>

Posted Feb 5, 2008 by JP Mortiboys