From 3eb617b424aa182fce64fa9d2c1c1f1cc63b8d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20W=C3=B3jtowicz?= Date: Sun, 9 Dec 2018 20:26:19 +0100 Subject: [PATCH 1/2] RA-1553: Added client side verification for password input --- api/src/main/resources/messages.properties | 5 +++ .../page/controller/PasswordValidation.java | 34 ++++++++++++++++ .../ChangePasswordPageController.java | 4 +- .../accounts/AccountPageController.java | 4 +- .../systemadmin/accounts/userFormFields.gsp | 38 +++++++++++++++++- .../webapp/pages/myaccount/changePassword.gsp | 40 ++++++++++++++++++- 6 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 omod/src/main/java/org/openmrs/module/adminui/page/controller/PasswordValidation.java diff --git a/api/src/main/resources/messages.properties b/api/src/main/resources/messages.properties index 8f21ed23..496086a9 100644 --- a/api/src/main/resources/messages.properties +++ b/api/src/main/resources/messages.properties @@ -16,6 +16,11 @@ adminui.noname=No Name adminui.field.too.long=The value you entered is too long adminui.field.exceeded.maxChars=A maximum of {0} character(s) is allowed adminui.field.require.minChars=At least {0} character(s) are required +adminui.field.require.pattern.begin=This password must have: +adminui.field.require.pattern.reqUpperAndLowerCase=both upper and lowercase characters +adminui.field.require.pattern.reqDigit=at least one digit +adminui.field.require.pattern.reqNonDigit=at least one non digit +adminui.field.require.pattern.reqRegex=match this Regex pattern: {0} adminui.retire=Retire {0}? adminui.purge=Delete {0} Forever? adminui.requiredField.label=required diff --git a/omod/src/main/java/org/openmrs/module/adminui/page/controller/PasswordValidation.java b/omod/src/main/java/org/openmrs/module/adminui/page/controller/PasswordValidation.java new file mode 100644 index 00000000..23edf035 --- /dev/null +++ b/omod/src/main/java/org/openmrs/module/adminui/page/controller/PasswordValidation.java @@ -0,0 +1,34 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.adminui.page.controller; + +import org.openmrs.api.AdministrationService; +import org.openmrs.ui.framework.page.PageModel; +import org.openmrs.util.OpenmrsConstants; + +public final class PasswordValidation { + private PasswordValidation() { + + } + + public static void addPasswordValidationAttributes(PageModel model, AdministrationService adminService) { + String passwordMinLength = adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_MINIMUM_LENGTH, "8"); + boolean passwordReqUpperAndLowerCase = "true".equals(adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_REQUIRES_UPPER_AND_LOWER_CASE, "true")); + boolean passwordReqDigit = "true".equals(adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_REQUIRES_DIGIT, "true")); + boolean passwordReqNonDigit = "true".equals(adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_REQUIRES_NON_DIGIT, "true")); + String passwordReqRegex = adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_CUSTOM_REGEX, ""); + + model.addAttribute("passwordMinLength", passwordMinLength); + model.addAttribute("passwordReqUpperAndLowerCase", passwordReqUpperAndLowerCase); + model.addAttribute("passwordReqDigit", passwordReqDigit); + model.addAttribute("passwordReqNonDigit", passwordReqNonDigit); + model.addAttribute("passwordReqRegex", passwordReqRegex); + } +} diff --git a/omod/src/main/java/org/openmrs/module/adminui/page/controller/myaccount/ChangePasswordPageController.java b/omod/src/main/java/org/openmrs/module/adminui/page/controller/myaccount/ChangePasswordPageController.java index ae6a2b5d..04fe22b9 100644 --- a/omod/src/main/java/org/openmrs/module/adminui/page/controller/myaccount/ChangePasswordPageController.java +++ b/omod/src/main/java/org/openmrs/module/adminui/page/controller/myaccount/ChangePasswordPageController.java @@ -23,6 +23,7 @@ import org.openmrs.api.context.Context; import org.openmrs.api.db.DAOException; import org.openmrs.messagesource.MessageSourceService; +import org.openmrs.module.adminui.page.controller.PasswordValidation; import org.openmrs.module.uicommons.UiCommonsConstants; import org.openmrs.module.uicommons.util.InfoErrorMessageUtil; import org.openmrs.ui.framework.annotation.SpringBean; @@ -53,8 +54,7 @@ public void get(PageModel model, @SpringBean("adminService") AdministrationServi } public void setModelAttributes(PageModel model, AdministrationService adminService) { - model.addAttribute("passwordMinLength", - adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_MINIMUM_LENGTH, "8")); + PasswordValidation.addPasswordValidationAttributes(model, adminService); } public String post(PageModel model, @SpringBean("userService") UserService userService, diff --git a/omod/src/main/java/org/openmrs/module/adminui/page/controller/systemadmin/accounts/AccountPageController.java b/omod/src/main/java/org/openmrs/module/adminui/page/controller/systemadmin/accounts/AccountPageController.java index 0784f99b..26f30294 100644 --- a/omod/src/main/java/org/openmrs/module/adminui/page/controller/systemadmin/accounts/AccountPageController.java +++ b/omod/src/main/java/org/openmrs/module/adminui/page/controller/systemadmin/accounts/AccountPageController.java @@ -35,6 +35,7 @@ import org.openmrs.module.adminui.account.Account; import org.openmrs.module.adminui.account.AccountService; import org.openmrs.module.adminui.account.AdminUiAccountValidator; +import org.openmrs.module.adminui.page.controller.PasswordValidation; import org.openmrs.module.appframework.domain.Extension; import org.openmrs.module.appframework.service.AppFrameworkService; import org.openmrs.module.providermanagement.Provider; @@ -284,8 +285,7 @@ public void setModelAttributes(PageModel model, Account account, OtherAccountDat propertyMaxLengthMap.put("username", administrationService.getMaximumPropertyLength(User.class, "username")); propertyMaxLengthMap.put("password", administrationService.getMaximumPropertyLength(User.class, "password")); model.addAttribute("propertyMaxLengthMap", propertyMaxLengthMap); - model.addAttribute("passwordMinLength", - administrationService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_MINIMUM_LENGTH, "8")); + PasswordValidation.addPasswordValidationAttributes(model, administrationService); ObjectMapper mapper = new ObjectMapper(); SimpleObject so = new SimpleObject(); diff --git a/omod/src/main/webapp/fragments/systemadmin/accounts/userFormFields.gsp b/omod/src/main/webapp/fragments/systemadmin/accounts/userFormFields.gsp index 91b6aff9..eae014c5 100644 --- a/omod/src/main/webapp/fragments/systemadmin/accounts/userFormFields.gsp +++ b/omod/src/main/webapp/fragments/systemadmin/accounts/userFormFields.gsp @@ -42,6 +42,35 @@ passwordAttributes[requiredAttribute] = requiredAttributeValue; } + def pattern + def patternErrorMessage + if (passwordReqUpperAndLowerCase || passwordReqDigit || passwordReqNonDigit || passwordReqRegex) { + pattern = "/^" + patternErrorMessage = ui.message("adminui.field.require.pattern.begin"); + if (passwordReqUpperAndLowerCase) { + pattern += "(?=.*?[A-Z])(?=.*?[a-z])" + patternErrorMessage += " " + ui.message("adminui.field.require.pattern.reqUpperAndLowerCase") + "," + } + if (passwordReqDigit) { + pattern += "(?=.*\\d)" + patternErrorMessage += " " + ui.message("adminui.field.require.pattern.reqDigit") + "," + } + if (passwordReqNonDigit) { + pattern += "(?=.*[^\\d])" + patternErrorMessage += " " + ui.message("adminui.field.require.pattern.reqNonDigit") + "," + } + if (passwordReqRegex) { + pattern += "(?=" + pattern += passwordReqRegex + pattern += ")" + patternErrorMessage += " " + ui.message("adminui.field.require.pattern.reqRegex", passwordReqRegex) + "," + } + patternErrorMessage = patternErrorMessage.substring(0, patternErrorMessage.length() - 1) + pattern += "[\\w|\\W]*\$/" + + passwordAttributes.put("ng-pattern", pattern) + } + def otherPasswordAttributes= ["ng-model": "uuidUserMap['"+userUuid+"'].password"] otherPasswordAttributes.putAll(passwordAttributes) %> @@ -104,8 +133,13 @@ ${ui.message("adminui.field.required")} - ${ui.message("adminui.field.require.minChars", passwordMinLength)} + ${ui.message("adminui.field.require.minChars", passwordMinLength)}
+
+ <% if (pattern) { %> + + ${patternErrorMessage} + <% } %> @@ -159,4 +193,4 @@ <% } %> <% } %> - \ No newline at end of file + diff --git a/omod/src/main/webapp/pages/myaccount/changePassword.gsp b/omod/src/main/webapp/pages/myaccount/changePassword.gsp index 609541ae..895a7578 100644 --- a/omod/src/main/webapp/pages/myaccount/changePassword.gsp +++ b/omod/src/main/webapp/pages/myaccount/changePassword.gsp @@ -7,6 +7,37 @@ ui.includeCss("adminui", "adminui.css") + def passwordAttributes = ["ng-model": "newPassword", required:"", "ng-minlength": passwordMinLength] + + def pattern + def patternErrorMessage + if (passwordReqUpperAndLowerCase || passwordReqDigit || passwordReqNonDigit || passwordReqRegex) { + pattern = "/^" + patternErrorMessage = ui.message("adminui.field.require.pattern.begin"); + if (passwordReqUpperAndLowerCase) { + pattern += "(?=.*?[A-Z])(?=.*?[a-z])" + patternErrorMessage += " " + ui.message("adminui.field.require.pattern.reqUpperAndLowerCase") + "," + } + if (passwordReqDigit) { + pattern += "(?=.*\\d)" + patternErrorMessage += " " + ui.message("adminui.field.require.pattern.reqDigit") + "," + } + if (passwordReqNonDigit) { + pattern += "(?=.*[^\\d])" + patternErrorMessage += " " + ui.message("adminui.field.require.pattern.reqNonDigit") + "," + } + if (passwordReqRegex) { + pattern += "(?=" + pattern += passwordReqRegex + pattern += ")" + patternErrorMessage += " " + ui.message("adminui.field.require.pattern.reqRegex", passwordReqRegex) + "," + } + patternErrorMessage = patternErrorMessage.substring(0, patternErrorMessage.length() - 1) + pattern += "[\\w|\\W]*\$/" + + passwordAttributes.put("ng-pattern", pattern) + } + %>