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:
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.
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.
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); }
It’s time to place our form in the template.
Let’s prepare error message first.
Find:
- {$error}
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.
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.
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.
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 🙂