From 117ce1aac42b87eb8922a8c53c0d06bcef6070f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Jandre?= <48719461+JoaoJandre@users.noreply.github.com> Date: Wed, 3 Aug 2022 11:09:00 -0300 Subject: [PATCH] Create password policies configurations (#6567) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Paraquetti --- .../com/cloud/user/AccountManagerImpl.java | 8 + .../java/com/cloud/user/PasswordPolicy.java | 91 +++++++ .../com/cloud/user/PasswordPolicyImpl.java | 245 ++++++++++++++++++ .../spring-server-core-managers-context.xml | 2 + .../cloud/user/AccountManagerImplTest.java | 40 +++ .../cloud/user/PasswordPolicyImplTest.java | 163 ++++++++++++ 6 files changed, 549 insertions(+) create mode 100644 server/src/main/java/com/cloud/user/PasswordPolicy.java create mode 100644 server/src/main/java/com/cloud/user/PasswordPolicyImpl.java create mode 100644 server/src/test/java/com/cloud/user/PasswordPolicyImplTest.java diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 2945580effe..3c1e2618df9 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -301,6 +301,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Inject private RoleService roleService; + @Inject + private PasswordPolicy passwordPolicy; + private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AccountChecker")); private int _allowedLoginAttempts; @@ -1368,6 +1371,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M if (StringUtils.isBlank(newPassword)) { throw new InvalidParameterValueException("Password cannot be empty or blank."); } + + passwordPolicy.verifyIfPasswordCompliesWithPasswordPolicies(newPassword, user.getUsername(), getAccount(user.getAccountId()).getDomainId()); + Account callingAccount = getCurrentCallingAccount(); boolean isRootAdminExecutingPasswordUpdate = callingAccount.getId() == Account.ACCOUNT_ID_SYSTEM || isRootAdmin(callingAccount.getId()); boolean isDomainAdmin = isDomainAdmin(callingAccount.getId()); @@ -2342,6 +2348,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M s_logger.debug("Creating user: " + userName + ", accountId: " + accountId + " timezone:" + timezone); } + passwordPolicy.verifyIfPasswordCompliesWithPasswordPolicies(password, userName, getAccount(accountId).getDomainId()); + String encodedPassword = null; for (UserAuthenticator authenticator : _userPasswordEncoders) { encodedPassword = authenticator.encode(password); diff --git a/server/src/main/java/com/cloud/user/PasswordPolicy.java b/server/src/main/java/com/cloud/user/PasswordPolicy.java new file mode 100644 index 00000000000..42ef7b475ef --- /dev/null +++ b/server/src/main/java/com/cloud/user/PasswordPolicy.java @@ -0,0 +1,91 @@ +// 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 com.cloud.user; + +import org.apache.cloudstack.framework.config.ConfigKey; + +public interface PasswordPolicy { + + ConfigKey PasswordPolicyMinimumSpecialCharacters = new ConfigKey<>( + "Advanced", + Integer.class, + "password.policy.minimum.special.characters", + "0", + "Minimum number of special characters that the user's password must have. The value 0 means the user's password does not require any special characters.", + true, + ConfigKey.Scope.Domain); + + ConfigKey PasswordPolicyMinimumLength = new ConfigKey<>( + "Advanced", + Integer.class, + "password.policy.minimum.length", + "0", + "Minimum length that the user's password must have. The value 0 means the user's password can have any length.", + true, + ConfigKey.Scope.Domain); + + ConfigKey PasswordPolicyMinimumUppercaseLetters = new ConfigKey<>( + "Advanced", + Integer.class, + "password.policy.minimum.uppercase.letters", + "0", + "Minimum number of uppercase letters that the user's password must have. The value 0 means the user's password does not require any uppercase letters.", + true, + ConfigKey.Scope.Domain); + + ConfigKey PasswordPolicyMinimumLowercaseLetters = new ConfigKey<>( + "Advanced", + Integer.class, + "password.policy.minimum.lowercase.letters", + "0", + "Minimum number of lowercase letters that the user's password must have. The value 0 means the user's password does not require any lowercase letters.", + true, + ConfigKey.Scope.Domain); + + ConfigKey PasswordPolicyMinimumDigits = new ConfigKey<>( + "Advanced", + Integer.class, + "password.policy.minimum.digits", + "0", + "Minimum number of digits that the user's password must have. The value 0 means the user's password does not require any digits.", + true, + ConfigKey.Scope.Domain); + + ConfigKey PasswordPolicyAllowPasswordToContainUsername = new ConfigKey<>( + "Advanced", + Boolean.class, + "password.policy.allowPasswordToContainUsername", + "true", + "Indicates if the user's password may contain their username. Set 'true' (default) if it is allowed, otherwise set 'false'.", + true, + ConfigKey.Scope.Domain); + + ConfigKey PasswordPolicyRegex = new ConfigKey<>( + "Advanced", + String.class, + "password.policy.regex", + ".+", + "A regular expression that the user's password must match. The default expression '.+' will match with any password.", + true, + ConfigKey.Scope.Domain); + + /** + * Checks if a given user's password complies with the configured password policies. + * If it does not comply, a {@link com.cloud.exception.InvalidParameterValueException} will be thrown. + * */ + void verifyIfPasswordCompliesWithPasswordPolicies(String password, String username, Long domainID); +} diff --git a/server/src/main/java/com/cloud/user/PasswordPolicyImpl.java b/server/src/main/java/com/cloud/user/PasswordPolicyImpl.java new file mode 100644 index 00000000000..40eec5674cd --- /dev/null +++ b/server/src/main/java/com/cloud/user/PasswordPolicyImpl.java @@ -0,0 +1,245 @@ +// 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 com.cloud.user; + +import com.cloud.exception.InvalidParameterValueException; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; + +public class PasswordPolicyImpl implements PasswordPolicy, Configurable { + + private Logger logger = Logger.getLogger(PasswordPolicyImpl.class); + + public void verifyIfPasswordCompliesWithPasswordPolicies(String password, String username, Long domainId) { + int numberOfSpecialCharactersInPassword = 0; + int numberOfUppercaseLettersInPassword = 0; + int numberOfLowercaseLettersInPassword = 0; + int numberOfDigitsInPassword = 0; + + char[] splitPassword = password.toCharArray(); + + + for (char character: splitPassword) { + if (!Character.isLetterOrDigit(character)) { + numberOfSpecialCharactersInPassword++; + } else if (Character.isUpperCase(character)) { + numberOfUppercaseLettersInPassword++; + } else if (Character.isLowerCase(character)) { + numberOfLowercaseLettersInPassword++; + } else if (Character.isDigit(character)) { + numberOfDigitsInPassword++; + } + } + + validateIfPasswordContainsTheMinimumNumberOfSpecialCharacters(numberOfSpecialCharactersInPassword, username, domainId); + validateIfPasswordContainsTheMinimumNumberOfUpperCaseLetters(numberOfUppercaseLettersInPassword, username, domainId); + validateIfPasswordContainsTheMinimumNumberOfLowerCaseLetters(numberOfLowercaseLettersInPassword, username, domainId); + validateIfPasswordContainsTheMinimumNumberOfDigits(numberOfDigitsInPassword, username, domainId); + validateIfPasswordContainsTheMinimumLength(password, username, domainId); + validateIfPasswordContainsTheUsername(password, username, domainId); + validateIfPasswordMatchesRegex(password, username, domainId); + } + + protected void validateIfPasswordContainsTheMinimumNumberOfSpecialCharacters(int numberOfSpecialCharactersInPassword, String username, Long domainId) { + Integer passwordPolicyMinimumSpecialCharacters = getPasswordPolicyMinimumSpecialCharacters(domainId); + + logger.trace(String.format("Validating if the new password for user [%s] contains the minimum number of special characters [%s] defined in the configuration [%s].", + username, passwordPolicyMinimumSpecialCharacters, PasswordPolicyMinimumSpecialCharacters.key())); + + if (passwordPolicyMinimumSpecialCharacters == 0) { + logger.trace(String.format("The minimum number of special characters for a user's password is 0; therefore, we will not validate the number of special characters for" + + " the new password of user [%s].", username)); + return; + } + + if (numberOfSpecialCharactersInPassword < passwordPolicyMinimumSpecialCharacters) { + logger.error(String.format("User [%s] informed [%d] special characters for their new password; however, the minimum number of special characters is [%d]. " + + "Refusing the user's new password.", username, numberOfSpecialCharactersInPassword, passwordPolicyMinimumSpecialCharacters)); + throw new InvalidParameterValueException(String.format("User password should contain at least [%d] special characters.", passwordPolicyMinimumSpecialCharacters)); + } + + logger.trace(String.format("The new password for user [%s] complies with the policy of minimum special characters [%s].", username, + PasswordPolicyMinimumSpecialCharacters.key())); + } + + protected void validateIfPasswordContainsTheMinimumNumberOfUpperCaseLetters(int numberOfUppercaseLettersInPassword, String username, Long domainId) { + Integer passwordPolicyMinimumUpperCaseLetters = getPasswordPolicyMinimumUpperCaseLetters(domainId); + + logger.trace(String.format("Validating if the new password for user [%s] contains the minimum number of upper case letters [%s] defined in the configuration [%s].", + username, passwordPolicyMinimumUpperCaseLetters, PasswordPolicyMinimumUppercaseLetters.key())); + + if (passwordPolicyMinimumUpperCaseLetters == 0) { + logger.trace(String.format("The minimum number of upper case letters for a user's password is 0; therefore, we will not validate the number of upper case letters for" + + " the new password of user [%s].", username)); + return; + } + + if (numberOfUppercaseLettersInPassword < passwordPolicyMinimumUpperCaseLetters) { + logger.error(String.format("User [%s] informed [%d] upper case letters for their new password; however, the minimum number of upper case letters is [%d]. " + + "Refusing the user's new password.", username, numberOfUppercaseLettersInPassword, passwordPolicyMinimumUpperCaseLetters)); + throw new InvalidParameterValueException(String.format("User password should contain at least [%d] upper case letters.", passwordPolicyMinimumUpperCaseLetters)); + } + + logger.trace(String.format("The new password for user [%s] complies with the policy of minimum upper case letters [%s].", username, + PasswordPolicyMinimumUppercaseLetters.key())); + } + + protected void validateIfPasswordContainsTheMinimumNumberOfLowerCaseLetters(int numberOfLowercaseLettersInPassword, String username, Long domainId) { + Integer passwordPolicyMinimumLowerCaseLetters = getPasswordPolicyMinimumLowerCaseLetters(domainId); + + logger.trace(String.format("Validating if the new password for user [%s] contains the minimum number of lower case letters [%s] defined in the configuration [%s].", + username, passwordPolicyMinimumLowerCaseLetters, PasswordPolicyMinimumLowercaseLetters.key())); + + if (passwordPolicyMinimumLowerCaseLetters == 0) { + logger.trace(String.format("The minimum number of lower case letters for a user's password is 0; therefore, we will not validate the number of lower case letters for" + + " the new password of user [%s].", username)); + return; + } + + if (numberOfLowercaseLettersInPassword < passwordPolicyMinimumLowerCaseLetters) { + logger.error(String.format("User [%s] informed [%d] lower case letters for their new password; however, the minimum number of lower case letters is [%d]. " + + "Refusing the user's new password.", username, numberOfLowercaseLettersInPassword, passwordPolicyMinimumLowerCaseLetters)); + throw new InvalidParameterValueException(String.format("User password should contain at least [%d] lower case letters.", passwordPolicyMinimumLowerCaseLetters)); + } + + logger.trace(String.format("The new password for user [%s] complies with the policy of minimum lower case letters [%s].", username, + PasswordPolicyMinimumLowercaseLetters.key())); + } + + protected void validateIfPasswordContainsTheMinimumNumberOfDigits(int numberOfDigitsInPassword, String username, Long domainId) { + Integer passwordPolicyMinimumDigits = getPasswordPolicyMinimumDigits(domainId); + + logger.trace(String.format("Validating if the new password for user [%s] contains the minimum number of digits [%s] defined in the configuration [%s].", + username, passwordPolicyMinimumDigits, PasswordPolicyMinimumDigits.key())); + + if (passwordPolicyMinimumDigits == 0) { + logger.trace(String.format("The minimum number of digits for a user's password is 0; therefore, we will not validate the number of digits for the new password of" + + " user [%s].", username)); + return; + } + + if (numberOfDigitsInPassword < passwordPolicyMinimumDigits) { + logger.error(String.format("User [%s] informed [%d] digits for their new password; however, the minimum number of digits is [%d]. " + + "Refusing the user's new password.", username, numberOfDigitsInPassword, passwordPolicyMinimumDigits)); + throw new InvalidParameterValueException(String.format("User password should contain at least [%d] digits.", passwordPolicyMinimumDigits)); + } + + logger.trace(String.format("The new password for user [%s] complies with the policy of minimum digits [%s].", username, PasswordPolicyMinimumDigits.key())); + } + + protected void validateIfPasswordContainsTheMinimumLength(String password, String username, Long domainId) { + Integer passwordPolicyMinimumLength = getPasswordPolicyMinimumLength(domainId); + + logger.trace(String.format("Validating if the new password for user [%s] contains the minimum length [%s] defined in the configuration [%s].", username, + passwordPolicyMinimumLength, PasswordPolicyMinimumLength.key())); + + if (passwordPolicyMinimumLength == 0) { + logger.trace(String.format("The minimum length of a user's password is 0; therefore, we will not validate the length of the new password of user [%s].", username)); + return; + } + + Integer passwordLength = password.length(); + if (passwordLength < passwordPolicyMinimumLength) { + logger.error(String.format("User [%s] informed [%d] characters for their new password; however, the minimum password length is [%d]. Refusing the user's new password.", + username, passwordLength, passwordPolicyMinimumLength)); + throw new InvalidParameterValueException(String.format("User password should contain at least [%d] characters.", passwordPolicyMinimumLength)); + } + + logger.trace(String.format("The new password for user [%s] complies with the policy of minimum length [%s].", username, PasswordPolicyMinimumLength.key())); + } + + protected void validateIfPasswordContainsTheUsername(String password, String username, Long domainId) { + logger.trace(String.format("Validating if the new password for user [%s] contains their username.", username)); + + if (getPasswordPolicyAllowPasswordToContainUsername(domainId)) { + logger.trace(String.format("Allow password to contain username is true; therefore, we will not validate if the password contains the username of user [%s].", username)); + return; + } + + if (StringUtils.containsIgnoreCase(password, username)) { + logger.error(String.format("User [%s] informed a new password that contains their username; however, the this is not allowed as configured in [%s]. " + + "Refusing the user's new password.", username, PasswordPolicyAllowPasswordToContainUsername.key())); + throw new InvalidParameterValueException("User password should not contain their username."); + } + + logger.trace(String.format("The new password for user [%s] complies with the policy of allowing passwords to contain username [%s].", username, + PasswordPolicyAllowPasswordToContainUsername.key())); + } + + protected void validateIfPasswordMatchesRegex(String password, String username, Long domainId) { + String passwordPolicyRegex = getPasswordPolicyRegex(domainId); + + logger.trace(String.format("Validating if the new password for user [%s] matches regex [%s] defined in the configuration [%s].", + username, passwordPolicyRegex, PasswordPolicyRegex.key())); + + if (passwordPolicyRegex == null){ + logger.trace(String.format("Regex is null; therefore, we will not validate if the new password matches with regex for user [%s].", username)); + return; + } + + if (!password.matches(passwordPolicyRegex)){ + logger.error(String.format("User [%s] informed a new password that does not match with regex [%s]. Refusing the user's new password.", username, passwordPolicyRegex)); + throw new InvalidParameterValueException("User password does not match with password policy regex."); + } + + logger.trace(String.format("The new password for user [%s] complies with the policy of matching regex [%s].", username, + PasswordPolicyRegex.key())); + } + + @Override + public String getConfigComponentName() { + return PasswordPolicyImpl.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{PasswordPolicyMinimumLength, PasswordPolicyMinimumSpecialCharacters, PasswordPolicyMinimumUppercaseLetters, PasswordPolicyMinimumLowercaseLetters, + PasswordPolicyMinimumDigits, PasswordPolicyAllowPasswordToContainUsername, PasswordPolicyRegex + }; + } + + public Integer getPasswordPolicyMinimumLength(Long domainId) { + return PasswordPolicyMinimumLength.valueIn(domainId); + } + + public Integer getPasswordPolicyMinimumSpecialCharacters(Long domainId) { + return PasswordPolicyMinimumSpecialCharacters.valueIn(domainId); + } + + public Integer getPasswordPolicyMinimumUpperCaseLetters(Long domainId) { + return PasswordPolicyMinimumUppercaseLetters.valueIn(domainId); + } + + public Integer getPasswordPolicyMinimumLowerCaseLetters(Long domainId) { + return PasswordPolicyMinimumLowercaseLetters.valueIn(domainId); + } + + public Integer getPasswordPolicyMinimumDigits(Long domainId) { + return PasswordPolicyMinimumDigits.valueIn(domainId); + } + + public Boolean getPasswordPolicyAllowPasswordToContainUsername(Long domainId) { + return PasswordPolicyAllowPasswordToContainUsername.valueIn(domainId); + } + + public String getPasswordPolicyRegex(Long domainId) { + return PasswordPolicyRegex.valueIn(domainId); + } + +} diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index 38cdf3c99bd..9c0e9a125f9 100644 --- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -52,6 +52,8 @@ + + (); UserAuthenticator authenticatorMock1 = Mockito.mock(UserAuthenticator.class); diff --git a/server/src/test/java/com/cloud/user/PasswordPolicyImplTest.java b/server/src/test/java/com/cloud/user/PasswordPolicyImplTest.java new file mode 100644 index 00000000000..dc3b7624fc4 --- /dev/null +++ b/server/src/test/java/com/cloud/user/PasswordPolicyImplTest.java @@ -0,0 +1,163 @@ +// 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 com.cloud.user; + +import com.cloud.exception.InvalidParameterValueException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class PasswordPolicyImplTest { + + @Spy + private PasswordPolicyImpl passwordPolicySpy; + + @Test + public void validateSpecialCharactersTestWithEnoughSpecialCharacters() { + + Mockito.doReturn(3).when(passwordPolicySpy).getPasswordPolicyMinimumSpecialCharacters(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfSpecialCharacters(3, "user", null); + } + + @Test (expected = InvalidParameterValueException.class) + public void validateSpecialCharactersTestWithoutEnoughSpecialCharacters() { + Mockito.doReturn(4).when(passwordPolicySpy).getPasswordPolicyMinimumSpecialCharacters(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfSpecialCharacters(3, "user", null); + } + + @Test + public void validateSpecialCharactersTestWithNoMinimumSpecialCharacters() { + Mockito.doReturn(0).when(passwordPolicySpy).getPasswordPolicyMinimumSpecialCharacters(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfSpecialCharacters(3, "user", null); + } + + @Test + public void validateUpperCaseLettersTestWithEnoughUpperCaseLetters() { + Mockito.doReturn(3).when(passwordPolicySpy).getPasswordPolicyMinimumUpperCaseLetters(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfUpperCaseLetters(3, "user", null); + } + + @Test (expected = InvalidParameterValueException.class) + public void validateUpperCaseLettersTestWithoutEnoughUpperCaseLetters() { + Mockito.doReturn(4).when(passwordPolicySpy).getPasswordPolicyMinimumUpperCaseLetters(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfUpperCaseLetters(3, "user", null); + } + + @Test + public void validateUpperCaseLettersTestWithNoMinimumUpperCaseLetters() { + Mockito.doReturn(0).when(passwordPolicySpy).getPasswordPolicyMinimumUpperCaseLetters(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfUpperCaseLetters(3, "user", null); + } + + @Test + public void validateLowerCaseLettersTestWithEnoughLowerCaseLetters() { + Mockito.doReturn(3).when(passwordPolicySpy).getPasswordPolicyMinimumLowerCaseLetters(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfLowerCaseLetters(3, "user", null); + } + + @Test (expected = InvalidParameterValueException.class) + public void validateLowerCaseLettersTestWithoutEnoughLowerCaseLetters() { + Mockito.doReturn(4).when(passwordPolicySpy).getPasswordPolicyMinimumLowerCaseLetters(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfLowerCaseLetters(3, "user", null); + } + + @Test + public void validateLowerCaseLettersTestWithNoMinimumLowerCaseLetters() { + Mockito.doReturn(0).when(passwordPolicySpy).getPasswordPolicyMinimumLowerCaseLetters(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfLowerCaseLetters(3, "user", null); + } + + @Test + public void validateDigitsTestWithEnoughDigits() { + Mockito.doReturn(3).when(passwordPolicySpy).getPasswordPolicyMinimumDigits(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfDigits(3, "user", null); + } + + @Test (expected = InvalidParameterValueException.class) + public void validateDigitsTestWithoutDigits() { + Mockito.doReturn(4).when(passwordPolicySpy).getPasswordPolicyMinimumDigits(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfDigits(3, "user", null); + } + + @Test + public void validateDigitsTestWithNoMinimumDigits() { + Mockito.doReturn(0).when(passwordPolicySpy).getPasswordPolicyMinimumDigits(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumNumberOfDigits(3, "user", null); + } + + @Test + public void validateMinimumLengthTestWithEnoughMinimumLength() { + Mockito.doReturn(5).when(passwordPolicySpy).getPasswordPolicyMinimumLength(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumLength("12345", "user", null); + } + + @Test (expected = InvalidParameterValueException.class) + public void validateMinimumLengthTestWithoutMinimumLength() { + Mockito.doReturn(5).when(passwordPolicySpy).getPasswordPolicyMinimumLength(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumLength("1234", "user", null); + } + + @Test + public void validateMinimumLengthTestWithNoMinimumLength() { + Mockito.doReturn(0).when(passwordPolicySpy).getPasswordPolicyMinimumLength(null); + passwordPolicySpy.validateIfPasswordContainsTheMinimumLength("123456", "user", null); + } + + @Test + public void validateUsernameInPasswordTestUsernameInPasswordAllowedAndWithoutUsernameInPassword() { + Mockito.doReturn(true).when(passwordPolicySpy).getPasswordPolicyAllowPasswordToContainUsername(null); + passwordPolicySpy.validateIfPasswordContainsTheUsername("123456", "user", null); + } + + @Test + public void validateUsernameInPasswordTestUsernameInPasswordAllowedAndWithUsernameInPassword() { + Mockito.doReturn(true).when(passwordPolicySpy).getPasswordPolicyAllowPasswordToContainUsername(null); + passwordPolicySpy.validateIfPasswordContainsTheUsername("user123", "user", null); + } + + @Test + public void validateUsernameInPasswordTestUsernameInPasswordNotAllowedAndWithoutUsernameInPassword() { + Mockito.doReturn(false).when(passwordPolicySpy).getPasswordPolicyAllowPasswordToContainUsername(null); + passwordPolicySpy.validateIfPasswordContainsTheUsername("123456", "user", null); + } + + @Test (expected = InvalidParameterValueException.class) + public void validateUsernameInPasswordTestUsernameInPasswordNotAllowedAndWithUsernameInPassword() { + Mockito.doReturn(false).when(passwordPolicySpy).getPasswordPolicyAllowPasswordToContainUsername(null); + passwordPolicySpy.validateIfPasswordContainsTheUsername("user123", "user", null); + } + + @Test + public void validateRegexTestWithRegexAndComplyingPassword() { + Mockito.doReturn("[a-z]+").when(passwordPolicySpy).getPasswordPolicyRegex(null); + passwordPolicySpy.validateIfPasswordMatchesRegex("abcd", "user", null); + } + + @Test (expected = InvalidParameterValueException.class) + public void validateRegexTestWithRegexAndWithoutComplyingPassword() { + Mockito.doReturn("[a-z]+").when(passwordPolicySpy).getPasswordPolicyRegex(null); + passwordPolicySpy.validateIfPasswordMatchesRegex("abcd123", "user", null); + } + + @Test + public void validateRegexTestWithoutRegex() { + Mockito.doReturn(null).when(passwordPolicySpy).getPasswordPolicyRegex(null); + passwordPolicySpy.validateIfPasswordMatchesRegex("abcd123", "user", null); + } + +}