Clarifying MySQL query for failed login

Hi I’m customizing the log in for my site, however it’s currently failing, I know the username and password are right, so I’m trying to see what the query being executed actually is. I have the web log on and it’s saying the query is

Querying SQL: SELECT * FROM my_users t WHERE t.email=:yp0 LIMIT

1

Which isn’t that helpful to me currently unless I’m missing something, can I get a more in depth query analysis at all? :)

Found this Which helps a bit

My query now:


Querying SQL: SELECT * FROM `my_users` `t` WHERE `t`.`email`=:yp0 LIMIT

1. Bound with :yp0='me_email@yahoo.com'

So i take it that my password is causing the problem?

I have been working to adjust the login, so far I have

login.php


<?php

$this->pageTitle=Yii::app()->name . ' - Login';

$this->breadcrumbs=array(

	'Login',

);

?>


<div class="form">

<?php $form=$this->beginWidget('CActiveForm', array(

	'id'=>'login-form',

	//'enableClientValidation'=>true,

	//'enableAjaxValidation'=>true,

	'clientOptions'=>array(

		'validateOnSubmit'=>true,

	),

)); ?>




	<div class="row">

		<?php echo $form->labelEx($model,'email'); ?>

		<?php echo $form->textField($model,'email'); ?>

		<?php echo $form->error($model,'email'); ?>

	</div>


	<div class="row">

		<?php echo $form->labelEx($model,'password'); ?>

		<?php echo $form->passwordField($model,'password'); ?>

		<?php echo $form->error($model,'password'); ?>

	</div>


	<div class="row buttons">

		<?php echo CHtml::submitButton('Login'); ?>

	</div>




<?php $this->endWidget(); ?>

</div><!-- form -->



LoginForm.php


<?php


/**

 * LoginForm class.

 * LoginForm is the data structure for keeping

 * user login form data. It is used by the 'login' action of 'SiteController'.

 */

class LoginForm extends CFormModel

{

	public $email;

	public $password;


	private $_identity;


	/**

	 * Declares the validation rules.

	 * The rules state that username and password are required,

	 * and password needs to be authenticated.

	 */

	public function rules()

	{

		return array(

		

		// email and password are required

        array('email, password', 'required'),

        array('email', 'email'),

        array('password', 'authenticate'),

		);

	}


	/**

	 * Declares attribute labels.

	 */

	public function attributeLabels()

	{

		return array(

			'rememberMe'=>'Remember me next time',

		);

	}


	/**

	 * Authenticates the password.

	 * This is the 'authenticate' validator as declared in rules().

	 */

	public function authenticate($attribute,$params)

	{

		if(!$this->hasErrors())

		{

			$this->_identity=new UserIdentity($this->email,$this->password);

			if(!$this->_identity->authenticate())

				$this->addError('password','Incorrect email or password');

				

		}

	}


	/**

	 * Logs in the user using the given username and password in the model.

	 * @return boolean whether login is successful

	 */

	public function login()

	{

		if($this->_identity===null)

		{

			$this->_identity=new UserIdentity($this->username,$this->password);

			$this->_identity->authenticate();

		}

		if($this->_identity->errorCode===UserIdentity::ERROR_NONE)

		{

			/*

			$duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days

			Yii::app()->user->login($this->_identity,$duration);

			return true;

			*/

		}

		else

			return false;

	}

}



UserIdentity.php


<?php


/**

 * UserIdentity represents the data needed to identity a user.

 * It contains the authentication method that checks if the provided

 * data can identity the user.

 */

class UserIdentity extends CUserIdentity

{

	/**

	 * Authenticates a user.

	 * The example implementation makes sure if the username and password

	 * are both 'demo'.

	 * In practical applications, this should be changed to authenticate

	 * against some persistent user identity storage (e.g. database).

	 * @return boolean whether authentication succeeds.

	 */

	 

	// Need to store the user's ID:

	private $_id;


	public function authenticate()

	{

		/*$users=array(

			// username => password

			'demo'=>'demo',

			'admin'=>'admin',

		);*/

		

		//http://www.larryullman.com/2010/01/07/custom-authentication-using-the-yii-framework/

		

		$users = MyUsers::model()->findByAttributes(array('email'=>$this->username));

		 

	

		if(!isset($users[$this->username]))

			$this->errorCode=self::ERROR_USERNAME_INVALID; // no user found

		else if($users[$this->username]!== sha1($this->password))

			$this->errorCode=self::ERROR_PASSWORD_INVALID; // password failed

		else

			

			$this->_id = $users->id;

			$this->errorCode=self::ERROR_NONE;

			return !$this->errorCode;


			// Store the role in a session:

		    $this->setState('type', $users->type);

	}

	




	public function getId() // override getId to store user ID

	{

		return $this->_id;

	}

}

This has beaten me so far, so would be nice if anyone can look at it with fresh eyes :D

Is this ok?


else if($users[$this->username]!== sha1($this->password))

Certainly looks wrong! I shall have a little tinker and report back, it may not be the only problem - thanks though

Updated

I updated my UserIdentity.php to this


		if(!isset($users[$this->username]))

			$this->errorCode=self::ERROR_USERNAME_INVALID; // no user found

		else if($users[$this->password]!== sha1($this->password)) 

			$this->errorCode=self::ERROR_PASSWORD_INVALID; // password failed  

		else

		$this->errorCode=self::ERROR_NONE;

		// Store the role in a session:

		$this->setState('type', $users->type);

	        // store user id

                $this->_id = $users->id;


	        return !$this->errorCode;

			

	}

Still can’t login though :frowning:

I echoed out the inputs inside the LoginForm::authenticate() and they returned the email and hashed value I expected, so i think it must be deeper into the process

Ok, try old debugging technique die() :)

Let say,


die(print_r($users));


die(var_dump($users[$this->password]));


die(var_dump(sha1($this->password])));

Let’s take it step by step.

You try to fetch a user by his email address:




$users = MyUsers::model()->findByAttributes(array('email'=>$this->username));



Which will return a single object in case it will find the user with the specified email.

Next, you do these checks:




if(!isset($users[$this->username]))

  $this->errorCode=self::ERROR_USERNAME_INVALID; // no user found

else if($users[$this->password]!== sha1($this->password)) 

  $this->errorCode=self::ERROR_PASSWORD_INVALID; // password failed  



Who’d you expect $users[$this->username] to be ?

Let me remind you, you get back an object from database not an array, so your comparison doesn’t make any sense.

Anyway, here is the "correct way":




$user = MyUsers::model()->findByAttributes(array('email'=>$this->username));

if(empty($user))

   $this->errorCode=self::ERROR_USERNAME_INVALID; // no user found

elseif($user->password!==sha1($this->password))

   $this->errorCode=self::ERROR_PASSWORD_INVALID; // password failed 

else

   $this->errorCode=self::ERROR_NONE;



Hi,

Appreciate your help with this.




die(print_r($users));



Returns a list of data of which nothing looks untoward, there was a lot so I didnt copy it across, it was mostly just going through the columns of my DB telling the types of data, length etc and the only record I have in there.


die(var_dump(sha1($this->password)));

returns string(40) "Hash value of my string"

Now,


		die(var_dump($users[$this->password]));

Returns: Property "MyUsers.Password" is not defined.

Which I guess is my problem, however what I’ve done wrong I’m not exactly sure at this point… :)

I hadn’t noticed your reply when I replied, I shall read your reply now - thanks :wink:

Just do as i suggested, that’s the correct way of accessing the object properties.

If you see allot of extra metadata when doing var_dump($user) don’t worry about it, you only care about you database table columns and you can access them by using the $model->column_name notation.

Another thing, i bet you code with error_reporting off, this is BAD. Your code should have thrown allot of warnings, so open index.php and add




ini_set('display_errors',1);

error_reporting(E_ALL | E_STRICT);



at the first line, just after your opening <?php tag.

Do you forget that CActiveRecord implements ArrayAccess interface? :)

It is completely legal to access AR object properties in array like fashion.

And now (after testing sincerely) solution is to write:




if(!isset($users['your_username_column_name']))

    $this->errorCode=self::ERROR_USERNAME_INVALID; // no user found

else if($users['your_password_column_name']!== sha1($this->password)) 

    $this->errorCode=self::ERROR_PASSWORD_INVALID; // password failed



or




if(!isset($users->your_username_column_name))

    $this->errorCode=self::ERROR_USERNAME_INVALID; // no user found

else if($users->your_password_column_name !== sha1($this->password)) 

    $this->errorCode=self::ERROR_PASSWORD_INVALID; // password failed



Remember, in Yii you can access properties of your ActiveRecord instance like it is an array.

Cheers

Thanks for your input twisted1919,

I’m not massively experienced with OOP, so I do get tripped up with these things.

I see what I did wrong now, however now I’m having the problem that

"MyUsers.password is not defined."

regarding this line:




 elseif($users->password!==sha1($this->password))



No, I code with error_reporting at its highest, I don’t know how this is impacted with Yii however, I’m used to seeing the old Black syntax etc errors, rather than Yii’s stack responses.

@ManInTheBox - Have you seen the way the comparison has been made ? does that makes any sense to you ?

I was talking about it not about the fact that actually you can access the data like plain array. Yes i knew about it, but i just refuse to use it.

@Jonny - Can you please share your users model and also your users table schema ?

Do you cache your tables schema ?

Yes, I saw it. And I wrote corrections in my previous post. Of course it needs to be checked value that the key points to, not the key’s name.

I was talking about this :)

I was talking about that too

// if password would be 235f5f557g57vx46x7b4xb4




$users[$this->password]!== sha1($this->password)



translates to




$users['235f5f557g57vx46x7b4xb4']!== sha1($this->password)

//whoot <img src='http://www.yiiframework.com/forum/public/style_emoticons/default/huh.gif' class='bbc_emoticon' alt='???' />



In the end I went for this approach, I do thank you both for your input with this, I am updating this using a tutorial, but the code is not as up to date with the latest release.

I do not get any error now when I try to login which is something :D

Thanks for your help :D

[sql]CREATE TABLE IF NOT EXISTS my_users (

id int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT ‘CONSTRAINT FOREIGN KEY (id) REFERENCES r_detail(created_by_user)’

type enum(‘admin’,‘member’) NOT NULL,

email varchar(80) NOT NULL,

pass char(40) NOT NULL,

first_name varchar(20) NOT NULL,

last_name varchar(40) NOT NULL,

active char(32) DEFAULT NULL,

date_created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

date_modified timestamp NOT NULL DEFAULT ‘0000-00-00 00:00:00’,

PRIMARY KEY (id),

UNIQUE KEY email (email)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;[/sql]

The model is the just the one generated from Yii itself no I havent cached my tables yet, I haven’t got that far into Yii, only been playing with it for about a week.

No wonder that Yii thrown the MyUsers.password not defined error.

Your database column name is "pass" NOT "password"(adjust your code accordingly).

I had a hunch that the password has another name in database,that’s why i asked for the schema.

Please pay more attention, these are small errors that can drive you crazy trying to find them.

Yes they can, when ManInTheBox mentioned about accessing AR object in an array fashion I did update it. Thanks though :D.