Cake PHP 2.4.x. AuthComponent login() always retur

2020-07-23 06:19发布

问题:

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. loggedInis 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!

回答1:

I have managed to narrow down the issue and while it doesn't seem to a bug in the core code, it does seem to qualify as a behaviour to be investigated further.

Assume you have a "clean" browser session, with NO SESSION COOKIES. In this case, the code will work as expected. Wrong users and/or passwords will be denied.

However, once you log on, Cake will handle the browser a session cookie (CAKEPHP) which, among other info, will link that session to the logged user.

If you try to log again ("on top of your current login"), $this->Auth->login() (without parameters) will yield TRUE no matter what. If you provide a wrong user/password, it will stick to the current valid one. If you provide a valid user/password, it will CHANGE to the new user.

Unless you log out, or the session cookie expires (or you manually delete it, for instance), it doesn't matter whether you call $this->Auth->login() with or without parameters.

While from a "page flow"/logical perspective it seems to makes sense (why would one call "Login" AGAIN after being logged in?), from a user behaviour perspective, this can lead to (as noticed here!) weird problems.

Let's say you log in, do whatever you want to do and leave your web app without logging out. You will remain logged in for the duration of Cake's Session Cookie. If you share the browser and the app with someone else, this someone may accidentally work on your account, by logging in but mistyping the password, in which case, as mentioned above, login() will return TRUE, but WON'T CHANGE users. Not to mention, potentially malicious uses.

In any case... it's probably good practice to force a $this->Auth->logout() before logging in to avoid potential app misbehaviours.

Since this approach seems to be present throughout Cake 2.x, I hope my issues clarifies other people as well.