How do I save users in my CakePHP app without requiring them to change their password each time?
I have code in place to check the two password fields and apply some verification rules, which works great for registration and for changing passwords in the 'edit' view. However, how do I skip the verification rules and saving the password if the password fields are left empty in the Edit view? Obviously, I don't want to skip this requirement on registration.
register.ctp and edit.ctp:
echo $form->create('User');
echo $form->input('username');
echo $form->input('pwd');
echo $form->input('pwd_repeat');
echo $form->end('Submit');
User.ctp validation rules:
'pwd' => array(
'length' => array(
'rule' => array('between', 8, 40),
'message' => 'Your password must be between 8 and 40 characters.',
),
),
'pwd_repeat' => array(
'length' => array(
'rule' => array('between', 8, 40),
'message' => 'Your password must be between 8 and 40 characters.',
),
'compare' => array(
'rule' => array('validate_passwords'),
'message' => 'The passwords you entered do not match.',
),
),
and the User.ctp logic before saving:
public function validate_passwords() { //password match check
return $this->data[$this->alias]['pwd'] === $this->data[$this->alias]['pwd_repeat'];
}
public function beforeSave($options = array()) { //set alias to real thing and hash password
$this->data['User']['password'] = $this->data[$this->alias]['pwd'];
$this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
return true;
}
var $validate = array(
'pwd' => array(
'length' => array(
'rule' => array('between', 8, 40),
'message' => 'Your password must be between 8 and 40 characters.',
'on' => 'create', // we only need this validation on create
),
),
// if we have a password entered, we need it to match pwd_repeat (both create and update)
// we no longer need the length validation
'pwd_repeat' => array(
'compare' => array(
'rule' => array('validate_passwords'),
'message' => 'Please confirm the password',
),
),
);
public function validate_passwords() { //password match check
return $this->data[$this->alias]['pwd'] === $this->data[$this->alias]['pwd_repeat'];
}
public function beforeSave($options = Array()) {
// if we have a password, we hash it before saving
if (isset($this->data[$this->alias]['pwd'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['pwd_repeat']);
}
return true;
}
If you are using CakePHP 2.2:
http://book.cakephp.org/2.0/en/models/data-validation.html#removing-rules-from-the-set
Also in the beforeSave function wrap the first two lines in a conditional for if both the password fields are not empty.
For those who want something without changing the model (and keeping rule onUpdate if new password send) : 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');
Just remove the field from edit.ctp
echo $form->create('User');
echo $form->input('username');
//echo $form->input('pwd');
//echo $form->input('pwd_repeat');
echo $form->end('Submit');
Because this->request->data populates hashed password in the password field. When you save the user password hashed again and become different then original one
Related
I am having a problem with Validating Top Level Domains. Basically, anything with .tech as the TLD is failing the email validation.
I have inherited this project and don't know Zend very well but I have traced the problem back to the hostname not being valid here is the code on GitHub;
// Match hostname part
if ($this->_options['domain']) {
$hostname = $this->_validateHostnamePart();
}
$local = $this->_validateLocalPart();
// If both parts valid, return true
if ($local && $length) {
if (($this->_options['domain'] && $hostname) || !$this->_options['domain']) {
return true;
}
}
return false;
Now, I have some local code here;
class Form_InviteToSpaceForm extends Twitter_Bootstrap_Form_Horizontal
{
public function init()
{
// Set the method for the display form to POST
$this->setMethod('post');
$this->setAction('/team');
$this->addElement('textarea', 'email', array(
'label' => 'Email addresses',
'dimension' => 10,
'required' => true,
'placeholder' => "person1#email.com person2#email.com",//line breaks don't work on placeholders, have to force line wrap with spaces
'filters' => array('StringTrim'),
'validators' => array(
array('validator' => 'NotEmpty'),
array('validator' => 'EmailAddress', 'options' => array('messages' => array('emailAddressInvalidFormat' => 'Incorrect email address specified.')))
)
));
If I comment out the line with the last array('messages' => array('emailAddressInvalidFormat' => 'Incorrect email address specified.'))) then this whole validation is avoided. But I don't want to avoid using this. I just want to be able to extend and add .tech or whatever else comes up from genuine clients. How can I do this with Zend?
You can write custom validator extended from Zend validator
class My_Validate_Hostname extends Zend_Validate_Hostname
{
public function __construct($options = array())
{
parent::__construct($options);
$this->_validTlds = array_merge($this->_validTlds, array('tech'));
}
}
and pass it to email validator
$emailValidator = new Zend_Validate_EmailAddress(array('messages' => array('emailAddressInvalidFormat' => 'Incorrect email address specified.')));
$emailValidator->setHostnameValidator(new My_Validate_Hostname());
....
$this->addElement('textarea', 'email', array(
'label' => 'Email addresses',
'dimension' => 10,
'required' => true,
'placeholder' => "person1#email.com person2#email.com",//line breaks don't work on placeholders, have to force line wrap with spaces
'filters' => array('StringTrim'),
'validators' => array(
array('validator' => 'NotEmpty'),
)
))->addValidator($emailValidator);
how can we check firstname and last name is unique validation in cakePHP ?
record1:
first name :raj
last name: kumar
if we enter same name in input field , it should show validation message "Record alredy Exists".
i know how to validate single field validation.
how to validate that the combination of first_name and last_name is unique?
Please help me in this.
Thanks in Advance
You will want to setup a custom validation rule for testing that the 'full name' is unique. For example, in your model add a new method for validation like this:-
public function validateUniqueFullName(array $data) {
$conditions = array(
'first_name' => $this->data[$this->alias]['first_name'],
'last_name' => $this->data[$this->alias]['last_name']
);
if (!empty($this->id)) {
// Make sure we exclude the current record.
$conditions[$this->alias . '.' . $this->primaryKey . ' !='] = $this->id;
}
return $this->find('count', array('conditions' => $conditions)) === 0;
}
Then add the new validation rule to the model's $validate property:-
public $validate = array(
'first_name' => array(
'unique' => array(
'rule' => 'validateUniqueFullName',
'message' => 'Not unique'
)
)
);
Where 'rule' => 'validateUniqueFullName' instructs Cake to use the new validation rule to check that the name is unique.
You'll probably want to tweak/improve the above custom rule to meet your exact requirements but it should put you on the right track.
Try this way
public $validate = array(
'facebook_link' => array(
'rule' => array('customValidation','facebook_link'),
'message' => 'Please enter facebook link.'
),
'twitter_link' => array(
'rule' => array('customValidation','twitter_link'),
'message' => 'Please enter twitter link.'
)
);
function customValidation($data , $filed) {
if(empty($data[$filed])) {
return false;
}
return true;
}
I have searched far and wide, tried every trick in the book, but I still can't get my CakePHP application to perform simple Password Confirm validation. I've tried creating a custom validation rule like this:
'passwordequal' => array('rule' => 'checkpasswords' , 'message' => 'Passwords Do Not Match')
Then defined 'checkpasswords' like this:
public function checkpasswords(){
if(strcmp($this->data['User']['new_password'],$this->data['User']['confirm_password']) == 0 )
{
return true;
}
return false;
}
'new_password' and 'confirm_password' are the password input fields. This didn't work. Then I tried one in which I hashed the 'confirm_password'. That didn't work either. I have other 'rules' as well that are not being validated, like 'notempty', which I believe is one of the standard CakePHP rules. Can anybody please help. I know this question has been asked a few times but none of those solutions have worked for me. CakePHP documentation hasn't helped much either.
two fields in view file
echo $this->Form->input('password');
echo $this->Form->input('repass');
Model file
<?php
class Post extends AppModel {
public $validate = array(
'repass' => array(
'equaltofield' => array(
'rule' => array('equaltofield','password'),
'message' => 'Require the same value to password.',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
'on' => 'create', // Limit validation to 'create' or 'update' operations
)
)
);
function equaltofield($check,$otherfield)
{
//get name of field
$fname = '';
foreach ($check as $key => $value){
$fname = $key;
break;
}
return $this->data[$this->name][$otherfield] === $this->data[$this->name][$fname];
}
}?>
Seems like your model is not loading correctly and using a dynamically generated model.
Comparing passwords in 2.x is nothing more than comparing any two fields as cake no longer hashes the pw automatically.
Can you confirm your validation method is being run, seems like its not especially if simple things like notEmpty is not working.
User Model
//...
public $validate = array(
"username" => array(
"alpha-numeric" => array(
'rule' => 'alphaNumeric',
'required' => true,
'message' => 'Username must contain only numbers and letters.'
),
"between" => array(
"rule" => array("between", 4, 25),
"message" => "Username must contain between 4 to 25 characters."
)
),
//...
User Controller
public function index() {
debug($this->data); // displays values as expected
debug($this->User->validates()); // always false, unless I remove alphanumeric rule
}
On submit, my form will always validate the username field as false (triggering its corresponding message), no matter what is in the text. The remaining fields are also not validated. If I remove the username validation rules all together, the remaining fields are still not validated. (Form is submitted as though validated)
What am I missing here?
Before manually calling $this->User->validates(), you must call the model's set method and provide the form's data.
public function index() {
$this->User->set($this->request->data); // Only way the next method will work.
debug($this->User->validates());
}
I want to test login function if it works propperly and only lets valid and active users in.
My user fixture contains:
array(
'password' => '*emptyPasswordHash*', // empty password
'username' => 'Lorem',
'balance' => 0,
'currency' => 'USD',
'id' => 1,
'user_group_id' => 3, //Customer
'active' => 1,
'hash' => 'LoremHash'
),
My test function looks like this:
function testLogin() {
//test valid login
$result = $this->testAction('/users/login', array(
'data' => array(
'User' => array(
'username' => 'Lorem',
'pass' => '',
'remember' => true
)),
'method' => 'post',
'return' => 'view'
));
debug($result);
}
The login form has 3 inputs: username, password and remember
I have set $this->Auth->autoRedirect = false; in the UsersController::beforeFilter and I am doing some cookie setting stuff
when I debug($this->data); in UsersController::login() it shows the exact same data when testing and when logging normaly. But while testing the login fails and I get $this->Auth->loginError message instead of a login.
How do I test login action propperly?
if you use cakes Auth component and dont hack it up you dont need to...
https://github.com/cakephp/cakephp/blob/master/cake/tests/cases/libs/controller/components/auth.test.php#L545
and if you really want to, look at how the pro's do it :)
Did you also set your custom function, like described in The CakePHP manual:
Normally, the AuthComponent will
attempt to verify that the login
credentials you've entered are
accurate by comparing them to what's
been stored in your user model.
However, there are times where you
might want to do some additional work
in determining proper credentials. By
setting this variable to one of
several different values, you can do
different things.