I have the following function in a CakePHP 1.3 app that takes a token and allows the user to change their password. Everything seems to work fine except that the password doesn't actually get changed :/ Any ideas what the problem is?
function admin_changepassword ( $token = null )
{
// If has a token or form has been submitted
if (!empty($token) || !(empty($this->data)))
{
$user = $this->User->find('first',array("MD5(User.email + '".Configure::read('Security.salt')."')"=>$token));
if (empty($user))
{
$this->redirect(array('admin'=>false,'controller'=>'pages','action'=>'display','home'));
$this->Session->setFlash('Invalid token');
}
else
{
$this->set('user',$user);
if (!empty($this->data['User']['password']))
{
$user['User']['password'] = $this->data['User']['password'];
$this->User->save($this->data);
$this->Session->setFlash('Your password has been changed! Please log in.');
$this->redirect(array('admin'=>true,'controller' => 'users', 'action' => 'login'));
}
}
}
else
{
$this->redirect(array('admin'=>false,'controller'=>'home','action'=>'display','home'));
$this->Session->setFlash('No token');
}
}
Personally I'd do something like this; but I'm assuming your user is actually logged in, using the auth component or similar. Untested; but logic looks like it should work to me.
<?php
function admin_changepassword ($token = NULL) {
// bail out early if there is no token set, and always set the flash before redirecting.
if($token==NULL) {
$this->Session->setFlash('No token');
$this->redirect(array('admin'=>false,'controller'=>'home','action'=>'display','home'));
}
// this is an admin action; the user already be logged in right?
$this->User->id = $this->Auth->user('id');
$user_password = $this->User->field('password');
// does the token match the hashed password, and did they enter a new password?
if($token==md5($user_password . Configure::read('Security.salt')) && !empty($this->data['User']['password'])) {
$this->User->saveField('password', $this->data['User']['password']);
$this->Session->setFlash('Your password has been changed! Please log in.');
$this->redirect(array('admin'=>true,'controller' => 'users', 'action' => 'login'));
}
// somethings gone wrong/password was not updated
$this->Session->setFlash('Your password was not changed.');
$this->redirect(array('admin'=>false,'controller'=>'home','action'=>'display','home'));
}
OK upon some testing, your problem is as I initially suggested in my comment.
You aren't setting the id of your user, so:
$this->User->save($this->data);
is not updating the password, it is adding a new row to your database.
You need to specify the ID of the user's to update.
$user = $this->User->find('first',array("MD5(User.email +
'".Configure::read('Security.salt')."')"=>$token));
// this line is redundant
$user['User']['password'] = $this->data['User']['password'];
$this->User->id = $user['User']['id']; // set user id
$this->User->save($this->data); // save it
If you check your users table I suspect you will find there are lots of empty records with the "changed" password. My testing agrees with the cake manual.
This is an old thread, but it might help someone
function changePwd(){
$this->User->id = $this->data->['User']['id']; //assuming this is set
//check if the password fields are empty
//check if the password fields match (password and confirm password one)
//convert password
$this->request->data['User']['password'] = $this->Auth->password($this->data['User']['password'] );
//saveField worked for me
if($this->User->saveField('password',$this->request->data['User']['password'])){
$this->Session->setFlash('Password changed successfully.','flashSuccess');
}else{ ....
}
Related
I'm trying to achieve a redirect to the home page of the user storing email in the session in Codeigniter. I have used password_hash($this->input->post('password'), PASSWORD_DEFAULT)) to hash the passwords and it works fine, but when I try to password_verify() it, it fails.
Here is my Model
public function canLogin($email, $password) {
$this->db->where('email',$email);
$query = $this->db->get($this->tableName);
$row = $query->row();
if ($row) {
return password_verify($password, $row->password);
}
else {
return false;
}
}
and here is my Controller
public function loginValidation() {
// User Model Loaded in constructor
if ($this->user->canLogin($_POST['email'], $_POST['password'])) {
$session_data = array('email' => $_POST['email'] );
$this->session->set_userdata($session_data);
redirect('profile/personal','Refresh');
} else {
echo 'fail';
$this->session->set_flashdata('error', 'Invalid Username or Password');
// redirect('login','Refresh');
}
}
I don't know where the logic went wrong and it everytime redirects to the same login page, I am trying to authenticate it, store email in session and redirect it to profile/personal , Can anyone point where I missed the logic?
#YashKaranke what is the password column's length? – Funk Forty Niner
#FunkFortyNiner It is 50 with datatype varchar – Yash Karanke
The password column's length is too short, it should be 60 or 255 as the manual on PHP.net for password_hash() suggests.
You now have to start over with new hashes.
The verification failed silently.
If you're using:
password_hash($this->input->post('password', PASSWORD_DEFAULT));
Are you sure this is hashing correctly? Shouldn't it be:
password_hash($this->input->post('password'), PASSWORD_DEFAULT);
I was learning this login tutorial online with the choice for user to change password after login and the notes given are
if ($validator->fails())
{
return Redirect::route('account-change-password')
->withErrors($validator);
}
else
{
$user = User::find(Auth::user()->id);
$old_password = Input::get('old_password');
$password = Input::get('password');
if(Hash::check($old_password, $user->getAuthPassword()))
{
$user->password = Hash::make($password);
if($user->save())
{
return Redirect::route('home')
->with('global', 'Your Password has been changed');
}
}
else
{
return Redirect::route('account-change-password')
->with('global', 'Your old password is incorrect');
}
}
in the tutorial inside the else $user = User::find(Auth::user()->id); is used right away and then the if statement to check the password is using $user = User::find(Auth::user()->id);
When I'm trying to do it on my on after watching the tutorial I did it differently.
if ($v->fails())
{
return Redirect::route('account-change-password')
->withErrors($v)
->with('global', 'Please check the errors in red');
}
else
{
if (Hash::check(Input::get('old_password'),Auth::user()->password))
{
$user = User::find(Auth::user()->id);
$user->password = Hash::make(Input::get('new_password'));
if ($user->save())
{
return Redirect::route('home')
->with('global', 'Password Changed.');
}
}
return Redirect::route('account-change-password')
->with('global', 'Old password is incorrect');
}
I checked the password and the password in the database right away with Auth::user()->password) by using the if statement and put the $user = User::find(Auth::user()->id); inside the if statement in order for me to get access to $user->password and change the password.
I know there are always lots different ways to do things but I'm just wondering if the way I am doing it would have some kind of problem in the future that I am not realizing it now and would be better to remember and stick to the way the tutorial taught.
Actually, you should check the source, it's within your reach so why not just dig in to deep, anyways, when you use following code:
$user = User::find('1');
$user->getAuthPassword();
This code returns the password of currently instantiated user and hence it's the user with id=1 and it could be any user you may want, depends on you, whom to retrieve from the database. The code inside the getAuthPassword function is:
public function getAuthPassword()
{
return $this->password;
}
So, it doesn't matter whether the user you retrieved from the database is logged in or not. If you retrieve the user with id=10 then you can get this user's password too.
On the other hand, the following code:
Auth::user()->password;
It returns the currently logged in user's password because Auth::user() returns the currently logged in user instance and this method will work only when the current user is logged in. So, yes, there is a difference in both cases.
i use tank auth as login handler in code igniter. when i use the forget password feature, i get a link send by mail http://xx.xx.xx/en//auth/reset_password/2/01b951fd2a02efa2d64f1fe70c2a4e3b. When i click this link it always says: "Your activation key is incorrect or expired. Please check your email again and follow the instructions."
i changed the segments so it gets the right segments but somehow it the goes wrong on if ($this->form_validation->run()). it Somehow wants the new_password and confirm_new_password as post data but from the link in the email no post data will ofcourse be sent.
Is this a bug in tank auth, is there a quickfix (does tank_auth forget a step, is something not configured right?)
reference code:
function reset_password()
{
$break =$this->uri->total_segments();
$new_pass_key= $this->uri->segment($break);
$user_id= $this->uri->segment($break-1);
$this->form_validation->set_rules('new_password', 'New Password', 'trim|required|xss_clean|min_length['.$this->config->item('password_min_length', 'tank_auth').']|max_length['.$this->config->item('password_max_length', 'tank_auth').']|alpha_dash');
$this->form_validation->set_rules('confirm_new_password', 'Confirm new Password', 'trim|required|xss_clean|matches[new_password]');
$data['errors'] = array();
if ($this->form_validation->run()) { //breaks here. For some reason wants to validate post data which
if (!is_null($data = $this->tank_auth->reset_password($user_id, $new_pass_key,$this->form_validation->set_value('new_password')))) { // success
$data['site_name'] = $this->config->item('website_name', 'tank_auth');
// Send email with new password
$this->_send_email('reset_password', $data['email'], $data);
$this->_show_message($this->lang->line('auth_message_new_password_activated').' '.anchor('/auth/login/', 'Login'));
} else { // fail
$this->_show_message($this->lang->line('auth_message_new_password_failed'));
}
} else {
// Try to activate user by password key (if not activated yet)
if ($this->config->item('email_activation', 'tank_auth')) {
$this->tank_auth->activate_user($user_id, $new_pass_key, FALSE);
}
if (!$this->tank_auth->can_reset_password($user_id, $new_pass_key)) {
$this->_show_message($this->lang->line('auth_message_new_password_failed'));
}
}
$this->load->view('auth/reset_password_form', $data);
}
Your new_pass_key and $user_id are wrong I guess.
It should work out of the box with this:
$user_id = $this->uri->segment(3);
$new_pass_key = $this->uri->segment(4);
EDIT:
$user_id = $this->uri->segment(4);
$new_pass_key = $this->uri->segment(5);
Why did you change that by the way?
I have an installation of Codeigniter, IonAuth + Hybridauth which I am reworking so my users can choose their own username instead of generating one using the first and last names returned by facebook.
So in my code below I check to see if a username was posted, if not I want to load the choose_username view but for some reason the view is not loading and its completely skipping that section which is why I added die('Why no view')
Update: This first piece of code runs fine in a new controller
Checkout the code here:
if(isset($_POST['username'])){
$username = $this->input->post('username', TRUE);
die($username);
}else{
$this->data['message'] = 'Please choose a username.';
$this->data['template'] = 'guests/partials/choose_username';
$this->load->view('guests/template/standard', $this->data);
die('Why no view?');
};
Longer version:
function login_provider($provider = '')
{
if(empty($provider)) redirect();
try
{
// create an instance for Hybridauth with the configuration file
$this->load->library('HybridAuthLib');
if ($this->hybridauthlib->serviceEnabled($provider))
{
// try to authenticate the selected $provider
$service = $this->hybridauthlib->authenticate($provider);
if ($service->isUserConnected())
{
// grab the user profile
$user_profile = $service->getUserProfile();
////////////
//var_dump($user_profile);
//die();
////////////
$provider_uid = $user_profile->identifier;
if($this->ion_auth->login_by_provider($provider,$provider_uid))
{
$data['user_profile'] = $this->ion_auth->user_by_provider();
//$this->load->view('auth/user_profile',$data);
$user = $this->ion_auth->user()->row();
//Redirect to custom subdomain
$url = explode('://',site_url());
if (strpos(site_url(),$user->username) !== false) {
redirect($url[0].'://'.str_replace('www','',$url[1]).'dashboard','refresh');
}
else{
redirect($url[0].'://'.$user->username.str_replace('www','',$url[1]).'dashboard');
};
}
else
{ // if authentication does not exist and email is not in use, then we create a new user
//Check if username was posted
if(isset($_POST['username'])){
$username = $this->input->post('username', TRUE);
die($username);
}else{
$this->data['message'] = 'Please choose a username.';
$this->data['template'] = 'guests/partials/choose_username';
$this->load->view('guests/template/standard', $this->data);
die('Why no view?');
};
So when I run the above code, all i get is a blank page with: Why no view.
As above, usually when I run into this sort of issue it's from a bug in the view code.
Also, I don't know what, is actually being passed by this post in the event of there not being username data but you might want to also be checking for an empty value for username. This is probably not the issue but it would be good to confirm that the initial if is evaluating the way you expect.
Can anyone see a problem with this line: $emailtoken = md5($user['User']['password'].CAKE_SESSION_STRING);
As it gives the following error: Use of undefined constant CAKE_SESSION_STRING - assumed 'CAKE_SESSION_STRING'
It still creates a token but gives that error and then when using the token it says that it is invalid :/
Here is the full function:
function admin_resetpassword ( $token = null )
{
// User submits their email address
if (!empty($this->data['User']['email']))
{
// user submitted initial form
$user = $this->User->findByEmail($this->data['User']['email']);
if (empty($user))
{
$this->Session->setFlash('Unknown email.');
return;
}
else
{
$emailtoken = md5($user['User']['password'].CAKE_SESSION_STRING);
// send email (temp flash to test code)
$this->Session->setFlash($emailtoken);
return;
}
}
// If the token is not empty on the url
if (!empty($token))
{
$user = $this->User->find(array("MD5(User.password + '".CAKE_SESSION_STRING."')"=>$token));
if (empty($user))
{
$this->Session->setFlash('Invalid token.');
return;
}
if (!empty($this->data['User']['password']))
{
$user['User']['password'] = $this->data['User']['password'];
$this->user->save($user);
$this->Session->setFlash('New password set.');
$this->redirect('/');
}
$this->set('token', $token);
$this->render('newpassword2');
}
}
the problem is CAKE_SESSION_STRING is not defined (as stated in the error).
If you want to get the salt or cipherSeed, use Configure::read('Security.salt'); or $this-Session->id; But you know this session id is lost after certain inactivity period, right? You won't be able to get that session id back later on (unless you save it somewhere).