diff --git a/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/model/handlers/PasswordValidationConfigurationHandler.java b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/model/handlers/PasswordValidationConfigurationHandler.java index 1b3eabee79e3..81f910582a5a 100644 --- a/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/model/handlers/PasswordValidationConfigurationHandler.java +++ b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/model/handlers/PasswordValidationConfigurationHandler.java @@ -73,7 +73,7 @@ public ValidationConfiguration getDefaultValidationConfiguration(String tenantDo if (isRuleBasedValidationByDefault()) { rules.add(getRuleConfig(LengthValidator.class.getSimpleName(), MIN_LENGTH, "8")); - rules.add(getRuleConfig(LengthValidator.class.getSimpleName(), MAX_LENGTH, "30")); + rules.add(getRuleConfig(LengthValidator.class.getSimpleName(), MAX_LENGTH, "64")); rules.add(getRuleConfig(NumeralValidator.class.getSimpleName(), MIN_LENGTH, "1")); rules.add(getRuleConfig(UpperCaseValidator.class.getSimpleName(), MIN_LENGTH, "1")); rules.add(getRuleConfig(LowerCaseValidator.class.getSimpleName(), MIN_LENGTH, "1")); @@ -85,7 +85,7 @@ public ValidationConfiguration getDefaultValidationConfiguration(String tenantDo } } else { rules.add(getRuleConfig(LengthValidator.class.getSimpleName(), MIN_LENGTH, "8")); - rules.add(getRuleConfig(LengthValidator.class.getSimpleName(), MAX_LENGTH, "30")); + rules.add(getRuleConfig(LengthValidator.class.getSimpleName(), MAX_LENGTH, "64")); rules.add(getRuleConfig(NumeralValidator.class.getSimpleName(), MIN_LENGTH, "1")); rules.add(getRuleConfig(UpperCaseValidator.class.getSimpleName(), MIN_LENGTH, "1")); rules.add(getRuleConfig(LowerCaseValidator.class.getSimpleName(), MIN_LENGTH, "1")); diff --git a/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/model/validators/AbstractRulesValidator.java b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/model/validators/AbstractRulesValidator.java index 59a6f6ffae82..00ee1868d36e 100644 --- a/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/model/validators/AbstractRulesValidator.java +++ b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/model/validators/AbstractRulesValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2022-2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -22,6 +22,7 @@ import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.input.validation.mgt.exceptions.InputValidationMgtClientException; import org.wso2.carbon.identity.input.validation.mgt.model.Property; import org.wso2.carbon.identity.input.validation.mgt.model.ValidationContext; @@ -33,11 +34,14 @@ import java.util.stream.Collectors; import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.Configs.MAX_LENGTH; +import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.Configs.MAX_PASSWORD_ALLOWED_LENGTH; import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.Configs.MIN_LENGTH; +import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.Configs.PASSWORD; import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.ErrorMessages.ERROR_DEFAULT_MIN_MAX_MISMATCH; import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.ErrorMessages.ERROR_INVALID_VALIDATOR_PROPERTY_VALUE; import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.ErrorMessages.ERROR_PROPERTY_NOT_SUPPORTED; import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.ErrorMessages.ERROR_PROPERTY_TYPE_MISMATCH; +import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.ErrorMessages.ERROR_VALIDATION_MAX_LENGTH_MISMATCH; import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.SUPPORTED_PARAMS; /** @@ -93,6 +97,21 @@ public boolean validateProps(ValidationContext context) throws InputValidationMg String.format(ERROR_DEFAULT_MIN_MAX_MISMATCH.getDescription(), this.getClass().getSimpleName(), properties.get(MIN_LENGTH), properties.get(MAX_LENGTH))); } + + // Validate the max length for the password field. + if (PASSWORD.equals(context.getField())) { + int maxPasswordValue = Integer.parseInt(IdentityUtil.getProperty(MAX_PASSWORD_ALLOWED_LENGTH)); + if (properties.get(MAX_LENGTH) != null && + Integer.parseInt(properties.get(MAX_LENGTH)) > maxPasswordValue) { + if (log.isDebugEnabled()) { + log.debug(String.format("The property %s should be less than or equal to %s for the tenant %s.", + MAX_LENGTH, maxPasswordValue, context.getTenantDomain())); + } + throw new InputValidationMgtClientException(ERROR_VALIDATION_MAX_LENGTH_MISMATCH.getCode(), + String.format(ERROR_VALIDATION_MAX_LENGTH_MISMATCH.getDescription(), PASSWORD, maxPasswordValue, + context.getTenantDomain())); + } + } return true; } diff --git a/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/utils/Constants.java b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/utils/Constants.java index f5c71c629c74..862bab71065e 100644 --- a/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/utils/Constants.java +++ b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/main/java/org/wso2/carbon/identity/input/validation/mgt/utils/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2022-2024, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -69,6 +69,7 @@ public static class Configs { public static final String MAX_CONSECUTIVE_CHR = "max.consecutive.character"; public static final String ENABLE_VALIDATOR = "enable.validator"; public static final String ENABLE_SPECIAL_CHARACTERS = "enable.special.characters"; + public static final String MAX_PASSWORD_ALLOWED_LENGTH = "PasswordPolicy.MaxPasswordAllowedLength"; // Keys for password regEx validation. public static final String JS_REGEX = "regex"; diff --git a/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/test/java/org/wso2/carbon/identity/input/validation/mgt/test/model/validators/AbstractRulesValidatorTest.java b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/test/java/org/wso2/carbon/identity/input/validation/mgt/test/model/validators/AbstractRulesValidatorTest.java new file mode 100644 index 000000000000..c76349cf85d6 --- /dev/null +++ b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/test/java/org/wso2/carbon/identity/input/validation/mgt/test/model/validators/AbstractRulesValidatorTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. 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.wso2.carbon.identity.input.validation.mgt.test.model.validators; + +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.input.validation.mgt.exceptions.InputValidationMgtClientException; +import org.wso2.carbon.identity.input.validation.mgt.model.ValidationContext; +import org.wso2.carbon.identity.input.validation.mgt.model.validators.AbstractRulesValidator; +import org.wso2.carbon.identity.input.validation.mgt.model.validators.LengthValidator; + +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; +import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.Configs.MAX_LENGTH; +import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.Configs.MAX_PASSWORD_ALLOWED_LENGTH; +import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.Configs.MIN_LENGTH; +import static org.wso2.carbon.identity.input.validation.mgt.utils.Constants.Configs.PASSWORD; + +/** + * Testing the AbstractRulesValidator class + */ +public class AbstractRulesValidatorTest { + + // Test constants. + private static final String TENANT_DOMAIN = "carbon.super"; + @Mock + private ValidationContext mockContext; + private MockedStatic identityUtil; + + @BeforeMethod + public void setup() { + + MockitoAnnotations.openMocks(this); + identityUtil = mockStatic(IdentityUtil.class); + } + + @AfterMethod + public void tearDown() { + + identityUtil.close(); + } + + @DataProvider(name = "validationScenarios") + public Object[][] validationScenarios() { + + Map validProperties = new HashMap<>(); + validProperties.put(MIN_LENGTH, "5"); + validProperties.put(MAX_LENGTH, "10"); + + Map invalidMinProperties = new HashMap<>(); + invalidMinProperties.put(MIN_LENGTH, "-1"); + + Map invalidMaxProperties = new HashMap<>(); + invalidMaxProperties.put(MAX_LENGTH, "-1"); + + Map minGreaterThanMaxProperties = new HashMap<>(); + minGreaterThanMaxProperties.put(MIN_LENGTH, "15"); + minGreaterThanMaxProperties.put(MAX_LENGTH, "10"); + + Map maxLengthExceedsPassword = new HashMap<>(); + maxLengthExceedsPassword.put(MAX_LENGTH, "65"); + + Map validPasswordProperties = new HashMap<>(); + validPasswordProperties.put(MAX_LENGTH, "64"); + + Map passwordPropertiesWithoutMax = new HashMap<>(); + passwordPropertiesWithoutMax.put(MIN_LENGTH, "8"); + + return new Object[][]{ + // Valid scenario. + {validProperties, "VALID_FIELD", TENANT_DOMAIN, true, null}, + + // Invalid MIN_LENGTH. + {invalidMinProperties, "VALID_FIELD", TENANT_DOMAIN, false, InputValidationMgtClientException.class}, + + // Invalid MAX_LENGTH. + {invalidMaxProperties, "VALID_FIELD", TENANT_DOMAIN, false, InputValidationMgtClientException.class}, + + // MIN_LENGTH greater than MAX_LENGTH. + {minGreaterThanMaxProperties, "VALID_FIELD", TENANT_DOMAIN, false, + InputValidationMgtClientException.class}, + + // MAX_LENGTH exceeds max password length. + {maxLengthExceedsPassword, PASSWORD, TENANT_DOMAIN, false, InputValidationMgtClientException.class}, + + // Valid password properties. + {validPasswordProperties, PASSWORD, TENANT_DOMAIN, true, null}, + + // Password properties without MAX_LENGTH. + {passwordPropertiesWithoutMax, PASSWORD, TENANT_DOMAIN, true, null}}; + } + + @Test(dataProvider = "validationScenarios") + public void testValidateProps(Map properties, String field, String tenantDomain, + boolean expectedResult, Class expectedException) { + // Mock context. + when(mockContext.getProperties()).thenReturn(properties); + when(mockContext.getField()).thenReturn(field); + when(mockContext.getTenantDomain()).thenReturn(tenantDomain); + + // Mock IdentityUtil. + when(IdentityUtil.getProperty(MAX_PASSWORD_ALLOWED_LENGTH)).thenReturn("64"); + + // Test execution. + AbstractRulesValidator validator = new LengthValidator(); // Replace with your validator class name + try { + boolean result = validator.validateProps(mockContext); + assertEquals(result, expectedResult, "Unexpected validation result."); + if (expectedException != null) { + fail("Expected exception but none was thrown."); + } + } catch (Exception e) { + if (expectedException == null || !expectedException.isInstance(e)) { + fail("Unexpected exception: " + e.getMessage()); + } + } + } +} + diff --git a/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/test/resources/testng.xml b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/test/resources/testng.xml index 9a0dc73db57d..68c16b8b6dea 100644 --- a/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/test/resources/testng.xml +++ b/components/input-validation-mgt/org.wso2.carbon.identity.input.validation.mgt/src/test/resources/testng.xml @@ -1,5 +1,5 @@ + diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml index 1eafa77d3c41..77a2b6890561 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml @@ -1032,6 +1032,10 @@ + + 64 + +