FINERACT-2081: External service credential values masked
This commit is contained in:
committed by
Adam Saghy
parent
07ea81e399
commit
b4b2ef3701
+36
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -233,7 +233,6 @@ public class AppUser extends AbstractPersistableCustom<Long> implements Platform
|
|||||||
if (command.isChangeInPasswordParameterNamed(passwordParamName, this.password, platformPasswordEncoder, getId())) {
|
if (command.isChangeInPasswordParameterNamed(passwordParamName, this.password, platformPasswordEncoder, getId())) {
|
||||||
final String passwordEncodedValue = command.passwordValueOfParameterNamed(passwordParamName, platformPasswordEncoder,
|
final String passwordEncodedValue = command.passwordValueOfParameterNamed(passwordParamName, platformPasswordEncoder,
|
||||||
getId());
|
getId());
|
||||||
actualChanges.put(passwordEncodedParamName, passwordEncodedValue);
|
|
||||||
updatePassword(passwordEncodedValue);
|
updatePassword(passwordEncodedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,7 +240,6 @@ public class AppUser extends AbstractPersistableCustom<Long> implements Platform
|
|||||||
if (command.hasParameter(passwordEncodedParamName)) {
|
if (command.hasParameter(passwordEncodedParamName)) {
|
||||||
if (command.isChangeInStringParameterNamed(passwordEncodedParamName, this.password)) {
|
if (command.isChangeInStringParameterNamed(passwordEncodedParamName, this.password)) {
|
||||||
final String newValue = command.stringValueOfParameterNamed(passwordEncodedParamName);
|
final String newValue = command.stringValueOfParameterNamed(passwordEncodedParamName);
|
||||||
actualChanges.put(passwordEncodedParamName, newValue);
|
|
||||||
updatePassword(newValue);
|
updatePassword(newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -18,10 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.infrastructure.configuration.data;
|
package org.apache.fineract.infrastructure.configuration.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
import org.apache.fineract.infrastructure.core.jersey.serializer.MaskedValueSerializer;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@@ -31,6 +33,7 @@ public class ExternalServicesPropertiesData implements Serializable {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
@JsonSerialize(using = MaskedValueSerializer.class)
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-3
@@ -20,13 +20,16 @@ package org.apache.fineract.infrastructure.configuration.service;
|
|||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.infrastructure.campaigns.sms.data.MessageGatewayConfigurationData;
|
import org.apache.fineract.infrastructure.campaigns.sms.data.MessageGatewayConfigurationData;
|
||||||
import org.apache.fineract.infrastructure.configuration.data.ExternalServicesPropertiesData;
|
import org.apache.fineract.infrastructure.configuration.data.ExternalServicesPropertiesData;
|
||||||
import org.apache.fineract.infrastructure.configuration.data.S3CredentialsData;
|
import org.apache.fineract.infrastructure.configuration.data.S3CredentialsData;
|
||||||
import org.apache.fineract.infrastructure.configuration.data.SMTPCredentialsData;
|
import org.apache.fineract.infrastructure.configuration.data.SMTPCredentialsData;
|
||||||
import org.apache.fineract.infrastructure.configuration.exception.ExternalServiceConfigurationNotFoundException;
|
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.apache.fineract.infrastructure.gcm.domain.NotificationConfigurationData;
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
@@ -99,14 +102,21 @@ public class ExternalServicesPropertiesReadPlatformServiceImpl implements Extern
|
|||||||
|
|
||||||
private static final class ExternalServiceMapper implements RowMapper<ExternalServicesPropertiesData> {
|
private static final class ExternalServiceMapper implements RowMapper<ExternalServicesPropertiesData> {
|
||||||
|
|
||||||
|
List<String> secretAttributes;
|
||||||
|
|
||||||
|
ExternalServiceMapper() {
|
||||||
|
secretAttributes = new ArrayList<>();
|
||||||
|
secretAttributes.add("password");
|
||||||
|
secretAttributes.add("server_key");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExternalServicesPropertiesData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
public ExternalServicesPropertiesData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
final String name = rs.getString("name");
|
final String name = rs.getString("name");
|
||||||
String value = rs.getString("value");
|
String value = rs.getString("value");
|
||||||
// Masking the password as we should not send the password back
|
// Masking the password as we should not send the password back
|
||||||
if (name != null && "password".equalsIgnoreCase(name)) {
|
if (name != null && secretAttributes.contains(name)) {
|
||||||
value = "XXXX";
|
value = StringUtil.maskValue(value);
|
||||||
}
|
}
|
||||||
return new ExternalServicesPropertiesData().setName(name).setValue(value);
|
return new ExternalServicesPropertiesData().setName(name).setValue(value);
|
||||||
}
|
}
|
||||||
|
|||||||
+40
@@ -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<String> {
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
-2
@@ -197,8 +197,6 @@ final class UsersApiResourceSwagger {
|
|||||||
|
|
||||||
@Schema(example = "Test")
|
@Schema(example = "Test")
|
||||||
public String firstname;
|
public String firstname;
|
||||||
@Schema(example = "abc3326b1bb376351c7baeb4175f5e0504e33aadf6a158474a6d71de1befae51")
|
|
||||||
public String passwordEncoded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(example = "1")
|
@Schema(example = "1")
|
||||||
|
|||||||
+24
-1
@@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.integrationtests;
|
package org.apache.fineract.integrationtests;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import io.restassured.builder.RequestSpecBuilder;
|
import io.restassured.builder.RequestSpecBuilder;
|
||||||
import io.restassured.builder.ResponseSpecBuilder;
|
import io.restassured.builder.ResponseSpecBuilder;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
@@ -83,7 +85,6 @@ public class ExternalServicesConfigurationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Checking for SMTP:
|
// Checking for SMTP:
|
||||||
|
|
||||||
configName = "username";
|
configName = "username";
|
||||||
externalServicesConfig = ExternalServicesConfigurationHelper.getExternalServicesConfigurationByServiceName(requestSpec,
|
externalServicesConfig = ExternalServicesConfigurationHelper.getExternalServicesConfigurationByServiceName(requestSpec,
|
||||||
responseSpec, "SMTP");
|
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,}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user