diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java index 924ac0cc83f..eeaff5b9657 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java @@ -80,6 +80,8 @@ public class FineractProperties { private FineractNotificationProperties notification; + private FineractPhoneProperties phone; + private FineractLoanProperties loan; private FineractSamplingProperties sampling; @@ -495,6 +497,13 @@ public static class UserNotificationSystemProperties { private boolean enabled; } + @Getter + @Setter + public static class FineractPhoneProperties { + + private String regex = "^\\+?[0-9]{7,15}$"; + } + @Getter @Setter public static class FineractLoanProperties { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/validation/PhoneNumberValidationService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/validation/PhoneNumberValidationService.java new file mode 100644 index 00000000000..c0c17706949 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/validation/PhoneNumberValidationService.java @@ -0,0 +1,39 @@ +/** + * 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.fineract.infrastructure.core.validation; + +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.core.config.FineractProperties; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class PhoneNumberValidationService { + + private final FineractProperties fineractProperties; + + public boolean isValid(String phoneNumber) { + if (phoneNumber == null) { + return true; + } + + return phoneNumber.matches(fineractProperties.getPhone().getRegex()); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/validation/PhoneNumberValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/validation/PhoneNumberValidator.java new file mode 100644 index 00000000000..8023281ae7c --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/validation/PhoneNumberValidator.java @@ -0,0 +1,45 @@ +/** + * 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.fineract.infrastructure.core.validation; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.Annotation; + +@Component +@RequiredArgsConstructor +public class PhoneNumberValidator implements ConstraintValidator { + + private final PhoneNumberValidationService phoneNumberValidationService; + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + + if (value == null || value.isBlank()) { + return true; + } + + return phoneNumberValidationService.isValid(value); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/validation/ValidPhoneNumber.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/validation/ValidPhoneNumber.java new file mode 100644 index 00000000000..266da230283 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/validation/ValidPhoneNumber.java @@ -0,0 +1,40 @@ +/** + * 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.fineract.infrastructure.core.validation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = PhoneNumberValidator.class) +public @interface ValidPhoneNumber { + + String message() default "{org.apache.fineract.organisation.staff.mobile-no.invalid}"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffCreateRequest.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffCreateRequest.java index ca1264b900f..a5a955fb359 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffCreateRequest.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffCreateRequest.java @@ -27,6 +27,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.apache.fineract.infrastructure.core.validation.ValidPhoneNumber; import org.hibernate.validator.constraints.Length; @Builder @@ -56,7 +57,7 @@ public class StaffCreateRequest implements Serializable { private String emailAddress; @Length(max = 50, message = "{org.apache.fineract.organisation.staff.mobile-no.max}") // @NotBlank(message = "{org.apache.fineract.organisation.staff.mobile-no.not-blank}") - @Pattern(regexp = "^\\+?[0-9]{7,15}$", message = "{org.apache.fineract.organisation.staff.mobile-no.invalid}") + @ValidPhoneNumber private String mobileNo; @Builder.Default @JsonProperty("isActive") diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffUpdateRequest.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffUpdateRequest.java index 1817934d01f..46a73917ee9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffUpdateRequest.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/data/StaffUpdateRequest.java @@ -28,6 +28,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.FieldNameConstants; +import org.apache.fineract.infrastructure.core.validation.ValidPhoneNumber; import org.apache.fineract.organisation.staff.validation.StaffForceStatus; import org.hibernate.validator.constraints.Length; @@ -62,7 +63,7 @@ public class StaffUpdateRequest implements Serializable { private String emailAddress; @Length(max = 50, message = "{org.apache.fineract.organisation.staff.mobile-no.max}") // @NotBlank(message = "{org.apache.fineract.organisation.staff.mobile-no.not-blank}") - @Pattern(regexp = "^\\+?[0-9]{7,15}$", message = "{org.apache.fineract.organisation.staff.mobile-no.invalid}") + @ValidPhoneNumber private String mobileNo; @JsonProperty("isActive") private Boolean isActive; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientDataValidator.java index 546c1051bb0..9611682cb4b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientDataValidator.java @@ -34,6 +34,7 @@ import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants; import org.apache.fineract.infrastructure.configuration.service.ConfigurationReadPlatformService; import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; @@ -50,13 +51,15 @@ public final class ClientDataValidator { private final FromJsonHelper fromApiJsonHelper; private final ConfigurationReadPlatformService configurationReadPlatformService; - private static final String MOBILE_NUMBER_REGEX = "^\\+?[0-9]{7,15}$"; + private final FineractProperties fineractProperties; @Autowired public ClientDataValidator(final FromJsonHelper fromApiJsonHelper, - final ConfigurationReadPlatformService configurationReadPlatformService) { + final ConfigurationReadPlatformService configurationReadPlatformService, + final FineractProperties fineractProperties) { this.fromApiJsonHelper = fromApiJsonHelper; this.configurationReadPlatformService = configurationReadPlatformService; + this.fineractProperties = fineractProperties; } public void validateForCreate(final String json) { @@ -165,7 +168,7 @@ public void validateForCreate(final String json) { if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.mobileNoParamName, element)) { final String mobileNo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.mobileNoParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.mobileNoParamName).value(mobileNo).ignoreIfNull() - .matchesRegularExpression(MOBILE_NUMBER_REGEX).notExceedingLengthOf(50); + .matchesRegularExpression(fineractProperties.getPhone().getRegex()).notExceedingLengthOf(50); } final Boolean active = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element); @@ -451,7 +454,7 @@ public void validateForUpdate(final String json) { atLeastOneParameterPassedForUpdate = true; final String mobileNo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.mobileNoParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.mobileNoParamName).value(mobileNo).ignoreIfNull() - .matchesRegularExpression(MOBILE_NUMBER_REGEX).notExceedingLengthOf(50); + .matchesRegularExpression(fineractProperties.getPhone().getRegex()).notExceedingLengthOf(50); } final Boolean active = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element); diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/data/ClientDataValidatorTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/data/ClientDataValidatorTest.java index bcaf9e4f403..9992ddd05bd 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/data/ClientDataValidatorTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/data/ClientDataValidatorTest.java @@ -26,6 +26,7 @@ import org.apache.fineract.infrastructure.configuration.data.GlobalConfigurationPropertyData; import org.apache.fineract.infrastructure.configuration.service.ConfigurationReadPlatformService; +import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.portfolio.client.api.ClientApiConstants; @@ -44,6 +45,9 @@ class ClientDataValidatorTest { @Mock private ConfigurationReadPlatformService configurationReadPlatformService; + @Mock + private FineractProperties fineractProperties; + private ClientDataValidator validator; @BeforeEach @@ -51,7 +55,12 @@ void setUp() { FromJsonHelper fromApiJsonHelper = new FromJsonHelper(); when(configurationReadPlatformService.retrieveGlobalConfiguration(anyString())) .thenReturn(new GlobalConfigurationPropertyData().setEnabled(false)); - validator = new ClientDataValidator(fromApiJsonHelper, configurationReadPlatformService); + + FineractProperties.FineractPhoneProperties phoneProperties = new FineractProperties.FineractPhoneProperties(); + phoneProperties.setRegex("^\\+?[0-9]{7,15}$"); + when(fineractProperties.getPhone()).thenReturn(phoneProperties); + + validator = new ClientDataValidator(fromApiJsonHelper, configurationReadPlatformService, fineractProperties); } private static String validMinimalCreateJson(String dateFormat) {