Clicking forgot password for the first time will not display pass_key in the email view but stored in pass_key field
clicking forgot password for the second time and so on will get the value of pass_key from previous pass_key insertion
UsersController.php
<?php
public function forgotPassword()
{
if ($this->request->is('post')) {
$query = $this->Users->findByEmail($this->request->data['email']);
$user = $query->first();
if (is_null($user)) {
$this->Flash->error('Email address does not exist. Please try again');
} else {
$passkey =Text::uuid(); // create key
$timeout = time() + DAY;
$url = Router::url(['controller' => 'users', 'action' => 'reset'], true) . '/' . $passkey; // Im just generate this link in the view and pass
$status = $this->Users->updateAll(array('Users.pass_key' => $passkey,'timeout' => $timeout), array('Users.id' => $user->id));
if($status){
$this->getMailer('User')->send('forgotPasswordEmail', [$user]);
$this->Flash->success(__('We already sent a link to your email'));
return $this->redirect(['action' => 'login']);
} else {
$this->Flash->error('Error saving reset passkey/timeout');
}
}
}
}
?>
UserMailer.php
<?php
namespace App\Mailer;
use Cake\Mailer\Mailer;
class UserMailer extends Mailer
{
public function forgotPasswordEmail($user)
{
// attach a text file
$this->attachments([
'text for user.txt'=> [
'file'=> 'files/instruction.txt',
'mimetype'=>'plain/text',
'contentId'=>'3734hf38'
],
// attach an image file
'edit.png'=>[
'file'=>'files/ourlogo.png',
'mimetype'=>'image/png',
'contentId'=>'734h3r38'
]
])
->to($user->email)
->emailFormat('html')
->subject(sprintf('Forgot Password link %s', $user->username))
->viewVars([
'username'=> $user->username,
'useremail'=>$user->email,
'passkey' => $user->pass_key,
'userid' => $user->id
])
// the template file you will use in this emial
->template('forgotPasswordEmail') // By default template with same name as method name is used.
// the layout .ctp file you will use in this email
->layout('customLayout');
}
}
?>
Template\Email\html\forgot_password_email.ctp
<?php
use Cake\Routing\Router;
?>
<p>Your username is: <?=$username?> </p>
<p>Click on the link below to Reset Your Password.</p>
<?php $resetUrl = Router::url(['controller' => 'users', 'action' => 'reset/' . $passkey]);?>
<p>Click here to Reset Your Password</p>
?>
<?php
$url = Router::url(['controller' => 'users', 'action' => 'reset'], true) . '/' . $passkey; // Im just generate this link in the view and pass
$status = $this->Users->updateAll(array('Users.pass_key' => $passkey,'timeout' => $timeout), array('Users.id' => $user->id));
if($status){
var_dump($url);exit; I get the exact pass_key but I dont know how to pass the url to forgot_password_email.ctp . I just know how to pass the using this [$user]
var_dump($user->pass_key);exit; will retrieve the previous transaction not current pass_key
.............
?>
Your problem is that you are generating the pass key and saving it to the database, but not updating the $user object, so when you pass that to the view it still has the old version.
Try this:
$this->Users->patchEntity($user, [
'pass_key' => Text::uuid(), // create key
'timeout' => time() + DAY,
]);
if ($this->Users->save($user)) {
Here, we update the $user entity with the new values, so that they are there for the view, and use the save function instead of updateAll.
Also, in your view, you should change to the following:
$resetUrl = Router::url(['controller' => 'users', 'action' => 'reset', $passkey]);
Related
I'm trying to implement a forgot password function in CakePHP 3.x.
I have created a form that accepts a user's email:
<?= $this->Form->create()?>
<div class="form-group">
<?= $this->Form->input('email', array('class' => 'form-group','autocomplete' => 'off' ,'required' => 'required'))?>
</div>
<div class="form-group">
<?= $this->Form->button('Reset Password', array('class' => 'form-group primary'))?>
</div>
<?= $this->Form->end()?>
In my controller I'm trying to find the user by the email, if the email exist then a random password will be generated and the password will be updated for that email id:
use Cake\ORM\TableRegistry;
use Cake\Auth\DefaultPasswordHasher;
public function forgotPassword($email = null){
if($this->request->is('post')) {
$email = $this->request->data['email'];
$emails = TableRegistry::get('Users');
$user = $emails->find()->where(['email' => $email ])->first();
if (!$user) {
$this->Flash->error(__('No user with that email found.'));
return $this->redirect(['controller' => 'Users','action' => 'forgotPassword']);
}else{
$random = 'a';
$hasher = new DefaultPasswordHasher();
$val = $hasher->hash($random);
$data = $this->Users->password = $val;
if ($this->Users->save($data)) {
$this->Flash->success(__('Password changed Succesfully.'));
return $this->redirect(['controller' => 'Users','action' => 'forgotPassword']);
}
}
}
}
You haven't actually stated a specific problem/question, but I think I might know what could help.
The whole DefaultPasswordHasher bit should be in the UsersEntity file, like in the tutorial: Blog tutorial
With the hashing properly placed in the entity like in the example it will automatically be called as soon as you use either PatchEntity or NewEntity (I think, confirmation please?).
Secondly, the $this->[model]->save() function works on entities, not just on data. So you would find the user's entity, patch the entity and then save it:
...} else {
$newpass = 'randomstring';
$user = $this->Users->PatchEntity($user, ['password' => $newpass]);
if ($this->Users->save($user)) ...
I'm new to the Yii framework, and the situation confused me. I have a main form in a view that it has some validations. In addition, I have a gridview inside a modal in this view that it is hidden until the end user click a button in order to choose a field. The problem appeared just when the boss told me to have some search field in the modal (I decided to use filter in gridview for this purpose). After traces I've found that the reason was form validation of the first form when the filter method of gridview is trying to send GET request to the server to receive grid rows that related just with the filter parameters, But it's not working maybe the failure is about my understanding of MVC pattern.
Anyway this is some part of my codes:
View:
/* this section placed inside a modal div beside the main form*/
echo GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'filterUrl' => Yii::$app->urlManager->createUrl('catm/grid'),
'rowOptions' => function ($model, $key, $index, $grid){
return ['onclick' => 'setText(\''. $model['name'] . '\');$(\'#myModal\').modal(\'hide\')',
'style' => 'cursor: pointer',
];
}
]);
Controller:
public function actionCreate()
{
$modelCatm = new Catm();
$modelApplication = new \app\models\Application();
$modelBranch = new \app\models\Branch();
$modelConfig = new \app\models\Config();
$modelMedia = new \app\models\Media();
$modelMessage = new \app\models\Message();
$modelServiceConfig = new \app\models\ServiceConfig();
$searchMdl = new BranchSearch();
$dataPrv = $searchMdl->search(Yii::$app->request->queryParams);
$webroot = Yii::$app->basePath;
if ($modelCatm->load(Yii::$app->request->post())){
//}
//print_r($_GET);
$transaction=Yii::$app->db->beginTransaction();
try {
//Branch:
$modelBranch->attributes = $_POST['Branch'];
//echo $modelBranch->attributes['name'];
if ($modelBranch->findBySql('select * from branch where name =\'' . $modelBranch->attributes['name'] . '\'')->count()==0) {
//echo 'not exists!';
if(!$modelBranch->save(false)) {echo 'error in model branch'; return 0;};
}
//Cashless ATM:
$modelCatm->attributes = $_POST['Catm'];
if(!$modelCatm->save(false)) {echo 'error in model catm'; return 0;};
//Application:
/* file upload handling and saving the path to database*/
$modelApplication->attributes = $_POST['Application'];
$modelApplication->catm_id = $modelCatm->id;
$modelApplication->file = UploadedFile::getInstanceByName('Application[file]');
//echo $modelApplication->file->baseName;
$modelApplication->validate();
$modelApplication->file->saveAs('uploads/application/' . $modelApplication->file->baseName . '.' . $modelApplication->file->extension);
$modelApplication->filepath = $webroot . '/uploads/application/' . $modelApplication->file->name;
if(!$modelApplication->save(false)) {echo 'error in model application'; return 0;};
//Media:
/* file upload handling and saving the path to database*/
$modelMedia->attributes = $_POST['Media'];
$modelMedia->catm_id = $modelCatm->id;
$modelMedia->file = UploadedFile::getInstanceByName('Media[file]');
//echo $modelMedia->file->baseName;
$modelMedia->validate();
$modelMedia->file->saveAs('uploads/media/' . $modelMedia->file->baseName . '.' . $modelMedia->file->extension);
$modelMedia->filepath = $webroot . '/uploads/media/' . $modelMedia->file->name;
if(!$modelMedia->save(false)) {echo 'error in model media'; return 0;};
//Messages:
$modelMessage->attributes = $_POST['Message'];
$modelMessage->catm_id = $modelCatm->id;
if(!$modelMessage->save()) {echo 'error in model message'; return 0;};
$transaction->commit();
} catch (Exception $ex) {
$transaction->rollback();
}
return $this->render('view',[
'modelCatm' => $modelCatm,
'modelBranch' => $modelBranch,
'modelApplication' => $modelApplication,
'modelConfig' => $modelConfig,
'modelMedia' => $modelMedia,
'modelMessage' => $modelMessage,
'modelServiceConfig' => $modelServiceConfig,
]);
} else {
return $this->render('create', [
'modelCatm' => $modelCatm,
'modelBranch' => $modelBranch,
'modelApplication' => $modelApplication,
'modelConfig' => $modelConfig,
'modelMedia' => $modelMedia,
'modelMessage' => $modelMessage,
'modelServiceConfig' => $modelServiceConfig,
'searchModel' => $searchMdl,
'dataProvider' => $dataPrv,
]);
}
}
I will update the question with more information of my code after your helpful comments if it is required.
It has nothing to do with your understanding of MVC mate, Yii does a lot of things for you automatically but sometimes in cases like this you just need to get to do some things by hand.
Probably you need to change the form action in your controller to check if this is a form submission or a gridview navigation / filter and handle it differently. You need to check if some get params exist and bypass the form validation in that case.
I'm currently working on a project using the Phalcon Framework that has pages with complex forms and a lot of inputs, to break it down nicely I'm dividing the forms into a step-by-step process.
How would one validate the form on each step before going to the next step and then save the whole form on the final step?
I can't seem to find anything documented about this sort of process as it likes to validate the form in it's entirety if I use the form builder.
Simple, just create a custom methods in your form class to validate any step, and the posted data from some step save into message class and store it into session by "stepX", when posted data is not valid just set defaults from post. When valid save it into session as i describe above.
For example how i mean "controller"
<?php
class MyController extends BaseController {
public function processStep1Action(){
$form = new MyForm();
if($this->request->isPost()){//im using my custom request class
if(!$form->isValid($this->request->getPost()){
//error messages goes here
$form->setDefaultsFromRequest($this->request); // it will set the filled data
}
else {
$messageClass = new MyMessageContainer();
$messageClass->setData($this->request);//inside parse requested data into message class, or parse it as $messageClass->name = $this->request->getPost('name');
$this->session->save('step1',$messageClass); //maybe it would be want to serialize it
//then redirect to the step 2 or x
}
}
}
}
So in the next step you can access data from sessions $this->session->get('step1'); so you can in final step load all posted data and store it into DB.
I hope this helps! :)
here is my form maybe it can be helpful for you.
<?php
namespace Manager\Library\Forms\User;
use Phalcon\Forms\Form,
Phalcon\Forms\Element\Email,
Phalcon\Forms\Element\Select,
Phalcon\Forms\Element\Password,
Phalcon\Forms\Element\Check,
Phalcon\Validation\Validator\Confirmation,
Phalcon\Validation\Validator\StringLength,
Phalcon\Forms\Element\Submit,
Phalcon\Validation\Validator\PresenceOf,
Model\Group;
class AddUser extends Form {
public function initialize()
{
$email = new Email('email');
$email->addValidators(array(
new \Phalcon\Validation\Validator\Email(array(
'message' => 'Nezadali jste email nebo má nesprávny tvar(email#domena.tld).'
))
));
$this->add($email);
$this->initGroupElement();
$password = new Password('password');
$password
->addValidator(new StringLength(array('min' => 6,'messageMinimum' => 'Nezadali jste heslo nebo je příliš krátke, minimální počet znaků je 6.')))
->addValidator(new Confirmation(array('with' => 'password-again',"message" => "Zadané hesla se neshodují.")));
$this->add($password);
$repeatPassword = new Password('password-again');
$this->add($repeatPassword);
$this->initializeProfileElements();
$active = new Check('active',array('value' => 1));
$this->add($active);
$this->add( new Submit('save') );
\Phalcon\Tag::setDefault('password', '');
\Phalcon\Tag::setDefault('password-again', '');
}
public function initializeEdit(){
$email = new Email('email');
$email->addValidators(array(
new \Phalcon\Validation\Validator\Email(array(
'message' => 'Nezadali jste email nebo má nesprávny tvar(email#domena.tld).'
))
));
$this->add($email);
$this->initGroupElement();
$password = new Password('password');
$this->add($password);
$repeatPassword = new Password('password-again');
$this->add($repeatPassword);
$this->initializeProfileElements();
$active = new Check('active',array('value' => 1));
$this->add($active);
$this->add( new Submit('save') );
\Phalcon\Tag::setDefault('password', '');
\Phalcon\Tag::setDefault('password-again', '');
}
protected function initGroupElement(){
$auth = \Core\Auth::getIdentity();
$groups = new Group();
// $groups->addColumns(array('id','name'));
//set global condition about Super Admin
$groups->addFilter('id', 1,'<>');
if($auth){
//set restrictions for main groups
if((int)$auth->group_id === 1){ //super admingroup
//no filter
}
else if((int)$auth->group_id === 2){ //admin group
$groups->addFilter('id', 1,'>');
}
else if((int)$auth->group_id === 6){//Provozovatel group
$groups->addFilter('id',array(3,6,7));
$groups->addFilter('public', 1,'=',true);
}
else { // other groups
$groups->addFilter('public', 1);
}
}
$groups = $groups->findFiltered();
$groupElement = new Select('group');
foreach($groups as $group){
$groupElement->addOption(array($group->id => $group->name));
}
$this->add($groupElement);
}
protected function initializeProfileElements(){
$forename = new \Phalcon\Forms\Element\Text('forename');
$this->add($forename);
$surname = new \Phalcon\Forms\Element\Text('surname');
$this->add($surname);
$street = new \Phalcon\Forms\Element\Text('street');
$this->add($street);
$postal = new \Phalcon\Forms\Element\Text('postal');
$this->add($postal);
$city = new \Phalcon\Forms\Element\Text('city');
$this->add($city);
$ic = new \Phalcon\Forms\Element\Text('ic');
$this->add($ic);
$dic = new \Phalcon\Forms\Element\Text('dic');
$this->add($dic);
}
public function setDefault($fieldName,$value){
\Phalcon\Tag::setDefault($fieldName, $value);
}
public function setDefaults($object){
if($object instanceof \Model\User){
$this->setDefaultsFromObject($object);
}
else if($object instanceof \Phalcon\Http\Request){
$this->setDefaultsFromRequest($object);
}
}
protected function setDefaultsFromObject(\Model\User $user){
$profile = $user->getRelated('\Model\Profile');
\Phalcon\Tag::setDefaults(array(
'email' => $user->email,
'group' => $user->group_id,
'active' => $user->active,
'forename' => $profile->forename,
'surname' => $profile->surname,
'street' => $profile->street,
'city' => $profile->city,
'postal' => $profile->postal,
'ic' => $profile->IC,
'dic' => $profile->DIC
));
}
protected function setDefaultsFromRequest(\Phalcon\Http\Request $request){
\Phalcon\Tag::setDefaults(array(
'email' => $request->getPost('email'),
'group' => $request->getPost('group'),
'active' => $request->getPost('active')
));
\Phalcon\Tag::setDefaults(array(
'forename' => $request->getPost('forename'),
'surname' => $request->getPost('surname'),
'street' => $request->getPost('street'),
'city' => $request->getPost('city'),
'postal' => $request->getPost('postal'),
'ic' => $request->getPost('ic'),
'dic' => $request->getPost('dic')
));
}
}
In addition to Kamil's answer, another option to consider is to use Javascript on the front-end to handle your multi-step form. This will add some complexity as you will need to have the javascript to handle the form steps and do preliminary validation, but it only requires a single submit where you can validate content within a single method.
After reading the post: logging without password
I made a personal attempt:
AppController:
function beforeFilter(){
$this->Auth->loginError = "This message shows up when the wrong credentials are
used";
//$this->Auth->authError = "This error shows up with the user tries to access a part
of the website that is protected.";
//$this->Auth->authError = "";
$this->Auth->fields = array(
'username' => 'username',
'password' => null
);
UsersController, inside add():
// Save new user
if ($this->User->save(array('username' => $this->request->data['User']['username'],
'password' => $this->Auth->password(null),
'name' => $this->request->data['User']['name'],
'surname' => $this->request->data['User']['surname'],
'chosenLayout' => $this->request->data['User']['chosenLayout'],
'dateCreated' => $this->request->data['User']['dateCreated'],
'dateModified' => $this->request->data['User']['dateModified'],
'role_id' =>$this->request->data['User']['role_id']
))) {
$this->Session->setFlash(__('message_success_user_added',
array($this->request->data['User']['username'])), 'default', array(), 'success');
$this->redirect(array('action' => 'index'));
}
else {
// Validation error
$this->Session->setFlash(__('message_fail_validation'), 'default', array(), 'fail');
}
Then entered as admin and created some dummy users with null or random password.
Checking the database encrypted passwords were all the same ( a hashed null string) which means the modification in add() function worked...
Inside UsersController login():
// Login User
public function login() {
// Check if the user is already logged in
if ($this->Session->check('Auth.User.id')){
// Redirect to login page
$this->redirect($this->Auth->loginRedirect);
}
else{
// If the user is not logged in
session_set_cookie_params(0);
// If the request is a POST request
if ($this->request->is('post')) {
//get credentials
$this->username = $this->request->data['User']['username'];
$this->password = $this->request->data['User']['password'];
$this->domain = $this->request->data['User']['domain'];
//debug($this->username);
debug($this->domain) ;
//Check if username exists in local DB
//debug($this->User->findByUsername( $this->username ));
if ($this->Auth->login(
array(
'username'=> $this->username,
'password'=> null)
)){
// debug($this->Auth->login(array(
// 'username'=> $this->username,
// 'password'=> null
// )));
// Successful login
// Get all the user information and store in Session
//debug($this->Auth);
$this->User->id = $this->Auth->user('id');
debug($this->User->id);
debug($this->User);
$this->User->contain(array('User', 'Role' => array('Ui', 'Action.name')));
$this->Session->write('User', $this->User->read());
$actions = array();
foreach ($this->Session->read('User.Role.Action') as $key => $value){
array_push($actions, $value['name']);
}
$this->Session->write('User.Role.Action', $actions);
debug($actions);
// Render different layout depending on user type
if($this->Session->read('User.Role.Ui.name') == Configure::read('usertype.msp')){
$this->Session->write('SessionValues.ui', Configure::read('usertype.msp'));
$this->Auth->loginRedirect = array('controller' => 'PortStats', 'action' =>
'index');
}
else if($this->Session->read('User.Role.Ui.name') ==
Configure::read('usertype.tsc')){
$this->Session->write('SessionValues.ui', Configure::read('usertype.tsc'));
$this->Auth->loginRedirect = array('controller' => 'PortStats', 'action' =>
'index');
}
else if($this->Session->read('User.Role.Ui.name') ==
Configure::read('usertype.superAdminUserType')){
$this->Auth->loginRedirect = array('controller' => 'Uis', 'action' => 'index');
}
// Redirect to main login page
$this->redirect($this->Auth->loginRedirect);
}
else {
// Failed login user
session_destroy();
$this->Session->setFlash(__('Login failed:
access not granted'), 'default', array(), 'fail');
}
}
}
}
Then I try to login with my new users.I get the failed login message.
Which means $this->Auth->login returns false.
It must be as easy as that but something is going wrong.
In the meantime my debug trace:
Warning (2): Invalid argument supplied for foreach()
[APP\Controller\UsersController.php, line 85]
Simplify it. It is not a login, but a registration process, so don't confuse those two totally different things.
You just
create the user including proper validation
on success use Auth->login($user['User']) to set the auth session manually
only then redirect manually to where you want the user to go after a registration here
For a live example see https://github.com/dereuromark/cakefest/blob/master/Controller/AccountController.php#L166
I am working on a project based on Zend framework. In user registration I have to check for unique email. My code is working fine when I register a user for the first time, but when I try to update the user information and press the update button, it gives the error message:
Email already taken
Please help me to solve this problem. My code is attached below:
$this->addElement('text', 'email', array(
'label' => 'Your email address:',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
'EmailAddress',
array('Db_NoRecordExists', true, array(
'table' => 'users',
'field' => 'email',
'messages' => array(
'recordFound' => 'Email already taken'
)
)
)
)
));
I have changed my controller to this:
public function addAction()
{
$modelUsers = new Model_Users();
$userId = $this->_getParam('userId');
$form = $this->_getAddForm();
if ($userId) {
$populateData = array();
$user = $modelUsers->fetch($userId);
if ($user instanceof Model_User) {
$populateData = $user->toArray();
}
$form->populate($populateData);
}
$request = $this->getRequest();
if ($request->isPost()) {
$email = $this->getRequest()->getParam('email');
if (strtolower(trim($email)) == $modelUsers->fetchByEmail($email)) {
// change $this->_user->getAccount()->getEmail() to retrieve the user's current email address
// remove validator from form
$form->getElement('email')->removeValidator('Db_NoRecordExists');
}
$post = $request->getPost();
if ($form->isValid($post)) {
$values = $form->getValidValues($post);
$data = array(
'firstName' => $values['firstName'],
'userTypeId' => 2,
'lastName' => $values['lastName'],
'email' => $values['email'],
'userName' => $values['userName'],
'password' => $values['password'],
'role' => $values['role']
);
if ($userId) {
$user = $modelUsers->fetch($userId);
if ($user instanceof Model_User) {
$user->setFromArray($data);
$success = $user->save();
if ($success) {
echo Zend_Json::encode(array('status' => self::STATUS_SUCCESS, 'message' => 'Successfully updated the user!'));
exit;
}
}
} else {
$user = $modelUsers->createRow($data);
$success = $user->save();
if ($success) {
echo Zend_Json::encode(array('status' => self::STATUS_SUCCESS, 'message' => 'Successfully added the user!'));
exit;
}
}
echo Zend_Json::encode(array('status' => self::STATUS_FAILED, 'message' => 'user not added'));
exit;
} else {
$errors = array();
$errors = $form->errors();
echo Zend_Json::encode(array('status' => self::STATUS_ERROR, 'data' => $errors));
exit;
}
}
$this->view->form = $form;
$this->_helper->layout->disableLayout();
}
Model:
public function fetchByEmail($email)
{
$email=fetchOne('SELECT email FROM users WHERE email = $email');
//$select->where('email=?',$email) ;
//$student = $this->fetchRow($select);
return $email;
}
But still this is not working
One simple way you can solve this problem is to remove the validator from that form element when the form is being edited. You may also want to keep the validator if they are attempting to change their email address since they shouldn't be able to change their email to one that already exists in the database.
Leave the validator in your Zend_Form class, add this code only when a user is being edited.
if ($this->getRequest->isPost()) {
$email = $this->getRequest()->getParam('email'); // get email from form
// if email address has not changed, remove validator from form
if (strtolower(trim($email)) == $this->_user->getAccount()->getEmail()) {
// change $this->_user->getAccount()->getEmail() to retrieve the user's current email address
// remove validator from form
$form->getElement('email')->removeValidator('Db_NoRecordExists');
}
// validate form
if ($form->isValid($this->getRequest()->getPost()) {
//...
}
}
So what you are doing is removing the Db_NoRecordExists validator from the form element when the form is being edited, and only if they are not attempting to change their email address if they are allowed to do so.
It will give you allways this error because you are using validation , that email is present in DB table or not.
But the email is existing in database that's why it is giving error.
Remove this validation , it will help you.
if(isset($_SESSION['session_id']))
{
// code for update query
}
else
{
// code for insert query
}