Updating user email and password with CakePHP - php

I have a CakePHP application with user registration. On the users page, I want them to be able to update their email and password. THis is my User model:
<?php
class User extends AppModel {
public $name = 'User';
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
),
'range' => array(
'rule' => array('between', 4, 20),
'message' => 'Between 4 and 20 characters'
),
'characters' => array(
'rule' => array('alphaNumeric'),
'message' => 'Alphanumeric characters only'
),
'unique' => array(
'rule' => array('isUnique'),
'message' => 'This username is taken'
)
),
'email' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'An email is required'
),
'validEmail' => array(
'rule' => array('email'),
'message' => 'Please provide a valid email'
),
'range' => array(
'rule' => array('between', 5, 64),
'message' => 'Between 5 and 64 characters'
),
'unique' => array(
'rule' => array('isUnique'),
'message' => 'This email has already been used'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
),
'range' => array(
'rule' => array('between', 5, 64),
'message' => 'Between 5 and 64 characters'
),
)
);
public function beforeSave() {
if (isset($this->data[$this->alias]['password'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
}
return true;
}
}
And I'm using the form helper to create the form:
<p>Modify your account settings</p>
<?php echo $this->Session->flash(); ?>
<?php
echo $this->Form->create('User');
echo $this->Form->input('currentPassword', array('type' => 'password'));
echo $this->Form->input('username', array('disabled' => 'disabled', 'value' => $username));
echo $this->Form->input('email');
echo $this->Form->input('newPassword', array('type' => 'password'));
echo $this->Form->end('Update');
?>
How would I go about checking if the current password is valid, checking if the new email and password pass validation rules and then updating the users record in my users table from within my controller?

Adding a new user - UsersController.php:
public function add() {
if ($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('Registration complete. :)'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('Error... Please try again.'));
}
}
}
Editing a user - UsersController.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')) {
if($this->Auth->user('password') == AuthComponent::password($this->request->data['User']['password']){
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}}else{
$this->Session->setFlash(__('Incorrect password.'));
}
else {
$this->request->data = $this->User->read(null, $id);
}
}
Just watch for {}. :D
If it doesn't work, try
if($this->Auth->user('password') == $this->request->data['User']['password'])...

For those who want something without changing the model and without displaying the hashed password : Updating user with or without password - CakePHP
TL;DR :
// 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');
// add in your controller `app/Model/User.php`
// 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');

you should also present a second password field for confirmation. this is usual for password updates.
for this and also if you want to check against the current password, see this behavior for how you can accomplish that:
http://www.dereuromark.de/2011/08/25/working-with-passwords-in-cakephp/

Ans:
How does this check if the password the user has entered is the same as what's stored in the database? – James Dawson
You can add the below function in your model.
function check_user($check) {
if(!empty($check["EMail"]) && !empty( $_POST['data']['User']['password']))
{
$user = $this->find('first',array('conditions'=>array('User.EMail'=>$check["EMail"],'User.IsVerified'=>1)));
if(empty($user)) {
return FALSE;
}
$Encrypted = md5($_POST['data']['User']['password']);
if($user['User']['password'] != ($Encrypted)) {
return FALSE;
}
}
return TRUE;
}
and validate rule
'EMail' => array(
'email' => array(
'rule' => array('email'),
'message' => 'Please enter valid email address..!',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
'on' => 'login', // Limit validation to 'create' or 'update' operations
),
'check_user'=>array(
'rule'=>'check_user',
'message'=>'Either your Username or Password is invalid',
'on' => 'login', // Limit validation to 'create' or 'update' operations
'last'=>TRUE,
),
),

protected function _update_password() {
$password_error = false;
/**
* Handle post
*/
if (($this->request->is('post') || $this->request->is('put')) && isset($this->request->data['User'])) {
$old_pass_in_db = $this->User->read('password', $this->Session->read('Auth.User.id'));
$old_pass_in_post = $this->Auth->password($this->request->data['User']['old_password']);
//assign post data
$this->User->set($this->request->data);
//validate
if (trim($old_pass_in_post) != trim($old_pass_in_db['User']['password'])) {
$this->User->validationErrors['old_password'] = __("Old password do not match.");
} else {
unset($this->User->validationErrors['old_password']);
}
if ($this->User->validates(array('fieldList' => array('password', 'password_confirm'))) && ($old_pass_in_post == $old_pass_in_db['User']['password'])) {
$this->User->id = $this->Session->read('Auth.User.id');
if ($this->User->save(array('password', $this->Auth->password($this->request->data['User']['password'])), false)) {
$this->Session->setFlash(__('Password updated successfully.', true), 'default', array('class' => 'alert alert-success'));
} //end save
} else {
$password_error = true;
}
}
$this->set('password_error', $password_error);
}

Related

Checking for 'isUniqueUsername' in cakephp doesn't seem to work?

I have a register form where when a user registers it asks for their Username, Password and email. For the username, I have a few rules such as:
Must be 5-12 characters
Must be unique
Must use only alpha, numbers, and dashes
With that being said, I can't seem to get my form to catch duplicate usernames, I was hoping someone could help me solve this!
Here is my User.php Model's Validate:
public $validate = array(
'username' => array(
'nonEmpty' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required',
'allowEmpty' => false
),
'between' => array(
'rule' => array('between', 5, 15),
'required' => true,
'message' => 'Usernames must be between 5 to 15 characters'
),
'unique' => array(
'rule' => array('isUniqueUsername'),
'message' => 'This username is already in use'
)
)
);
I removed the 3rd rule (alpha/number/dash) for the sake of the example
Followed by my function to check my DB for the username:
function isUniqueUsername($check) {
$username = $this->find(
'all',
array(
'fields' => array(
'User.id',
'User.username'
),
'conditions' => array(
'User.username' => $check['username']
)
)
);
if(!empty($username)){
if($this->data[$this->alias]['id'] == $username['']['id']){
return true;
}else{
return false;
}
}else{
return true;
}
}
here is the Controller function for "Register"
public function register() {
if ($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been created'));
$this->redirect($this->Auth->redirect());
} else {
$this->Session->setFlash(__('The user could not be created. Please, try again.'));
}
}
}
Change
'unique' => array(
'rule' => array('isUniqueUsername'),
'message' => 'This username is already in use'
)
To
'unique' => array(
'rule' => 'isUnique',
'message' => 'This username is already in use'
)
You do not need that function to check for uniqueness, Cake does it for you with magic.

CakePHP Login issue. and it doesnt work

I know there are many questions on this issue, but I've tried to add all resolutions but still no luck so far, so I am sharing my code to see if anyone can give me a solution. Thanks.
I've successfully hashed my password, I already added text field in the password database so I won't have any problem with character amount. But every time I try to login, it says Invalid username/password, and it doesn't redirect or anything. I am a newbie in programming and PHP. Please help.
Here is my code:
AppController.php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $helpers = array('Html', 'Session', 'Form' );
public $components = array(
'DebugKit.Toolbar',
'Session',
'Auth'=>array(
'loginRedirect'=>array('Controller'=>'user', 'action'=>'index'),
'logoutRedirect'=>array('Controller'=>'user', 'action'=>'index'),
'authError'=>"you are not allowed to access that page",
'authorize'=>array('Controller')
)
);
public function isAuthorized($user){
return TRUE;
}
public function beforeFilter() {
$this->Auth->allow('index', 'add');
}
}
login.ctp
<h1> Login Form</h1>
<?php
echo $this->Form->create('User');
echo $this->Form->input('user_name');
echo $this->Form->input('password');
echo $this->Form->end(__('Login'));
?>
User.php
// hash password before saving It
/*
public function beforeSave($options = array()) {
$this->data['User']['password'] =
AuthComponent::password($this->data['User']['password']);
return TRUE;
}
*/
public function beforeSave($options = array()) {
if (isset($this->data['User']['password'])) {
$this->data['User']['password'] = AuthComponent::password($this->data['User'] ['password']);
}
return TRUE;
}
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'user_id';
/**
* Display field
*
* #var string
*/
public $displayField = 'user_name';
/**
* Validation rules
*
* #var array
*/
public $validate = array(
//USERNAME VALIDATION
'username' => array(
'required' => array(
'rule' => array('minLength', 1),
'allowEmpty' => false,
'message' => 'Please enter a title.'
)
),
'user_name' => array(
'required' => array(
'rule' => array( 'isUnique' ),
'message' => 'Username already exist. Please try again',
//'allowEmpty' => false,
//'required' => TRUE,
//'last' => TRUE, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
//EMAIL ADDRESS VALIDATION
'email_address' => array(
'required' => array(
'rule' => array('minLength', 1),
'allowEmpty' => false,
'message' => 'Please add an email'
)
),
'email_address' => array(
'required' => array(
'rule' => array( 'isUnique' ),
'message' => 'Email already exist in our database. Please try again',
//'allowEmpty' => false,
//'required' => TRUE,
//'last' => TRUE, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'password'=>array(
'not empty' => array(
'rule'=>'notEmpty',
'Message'=>'Password is empty'
),
'Match Passwords'=> array(
'rule'=>'matchPasswords',
'message'=>'Password do not match'
)
),
'password_confirmation'=>array(
'not empty' => array(
'rule'=>'notEmpty',
'Message'=>'verify password'
)
)
);
// PASSWORD CONFIRMATION VALIDATION FUNCTION
public function matchPasswords($data){
if ($data['password'] == $this->data['User']['password_confirmation']) {
return True;
}
$this->invalidate('password_confirmation', 'Your password do not match');
return FALSE;
}
}

Show all model validation errors on top of the page in cakePHP

I am new to CakePHP. When I am using Model Field Validations then it is showing error message infront of each required form field. I want to show it in a div at the top of the form. How I can implement it. Thanks in advance.
Here is my Code:
Model:
<?php
class User extends AppModel {
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
),
array(
'rule' => array('minLength', 8),
'message' => 'Username must be at least 6 characters long'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
),
'city' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A City is required'
)
),
'state' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A State is required'
)
),
'role' => array(
'valid' => array(
'rule' => array('inList', array('admin', 'author')),
'message' => 'Please enter a valid role',
'allowEmpty' => false
)
)
);
}
UsersController.php
public function add() {
$this->set('states_options', $this->State->find('list', array('fields' =>array('id','name') )));
$this->set('cities_options', array());
if ($this->request->is('post')) {
$this->User->set($this->request->data);
if($this->User->validates())
{
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
else {
$errors = $this->User->validationErrors;
$this->set('ValidateAjay',$errors);
//pr($errors);die;
}
}
}
User View:
<!--<script src="http://code.jquery.com/jquery-1.7.2.js"></script>-->
<script>
$(document).ready(function(){
$('#UserState').change(function(){
var stateid=$(this).val();
$.ajax({
type: "POST",
url: "checkcity",
data:'stateid='+stateid+'&part=checkcity',
success: function(data) {
$("#city_div").html(data);
}
});
});
});
</script>
<div class="users form">
<?php
if(!empty($ValidateAjay)){
pr($ValidateAjay);
}
echo $this->Form->create('User'); ?>
<fieldset>
<legend><?php echo __('Add User'); ?></legend>
<?php
echo $this->Form->input('username');
echo $this->Form->input('password');
echo $this->Form->input('state', array('options' => $states_options , 'empty' => 'Select State' ));
?>
<div id="city_div">
<?php
echo $this->Form->input('city', array('options' => $cities_options, 'empty' => 'Select City' ));
?>
</div>
<?php
echo $this->Form->input('role', array(
'options' => array('admin' => 'Admin', 'author' => 'Author')
));
?>
</fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
</div>
You can get all validation errors from the $this->validationErrors variable on the view. Then feel free to iterate through them and display as you like. They will be organized by model, like so:
array(
'User' => array(
'username' => 'This field cannot be empty.',
'password' => 'This field cannot be empty.'
)
);
Then you can iterate through them on the view and display them as such. This example displays them as an unordered list:
$errors = '';
foreach ($this->validationErrors['User'] as $validationError) {
$errors .= $this->Html->tag('li', $validationError);
}
echo $this->Html->tag('ul', $errors);
Lastly, you can hide the form helper's automatic error messages by hiding them with CSS or setting the FormHelper defaults to not show them.
CSS
.input.error {
display: none;
}
or
in the view
$this->Form->inputDefaults(array(
'error' => false
));
jeremyharris example makes a lot of sense, however if you don't want to manually set loop for every form field, you can try this:
$errors = '';
foreach($this->validationErrors as $assoc) {
foreach ($assoc as $k => $v) {
$errors .= $this->Html->tag('li', $v);
}
}
echo $this->Html->tag('ul', $errors);
So if your validation returns multiple errors, the output will looks like this:
- A username is required
- A password is required
Though this is a very old post, I'd like to answer my solution here as well.
To get Errors of Model Validations, just use the $model->getErrors();. This will return an array of errors with field key.
Example
$errors = $user->getErrors();

Unique username and email with CakePHP Auth component

I'm new to Cake and building an application to learn it. I'm having a few troubles with my user registration system. So far this is my registration code in my Users controller:
public function register() {
$this->set('title_for_layout', 'Register');
if ($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->Session->setFlash('The user has been saved');
$this->redirect(array('action' => 'register'));
} else {
$this->Session->setFlash('The user could not be saved. Please, try again.');
}
}
}
And within my User model I have this method where I hash the passwords:
public function beforeSave() {
if (isset($this->data[$this->alias]['password'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
}
return true;
}
This works, the user is added to my users table in my database with their username, email and hashed password. However, there are no checks done to make sure the username and email are unique.
From my limited understanding, I would need to add some validation rules to my User model to make sure the username and email fields are unique before they're entered into the table? At the moment I just have these validation rules:
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
)
),
'email' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'An email is required'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
)
);
Also, my registration form has a Password (confirm) field called passwordConf. I would like to check if the user entered his passwords correctly before they're entered into the users table, but I'm not sure how to do that. I'm guessing that somewhere in my register method I need to check if the two passwords are the same.
Thanks for any help.
The isUnique rule will work with your username and email fields. Here is a sample of code that shows how to use multiple rules per field:
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'You must enter a username.'
),
'length' => array(
'rule' => array('between', 3, 15),
'message' => 'Your username must be between 3 and 15 characters long.'
),
'unique' => array(
'rule' => 'isUnique',
'message' => 'This username has already been taken.'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'You must enter a password.'
),
'length' => array(
'rule' => array('minLength', '6'),
'message' => 'Your password must be at least 6 characters long.'
)
),
'email' => array(
'email' => array(
'rule' => array('email'),
'message' => 'Please enter a valid email address.'
)
)
);
As for comparing the passwords just edit your beforeSave callback and check the passwords against each other, returning true if they match and false if they do not. Something like this:
public function beforeSave() {
if (isset($this->data[$this->alias]['password'])) {
if($this->data[$this->alias]['password'] === $this->data[$this->alias]['passwordConf']) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
return true;
} else {
return false;
}
}
return true;
}
CakePHP actually has a validation rule called isUnique, which you can use to check the username and e-mail. A list of built in rules can be found here. You can use this and the Data Validation Tutorial to check the user name and e-mail. As to checking if the passwords are the same, you MAY be able to use the EqualTo rule shown in the rules list, assuming you can make your validation rules on the fly every request.

cakephp password validation

var $validate = array(
'password' => array(
'passwordlength' => array('rule' => array('between', 8, 50),'message' => 'Enter 8-50 chars'),
'passwordequal' => array('checkpasswords','message' => 'Passwords dont match')
)
);
function checkpasswords()
{
return strcmp($this->data['Airline']['password'],$this->data['Airline']['confirm password']);
}
This code is not working and always gives the error message even if they match. Also when i do a edit i get the followoing error as there is no password field. is there any fix
Undefined index: password [APP/models/airline.php, line 25]
Are you using the AuthComponent? Be aware that it hashes all incoming password fields (but not "password confirm" fields, check with debug($this->data)), so the fields will never be the same. Read the manual and use AuthComponent::password to do the check.
Having said that, here's something I use:
public $validate = array(
'password' => array(
'confirm' => array(
'rule' => array('password', 'password_control', 'confirm'),
'message' => 'Repeat password',
'last' => true
),
'length' => array(
'rule' => array('password', 'password_control', 'length'),
'message' => 'At least 6 characters'
)
),
'password_control' => array(
'notempty' => array(
'rule' => array('notEmpty'),
'allowEmpty' => false,
'message' => 'Repeat password'
)
)
);
public function password($data, $controlField, $test) {
if (!isset($this->data[$this->alias][$controlField])) {
trigger_error('Password control field not set.');
return false;
}
$field = key($data);
$password = current($data);
$controlPassword = $this->data[$this->alias][$controlField];
switch ($test) {
case 'confirm' :
if ($password !== Security::hash($controlPassword, null, true)) {
$this->invalidate($controlField, 'Repeat password');
return false;
}
return true;
case 'length' :
return strlen($controlPassword) >= 6;
default :
trigger_error("Unknown password test '$test'.");
}
}
This is bad for the following reasons:
Has tight coupling to the form, always expects a field password_control to be present. You need to use field whitelisting or disable validation if you don't have one in your data, i.e.: $this->User->save($this->data, true, array('field1', 'field2')).
Manually hashes the password the way the AuthComponent does (since there's no clean access to components from the model). If you change the algorithm used in the AuthComponent, you need to change it here as well.
Having said that, it transparently validates and produces proper error messages for both the password and password control fields without requiring any additional code in the controller.
here is the mistake
'passwordequal' => array('checkpasswords','message' => 'Passwords dont match')
I changed it to
'passwordequal' => array('rule' =>'checkpasswords','message' => 'Passwords dont match')
also strcmp function also had mistakes as it would return 0 (i.e False) all the time in the above code
if(strcmp($this->data['Airline']['password'],$this->data['Airline']['confirm_password']) ==0 )
{
return true;
}
return false;
For Validate Password,old password and confirm Password
class Adminpassword extends AppModel
{
public $name = 'Admin';
public $primaryKey = 'id';
public $validate = array(
'oldpassword' => array(
array(
'rule' => 'notEmpty',
'required' => true,
'message' => 'Please Enter Current password'
),
array(
'rule' =>'checkcurrentpasswords',
'message' => 'Current Password does not match'
)
),
'password' => array(
array(
'rule' => 'notEmpty',
'required' => true,
'message' => 'Please Enter password'
),
array(
'rule' => array('minLength', 6),
'message' => 'Passwords must be at least 6 characters long.',
)
),
'cpassword' => array(
array(
'rule' => 'notEmpty',
'required' => true,
'message' => 'Please Enter Confirm password'
),
array(
'rule' => 'checkpasswords',
'required' => true,
'message' => 'Password & Confirm Password must be match.'
)
)
);
function checkpasswords() // to check pasword and confirm password
{
if(strcmp($this->data['Adminpassword']['password'],$this->data['Adminpassword']['cpassword']) == 0 )
{
return true;
}
return false;
}
function checkcurrentpasswords() // to check current password
{
$this->id = $this->data['Adminpassword']['id'];
$user_data = $this->field('password');
//print_r(Security::hash($this->data['Adminpassword']['oldpassword'], 'sha1', true));
if ($user_data == (Security::hash($this->data['Adminpassword']['oldpassword'], 'sha1', true)))
{
return true;
}
else
{
return false;
}
}
}
For CakePHP 2.x users using Authentication you may note that "AuthComponent no longer automatically hashes every password it can find." I.e. the solutions above may not be the correct way of solving the problem for 2.x.
http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#hashing-passwords
Heres is my solution:
You must to make a method named match (You can name it what you like):
public function match($check, $with) {
// Getting the keys of the parent field
foreach ($check as $k => $v) {
$$k = $v;
}
// Removing blank fields
$check = trim($$k);
$with = trim($this->data[$this->name][$with]);
// If both arent empty we compare and return true or false
if (!empty($check) && !empty($with)) {
return $check == $with;
}
// Return false, some fields is empty
return false;
}
And the $validate method must be like this:
public $validate = array(
'password' => array(
'match' => array(
'rule' => array('match', 'password2'),
'message' => 'Passwords doesnt match',
),
),
);
Where password2 is the field to compare your first password field
I'm Glad to share it! :D
Would this help: http://sumanrs.wordpress.com/2011/10/01/cakephp-user-password-manager-authentication-missing-guide/ ? That should take care of password validation.

Categories