Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve: Analytics Event Data Masking #12190

Merged
merged 2 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,13 @@ public class Constants {
public static final String ANONYMOUS_VALUE = "anonymous";
public static final String UNKNOWN_VALUE = "UNKNOWN";
public static final int UNKNOWN_INT_VALUE = -1;
public static final String IPV4_PROP_TYPE = "IPV4";
public static final String IPV6_PROP_TYPE = "IPV6";
public static final String EMAIL_PROP_TYPE = "EMAIL";
public static final String USERNAME_PROP_TYPE = "USERNAME";

public static final String IPV4_MASK_VALUE = "***";
public static final String IPV6_MASK_VALUE = "**";
public static final String EMAIL_MASK_VALUE = "*****";
public static final String USERNAME_MASK_VALUE = "*****";
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,7 @@ public interface AnalyticsDataProvider {
default Map<String, Object> getProperties() {
return Collections.EMPTY_MAP;
}

Map<String, String> getMaskProperties();

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@
import org.wso2.carbon.apimgt.common.analytics.publishers.dto.Target;
import org.wso2.carbon.apimgt.common.analytics.publishers.impl.SuccessRequestDataPublisher;

import java.util.Iterator;
import java.util.Map;

import static org.wso2.carbon.apimgt.common.analytics.Constants.EMAIL_MASK_VALUE;
import static org.wso2.carbon.apimgt.common.analytics.Constants.EMAIL_PROP_TYPE;
import static org.wso2.carbon.apimgt.common.analytics.Constants.IPV4_MASK_VALUE;
import static org.wso2.carbon.apimgt.common.analytics.Constants.IPV4_PROP_TYPE;
import static org.wso2.carbon.apimgt.common.analytics.Constants.IPV6_MASK_VALUE;
import static org.wso2.carbon.apimgt.common.analytics.Constants.IPV6_PROP_TYPE;
import static org.wso2.carbon.apimgt.common.analytics.Constants.USERNAME_MASK_VALUE;
import static org.wso2.carbon.apimgt.common.analytics.Constants.USERNAME_PROP_TYPE;

/**
* Success request data collector.
*/
Expand All @@ -58,6 +70,22 @@

Event event = new Event();
event.setProperties(provider.getProperties());

// Masking the configured data
Map<String, String> maskData = provider.getMaskProperties();
Iterator<Map.Entry<String, String>> iterator = maskData.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();

Check warning on line 78 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L77-L78

Added lines #L77 - L78 were not covered by tests
Map<String, Object> props = event.getProperties();
if (props != null) {

Check warning on line 80 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L80

Added line #L80 was not covered by tests
Object value = props.get(entry.getKey());
if (value != null) {
String maskStr = maskAnalyticsData(entry.getValue(), value);

Check warning on line 83 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L82-L83

Added lines #L82 - L83 were not covered by tests
props.replace(entry.getKey(), maskStr);
}
}

Check warning on line 86 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L86

Added line #L86 was not covered by tests
}

API api = provider.getApi();
Operation operation = provider.getOperation();
Target target = provider.getTarget();
Expand All @@ -70,14 +98,34 @@
}
Latencies latencies = provider.getLatencies();
MetaInfo metaInfo = provider.getMetaInfo();

String userAgent = provider.getUserAgentHeader();
String userName = provider.getUserName();

// Mask UserName if configured
if (userName != null) {
if (maskData.containsKey("api.ut.userName")) {

Check warning on line 107 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L107

Added line #L107 was not covered by tests
userName = maskAnalyticsData(maskData.get("api.ut.userName"), userName);
} else if (maskData.containsKey("api.ut.userId")) {

Check warning on line 109 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L109

Added line #L109 was not covered by tests
userName = maskAnalyticsData(maskData.get("api.ut.userId"), userName);
}
}

String userIp = provider.getEndUserIP();
if (userIp == null) {
userIp = Constants.UNKNOWN_VALUE;
} else {
// Mask User IP if configured
if (maskData.containsKey("api.analytics.user.ip")) {

Check warning on line 119 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L119

Added line #L119 was not covered by tests
userIp = maskAnalyticsData(maskData.get("api.analytics.user.ip"), userIp);
}
}
if (userAgent == null) {
userAgent = Constants.UNKNOWN_VALUE;
} else {
if (maskData.containsKey("api.analytics.user.agent")) {

Check warning on line 126 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L126

Added line #L126 was not covered by tests
userAgent = maskAnalyticsData(maskData.get("api.analytics.user.agent"), userAgent);
}
}

event.setApi(api);
Expand All @@ -95,4 +143,33 @@
this.processor.publish(event);
}

private String maskAnalyticsData(String type, Object value) {
if (value instanceof String) {
switch (type) {
case IPV4_PROP_TYPE:

Check warning on line 149 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L149

Added line #L149 was not covered by tests
String[] octets = value.toString().split("\\.");

// Sample output: 192.168.***.98

Check warning on line 152 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L152

Added line #L152 was not covered by tests
return octets[0] + "." + octets[1] + "." + IPV4_MASK_VALUE + "." + octets[3];
case IPV6_PROP_TYPE:

Check warning on line 154 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L154

Added line #L154 was not covered by tests
octets = value.toString().split(":");

// Sample output: 2001:0db8:85a3:****:****:****:****:7334

Check warning on line 157 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L157

Added line #L157 was not covered by tests
return octets[0] + ":" + octets[1] + ":" + octets[2] + ":" + IPV6_MASK_VALUE + ":" + IPV6_MASK_VALUE
+ ":" + IPV6_MASK_VALUE + ":" + IPV6_MASK_VALUE + ":" + octets[7];
case EMAIL_PROP_TYPE:

Check warning on line 160 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L160

Added line #L160 was not covered by tests
String[] email = value.toString().split("@");

// Sample output: *****@gmail.com

Check warning on line 163 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L163

Added line #L163 was not covered by tests
return EMAIL_MASK_VALUE + "@" + email[1];
case USERNAME_PROP_TYPE:

Check warning on line 165 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L165

Added line #L165 was not covered by tests
return USERNAME_MASK_VALUE;
default:
// Sample output: ********

Check warning on line 168 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L168

Added line #L168 was not covered by tests
return USERNAME_MASK_VALUE;
}
}

Check warning on line 171 in components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.common.analytics/src/main/java/org/wso2/carbon/apimgt/common/analytics/collectors/impl/SuccessRequestDataCollector.java#L171

Added line #L171 was not covered by tests
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,13 @@ public MetaInfo getMetaInfo() {
return metaInfo;
}

@Override
public Map<String, String> getMaskProperties() {
Map<String, String> maskProperties = ServiceReferenceHolder.getInstance().getApiManagerConfigurationService()
.getAPIAnalyticsConfiguration().getMaskDataProperties();
return maskProperties;
}

@Override
public int getProxyResponseCode() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,13 @@
return metaInfo;
}

@Override
public Map<String, String> getMaskProperties() {
Map<String, String> maskProperties = ServiceReferenceHolder.getInstance().getApiManagerConfigurationService()
.getAPIAnalyticsConfiguration().getMaskDataProperties();
return maskProperties;

Check warning on line 261 in components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/streaming/websocket/WebSocketAnalyticsDataProvider.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/streaming/websocket/WebSocketAnalyticsDataProvider.java#L259-L261

Added lines #L259 - L261 were not covered by tests
}

@Override
public int getProxyResponseCode() {
if (isSuccessRequest()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class APIManagerAnalyticsConfiguration {
private String responseSchemaName;
private String faultSchemaName;
private Map<String, String> reporterProperties;
private Map<String, String> maskDataProperties;

private APIManagerAnalyticsConfiguration() {
}
Expand All @@ -77,6 +78,7 @@ public void setAPIManagerConfiguration(APIManagerConfiguration config){
this.responseSchemaName = config.getFirstProperty(APIConstants.API_ANALYTICS_RESPONSE_SCHEMA_NAME);
this.faultSchemaName = config.getFirstProperty(APIConstants.API_ANALYTICS_FAULT_SCHEMA_NAME);
this.reporterProperties = config.getAnalyticsProperties();
this.maskDataProperties = config.getAnalyticsMaskProperties();
}
}

Expand Down Expand Up @@ -235,6 +237,9 @@ public void setReporterClass(String reporterClass) {
public Map<String, String> getReporterProperties() {
return reporterProperties;
}
public Map<String, String> getMaskDataProperties() {
return maskDataProperties;
}

public void setReporterProperties(Map<String, String> reporterProperties) {
this.reporterProperties = reporterProperties;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
private RedisConfig redisConfig = new RedisConfig();
private Map<String, List<String>> restApiJWTAuthAudiences = new HashMap<>();
private JSONObject subscriberAttributes = new JSONObject();
private static Map<String, String> analyticsMaskProps;

public Map<String, List<String>> getRestApiJWTAuthAudiences() {
return restApiJWTAuthAudiences;
Expand Down Expand Up @@ -357,6 +358,19 @@
analyticsProps.put(name, value);
}
}

// Load all the mask properties
OMElement maskProperties = element.getFirstChildWithName(new QName("MaskProperties"));
Iterator maskPropertiesIterator = maskProperties.getChildrenWithLocalName("Property");
Map<String, String> maskProps = new HashMap<>();
while (maskPropertiesIterator.hasNext()) {
OMElement propertyElem = (OMElement) maskPropertiesIterator.next();
String name = propertyElem.getAttributeValue(new QName("name"));
String value = propertyElem.getText();
maskProps.put(name, value.toUpperCase());
}

Check warning on line 371 in components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIManagerConfiguration.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIManagerConfiguration.java#L367-L371

Added lines #L367 - L371 were not covered by tests
analyticsMaskProps = maskProps;

OMElement authTokenElement = element.getFirstChildWithName(new QName("AuthToken"));
String resolvedAuthToken = MiscellaneousUtil.resolve(authTokenElement, secretResolver);
analyticsProps.put("auth.api.token", resolvedAuthToken);
Expand Down Expand Up @@ -2184,6 +2198,20 @@
}
}

OMElement properties = omElement.getFirstChildWithName(new
QName(APIConstants.API_GATEWAY_ADDITIONAL_PROPERTIES));
Map<String, String> additionalProperties = new HashMap<>();
if (properties != null) {
Iterator gatewayAdditionalProperties = properties.getChildrenWithLocalName
(APIConstants.API_GATEWAY_ADDITIONAL_PROPERTY);
while (gatewayAdditionalProperties.hasNext()) {
OMElement propertyElem = (OMElement) gatewayAdditionalProperties.next();
String propName = propertyElem.getAttributeValue(new QName("name"));
String resolvedValue = MiscellaneousUtil.resolve(propertyElem, secretResolver);
additionalProperties.put(propName, resolvedValue);
}
}

Check warning on line 2214 in components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIManagerConfiguration.java

View check run for this annotation

Codecov / codecov/patch

components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIManagerConfiguration.java#L2214

Added line #L2214 was not covered by tests
OMElement eventWaitingTimeElement = omElement
.getFirstChildWithName(new QName(APIConstants.EVENT_WAITING_TIME_CONFIG));
if (eventWaitingTimeElement != null) {
Expand Down Expand Up @@ -2211,6 +2239,10 @@
public static Map<String, String> getAnalyticsProperties() {
return analyticsProperties;
}

public static Map<String, String> getAnalyticsMaskProperties() {
return analyticsMaskProps;
}

public static Map<String, String> getPersistenceProperties() {
return persistenceProperties;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@
{% endfor %}
</Properties>

<MaskProperties>
{% for key,value in apim.analytics.mask.items() %}
<Property name="{{key}}">{{value}}</Property>
{% endfor %}
</MaskProperties>
</Analytics>

<!--
Expand Down
Loading