DarkAuth v1.3 - an alternative Auth

by theChrisWalker
An update to the original DarkAuth component (http://bakery.cakephp.org/articles/view/darkauth-another-way), with increased efficiency and easier access control. It works exactly how I want to, so it might not be your first choice, but it's a solid alternative to the inbuilt AuthComponent. Main Features: - Per action / per controller / inline access control - Group support for HABTM and BelongsTo Group associations, or disable groups control completely. - A "super-user" functionality allowing one group automatic access granted - Optional tamper-proof Cookie support - Custom password hashing to suit your Model - Passes User Info and Access Matrix to view - Methods to access User Info / Access Matrix in Controller

Update: 14th May 2008

After some discussion regarding this component (thanks Sam), I thought I would point out at this stage that the component is really geared towards "Role-based" authentication, something I hadn't even noticed at while building it.

That being said, it makes it even more unlikely that it would be used in a non-HABTM type situation - so be aware if you are thinking of using this is a "User belongsTo Group" that you may find it hard to get the fluid access control you want.

On the other hand if you are using it purely to restrict access with NO group control, then it couldn't be easier!


Firstly thanks to everyone who encouraged me after the first release of DarkAuth. Looking back at it, it was flawed yet I got support from the community and have worked to better it.

Again, I'd better whet your appetite with just how easy it is to use.

Let's say you have a SecretsController which you want to restrict access to:

Controller Class:

<?php 
class SecretsController extends AppController {
  var 
$name 'Secrets';
  var 
$_DarkAuth;

...

}
?>

See what I did there? Now you'd have to be logged in to access this Controller. So, now you want to refine this to allow only logged on users that are a member of the group Admin or the group SecretKeepers. Easy!

Controller Class:

<?php 
class SecretsController extends AppController {
  var 
$name 'Secrets';
  var 
$_DarkAuth = array('required'=>array('Admin','SecretKeepers'));

...

}
?>

Not bad eh? So moving on, by default your users if not allowed access will see your specified "deny" page, but you want them to be redirected back to /public. Again, straightforward.

Controller Class:

<?php 
class SecretsController extends AppController {
  var 
$name 'Secrets';
  var 
$_DarkAuth = array(
         
'required'=>array('Admin','SecretKeepers'),
         
'onDeny'=>'/public'
      
);

...

}
?>

Now, any attempt to access your SecretsController will be presented with a login page if not logged in and if logged in but not a member of the group, will be redirected to /public.

This can be done per action as well, to further enhance the usefullness of the component. E.g. you have a DocumentsController which might have some top secret documents in it. You only want members of the group TopBrass to be able to see them.

Controller Class:

<?php 
  
function display($id=0){
    
$this->Document->id $id;
    
$doc $this->Document->read();

    if(
$doc['Document']['top_secret'] == true){
       
$this->DarkAuth->requiresAuth('TopBrass');
    }
    ...

  }
?>

Note that the function DarkAuth::requiresAuth() stops processing after you call it and will bring up an "access denied" view if authentication fails. So no need to exit().

Or more common if you wanted to show content based on whether authentication was present? The DarkAuth::isAllowed() function returns whether access is allowed, but doesn't stop processing:

Controller Class:

<?php 
  
if($this->DarkAuth->isAllowed(array('ChocolateLover'))){
    
$data $this->CookieJar->findAll(array('Chocolate'=>true));
  }else{
    
$data $this->CookieJar->findAll(array('Chocolate'=>false));
  }
?>

The final selling point (in my opinion)! $_DarkAuth available in the View, automatically populated with the user info from the user model and the access control list. e.g.

View Template:


<?php
  pr
($_DarkAuth);
?>

Yields if logged in:


array(
      'User' => array(
                      'id' => 1
                      'username' => superstar
                      'password' => abcdef1234567890abcdef1234567890
                      'other_info' => Some data
                )
  'Access' => array(
                    'group_you_have_access_to' => 1
                    'another_group_you_have_access_to' => 1
                    'group_you_have_NO_access_to' => 0
              )
)

Which means you can do this:

View Template:


<?php
if(!empty($_DarkAuth['User'])){ 
  echo 
"Some content for logged in people!";
}
if(
$_DarkAuth['Access']['some_group']){
  echo 
"You have access to 'some_group'";
}else{
  echo 
"You don't have access to 'some_group'";
}
?>

Convinced? I hope so. Now on the Code and Setup!


Here's the v1.3 Code.

Component Class:

<?php 
class DarkAuthComponent extends Object {

  var 
$user_model_name 'User';
  var 
$user_name_field 'email'//e.g. email or firstname or username...
  
var $user_name_case_folding 'lower'//do you want to case fold the username before verifying? either 'lower','upper','none', to change case to lower/upper/leave it alone before matching.
  
var $user_pass_field 'pswd';
  var 
$user_live_field 'live'// surely you have a field in you users table to show whether the user is active or not? set to null if not.
  
var $user_live_value 1;
  var 
$group_model_name 'Group'//Group for access control if used, if not used please set to an empty string. NB: DON'T CALL requiresAuth with Groups if no group model. it will error.
  
var $group_name_field 'name'// the name of the field used for the groups name. This will be used to check against passed groups.
  
var $HABTM true//set to false if you don't use a HABTM group relationship. Ignore if no association.
  
var $superuser_group 'Root'//if you want a single group to have automatically granted access to any restriction.
  
var $login_view '/login';  //this is the login view, usually {user_controller}/login but you may have changed the routes.
  
var $deny_view '/deny';  //this is the default denied access view.
  
var $logout_page '/'// NB this is were to redirect AFTER logout by default
  
var $login_failed_message '<p class="error">Login Failed, Please check your details and try again.</p>'//This message is setFlash()'d on failed login.
  
var $logout_message '<p class="success">You have been succesfully logged out.</p>'//Message to setFlash after logout.
  
var $allow_cookie false//Allow use of cookies to remember authenticated sessions.
  
var $cookie_expiry '+6 Months'//how long until cookies expire. format is "strtotime()" based (http://php.net/strtotime).
    
var $session_secure_key 'sRmtVStkedAdlxBy'//some random stuff that someone is unlikey to guess. 

  /*
   * You can edit this function to explain how you want to hash your passwords.
   * Also you can use it as a static function in your controller to hash passwords beforeSave
   */
  
function hasher($plain_text){
    
$hashed md5('dark'.$plain_text.'cake');
    return 
$hashed;
  }

##########################################################################
 /*
  * DON'T EDIT THESE OR ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU'RE DOING
  */
  
var $controller;
  var 
$here;
  var 
$components=array('Session');
  var 
$current_user;
  var 
$from_session;
  var 
$from_post;
  var 
$from_cookie;
  
  
  function 
startup(&$controller){
  
      
//Let's check they have changed the secure key from the default.
        
if($this->session_secure_key == 'sRmtVStkedAdlxBy'){
            die(
'<p>Please change the DarkAuth::session_secure_key value from it default.</p>');
        }
        
    
$this->controller $controller;
    
    
$this->here substr($this->controller->here,strlen($this->controller->base));
      
    
$this->controller->_login();
    
    
//now check session/cookie info.
    
$this->getUserInfoFromSessionOrCookie();

    
//now see if the calling controller wants auth
    
if( array_key_exists('_DarkAuth'$this->controller) ){
      
// We want Auth for any action here
      
if(!empty($this->controller->_DarkAuth['onDeny'])){
              
$deny $this->controller->_DarkAuth['onDeny'];
            }else{
              
$deny null;
            }
            if(!empty(
$this->controller->_DarkAuth['required'])){
              
$this->requiresAuth($this->controller->_DarkAuth['required'],$deny);
            }else{
        
$this->requiresAuth(null,$deny);
      }
    }
    
//finally give the view access to the data
    
$DA = array(
      
'User'=>$this->getUserInfo(),
      
'Access'=>$this->getAccessList()
    );
    
$this->controller->set('_DarkAuth',$DA);
  }

    function 
secure_key(){
        static 
$key;
        if(!
$key){
            
$key md5(Configure::read('Security.salt').'!DarkAuth!'.$this->session_secure_key);
        }
        return 
$key;
    }

  function 
requiresAuth($groups=array(),$deny_redirect=null){
        if( empty(
$this->current_user) ){
            
// Still no info! render login page!
            
if($this->from_post){
                
$this->Session->setFlash($this->login_failed_message); 
            }
      
$this->controller->render($this->login_view);
      exit();
    }else{
      if(
$this->from_post){
                
// user just authed, so redirect to avoid post data refresh.
                
$this->controller->redirect($this->here,null,null,true);
                exit();
      }
      
// User is authenticated, so we just need to check against the groups.
      
if( empty($groups) ){
        
// No Groups specified so we are good to go!
        
$deny false;
      }else{
        
$deny = !$this->isAllowed($groups);
      }
      if(
$deny){
        
// Current User Doesn't Have Access! DENY
        
if($deny_redirect){
                    
$this->controller->redirect($deny_redirect);
                    exit();
                }else{
                    
$this->controller->render($this->deny_view);
                    exit();
                }
      }
    }
    return 
true;
  }
 
  function 
isAllowed($groups=array()){
    if( empty(
$this->current_user) ){
      
// No information about the user! FALSE
      
return false;
    }else{
      
// User is authenticated, so we just need to check against the groups.
      
if( empty($groups) ){
        
// No Groups specified so we are good to go! TRUE
        
return true;
      }
      
      if(!
is_array($groups)){
        
//if a string passed, turn to an array with one element
        
$groups = array(=> $groups); 
      }
      
      
$access $this->getAccessList();
            
      foreach(
$groups as $g){
        if(
array_key_exists($g,$access) && $access[$g]){
          return 
true;
        }
      }
    }
  }
  
  function 
getCookieInfo(){
        if(!
array_key_exists('DarkAuth',$_COOKIE)){
            
//No cookie
            
return false;
        }
        list(
$hash,$data) = explode("|||",$_COOKIE['DarkAuth']);
        if(
$hash != md5($data.$this->secure_key())){
            
//Cookie has been tampered with
            
return false;
        }
        
$crumbs unserialize(base64_decode($data));
        if(!
array_key_exists('username',$crumbs) ||
             !
array_key_exists('password',$crumbs) ||
             !
array_key_exists('expiry'  ,$crumbs)){
            
//Cookie doesn't contain the correct info.
            
return false;
        }
        if(!isset(
$crumbs['expiry']) || $crumbs['expiry'] <= time()){
            
//Cookie is out of date!
            
return false;
        }
        
//All checks passed, cookie is genuine. remove expiry time and return
        
unset($crumbs['expiry']);
        return 
$crumbs;        
  }
  
  function 
setCookieInfo($data,$expiry=0){
      if(
$data === false){
            
//remove cookie!
            
$cookie false;
            
$expiry 100//should be in the past enough!
      
}else{
            
$serial base64_encode(serialize($data));
            
$hash md5($serial.$this->secure_key());
            
$cookie $hash."|||".$serial;
        }
        if(
$_SERVER['SERVER_NAME']=='localhost'){
          
$domain null;
        }else{
          
$domain '.'.$_SERVER['SERVER_NAME'];
        }
        return 
setcookie('DarkAuth'$cookie$expiry$this->controller->base$domain);
  }

  function 
authenticate_from_post($data){
        
$this->from_post true;
        return 
$this->authenticate($data);
  }
  function 
authenticate_from_session($data){
        
$this->from_session true;
        return 
$this->authenticate($data);
    }
    function 
authenticate_from_cookie(){
        
$this->from_cookie true;
        return 
$this->authenticate($this->getCookieInfo());
    }
    
  function 
authenticate($data){
        if(
$data === false){
            
$this->destroyData();
            return 
false;
        }
    if(
$this->from_session || $this->from_cookie){
      
$hashed_password $data['password'];
    }else{
      
$hashed_password $this->hasher($data['password']);
    }    
    switch(
$this->user_name_case_folding){
            case 
'lower':
                
$data['username'] = strtolower($data['username']);
                break;            
            case 
'upper';
                
$data['username'] = strtoupper($data['username']);
                break;
            default: break;
    }
    
$conditions = array(
      
$this->user_model_name.".".$this->user_name_field => $data['username'],
      
$this->user_model_name.".".$this->user_pass_field => $hashed_password
    
);
    if(
$this->user_live_field){
      
$field $this->user_model_name.".".$this->user_live_field;
      
$conditions[$field] = $this->user_live_value;
    };
    
$check $this->controller->{$this->user_model_name}->find($conditions);
    if(
$check){
       
$this->Session->write($this->secure_key(),$check);
       if(
                  
$this->allow_cookie && //check we're allowing cookies
                  
$this->from_post && //check this was a posted login attempt.
                  
array_key_exists('remember_me',$data) && //check they where given the option!
                  
$data['remember_me'] == true //check they WANT a cookie set
             
){
                 
// set our cookie!
                 
if(array_key_exists('cookie_expiry',$data)){
                   
$this->cookie_expiry $data['cookie_expiry'];
                 }else{
                   
$this->cookie_expiry;
                 }
                 if(
strtotime($this->cookie_expiry) <= time()){
                    
// Session cookie? might as well not set at all...
                 
}else{
                   
$expiry strtotime($this->cookie_expiry);
                   
$this->setCookieInfo(array('username'=>$data['username'], 'password'=>$hashed_password'expiry'=>$expiry), $expiry);
                 } 
             }
       
$this->current_user $check;
       return 
true;
    }else{
        if(
$this->from_post){
          
$this->Session->setFlash($this->login_failed_message); 
            }
      
$this->destroyData();
      return 
false;
    }
  }

  function 
getUserInfo(){
    return 
$this->current_user[$this->user_model_name];
  }
  function 
getAllUserInfo(){
    return 
$this->current_user;
  }
  function 
getAccessList(){
    static 
$access_list false;
    if(!
$access_list){
      
$access_list $this->_generateAccessList();
    }
    return 
$access_list;
  }
  function 
_generateAccessList(){
    if(!
$this->group_model_name){
      return array();
    }
    
$all_groups $this->controller->{$this->user_model_name}->{$this->group_model_name}->find('list');
    if(!
count($all_groups)){  return array(); }
    
$access array_combine($all_groups,array_fill(0,count($all_groups),0)); //create empty array.
    
    
if(empty($this->current_user)){
      
// NO AUTHENTICATION, SO EMTPY ARRAY!
      
return $access;
    } 
    if(
$this->HABTM){
      
// could be many groups 
      
$ugroups Set::combine($this->current_user[$this->group_model_name],'{n}.id','{n}.'.$this->group_name_field);
      foreach(
$all_groups as $id => $role){
        if(
in_array($role,$ugroups)){
          
$access[$role] = 1;
        }else{
          
$access[$role] = 0;
        }
      }
    }else{
      
// single group assoc, id = user.group_id
      
$foreign_key $this->controller->{$this->user_model_name}->belongsTo[$this->group_model_name]['foreignKey'];
      foreach(
$all_groups as $id => $role){
        if(
$this->current_user[$this->user_model_name][$foreign_key] == $id){
          
$access[$role] = 1;
        }else{
          
$access[$role] = 0;
        }
      }
    }
    if(
$this->superuser_group && $access[$this->superuser_group]){
      return 
array_combine($all_groups,array_fill(0,count($all_groups),1));
    }else{
      return 
$access;
    }
  }

  function 
destroyData(){
    
$this->Session->delete($this->secure_key());
    if(
$this->allow_cookie){
      
$this->setcookieInfo(false); 
    }
    
$this->current_user null;
  }

  function 
logout($redirect=false){
    
$this->destroyData();
    if(!
$redirect){
      
$redirect $this->logout_page;
    }
        
$this->Session->setFlash($this->logout_message); 
    
$this->controller->redirect($redirect,null,true);
    exit();
  }

  function 
getUserInfoFromSessionOrCookie(){
    if( !empty(
$this->current_user) ){ 
      return 
false
    }
    if(
$this->Session->valid() && $this->Session->check($this->secure_key()) ){
      
$this->current_user $this->Session->read($this->secure_key());
      return 
$this->authenticate_from_session(array(
        
'username' => $this->current_user[$this->user_model_name][$this->user_name_field],
        
'password' => $this->current_user[$this->user_model_name][$this->user_pass_field],
      ));
    }elseif(
$this->allow_cookie){
            return 
$this->authenticate_from_cookie();
    }
  }
}
?>

Now on to the Setup and the default Views for Login and Deny.


The following steps should guide you through the setup process and the files you need to alter.

Of course, you will need to have the models for your User table (and groups if applicable). I will assume you have these models setup with Cake conventions with the following schema (using HABTM association):


CREATE TABLE `users` (
`id` int(11) NOT NULL auto_increment,
`created` datetime default NULL,
`modified` datetime default NULL,
`live` tinyint(1) NOT NULL default 0,
`username` varchar(16) NOT NULL default '',
`pswd` varchar(32) NOT NULL default '',
PRIMARY KEY (`id`)
);

CREATE TABLE `groups` (
`id` int(11) NOT NULL auto_increment,
`created` datetime default NULL,
`modified` datetime default NULL,
`live` tinyint(1) NOT NULL default 0,
`name` varchar(32) NOT NULL default '',
PRIMARY KEY (`id`)
);

CREATE TABLE `groups_users` (
`group_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
KEY `group_id` (`group_id`,`user_id`)
);

If you don't use the HABTM association, then remember to set var HABTM = false; later. This will then assume that the user $belongsTo a group (and therefore you'd need a "group_id" field in your "users" table).

Look at the Cake Manual for how to setup the Models for these tables.

Step 1: AppController


If you have created an AppController in your own controllers directory, nows the time, create a file called app_controller.php and populate it as follows. If you have got one, it should be easy enough to see what you'll need to add to yours.

Controller Class:

<?php 
class AppController extends Controller {
  var 
$uses = array('User');
  var 
$components = array('DarkAuth');

  function 
_login(){
    if(
is_array($this->data) && array_key_exists('DarkAuth',$this->data) ){ 
      
$this->DarkAuth->authenticate_from_post($this->data['DarkAuth']);
      
$this->data['DarkAuth']['password'] = '';
    }
  }
  
  function 
logout(){
    
$this->DarkAuth->logout();
  }
}
?>

Step 2: Login and Deny Views


You can create these however you want, however I discovered something very useful in that you can render Views using Controller::render() using absolute paths, so Controller::render('/login') would render a view in the root of your Views Folder. Using this to our advantage we can allow an arbitrary controller access to a view via the same render path. So I create a login View at /app/views/login.ctp, again it's up to you but it must post the following data:

[DarkAuth][username],
[DarkAuth][password]
and optionally if you have set the "$allow_cookie" variable:

[DarkAuth][remember_me],
[DarkAuth][cookie_expires],

Here's a simple one which will do the trick:

View Template:


<?php
  $this
->pageTitle 'Access Restricted';
  echo 
$form->create('DarkAuth',array('url'=>substr($this->here,strlen($this->base))));
  echo 
$form->input('DarkAuth.username');
  echo 
$form->password('DarkAuth.password');
                
/* Uncomment for cookies...
  echo $form->input('DarkAuth.remember_me',array(
          'label'=>'Remember Me? (uses cookies)',
          'type'=>'checkbox'
          ));
  echo $form->input('DarkAuth.cookie_expiry',array(
          'options'=>array(
                       'now'=>'end of session',
                       '+1 week'=>'in a week',
                       '+1 Months'=>'in a month',
                       '+6 Months'=>'in 6 months',
                     ),
          'label'=>'If so, for how long?'
          ));
*/
  
echo $form->end('login');
?>

And a page for /app/views/deny.ctp:

View Template:


<?php
  $this
->pageTitle 'Access Denied!';
?>
  <p>I'm sorry, but you don't have sufficient permission to access this page!</p>

Step 4: Edit the Component's Variables and Hasher


There are a number which need to be configured to match your user and group models, the fields they use for username and password and the association type.

There are others for successful logout, login failure messages, default redirections and more. Please look over them to get the component to work how you want it.

The final thing to configure is the DarkAuth::hasher() function (which can be used anywhere to hash passwords in the same way that they are hashed in the database. Make sure your either use the same hashing function or amend this one how you want.

Step 5: The Logout Route


This is optional, as we put the logout() function in AppController so accessible from any controller. However, I find it more aesthetically pleasing to have a route for logout at /logout. Add this to your app/config/routes.php:


Router::connect('/logout',array('controller'=>'users','action'=>'logout'));

NB any controller would do, but you're pretty sure to have a UsersController.

Step 6: Enjoy!


That's it. Hopefully you haven't had too many issues, and your App is now secure and happy.

1 | 2 | 3

Report

More on Components

Advertising

Comments

  • OriginalTwist posted on 11/17/10 01:46:36 AM
    I was looking at the php for this and could not figure out where it throw in a session->write() instruction to add the user id to the session. I am trying to get the 5 star rating plugin found at the following to work:

    http://bakery.cakephp.org/articles/schneimi/2010/08/19/ajax-star-rating-plugin-1 to

    However it needs to read the user id from the session such as Auth.User.Id but i cant figure ou how to do this with DarkAuth which is what i want to use fro authentication due to its simplicity and beautifully written purpose.

    Any Suggestions?
  • adyopo posted on 07/22/10 08:43:49 AM
    So, I'm using DarkAuth with the cakephp 1.3.2 and I think I may have stumbled into a problem. My controller class looks like this:
    class UsersController extends AppController {

    var $name = 'Users';
    var $helpers = array('Html', 'Form');
    var $uses = array('User');
    var $_DarkAuth;
    And I'm using the requiresAuth method like so:
    	function add() {
    //$this->DarkAuth->requiresAuth('administrators');
    if (!empty($this->data)) {
    $this->User->create();
    If I leave the commented line as it is and comment the var $_DarkAuth line the add method runs just fine. When I remove the comments the page loads as a blank page. Please help me, thanks.
    If I'm ignorant please point out my errors.
  • dhammala posted on 05/14/10 11:15:56 AM
    Emanuele, Check your source files carefully -- the code snippet you posted is trying to set HTTP headers but can't because something else in the script has already sent something to the browser. Most often, this is a blank line or space character before/after the tags. Read the error message carefully for a hint at where this might be happening.
    -Scott
  • blackbelt posted on 05/14/10 10:23:26 AM
    Hey dude, sorry to disappoint you, but I ve the following issue:

    Cannot modify header information - headers already sent by (output started at /opt/lampp/htdocs/at/controllers/components/dark_auth.php..
    caused by:
    if($this->from_post){
    // user just authed, so redirect to avoid post data refresh.
    $this->controller->redirect($this->here,null,null,true);
    exit();
    }
    can u please help me to understand why of this?
    thanks in advance
  • blackbelt posted on 05/05/10 10:22:31 AM
    Hey, why did u needed to fill the access array with 0 or 1? Can't you only check if (array_key_exists($g,$access)) ?
    Thanks again.
  • blackbelt posted on 05/05/10 05:03:04 AM
    Hey Dude, thanks for the amazing componet. I can't really figure out if DarkAuth is built upon Auth or not. Can u please help me to understand?
    • theChrisWalker posted on 05/05/10 05:05:23 AM
      Hey Dude, thanks for the amazing componet. I can't really figure out if DarkAuth is built upon Auth or not. Can u please help me to understand?
      Nothing to do with Auth at all, and has a very different workflow.
      • blackbelt posted on 05/05/10 05:21:44 AM
        Hey Dude, thanks for the amazing componet. I can't really figure out if DarkAuth is built upon Auth or not. Can u please help me to understand?
        Nothing to do with Auth at all, and has a very different workflow.

        ok, thanks a lot.
  • theChrisWalker posted on 04/15/10 12:50:12 PM
    Why wouldn't you? HTTP is inherently stateless, with state bolted on by sessions. I would always validate my session, and the authorization to do anything goes with that.

    I'm no security expert, so this seems like a logical idea. I wouldn't be surprised to find it's not a standard or even especially efficient solution.
    • Ferco posted on 04/15/10 02:27:55 PM
      Why wouldn't you? HTTP is inherently stateless, with state bolted on by sessions. I would always validate my session, and the authorization to do anything goes with that.

      I'm no security expert, so this seems like a logical idea. I wouldn't be surprised to find it's not a standard or even especially efficient solution.

      Normally I would trust the web server to keep track of the user with the session. I don't know the detail, but I think the web server sets some session id in an encrypted cookie, so it can identify the remote user with a local data session. I also trust the web server to be secure enough to prevent session thieving...
      I think your solution isn't the most eficient, but we can never be paranoic enough. I'll just put some cache there to reduce the autentication querys.
      Thanks for your excelent work.
  • Ferco posted on 04/14/10 03:51:48 PM
    Hi. I hope you're still around here...

    I'm really wondering myself why would you want to autenticate the user on every page from the data of the session... I supose there is a security reason, but I can't seem to find it. I really appreciate your answer. Thanks.
  • squidliberty posted on 04/29/09 04:16:27 PM
    If you want the component to authenticate all pages with admin routing automatically, add the following line at the bottom of the startup() function:
    if (isset($this->controller->params[Configure::read('Routing.admin')])) $this->requiresAuth('Admin');
  • CristianDeluxe posted on 04/19/09 07:32:06 PM
    If you are using cookies and darkauth don't erase it properly, try changing this:


    <?php
    function setCookieInfo($data,$expiry=0){
          if(
    $data === false){
                
    //remove cookie!
                
    $cookie false
    // ...
    }
    ?>

    for this:

    <?php
    function setCookieInfo($data,$expiry=0){
          if(
    $data === false){
                
    //remove cookie!
                
    $cookie ""// <----------
    // ...
    }
    ?>

    And cookies will be erased forever!!
    Good Component!!
  • sholsinger posted on 04/06/09 12:58:24 PM
    I suggest we borrow a best practice from CodeIgniter in that we remove any trailing PHP end tags from Model and Controller files. This would eliminate a lot of the problems with cookies, output buffering, etc, because the PHP parser ignores whitespace at the end of a file.

    Therefore:

    ### Start of my_controller.php
    <?php
        
    class MyController extends AppController
        
    {
           
    # [...]
        
    }
    /* end of file my_controller.php */
    • riledhel posted on 04/06/09 02:58:34 PM
      @Stephen Holsinger: Maybe your comment belongs in Trac as a feature request. I think it's annoying but we a little experience you can handle it.

      I suggest we borrow a best practice from CodeIgniter in that we remove any trailing PHP end tags from Model and Controller files. This would eliminate a lot of the problems with cookies, output buffering, etc, because the PHP parser ignores whitespace at the end of a file.

      Therefore:

      ### Start of my_controller.php
      <?php
          
      class MyController extends AppController
          
      {
             
      # [...]
          
      }
      /* end of file my_controller.php */
  • antonio__marco posted on 03/20/09 01:02:46 PM
    Hi.

    When I run the mySpace action the form login is showed. After login, the mySpace view is showed. Until now, everything is fine.

    But when I click the button, the login form is showed again.

    What is the problem?

    My claimants_controller.php content is:

    Controller Class:

    <?php 
    class ClaimantsController extends AppController {
        var 
    $name 'Claimants';

        var 
    $_DarkAuth = array(
            
    'required' => array(
                
    'APPADMIN',
                
    'CITIZEN'
            
    )
        );

        function 
    mySpace() {
            
    // Do something...
        
    }
        
        function 
    myNotifications() {
            
    // Do something...
        
    }
    }
    ?>

    The my_space.ctp content is:

    View Template:


    <h1>See My Notifications</h1>
    <button onClick="JavaScript: window.location = '/projetos/aprovacao/claimants/myNotifications';">My Notifications</strong></button>

    Thanks for help.
  • juansaab posted on 03/19/09 05:35:15 PM
    Thanks for your help, when the file has spaces after de php tags show this kind of errors. But, somebody knows the reason?¿
  • juansaab posted on 03/19/09 01:52:52 AM
    Hello, I'm cooking my first project. I make your steps for use this great component but there of, don't works fine.

    I make all, the relations in the tables, the scripts, for allow the login page I make a route in the apropiate conf file. All runs fine but when I put the string "var $_DarkAuth;" in my users controller for probe, I loss the default style, all changed to a white background and show me a querys from the database. (I can't see nothing only this querys)

    When I try to logout I obtain that prompt "Warning (2): Cannot modify header information - headers already sent by (output started at /var/www/gabriela-date-p/app/controllers/app_controller.php:22) [CORE/cake/libs/controller/controller.php, line 615]" and the styles disappear.

    I hope that you can help me thanks so much...

    Juan
    • riledhel posted on 03/19/09 10:41:01 AM
      @Juan: You probably forgot to open or close a php tag around line 22 in your app_controller. Also check that there are no more lines after the last closing php tag. Sometimes is just that. Hope it helps.

      Hello, I'm cooking my first project. I make your steps for use this great component but there of, don't works fine.

      I make all, the relations in the tables, the scripts, for allow the login page I make a route in the apropiate conf file. All runs fine but when I put the string "var $_DarkAuth;" in my users controller for probe, I loss the default style, all changed to a white background and show me a querys from the database. (I can't see nothing only this querys)

      When I try to logout I obtain that prompt "Warning (2): Cannot modify header information - headers already sent by (output started at /var/www/gabriela-date-p/app/controllers/app_controller.php:22) [CORE/cake/libs/controller/controller.php, line 615]" and the styles disappear.

      I hope that you can help me thanks so much...

      Juan
  • dokalanyi posted on 02/17/09 12:40:35 AM
    i think it would be nice if i could have var $_DarkAuth = array('except' => array('public_action', 'public_action2')) or even better, in the app_controller having $_DarkAuth = array('except' => array('books/public_action', 'stores/public_action2')) and in the component file something like no_of_failed_logins = 3; such that if a user enters a wrong password thrice, the account is locked and other authentication means are used like favourite pets name, or a series of questions to verify authenticity.
  • euromark posted on 07/27/08 05:33:05 PM
    its $DarkAuth_User not $_DarkAuth_User, as still pointed out on this site..
    i figured it out.. :)

    i want to point out, that this function could be useful as well (made it up today):

        /**
         * Redirect, if user already is logged in (login/register etc.)
         * @access public
         * @param where_to: e.g. '/CONTROLLER/ACTION' | default: '/users/home'
         */
        function redirectLoggedIn($where_to=null) {
            if (!empty($this->current_user))
            {
                if (!empty($where_to))
                {
                    $this->controller->redirect($where_to);
                }
                $this->controller->redirect('/users/home');
            }
        }
  • euromark posted on 07/27/08 05:55:43 AM
    if I try to check in the view if somebody is logged in or not, i always get the warning:
    Undefined variable: _DarkAuth [APP\views\users\index.ctp, line 6]
    if($_DarkAuth['User']['user_id']==$id){
    echo "This is your personal page";
    }

    the user already logged in - the data is in the session - but $_DarkAuth still is empty..

    another thing:
    if a user is successfully logged out, the session userdata is still present. this usually happens only in IE(7) - not in firefox
    i already inserted "no caching" headers - but doesnt work either..

  • oberaldo posted on 07/17/08 03:52:05 PM
    Now, i cant update the last_login field in my table.
    in my app_controller, i do this:

    Controller Class:

    <?php 
    function _login()
    {
    if(
    is_array($this->data) && array_key_exists('DarkAuth'$this->data))
    {
    if(
    $this->DarkAuth->authenticate_from_post($this->data['DarkAuth'])){
                    
    $this->data['DarkAuth']['password'] = '';
    $usuario $this->DarkAuth->getUserInfo();
    $this->User->id $usuario['id'];
    $this->User->saveField('last_visit'date(DATE_ATOM));
    }
    }
    }
    ?>

    Thanks Chris
  • oberaldo posted on 07/03/08 08:13:34 PM
    Hi Chris,
    I see all code of this component, but i can't see where i change to update a field in table Users, after this user make the login action.
    I add a field named "lastvisit" in my Users table.

    i dont know if a add some code to _login action, or to a function authorize...please, i can help me?
    Thanks!
    • theChrisWalker posted on 07/04/08 02:30:38 AM
      Hi Chris,
      I see all code of this component, but i can't see where i change to update a field in table Users, after this user make the login action.
      I add a field named "lastvisit" in my Users table.

      i dont know if a add some code to _login action, or to a function authorize...please, i can help me?
      Thanks!

      I reckon the best place to put it is in the "authenticate_from_post()" method, as by POST will mean they have "just" logged in. Change the function so instead of returning "$this->authenticate" you use it in an "if" clause:

      Component Class:

      <?php function authenticate_from_post($data){
        
      $this->from_post true;
        if( 
      $this->authenticate($data) ){
          
      // Save last login record
          
      $this->controller->{$this->user_model_name}->save( ... );
          return 
      true;
        }else{
          return 
      false;
        }
      }
      ?>
      • oberaldo posted on 07/06/08 12:31:48 PM
        I reckon the best place to put it is in the "authenticate_from_post()" method, as by POST will mean they have "just" logged in. Change the function so instead of returning "$this->authenticate" you use it in an "if" clause:
        Hi Chris, me again.
        I try some things, but i can't do it work correctly.

        Component Class:

        <?php if($this->authenticate($data))
                {
                    
        $last_visit date('Y-m-d H:s:i');
                    
        $this->controller->{$this->user_model_name}->save($data['last_visit'], $last_visit);
                    return 
        true;
                } else {
                    return 
        false;
                }
        ?>
        I try this and some other, with
        $this->controller->{$this->user_model_name}->save($this->data['User']['last_visit'], $last_visit); and some others, but i cant...
        Any idea for me?
        My table is User(model User) and the field for user last visit is "last_visit".
        You can help me?
        Thank you!
  • riledhel posted on 06/24/08 02:35:25 PM
    Hi, I think your component is very useful and I wanted to thank you for your time and effort. I followed your tutorial here but can't make it work. I get the following error with DDD:

    Fatal error: Maximum function nesting level of '100' reached, aborting! in /var/www/tandil/cake/libs/debugger.php on line 143

    I think that when the app is trying to load the component it crashes, but I don't know how to fix this. Any ideas? I'm using the 1.2.0.7125 RC1. I can also post you the Call Stack if you need it.

    Thanks in advance,

    Javier.-
    • theChrisWalker posted on 06/25/08 02:55:55 AM
      Hi, I think your component is very useful and I wanted to thank you for your time and effort. I followed your tutorial here but can't make it work. I get the following error with DDD:

      Fatal error: Maximum function nesting level of '100' reached, aborting! in /var/www/tandil/cake/libs/debugger.php on line 143

      I think that when the app is trying to load the component it crashes, but I don't know how to fix this. Any ideas? I'm using the 1.2.0.7125 RC1. I can also post you the Call Stack if you need it.

      Not come across this one before, do send me all the details though and I'll have a look. Some people have reported problems with IIS, so would be interested in you server setup too.

      http://thechriswalker.net/contact
  • oberaldo posted on 06/15/08 11:35:11 AM
    Hi Chris,
    How i can allow the user only view your self profile, in my controller?
    Ex. If user with id 5 try to access www.site.com/users/view/1 ou www.site.com/users/edit/1 this user receive one error message.
    Thank's for a great component and tutorial
    • theChrisWalker posted on 06/15/08 12:38:13 PM
      Hi Chris,
      How i can allow the user only view your self profile, in my controller?
      Ex. If user with id 5 try to access www.site.com/users/view/1 ou www.site.com/users/edit/1 this user receive one error message.
      Thank's for a great component and tutorial

      Controller Class:

      <?php function edit($id){
        
      $user $this->DarkAuth->getUserInfo();
        if(
      $user['id'] == $id){
          
      // allow
        
      }else{
          
      // error message.
        
      }
      }
      ?>

      Nothing in this component to do that automagically, but I have implemented it in this way many times... good luck!
  • theChrisWalker posted on 06/13/08 02:48:53 AM
    This code doesn't quite work with RC1, only a little glitch and easy to fix.

    Simply replace the 2 instances of
    $this->controller->render to
    echo $this->controller->render
    • doze posted on 05/20/09 06:21:13 PM
      This code doesn't quite work with RC1, only a little glitch and easy to fix.

      Simply replace the 2 instances of
      $this->controller->render to
      echo $this->controller->render

      I'm using the latest version of cake (1.2.3.8166) and searched for hours why my deny and login pages of restricted controllers are not showing up.. actually nothing was showing up. Then finally found this from the comments..

      So yes, I confirm that also. Add the echo and all is fine.

      I wonder how there weren't any more info about this? I'm quite new to cakephp, but I thought that this component was very popular.. so anyone with the latest cake builds should have issues with this.

      Anyways, please Chris update the component code in the article accordingly!
  • shageman posted on 06/12/08 06:13:20 PM
    I have to live with output_buffering being set to off on my server. I believe that that is why using DarkAuth doesn't work for me. After I enter my credentials into the login form I get Cannot modify header information - headers already sent by (output started at /opt/lampp/htdocs/at/controllers/components/dark_auth.php:383)
    It seems to me that this is because DarkAuth writes to the output before the page loads.

    Has anyone got it working with "output_buffering = Off"?
    • dhammala posted on 06/12/08 08:29:34 PM
      I have to live with output_buffering being set to off on my server. I believe that that is why using DarkAuth doesn't work for me. After I enter my credentials into the login form I get Cannot modify header information - headers already sent by (output started at /opt/lampp/htdocs/at/controllers/components/dark_auth.php:383)
      It seems to me that this is because DarkAuth writes to the output before the page loads.

      Has anyone got it working with "output_buffering = Off"?

      Have you checked what is on line 383? My file ends at line 355. If you have any white space characters outside of the then that will be sent to the browser, and with no output buffering, headers for cookies can't be written and this warning will be produced. I would check your file for whitespace..
      • theChrisWalker posted on 06/13/08 02:44:05 AM
        @Stephan, Scott is probably spot on and thats the first thing I would suggest.

        @Scott, Thanks, that's almost certinaly the cause.
  • theChrisWalker posted on 05/11/08 11:15:41 AM
    Thank you all for the support, I have changed the code as suggested by Scott, and the bits of the tutorial noticed by Bruno.

    Doing what debugging I could, there doesn't seem to be any Group in User, so you can't run the find() method on the invalid Group object. I don't know if it's relevant that the user isn't logged in.

    I would certainly appreciate any help or suggestions.

    I think the issue is that you don't have a Group model associated with your User Model. If that is the case, you'll need to set the $group_model_name variable to false, or an empty string.

    This will tell the component you are not using groups. I will try and make that clearer in the comments.
  • normally posted on 05/08/08 04:28:55 PM
    I fear this may be something simple because no one else seems to be having this problem, but I am currently stumped, so here goes...

    I've dropped in the code and made the indicated modifications to my code. Now, when viewing a page I get a fatal error on this line of the _generateAccessList() function:

    $all_groups = $this->controller->{$this->user_model_name}->{$this->group_model_name}->find('list');
    Doing what debugging I could, there doesn't seem to be any Group in User, so you can't run the find() method on the invalid Group object. I don't know if it's relevant that the user isn't logged in.

    I would certainly appreciate any help or suggestions.
  • bslima posted on 05/07/08 07:22:19 AM
    In the tutorial you tell us to use
    var $_DarkAuth = array('requires'=>array('Admin','SecretKeepers'));

    When actually we have to use 'required' insted of 'requires'.

    Just this litlle thing ....
    You component rlz :)
  • dhammala posted on 05/06/08 02:32:23 PM
    Line 308 should be:

          // single group assoc, id = user.group_id
          $foreign_key = $this->controller->{$this->user_model_name}->belongsTo[$this->group_model_name]['foreignKey'];

    PS: great job, btw! I use this component in all of my Cakes!
login to post a comment.