From 617f04c975d0c6e17dea3391014c52ac24fbe401 Mon Sep 17 00:00:00 2001 From: lakshya chauhan Date: Mon, 4 Apr 2022 21:01:13 +0530 Subject: [PATCH 1/4] improved maintenance feature --- classes/controller/FrontController.php | 178 ++++++++++++------ .../admin/AdminMaintenanceController.php | 14 ++ img/maintenance_banner.png | Bin 0 -> 29545 bytes js/maintenance.js | 21 +++ .../css/maintenance.css | 123 ++++++++++-- .../hotel-reservation-theme/maintenance.tpl | 86 ++++++--- 6 files changed, 336 insertions(+), 86 deletions(-) create mode 100644 img/maintenance_banner.png create mode 100644 js/maintenance.js diff --git a/classes/controller/FrontController.php b/classes/controller/FrontController.php index 521b68c63..39d84c2ca 100644 --- a/classes/controller/FrontController.php +++ b/classes/controller/FrontController.php @@ -214,7 +214,46 @@ class FrontControllerCore extends Controller self::$initialized = true; parent::init(); + $this->context->smarty->assign([ + 'css_dir' => _THEME_CSS_DIR_, + 'img_ps_dir' => _PS_IMG_, + 'tpl_dir' => _PS_THEME_DIR_, + 'js_dir' => _THEME_JS_DIR_, + 'base_dir' => __PS_BASE_URI__, + 'language_code' => $this->context->language->language_code ? $this->context->language->language_code : $this->context->language->iso_code, + ]); + /* get page name to display it in body id */ + + // Are we in a payment module + $module_name = ''; + if (Validate::isModuleName(Tools::getValue('module'))) { + $module_name = Tools::getValue('module'); + } + + if (!empty($this->page_name)) { + $page_name = $this->page_name; + } elseif (!empty($this->php_self)) { + $page_name = $this->php_self; + } elseif (Tools::getValue('fc') == 'module' && $module_name != '' && (Module::getInstanceByName($module_name) instanceof PaymentModule)) { + $page_name = 'module-payment-submit'; + } + // @retrocompatibility Are we in a module ? + elseif (preg_match('#^'.preg_quote($this->context->shop->physical_uri, '#').'modules/([a-zA-Z0-9_-]+?)/(.*)$#', $_SERVER['REQUEST_URI'], $m)) { + $page_name = 'module-'.$m[1].'-'.str_replace(array('.php', '/'), array('', '-'), $m[2]); + } else { + $page_name = Dispatcher::getInstance()->getController(); + $page_name = (preg_match('/^[0-9]/', $page_name) ? 'page_'.$page_name : $page_name); + } + + $this->context->smarty->assign(Meta::getMetaTags($this->context->language->id, $page_name)); + $this->context->smarty->assign('request_uri', Tools::safeOutput(urldecode($_SERVER['REQUEST_URI']))); + + // Init cookie language + // @TODO This method must be moved into switchLanguage + Tools::setCookieLanguage($this->context->cookie); + + $this->displayMaintenancePage(); // If current URL use SSL, set it true (used a lot for module redirect) if (Tools::usingSecureMode()) { $useSSL = true; @@ -236,13 +275,8 @@ class FrontControllerCore extends Controller $this->context->smarty->assign('account_created', 1); unset($this->context->cookie->account_created); } - ob_start(); - // Init cookie language - // @TODO This method must be moved into switchLanguage - Tools::setCookieLanguage($this->context->cookie); - $protocol_link = (Configuration::get('PS_SSL_ENABLED') || Tools::usingSecureMode()) ? 'https://' : 'http://'; $useSSL = ((isset($this->ssl) && $this->ssl && Configuration::get('PS_SSL_ENABLED')) || Tools::usingSecureMode()) ? true : false; $protocol_content = ($useSSL) ? 'https://' : 'http://'; @@ -370,32 +404,6 @@ class FrontControllerCore extends Controller $this->context->cart = $cart; } - /* get page name to display it in body id */ - - // Are we in a payment module - $module_name = ''; - if (Validate::isModuleName(Tools::getValue('module'))) { - $module_name = Tools::getValue('module'); - } - - if (!empty($this->page_name)) { - $page_name = $this->page_name; - } elseif (!empty($this->php_self)) { - $page_name = $this->php_self; - } elseif (Tools::getValue('fc') == 'module' && $module_name != '' && (Module::getInstanceByName($module_name) instanceof PaymentModule)) { - $page_name = 'module-payment-submit'; - } - // @retrocompatibility Are we in a module ? - elseif (preg_match('#^'.preg_quote($this->context->shop->physical_uri, '#').'modules/([a-zA-Z0-9_-]+?)/(.*)$#', $_SERVER['REQUEST_URI'], $m)) { - $page_name = 'module-'.$m[1].'-'.str_replace(array('.php', '/'), array('', '-'), $m[2]); - } else { - $page_name = Dispatcher::getInstance()->getController(); - $page_name = (preg_match('/^[0-9]/', $page_name) ? 'page_'.$page_name : $page_name); - } - - $this->context->smarty->assign(Meta::getMetaTags($this->context->language->id, $page_name)); - $this->context->smarty->assign('request_uri', Tools::safeOutput(urldecode($_SERVER['REQUEST_URI']))); - /* Breadcrumb */ $navigation_pipe = (Configuration::get('PS_NAVIGATION_PIPE') ? Configuration::get('PS_NAVIGATION_PIPE') : '>'); $this->context->smarty->assign('navigationPipe', $navigation_pipe); @@ -404,7 +412,6 @@ class FrontControllerCore extends Controller if (!empty($this->php_self) && !Tools::getValue('ajax')) { $this->canonicalRedirection($this->context->link->getPageLink($this->php_self, $this->ssl, $this->context->language->id)); } - Product::initPricesComputation(); $display_tax_label = $this->context->country->display_tax_label; @@ -444,13 +451,11 @@ class FrontControllerCore extends Controller 'force_ssl' => Configuration::get('PS_SSL_ENABLED') && Configuration::get('PS_SSL_ENABLED_EVERYWHERE'), 'content_dir' => $protocol_content.Tools::getHttpHost().__PS_BASE_URI__, 'base_uri' => $protocol_content.Tools::getHttpHost().__PS_BASE_URI__.(!Configuration::get('PS_REWRITING_SETTINGS') ? 'index.php' : ''), - 'tpl_dir' => _PS_THEME_DIR_, 'tpl_uri' => _THEME_DIR_, 'modules_dir' => _MODULE_DIR_, 'mail_dir' => _MAIL_DIR_, 'lang_iso' => $this->context->language->iso_code, 'lang_id' => (int)$this->context->language->id, - 'language_code' => $this->context->language->language_code ? $this->context->language->language_code : $this->context->language->iso_code, 'come_from' => Tools::getHttpHost(true, true).Tools::htmlentitiesUTF8(str_replace(array('\'', '\\'), '', urldecode($_SERVER['REQUEST_URI']))), 'cart_qties' => (int)$cart->nbProducts(), 'currencies' => Currency::getCurrencies(), @@ -495,7 +500,6 @@ class FrontControllerCore extends Controller )); $assign_array = array( - 'img_ps_dir' => _PS_IMG_, 'img_cat_dir' => _THEME_CAT_DIR_, 'img_lang_dir' => _THEME_LANG_DIR_, 'img_prod_dir' => _THEME_PROD_DIR_, @@ -505,8 +509,6 @@ class FrontControllerCore extends Controller 'img_store_dir' => _THEME_STORE_DIR_, 'img_col_dir' => _THEME_COL_DIR_, 'img_dir' => _THEME_IMG_DIR_, - 'css_dir' => _THEME_CSS_DIR_, - 'js_dir' => _THEME_JS_DIR_, 'pic_dir' => _THEME_PROD_PIC_DIR_ ); @@ -539,14 +541,12 @@ class FrontControllerCore extends Controller self::$link = $link; $defaultCountry = $this->context->country; - $this->displayMaintenancePage(); - if ($this->restrictedCountry) { $this->displayRestrictedCountryPage(); } if (Tools::isSubmit('live_edit') && !$this->checkLiveEditAccess()) { - Tools::redirect('index.php?controller=404'); + Tools::redirect('index.php'); } $this->iso = $iso; @@ -750,22 +750,92 @@ class FrontControllerCore extends Controller */ protected function displayMaintenancePage() { - if ($this->maintenance == true || !(int)Configuration::get('PS_SHOP_ENABLE')) { - $this->maintenance = true; - if (!in_array(Tools::getRemoteAddr(), explode(',', Configuration::get('PS_MAINTENANCE_IP')))) { - header('HTTP/1.1 503 temporarily overloaded'); - - $this->context->smarty->assign($this->initLogoAndFavicon()); - $this->context->smarty->assign(array( - 'HOOK_MAINTENANCE' => Hook::exec('displayMaintenance', array()), - )); - - // If the controller is a module, then getTemplatePath will try to find the template in the modules, so we need to instanciate a real frontcontroller - $front_controller = preg_match('/ModuleFrontController$/', get_class($this)) ? new FrontController() : $this; - $this->smartyOutputContent($front_controller->getTemplatePath($this->getThemeDir().'maintenance.tpl')); - exit; + if (isset(Context::getContext()->cookie->remote_addr) + && Context::getContext()->cookie->remote_addr != ip2long(Tools::getRemoteAddr()) + ) { + $cookie = Context::getContext()->cookie; + if (isset($cookie->remote_addr)) { + $cookie->enable_maintenance_view = false; + $cookie->remote_addr = false; + $cookie->write(); } } + + if (Tools::isSubmit('SubmitLogin') + && Configuration::get('PS_ALLOW_EMP') + && !Context::getContext()->cookie->enable_maintenance_view + ) { + $email = trim(Tools::getValue('email')); + $passwd = trim(Tools::getValue('passwd')); + $this->processLogin($email, $passwd); + } + + if (!Context::getContext()->cookie->enable_maintenance_view || !Configuration::get('PS_ALLOW_EMP')) { + Context::getContext()->cookie->__unset('enable_maintenance_view'); + Context::getContext()->cookie->write(); + if ($this->maintenance == true || !(int)Configuration::get('PS_SHOP_ENABLE')) { + $this->maintenance = true; + if (!in_array(Tools::getRemoteAddr(), explode(',', Configuration::get('PS_MAINTENANCE_IP')))) { + header('HTTP/1.1 503 temporarily overloaded'); + + $this->context->smarty->assign($this->initLogoAndFavicon()); + $this->context->smarty->assign(array( + 'HOOK_MAINTENANCE' => Hook::exec('displayMaintenance', array()), + 'allowEmployee' => Configuration::get('PS_ALLOW_EMP'), + 'maintenance' => $this->context->link->getAdminLink('AdminMaintenance', false), + )); + // If the controller is a module, then getTemplatePath will try to find the template in the modules, so we need to instanciate a real frontcontroller + $front_controller = preg_match('/ModuleFrontController$/', get_class($this)) ? new FrontController() : $this; + $this->smartyOutputContent($front_controller->getTemplatePath($this->getThemeDir().'maintenance.tpl')); + exit; + } + } + } + } + + protected function processLogin($email, $passwd) + { + $result = false; + /* Check fields validity */ + if (empty($email)) { + $this->errors[] = Tools::displayError('Email is empty.'); + } elseif (!Validate::isEmail($email)) { + $this->errors[] = Tools::displayError('Invalid email address.'); + } + + if (empty($passwd)) { + $this->errors[] = Tools::displayError('The password field is blank.'); + } elseif (!Validate::isPasswd($passwd)) { + $this->errors[] = Tools::displayError('Invalid password.'); + } + + if (!count($this->errors)) { + // Find employee + $this->context->employee = new Employee(); + $is_employee_loaded = $this->context->employee->getByEmail($email, $passwd); + $employee_associated_shop = $this->context->employee->getAssociatedShops(); + if (!$is_employee_loaded) { + $this->errors[] = Tools::displayError('The Employee does not exist, or the password provided is incorrect.'); + $this->context->employee->logout(); + } elseif (empty($employee_associated_shop) && !$this->context->employee->isSuperAdmin()) { + $this->errors[] = Tools::displayError('This employee does not manage the shop anymore (Either the shop has been deleted or permissions have been revoked).'); + $this->context->employee->logout(); + } else { + // Update cookie + $cookie = Context::getContext()->cookie; + $cookie->enable_maintenance_view = $this->context->employee->id; + $cookie->remote_addr = ip2long(Tools::getRemoteAddr()); + $cookie->write(); + } + if (count($this->errors)) { + $this->context->smarty->assign('errors', $this->errors); + } else { + Tools::redirect('index.php'); + } + } else { + $this->context->smarty->assign('errors', $this->errors); + } + return $result; } /** diff --git a/controllers/admin/AdminMaintenanceController.php b/controllers/admin/AdminMaintenanceController.php index 494359d86..0ca65efe3 100644 --- a/controllers/admin/AdminMaintenanceController.php +++ b/controllers/admin/AdminMaintenanceController.php @@ -48,6 +48,14 @@ class AdminMaintenanceControllerCore extends AdminController 'cast' => 'intval', 'type' => 'bool' ), + 'PS_ALLOW_EMP' => array( + 'title' => $this->l('Allow Employees'), + 'desc' => $this->l('Allow the employees to access the site using email and password.'), + 'validation' => 'isBool', + 'cast' => 'intval', + 'type' => 'bool', + 'class' => "hello" + ), 'PS_MAINTENANCE_IP' => array( 'title' => $this->l('Maintenance IP'), 'hint' => $this->l('IP addresses allowed to access the front office even if the shop is disabled. Please use a comma to separate them (e.g. 42.24.4.2,127.0.0.1,99.98.97.96)'), @@ -60,4 +68,10 @@ class AdminMaintenanceControllerCore extends AdminController ), ); } + + public function setMedia() + { + parent::setMedia(); + $this->addJS(_PS_JS_DIR_.'maintenance.js'); + } } diff --git a/img/maintenance_banner.png b/img/maintenance_banner.png new file mode 100644 index 0000000000000000000000000000000000000000..7bb75db80d11f17f2ecd607a2b6528793e201519 GIT binary patch literal 29545 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 literal 0 HcmV?d00001 diff --git a/js/maintenance.js b/js/maintenance.js new file mode 100644 index 000000000..26015d1a9 --- /dev/null +++ b/js/maintenance.js @@ -0,0 +1,21 @@ +$(document).ready(function(){ + + if ($("#PS_SHOP_ENABLE_on").is(':checked')) { + $('#conf_id_PS_ALLOW_EMP').hide(); + } + if ($("#PS_SHOP_ENABLE_off").is(':checked')) { + $("#conf_id_PS_ALLOW_EMP").show(); + } + + $(".clicker.blue").on('click', function(){ + $('.hiddendiv').show('slow'); + }); + + $("#cancelLogin").on('click', function(){ + $('.hiddendiv').hide('slow'); + }); + + $("input[type='radio'][name='PS_SHOP_ENABLE']").change(function() { + $("#conf_id_PS_ALLOW_EMP").toggle('slow'); + }); +}); \ No newline at end of file diff --git a/themes/hotel-reservation-theme/css/maintenance.css b/themes/hotel-reservation-theme/css/maintenance.css index 776764667..32f6de2c6 100644 --- a/themes/hotel-reservation-theme/css/maintenance.css +++ b/themes/hotel-reservation-theme/css/maintenance.css @@ -4,18 +4,119 @@ #body { background-color: #F7F7F7; } -#maintenance { - margin: 50px 0 0 0; - background-color: #fff; - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); } - @media (min-width: 1200px) { - #maintenance { - margin: 126px 0 0 0; - padding: 50px; } } +h1 { + font-size: 50px; +} + +p { + font-size: 16px; + color: #666; +} + +.blue { + color: #1A7EFF; + text-decoration: none; +} + +.containter { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + column-gap: 60px; +} + +.left { + text-align: right; +} + +.clicker { + outline: none; + cursor: pointer; +} + +.hiddendiv { + display: none; + width: 300px; +} + +.text-center { + text-align: center; +} + +input[type='text'], +input[type='email'], +input[type='password'] { + border: 1px solid #c9c9c9; + width: 90%; + padding: 10px 14px; + font-size: 14px; + margin-bottom: 14px; + border-radius: 4px; +} + +button[type='submit'] { + border: 1px solid #1A7EFF; + background-color: #1A7EFF; + padding: 10px 20px; + font-size: 14px; + margin-bottom: 14px; + color: white; + border-radius: 4px; + cursor: pointer; +} + +button[type='button'].cancel-login { + border: 1px solid #c5c5c5; + background-color: #fff; + padding: 10px 20px; + font-size: 14px; + margin-bottom: 14px; + color: #333; + border-radius: 4px; + cursor: pointer; +} + +@media screen and (max-width: 780px) { + .containter { + flex-direction:column; + } +} + +.alert { + padding: 5px; + margin-bottom: 10px; + border: 1px solid transparent; + border-radius: 0px; + font-weight: bold; +} + +.alert.alert-danger p { + color: #FFF; +} + +.alert.alert-danger li { + font-size: 14px; +} + +.alert-danger { + background-color: #FD6E6A; + border-color: #FD6E6A; + color: #fff; +} + +.alert.alert-danger { + text-shadow: 1px 1px rgb(0 0 0 / 10%); +} + +.alert-danger .alert-link { + font-weight: bold; + font-size: 16px; + color: #FFF; +} + #maintenance .logo { - margin: 0 0 31px 0; + padding: 12px; text-align: center; } #maintenance h1 { font: 24px "Open Sans", sans-serif; diff --git a/themes/hotel-reservation-theme/maintenance.tpl b/themes/hotel-reservation-theme/maintenance.tpl index a1a3a02e7..3c9bf04f1 100644 --- a/themes/hotel-reservation-theme/maintenance.tpl +++ b/themes/hotel-reservation-theme/maintenance.tpl @@ -28,30 +28,74 @@ {$meta_title|escape:'html':'UTF-8'} -{if isset($meta_description)} - -{/if} -{if isset($meta_keywords)} - -{/if} + {if isset($meta_description)} + + {/if} + {if isset($meta_keywords)} + + {/if} - - + + + + -

-
- - {$HOOK_MAINTENANCE} -
-

{l s='We\'ll be back soon.'}

- {l s='We are currently updating our site and will be back really soon.'} -
- {l s='Thanks for your patience.'} -
-
-
+
+ +
+ {include file="$tpl_dir./errors.tpl"} +
+
+
+
+ +
+
+
+

{l s='We\'ll be back soon.'}

+

{l s='We are currently updating our site and will be back really soon.'}

+

{l s='Thanks for your patience!'}

+ {if isset($allowEmployee) && $allowEmployee} +
+

Are you member?

+
+
+
+
+
+ +
+ +
+
+ +
+ +
+ + +
+
+
+
+
+ {/if} +
+
+
- + \ No newline at end of file From 5fb51e769216beab38d9e7c650db728de3721a0c Mon Sep 17 00:00:00 2001 From: lakshya chauhan Date: Thu, 21 Jul 2022 17:08:13 +0530 Subject: [PATCH 2/4] added login rate limit --- classes/LoginFail.php | 87 ++++++++++++++++++++++++++ classes/controller/FrontController.php | 24 ++++--- 2 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 classes/LoginFail.php diff --git a/classes/LoginFail.php b/classes/LoginFail.php new file mode 100644 index 000000000..c5914c639 --- /dev/null +++ b/classes/LoginFail.php @@ -0,0 +1,87 @@ + +* @copyright 2010-2022 Webkul IN +* @license https://store.webkul.com/license.html +*/ + +class LoginFailCore extends ObjectModel +{ + + public $id_failed_login; + public $ip_address; + public $email; + public $created_at; + const USERNAME_ATTEMPTS_PER_QUARTER_HOUR = 3; + const IP_ATTEMPTS_QUARTER_HOUR = 12; + const USERNAME_ATTEMPTS_PER_HOUR = 6; + const IP_ATTEMPTS_PER_HOUR = 24; + const QUARTER_HOUR = 60*15; + const HOUR = 60*60; + + /** + * @see ObjectModel::$definition + */ + public static $definition = array( + 'table' => 'login_fail', + 'primary' => 'id_login_fail', + 'fields' => array( + 'ip_address' => array('type' => self::TYPE_STRING, 'size' => 50), + 'email' => array('type' => self::TYPE_STRING, 'validate' => 'isEmail', 'required' => true, 'size' => 128), + ), + ); + + public function cleanData() + { + $time = (time() - (self::HOUR*2)); + Db::getInstance()->execute( + ' + DELETE FROM `' . _DB_PREFIX_ . 'login_fail` + WHERE `created_at` < "' . pSQL(date("Y-m-d H:i:s", $time)) . '"' + ); + } + + public function getUserFailedCount($time, $email=false, $ip_address=false, $attempts) + { + $sql = "SELECT COUNT(id_login_fail) FROM `". _DB_PREFIX_ ."login_fail` WHERE `created_at` > '" + . pSQL(date('Y-m-d H:i:s', $time)) ."'"; + + if ($email) { + $sql .= " AND `email` = '".pSQL($email)."'"; + } + + if ($ip_address) { + $sql .= " AND `ip_address` = '".pSQL($ip_address)."'"; + } + return Db::getInstance()->getValue($sql) >= $attempts; + } + + public function checkLimit($email) + { + $has_error = false; + $minutes_ago = time() - self::QUARTER_HOUR; + $hours_ago = time() - self::HOUR; + $ip_address = Tools::getRemoteAddr(); + + $has_error = $this->getUserFailedCount($minutes_ago, $email, false, self::USERNAME_ATTEMPTS_PER_QUARTER_HOUR)? true : $has_error; + $has_error = $this->getUserFailedCount($minutes_ago, false, $ip_address, self::IP_ATTEMPTS_QUARTER_HOUR)? true : $has_error; + $has_error = $this->getUserFailedCount($hours_ago, $email, false, self::USERNAME_ATTEMPTS_PER_HOUR)? true : $has_error; + $has_error = $this->getUserFailedCount($hours_ago, false, $ip_address, self::IP_ATTEMPTS_PER_HOUR)? true : $has_error; + + return $has_error; + } +} diff --git a/classes/controller/FrontController.php b/classes/controller/FrontController.php index 39d84c2ca..cf68e1cb3 100644 --- a/classes/controller/FrontController.php +++ b/classes/controller/FrontController.php @@ -249,10 +249,7 @@ class FrontControllerCore extends Controller $this->context->smarty->assign(Meta::getMetaTags($this->context->language->id, $page_name)); $this->context->smarty->assign('request_uri', Tools::safeOutput(urldecode($_SERVER['REQUEST_URI']))); - // Init cookie language - // @TODO This method must be moved into switchLanguage Tools::setCookieLanguage($this->context->cookie); - $this->displayMaintenancePage(); // If current URL use SSL, set it true (used a lot for module redirect) if (Tools::usingSecureMode()) { @@ -277,6 +274,9 @@ class FrontControllerCore extends Controller } ob_start(); + // Init cookie language + // @TODO This method must be moved into switchLanguage + $protocol_link = (Configuration::get('PS_SSL_ENABLED') || Tools::usingSecureMode()) ? 'https://' : 'http://'; $useSSL = ((isset($this->ssl) && $this->ssl && Configuration::get('PS_SSL_ENABLED')) || Tools::usingSecureMode()) ? true : false; $protocol_content = ($useSSL) ? 'https://' : 'http://'; @@ -750,7 +750,7 @@ class FrontControllerCore extends Controller */ protected function displayMaintenancePage() { - if (isset(Context::getContext()->cookie->remote_addr) + if (isset(Context::getContext()->cookie->remote_addr) && Context::getContext()->cookie->remote_addr != ip2long(Tools::getRemoteAddr()) ) { $cookie = Context::getContext()->cookie; @@ -761,7 +761,7 @@ class FrontControllerCore extends Controller } } - if (Tools::isSubmit('SubmitLogin') + if (Tools::isSubmit('SubmitLogin') && Configuration::get('PS_ALLOW_EMP') && !Context::getContext()->cookie->enable_maintenance_view ) { @@ -769,7 +769,7 @@ class FrontControllerCore extends Controller $passwd = trim(Tools::getValue('passwd')); $this->processLogin($email, $passwd); } - + if (!Context::getContext()->cookie->enable_maintenance_view || !Configuration::get('PS_ALLOW_EMP')) { Context::getContext()->cookie->__unset('enable_maintenance_view'); Context::getContext()->cookie->write(); @@ -777,7 +777,7 @@ class FrontControllerCore extends Controller $this->maintenance = true; if (!in_array(Tools::getRemoteAddr(), explode(',', Configuration::get('PS_MAINTENANCE_IP')))) { header('HTTP/1.1 503 temporarily overloaded'); - + $this->context->smarty->assign($this->initLogoAndFavicon()); $this->context->smarty->assign(array( 'HOOK_MAINTENANCE' => Hook::exec('displayMaintenance', array()), @@ -796,6 +796,8 @@ class FrontControllerCore extends Controller protected function processLogin($email, $passwd) { $result = false; + $objLoginFail = new LoginFail; + /* Check fields validity */ if (empty($email)) { $this->errors[] = Tools::displayError('Email is empty.'); @@ -809,6 +811,10 @@ class FrontControllerCore extends Controller $this->errors[] = Tools::displayError('Invalid password.'); } + if ($objLoginFail->checkLimit($email)) { + $this->errors[] =Tools::displayError('You have exceeded the limit of login attempts, please try after some time'); + } + if (!count($this->errors)) { // Find employee $this->context->employee = new Employee(); @@ -829,6 +835,10 @@ class FrontControllerCore extends Controller } if (count($this->errors)) { $this->context->smarty->assign('errors', $this->errors); + $objLoginFail->email = $email; + $objLoginFail->ip_address = Tools::getRemoteAddr(); + $objLoginFail->save(); + $objLoginFail->cleanData(); } else { Tools::redirect('index.php'); } From cd71298fcf85ce07c378979e5854152d98ba9285 Mon Sep 17 00:00:00 2001 From: "lakshya.chauhan193" Date: Thu, 10 Nov 2022 16:36:01 +0530 Subject: [PATCH 3/4] sql file updated --- classes/LoginFail.php | 12 ++-- install/data/db_structure.sql | 8 +++ .../css/maintenance.css | 55 ++++++++++++------- .../hotel-reservation-theme/maintenance.tpl | 34 ++++++++---- 4 files changed, 72 insertions(+), 37 deletions(-) diff --git a/classes/LoginFail.php b/classes/LoginFail.php index c5914c639..6b6bf33b0 100644 --- a/classes/LoginFail.php +++ b/classes/LoginFail.php @@ -22,7 +22,7 @@ class LoginFailCore extends ObjectModel { - public $id_failed_login; + public $id_login_fail; public $ip_address; public $email; public $created_at; @@ -77,11 +77,11 @@ class LoginFailCore extends ObjectModel $hours_ago = time() - self::HOUR; $ip_address = Tools::getRemoteAddr(); - $has_error = $this->getUserFailedCount($minutes_ago, $email, false, self::USERNAME_ATTEMPTS_PER_QUARTER_HOUR)? true : $has_error; - $has_error = $this->getUserFailedCount($minutes_ago, false, $ip_address, self::IP_ATTEMPTS_QUARTER_HOUR)? true : $has_error; - $has_error = $this->getUserFailedCount($hours_ago, $email, false, self::USERNAME_ATTEMPTS_PER_HOUR)? true : $has_error; - $has_error = $this->getUserFailedCount($hours_ago, false, $ip_address, self::IP_ATTEMPTS_PER_HOUR)? true : $has_error; + $has_error |= $this->getUserFailedCount($minutes_ago, $email, false, self::USERNAME_ATTEMPTS_PER_QUARTER_HOUR)? true : $has_error; + $has_error |= $this->getUserFailedCount($minutes_ago, false, $ip_address, self::IP_ATTEMPTS_QUARTER_HOUR)? true : $has_error; + $has_error |= $this->getUserFailedCount($hours_ago, $email, false, self::USERNAME_ATTEMPTS_PER_HOUR)? true : $has_error; + $has_error |= $this->getUserFailedCount($hours_ago, false, $ip_address, self::IP_ATTEMPTS_PER_HOUR)? true : $has_error; return $has_error; } -} +} \ No newline at end of file diff --git a/install/data/db_structure.sql b/install/data/db_structure.sql index 60fc52813..fb031152b 100644 --- a/install/data/db_structure.sql +++ b/install/data/db_structure.sql @@ -2687,3 +2687,11 @@ CREATE TABLE IF NOT EXISTS `PREFIX_cms_role_lang` ( PRIMARY KEY (`id_cms_role`,`id_lang`, id_shop) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `PREFIX_login_fail` ( + `id_login_fail` int(11) unsigned NOT NULL AUTO_INCREMENT, + `ip_address` varchar(50) NOT NULL, + `email` varchar(128) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id_login_fail`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/themes/hotel-reservation-theme/css/maintenance.css b/themes/hotel-reservation-theme/css/maintenance.css index 32f6de2c6..9ff35daca 100644 --- a/themes/hotel-reservation-theme/css/maintenance.css +++ b/themes/hotel-reservation-theme/css/maintenance.css @@ -1,6 +1,10 @@ /********************************************************************* Maintenance Page Styles **********************************************************************/ +body { + font: 14px; + font-family: "Open Sans"; +} #body { background-color: #F7F7F7; } @@ -84,11 +88,21 @@ button[type='button'].cancel-login { } .alert { - padding: 5px; - margin-bottom: 10px; + position: relative; + padding: 0.8rem 1.5rem; + margin-bottom: 1rem; border: 1px solid transparent; - border-radius: 0px; - font-weight: bold; + border-radius: 0.25rem; +} + +.alert ol { + margin: 5px; +} + +.alert li { + font-size: 1rem; + font-weight: 400; + line-height: 1.5; } .alert.alert-danger p { @@ -100,9 +114,9 @@ button[type='button'].cancel-login { } .alert-danger { - background-color: #FD6E6A; - border-color: #FD6E6A; - color: #fff; + color: #842029; + background-color: #f8d7da; + border-color: #f5c2c7; } .alert.alert-danger { @@ -115,19 +129,18 @@ button[type='button'].cancel-login { color: #FFF; } - #maintenance .logo { - padding: 12px; - text-align: center; } - #maintenance h1 { - font: 24px "Open Sans", sans-serif; - color: #333; - padding: 0 0 14px 0; - margin: 0 0 19px 0; } - #maintenance #message { - font: 14px "Open Sans", sans-serif; - padding: 0 0 0 18px; - color: #555454; - text-align: center; } +#maintenance .logo { + padding: 12px; + text-align: center; } +#maintenance h2 { + font: 24px; + color: #333; + padding: 0 0 14px 0; + margin: 0 0 19px 0; } +#maintenance #message { + padding: 0 0 0 18px; + color: #555454; + text-align: center; } .container { padding-right: 15px; @@ -149,4 +162,4 @@ button[type='button'].cancel-login { padding-left: 0; padding-right: 0; } } -/*# sourceMappingURL=maintenance.css.map */ +/*# sourceMappingURL=maintenance.css.map */ \ No newline at end of file diff --git a/themes/hotel-reservation-theme/maintenance.tpl b/themes/hotel-reservation-theme/maintenance.tpl index 3c9bf04f1..d07051087 100644 --- a/themes/hotel-reservation-theme/maintenance.tpl +++ b/themes/hotel-reservation-theme/maintenance.tpl @@ -25,6 +25,7 @@ + {$meta_title|escape:'html':'UTF-8'} @@ -41,13 +42,24 @@ +
- {include file="$tpl_dir./errors.tpl"} + {if isset($errors) && $errors} +
+ {l s='Error!'} +
    + {foreach from=$errors key=k item=error} +
  1. {$error}
  2. + {/foreach} +
+
+ {/if}
@@ -60,32 +72,33 @@

{l s='We are currently updating our site and will be back really soon.'}

{l s='Thanks for your patience!'}

{if isset($allowEmployee) && $allowEmployee} -
-

Are you member?

-
+
+

{l s='Are you member?'}

+
- +
- +
-
@@ -98,4 +111,5 @@
+ \ No newline at end of file From 1d44813f2641c46a029bf6d4ac425b8bb65e5df5 Mon Sep 17 00:00:00 2001 From: "lakshya.chauhan193" Date: Tue, 29 Nov 2022 17:12:14 +0530 Subject: [PATCH 4/4] changed table name LoginFail to MaintenanceAccess --- .../{LoginFail.php => MaintenanceAccess.php} | 35 ++++++++++++------- classes/controller/FrontController.php | 11 +++--- install/data/db_structure.sql | 10 +++--- 3 files changed, 32 insertions(+), 24 deletions(-) rename classes/{LoginFail.php => MaintenanceAccess.php} (62%) diff --git a/classes/LoginFail.php b/classes/MaintenanceAccess.php similarity index 62% rename from classes/LoginFail.php rename to classes/MaintenanceAccess.php index 6b6bf33b0..11e0f758d 100644 --- a/classes/LoginFail.php +++ b/classes/MaintenanceAccess.php @@ -19,13 +19,13 @@ * @license https://store.webkul.com/license.html */ -class LoginFailCore extends ObjectModel +class MaintenanceAccessCore extends ObjectModel { - public $id_login_fail; + public $id_maintenance_access; public $ip_address; public $email; - public $created_at; + public $date_add; const USERNAME_ATTEMPTS_PER_QUARTER_HOUR = 3; const IP_ATTEMPTS_QUARTER_HOUR = 12; const USERNAME_ATTEMPTS_PER_HOUR = 6; @@ -37,11 +37,12 @@ class LoginFailCore extends ObjectModel * @see ObjectModel::$definition */ public static $definition = array( - 'table' => 'login_fail', - 'primary' => 'id_login_fail', + 'table' => 'maintenance_access', + 'primary' => 'id_maintenance_access', 'fields' => array( 'ip_address' => array('type' => self::TYPE_STRING, 'size' => 50), 'email' => array('type' => self::TYPE_STRING, 'validate' => 'isEmail', 'required' => true, 'size' => 128), + 'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'), ), ); @@ -49,15 +50,14 @@ class LoginFailCore extends ObjectModel { $time = (time() - (self::HOUR*2)); Db::getInstance()->execute( - ' - DELETE FROM `' . _DB_PREFIX_ . 'login_fail` - WHERE `created_at` < "' . pSQL(date("Y-m-d H:i:s", $time)) . '"' + 'DELETE FROM `' . _DB_PREFIX_ .'maintenance_access` + WHERE `date_add` < "' . pSQL(date("Y-m-d H:i:s", $time)) . '"' ); } public function getUserFailedCount($time, $email=false, $ip_address=false, $attempts) { - $sql = "SELECT COUNT(id_login_fail) FROM `". _DB_PREFIX_ ."login_fail` WHERE `created_at` > '" + $sql = "SELECT COUNT(`id_maintenance_access`) FROM `". _DB_PREFIX_ ."maintenance_access` WHERE `date_add` > '" . pSQL(date('Y-m-d H:i:s', $time)) ."'"; if ($email) { @@ -67,6 +67,7 @@ class LoginFailCore extends ObjectModel if ($ip_address) { $sql .= " AND `ip_address` = '".pSQL($ip_address)."'"; } + return Db::getInstance()->getValue($sql) >= $attempts; } @@ -77,10 +78,18 @@ class LoginFailCore extends ObjectModel $hours_ago = time() - self::HOUR; $ip_address = Tools::getRemoteAddr(); - $has_error |= $this->getUserFailedCount($minutes_ago, $email, false, self::USERNAME_ATTEMPTS_PER_QUARTER_HOUR)? true : $has_error; - $has_error |= $this->getUserFailedCount($minutes_ago, false, $ip_address, self::IP_ATTEMPTS_QUARTER_HOUR)? true : $has_error; - $has_error |= $this->getUserFailedCount($hours_ago, $email, false, self::USERNAME_ATTEMPTS_PER_HOUR)? true : $has_error; - $has_error |= $this->getUserFailedCount($hours_ago, false, $ip_address, self::IP_ATTEMPTS_PER_HOUR)? true : $has_error; + $has_error |= $this->getUserFailedCount( + $minutes_ago, $email, false, self::USERNAME_ATTEMPTS_PER_QUARTER_HOUR + )? true : $has_error; + $has_error |= $this->getUserFailedCount( + $minutes_ago, false, $ip_address, self::IP_ATTEMPTS_QUARTER_HOUR + )? true : $has_error; + $has_error |= $this->getUserFailedCount( + $hours_ago, $email, false, self::USERNAME_ATTEMPTS_PER_HOUR + )? true : $has_error; + $has_error |= $this->getUserFailedCount( + $hours_ago, false, $ip_address, self::IP_ATTEMPTS_PER_HOUR + )? true : $has_error; return $has_error; } diff --git a/classes/controller/FrontController.php b/classes/controller/FrontController.php index cf68e1cb3..7bf72d4c8 100644 --- a/classes/controller/FrontController.php +++ b/classes/controller/FrontController.php @@ -796,7 +796,7 @@ class FrontControllerCore extends Controller protected function processLogin($email, $passwd) { $result = false; - $objLoginFail = new LoginFail; + $objMaintenanceAccess = new MaintenanceAccess(); /* Check fields validity */ if (empty($email)) { @@ -811,7 +811,7 @@ class FrontControllerCore extends Controller $this->errors[] = Tools::displayError('Invalid password.'); } - if ($objLoginFail->checkLimit($email)) { + if ($objMaintenanceAccess->checkLimit($email)) { $this->errors[] =Tools::displayError('You have exceeded the limit of login attempts, please try after some time'); } @@ -835,10 +835,9 @@ class FrontControllerCore extends Controller } if (count($this->errors)) { $this->context->smarty->assign('errors', $this->errors); - $objLoginFail->email = $email; - $objLoginFail->ip_address = Tools::getRemoteAddr(); - $objLoginFail->save(); - $objLoginFail->cleanData(); + $objMaintenanceAccess->email = $email; + $objMaintenanceAccess->ip_address = Tools::getRemoteAddr(); + $objMaintenanceAccess->save(); } else { Tools::redirect('index.php'); } diff --git a/install/data/db_structure.sql b/install/data/db_structure.sql index fb031152b..832eea2ba 100644 --- a/install/data/db_structure.sql +++ b/install/data/db_structure.sql @@ -2688,10 +2688,10 @@ CREATE TABLE IF NOT EXISTS `PREFIX_cms_role_lang` ( ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; -CREATE TABLE IF NOT EXISTS `PREFIX_login_fail` ( - `id_login_fail` int(11) unsigned NOT NULL AUTO_INCREMENT, +CREATE TABLE IF NOT EXISTS `PREFIX_maintenance_access` ( + `id_maintenance_access` int(11) unsigned NOT NULL AUTO_INCREMENT, `ip_address` varchar(50) NOT NULL, `email` varchar(128) NOT NULL, - `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id_login_fail`) -) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8; \ No newline at end of file + `date_add` datetime NOT NULL, + PRIMARY KEY (`id_maintenance_access`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8;