Sorry for the VERY long question, but I am having problems with auth->login() and after long hours of debugging, I would like to share my findings. It may well be something stupid on my side, but bear with me...
Model "Cliente" (relevant parts):
<?php
App::uses('AppModel', 'Model');
App::uses('BrValidation', 'Localized.Validation');
App::uses('AuthComponent', 'Controller/Component');
App::uses('SimplePasswordHasher', 'Controller/Component/Auth');
/**
* Cliente Model
*
*/
class Cliente extends AppModel {
public $primaryKey = 'idcliente';
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$passwordHasher = new SimplePasswordHasher();
$this->data[$this->alias]['password'] = $passwordHasher->hash(
$this->data[$this->alias]['password']
);
}
return true;
}
}
ClientesController (relevant parts):
<?php
App::uses('AppController', 'Controller');
App::uses('CakeEmail', 'Network/Email');
/**
* Clientes Controller
*
*/
class ClientesController extends AppController {
public $components = array('Paginator','RequestHandler');
/** beforeFilter
* Configura partes do sistema que podem ser acessadas sem login
*/
public function beforeFilter() {
//parent::beforeFilter();
//$this->Auth->allow();
}
public function login() {
//if already logged-in, redirect
if($this->Session->check('Auth.Cliente')){
$this->redirect(array('action' => 'index'));
}
// if we get the post information, try to authenticate
if ($this->request->is('post')) {
CakeLog::write('debug',"POS IF POST\n");
if ($this->Auth->login()) {
CakeLog::write('debug',"True no LOGIN\n");
$this->Session->setFlash(__('Welcome, '. $this->Auth->user('Cliente.username')));
$this->redirect($this->Auth->redirectUrl());
} else {
$this->Session->setFlash(__('Invalid username or password'));
}
}
}
AppController (relevant parts) class AppController extends Controller {
public $components = array(
'DebugKit.Toolbar',
'Session',
'Auth' => array(
'loginAction' => array('controller' => 'clientes', 'action' => 'login'),
'loginRedirect' => array('controller' => 'clientes', 'action' => 'poslogin'),
'logoutRedirect' => array('controller' => 'clientes', 'action' => 'login'),
'authError' => 'Você precisa se logar para ver esta página.',
'loginError' => 'Usuário ou senha inválidos. Tente de novo.',
'authenticate' => array(
'Form' => array(
'userModel' => 'Cliente',
'fields' => array ( 'username' => 'username', 'password' => 'password')
)
)
));
No need to post the associated view, as the Form collects the info and POSTs it as expected.
As it stands, $this->Auth->login() will ALWAYS return TRUE, regardless of the POSTed data. It really doesn't matter whether user exists or not and whether the password is correct or not.
Cake is looking up the right tables and right users. I have turned on MySQL General Log to ensure that the actual queries are correct, so no issues here.
I have then moved to the actual Cake code and found the following. The code we are interested is located at $ROOT/lib/Cake/Controller/Component/AuthComponent.php
, lines 595-607 (the login() function), below:
public function login($user = null) {
$this->_setDefaults();
if (empty($user)) {
$user = $this->identify($this->request, $this->response);
}
if ($user) {
$this->Session->renew();
$this->Session->write(self::$sessionKey, $user);
}
CakeLog::write('debug',"LOGGEDIN:". $this->loggedIn());
return $this->loggedIn();
}
As we pass no arguments, Cake will attempt to identify
the user (line 599) and then will return its loggedIn
current status. loggedIn
is simply the boolean result for a user()
call as we can see in lines 820-822, so let's have a look at user()
(lines 648-658).
public static function user($key = null) {
if (!empty(self::$_user)) {
$user = self::$_user;
} elseif (self::$sessionKey && CakeSession::check(self::$sessionKey)) {
$user = CakeSession::read(self::$sessionKey);
} else {
CakeLog::write('debug',"return null");
return null;
}
if ($key === null) {
ob_start();
var_dump($user);
$d=ob_get_contents();
ob_clean();
CakeLog::write('debug',"return user: " . $d);
return $user;
}
CakeLog::write('debug',"return hash" . Hash::get($user,$key));
return Hash::get($user, $key);
}
Since we call user()
with no arguments, $key
will be NULL and the code will simply return $user
and - to me, at least - that's where the problem lies. If I var_dump($user)
as above, I get an empty array, but I get something "TRUE" and the login process "suceeeds" (erroneously of course).
2014-05-25 00:24:04 Debug: return user: array(1) {
["Cliente"]=>
array(3) {
["idcliente"]=>
NULL
["username"]=>
NULL
["password"]=>
NULL
}
}
I am using Cake 2.4.1, but I have checked ALL 2.4 releases up to 2.4.10 and the code hasn't changed, so this will happen throughout Cake 2.4. I haven't looked up other minor versions though. I am running PHP 5.5.3 on OSX, but this doesn't sound too relevant to me here.
I haven't read any ChangeLogs where this issue is raised and there are some unanswered questions in SO regarding this issue.
I didn't want to change core code without sharing first and am not sure if SO is the "proper" place to post this, and it could well be that I am simply overlooking something.
If anybody can shed some light and run some thoughts, that would be really appreciated.
Thanks!