From 648d737bf7ebe0258d1dcba9499b80f7e38326ae Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 17 Aug 2023 09:44:22 -0400 Subject: [PATCH] Change password security message (#3057) Returns the error message instead of the validation message because validation message should already have been shown in above case statements. Fix: #3055 Is this a backport? If so, please add backport PR # and/or commits # [Please provide details of testing done: unit testing, integration testing and manual testing] - [ ] New functionality includes testing - [ ] New functionality has been documented - [ ] Commits are signed per the DCO using --signoff By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). --------- Signed-off-by: Craig Perkins Signed-off-by: Derek Ho Co-authored-by: Craig Perkins (cherry picked from commit 847f91108a4a10c8368fde26a8f79266edf423c3) --- .../security/api/CreateResetPasswordTest.java | 108 +++++ .../rest/validation/PasswordValidator.java | 12 + .../validation/RequestContentValidator.java | 376 ++++++++++++++++++ .../security/dlic/rest/api/UserApiTest.java | 9 +- .../validation/PasswordValidatorTest.java | 16 + 5 files changed, 519 insertions(+), 2 deletions(-) create mode 100644 src/integrationTest/java/org/opensearch/security/api/CreateResetPasswordTest.java create mode 100644 src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java diff --git a/src/integrationTest/java/org/opensearch/security/api/CreateResetPasswordTest.java b/src/integrationTest/java/org/opensearch/security/api/CreateResetPasswordTest.java new file mode 100644 index 0000000000..44f8dca20b --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/api/CreateResetPasswordTest.java @@ -0,0 +1,108 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + */ +package org.opensearch.security.api; + +import java.util.List; +import java.util.Map; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.security.dlic.rest.validation.RequestContentValidator; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.test.framework.TestSecurityConfig.User; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; +import org.opensearch.test.framework.cluster.TestRestClient; +import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.opensearch.security.SecurityConfigurationTests.ADDITIONAL_USER_1; +import static org.opensearch.security.SecurityConfigurationTests.CREATE_USER_BODY; +import static org.opensearch.security.SecurityConfigurationTests.INTERNAL_USERS_RESOURCE; +import static org.opensearch.security.support.ConfigConstants.SECURITY_BACKGROUND_INIT_IF_SECURITYINDEX_NOT_EXIST; +import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ROLES_ENABLED; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; +import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class CreateResetPasswordTest { + + private static final User USER_ADMIN = new User("admin").roles(ALL_ACCESS); + + public static final String INVALID_PASSWORD_REGEX = "user 1 fair password"; + + public static final String VALID_WEAK_PASSWORD = "Asdfghjkl1!"; + + public static final String VALID_SIMILAR_PASSWORD = "456Additional00001_1234!"; + + private static final String CUSTOM_PASSWORD_MESSAGE = + "Password must be minimum 5 characters long and must contain at least one uppercase letter, one lowercase letter, one digit, and one special character."; + + private static final String CUSTOM_PASSWORD_REGEX = "(?=.*[A-Z])(?=.*[^a-zA-Z\\d])(?=.*[0-9])(?=.*[a-z]).{5,}"; + + @ClassRule + public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users(USER_ADMIN) + .anonymousAuth(false) + .nodeSettings( + Map.of( + SECURITY_RESTAPI_ROLES_ENABLED, + List.of("user_" + USER_ADMIN.getName() + "__" + ALL_ACCESS.getName()), + SECURITY_BACKGROUND_INIT_IF_SECURITYINDEX_NOT_EXIST, + true, + ConfigConstants.SECURITY_RESTAPI_PASSWORD_VALIDATION_REGEX, + CUSTOM_PASSWORD_REGEX, + ConfigConstants.SECURITY_RESTAPI_PASSWORD_VALIDATION_ERROR_MESSAGE, + CUSTOM_PASSWORD_MESSAGE + ) + ) + .build(); + + @Test + public void shouldValidateCreateUserAPIErrorMessages() { + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse httpResponse = client.putJson( + INTERNAL_USERS_RESOURCE + ADDITIONAL_USER_1, + String.format(CREATE_USER_BODY, INVALID_PASSWORD_REGEX) + ); + + assertThat(httpResponse.getStatusCode(), equalTo(400)); + assertThat(httpResponse.getBody(), containsString(CUSTOM_PASSWORD_MESSAGE)); + } + + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse httpResponse = client.putJson( + INTERNAL_USERS_RESOURCE + ADDITIONAL_USER_1, + String.format(CREATE_USER_BODY, VALID_WEAK_PASSWORD) + ); + + assertThat(httpResponse.getStatusCode(), equalTo(400)); + assertThat(httpResponse.getBody(), containsString(RequestContentValidator.ValidationError.WEAK_PASSWORD.message())); + } + + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse httpResponse = client.putJson( + INTERNAL_USERS_RESOURCE + ADDITIONAL_USER_1, + String.format(CREATE_USER_BODY, VALID_SIMILAR_PASSWORD) + ); + + assertThat(httpResponse.getStatusCode(), equalTo(400)); + assertThat(httpResponse.getBody(), containsString(RequestContentValidator.ValidationError.SIMILAR_PASSWORD.message())); + } + } + +} diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/PasswordValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/PasswordValidator.java index ac521dee8a..52cde5e69b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/PasswordValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/PasswordValidator.java @@ -86,7 +86,11 @@ ErrorType validate(final String username, final String password) { minPasswordLength, password.length() ); +<<<<<<< HEAD return ErrorType.INVALID_PASSWORD; +======= + return RequestContentValidator.ValidationError.INVALID_PASSWORD_TOO_SHORT; +>>>>>>> 847f9110 (Change password security message (#3057)) } if (password.length() > MAX_LENGTH) { logger.debug( @@ -94,11 +98,19 @@ ErrorType validate(final String username, final String password) { MAX_LENGTH, password.length() ); +<<<<<<< HEAD return ErrorType.INVALID_PASSWORD; } if (Objects.nonNull(passwordRegexpPattern) && !passwordRegexpPattern.matcher(password).matches()) { logger.debug("Regex does not match password"); return ErrorType.INVALID_PASSWORD; +======= + return RequestContentValidator.ValidationError.INVALID_PASSWORD_TOO_LONG; + } + if (Objects.nonNull(passwordRegexpPattern) && !passwordRegexpPattern.matcher(password).matches()) { + logger.debug("Regex does not match password"); + return RequestContentValidator.ValidationError.INVALID_PASSWORD_INVALID_REGEX; +>>>>>>> 847f9110 (Change password security message (#3057)) } final Strength strength = zxcvbn.measure(password, ImmutableList.of(username)); if (strength.getScore() < scoreStrength.score()) { diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java new file mode 100644 index 0000000000..4f9309b788 --- /dev/null +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java @@ -0,0 +1,376 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.dlic.rest.validation; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.rest.RestRequest; +import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.ssl.util.Utils; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_PASSWORD_VALIDATION_ERROR_MESSAGE; + +public class RequestContentValidator implements ToXContent { + + public final static RequestContentValidator NOOP_VALIDATOR = new RequestContentValidator(new ValidationContext() { + @Override + public Object[] params() { + return new Object[0]; + } + + @Override + public Settings settings() { + return Settings.EMPTY; + } + + @Override + public Map allowedKeys() { + return Collections.emptyMap(); + } + }) { + @Override + public ValidationResult validate(RestRequest request) { + return ValidationResult.success(DefaultObjectMapper.objectMapper.createObjectNode()); + } + + @Override + public ValidationResult validate(RestRequest request, JsonNode jsonNode) { + return ValidationResult.success(DefaultObjectMapper.objectMapper.createObjectNode()); + } + }; + + public final static String INVALID_KEYS_KEY = "invalid_keys"; + + /* public for testing */ + public final static String MISSING_MANDATORY_KEYS_KEY = "missing_mandatory_keys"; + + /* public for testing */ + public final static String MISSING_MANDATORY_OR_KEYS_KEY = "specify_one_of"; + + protected final static Logger LOGGER = LogManager.getLogger(RequestContentValidator.class); + + public static enum DataType { + STRING, + ARRAY, + OBJECT, + BOOLEAN; + } + + public interface ValidationContext { + + default boolean hasParams() { + return params() != null && params().length > 0; + } + + default Set mandatoryKeys() { + return Collections.emptySet(); + } + + default Set mandatoryOrKeys() { + return Collections.emptySet(); + } + + Object[] params(); + + Settings settings(); + + Map allowedKeys(); + + } + + protected ValidationError validationError; + + protected final ValidationContext validationContext; + + protected final Map wrongDataTypes = new HashMap<>(); + + /** Contain errorneous keys */ + private final Set missingMandatoryKeys = new HashSet<>(); + + private final Set invalidKeys = new HashSet<>(); + + private final Set missingMandatoryOrKeys = new HashSet<>(); + + protected RequestContentValidator(final ValidationContext validationContext) { + this.validationError = ValidationError.NONE; + this.validationContext = validationContext; + } + + public ValidationResult validate(final RestRequest request) throws IOException { + return parseRequestContent(request).map(this::validateContentSize).map(jsonContent -> validate(request, jsonContent)); + } + + public ValidationResult validate(final RestRequest request, final JsonNode jsonContent) throws IOException { + return validateContentSize(jsonContent).map(this::validateJsonKeys) + .map(this::validateDataType) + .map(this::nullValuesInArrayValidator) + .map(ignored -> validatePassword(request, jsonContent)); + } + + private ValidationResult parseRequestContent(final RestRequest request) { + try { + final JsonNode jsonContent = DefaultObjectMapper.readTree(request.content().utf8ToString()); + return ValidationResult.success(jsonContent); + } catch (final IOException ioe) { + LOGGER.error(ValidationError.BODY_NOT_PARSEABLE.message(), ioe); + this.validationError = ValidationError.BODY_NOT_PARSEABLE; + return ValidationResult.error(this); + } + } + + private ValidationResult validateContentSize(final JsonNode jsonContent) { + if (jsonContent.size() == 0) { + this.validationError = ValidationError.PAYLOAD_MANDATORY; + return ValidationResult.error(this); + } + return ValidationResult.success(jsonContent); + } + + private ValidationResult validateJsonKeys(final JsonNode jsonContent) { + final Set requestedKeys = new HashSet<>(); + jsonContent.fieldNames().forEachRemaining(requestedKeys::add); + // mandatory settings, one of ... + if (Collections.disjoint(requestedKeys, validationContext.mandatoryOrKeys())) { + missingMandatoryOrKeys.addAll(validationContext.mandatoryOrKeys()); + } + final Set mandatory = new HashSet<>(validationContext.mandatoryKeys()); + mandatory.removeAll(requestedKeys); + missingMandatoryKeys.addAll(mandatory); + + final Set allowed = new HashSet<>(validationContext.allowedKeys().keySet()); + requestedKeys.removeAll(allowed); + invalidKeys.addAll(requestedKeys); + if (!missingMandatoryKeys.isEmpty() || !invalidKeys.isEmpty() || !missingMandatoryOrKeys.isEmpty()) { + this.validationError = ValidationError.INVALID_CONFIGURATION; + return ValidationResult.error(this); + } + return ValidationResult.success(jsonContent); + } + + private ValidationResult validateDataType(final JsonNode jsonContent) { + try (final JsonParser parser = new JsonFactory().createParser(jsonContent.toString())) { + JsonToken token; + while ((token = parser.nextToken()) != null) { + if (token.equals(JsonToken.FIELD_NAME)) { + String currentName = parser.getCurrentName(); + final DataType dataType = validationContext.allowedKeys().get(currentName); + if (dataType != null) { + JsonToken valueToken = parser.nextToken(); + switch (dataType) { + case STRING: + if (valueToken != JsonToken.VALUE_STRING) { + wrongDataTypes.put(currentName, "String expected"); + } + break; + case ARRAY: + if (valueToken != JsonToken.START_ARRAY && valueToken != JsonToken.END_ARRAY) { + wrongDataTypes.put(currentName, "Array expected"); + } + break; + case OBJECT: + if (!valueToken.equals(JsonToken.START_OBJECT) && !valueToken.equals(JsonToken.END_OBJECT)) { + wrongDataTypes.put(currentName, "Object expected"); + } + break; + } + } + } + } + } catch (final IOException ioe) { + LOGGER.error("Couldn't create JSON for payload {}", jsonContent, ioe); + this.validationError = ValidationError.BODY_NOT_PARSEABLE; + return ValidationResult.error(this); + } + if (!wrongDataTypes.isEmpty()) { + this.validationError = ValidationError.WRONG_DATATYPE; + return ValidationResult.error(this); + } + return ValidationResult.success(jsonContent); + } + + private ValidationResult nullValuesInArrayValidator(final JsonNode jsonContent) { + for (final Map.Entry allowedKey : validationContext.allowedKeys().entrySet()) { + JsonNode value = jsonContent.get(allowedKey.getKey()); + if (value != null) { + if (hasNullArrayElement(value)) { + this.validationError = ValidationError.NULL_ARRAY_ELEMENT; + return ValidationResult.error(this); + } + } + } + return ValidationResult.success(jsonContent); + } + + private boolean hasNullArrayElement(final JsonNode node) { + for (final JsonNode element : node) { + if (element.isNull()) { + if (node.isArray()) { + return true; + } + } else if (element.isContainerNode()) { + if (hasNullArrayElement(element)) { + return true; + } + } + } + return false; + } + + private ValidationResult validatePassword(final RestRequest request, final JsonNode jsonContent) { + if (jsonContent.has("password")) { + final PasswordValidator passwordValidator = PasswordValidator.of(validationContext.settings()); + final String password = jsonContent.get("password").asText(); + if (Strings.isNullOrEmpty(password)) { + this.validationError = ValidationError.INVALID_PASSWORD_TOO_SHORT; + return ValidationResult.error(this); + } + final String username = Utils.coalesce( + request.param("name"), + validationContext.hasParams() ? (String) validationContext.params()[0] : null + ); + if (Strings.isNullOrEmpty(username)) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Unable to validate username because no user is given"); + } + this.validationError = ValidationError.NO_USERNAME; + return ValidationResult.error(this); + } + this.validationError = passwordValidator.validate(username, password); + if (this.validationError != ValidationError.NONE) { + return ValidationResult.error(this); + } + } + return ValidationResult.success(jsonContent); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { + builder.startObject(); + switch (this.validationError) { + case INVALID_CONFIGURATION: + builder.field("status", "error"); + builder.field("reason", ValidationError.INVALID_CONFIGURATION.message()); + addErrorMessage(builder, INVALID_KEYS_KEY, invalidKeys); + addErrorMessage(builder, MISSING_MANDATORY_KEYS_KEY, missingMandatoryKeys); + addErrorMessage(builder, MISSING_MANDATORY_OR_KEYS_KEY, missingMandatoryOrKeys); + break; + case INVALID_PASSWORD_INVALID_REGEX: + builder.field("status", "error"); + builder.field( + "reason", + validationContext.settings() + .get(SECURITY_RESTAPI_PASSWORD_VALIDATION_ERROR_MESSAGE, "Password does not match minimum criteria") + ); + break; + case WRONG_DATATYPE: + builder.field("status", "error"); + builder.field("reason", ValidationError.WRONG_DATATYPE.message()); + for (Map.Entry entry : wrongDataTypes.entrySet()) { + builder.field(entry.getKey(), entry.getValue()); + } + break; + default: + builder.field("status", "error"); + builder.field("reason", validationError.message()); + break; + } + builder.endObject(); + return builder; + } + + private void addErrorMessage(final XContentBuilder builder, final String message, final Set keys) throws IOException { + if (!keys.isEmpty()) { + builder.startObject(message).field("keys", String.join(",", keys)).endObject(); + } + } + + public static RequestContentValidator of(final ValidationContext validationContext) { + return new RequestContentValidator(new ValidationContext() { + + private final Object[] params = validationContext.params(); + + private final Set mandatoryKeys = validationContext.mandatoryKeys(); + + private final Set mandatoryOrKeys = validationContext.mandatoryOrKeys(); + + private final Map allowedKeys = validationContext.allowedKeys(); + + @Override + public Settings settings() { + return validationContext.settings(); + } + + @Override + public Object[] params() { + return params; + } + + @Override + public Set mandatoryKeys() { + return mandatoryKeys; + } + + @Override + public Set mandatoryOrKeys() { + return mandatoryOrKeys; + } + + @Override + public Map allowedKeys() { + return allowedKeys; + } + }); + } + + public enum ValidationError { + NONE("ok"), + INVALID_CONFIGURATION("Invalid configuration"), + INVALID_PASSWORD_TOO_SHORT("Password is too short"), + INVALID_PASSWORD_TOO_LONG("Password is too long"), + INVALID_PASSWORD_INVALID_REGEX("Password does not match validation regex"), + NO_USERNAME("No username is given"), + WEAK_PASSWORD("Weak password"), + SIMILAR_PASSWORD("Password is similar to user name"), + WRONG_DATATYPE("Wrong datatype"), + BODY_NOT_PARSEABLE("Could not parse content of request."), + PAYLOAD_NOT_ALLOWED("Request body not allowed for this action."), + PAYLOAD_MANDATORY("Request body required for this action."), + SECURITY_NOT_INITIALIZED("Security index not initialized"), + NULL_ARRAY_ELEMENT("`null` is not allowed as json array element"); + + private final String message; + + private ValidationError(String message) { + this.message = message; + } + + public String message() { + return message; + } + + } +} diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/UserApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/UserApiTest.java index 23f6065f34..ac3552f42f 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/api/UserApiTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/api/UserApiTest.java @@ -660,7 +660,7 @@ public void testRegExpPasswordRules() throws Exception { verifyCouldNotCreatePasswords(HttpStatus.SC_BAD_REQUEST); verifyCanCreatePasswords(); - verifySimilarity("xxx"); + verifySimilarity(RequestContentValidator.ValidationError.SIMILAR_PASSWORD.message()); addUserWithPasswordAndHash("empty_password", "", "$%^123", HttpStatus.SC_BAD_REQUEST); addUserWithPasswordAndHash("null_password", null, "$%^123", HttpStatus.SC_BAD_REQUEST); @@ -803,7 +803,12 @@ public void testScoreBasedPasswordRules() throws Exception { AbstractConfigurationValidator.ErrorType.WEAK_PASSWORD.getMessage() ); - addUserWithPassword("admin", "pas", HttpStatus.SC_BAD_REQUEST, "Password does not match minimum criteria"); + addUserWithPassword( + "admin", + "pas", + HttpStatus.SC_BAD_REQUEST, + RequestContentValidator.ValidationError.INVALID_PASSWORD_TOO_SHORT.message() + ); verifySimilarity(AbstractConfigurationValidator.ErrorType.SIMILAR_PASSWORD.getMessage()); diff --git a/src/test/java/org/opensearch/security/dlic/rest/validation/PasswordValidatorTest.java b/src/test/java/org/opensearch/security/dlic/rest/validation/PasswordValidatorTest.java index 7ea6f23898..fcb96c7335 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/validation/PasswordValidatorTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/validation/PasswordValidatorTest.java @@ -126,12 +126,21 @@ public void testRegExpBasedValidation() { .put(SECURITY_RESTAPI_PASSWORD_VALIDATION_REGEX, "(?=.*[A-Z])(?=.*[^a-zA-Z\\\\d])(?=.*[0-9])(?=.*[a-z]).{8,}") .build() ); +<<<<<<< HEAD verifyWeakPasswords(passwordValidator, AbstractConfigurationValidator.ErrorType.INVALID_PASSWORD); verifyFairPasswords(passwordValidator, AbstractConfigurationValidator.ErrorType.INVALID_PASSWORD); for (final String password : GOOD_PASSWORDS.subList(0, GOOD_PASSWORDS.size() - 2)) assertEquals( password, AbstractConfigurationValidator.ErrorType.INVALID_PASSWORD, +======= + verifyWeakPasswords(passwordValidator, RequestContentValidator.ValidationError.INVALID_PASSWORD_INVALID_REGEX); + verifyFairPasswords(passwordValidator, RequestContentValidator.ValidationError.INVALID_PASSWORD_INVALID_REGEX); + for (final String password : GOOD_PASSWORDS.subList(0, GOOD_PASSWORDS.size() - 2)) + assertEquals( + password, + RequestContentValidator.ValidationError.INVALID_PASSWORD_INVALID_REGEX, +>>>>>>> 847f9110 (Change password security message (#3057)) passwordValidator.validate("some_user_name", password) ); for (final String password : GOOD_PASSWORDS.subList(GOOD_PASSWORDS.size() - 2, GOOD_PASSWORDS.size())) @@ -151,7 +160,14 @@ public void testMinLength() { Settings.builder().put(SECURITY_RESTAPI_PASSWORD_MIN_LENGTH, 15).build() ); for (final String password : STRONG_PASSWORDS) { +<<<<<<< HEAD assertEquals(AbstractConfigurationValidator.ErrorType.INVALID_PASSWORD, passwordValidator.validate(password, "some_user_name")); +======= + assertEquals( + RequestContentValidator.ValidationError.INVALID_PASSWORD_TOO_SHORT, + passwordValidator.validate(password, "some_user_name") + ); +>>>>>>> 847f9110 (Change password security message (#3057)) } }