Skip to content

Commit

Permalink
Anonymise previous session ID and user identifiers in subject when us…
Browse files Browse the repository at this point in the history
…er anonymisation is enabled (close #549)
  • Loading branch information
matus-tomlein committed Nov 3, 2022
1 parent 2ed0c5e commit b5529d9
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 29 deletions.
Expand Up @@ -410,11 +410,14 @@ public void testMultipleTrackersUpdateDifferentSessions() throws InterruptedExce
}

@Test
public void testAnonymisesUserIdentifiers() {
public void testAnonymisesUserAndPreviousSessionIdentifiers() {
Session session = new Session(600, 300, TimeUnit.SECONDS, null, getContext());
Map<String, Object> context = getSessionContext(session, "eid", 1000, true);
getSessionContext(session, "eid1", 1000, false);
session.startNewSession(); // so that a reference to previous session is created
Map<String, Object> context = getSessionContext(session, "eid2", 1001, true);

assertEquals("00000000-0000-0000-0000-000000000000", context.get(Parameters.SESSION_USER_ID));
assertNull(context.get(Parameters.SESSION_PREVIOUS_ID));
}

// Private methods
Expand Down
Expand Up @@ -13,97 +13,113 @@

package com.snowplowanalytics.snowplow.tracker;

import android.test.AndroidTestCase;
import android.content.Context;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import com.snowplowanalytics.snowplow.Snowplow;
import com.snowplowanalytics.snowplow.controller.TrackerController;
import com.snowplowanalytics.snowplow.internal.constants.Parameters;
import com.snowplowanalytics.snowplow.internal.tracker.Subject;
import com.snowplowanalytics.snowplow.internal.tracker.Logger;
import com.snowplowanalytics.snowplow.network.HttpMethod;

import java.util.Map;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public class SubjectTest extends AndroidTestCase {
import org.junit.Test;
import org.junit.runner.RunWith;

// Helper Methods
import java.util.Map;

private Subject getSubject() {
Logger.updateLogLevel(LogLevel.DEBUG);
return new Subject(getContext(), null);
}
@RunWith(AndroidJUnit4.class)
public class SubjectTest {

// Tests

@Test
public void testGetSubjectStandardPairs() throws Exception {
Subject subject = getSubject();
Map<String, String> standardPairs = subject.getSubject();
Map<String, String> standardPairs = subject.getSubject(false);

assertTrue(standardPairs.containsKey("tz"));
assertTrue(standardPairs.containsKey("lang"));
assertTrue(standardPairs.containsKey("res"));
}

@Test
public void testSetUserId() {
Subject subject = getSubject();
subject.setUserId("newUserId");
assertEquals("newUserId", subject.getSubject().get("uid"));
assertEquals("newUserId", subject.getSubject(false).get("uid"));
}

@Test
public void testSetScreenRes() {
Subject subject = getSubject();
subject.setScreenResolution(3000,1000);
assertEquals("3000x1000", subject.getSubject().get("res"));
assertEquals("3000x1000", subject.getSubject(false).get("res"));
}

@Test
public void testSetViewPort() {
Subject subject = getSubject();
subject.setViewPort(3000,1000);
assertEquals("3000x1000", subject.getSubject().get("vp"));
assertEquals("3000x1000", subject.getSubject(false).get("vp"));
}

@Test
public void testSetColorDepth() {
Subject subject = getSubject();
subject.setColorDepth(1000);
assertEquals("1000", subject.getSubject().get("cd"));
assertEquals("1000", subject.getSubject(false).get("cd"));
}

@Test
public void testSetTimezone() {
Subject subject = getSubject();
subject.setTimezone("fake/timezone");
assertEquals("fake/timezone", subject.getSubject().get("tz"));
assertEquals("fake/timezone", subject.getSubject(false).get("tz"));
}

@Test
public void testSetLanguage() {
Subject subject = getSubject();
subject.setLanguage("French");
assertEquals("French", subject.getSubject().get("lang"));
assertEquals("French", subject.getSubject(false).get("lang"));
}

@Test
public void testSetIpAddress() {
Subject subject = getSubject();
subject.setIpAddress("127.0.0.1");
assertEquals("127.0.0.1", subject.getSubject().get("ip"));
assertEquals("127.0.0.1", subject.getSubject(false).get("ip"));
}

@Test
public void testSetUseragent() {
Subject subject = getSubject();
subject.setUseragent("Agent");
assertEquals("Agent", subject.getSubject().get("ua"));
assertEquals("Agent", subject.getSubject(false).get("ua"));
}

@Test
public void testSetNetworkUID() {
Subject subject = getSubject();
subject.setNetworkUserId("nuid-test");
assertEquals("nuid-test", subject.getSubject().get("tnuid"));
assertEquals("nuid-test", subject.getSubject(false).get("tnuid"));
}

@Test
public void testSetDomainUID() {
Subject subject = getSubject();
subject.setDomainUserId("duid-test");
assertEquals("duid-test", subject.getSubject().get("duid"));
assertEquals("duid-test", subject.getSubject(false).get("duid"));
}

@Test
public void testSubjectUserIdCanBeUpdated() {
TrackerController tracker = Snowplow.createTracker(getContext(), "default", "https://fake-url", HttpMethod.POST);
assertNotNull(tracker.getSubject());
Expand All @@ -114,5 +130,27 @@ public void testSubjectUserIdCanBeUpdated() {
assertNull(tracker.getSubject().getUserId());
}

@Test
public void testAnonymisesUserIdentifiers() {
Subject subject = getSubject();
subject.setUserId("uid-test");
subject.setDomainUserId("duid-test");
subject.setNetworkUserId("nuid-test");
subject.setIpAddress("127.0.0.1");
assertNull(subject.getSubject(true).get("uid"));
assertNull(subject.getSubject(true).get("duid"));
assertNull(subject.getSubject(true).get("tnuid"));
assertNull(subject.getSubject(true).get("ip"));
}

// Helper Methods

private Subject getSubject() {
Logger.updateLogLevel(LogLevel.DEBUG);
return new Subject(getContext(), null);
}

private Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
}
}
Expand Up @@ -486,7 +486,7 @@ public TrackerConfiguration diagnosticAutotracking(boolean diagnosticAutotrackin
}

/**
* Whether to anonymise client-side user identifiers in session and platform context entities
* Whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
*/
@NonNull
public TrackerConfiguration userAnonymisation(boolean userAnonymisation) {
Expand Down
Expand Up @@ -204,6 +204,7 @@ public synchronized SelfDescribingJson getSessionContext(@NonNull String eventId
sessionCopy.put(Parameters.SESSION_EVENT_INDEX, eventIndex);
if (userAnonymisation) {
sessionCopy.put(Parameters.SESSION_USER_ID, new UUID(0, 0).toString());
sessionCopy.put(Parameters.SESSION_PREVIOUS_ID, null);
}

return new SelfDescribingJson(TrackerConstants.SESSION_SCHEMA, sessionCopy);
Expand Down
Expand Up @@ -230,10 +230,19 @@ public void setDomainUserId(@NonNull String domainUserId) {
}

/**
* @param userAnonymisation Whether to anonymize user identifiers
* @return the standard subject pairs
*/
@NonNull
public Map<String, String> getSubject() {
public Map<String, String> getSubject(boolean userAnonymisation) {
if (userAnonymisation) {
Map<String, String> pairsCopy = new HashMap<>(standardPairs);
pairsCopy.remove(Parameters.UID);
pairsCopy.remove(Parameters.DOMAIN_UID);
pairsCopy.remove(Parameters.NETWORK_UID);
pairsCopy.remove(Parameters.IP_ADDRESS);
return pairsCopy;
}
return this.standardPairs;
}
}
Expand Up @@ -340,7 +340,7 @@ public TrackerBuilder screenviewEvents(@NonNull Boolean screenviewEvents) {
}

/**
* @param userAnonymisation whether to anonymise client-side user identifiers in session and platform context entities
* @param userAnonymisation whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
* @return itself
*/
@NonNull
Expand Down Expand Up @@ -676,7 +676,7 @@ private void addBasicPropertiesToPayload(@NonNull Payload payload, @NonNull Trac
payload.add(Parameters.NAMESPACE, this.namespace);
payload.add(Parameters.TRACKER_VERSION, this.trackerVersion);
if (this.subject != null) {
payload.addMap(new HashMap<>(this.subject.getSubject()));
payload.addMap(new HashMap<>(this.subject.getSubject(userAnonymisation)));
}
payload.add(Parameters.PLATFORM, this.devicePlatform.getValue());
}
Expand Down Expand Up @@ -812,7 +812,7 @@ private void addServiceEventPayload(@NonNull Payload payload, @NonNull List<Self

// If there is a subject present for the Tracker add it
if (this.subject != null) {
payload.addMap(new HashMap<>(this.subject.getSubject()));
payload.addMap(new HashMap<>(this.subject.getSubject(userAnonymisation)));
}

// Add Mobile Context
Expand Down
Expand Up @@ -182,12 +182,12 @@ public interface TrackerConfigurationInterface {
void setDiagnosticAutotracking(boolean diagnosticAutotracking);

/**
* Whether to anonymise client-side user identifiers in session and platform context entities
* Whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
*/
boolean isUserAnonymisation();

/**
* Whether to anonymise client-side user identifiers in session and platform context entities
* Whether to anonymise client-side user identifiers in session (userId, previousSessionId), subject (userId, networkUserId, domainUserId, ipAddress) and platform context entities (IDFA)
*/
void setUserAnonymisation(boolean userAnonymisation);

Expand Down

0 comments on commit b5529d9

Please sign in to comment.