This is an automated email from the ASF dual-hosted git repository. solomax pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/openmeetings.git
The following commit(s) were added to refs/heads/master by this push: new 445db6e [OPENMEETINGS-2443] password validator can be configured to skip missing upper-case letter 445db6e is described below commit 445db6ebb746cf5b3f53cf8f99c27f49bc793be2 Author: Maxim Solodovnik <solomax...@gmail.com> AuthorDate: Thu Sep 17 10:36:24 2020 +0700 [OPENMEETINGS-2443] password validator can be configured to skip missing upper-case letter --- openmeetings-core/pom.xml | 5 + .../core/util/StrongPasswordValidator.java | 5 +- .../core/util/TestStrongPasswordValidator.java | 130 +++++++++++++++++++++ .../db/dao/basic/ConfigurationDao.java | 8 ++ .../installation/ImportInitvalues.java | 2 + .../openmeetings/util/OpenmeetingsVariables.java | 10 ++ pom.xml | 6 + 7 files changed, 164 insertions(+), 2 deletions(-) diff --git a/openmeetings-core/pom.xml b/openmeetings-core/pom.xml index 256af41..7ebfad7 100644 --- a/openmeetings-core/pom.xml +++ b/openmeetings-core/pom.xml @@ -107,6 +107,11 @@ </dependency> <!-- Test dependencies --> <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.apache.openmeetings</groupId> <artifactId>openmeetings-util</artifactId> <version>${project.version}</version> diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/StrongPasswordValidator.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/StrongPasswordValidator.java index de03860..c2c7893 100644 --- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/StrongPasswordValidator.java +++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/StrongPasswordValidator.java @@ -19,6 +19,7 @@ package org.apache.openmeetings.core.util; import static org.apache.openmeetings.util.OpenmeetingsVariables.getMinPasswdLength; +import static org.apache.openmeetings.util.OpenmeetingsVariables.isPwdCheckUpper; import java.util.Locale; import java.util.Map; @@ -56,7 +57,7 @@ public class StrongPasswordValidator implements IValidator<String> { } private static boolean noUpperCase(String password) { - return password == null || password.equals(password.toLowerCase(Locale.ROOT)); + return password == null || (isPwdCheckUpper() && password.equals(password.toLowerCase(Locale.ROOT))); } private static boolean noLowerCase(String password) { @@ -68,7 +69,7 @@ public class StrongPasswordValidator implements IValidator<String> { } private static boolean checkWord(String password, String word) { - if (Strings.isEmpty(word) || word.length() < 3) { + if (Strings.isEmpty(password) || Strings.isEmpty(word) || word.length() < 3) { return false; } for (int i = 0; i < word.length() - 3; ++i) { diff --git a/openmeetings-core/src/test/java/org/apache/openmeetings/core/util/TestStrongPasswordValidator.java b/openmeetings-core/src/test/java/org/apache/openmeetings/core/util/TestStrongPasswordValidator.java new file mode 100644 index 0000000..d1b30d6 --- /dev/null +++ b/openmeetings-core/src/test/java/org/apache/openmeetings/core/util/TestStrongPasswordValidator.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License") + you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openmeetings.core.util; + +import static org.apache.openmeetings.util.OpenmeetingsVariables.setPwdCheckUpper; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import org.apache.openmeetings.db.dao.label.LabelDao; +import org.apache.openmeetings.db.entity.user.Address; +import org.apache.openmeetings.db.entity.user.User; +import org.apache.wicket.validation.Validatable; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.MockedStatic; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class TestStrongPasswordValidator { + private static User getUser(String login, String email) { + User u = new User(); + u.setLogin(login); + u.setAddress(new Address()); + u.getAddress().setEmail(email); + return u; + } + + private static Stream<Arguments> provideTestArgs() { + List<Arguments> args = new ArrayList<>(); + for (boolean web : new boolean[] {true, false}) { + args.add(Arguments.of(null, web, getUser(null, null), 5)); + User u1 = getUser("1", null); + args.add(Arguments.of(null, web, u1, 5)); + User u2 = getUser("2222", null); + args.add(Arguments.of("1", web, u2, 4)); + User u3 = getUser("2222", "2222@local"); + args.add(Arguments.of("password", web, u3, 3)); + args.add(Arguments.of("passWord", web, u3, 2)); + args.add(Arguments.of("passWord222", web, u3, 2)); + args.add(Arguments.of("passWord2!", web, u3, 0)); + } + return args.stream(); + } + + void runWrapped(Runnable task) { + try (MockedStatic<LabelDao> labelMock = mockStatic(LabelDao.class)) { + labelMock.when(() -> LabelDao.getString(any(String.class), any(Long.class))).then(new Answer<String>() { + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArgument(0); + } + }); + task.run(); + } + } + + @Test + void testDefCtr() { + runWrapped(() -> { + Validatable<String> pass = new Validatable<>(null); + StrongPasswordValidator validator = new StrongPasswordValidator(new User()); + validator.validate(pass); + assertEquals(5, pass.getErrors().size()); + }); + } + + @Test + void testSetUser() { + runWrapped(() -> { + Validatable<String> pass = new Validatable<>(null); + StrongPasswordValidator validator = new StrongPasswordValidator(null); + validator.setUser(new User()); + validator.validate(pass); + assertEquals(5, pass.getErrors().size()); + }); + } + + + @Test + void testNoUpper() { + try { + setPwdCheckUpper(false); + runWrapped(() -> { + int expectedErrors = 2; + String pwd = "password"; + Validatable<String> pass = new Validatable<>(pwd); + User u = getUser("2222", "2222@local"); + StrongPasswordValidator validator = new StrongPasswordValidator(u); + validator.validate(pass); + assertEquals(expectedErrors, pass.getErrors().size(), "Expected exactly " + expectedErrors + " errors, pass: '" + pwd + "', user: " + u); + }); + } finally { + setPwdCheckUpper(true); + } + } + + @ParameterizedTest + @MethodSource("provideTestArgs") + void testNull(String pwd, boolean web, User u, int expectedErrors) { + runWrapped(() -> { + Validatable<String> pass = new Validatable<>(pwd); + StrongPasswordValidator validator = new StrongPasswordValidator(web, u); + validator.validate(pass); + assertEquals(expectedErrors, pass.getErrors().size(), "Expected exactly " + expectedErrors + " errors, pass: '" + pwd + "', user: " + u); + }); + } +} diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java index 7be84df..82af13d 100644 --- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java +++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java @@ -292,6 +292,9 @@ public class ConfigurationDao implements IDataProviderDao<Configuration> { case CONFIG_PASS_MIN_LENGTH: reloadPasswdMinLength(); break; + case CONFIG_PASS_CHECK_UPPER: + reloadPwdCheckUpper(); + break; case CONFIG_DEFAULT_GROUP_ID: reloadDefaultGroup(); break; @@ -429,6 +432,10 @@ public class ConfigurationDao implements IDataProviderDao<Configuration> { setMinPasswdLength(getInt(CONFIG_LOGIN_MIN_LENGTH, USER_PASSWORD_MINIMUM_LENGTH)); } + private void reloadPwdCheckUpper() { + setPwdCheckUpper(getBool(CONFIG_PASS_CHECK_UPPER, true)); + } + private void reloadDefaultGroup() { setDefaultGroup(getLong(CONFIG_DEFAULT_GROUP_ID, null)); } @@ -505,6 +512,7 @@ public class ConfigurationDao implements IDataProviderDao<Configuration> { reloadRoomSettings(); reloadLoginMinLength(); reloadPasswdMinLength(); + reloadPwdCheckUpper(); reloadDefaultGroup(); reloadSipContext(); reloadFnameMinLength(); diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java b/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java index a0ae9a9..937adcb 100644 --- a/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java +++ b/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java @@ -72,6 +72,7 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MP4_AUDI import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MP4_AUDIO_RATE; import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MP4_VIDEO_PRESET; import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MYROOMS_ENABLED; +import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PASS_CHECK_UPPER; import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PASS_MIN_LENGTH; import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_FFMPEG; import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_IMAGEMAGIC; @@ -384,6 +385,7 @@ public class ImportInitvalues { + DEFAULT_CSP_STYLE + ")" + cspMore, VER_5_0_0); addCfg(list, CONFIG_SMTP_SSL, String.valueOf(false), Configuration.Type.BOOL, "Enable SSL", VER_5_0_0); addCfg(list, CONFIG_CSP_ENABLED, String.valueOf(true), Configuration.Type.BOOL, "Whether or not CSP secure headers are enabled", VER_5_0_0); + addCfg(list, CONFIG_PASS_CHECK_UPPER, String.valueOf(true), Configuration.Type.BOOL, "Whether or not Password MUST contain uppercase characters", "5.0.1"); return list; } public void loadConfiguration(InstallationConfig cfg) { diff --git a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java index 4ce4e33..69d1b35 100644 --- a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java +++ b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java @@ -51,6 +51,7 @@ public class OpenmeetingsVariables { public static final String CONFIG_SIP_EXTEN_CONTEXT = "sip.exten.context"; public static final String CONFIG_LOGIN_MIN_LENGTH = "user.login.minimum.length"; public static final String CONFIG_PASS_MIN_LENGTH = "user.pass.minimum.length"; + public static final String CONFIG_PASS_CHECK_UPPER = "user.pass.check.upper"; public static final String CONFIG_IGNORE_BAD_SSL = "oauth2.ignore.bad.ssl"; public static final String CONFIG_REDIRECT_URL_FOR_EXTERNAL = "redirect.url.for.external.users"; public static final String CONFIG_APPOINTMENT_REMINDER_MINUTES = "number.minutes.reminder.send"; @@ -128,6 +129,7 @@ public class OpenmeetingsVariables { private static int extProcessTtl = 20; private static int minLoginLength = USER_LOGIN_MINIMUM_LENGTH; private static int minPasswdLength = USER_PASSWORD_MINIMUM_LENGTH; + private static boolean pwdCheckUpper = true; private static JSONObject roomSettings = new JSONObject(); private static boolean initComplete = false; private static long maxUploadSize = DEFAULT_MAX_UPLOAD_SIZE; @@ -328,6 +330,14 @@ public class OpenmeetingsVariables { minPasswdLength = length; } + public static boolean isPwdCheckUpper() { + return pwdCheckUpper; + } + + public static void setPwdCheckUpper(boolean check) { + pwdCheckUpper = check; + } + public static Long getDefaultGroup() { return defaultGroup; } diff --git a/pom.xml b/pom.xml index ec0902a..0c8b6d3 100644 --- a/pom.xml +++ b/pom.xml @@ -641,6 +641,12 @@ <artifactId>jodconverter-local</artifactId> <version>4.3.0</version> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> </dependencies> </dependencyManagement> <dependencies>