This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new b4b2ef3701 FINERACT-2081: External service credential values masked
b4b2ef3701 is described below
commit b4b2ef37019aa499f66bb193dbdd8412a4c207ec
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Wed Apr 9 21:32:24 2025 -0500
FINERACT-2081: External service credential values masked
---
.../infrastructure/core/service/StringUtil.java | 24 +++++++++----------
.../useradministration/domain/AppUser.java | 2 --
.../data/ExternalServicesPropertiesData.java | 3 +++
...lServicesPropertiesReadPlatformServiceImpl.java | 16 ++++++++++---
.../jersey/serializer/MaskedValueSerializer.java} | 28 ++++++++++++----------
.../api/UsersApiResourceSwagger.java | 2 --
.../ExternalServicesConfigurationTest.java | 25 ++++++++++++++++++-
7 files changed, 68 insertions(+), 32 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/ExternalServicesPropertiesData.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/StringUtil.java
similarity index 61%
copy from
fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/ExternalServicesPropertiesData.java
copy to
fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/StringUtil.java
index 663edb772d..35c9053a67 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/ExternalServicesPropertiesData.java
+++
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/StringUtil.java
@@ -16,21 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.infrastructure.configuration.data;
+package org.apache.fineract.infrastructure.core.service;
-import java.io.Serializable;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.experimental.Accessors;
+public final class StringUtil {
-@Data
-@NoArgsConstructor
-@Accessors(chain = true)
-public class ExternalServicesPropertiesData implements Serializable {
+ private StringUtil() {}
- private static final long serialVersionUID = 1L;
+ public static String maskValue(String value) {
+ return maskValue(value, 4);
+ }
- private String name;
- private String value;
+ 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 64f83883ff..55c791964a 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<Long> 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<Long> 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 663edb772d..ba88e8284d 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 bcdd898260..1d2d89a04a 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<ExternalServicesPropertiesData> {
+ List<String> 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/configuration/data/ExternalServicesPropertiesData.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/jersey/serializer/MaskedValueSerializer.java
similarity index 52%
copy from
fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/ExternalServicesPropertiesData.java
copy to
fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/jersey/serializer/MaskedValueSerializer.java
index 663edb772d..62a23675fd 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/core/jersey/serializer/MaskedValueSerializer.java
@@ -16,21 +16,25 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.infrastructure.configuration.data;
+package org.apache.fineract.infrastructure.core.jersey.serializer;
-import java.io.Serializable;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.experimental.Accessors;
+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;
-@Data
-@NoArgsConstructor
-@Accessors(chain = true)
-public class ExternalServicesPropertiesData implements Serializable {
+public class MaskedValueSerializer extends JsonSerializer<String> {
- private static final long serialVersionUID = 1L;
+ @Override
+ public void serialize(String value, JsonGenerator gen, SerializerProvider
serializers) throws IOException {
+ if (value == null || value.isBlank()) {
+ gen.writeString(value);
+ return;
+ }
- private String name;
- private String value;
+ 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 35a13cb93f..11b58fcd9a 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 497507150d..efaca9f8bf 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,}");
+ }
}