diff --git a/include/Webservices/Custom/ChangePassword.php b/include/Webservices/Custom/ChangePassword.php index eafc3211e696d2902da2b17315ff68110e4bc956..eba0cb9c8e41cc5efcfff29abf2d600b2eece87e 100644 --- a/include/Webservices/Custom/ChangePassword.php +++ b/include/Webservices/Custom/ChangePassword.php @@ -41,30 +41,43 @@ function vtws_changePassword($id, $oldPassword, $newPassword, $confirmPassword, WebServiceErrorCode::$INVALIDOLDPASSWORD)); } } - if(strcmp($newPassword, $confirmPassword) === 0) { - $db = PearDatabase::getInstance(); - $db->dieOnError = true; - $db->startTransaction(); - $success = $newUser->change_password($oldPassword, $newPassword, false); - $error = $db->hasFailedTransaction(); - $db->completeTransaction(); - if($error) { - throw new WebServiceException(WebServiceErrorCode::$DATABASEQUERYERROR, - vtws_getWebserviceTranslatedString('LBL_'. - WebServiceErrorCode::$DATABASEQUERYERROR)); - } - if(!$success) { - throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, - vtws_getWebserviceTranslatedString('LBL_'. - WebServiceErrorCode::$CHANGEPASSWORDFAILURE)); - } - } else { - throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, - vtws_getWebserviceTranslatedString('LBL_'. - WebServiceErrorCode::$CHANGEPASSWORDFAILURE)); - } + if(isPasswordStrong($newPassword)) { + if(strcmp($newPassword, $confirmPassword) === 0) { + $db = PearDatabase::getInstance(); + $db->dieOnError = true; + $db->startTransaction(); + $success = $newUser->change_password($oldPassword, $newPassword, false); + $error = $db->hasFailedTransaction(); + $db->completeTransaction(); + if($error) { + throw new WebServiceException(WebServiceErrorCode::$DATABASEQUERYERROR, + vtws_getWebserviceTranslatedString('LBL_'. + WebServiceErrorCode::$DATABASEQUERYERROR)); + } + if(!$success) { + throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, + vtws_getWebserviceTranslatedString('LBL_'. + WebServiceErrorCode::$CHANGEPASSWORDFAILURE)); + } + } else { + throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, + vtws_getWebserviceTranslatedString('LBL_'. + WebServiceErrorCode::$CHANGEPASSWORDFAILURE)); + } + } else { + throw new WebServiceException(WebServiceErrorCode::$CHANGEPASSWORDFAILURE, + vtws_getWebserviceTranslatedString('LBL_'. + WebServiceErrorCode::$PASSWORDNOTSTRONG)); + } VTWS_PreserveGlobal::flush(); return array('message' => 'Changed password successfully'); } } + +function isPasswordStrong($new_password){ + if (preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})/i', $new_password) == 1) { + return true; + } + return false; +} ?> \ No newline at end of file diff --git a/include/Webservices/WebServiceErrorCode.php b/include/Webservices/WebServiceErrorCode.php index 63e991f08c6cd98ab2286e8271827d415fd259e4..7b7a06d2327c04a08465a6019360ddeb60581a56 100644 --- a/include/Webservices/WebServiceErrorCode.php +++ b/include/Webservices/WebServiceErrorCode.php @@ -42,6 +42,7 @@ public static $POTENTIAL_RELATED_UPDATE_FAILED = "POTENTIAL_RELATEDLIST_UPDATE_FAILED"; public static $FAILED_TO_CREATE = "FAILED_TO_CREATE"; public static $INACTIVECURRENCY = "CURRENCY_INACTIVE"; + public static $PASSWORDNOTSTRONG = "PASSWORD_NOT_STRONG"; } ?> diff --git a/languages/en_us/Vtiger.php b/languages/en_us/Vtiger.php index fec33408cb3c3829e387560eb50fc1ff12cb95e7..b93df6ce8be8a4de9ee36c93ca9c84760a280e53 100644 --- a/languages/en_us/Vtiger.php +++ b/languages/en_us/Vtiger.php @@ -1942,4 +1942,5 @@ $jsLanguageStrings = array( 'JS_RELATED_ACCOUNT_IS_NOT_AVAILABLE' => 'Related Organization record is not available', 'JS_RELATED_CONTACT_IS_NOT_AVAILABLE' => 'Related Contact record is not available', 'JS_REPEAT_DATE_SHOULD_BE_GREATER_THAN_START_DATE' => 'Repeat date should be greater than or equal to Start Date', + 'JS_PASSWORD_NOT_STRONG' => 'To keep your data safe, we suggest that you use a strong password <br> <ul> <li>Password should be at least 8 characters long </li> <li>Include at least one number </li> <li>Include at least one lowercase alphabet </li> <li>Include at least one uppercase alphabet </li> <li>Include at least one special character in the password </li> </ul>', ); diff --git a/languages/en_us/Webservices.php b/languages/en_us/Webservices.php index 5cac9203a02497b35b6229c8858016034861419a..8d0dfd545c22723a5343dca33afd9e44163aca8c 100644 --- a/languages/en_us/Webservices.php +++ b/languages/en_us/Webservices.php @@ -14,4 +14,5 @@ $languageStrings = array( 'LBL_DATABASE_QUERY_ERROR' => 'Database error while performing requested operation', 'LBL_INVALID_OLD_PASSWORD' => 'Invalid value given for old password.', 'LBL_NEW_PASSWORD_MISMATCH' => "New Password and confirm password don't match", + 'LBL_PASSWORD_NOT_STRONG' => 'To keep your data safe, we suggest that you use a strong password <br> <ul> <li>Password should be at least 8 characters long </li> <li>Include at least one number </li> <li>Include at least one lowercase alphabet </li> <li>Include at least one uppercase alphabet </li> <li>Include at least one special character in the password </li> </ul>', ); \ No newline at end of file diff --git a/layouts/v7/modules/Install/resources/Index.js b/layouts/v7/modules/Install/resources/Index.js index af13ce3359902a69cdb56c0b81b87b575d80266c..46a63970354432612a9039dbbf40fabbd743bdfc 100644 --- a/layouts/v7/modules/Install/resources/Index.js +++ b/layouts/v7/modules/Install/resources/Index.js @@ -73,7 +73,7 @@ jQuery.Class('Install_Index_Js', {}, { }); jQuery('input[name="password"]').on('blur', function (e) { - var retypePassword = jQuery('input[name="retype_password"]'); + var retypePassword = jQuery('input[name="retype_password"]'); if (retypePassword.val() != '' && retypePassword.val() !== jQuery(e.currentTarget).val()) { jQuery('#passwordError').html('Please re-enter passwords. The \"Password\" and \"Re-type password\" values do not match.'); } else { @@ -114,6 +114,13 @@ jQuery.Class('Install_Index_Js', {}, { error = true; } + var passwordField = jQuery('input[name="password"]'); + if(passwordField.val() != ''){ + if(!vtUtils.isPasswordStrong(passwordField.val())) { + error = true; + var passwordNotStrong = true; + } + } var emailField = jQuery('input[name="admin_email"]'); var regex = /^[_/a-zA-Z0-9*]+([!"#$%&'()*+,./:;<=>?\^_`{|}~-]?[a-zA-Z0-9/_/-])*@[a-zA-Z0-9]+([\_\-\.]?[a-zA-Z0-9]+)*\.([\-\_]?[a-zA-Z0-9])+(\.?[a-zA-Z0-9]+)?$/; if (!regex.test(emailField.val()) && emailField.val() != '') { @@ -133,7 +140,16 @@ jQuery.Class('Install_Index_Js', {}, { 'Warning! Invalid email address.' + '</div>' + '</div>'; - } else { + } else if(passwordNotStrong){ + content = '<div class="col-sm-12">' + + '<div class="alert errorMessageContent">' + + '<button class="close" data-dismiss="alert" type="button">x</button>' + + 'To keep your data safe, we suggest that you use a strong password <br>'+ + '<ul> <li>Password should be at least 8 characters long </li> <li>Include at least one number </li><li>Include at least one lowercase alphabet </li> <li>Include at least one uppercase alphabet </li>'+ + '<li>Include at least one special character in the password </li> </ul>' + + '</div>' + + '</div>'; + }else { content = '<div class="col-sm-12">' + '<div class="alert errorMessageContent">' + '<button class="close" data-dismiss="alert" type="button">x</button>' + diff --git a/layouts/v7/modules/Users/FPLogin.tpl b/layouts/v7/modules/Users/FPLogin.tpl index e383b6c3cfb1a2b0c9b445a993d1dd4202d2f789..8d4412a6d10ea1c692f4dab9ba2d2e890bb1f955 100644 --- a/layouts/v7/modules/Users/FPLogin.tpl +++ b/layouts/v7/modules/Users/FPLogin.tpl @@ -12,7 +12,7 @@ {*<DIV>TEMPLATE: layout/modules/Users/FPLogin.tpl</DIV>*} {if $ERROR} - Error, please retry setting the password!! + {$MESSAGE} {else} <h4>Loading .... </h4> <form class="form-horizontal" name="login" id="login" method="post" action="../../../index.php?module=Users&action=Login"> diff --git a/layouts/v7/modules/Users/resources/Detail.js b/layouts/v7/modules/Users/resources/Detail.js index 9de7ede0a17a7999699eae23b45fedf48b20268c..9970c095b3ef4db78fa77a68f42fe471dca87cfd 100644 --- a/layouts/v7/modules/Users/resources/Detail.js +++ b/layouts/v7/modules/Users/resources/Detail.js @@ -28,35 +28,41 @@ Vtiger_Detail_Js("Users_Detail_Js",{ var old_password = form.find('[name="old_password"]'); var userid = form.find('[name="userid"]').val(); - if(new_password.val() === confirm_password.val()){ - var params = { - 'data' : { - 'module': app.getModuleName(), - 'action' : "SaveAjax", - 'mode' : 'savePassword', - 'old_password' : old_password.val(), - 'new_password' : new_password.val(), - 'userid' : userid - } - }; + if(vtUtils.isPasswordStrong(new_password.val())) { + if(new_password.val() === confirm_password.val()){ + var params = { + 'data' : { + 'module': app.getModuleName(), + 'action' : "SaveAjax", + 'mode' : 'savePassword', + 'old_password' : old_password.val(), + 'new_password' : new_password.val(), + 'userid' : userid + } + }; - app.request.post(params).then( - function(err, data) { - if(err == null){ - app.helper.hideModal(); - var successMessage = app.vtranslate(data.message); - app.helper.showSuccessNotification({"message":successMessage}); - }else{ - app.helper.showErrorNotification({"message":err}); - return false; - } - } - ); - } else { - var errorMessage = app.vtranslate('JS_PASSWORD_MISMATCH_ERROR'); - app.helper.showErrorNotification({"message":errorMessage}); - return false; - } + app.request.post(params).then( + function(err, data) { + if(err == null){ + app.helper.hideModal(); + var successMessage = app.vtranslate(data.message); + app.helper.showSuccessNotification({"message":successMessage}); + }else{ + app.helper.showErrorNotification({"message":err}); + return false; + } + } + ); + } else { + var errorMessage = app.vtranslate('JS_PASSWORD_MISMATCH_ERROR'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } + }else{ + var errorMessage = app.vtranslate('JS_PASSWORD_NOT_STRONG'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } } }; form.vtValidate(params); @@ -156,6 +162,11 @@ Vtiger_Detail_Js("Users_Detail_Js",{ var form = jQuery(form); var new_password = form.find('[name="new_password"]'); var confirm_password = form.find('[name="confirm_password"]'); + if(!vtUtils.isPasswordStrong(new_password.val())) { + var errorMessage = app.vtranslate('JS_PASSWORD_NOT_STRONG'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } if (new_password.val() !== confirm_password.val()) { var params = { diff --git a/layouts/v7/modules/Users/resources/Edit.js b/layouts/v7/modules/Users/resources/Edit.js index 09d594d14e5e515a59b6c2a32235a3fc5580bd2c..551617fe67fe4fc0348f9e0207d99277f66284b1 100644 --- a/layouts/v7/modules/Users/resources/Edit.js +++ b/layouts/v7/modules/Users/resources/Edit.js @@ -17,19 +17,24 @@ Vtiger_Edit_Js("Users_Edit_Js",{},{ */ registerRecordPreSaveEvent : function(form){ var thisInstance = this; + if(typeof form == 'undefined') { + var form = this.getForm(); + } app.event.on(Vtiger_Edit_Js.recordPresaveEvent, function(e, data) { + var editForm = thisInstance.getForm(); + editForm.find('.saveButton').attr('disabled',true); var userName = jQuery('input[name="user_name"]').val(); var newPassword = jQuery('input[name="user_password"]').val(); var confirmPassword = jQuery('input[name="confirm_password"]').val(); var record = jQuery('input[name="record"]').val(); - var firstName = jQuery('input[name="first_name"]').val(); - var lastName = jQuery('input[name="last_name"]').val(); - var specialChars = /[<\>\"\,]/; - if((specialChars.test(firstName)) || (specialChars.test(lastName))) { - app.helper.showErrorNotification({message :app.vtranslate('JS_COMMA_NOT_ALLOWED_USERS')}); - e.preventDefault(); - return false; - } + var firstName = jQuery('input[name="first_name"]').val(); + var lastName = jQuery('input[name="last_name"]').val(); + var specialChars = /[<\>\"\,]/; + if((specialChars.test(firstName)) || (specialChars.test(lastName))) { + app.helper.showErrorNotification({message :app.vtranslate('JS_COMMA_NOT_ALLOWED_USERS')}); + e.preventDefault(); + return false; + } var firstName = jQuery('input[name="first_name"]').val(); var lastName = jQuery('input[name="last_name"]').val(); if((firstName.indexOf(',') !== -1) || (lastName.indexOf(',') !== -1)) { @@ -38,10 +43,16 @@ Vtiger_Edit_Js("Users_Edit_Js",{},{ return false; } if(record == ''){ - if(newPassword != confirmPassword){ - app.helper.showErrorNotification({message :app.vtranslate('JS_REENTER_PASSWORDS')}); - e.preventDefault(); - } + if(!vtUtils.isPasswordStrong(newPassword)) { + app.helper.showErrorNotification({message :app.vtranslate('JS_PASSWORD_NOT_STRONG')}); + editForm.find('.saveButton').removeAttr('disabled'); + e.preventDefault(); + return false; + } + if(newPassword != confirmPassword){ + app.helper.showErrorNotification({message :app.vtranslate('JS_REENTER_PASSWORDS')}); + e.preventDefault(); + } if(!(userName in thisInstance.duplicateCheckCache)) { e.preventDefault(); diff --git a/layouts/v7/modules/Users/resources/List.js b/layouts/v7/modules/Users/resources/List.js index a618adcd093e17183aca54a2f79c3e443566c997..cc041dc2cce3bb8bafc9c092083cdb816e910e5a 100644 --- a/layouts/v7/modules/Users/resources/List.js +++ b/layouts/v7/modules/Users/resources/List.js @@ -192,32 +192,38 @@ Settings_Vtiger_List_Js("Settings_Users_List_Js",{ var old_password = form.find('[name="old_password"]'); var userid = form.find('[name="userid"]').val(); - if(new_password.val() === confirm_password.val()){ - var params = { - 'data' : { - 'module': app.getModuleName(), - 'action' : "SaveAjax", - 'mode' : 'savePassword', - 'old_password' : old_password.val(), - 'new_password' : new_password.val(), - 'userid' : userid - } - }; - - app.request.post(params).then( - function(err, data) { - if(err == null){ - app.helper.hideModal(); - var successMessage = app.vtranslate(data.message); - app.helper.showSuccessNotification({"message":successMessage}); - }else{ - app.helper.showErrorNotification({"message":err}); - return false; - } - } - ); - } else { - var errorMessage = app.vtranslate('JS_PASSWORD_MISMATCH_ERROR'); + if(vtUtils.isPasswordStrong(new_password.val())) { + if(new_password.val() === confirm_password.val()){ + var params = { + 'data' : { + 'module': app.getModuleName(), + 'action' : "SaveAjax", + 'mode' : 'savePassword', + 'old_password' : old_password.val(), + 'new_password' : new_password.val(), + 'userid' : userid + } + }; + + app.request.post(params).then( + function(err, data) { + if(err == null){ + app.helper.hideModal(); + var successMessage = app.vtranslate(data.message); + app.helper.showSuccessNotification({"message":successMessage}); + }else{ + app.helper.showErrorNotification({"message":err}); + return false; + } + } + ); + } else { + var errorMessage = app.vtranslate('JS_PASSWORD_MISMATCH_ERROR'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } + } else { + var errorMessage = app.vtranslate('JS_PASSWORD_NOT_STRONG'); app.helper.showErrorNotification({"message":errorMessage}); return false; } @@ -245,6 +251,11 @@ Settings_Vtiger_List_Js("Settings_Users_List_Js",{ var form = jQuery(form); var new_password = form.find('[name="new_password"]'); var confirm_password = form.find('[name="confirm_password"]'); + if(!vtUtils.isPasswordStrong(new_password.val())) { + var errorMessage = app.vtranslate('JS_PASSWORD_NOT_STRONG'); + app.helper.showErrorNotification({"message":errorMessage}); + return false; + } if (new_password.val() !== confirm_password.val()) { var params = { diff --git a/layouts/v7/modules/Vtiger/resources/Utils.js b/layouts/v7/modules/Vtiger/resources/Utils.js index a19b41474819f8ef09f657e590c2fea772b6901a..4dc32a3cf81f39eeeac612e2bd0e0a315897ed9b 100644 --- a/layouts/v7/modules/Vtiger/resources/Utils.js +++ b/layouts/v7/modules/Vtiger/resources/Utils.js @@ -377,18 +377,32 @@ var vtUtils = { return string.replace(tags, function ($0, $1) { return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; }); - }, + }, - addMask: function (container) { - if (container.length && jQuery('#vt-mask').length == 0) { - var mask = '<div id="vt-mask" class="vt-page-mask" ></div>' - container.before(mask); - } - }, + addMask: function (container) { + if (container.length && jQuery('#vt-mask').length == 0) { + var mask = '<div id="vt-mask" class="vt-page-mask" ></div>' + container.before(mask); + } + }, - removeMask: function () { - if (jQuery('#vt-mask').length) { - jQuery('#vt-mask').remove(); - } - }, + removeMask: function () { + if (jQuery('#vt-mask').length) { + jQuery('#vt-mask').remove(); + } + }, + + isPasswordStrong : function(password) { + /* + * ^ The password string will start this way + * (?=.*[a-z]) The string must contain at least 1 lowercase alphabetical character + * (?=.*[A-Z]) The string must contain at least 1 uppercase alphabetical character + * (?=.*[0-9]) The string must contain at least 1 numeric character + * (?=.*[!@#\$%\^&\*]) The string must contain at least one special character, but we are escaping reserved RegEx characters to avoid conflict + * (?=.{8,}) The string must be eight characters or longer + */ + var strongPasswordRegex = new RegExp("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})"); + var isStrong = strongPasswordRegex.test(password)? true : false; + return isStrong; + }, } diff --git a/modules/Users/actions/ForgotPassword.php b/modules/Users/actions/ForgotPassword.php index 38556c0214a8909a9b58dddcfb06c89906b76cae..721e3262e7b3489aec7c1de4d3cb972c58c1fccf 100644 --- a/modules/Users/actions/ForgotPassword.php +++ b/modules/Users/actions/ForgotPassword.php @@ -43,13 +43,19 @@ class Users_ForgotPassword_Action { $userId = getUserId_Ol($userName); $user = Users::getActiveAdminUser(); $wsUserId = vtws_getWebserviceEntityId('Users', $userId); - vtws_changePassword($wsUserId, '', $newPassword, $confirmPassword, $user); + try{ + vtws_changePassword($wsUserId, '', $newPassword, $confirmPassword, $user); + } catch (Exception $e){ + $viewer->assign('ERROR', true); + $viewer->assign('MESSAGE', html_entity_decode($e->getMessage())); + } } else { $viewer->assign('ERROR', true); + $viewer->assign('MESSAGE', 'Error, please retry setting the password!!'); } - $shortURLModel->delete(); - $viewer->assign('USERNAME', $userName); - $viewer->assign('PASSWORD', $newPassword); + $shortURLModel->delete(); + $viewer->assign('USERNAME', $userName); + $viewer->assign('PASSWORD', $newPassword); $viewer->view('FPLogin.tpl', 'Users'); }