I've made a few apps with Yii2, but having some trouble with the login functionality here. The passwords don't seem to match.
_form.php
<?= $form->field($model, 'first_name')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'last_name')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'email')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'pass')->passwordInput(['maxlength' => true]) ?>
User.php
public function beforeSave($insert)
{
if($this->isNewRecord)
{
$this->pass = Yii::$app->getSecurity()->generatePasswordHash($this->pass);
}
return parent::beforeSave($insert);
}
LoginForm.php
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
echo 'current: ' . $user->pass;
echo '<br />';
echo 'plain: ' . Yii::$app->getSecurity()->generatePasswordHash('Password');
echo '<br />';
echo 'function: ' . $user->validatePassword($this->pass);
if (!$user || !$user->validatePassword($this->pass)) {
$this->addError($attribute, 'Incorrect user or password.');
}
}
}
My email and password fail here. I echoed out some things to check. The user value from the DB is always the same, the plain version where I pass "Password" always returns a different hash and the third print_r() returns nothing. I'm not sure what I'm doing wrong here?
Check this code:
$password = 'Password';
$passwordHash = Yii::$app->getSecurity()->generatePasswordHash($password);
Yii::$app->getSecurity()->validatePassword($password, $passwordHash);
You should:
Correct write result of generatePasswordHash to database
use that hash on validatePassword method
Write correct user->validatePassword method like this:
public function validatePassword($password)
{
return Yii::$app->security->validatePassword($password, $this->password_hash);
}
the generatePasswordHash function will always create different value if you do print_r().
but the validatePassword function always return true if your password string is correct
so never mind about it.
maybe your column length in database is to short to save the hash
Related
I'm having trouble displaying the validation errors of a form using a custom validator.
The errors does exist as the debug method shows, it just won't be displayed in the form.
I'd like to be able to show the error message under (or above, or anywhere) the field.
What I've tried
Well, the documentation does state:
When using View\Helper\FormHelper::control(), errors are rendered by
default, so you don’t need to use isFieldError() or call error()
manually.
Nevertheless, I added the following in the form (just below the email control), which didn't do anything more. No message displayed.
if ($this->Form->isFieldError('email')) {
echo $this->Form->error('email', 'Yes, it fails!');
}
I've also found several questions and answers about this issue on SO, but they look outdated (from '09 to '13) and do not seem to correspond to today's CakePHP syntax.
What I've done
Users/forgot_password.ctp
<?= $this->Form->create() ?>
<?= $this->Form->control('email', ['type' => 'email']) ?>
<?= $this->Form->button(__('Reset my password')) ?>
<?= $this->Form->end() ?>
UsersController.php
(notice the specific validation set, as explained in documentation)
public function forgotPassword()
{
if ($this->request->is('post')) {
$user = $this->Users->newEntity($this->request->getData(), ['validate' => 'email']);
if ($user->errors()) {
debug($user->errors()); // <- shows the validation error
$this->Flash->error(__('An error occurred.'));
} else {
// ... procedure to reset password (which works fine!) and redirect to login...
return $this->redirect(['action' => 'login']);
}
}
}
UsersTable.php
public function validationEmail(Validator $validator)
{
$validator
->email('email')
->notEmpty('email', __('An email address is required.'));
return $validator;
}
What it looks like
Update
Thanks to #ndm comment, here is the correct way to display the error.
In UsersController.php:
public function forgotPassword()
{
// user context for the form
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity(§user, $this->request->getData(), ['validate' => 'email']); <- validation done on patchEntity
if ($user->errors()) {
$this->Flash->error(__('An error occurred.'));
} else {
// ... procedure to reset password and redirect to login...
return $this->redirect(['action' => 'login']);
}
}
// pass context to view
$this->set(compact('user'));
}
And in the view forgotPassword.ctp:
<?= $this->Form->create($user) ?>
//modify your function as below
public function forgotPassword()
{
if ($this->request->is('post')) {
$user = $this->Users->newEntity($this->request->getData(), ['validate' => 'email']);
if ($user->getErrors()) {
debug($user->getError('email')); // <- shows the validation error
$this->Flash->error(__($user->getError('email')['_empty']));
} else {
// ... procedure to reset password (which works fine!) and redirect to login...
return $this->redirect(['action' => 'login']);
}
}
}
So i wanted to do a very basic authentication , and by following the Blog tutorial on https://book.cakephp.org/3.0/en/tutorials-and-examples/blog-auth-example/auth.html i tried to make it only use Username to log in, but the login system doesn't work because
$user = $this->Auth->identify();
Always returns false, I have searched cake's documentation on Authentication but can't find anything to help me.
Can someone tell me how do to make a costum login authentication.
Current login action:
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username, try again'));
}
}
And its View:
<div class="users form">
<?= $this->Flash->render() ?>
<?= $this->Form->create() ?>
<fieldset>
<legend><?= __('Please enter your username') ?></legend>
<?= $this->Form->control('username') ?>
</fieldset>
<?= $this->Form->button(__('Login')); ?>
<?= $this->Form->end() ?>
</div>
AuthComponent Init:
public function initialize(array $config)
{
$controller = $this->_registry->getController();
$this->eventManager($controller->eventManager());
$this->response =& $controller->response;
$this->session = $controller->request->session();
}
AFAIK, CakePHP doesn't have a method of logging in a user by just the username. I'm providing an example of what the could could/would look like. This is off the top of my head and in no way tested.
public function login() {
if($this->request->is(['post'])) {
if ($this->Users->exists(['username' => $this->request->getData('username')])) {
$user = $this->Users->findByUsername($this->request->getData('username'))->first()->toArray();
$this->Auth->setUser($user);
}
}
}
After a few trial and error I managed to make my own login with username only that gets all usernames from a column in Database and stores them in an Iterator with
$usernames = $this->UsersTable->find()->extract('username');
and then I convert to an array of strings with
$usernamesArray= iterator_to_array($usernames);
I check if the requested data is Post and I do a loop to check all usernames, after that i trim both the input which is $user and the $usernameArray to ensure they are equal and if it is I authenticate my user and redirect him to my main page again.
$usernames = $this->Table->find()->extract('username');
$usernamesArray= iterator_to_array($usernames);
$user = $this->request->getData('username');
if ($this->request->is('post')) {
for($i = 0 ; $i < count($usernamesArray) ; $i++ ){
if(trim($user) == trim($usernamesArray[$i])){
$this->redirect(['action' => 'index']);
$this->Auth->setUser($user);
return;
}
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
I am using CakePHP 2.9 version. $this->Auth-login() function in Userscontroller doesnt get username. even though my database(user) have 'username' and 'password' field. It is redirecting to else statement.
UsersController:
public $components = array('Paginator','Auth','Session','Flash');
public function beforeFilter() {
$this->Auth->allow('login','logout');
}
public function login(){
if($this->request->is('post')){
$this->set('user1',$this->Auth->request);
if($this->Auth->login()){
return $this->redirect($this->Auth->redirect(['controller'=>'accessors','action'=>'index']));
}
else{
$this->Flash->set('Please check with username and password.');
} }}
login.ctp
echo $this->Form->create('User');
echo $this->Form->input('username',$options=array('div'=>array('class'=>'input text required'),'type'=>'text','autocomplete'=>'off'));
echo $this->Form->input('password',$options=array('div'=>'input password required'));
echo $this->Form->button('Submit', $options=array('type'=>'submit'));
echo $this->Form->button('Reset', $options=array('type'=>'reset')).'</br>';
Here is the screenshot of variables with assigned values captured through DebugKit
Can someone tell me where did i make mistake ?
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)) ...
Need help because i'm still new to Yii2. I want to encrypt the password before saving it to the database. So i'm using sha1 but the problem is that the password field in the form has contents when i apply this line of code in the controller shown below.
$model->password = sha1($model->attributes['password']);
This is the Controller create method:
public function actionCreate()
{
$model = new Employeeinformation();
//$model->password = sha1($model->attributes['password']);
$model->created_date = date('Y-m-d H:i:s');
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->employee_id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
This is the form:
<div class="employeeinformation-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'employee_id')->textInput(['minlength' => true, 'maxlength' => true]) ?>
<?= $form->field($model, 'password')->passwordInput(['maxlength' => true]) ?>
<?= $form->field($model, 'last_name')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'first_name')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'hired_date')->widget(\yii\jui\DatePicker::classname(), [
'language' => 'en',
'dateFormat' => 'yyyy-MM-dd',
]) ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
Screenshot of my problem:
http://i.imgur.com/YTDW1Ud.png
Thank you in advance.
I want to encrypt the password before saving it to the database.
No you don't. Well, you might think you want to encrypt the password, but if you're trying to protect users you actually want to hash the password, not encrypt it.
SHA1 doesn't provide encryption, it's a hash function. This is a very common misconception. You can learn more about basic cryptography terms and concepts at the linked blog post.
More importantly: You don't want a fast hash like SHA1 for passwords. Use password_hash() and password_verify() and you'll have secure password storage. You don't even need to particularly care what these functions do internally to use them correctly.
public function actionCreate()
{
$model = new Employeeinformation();
$post = Yii::$app->request->post();
if ($model->load($post)) {
$model->password = password_hash($model->password, PASSWORD_DEFAULT);
$model->created_date = date('Y-m-d H:i:s');
if ($model->save()) {
return $this->redirect(['view', 'id' => $model->employee_id]);
}
}
return $this->render('create', [
'model' => $model,
]);
}
When employees login, you just need to do this:
if (password_verify($request->password, $storedEmployeeData->hashed_password)) {
// Success
}
Yii2 comes with user module in advanced setup. See how it store user passwords in encrypted way.
You can use setPassword() method in User Model to get hashed passwords.
public function setPassword($password)
{
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
}
and call this method before saving model data.
public function signup()
{
if ($this->validate()) {
$user = new User();
$user->username = $this->username;
$user->email = $this->email;
$user->setPassword($this->password);
$user->generateAuthKey();
if ($user->save()) {
return $user;
}
}
return null;
}
Also look at the Yii2 doc for passwords and authentication.
The content for password is there because you set the attribute before sending the data through the save (and validate) method.
If you like to do it in the controller, you can do it as the following:
public function actionCreate()
{
$model = new Employeeinformation();
if ($model->load(Yii::$app->request->post())){
$model->password = sha1($model->password);
$model->created_date = date('Y-m-d H:i:s');
if ($model->save())
return $this->redirect(['view', 'id' => $model->employee_id]);
}
return $this->render('create', [
'model' => $model,
]);
}
Another way, is to do the password hashing in the beforeSave method of the Employeeinformation model (add this method inside the model class):
public function beforeSave($insert)
{
if(isset($this->password))
$model->password = sha1($model->password);
$model->created_date = date('Y-m-d H:i:s');
return parent::beforeSave($insert);
}
If done using the beforeSave method, these two lines in the controller code can be removed as they are no longer necessary:
$model->password = sha1($model->password);
$model->created_date = date('Y-m-d H:i:s');
However, referring to http://www.yiiframework.com/doc-2.0/guide-security-passwords.html, it is not recommended to use md5 or sha1 for password encryption. Yii2 provide two helper functions to generate & verify password.
Use this to encrypt password:
$hash = Yii::$app->getSecurity()->generatePasswordHash($password);
And to verify it:
if (Yii::$app->getSecurity()->validatePassword($password, $hash)) {
// all good, logging user in
} else {
// wrong password
}
This is a better choice than sha1 that is used in the original code you posted.
you can look at User model for example, there are method setPassword()
public function setPassword($password)
{
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
}
this is how to you set password on database, and also it's already encrypt by yii2 encription
$password = md5($password);
Best way to handle, make sure to correlate this to the login screen to check
$passwordentered = md5($passwordentered);
if ($passwordentered = "Correct"){
"Grant Access"
}
Hope this helps.
In your model add:
public function beforeSave()
{
$this->password=md5($this->password);
return true;
}
Now add this to your controller:
$model->password = md5($model->password);