diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/StringUtil.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/StringUtil.java new file mode 100644 index 000000000..35c9053a6 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/StringUtil.java @@ -0,0 +1,36 @@ +/** + * 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.service; + +public final class StringUtil { + + private StringUtil() {} + + public static String maskValue(String value) { + return maskValue(value, 4); + } + + public static String maskValue(String value, Integer unmaskedLength) { + if (value.length() <= unmaskedLength) { + return "****"; + } + return value.substring(0, 1) + "*".repeat(value.length() - 1 - unmaskedLength) + value.substring(value.length() - unmaskedLength); + } + +} diff --git a/fineract-core/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java b/fineract-core/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java index 64f83883f..55c791964 100644 --- a/fineract-core/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java +++ b/fineract-core/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java @@ -233,7 +233,6 @@ public class AppUser extends AbstractPersistableCustom implements Platform if (command.isChangeInPasswordParameterNamed(passwordParamName, this.password, platformPasswordEncoder, getId())) { final String passwordEncodedValue = command.passwordValueOfParameterNamed(passwordParamName, platformPasswordEncoder, getId()); - actualChanges.put(passwordEncodedParamName, passwordEncodedValue); updatePassword(passwordEncodedValue); } } @@ -241,7 +240,6 @@ public class AppUser extends AbstractPersistableCustom implements Platform if (command.hasParameter(passwordEncodedParamName)) { if (command.isChangeInStringParameterNamed(passwordEncodedParamName, this.password)) { final String newValue = command.stringValueOfParameterNamed(passwordEncodedParamName); - actualChanges.put(passwordEncodedParamName, newValue); updatePassword(newValue); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/ExternalServicesPropertiesData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/ExternalServicesPropertiesData.java index 663edb772..ba88e8284 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/ExternalServicesPropertiesData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/ExternalServicesPropertiesData.java @@ -18,10 +18,12 @@ */ package org.apache.fineract.infrastructure.configuration.data; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.io.Serializable; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import org.apache.fineract.infrastructure.core.jersey.serializer.MaskedValueSerializer; @Data @NoArgsConstructor @@ -31,6 +33,7 @@ public class ExternalServicesPropertiesData implements Serializable { private static final long serialVersionUID = 1L; private String name; + @JsonSerialize(using = MaskedValueSerializer.class) private String value; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java index bcdd89826..1d2d89a04 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java @@ -20,13 +20,16 @@ package org.apache.fineract.infrastructure.configuration.service; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.campaigns.sms.data.MessageGatewayConfigurationData; import org.apache.fineract.infrastructure.configuration.data.ExternalServicesPropertiesData; import org.apache.fineract.infrastructure.configuration.data.S3CredentialsData; import org.apache.fineract.infrastructure.configuration.data.SMTPCredentialsData; import org.apache.fineract.infrastructure.configuration.exception.ExternalServiceConfigurationNotFoundException; +import org.apache.fineract.infrastructure.core.service.StringUtil; import org.apache.fineract.infrastructure.gcm.domain.NotificationConfigurationData; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; @@ -99,14 +102,21 @@ public class ExternalServicesPropertiesReadPlatformServiceImpl implements Extern private static final class ExternalServiceMapper implements RowMapper { + List secretAttributes; + + ExternalServiceMapper() { + secretAttributes = new ArrayList<>(); + secretAttributes.add("password"); + secretAttributes.add("server_key"); + } + @Override public ExternalServicesPropertiesData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { - // TODO Auto-generated method stub final String name = rs.getString("name"); String value = rs.getString("value"); // Masking the password as we should not send the password back - if (name != null && "password".equalsIgnoreCase(name)) { - value = "XXXX"; + if (name != null && secretAttributes.contains(name)) { + value = StringUtil.maskValue(value); } return new ExternalServicesPropertiesData().setName(name).setValue(value); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/jersey/serializer/MaskedValueSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/jersey/serializer/MaskedValueSerializer.java new file mode 100644 index 000000000..62a23675f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/jersey/serializer/MaskedValueSerializer.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.jersey.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import java.io.IOException; +import org.apache.fineract.infrastructure.core.service.StringUtil; + +public class MaskedValueSerializer extends JsonSerializer { + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value == null || value.isBlank()) { + gen.writeString(value); + return; + } + + String masked = StringUtil.maskValue(value); + gen.writeString(masked); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/api/UsersApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/api/UsersApiResourceSwagger.java index 35a13cb93..11b58fcd9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/useradministration/api/UsersApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/api/UsersApiResourceSwagger.java @@ -197,8 +197,6 @@ final class UsersApiResourceSwagger { @Schema(example = "Test") public String firstname; - @Schema(example = "abc3326b1bb376351c7baeb4175f5e0504e33aadf6a158474a6d71de1befae51") - public String passwordEncoded; } @Schema(example = "1") diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalServicesConfigurationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalServicesConfigurationTest.java index 497507150..efaca9f8b 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalServicesConfigurationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalServicesConfigurationTest.java @@ -18,6 +18,8 @@ */ package org.apache.fineract.integrationtests; +import static org.junit.jupiter.api.Assertions.assertTrue; + import io.restassured.builder.RequestSpecBuilder; import io.restassured.builder.ResponseSpecBuilder; import io.restassured.http.ContentType; @@ -83,7 +85,6 @@ public class ExternalServicesConfigurationTest { } // Checking for SMTP: - configName = "username"; externalServicesConfig = ExternalServicesConfigurationHelper.getExternalServicesConfigurationByServiceName(requestSpec, responseSpec, "SMTP"); @@ -111,6 +112,28 @@ public class ExternalServicesConfigurationTest { } + // Checking for Notifications: + configName = "server_key"; + externalServicesConfig = ExternalServicesConfigurationHelper.getExternalServicesConfigurationByServiceName(requestSpec, + responseSpec, "NOTIFICATION"); + Assertions.assertNotNull(externalServicesConfig); + + for (Integer configIndex = 0; configIndex < externalServicesConfig.size(); configIndex++) { + String name = (String) externalServicesConfig.get(configIndex).get("name"); + String value = null; + if (name.equals(configName)) { + value = (String) externalServicesConfig.get(configIndex).get("value"); + if (value == null) { + value = "testnull"; + } + LOG.info("{} : {}", name, value); + assertTrue(hasMoreThanThreeStars(value)); + } + + } } + private boolean hasMoreThanThreeStars(String input) { + return input != null && input.matches("(.*\\*.*){4,}"); + } }