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,}");
+    }
 }

Reply via email to