PrestaCraft on Facebook

Please like our page on Facebook to get notifications about new tutorials :)


PrestaCraft

PrestaCraft on Facebook

Please like our page on Facebook to get notifications about new tutorials :)


PrestaCraft

There are many systems and applications containing common security feature, which asks the user for a new password after X days have passed. It’s usually used to increase security of users and their accounts.

What if we would like to do the same thing in PrestaShop BackOffice login screen? Keep reading.

To make this possible, we are going to add a new column to the database. It’ll contain the date of user last password change. When we have this, we can count how many days have passed since last password update. In this article I am assuming that 30 days have to pass to make password change form visible.

It’s going to look like this:

pwchange

Before any changes, please remember to do a backup of all your files! I don’t take any responsibility for presumable inconveniences or bugs which may occur after those modifications.

Execute SQL command:

ALTER TABLE ps_employee 
ADD COLUMN password_date DATETIME;

Set the value of password_date column for Your account. The date should be 30 days older or more than the current one. You can edit this value directly using phpMyAdmin or by executing below SQL command:

UPDATE ps_employee 
SET password_date="2014-10-10 10:00:00" 
WHERE id_employee=[YOUR ID];

After this database modification, let’s edit the class model file to make it aware of the new field.

Edit: /classes/Employee.php

Find:

public $remote_addr;

 Add after:

public $password_date;

Find:

'id_last_customer' =>			array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),

 Add after:

'password_date' => 				array('type' => self::TYPE_DATE, 'validate' => 'isDate'),

That was easy.
Let’s edit the controller now.

Edit: /controllers/admin/AdminLoginController.php

We’ll start with changes in processLogin() function.
Find:

$cookie->remote_addr = $this->context->employee->remote_addr;

 Add after:

$pwDate = strtotime($this->context->employee->password_date);

We’re using Employee model via Context call, so we can fetch any field from ps_employee table. We need the date of password change. We’ll use strtotime PHP function, which helps in counting the days since last password change.

Let’s focus on password change. It should happen after successful login. However, after BackOffice login we’re redirected to the Dashboard. To prevent this, we should ask a following quesion:

Is the date of last password change older than 30 days?
1. Yeah -> Display password change form, but stay on the same page and don’t redirect me.
2. Nope -> Everything is fine. Go to the BackOffice Dashboard (default action).

Here’s the code.
Find:

// If there is a valid controller name submitted, redirect to it
if (isset($_POST['redirect']) && Validate::isControllerName($_POST['redirect']))
	$url = $this->context->link->getAdminLink($_POST['redirect']);
else
{
	$tab = new Tab((int)$this->context->employee->default_tab);
	$url = $this->context->link->getAdminLink($tab->class_name);

 Replace with:

// Check if date is older than 30 days:
if($pwDate < strtotime('-30 days')) { // Yes it is -> set the URL variable to current page (to not change our location and stay on the same page).
	$url='index.php?controller=AdminLogin';
// Additionaly we'll make the AJAX call with passwordChange variable. It'll be used in password change form.
		if (Tools::isSubmit('ajax'))
			die(Tools::jsonEncode(array('passwordChange' => true, 'redirect' => $url)));
} 
else 
{
// No it isn't -> redirect me to the BackOffice
	// If there is a valid controller name submitted, redirect to it
	if (isset($_POST['redirect']) && Validate::isControllerName($_POST['redirect']))
		$url = $this->context->link->getAdminLink($_POST['redirect']);
	else
	{
		$tab = new Tab((int)$this->context->employee->default_tab);
		$url = $this->context->link->getAdminLink($tab->class_name);
	}
Edit: /adminXXX/themes/default/template/controllers/login/content.tpl

It’s time to place our form in the template.
Let’s prepare error message first.
Find:

  • {$error}
{/foreach} {/if}

 Add after:

{$errorPw}

And now the form.

Find:

 Add after:

We have to declare when this form and error messages should be displayed to user. Open login.js file.

Edit: /js/login.js

We’ll add some validation. Below condition checks if BackOffice password has less than 8 characters and differs from a new one. If condition passes the error messages will appear.

Find:

$(document).ready(function() {

 Add after:

$( "#change_password" ).submit(function( event ) {
    if($('#new_password').val() == "" || $('#new_password').val().length < 8 || $('#new_password_conf').val() == "" || ($('#new_password').val() != $('#new_password_conf').val()))
{
    $("#error_pw_conf").removeClass('hide').fadeIn('slow');
    event.preventDefault();
} 
});

It’s time to display our form.

Find:

success: function(jsonData) {
    if (jsonData.hasErrors) {
        displayErrors(jsonData.errors);
        l.stop();
    } else {
        window.location.assign(jsonData.redirect);
    }
},

 Replace with:

success: function(jsonData) {
    if (jsonData.hasErrors) {
        displayErrors(jsonData.errors);
        l.stop();
    } else {
    if (jsonData.passwordChange) {
        $('#login_form').hide();
        $('#change_password').fadeIn('slow');
    } else {
        window.location.assign(jsonData.redirect);
    }	
    }
},

The last thing to do in this file is removing unnecessary displayLogin(); function.

Find:

alert(jsonData.confirm);
$('#forgot_password_form').hide();
displayLogin();

 Remove:

displayLogin();

Let’s go back to the controller.

Edit: /controllers/admin/AdminLoginController.php

Find:

public function initContent()
{

 Add after:

$this->context->smarty->assign('passwordChanged', $this->l('Your password has been changed.'));
$this->context->smarty->assign('errorPw', $this->l('Inserted passwords are not the same or Your password is too short.'));

Here we’ve added error messages and assigned them to the smarty variables.

Find:

elseif (Tools::isSubmit('submitForgot'))
    $this->processForgot();

 Add after:

elseif (Tools::isSubmit('submitChangePw'))
    $this->processChangePassword();

There we’ve placed processChangePassword() function, which is going to be called after sending password change form.

Find:

else if (Tools::isSubmit('ajax'))
    die(Tools::jsonEncode(array('hasErrors' => true, 'errors' => $this->errors)));
}

 Add after:

public function processChangePassword()
{

    // Get password value from the form
    $new = trim(Tools::getValue('new_password'));
    $new_confirm = trim(Tools::getValue('new_password_conf'));

    // Encrypt password using PrestaShop standards
    $newPwEncrypted = md5(_COOKIE_KEY_.$new);
		
    $cookie = Context::getContext()->cookie;

    // Messages
    $passMsg = $this->l('Your password has been changed. You can sign in to Administration Panel using Your new password.');
    $passErrorMsg = $this->l('An error ocurred during password change. Please contact website administrator.');
    $sameMsg = $this->l('Your new password can not be the same as the current one. Please sign in again and type another password.');


    // Check if a new password is the same as the current one
    $pw = 'SELECT id_employee FROM '._DB_PREFIX_.'employee 
    WHERE id_employee='.$cookie->id_employee.' and passwd="'.$newPwEncrypted.'"';
    $row = Db::getInstance()->getRow($pw);


    // If both passwords are the same, throw the error message
    if($row)
    {
        echo "



"; echo $sameMsg; echo "
"; } else { // If new password differs from a current one, update it if($new == $new_confirm) { $sql = 'UPDATE '._DB_PREFIX_.'employee SET passwd="'.$newPwEncrypted.'", password_date=now() WHERE id_employee='.$cookie->id_employee.';'; if(Db::getInstance()->execute($sql)) { echo "
"; echo $passMsg; echo "
"; } else { echo "
"; echo $passErrorMsg; echo "
"; } } } }

Comments placed in this code should help you understand the logic.

Edit: /classes/Employee.php

The last thing we’re going to do is to set last login date while adding new employee..
Find:

$this->last_passwd_gen = date('Y-m-d H:i:s', strtotime('-'.Configuration::get('PS_PASSWD_TIME_BACK').'minutes'));

 Add after:

$this->password_date = date('Y-m-d H:i:s');

Set overrides

Last and very important thing is to move all Your changes to override/ directory.
1. Copy and paste files:


Source Destination
classes/Employee.php override/classes/Employee.php
controllers/admin/AdminLoginController.php override/controllers/admin/AdminLoginController.php

2. Replace in DESTINATION files:
class EmployeeCore extends ObjectModel replace with class Employee extends EmployeeCore
class AdminLoginControllerCore extends AdminController replace with class AdminLoginController extends AdminLoginControllerCore

3. Purge cache:
Remove /cache/class_index.php file.

That’s all. Thanks for reading. Any feedback is highly appreciated 🙂

Leave a comment

Your email address will not be published.

Be the first to post a comment!

Leave a comment

Your email address will not be published.