Skip to content

Commit

Permalink
Merge pull request #144 from rollbar/gdpr
Browse files Browse the repository at this point in the history
Configuration options for capturing IP addresses
  • Loading branch information
rokob authored May 16, 2018
2 parents d21c57d + 4bd9767 commit 3aff55e
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ There is an Android specific part of the library. Therefore for Android you shou
interface which requires you to add this dependency to your build process. For example,

```groovy
compile('com.rollbar:rollbar-api:1.1.0')
compile('com.rollbar:rollbar-java:1.1.0')
compile('com.rollbar:rollbar-android:1.1.0@aar')
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ public class ExampleUnitTest {
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
}
16 changes: 16 additions & 0 deletions rollbar-android/src/main/java/com/rollbar/android/Rollbar.java
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,21 @@ public Rollbar(Context context, String accessToken, String environment, boolean
* @param configProvider a configuration provider that can be used to customize the configuration further.
*/
public Rollbar(Context context, String accessToken, String environment, boolean registerExceptionHandler, boolean includeLogcat, ConfigProvider configProvider) {
this(context, accessToken, environment, registerExceptionHandler, includeLogcat, configProvider, "full");
}

/**
* Construct a new Rollbar instance.
*
* @param context Android context to use.
* @param accessToken a Rollbar access token with at least post_client_item scope
* @param environment the environment to set for items
* @param registerExceptionHandler whether or not to handle uncaught exceptions.
* @param includeLogcat whether or not to include logcat output with items
* @param configProvider a configuration provider that can be used to customize the configuration further.
* @param captureIp one of: full, anonymize, none. This determines how the remote ip is captured.
*/
public Rollbar(Context context, String accessToken, String environment, boolean registerExceptionHandler, boolean includeLogcat, ConfigProvider configProvider, String captureIp) {
if (accessToken == null) {
try {
accessToken = loadAccessTokenFromManifest(context);
Expand All @@ -220,6 +235,7 @@ public Rollbar(Context context, String accessToken, String environment, boolean
.versionCode(versionCode)
.versionName(versionName)
.includeLogcat(includeLogcat)
.captureIp(captureIp)
.build();

environment = environment == null ? DEFAULT_ENVIRONMENT : environment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ public class ClientProvider implements Provider<Client> {
private final int versionCode;
private final String versionName;
private boolean includeLogcat;
private final int captureIp;

private static final String CAPTURE_IP_ANONYMIZE = "anonymize";
private static final String CAPTURE_IP_NONE = "none";
private static final int CAPTURE_IP_TYPE_FULL = 0;
private static final int CAPTURE_IP_TYPE_ANONYMIZE = 1;
private static final int CAPTURE_IP_TYPE_NONE = 2;

private static final int MAX_LOGCAT_SIZE = 100;

Expand All @@ -28,6 +35,17 @@ public class ClientProvider implements Provider<Client> {
this.versionCode = builder.versionCode;
this.versionName = builder.versionName;
this.includeLogcat = builder.includeLogcat;
if (builder.captureIp != null) {
if (builder.captureIp.equals(CAPTURE_IP_ANONYMIZE)) {
this.captureIp = CAPTURE_IP_TYPE_ANONYMIZE;
} else if (builder.captureIp.equals(CAPTURE_IP_NONE)) {
this.captureIp = CAPTURE_IP_TYPE_NONE;
} else {
this.captureIp = CAPTURE_IP_TYPE_FULL;
}
} else {
this.captureIp = CAPTURE_IP_TYPE_FULL;
}
}

@Override
Expand All @@ -46,15 +64,20 @@ public Client provide() {
androidData.put("logs", getLogcatInfo());
}

return new Client.Builder()
Client.Builder clientBuilder = new Client.Builder()
.addClient("android", androidData)
.addTopLevel("code_version", this.versionCode)
.addTopLevel("name_version", this.versionName)
.addTopLevel("version_code", this.versionCode)
.addTopLevel("version_name", this.versionName)
.addTopLevel("user_ip", "$remote_ip")
.addTopLevel("timestamp", System.currentTimeMillis() / 1000)
.build();
.addTopLevel("timestamp", System.currentTimeMillis() / 1000);

if (this.captureIp == CAPTURE_IP_TYPE_FULL) {
clientBuilder.addTopLevel("user_ip", "$remote_ip");
} else if (this.captureIp == CAPTURE_IP_TYPE_ANONYMIZE) {
clientBuilder.addTopLevel("user_ip", "$remote_ip_anonymize");
}
return clientBuilder.build();
}

private ArrayList<String> getLogcatInfo() {
Expand Down Expand Up @@ -88,6 +111,7 @@ public static final class Builder {
private int versionCode;
private String versionName;
private boolean includeLogcat;
private String captureIp;

/**
* The Android version code from the context
Expand Down Expand Up @@ -119,6 +143,16 @@ public Builder includeLogcat(boolean includeLogcat) {
return this;
}

/**
* How to capture the remote client ip, either completely, anonymized, or not at all.
* @param captureIp one of: full, anonymize, none.
* @return the builder instance.
*/
public Builder captureIp(String captureIp) {
this.captureIp = captureIp;
return this;
}

/**
* Builds the {@link ClientProvider client provider}.
* @return the client provider.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class RollbarFilter implements Filter {

static final String CONFIG_PROVIDER_CLASS_PARAM_NAME = "config_provider";

static final String CONFIG_IP_CAPTURE_PARAM_NAME = "capture_ip";

private Rollbar rollbar;

public RollbarFilter() {
Expand All @@ -45,12 +47,14 @@ public void init(FilterConfig filterConfig) throws ServletException {
String userIpHeaderName = filterConfig.getInitParameter(USER_IP_HEADER_PARAM_NAME);
String configProviderClassName =
filterConfig.getInitParameter(CONFIG_PROVIDER_CLASS_PARAM_NAME);
String captureIp = filterConfig.getInitParameter(CONFIG_IP_CAPTURE_PARAM_NAME);

ConfigProvider configProvider = getConfigProvider(configProviderClassName);
Config config;

RequestProvider requestProvider = new RequestProvider.Builder()
.userIpHeaderName(userIpHeaderName)
.captureIp(captureIp)
.build();

ConfigBuilder configBuilder = withAccessToken(accessToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,33 @@
public class RequestProvider implements Provider<Request> {

private final String userIpHeaderName;
private final int captureIp;

// CAPTURE_IP_ANONYMIZE is the string value used to signify anonymizing captured IP addresses
public static final String CAPTURE_IP_ANONYMIZE = "anonymize";
// CAPTURE_IP_NONE is the string value used to signify not capturing IP addresses
public static final String CAPTURE_IP_NONE = "none";

private static final int CAPTURE_IP_TYPE_FULL = 0;
private static final int CAPTURE_IP_TYPE_ANONYMIZE = 1;
private static final int CAPTURE_IP_TYPE_NONE = 2;

/**
* Constructor.
*/
RequestProvider(Builder builder) {
this.userIpHeaderName = builder.userIpHeaderName;
if (builder.captureIp != null) {
if (builder.captureIp.equals(CAPTURE_IP_ANONYMIZE)) {
this.captureIp = CAPTURE_IP_TYPE_ANONYMIZE;
} else if (builder.captureIp.equals(CAPTURE_IP_NONE)) {
this.captureIp = CAPTURE_IP_TYPE_NONE;
} else {
this.captureIp = CAPTURE_IP_TYPE_FULL;
}
} else {
this.captureIp = CAPTURE_IP_TYPE_FULL;
}
}

@Override
Expand All @@ -48,11 +69,53 @@ public Request provide() {
}

private String userIp(HttpServletRequest request) {
String rawIp;
if (userIpHeaderName == null || "".equals(userIpHeaderName)) {
return request.getRemoteAddr();
rawIp = request.getRemoteAddr();
} else {
rawIp = request.getHeader(userIpHeaderName);
}
if (rawIp == null) {
return rawIp;
}

return request.getHeader(userIpHeaderName);
if (captureIp == CAPTURE_IP_TYPE_FULL) {
return rawIp;
} else if (captureIp == CAPTURE_IP_TYPE_ANONYMIZE) {
if (rawIp.contains(".")) {
// IPV4
String[] parts = rawIp.split("\\.");
if (parts.length < 3) {
return rawIp;
}
// Java 7 does not have String.join
StringBuffer ip = new StringBuffer(parts[0]);
ip.append(".");
ip.append(parts[1]);
ip.append(".");
ip.append(parts[2]);
ip.append(".0");
return ip.toString();
} else if (rawIp.contains(":")) {
// IPV6
String[] parts = rawIp.split(":");
if (parts.length < 3) {
return rawIp;
}
StringBuffer ip = new StringBuffer(parts[0]);
ip.append(":");
ip.append(parts[1]);
ip.append(":");
ip.append(parts[2]);
ip.append(":0000:0000:0000:0000:0000");
return ip.toString();
} else {
return rawIp;
}
} else if (captureIp == CAPTURE_IP_TYPE_NONE) {
return null;
}
return null;
}

private static String url(HttpServletRequest request) {
Expand Down Expand Up @@ -102,6 +165,7 @@ private static String queryString(HttpServletRequest request) {
public static final class Builder {

private String userIpHeaderName;
private String captureIp;

/**
* The request header name to retrieve the user ip.
Expand All @@ -113,6 +177,17 @@ public Builder userIpHeaderName(String userIpHeaderName) {
return this;
}

/**
* The policy to use for capturing the user ip.
* @param captureIp One of: full, anonymize, none
* If this value is empty, null, or otherwise invalid the default policy is full.
* @return the builder instance.
*/
public Builder captureIp(String captureIp) {
this.captureIp = captureIp;
return this;
}

/**
* Builds the {@link RequestProvider request provider}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static java.util.Arrays.asList;
import static java.util.Collections.enumeration;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNull;
import static org.hamcrest.core.Is.is;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -125,4 +126,39 @@ public void shouldRetrieveTheRemoteAddressUsingRequestRemoteAddress() {

assertThat(result.getUserIp(), is(remoteAddr));
}
}

@Test
public void shouldRetrieveTheRemoteAddressUsingRequestRemoteAddressAndAnonymize() {
String userIpHeaderName = "";

RequestProvider sut = new RequestProvider.Builder()
.userIpHeaderName(userIpHeaderName)
.captureIp("anonymize")
.build();

String remoteAddr = "192.168.1.1";
String remoteAddrAnon = "192.168.1.0";
when(request.getRemoteAddr()).thenReturn(remoteAddr);

Request result = sut.provide();

assertThat(result.getUserIp(), is(remoteAddrAnon));
}

@Test
public void shouldRetrieveTheRemoteAddressUsingRequestRemoteAddressAndNotCaptureIfCaptureIpIsNone() {
String userIpHeaderName = "";

RequestProvider sut = new RequestProvider.Builder()
.userIpHeaderName(userIpHeaderName)
.captureIp("none")
.build();

String remoteAddr = "192.168.1.1";
when(request.getRemoteAddr()).thenReturn(remoteAddr);

Request result = sut.provide();

assertNull(result.getUserIp());
}
}

0 comments on commit 3aff55e

Please sign in to comment.