I've been using this tutorial to rewrite my login/logout functionality to handle authentication and matching passwords in the model.
It now adds the user perfectly, but upon login the username/password fields are rejected. It seems the login function is looking for a differently hashed password than my new hashPasswords() function is storing. Any suggestions on what the problem could be?
It's a fairly standard setup. Thanks for any help!
Pertinent Sections of code:
User Model:
var $validate = array(
'password' => array(
'The Password must be filled' =>array(
'rule' => 'notEmpty',
'message' => 'Please supply a valid password.'
),
'The Password must be between 5 and 15 characters' => array(
'rule' => array('between', 5, 15),
'message' => 'The password must be between 5 and 15 characters.'
),
'The passwords do not match' => array(
'rule' => 'matchPasswords',
'message' => 'The passwords do not match.'
)
),
function hashPasswords($data) {
if (isset($this->data['User']['password'])) {
$this->data['User']['password'] = Security::hash($this->data['User']['password'], NULL, TRUE);
return $data;
}
return $data;
}
function beforeSave() {
$this->hashPasswords(NULL, TRUE);
return TRUE;
}
Users Controller:
function beforeFilter() {
parent::beforeFilter();
if ($this->action == 'add' || $this->action == 'edit' ) {
$this->Auth->authenticate = $this->User;
}
}
function add() {
if (!empty($this->data)) {
if ($this->User->save($this->data)) {
$this->Session->setFlash('Your Account Has Been Created.');
$this->redirect(array('action' => 'homepage'));
}
}
}
function login() {
}
I haven't seen the video, but -
When $this->data['User'] has a 'username' and 'password' array, and is used to save a user - cake actually hashes the password. What may potentially be happening is your hashed password being hashed again - check out the cake documentation on hashPassword
As far as password matching goes - it is actually far easier to do this on the client side where they aren't hashed (lots of Jquery functions out there that validate forms). You can even go as far as writing a simple:
if($this->data['User']['password'] != $this->data['User']['password_conf']{
$this->Session->setFlash('Passwords do not match');
} else {
//save user etc
}
If you want validate in the model, then certainly write a custom validation rule - but again the password will be hashed for you - you only need to compare the non-hashed versions and see if they match, returning true if they do.
Also - everyone has completely different ways of authenticating users - first and foremost read the documentation - there's an excellent tutorial for a simple Authenticated / ACL application which takes about 30 mins to go through and should be anyones starting point.
Related
I'm using cakephp 2.xx, I want to hashing password with sha256 before it going to database,
before it I wanna make validation value password in my form input, validation which check password input and re-confirm password is match, if In my controller, when form catch validation, the password automatically hash
if ($this->request->data['Driver']['password'] != $this->request->data['Driver']['confirm_password']) {
$this->request->data['Driver']['password'] = hash('sha256',$this->request->data['Driver']['password']);
}
necessarily, the password hash when form no catch validate at all, so how can I make validation in my model ?
Thanks In Advance.
In your model (Driver.php)
Validation
<?php
public $validate = array(
'password' => array(
'notempty' => array(
'rule' => array('notempty'),
),
'password_confirm'=>array(
'rule'=>array('password_confirm'),
'message'=>'Password Confirmation must match Password',
),
),
);
?>
Custom validation rule
<?php
public function password_confirm(){
if ($this->data['Driver']['password'] !== $this->data['Driver']['password_confirmation']){
return false;
}
return true;
}
?>
Hashing,but I think that better to choose AuthComponent
<?php
public function beforeSave($options = array()) {
$this->data['Driver']['password'] = hash('sha256',$this->data['Driver']['password']);
return true;
}
?>
It's overall description and you probably would need to modify some parts of it
I try to find a good and clean way to deal with an admin panel "edit user" in cakePHP v.2.7.
To be clear : I want to be able to edit my user with or without overwriting their password, but the cakePHP validator tool don't let me do what I want...
I've already take a look at CakePHP: Edit Users without changing password and Updating user email and password with CakePHP but it seem really dirty :
the first one don't apply the rules onUpdate
the second display the hash (just no... u_u")
There is no other way to do it ? (with as few line as possible)
TL;DR :
View
// add in your view `app/View/Users/edit.ctp`
// a 'fake' field you'll only use on the controller
echo $this->Form->input('new_password');
Controller
// add in your controller `app/Model/User.php#edit()`
// if we have a new password, create key `password` in data
if(!empty($new_password = $this->request->data['User']['new_password']))
$this->request->data['User']['password'] = $new_password;
else // else, we remove the rules on password
$this->User->validator()->remove('password');
Ok, I finally get what I want, here is my code :
On your app/View/Users/edit.ctp you add a field to your form (a custom one, don't add it to your DB)
<?php
// app/View/Users/edit.ctp
echo $this->Form->create('User');
// your other fields
// a 'fake' field you'll only use on the controller
echo $this->Form->input('new_password');
Don't change your app/Model/User.php ; here is mine :
<?php
// app/Model/User.php
App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel {
public $validate = array(
// [...] other rules
'password' => array(
'passwordLength'=>array(
'rule' => array('minLength', 8),
'message' => 'Too short...',
),
'passwordNotBlank'=>array(
'rule' => 'notBlank',
'required' => true,
'allowEmpty' => false,
'message' => 'A password is required',
),
),
);
public function beforeSave($options = array()) {
if (!empty($pwd = $this->data[$this->alias]['password']))
$this->data[$this->alias]['password'] = AuthComponent::password($pwd);
return true;
}
}
And on your app/Controller/UsersController.php you use this :
<?php
public function edit($id = null) {
$this->User->id = $id;
if (!$this->User->exists())
throw new NotFoundException(__('Invalid user'));
if ($this->request->is('post') || $this->request->is('put')) {
// IMPORTANT >>>>>>>>>>>
// if we have a new password, create key `password` in data
if(!empty($new_password = $this->request->data['User']['new_password']))
$this->request->data['User']['password'] = $new_password;
else // else, we remove the rules on password
$this->User->validator()->remove('password');
// <<<<<<<<<<<<<<<<<<<<<
// then we try to save
if ($this->User->save($this->request->data)) {
$this->Flash->success(__('The user has been updated'));
$this->redirect(array('action' => 'index'));
}
else
$this->Flash->warning(__('The user could not be updated.'));
}
else {
$this->request->data = $this->User->read(null, $id);
unset($this->request->data['User']['password']);
}
}
With the 4 important lines, you are now able to set a new password if needed or disable the validation on the password.
I used this for reference
http://book.cakephp.org/2.0/en/models/data-validation.html#removing-rules-from-the-set
I have two field "password" (This field is in the database) and confirm_password (This field is not in the database)
Well, I need to compare if password == confirm_password.. but I'm not knowing create a custom validation to "confirm_password"... Would need to have this field in the database?
How do I do?
Generally you can access all data in a custom validation rule via the $context argument, where it's stored in the data key, ie $context['data']['confirm_password'], which you could then compare to the current fields value.
$validator->add('password', 'passwordsEqual', [
'rule' => function ($value, $context) {
return
isset($context['data']['confirm_password']) &&
$context['data']['confirm_password'] === $value;
}
]);
That being said, recently a compareWith validation rule was introduced which does exactly that.
https://github.com/cakephp/cakephp/pull/5813
$validator->add('password', [
'compare' => [
'rule' => ['compareWith', 'confirm_password']
]
]);
Now there has a method call sameAs in validator class, for version 3.2 or grater.
$validator -> sameAs('password_match','password','Passwords not equal.');
see API
I know it's late answer but will help to other.
// Your password hash value (get from database )
$hash = '$2y$10$MC84b2abTpj3TgHbpcTh2OYW5sb2j7YHg.Rj/DWiUBKYRJ5./NaRi';
$plain_text = '123456'; // get from form and do not make hash. just use what user entred.
if (password_verify($plain_text, $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
OR
$hasher = new DefaultPasswordHasher();
$check = $hasher->check($plain_text,$hash); // it will return true/false
I am completing my login function for my application and I'm receiving the trying to get property of non object on a few lines. The first one is shown below that does a function call to the is_user_locked method using the object property lock_date inside the user_data object. I understand that this means that at this point their is no user_data to work with so it can not use the properties. I'm curious to know how should I account for this so that I don't abuse using too many nested if statements.
if (count($user_data) == 0) {
$output = array('content' => 'The user was not found in the database!', 'title' =>
'User Not Found');
}
if ($this->is_user_locked($user_data->lock_date)) {
$output = array('content' => 'This user account is currently locked!', 'title' =>
'Account Locked');
}
Any ideas on why this could be? Any and all suggestions would be helpful.
I'm curious to know how should I account for this so that I don't
abuse using too many nested if statements.
How about something like this ?
if( $user_data ) {
// $user_data contains something so let's proceed
if ($this->is_user_locked($user_data->lock_date)) {
$output = array('content' => 'This user account is currently locked!', 'title' => 'Account Locked');
}
} else {
// Nothing in $user_data so throw Exception or display error
$output = array('content' => 'The user was not found in the database!', 'title' =>
'User Not Found');
}
See this link.
if($var): same as $var == NULL.
What seems to be happening is $user_data->lock_date should be $user_data['lock_date']. I'm not 100% sure by the code you posted but it seems you're just referencing an array element incorrectly.
Terinary operators can be used to avoid if statements
$user_data === null ? "it's null" : "otherwise it's not";
i am a huge fan of negative if checks. the idea is that if you do not get what you expect, you immediately exit. this can be getting back a result, or doing a true false check. Of course you are first going to validate the user name using CI form validation. so for example in your model, the method that checks for the user name - if the result is 0, just have it return false. Then in your controller:
// Validate the user name and other form info
// if validation passes, grab the username,
// note the TRUE, that tells CI to run it through XSS clean
$username = $this->input->post( 'username', TRUE ) ;
// if we did NOT get a user back from model, immediately go to new method
if ( ! $user = $this->users_m->_findUser($username) ) {
$this->_userNotFound(); }
// similar, if the user account is locked, go to new method
// if you return an array from model this would be $user['lock_date']
elseif ($this->isUserLocked($user->lock_date) == TRUE) {
// DO NOT try and write messages etc here. put all that in a separate method
$this->_userAccountLocked(); }
else {
// you have a $user and the user is not locked
// its tempting to write a bunch of stuff here
// do not do that. keep it clean, and go to separate method
$this->_displayAccount($user) ; }
Note that i put an underscore before all the method names - CI will automatically make those private.
Also note the Camel case for method names -- versus underscores. some people prefer it.
You could do this:
if (count($user_data) == 0) {
$output = array('content' => 'The user was not found in the database!', 'title' =>
'User Not Found');
} else if ($this->is_user_locked($user_data->lock_date)) {
$output = array('content' => 'This user account is currently locked!', 'title' =>
'Account Locked');
}
Which is pretty much identical to your initial code, but with an else thrown in.
Greetings,
I am setting up a pretty standard registration form with password field.
The problem is, after a failed submission (due to empty field, incorrect format etc), the controller reloads the registration page, but with the password field containing the hashed value of the previously entered password. How do I make it empty after each failed submission?
View:
echo $form->password('Vendor.password', array('class' => 'text-input'));
Controller:
Security::setHash('sha1');
$this->Auth->sessionKey = 'Member';
$this->Auth->fields = array(
'username' => 'email',
'password' => 'password'
);
Help is very much appreciated, thanks!
You may run into another problem down the road with cakePHP password validation.
The problem is that cake hashes passwords first, then does validation, which can cause the input to fail even if it is valid according to your rules. This is why the password is returned to the input field hashed instead of normal.
to fix this, instead of using the special field name 'password', use a different name like 'tmp_pass'. This way, cakePHP Auth won't automatically hash the field.
Here's a sample form
echo $form->create('Vendor', array('action' => 'register'));
echo $form->input('email');
echo $form->input( 'tmp_pass', array( 'label' => 'Password','type'=>'password' ));
echo $form->end('Register');
In your Vendor model, don't assign validation rules to 'password' instead assign these rules to 'tmp_pass', for example
var $validate = array('email' => 'email', 'password' => ... password rules... );
becomes
var $validate = array('email' => 'email', 'tmp_pass' => ... password rules... );
Finally, in your Vendor model, implement beforeSave().
First, see if the data validates ('tmp_pass' will be validated against your rules).
If successful, manually hash tmp_pass and put it in $this->data['Vendor']['password'] then return true. If unsuccessful, return false.
function beforeSave() {
if($this->validates()){
$this->data['Vendor']['password'] = sha1(Configure::read('Security.salt') . $this->data['User']['tmp_pass']);
return true;
}
else
return false;
}
this?
password('Vendor.password', array('class' => 'text-input','value'=>''))
In your controller:
function beforeRender() {
parent::beforeRender();
$this->data['Vendor']['password'] = '';
}