I am trying to match current password before if change. For that I have taken Auth password and then I matched it with current password. But it's returning always false.
Here is the controller code that I have tried
if ($this->request->is(['patch', 'post', 'put'])) {
$obj = new DefaultPasswordHasher;
$postpassword = $obj->hash($this->request->data['current_password']);
if($this->Auth->user('password') == $postpassword)
{
// code to save change password.
}
else
$this->Flash->error(__('The password you have entered does not match !'));
}
Here $postpassword hash working fine, but $this->Auth->user('password') return value 1. How can I get auth password and match with $postpassword ?
Edit
I have get some knowledge then I have solve this problem like this way
$password = '$2y$10$pHbHu6xhNAw/v5HuQ1DSjOm5MPkqZukD1.532ACu7YLgD1ef9K7i2';
if ($this->request->is(['patch', 'post', 'put'])) {
$obj = new DefaultPasswordHasher;
$postpassword = $obj->check($this->request->data['current_password'], $password);
if($postpassword==1)
$this -> set('password',"hello");
}
Now I need just $this->Auth->user('password'); in controller.
Is it possible in cakephp auth component ?
Its pretty easy this way:
Insert this in your (Users)Table :
use Cake\Auth\DefaultPasswordHasher;
use Cake\Validation\Validator;
Extend your function validationDefault(Validator $validator ) like the following:
public function validationDefault(Validator $validator ) {
$validator
->add('current_password','custom',[
'rule'=> function($value, $context){
$user = $this->get($context['data']['id']);
if ($user) {
if ((new DefaultPasswordHasher)->check($value, $user->password)) {
return true;
}
}
return false;
},
'message'=>'The old password does not match the current password!',
])
->notEmpty('current_password');
return $validator;
}
And thats it! :)
The authentication adapter removes the password before returning the data for the identified user, however you should receive null, not 1.
Anyways, if you need that information, you'll have to read it manually, like for example
$Table->get($this->Auth->user('id'))->password
However, as already mentioned in the comments, this is stuff that is better being done using validation or application rules.
See for example DefaultPasswordHasher generating different hash for the same value
No need to hash password,Auth will perform all.
if ($this->request->is(['patch', 'post', 'put'])) {
$obj = new DefaultPasswordHasher;
if($this->Auth->user('password') == $this->request->data['current_password'])
{
// code to save change password.
}
else
$this->Flash->error(__('The password you have entered does not match !'));
}
Related
I have setup latest cake ph setup and when I try to login user it gives me this error
Argument 1 passed to Cake\Http\Session::_overwrite() must be of the type array, null given
My code is
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
} else {
$this->Flash->error(__('Username or password is incorrect'));
}
When I check $user it gives me the identified user but later it gives me above error.
Remove array type in Session.php in _overwrite function in vendor. as shown below code.
It's work for me.
/vendor/cakephp/cakephp/src/Http
protected function _overwrite( &$old, array $new): void{
if (!empty($old)) {
foreach ($old as $key => $var) {
if (!isset($new[$key])) {
unset($old[$key]);
}
}
}
foreach ($new as $key => $var) {
$old[$key] = $var;
}
}
My app is developed in CakePHP 3.x.
I use CakedDC Users plugin, and it works fine, except when the user wants to change his password, and click on Submit.
Let's say we have a Profile ID = 52606b3f-c72d-4485-9c76-3b0f8
The Edit page has a url like this:
localhost/my_app/profile/52606b3f-c72d-4485-9c76-3b0f8
The changePassword page has a url like this:
localhost/my_app/users/users/change-password/52606b3f-c72d-4485-9c76-3b0f8
When I click on Submit, it redirects to the profile page, but the ID is lost:
localhost/my_app/profile
and I get this error message:
Record not found in table "users" with primary key [NULL]
I think the reason is that the ID is not passed. And I don't find where and how to fix it.
Any help please ?.
When id is not passed, the id is taken from the logged in user. You can take a look at src/Controller/Traits/ProfileTrait.php. Could you debug $this->Auth->user('id')?
Also, you could customize the redirect url after changing the password. Configure::write('Users.Profile.route', [{url}]), see src/Controller/Traits/PasswordManagementTrait.php Ln44.
I don't remember my initial code, but, after months, I found the solution.
in src/Controller/Traits/ProfileTrait.php, set $redirect = Configure::read('Users.Profile.route');
public function changePassword()
{
$user = $this->getUsersTable()->newEntity();
$id = $this->Auth->user('id');
if (!empty($id)) {
$user->id = $this->Auth->user('id');
$validatePassword = true;
//#todo add to the documentation: list of routes used
$redirect = Configure::read('Users.Profile.route');
} else {
$user->id = $this->request->session()->read(Configure::read('Users.Key.Session.resetPasswordUserId'));
$validatePassword = false;
if (!$user->id) {
$this->Flash->error(__d('CakeDC/Users', 'User was not found'));
$this->redirect($this->Auth->config('loginAction'));
return;
}
//#todo add to the documentation: list of routes used
$redirect = $this->Auth->config('loginAction');
}
$this->set('validatePassword', $validatePassword);
if ($this->request->is('post')) {
try {
$validator = $this->getUsersTable()->validationPasswordConfirm(new Validator());
if (!empty($id)) {
$validator = $this->getUsersTable()->validationCurrentPassword($validator);
}
$user = $this->getUsersTable()->patchEntity($user, $this->request->data(), ['validate' => $validator]);
if ($user->errors()) {
$this->Flash->error(__d('CakeDC/Users', 'Password could not be changed'));
} else {
$user = $this->getUsersTable()->changePassword($user);
if ($user) {
$this->Flash->success(__d('CakeDC/Users', 'Password has been changed successfully'));
return $this->redirect($redirect);
} else {
$this->Flash->error(__d('CakeDC/Users', 'Password could not be changed'));
}
}
} catch (UserNotFoundException $exception) {
$this->Flash->error(__d('CakeDC/Users', 'User was not found'));
} catch (WrongPasswordException $wpe) {
$this->Flash->error(__d('CakeDC/Users', '{0}', $wpe->getMessage()));
} catch (Exception $exception) {
$this->Flash->error(__d('CakeDC/Users', 'Password could not be changed'));
}
}
$this->set(compact('user'));
$this->set('_serialize', ['user']);
}
I have just started using CakePHP and love using it! I have created a login system and registration system, however am really struggling with the "forgotten password" section.
I want to use a tokenhash and expiry date in the Users DB so that it cant be abused, users would need to enter username and email to get an activation link emailed to them with a newly generated tokenhash
There are quite a few tutorials out there but I find most of them work for the first part e.g. emailing the activation link/ resetting token and timer but all seem to fail on the change of the password.
Please help me, either with a working tutorial from the net or a solution that applies the above required things.
Thanks in advance
Steve
Below I am writing the code that I wrote for one of my project, this might help you out.
1- I created a new table which contains the unique token for every user.
Table Name:- user_password_resets
Columns : userclient_id, token
2- A email template name as:- change_password.html inside /webroot/template/change_password.html
public function login_send() {
$this->isLoggedIn(); //Check if the user is logged in
if($this->request->is('post')) { #if the form is submitted
$login = $this->data['User']['login'];
$conditions = array('User.login'=>$login);
if($this->User->hasAny($conditions)) {
$users = $this->User->find('first', array('conditions'=>$conditions));
#Generate the token
$token = md5(uniqid(rand(),true));
#Save token and other details in user_password_reset_links table
$users = $this->User->find('first', array('conditions'=>array('User.login'=>$login)));
$my_name = $users['User']['first_name'];
$reset_links = array();
$reset_links['UserPasswordReset']['userclient_id'] = $users['User']['client_id'];
$reset_links['UserPasswordReset']['token'] = $token;
$conditions = array('UserPasswordReset.userclient_id'=>$users['User']['client_id']);
if($this->UserPasswordReset->hasAny($conditions)) {
$user_id = $users['User']['client_id'];
$this->UserPasswordReset->updateAll(array('UserPasswordReset.token'=>"'$token'"), array("UserPasswordReset.userclient_id"=>"$user_id"));
} else {
$this->UserPasswordReset->create();
$this->UserPasswordReset->save($reset_links);
}
$password_reset_link = BASE_URL."users/reset_password/$token";
#Send Welcome Email
$mailContent = file_get_contents(BASE_URL . "templates/change_password.html");
$rootlink = BASE_URL;
$arrMail = array(
"{NICK}" => ucfirst($my_name),
"{rootlink}" => BASE_URL,
"{SITE_TITLE}" => SITE_TITLE,
"{PASSWORD_RESET_LINK}"=>$password_reset_link
);
$mails = explode(',', $users['User']['email']);
$msg = #str_replace(array_keys($arrMail), array_values($arrMail), $mailContent);
$data = array();
$data['to'] = #$mails[0];
$data['body'] = $msg;
$data['subject'] = SITE_TITLE.'- Reset Password.';
$this->send_mail($data);
$this->Session->setFlash('A password reset link has been sent to the email address.', 'default', array('class'=>'successMsg'));
$this->redirect(array('controller'=>'users', 'action'=>'login'));
exit;
} else {
$this->Session->setFlash('The Username entered is not registered with Captain Marketing.', 'default', array('class'=>'errorMsg'));
$this->redirect(array('controller'=>'users', 'action'=>'login_send'));
exit;
}
}
$this->set('title_for_layout', '-Send password reset link');
}
My question is how can I refresh my view "search.ctp" to take into account the record I just deleted. The problem is the following.
My controller code
public function search() {
if ($this->request->is('post')) {
$this->set("isPost", TRUE);
$query = $this->data;
$output = $this->Question->find("all", array("conditions"=>array("Question.lectureId"=>$query["Lecture"]["Lecture"],
"Question.type"=>$query["Lecture"]["status"])));
$this->set("questions", $output);
} else {
$this->LoadModel("Lecture");
$outputL = array();
$for = $this->Lecture->find("all", array("fields" => array("_id", "title")));
foreach ($for as $key => $value) {
$outputL[$value["Lecture"]["_id"]] = $value["Lecture"]["title"];
}
$this->set("lectures",$outputL);
//
$statuses = array(
"" => "Select a question type",
"anonymousQuestion" => "anonymousQuestion",
"handUp" => "handUp",
"userQuestion" => "userQuestion"
);
$this->set("statuses", $statuses);
}
}
So the following happens;
I open the view "search.ctp" ("my admin interface"), set the 2 search params,
and use the submit button to post that data. Then my IF statement recognizes that as POSt and gives me back my query results. The problem is when i delete a record...
It redirects me back to my search action to enter the query params again... How do i just refresh the page with the same query params and NOT leave my view.
o forgot my delete function code:
public function delete($id = null) {
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$this->Question->id = $id;
if (!$this->Question->exists()) {
throw new NotFoundException(__('Invalid configuration'));
}
if ($this->Question->delete()) {
$this->Session->setFlash(__('Question deleted'));
return $this->redirect(array("action"=>"search"));
}
$this->Session->setFlash(__('Question was not deleted'));
$this->redirect(array('action' => 'search'));
}
As a workaround i made another function that does the same thing with GET request that my search function does with a POST request. Basically returns the data with the query params. And i used the Session helper to carry the query over to my other function. Dont know how smart that was, but it does the trick for me...
Still would be nice to know if someone has a solution where i dont have to make another function/view
When isAuthorized = false the user is redirected to '/' is there a way to change this. I want to redirect to the user dashboard (/users/dashboard) with a flash message saying 'Access prohibited' or something like that.
Cheers!
public function isAuthorized($user) {
if (isset($user['role']) && $user['role'] === 'admin') {
return true; //Admin can access every action
}
return false; // The rest don't
}
If your isAuthorised variable is being evaluated in your controller.
You can call the redirect function.
$this->redirect(array('controller' => 'users', 'action' => 'dashboard'));
If you are actually inside the users controller allready, just call
$this->redirect(array('action' => 'dashboard'));
If not, where are you checking the isAuthorised value?
This is not an ideal solution. However it seems that there is no way to do this with the current built in AuthComponent
Edit: Added code as an example.
public function isAuthorized($user) {
if (parent::isAuthorized($user)) {
return true;
}
// Authorised actions
if (in_array($this->action, array('dashboard'))) {
return true;
}
// Will break out on this call
$this->redirect(array('controller' => 'users', 'action' => 'dashboard'));
return false;
}
I think the best way is to use exception and extends like that :
AppController.php
public function isAuthorized($user) {
throw new ForbiddenException(__('You are not authorized to access.'));
}
AnotherController.php
public function isAuthorized($user) {
if (isset($user['role']) && $user['role'] === 'admin') {
return true;
}
return parent::isAuthorized($user);
}
With this code you are able to manage the roles and the error.
If they are being logged out you can send them where you want with:
$this->Auth->logoutRedirect
I would personally use:
$this->Auth->authError = "You are not authorized to access.";
In order to redirect them to root with a flash message notifying them of the error.
Is a wrong behavior of AuthComponent.
In a nutshell: if the url is visited by a link, the framework is able to reconstruct the path and then redirect to the referring page. Otherwise (by direct entry into a url bar) it fails and it redirects to the home page.
The "bug" is documented and it will be corrected in a future release.
See: http://cakephp.lighthouseapp.com/projects/42648/tickets/591-inconsistent-redirect-behaviour-by-auth-acl
I do a little better than #deep55.
isAuthorized() method can throw an exception no problem, but i think that the inheritance of the Controllers would permit us to improve authorization algorithm using first AppController.isAuthorized(), not last.
So, here is my solution, assuming that I use a user model called Utilisateur, and a role model called Role.
AppController :
/**
* Parent method
*/
public function isAuthorized($user){
App::uses('Utilisateur','Model');
$User = new Utilisateur();
$isAdmin = $User->hasRole(10,$user['id']);
if ($isAdmin) {
return true;
}
}
/**
* Reject unauthorized actions
*/
public function rejectRequest(){
$errorMessage = __("Sorry, you can't do this.");
if ($this->isRest()) {
throw new ForbiddenException($errorMessage);
} else {
$this->Auth->authError = $errorMessage;
$this->Auth->flash['params']['class'] = 'alert-danger';
}
return false ;
}
Utilisateur model :
/**
* hasRole method return true if the user belongs to the correct role group
*/
public function hasRole($role_id, $user_id){
if (!isset($user_id)) {
if (!empty($this->id)) {
$user_id = $this->id ;
} else throw new Exception("Error, parameter $user_id is missing", 1);
}
$user = $this->find('first',array(
'conditions' => array('Utilisateur.id' => $user_id),
'fields' => array('id'),
'contain' => array('Role.id')
));
$roles = $user['Role'];
foreach ($roles as $r) {
if ($role_id == $r['id']) {
return true;
}
}
}
And last, in a specific controller :
/**
* Child method
*/
public function isAuthorized($user){
if (parent::isAuthorized($user)) {
return true;
}
if ( false ) {
return true ;
}
if ( false ) {
return true ;
}
return $this->rejectRequest() ;
}
For Cake version 2, as written in documentation for AuthComponent:
AuthComponent::$unauthorizedRedirect
Controls handling of unauthorized access. By default unauthorized user is redirected to the referrer URL or AuthComponent::$loginRedirect or ‘/’. If set to false a ForbiddenException exception is thrown instead of redirecting.
you can configure AuthComponent to redirect you to custom page in one place using unauthorizedRedirect property.
Just set it in the place where you configure Auth as a component
'Auth' => array(
... other settings...,
'unauthorizedRedirect' => '/users/dashboard'
)
After being redirected, you can print error message defined by authError property
echo $this->Session->flash();
echo $this->Session->flash('auth');
but it will be the same message for any authentication or authorization error.