diff --git a/pom.xml b/pom.xml
index 76b0cfc62..f405a68d9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.uid2
uid2-operator
- 5.56.42
+ 5.56.43-alpha-201-SNAPSHOT
UTF-8
diff --git a/src/main/java/com/uid2/operator/model/AdvertisingToken.java b/src/main/java/com/uid2/operator/model/AdvertisingToken.java
deleted file mode 100644
index e1fcc4725..000000000
--- a/src/main/java/com/uid2/operator/model/AdvertisingToken.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.uid2.operator.model;
-
-import java.time.Instant;
-import com.uid2.shared.model.TokenVersion;
-
-public class AdvertisingToken extends VersionedToken {
- public final OperatorIdentity operatorIdentity;
- public final PublisherIdentity publisherIdentity;
- public final UserIdentity userIdentity;
-
- public AdvertisingToken(TokenVersion version, Instant createdAt, Instant expiresAt, OperatorIdentity operatorIdentity,
- PublisherIdentity publisherIdentity, UserIdentity userIdentity) {
- super(version, createdAt, expiresAt);
- this.operatorIdentity = operatorIdentity;
- this.publisherIdentity = publisherIdentity;
- this.userIdentity = userIdentity;
- }
-}
-
diff --git a/src/main/java/com/uid2/operator/model/AdvertisingTokenRequest.java b/src/main/java/com/uid2/operator/model/AdvertisingTokenRequest.java
new file mode 100644
index 000000000..d63fa66a8
--- /dev/null
+++ b/src/main/java/com/uid2/operator/model/AdvertisingTokenRequest.java
@@ -0,0 +1,28 @@
+package com.uid2.operator.model;
+
+import java.time.Instant;
+
+import com.uid2.operator.model.identities.RawUid;
+import com.uid2.operator.util.PrivacyBits;
+import com.uid2.shared.model.TokenVersion;
+
+// class containing enough information to create a new uid token (aka advertising token)
+public class AdvertisingTokenRequest extends VersionedTokenRequest {
+ public final OperatorIdentity operatorIdentity;
+ public final SourcePublisher sourcePublisher;
+ public final RawUid rawUid;
+ public final PrivacyBits privacyBits;
+ public final Instant establishedAt;
+
+ public AdvertisingTokenRequest(TokenVersion version, Instant createdAt, Instant expiresAt, OperatorIdentity operatorIdentity,
+ SourcePublisher sourcePublisher, RawUid rawUid, PrivacyBits privacyBits,
+ Instant establishedAt) {
+ super(version, createdAt, expiresAt);
+ this.operatorIdentity = operatorIdentity;
+ this.sourcePublisher = sourcePublisher;
+ this.rawUid = rawUid;
+ this.privacyBits = privacyBits;
+ this.establishedAt = establishedAt;
+ }
+}
+
diff --git a/src/main/java/com/uid2/operator/model/MapRequest.java b/src/main/java/com/uid2/operator/model/IdentityMapRequestItem.java
similarity index 60%
rename from src/main/java/com/uid2/operator/model/MapRequest.java
rename to src/main/java/com/uid2/operator/model/IdentityMapRequestItem.java
index d39e87238..079af8e76 100644
--- a/src/main/java/com/uid2/operator/model/MapRequest.java
+++ b/src/main/java/com/uid2/operator/model/IdentityMapRequestItem.java
@@ -1,18 +1,19 @@
package com.uid2.operator.model;
+import com.uid2.operator.model.identities.HashedDii;
+
import java.time.Instant;
-public final class MapRequest {
- public final UserIdentity userIdentity;
+public final class IdentityMapRequestItem {
+ public final HashedDii hashedDii;
public final OptoutCheckPolicy optoutCheckPolicy;
public final Instant asOf;
- public MapRequest(
- UserIdentity userIdentity,
+ public IdentityMapRequestItem(
+ HashedDii hashedDii,
OptoutCheckPolicy optoutCheckPolicy,
- Instant asOf)
- {
- this.userIdentity = userIdentity;
+ Instant asOf) {
+ this.hashedDii = hashedDii;
this.optoutCheckPolicy = optoutCheckPolicy;
this.asOf = asOf;
}
diff --git a/src/main/java/com/uid2/operator/model/IdentityMapResponseItem.java b/src/main/java/com/uid2/operator/model/IdentityMapResponseItem.java
new file mode 100644
index 000000000..0bdc70389
--- /dev/null
+++ b/src/main/java/com/uid2/operator/model/IdentityMapResponseItem.java
@@ -0,0 +1,23 @@
+package com.uid2.operator.model;
+
+// Contains the computed raw UID and its bucket ID from identity/map request
+public class IdentityMapResponseItem {
+ public static final IdentityMapResponseItem OptoutIdentity = new IdentityMapResponseItem(new byte[33], "", null, null);
+ // The raw UID is also known as Advertising Id (historically)
+ public final byte[] rawUid;
+ public final String bucketId;
+ public final byte[] previousRawUid;
+ public final Long refreshFrom;
+
+ public IdentityMapResponseItem(byte[] rawUid, String bucketId, byte[] previousRawUid, Long refreshFrom) {
+ this.rawUid = rawUid;
+ this.bucketId = bucketId;
+ this.previousRawUid = previousRawUid;
+ this.refreshFrom = refreshFrom;
+ }
+
+ // historically Optout is known as Logout
+ public boolean isOptedOut() {
+ return this.equals(OptoutIdentity) || this.bucketId == null || this.bucketId.isEmpty();
+ }
+}
diff --git a/src/main/java/com/uid2/operator/model/IdentityRequest.java b/src/main/java/com/uid2/operator/model/IdentityRequest.java
deleted file mode 100644
index 74d8917dd..000000000
--- a/src/main/java/com/uid2/operator/model/IdentityRequest.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.uid2.operator.model;
-
-public final class IdentityRequest {
- public final PublisherIdentity publisherIdentity;
- public final UserIdentity userIdentity;
- public final OptoutCheckPolicy optoutCheckPolicy;
-
- public IdentityRequest(
- PublisherIdentity publisherIdentity,
- UserIdentity userIdentity,
- OptoutCheckPolicy tokenGeneratePolicy)
- {
- this.publisherIdentity = publisherIdentity;
- this.userIdentity = userIdentity;
- this.optoutCheckPolicy = tokenGeneratePolicy;
- }
-
- public boolean shouldCheckOptOut() {
- return optoutCheckPolicy.equals(OptoutCheckPolicy.RespectOptOut);
- }
-}
diff --git a/src/main/java/com/uid2/operator/model/MappedIdentity.java b/src/main/java/com/uid2/operator/model/MappedIdentity.java
deleted file mode 100644
index df669c171..000000000
--- a/src/main/java/com/uid2/operator/model/MappedIdentity.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.uid2.operator.model;
-
-public class MappedIdentity {
- public static final MappedIdentity LogoutIdentity = new MappedIdentity(new byte[33], "", null, null);
- public final byte[] advertisingId;
- public final String bucketId;
- public final byte[] previousAdvertisingId;
- public final Long refreshFrom;
-
- public MappedIdentity(byte[] advertisingId, String bucketId, byte[] previousAdvertisingId, Long refreshFrom) {
- this.advertisingId = advertisingId;
- this.bucketId = bucketId;
- this.previousAdvertisingId = previousAdvertisingId;
- this.refreshFrom = refreshFrom;
- }
-
- public boolean isOptedOut() {
- return this.equals(LogoutIdentity) || this.bucketId == null || this.bucketId.isEmpty();
- }
-}
diff --git a/src/main/java/com/uid2/operator/model/PublisherIdentity.java b/src/main/java/com/uid2/operator/model/PublisherIdentity.java
deleted file mode 100644
index d7a0fae57..000000000
--- a/src/main/java/com/uid2/operator/model/PublisherIdentity.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.uid2.operator.model;
-
-public class PublisherIdentity {
- public final int siteId;
- public final int clientKeyId;
- public final long publisherId;
-
- public PublisherIdentity(int siteId, int clientKeyId, long publisherId) {
- this.siteId = siteId;
- this.clientKeyId = clientKeyId;
- this.publisherId = publisherId;
- }
-}
diff --git a/src/main/java/com/uid2/operator/model/RefreshResponse.java b/src/main/java/com/uid2/operator/model/RefreshResponse.java
deleted file mode 100644
index fbe41f96b..000000000
--- a/src/main/java/com/uid2/operator/model/RefreshResponse.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.uid2.operator.model;
-
-import java.time.Duration;
-
-public class RefreshResponse {
-
- public static RefreshResponse Invalid = new RefreshResponse(Status.Invalid, IdentityTokens.LogoutToken);
- public static RefreshResponse Optout = new RefreshResponse(Status.Optout, IdentityTokens.LogoutToken);
- public static RefreshResponse Expired = new RefreshResponse(Status.Expired, IdentityTokens.LogoutToken);
- public static RefreshResponse Deprecated = new RefreshResponse(Status.Deprecated, IdentityTokens.LogoutToken);
- public static RefreshResponse NoActiveKey = new RefreshResponse(Status.NoActiveKey, IdentityTokens.LogoutToken);
- private final Status status;
- private final IdentityTokens tokens;
- private final Duration durationSinceLastRefresh;
- private final boolean isCstg;
-
- private RefreshResponse(Status status, IdentityTokens tokens, Duration durationSinceLastRefresh, boolean isCstg) {
- this.status = status;
- this.tokens = tokens;
- this.durationSinceLastRefresh = durationSinceLastRefresh;
- this.isCstg = isCstg;
- }
-
- private RefreshResponse(Status status, IdentityTokens tokens) {
- this(status, tokens, null, false);
- }
-
- public static RefreshResponse createRefreshedResponse(IdentityTokens tokens, Duration durationSinceLastRefresh, boolean isCstg) {
- return new RefreshResponse(Status.Refreshed, tokens, durationSinceLastRefresh, isCstg);
- }
-
- public Status getStatus() {
- return status;
- }
-
- public IdentityTokens getTokens() {
- return tokens;
- }
-
- public Duration getDurationSinceLastRefresh() {
- return durationSinceLastRefresh;
- }
-
- public boolean isCstg() { return isCstg;}
-
- public boolean isRefreshed() {
- return Status.Refreshed.equals(this.status);
- }
-
- public boolean isOptOut() {
- return Status.Optout.equals(this.status);
- }
-
- public boolean isInvalidToken() {
- return Status.Invalid.equals(this.status);
- }
-
- public boolean isDeprecated() {
- return Status.Deprecated.equals(this.status);
- }
-
- public boolean isExpired() {
- return Status.Expired.equals(this.status);
- }
-
- public boolean noActiveKey() {
- return Status.NoActiveKey.equals(this.status);
- }
-
- public enum Status {
- Refreshed,
- Invalid,
- Optout,
- Expired,
- Deprecated,
- NoActiveKey
- }
-
-}
diff --git a/src/main/java/com/uid2/operator/model/RefreshToken.java b/src/main/java/com/uid2/operator/model/RefreshToken.java
deleted file mode 100644
index 8c70b3bf9..000000000
--- a/src/main/java/com/uid2/operator/model/RefreshToken.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.uid2.operator.model;
-
-import java.time.Instant;
-import com.uid2.shared.model.TokenVersion;
-
-public class RefreshToken extends VersionedToken {
- public final OperatorIdentity operatorIdentity;
- public final PublisherIdentity publisherIdentity;
- public final UserIdentity userIdentity;
-
- public RefreshToken(TokenVersion version, Instant createdAt, Instant expiresAt, OperatorIdentity operatorIdentity,
- PublisherIdentity publisherIdentity, UserIdentity userIdentity) {
- super(version, createdAt, expiresAt);
- this.operatorIdentity = operatorIdentity;
- this.publisherIdentity = publisherIdentity;
- this.userIdentity = userIdentity;
- }
-}
diff --git a/src/main/java/com/uid2/operator/model/SourcePublisher.java b/src/main/java/com/uid2/operator/model/SourcePublisher.java
new file mode 100644
index 000000000..bd19740a1
--- /dev/null
+++ b/src/main/java/com/uid2/operator/model/SourcePublisher.java
@@ -0,0 +1,24 @@
+package com.uid2.operator.model;
+
+// The original publisher that requests to generate a UID token
+public class SourcePublisher {
+ public final int siteId;
+
+ // these 2 values are added into adverting/UID token and refresh token payload but
+ // are not really used for any real purposes currently so sometimes are set to 0
+ // see the constructor below
+ public final int clientKeyId;
+ public final long publisherId;
+
+ public SourcePublisher(int siteId, int clientKeyId, long publisherId) {
+ this.siteId = siteId;
+ this.clientKeyId = clientKeyId;
+ this.publisherId = publisherId;
+ }
+
+ public SourcePublisher(int siteId) {
+ this.siteId = siteId;
+ this.clientKeyId = 0;
+ this.publisherId = 0;
+ }
+}
diff --git a/src/main/java/com/uid2/operator/model/TokenGenerateRequest.java b/src/main/java/com/uid2/operator/model/TokenGenerateRequest.java
new file mode 100644
index 000000000..39f3b56fc
--- /dev/null
+++ b/src/main/java/com/uid2/operator/model/TokenGenerateRequest.java
@@ -0,0 +1,40 @@
+package com.uid2.operator.model;
+
+import com.uid2.operator.model.identities.HashedDii;
+import com.uid2.operator.util.PrivacyBits;
+
+import java.time.Instant;
+
+public final class TokenGenerateRequest {
+ public final SourcePublisher sourcePublisher;
+ public final HashedDii hashedDii;
+ public final OptoutCheckPolicy optoutCheckPolicy;
+
+ public final PrivacyBits privacyBits;
+ public final Instant establishedAt;
+
+ public TokenGenerateRequest(
+ SourcePublisher sourcePublisher,
+ HashedDii hashedDii,
+ OptoutCheckPolicy tokenGeneratePolicy,
+ PrivacyBits privacyBits,
+ Instant establishedAt) {
+ this.sourcePublisher = sourcePublisher;
+ this.hashedDii = hashedDii;
+ this.optoutCheckPolicy = tokenGeneratePolicy;
+ this.privacyBits = privacyBits;
+ this.establishedAt = establishedAt;
+ }
+
+ public TokenGenerateRequest(
+ SourcePublisher sourcePublisher,
+ HashedDii hashedDii,
+ OptoutCheckPolicy tokenGeneratePolicy) {
+ this(sourcePublisher, hashedDii, tokenGeneratePolicy, PrivacyBits.DEFAULT, Instant.now());
+
+ }
+
+ public boolean shouldCheckOptOut() {
+ return optoutCheckPolicy.equals(OptoutCheckPolicy.RespectOptOut);
+ }
+}
diff --git a/src/main/java/com/uid2/operator/model/IdentityTokens.java b/src/main/java/com/uid2/operator/model/TokenGenerateResponse.java
similarity index 50%
rename from src/main/java/com/uid2/operator/model/IdentityTokens.java
rename to src/main/java/com/uid2/operator/model/TokenGenerateResponse.java
index 76dd2c4d7..21338f6f4 100644
--- a/src/main/java/com/uid2/operator/model/IdentityTokens.java
+++ b/src/main/java/com/uid2/operator/model/TokenGenerateResponse.java
@@ -1,20 +1,27 @@
package com.uid2.operator.model;
import com.uid2.shared.model.TokenVersion;
+import io.vertx.core.json.JsonObject;
import java.time.Instant;
-public class IdentityTokens {
- public static IdentityTokens LogoutToken = new IdentityTokens("", null, "", Instant.EPOCH, Instant.EPOCH, Instant.EPOCH);
+// this defines all the fields for the response of the /token/generate and /client/generate endpoints before they are
+// jsonified
+// todo: can be converted to record later
+public class TokenGenerateResponse {
+ public static final TokenGenerateResponse OptOutResponse = new TokenGenerateResponse("", null, "", Instant.EPOCH, Instant.EPOCH, Instant.EPOCH);
+
+ //aka UID token
private final String advertisingToken;
private final TokenVersion advertisingTokenVersion;
private final String refreshToken;
+ // when the advertising token/uid token expires
private final Instant identityExpires;
private final Instant refreshExpires;
private final Instant refreshFrom;
- public IdentityTokens(String advertisingToken, TokenVersion advertisingTokenVersion, String refreshToken,
- Instant identityExpires, Instant refreshExpires, Instant refreshFrom) {
+ public TokenGenerateResponse(String advertisingToken, TokenVersion advertisingTokenVersion, String refreshToken,
+ Instant identityExpires, Instant refreshExpires, Instant refreshFrom) {
this.advertisingToken = advertisingToken;
this.advertisingTokenVersion = advertisingTokenVersion;
this.refreshToken = refreshToken;
@@ -47,7 +54,18 @@ public Instant getRefreshFrom() {
return refreshFrom;
}
- public boolean isEmptyToken() {
+ public boolean isOptedOut() {
return advertisingToken == null || advertisingToken.isEmpty();
}
+
+ public JsonObject toTokenGenerateResponseJson() {
+ final JsonObject json = new JsonObject();
+ json.put("advertising_token", this.getAdvertisingToken());
+ json.put("refresh_token", this.getRefreshToken());
+ json.put("identity_expires", this.getIdentityExpires().toEpochMilli());
+ json.put("refresh_expires", this.getRefreshExpires().toEpochMilli());
+ json.put("refresh_from", this.getRefreshFrom().toEpochMilli());
+ return json;
+ }
}
+
diff --git a/src/main/java/com/uid2/operator/model/TokenRefreshRequest.java b/src/main/java/com/uid2/operator/model/TokenRefreshRequest.java
new file mode 100644
index 000000000..e2e51971a
--- /dev/null
+++ b/src/main/java/com/uid2/operator/model/TokenRefreshRequest.java
@@ -0,0 +1,26 @@
+package com.uid2.operator.model;
+
+import java.time.Instant;
+
+import com.uid2.operator.model.identities.FirstLevelHash;
+import com.uid2.operator.util.PrivacyBits;
+import com.uid2.shared.model.TokenVersion;
+
+// class containing enough data to create a new refresh token
+public class TokenRefreshRequest extends VersionedTokenRequest {
+ public final OperatorIdentity operatorIdentity;
+ public final SourcePublisher sourcePublisher;
+ public final FirstLevelHash firstLevelHash;
+ // by default, inherited from the previous refresh token's privacy bits
+ public final PrivacyBits privacyBits;
+
+
+ public TokenRefreshRequest(TokenVersion version, Instant createdAt, Instant expiresAt, OperatorIdentity operatorIdentity,
+ SourcePublisher sourcePublisher, FirstLevelHash firstLevelHash, PrivacyBits privacyBits) {
+ super(version, createdAt, expiresAt);
+ this.operatorIdentity = operatorIdentity;
+ this.sourcePublisher = sourcePublisher;
+ this.firstLevelHash = firstLevelHash;
+ this.privacyBits = privacyBits;
+ }
+}
diff --git a/src/main/java/com/uid2/operator/model/TokenRefreshResponse.java b/src/main/java/com/uid2/operator/model/TokenRefreshResponse.java
new file mode 100644
index 000000000..40e5d73c9
--- /dev/null
+++ b/src/main/java/com/uid2/operator/model/TokenRefreshResponse.java
@@ -0,0 +1,80 @@
+package com.uid2.operator.model;
+
+import java.time.Duration;
+
+public class TokenRefreshResponse {
+
+ public static final TokenRefreshResponse Invalid = new TokenRefreshResponse(Status.Invalid,
+ TokenGenerateResponse.OptOutResponse);
+ public static final TokenRefreshResponse Optout = new TokenRefreshResponse(Status.Optout, TokenGenerateResponse.OptOutResponse);
+ public static final TokenRefreshResponse Expired = new TokenRefreshResponse(Status.Expired, TokenGenerateResponse.OptOutResponse);
+ public static final TokenRefreshResponse Deprecated = new TokenRefreshResponse(Status.Deprecated, TokenGenerateResponse.OptOutResponse);
+ public static final TokenRefreshResponse NoActiveKey = new TokenRefreshResponse(Status.NoActiveKey, TokenGenerateResponse.OptOutResponse);
+ private final Status status;
+ private final TokenGenerateResponse tokenGenerateResponse;
+ private final Duration durationSinceLastRefresh;
+ private final boolean isCstg;
+
+ private TokenRefreshResponse(Status status, TokenGenerateResponse tokenGenerateResponse, Duration durationSinceLastRefresh, boolean isCstg) {
+ this.status = status;
+ this.tokenGenerateResponse = tokenGenerateResponse;
+ this.durationSinceLastRefresh = durationSinceLastRefresh;
+ this.isCstg = isCstg;
+ }
+
+ private TokenRefreshResponse(Status status, TokenGenerateResponse tokenGenerateResponse) {
+ this(status, tokenGenerateResponse, null, false);
+ }
+
+ public static TokenRefreshResponse createRefreshedResponse(TokenGenerateResponse tokenGenerateResponse, Duration durationSinceLastRefresh, boolean isCstg) {
+ return new TokenRefreshResponse(Status.Refreshed, tokenGenerateResponse, durationSinceLastRefresh, isCstg);
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public TokenGenerateResponse getIdentityResponse() {
+ return tokenGenerateResponse;
+ }
+
+ public Duration getDurationSinceLastRefresh() {
+ return durationSinceLastRefresh;
+ }
+
+ public boolean isCstg() { return isCstg;}
+
+ public boolean isRefreshed() {
+ return Status.Refreshed.equals(this.status);
+ }
+
+ public boolean isOptOut() {
+ return Status.Optout.equals(this.status);
+ }
+
+ public boolean isInvalidToken() {
+ return Status.Invalid.equals(this.status);
+ }
+
+ public boolean isDeprecated() {
+ return Status.Deprecated.equals(this.status);
+ }
+
+ public boolean isExpired() {
+ return Status.Expired.equals(this.status);
+ }
+
+ public boolean noActiveKey() {
+ return Status.NoActiveKey.equals(this.status);
+ }
+
+ public enum Status {
+ Refreshed,
+ Invalid,
+ Optout,
+ Expired,
+ Deprecated,
+ NoActiveKey
+ }
+
+}
diff --git a/src/main/java/com/uid2/operator/model/UserIdentity.java b/src/main/java/com/uid2/operator/model/UserIdentity.java
deleted file mode 100644
index 760d0ffb6..000000000
--- a/src/main/java/com/uid2/operator/model/UserIdentity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.uid2.operator.model;
-
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.Objects;
-
-public class UserIdentity {
- public final IdentityScope identityScope;
- public final IdentityType identityType;
- public final byte[] id;
- public final int privacyBits;
- public final Instant establishedAt;
- public final Instant refreshedAt;
-
- public UserIdentity(IdentityScope identityScope, IdentityType identityType, byte[] id, int privacyBits,
- Instant establishedAt, Instant refreshedAt) {
- this.identityScope = identityScope;
- this.identityType = identityType;
- this.id = id;
- this.privacyBits = privacyBits;
- this.establishedAt = establishedAt;
- this.refreshedAt = refreshedAt;
- }
-
- public boolean matches(UserIdentity that) {
- return this.identityScope.equals(that.identityScope) &&
- this.identityType.equals(that.identityType) &&
- Arrays.equals(this.id, that.id);
- }
-}
diff --git a/src/main/java/com/uid2/operator/model/VersionedToken.java b/src/main/java/com/uid2/operator/model/VersionedTokenRequest.java
similarity index 68%
rename from src/main/java/com/uid2/operator/model/VersionedToken.java
rename to src/main/java/com/uid2/operator/model/VersionedTokenRequest.java
index 5be86b80e..5cc9c5335 100644
--- a/src/main/java/com/uid2/operator/model/VersionedToken.java
+++ b/src/main/java/com/uid2/operator/model/VersionedTokenRequest.java
@@ -1,16 +1,16 @@
package com.uid2.operator.model;
import java.time.Instant;
-import java.util.Objects;
+
import com.uid2.shared.model.TokenVersion;
-public abstract class VersionedToken {
+public abstract class VersionedTokenRequest {
public final TokenVersion version;
public final Instant createdAt;
public final Instant expiresAt;
- public VersionedToken(TokenVersion version, Instant createdAt, Instant expiresAt) {
+ public VersionedTokenRequest(TokenVersion version, Instant createdAt, Instant expiresAt) {
this.version = version;
this.createdAt = createdAt;
this.expiresAt = expiresAt;
diff --git a/src/main/java/com/uid2/operator/model/IdentityType.java b/src/main/java/com/uid2/operator/model/identities/DiiType.java
similarity index 67%
rename from src/main/java/com/uid2/operator/model/IdentityType.java
rename to src/main/java/com/uid2/operator/model/identities/DiiType.java
index b64817df5..062b55d35 100644
--- a/src/main/java/com/uid2/operator/model/IdentityType.java
+++ b/src/main/java/com/uid2/operator/model/identities/DiiType.java
@@ -1,15 +1,15 @@
-package com.uid2.operator.model;
+package com.uid2.operator.model.identities;
import com.uid2.operator.vertx.ClientInputValidationException;
-public enum IdentityType {
+public enum DiiType {
Email(0), Phone(1);
public final int value;
- IdentityType(int value) { this.value = value; }
+ DiiType(int value) { this.value = value; }
- public static IdentityType fromValue(int value) {
+ public static DiiType fromValue(int value) {
switch (value) {
case 0: return Email;
case 1: return Phone;
diff --git a/src/main/java/com/uid2/operator/model/identities/FirstLevelHash.java b/src/main/java/com/uid2/operator/model/identities/FirstLevelHash.java
new file mode 100644
index 000000000..49b2728f4
--- /dev/null
+++ b/src/main/java/com/uid2/operator/model/identities/FirstLevelHash.java
@@ -0,0 +1,19 @@
+package com.uid2.operator.model.identities;
+
+import java.time.Instant;
+import java.util.Arrays;
+
+/**
+ * Contains a first level salted hash computed from Hashed DII (email/phone number)
+ * @param establishedAt for brand new token generation, it should be the time it is generated if the first level hash is from token/refresh call, it will be when the raw UID was originally created in the earliest token generation
+ */
+public record FirstLevelHash(IdentityScope identityScope, DiiType diiType, byte[] firstLevelHash,
+ Instant establishedAt) {
+
+ // explicitly not checking establishedAt - this is only for making sure the first level hash matches a new input
+ public boolean matches(FirstLevelHash that) {
+ return this.identityScope.equals(that.identityScope) &&
+ this.diiType.equals(that.diiType) &&
+ Arrays.equals(this.firstLevelHash, that.firstLevelHash);
+ }
+}
diff --git a/src/main/java/com/uid2/operator/model/identities/HashedDii.java b/src/main/java/com/uid2/operator/model/identities/HashedDii.java
new file mode 100644
index 000000000..64c7bbf0f
--- /dev/null
+++ b/src/main/java/com/uid2/operator/model/identities/HashedDii.java
@@ -0,0 +1,7 @@
+package com.uid2.operator.model.identities;
+
+// Contains a hash Directly Identifying Information (DII) (email or phone) see https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii
+// This hash can either be computed from a raw email/phone number DII input or provided by the UID Participant directly
+//
+public record HashedDii(IdentityScope identityScope, DiiType diiType, byte[] hashedDii) {
+}
diff --git a/src/main/java/com/uid2/operator/IdentityConst.java b/src/main/java/com/uid2/operator/model/identities/IdentityConst.java
similarity index 65%
rename from src/main/java/com/uid2/operator/IdentityConst.java
rename to src/main/java/com/uid2/operator/model/identities/IdentityConst.java
index ae1ad6974..63fa62f96 100644
--- a/src/main/java/com/uid2/operator/IdentityConst.java
+++ b/src/main/java/com/uid2/operator/model/identities/IdentityConst.java
@@ -1,20 +1,24 @@
-package com.uid2.operator;
+package com.uid2.operator.model.identities;
import com.uid2.operator.service.EncodingUtils;
public class IdentityConst {
-
+ // DIIs for generating optout tokens for legacy participants - to be deprecated
public static final String OptOutTokenIdentityForEmail = "optout@unifiedid.com";
public static final String OptOutTokenIdentityForPhone = "+00000000001";
+
+ // DIIs for testing with token/validate endpoint, see https://unifiedid.com/docs/endpoints/post-token-validate
public static final String ValidateIdentityForEmail = "validate@example.com";
public static final String ValidateIdentityForPhone = "+12345678901";
public static final byte[] ValidateIdentityForEmailHash = EncodingUtils.getSha256Bytes(IdentityConst.ValidateIdentityForEmail);
public static final byte[] ValidateIdentityForPhoneHash = EncodingUtils.getSha256Bytes(IdentityConst.ValidateIdentityForPhone);
+
+ // DIIs to use when you want to generate an optout response in token generation or identity map
public static final String OptOutIdentityForEmail = "optout@example.com";
public static final String OptOutIdentityForPhone = "+00000000000";
+
+ // DIIs to use when you want to generate a UID token but when doing refresh token, you want to always get an optout response
+ // to test the optout handling workflow
public static final String RefreshOptOutIdentityForEmail = "refresh-optout@example.com";
public static final String RefreshOptOutIdentityForPhone = "+00000000002";
-
-
-
}
diff --git a/src/main/java/com/uid2/operator/model/IdentityScope.java b/src/main/java/com/uid2/operator/model/identities/IdentityScope.java
similarity index 94%
rename from src/main/java/com/uid2/operator/model/IdentityScope.java
rename to src/main/java/com/uid2/operator/model/identities/IdentityScope.java
index 0bff1edc1..3dc19a764 100644
--- a/src/main/java/com/uid2/operator/model/IdentityScope.java
+++ b/src/main/java/com/uid2/operator/model/identities/IdentityScope.java
@@ -1,4 +1,4 @@
-package com.uid2.operator.model;
+package com.uid2.operator.model.identities;
import com.uid2.operator.vertx.ClientInputValidationException;
diff --git a/src/main/java/com/uid2/operator/model/identities/RawUid.java b/src/main/java/com/uid2/operator/model/identities/RawUid.java
new file mode 100644
index 000000000..4ae619d00
--- /dev/null
+++ b/src/main/java/com/uid2/operator/model/identities/RawUid.java
@@ -0,0 +1,13 @@
+package com.uid2.operator.model.identities;
+
+import java.util.Arrays;
+
+// A raw UID is stored inside
+public record RawUid(IdentityScope identityScope, DiiType diiType, byte[] rawUid) {
+
+ public boolean matches(RawUid that) {
+ return this.identityScope.equals(that.identityScope) &&
+ this.diiType.equals(that.diiType) &&
+ Arrays.equals(this.rawUid, that.rawUid);
+ }
+}
diff --git a/src/main/java/com/uid2/operator/monitoring/TokenResponseStatsCollector.java b/src/main/java/com/uid2/operator/monitoring/TokenResponseStatsCollector.java
index 523afa16f..b5a76b8f1 100644
--- a/src/main/java/com/uid2/operator/monitoring/TokenResponseStatsCollector.java
+++ b/src/main/java/com/uid2/operator/monitoring/TokenResponseStatsCollector.java
@@ -1,6 +1,6 @@
package com.uid2.operator.monitoring;
-import com.uid2.operator.model.RefreshResponse;
+import com.uid2.operator.model.TokenRefreshResponse;
import com.uid2.operator.vertx.UIDOperatorVerticle;
import com.uid2.shared.model.TokenVersion;
import com.uid2.shared.store.ISiteStore;
@@ -65,17 +65,17 @@ private static void recordInternal(ISiteStore siteStore, Integer siteId, Endpoin
builder.register(Metrics.globalRegistry).increment();
}
- public static void recordRefresh(ISiteStore siteStore, Integer siteId, Endpoint endpoint, RefreshResponse refreshResponse, PlatformType platformType) {
+ public static void recordRefresh(ISiteStore siteStore, Integer siteId, Endpoint endpoint, TokenRefreshResponse refreshResponse, PlatformType platformType) {
if (!refreshResponse.isRefreshed()) {
if (refreshResponse.isOptOut() || refreshResponse.isDeprecated()) {
- recordInternal(siteStore, siteId, endpoint, ResponseStatus.OptOut, refreshResponse.getTokens().getAdvertisingTokenVersion(), refreshResponse.isCstg(), platformType);
+ recordInternal(siteStore, siteId, endpoint, ResponseStatus.OptOut, refreshResponse.getIdentityResponse().getAdvertisingTokenVersion(), refreshResponse.isCstg(), platformType);
} else if (refreshResponse.isInvalidToken()) {
- recordInternal(siteStore, siteId, endpoint, ResponseStatus.InvalidToken, refreshResponse.getTokens().getAdvertisingTokenVersion(), refreshResponse.isCstg(), platformType);
+ recordInternal(siteStore, siteId, endpoint, ResponseStatus.InvalidToken, refreshResponse.getIdentityResponse().getAdvertisingTokenVersion(), refreshResponse.isCstg(), platformType);
} else if (refreshResponse.isExpired()) {
- recordInternal(siteStore, siteId, endpoint, ResponseStatus.ExpiredToken, refreshResponse.getTokens().getAdvertisingTokenVersion(), refreshResponse.isCstg(), platformType);
+ recordInternal(siteStore, siteId, endpoint, ResponseStatus.ExpiredToken, refreshResponse.getIdentityResponse().getAdvertisingTokenVersion(), refreshResponse.isCstg(), platformType);
}
} else {
- recordInternal(siteStore, siteId, endpoint, ResponseStatus.Success, refreshResponse.getTokens().getAdvertisingTokenVersion(), refreshResponse.isCstg(), platformType);
+ recordInternal(siteStore, siteId, endpoint, ResponseStatus.Success, refreshResponse.getIdentityResponse().getAdvertisingTokenVersion(), refreshResponse.isCstg(), platformType);
}
}
}
diff --git a/src/main/java/com/uid2/operator/service/EncryptedTokenEncoder.java b/src/main/java/com/uid2/operator/service/EncryptedTokenEncoder.java
index f151f0919..cb5f03ccc 100644
--- a/src/main/java/com/uid2/operator/service/EncryptedTokenEncoder.java
+++ b/src/main/java/com/uid2/operator/service/EncryptedTokenEncoder.java
@@ -1,6 +1,11 @@
package com.uid2.operator.service;
import com.uid2.operator.model.*;
+import com.uid2.operator.model.identities.DiiType;
+import com.uid2.operator.model.identities.FirstLevelHash;
+import com.uid2.operator.model.identities.IdentityScope;
+import com.uid2.operator.model.identities.RawUid;
+import com.uid2.operator.util.PrivacyBits;
import com.uid2.operator.vertx.ClientInputValidationException;
import com.uid2.shared.Const.Data;
import com.uid2.shared.encryption.AesCbc;
@@ -22,16 +27,16 @@ public EncryptedTokenEncoder(KeyManager keyManager) {
this.keyManager = keyManager;
}
- public byte[] encode(AdvertisingToken t, Instant asOf) {
+ public byte[] encodeIntoAdvertisingToken(AdvertisingTokenRequest t, Instant asOf) {
final KeysetKey masterKey = this.keyManager.getMasterKey(asOf);
- final KeysetKey siteEncryptionKey = this.keyManager.getActiveKeyBySiteIdWithFallback(t.publisherIdentity.siteId, Data.AdvertisingTokenSiteId, asOf);
+ final KeysetKey siteEncryptionKey = this.keyManager.getActiveKeyBySiteIdWithFallback(t.sourcePublisher.siteId, Data.AdvertisingTokenSiteId, asOf);
return t.version == TokenVersion.V2
- ? encodeV2(t, masterKey, siteEncryptionKey)
- : encodeV3(t, masterKey, siteEncryptionKey); //TokenVersion.V4 also calls encodeV3() since the byte array is identical between V3 and V4
+ ? encodeIntoAdvertisingTokenV2(t, masterKey, siteEncryptionKey)
+ : encodeIntoAdvertisingTokenV3(t, masterKey, siteEncryptionKey); //TokenVersion.V4 also calls encodeV3() since the byte array is identical between V3 and V4
}
- private byte[] encodeV2(AdvertisingToken t, KeysetKey masterKey, KeysetKey siteKey) {
+ private byte[] encodeIntoAdvertisingTokenV2(AdvertisingTokenRequest t, KeysetKey masterKey, KeysetKey siteKey) {
final Buffer b = Buffer.buffer();
b.appendByte((byte) t.version.rawVersion);
@@ -39,7 +44,7 @@ private byte[] encodeV2(AdvertisingToken t, KeysetKey masterKey, KeysetKey siteK
Buffer b2 = Buffer.buffer();
b2.appendLong(t.expiresAt.toEpochMilli());
- encodeSiteIdentityV2(b2, t.publisherIdentity, t.userIdentity, siteKey);
+ encodeSiteIdentityV2(b2, t.sourcePublisher, t.rawUid, siteKey, t.privacyBits, t.establishedAt);
final byte[] encryptedId = AesCbc.encrypt(b2.getBytes(), masterKey).getPayload();
@@ -48,13 +53,15 @@ private byte[] encodeV2(AdvertisingToken t, KeysetKey masterKey, KeysetKey siteK
return b.getBytes();
}
- private byte[] encodeV3(AdvertisingToken t, KeysetKey masterKey, KeysetKey siteKey) {
+ private byte[] encodeIntoAdvertisingTokenV3(AdvertisingTokenRequest t, KeysetKey masterKey, KeysetKey siteKey) {
final Buffer sitePayload = Buffer.buffer(69);
- encodePublisherIdentityV3(sitePayload, t.publisherIdentity);
- sitePayload.appendInt(t.userIdentity.privacyBits);
- sitePayload.appendLong(t.userIdentity.establishedAt.toEpochMilli());
- sitePayload.appendLong(t.userIdentity.refreshedAt.toEpochMilli());
- sitePayload.appendBytes(t.userIdentity.id); // 32 or 33 bytes
+ encodePublisherRequesterV3(sitePayload, t.sourcePublisher);
+ sitePayload.appendInt(t.privacyBits.getAsInt());
+ sitePayload.appendLong(t.establishedAt.toEpochMilli());
+ // this is the refreshedAt field in the spec - but effectively it is the time this advertising token is generated
+ // this is a redundant field as it is stored in master payload again, can consider dropping this field in future token version
+ sitePayload.appendLong(t.createdAt.toEpochMilli());
+ sitePayload.appendBytes(t.rawUid.rawUid()); // 32 or 33 bytes
final Buffer masterPayload = Buffer.buffer(130);
masterPayload.appendLong(t.expiresAt.toEpochMilli());
@@ -64,7 +71,7 @@ private byte[] encodeV3(AdvertisingToken t, KeysetKey masterKey, KeysetKey siteK
masterPayload.appendBytes(AesGcm.encrypt(sitePayload.getBytes(), siteKey).getPayload());
final Buffer b = Buffer.buffer(164);
- b.appendByte(encodeIdentityTypeV3(t.userIdentity));
+ b.appendByte(encodeIdentityTypeV3(t.rawUid.identityScope(), t.rawUid.diiType()));
b.appendByte((byte) t.version.rawVersion);
b.appendInt(masterKey.getId());
b.appendBytes(AesGcm.encrypt(masterPayload.getBytes(), masterKey).getPayload());
@@ -73,7 +80,7 @@ private byte[] encodeV3(AdvertisingToken t, KeysetKey masterKey, KeysetKey siteK
}
@Override
- public RefreshToken decodeRefreshToken(String s) {
+ public TokenRefreshRequest decodeRefreshToken(String s) {
if (s != null && !s.isEmpty()) {
final byte[] bytes;
try {
@@ -92,7 +99,7 @@ public RefreshToken decodeRefreshToken(String s) {
throw new ClientInputValidationException("Invalid refresh token version");
}
- private RefreshToken decodeRefreshTokenV2(Buffer b) {
+ private TokenRefreshRequest decodeRefreshTokenV2(Buffer b) {
final Instant createdAt = Instant.ofEpochMilli(b.getLong(1));
//final Instant expiresAt = Instant.ofEpochMilli(b.getLong(9));
final Instant validTill = Instant.ofEpochMilli(b.getLong(17));
@@ -117,17 +124,19 @@ private RefreshToken decodeRefreshTokenV2(Buffer b) {
throw new ClientInputValidationException("Failed to decode refreshTokenV2: Identity segment is not valid base64.", e);
}
- final int privacyBits = b2.getInt(8 + length);
+ final PrivacyBits privacyBits = PrivacyBits.fromInt(b2.getInt(8 + length));
final long establishedMillis = b2.getLong(8 + length + 4);
- return new RefreshToken(
+ return new TokenRefreshRequest(
TokenVersion.V2, createdAt, validTill,
new OperatorIdentity(0, OperatorType.Service, 0, 0),
- new PublisherIdentity(siteId, 0, 0),
- new UserIdentity(IdentityScope.UID2, IdentityType.Email, identity, privacyBits, Instant.ofEpochMilli(establishedMillis), null));
+ new SourcePublisher(siteId),
+ new FirstLevelHash(IdentityScope.UID2, DiiType.Email, identity,
+ Instant.ofEpochMilli(establishedMillis)),
+ privacyBits);
}
- private RefreshToken decodeRefreshTokenV3(Buffer b, byte[] bytes) {
+ private TokenRefreshRequest decodeRefreshTokenV3(Buffer b, byte[] bytes) {
final int keyId = b.getInt(2);
final KeysetKey key = this.keyManager.getKey(keyId);
@@ -141,27 +150,28 @@ private RefreshToken decodeRefreshTokenV3(Buffer b, byte[] bytes) {
final Instant expiresAt = Instant.ofEpochMilli(b2.getLong(0));
final Instant createdAt = Instant.ofEpochMilli(b2.getLong(8));
final OperatorIdentity operatorIdentity = decodeOperatorIdentityV3(b2, 16);
- final PublisherIdentity publisherIdentity = decodePublisherIdentityV3(b2, 29);
- final int privacyBits = b2.getInt(45);
+ final SourcePublisher sourcePublisher = decodeSourcePublisherV3(b2, 29);
+ final PrivacyBits privacyBits = PrivacyBits.fromInt(b2.getInt(45));
final Instant establishedAt = Instant.ofEpochMilli(b2.getLong(49));
final IdentityScope identityScope = decodeIdentityScopeV3(b2.getByte(57));
- final IdentityType identityType = decodeIdentityTypeV3(b2.getByte(57));
- final byte[] id = b2.getBytes(58, 90);
+ final DiiType diiType = decodeIdentityTypeV3(b2.getByte(57));
+ final byte[] firstLevelHash = b2.getBytes(58, 90);
if (identityScope != decodeIdentityScopeV3(b.getByte(0))) {
throw new ClientInputValidationException("Failed to decode refreshTokenV3: Identity scope mismatch");
}
- if (identityType != decodeIdentityTypeV3(b.getByte(0))) {
+ if (diiType != decodeIdentityTypeV3(b.getByte(0))) {
throw new ClientInputValidationException("Failed to decode refreshTokenV3: Identity type mismatch");
}
- return new RefreshToken(
- TokenVersion.V3, createdAt, expiresAt, operatorIdentity, publisherIdentity,
- new UserIdentity(identityScope, identityType, id, privacyBits, establishedAt, null));
+ return new TokenRefreshRequest(
+ TokenVersion.V3, createdAt, expiresAt, operatorIdentity, sourcePublisher,
+ new FirstLevelHash(identityScope, diiType, firstLevelHash, establishedAt),
+ privacyBits);
}
@Override
- public AdvertisingToken decodeAdvertisingToken(String base64AdvertisingToken) {
+ public AdvertisingTokenRequest decodeAdvertisingToken(String base64AdvertisingToken) {
//Logic and code copied from: https://github.com/IABTechLab/uid2-client-java/blob/0220ef43c1661ecf3b8f4ed2db524e2db31c06b5/src/main/java/com/uid2/client/Uid2Encryption.java#L37
if (base64AdvertisingToken.length() < 4) {
throw new ClientInputValidationException("Advertising token is too short");
@@ -196,7 +206,7 @@ public AdvertisingToken decodeAdvertisingToken(String base64AdvertisingToken) {
return decodeAdvertisingTokenV3orV4(b, bytes, tokenVersion);
}
- public AdvertisingToken decodeAdvertisingTokenV2(Buffer b) {
+ public AdvertisingTokenRequest decodeAdvertisingTokenV2(Buffer b) {
try {
final int masterKeyId = b.getInt(1);
@@ -214,18 +224,20 @@ public AdvertisingToken decodeAdvertisingTokenV2(Buffer b) {
final int siteId = b3.getInt(0);
final int length = b3.getInt(4);
- final byte[] advertisingId = EncodingUtils.fromBase64(b3.slice(8, 8 + length).getBytes());
+ final byte[] rawUid = EncodingUtils.fromBase64(b3.slice(8, 8 + length).getBytes());
- final int privacyBits = b3.getInt(8 + length);
+ final PrivacyBits privacyBits = PrivacyBits.fromInt(b3.getInt(8 + length));
final long establishedMillis = b3.getLong(8 + length + 4);
- return new AdvertisingToken(
+ return new AdvertisingTokenRequest(
TokenVersion.V2,
Instant.ofEpochMilli(establishedMillis),
Instant.ofEpochMilli(expiresMillis),
new OperatorIdentity(0, OperatorType.Service, 0, masterKeyId),
- new PublisherIdentity(siteId, siteKeyId, 0),
- new UserIdentity(IdentityScope.UID2, IdentityType.Email, advertisingId, privacyBits, Instant.ofEpochMilli(establishedMillis), null)
+ new SourcePublisher(siteId, siteKeyId, 0),
+ new RawUid(IdentityScope.UID2, DiiType.Email, rawUid),
+ privacyBits,
+ Instant.ofEpochMilli(establishedMillis)
);
} catch (Exception e) {
@@ -234,7 +246,7 @@ public AdvertisingToken decodeAdvertisingTokenV2(Buffer b) {
}
- public AdvertisingToken decodeAdvertisingTokenV3orV4(Buffer b, byte[] bytes, TokenVersion tokenVersion) {
+ public AdvertisingTokenRequest decodeAdvertisingTokenV3orV4(Buffer b, byte[] bytes, TokenVersion tokenVersion) {
final int masterKeyId = b.getInt(2);
final byte[] masterPayloadBytes = AesGcm.decrypt(bytes, 6, this.keyManager.getKey(masterKeyId));
@@ -245,27 +257,29 @@ public AdvertisingToken decodeAdvertisingTokenV3orV4(Buffer b, byte[] bytes, Tok
final int siteKeyId = masterPayload.getInt(29);
final Buffer sitePayload = Buffer.buffer(AesGcm.decrypt(masterPayloadBytes, 33, this.keyManager.getKey(siteKeyId)));
- final PublisherIdentity publisherIdentity = decodePublisherIdentityV3(sitePayload, 0);
- final int privacyBits = sitePayload.getInt(16);
+ final SourcePublisher sourcePublisher = decodeSourcePublisherV3(sitePayload, 0);
+ final PrivacyBits privacyBits = PrivacyBits.fromInt(sitePayload.getInt(16));
final Instant establishedAt = Instant.ofEpochMilli(sitePayload.getLong(20));
+ // refreshedAt is currently not used
final Instant refreshedAt = Instant.ofEpochMilli(sitePayload.getLong(28));
- final byte[] id = sitePayload.slice(36, sitePayload.length()).getBytes();
- final IdentityScope identityScope = id.length == 32 ? IdentityScope.UID2 : decodeIdentityScopeV3(id[0]);
- final IdentityType identityType = id.length == 32 ? IdentityType.Email : decodeIdentityTypeV3(id[0]);
+ final byte[] rawUid = sitePayload.slice(36, sitePayload.length()).getBytes();
+ final IdentityScope identityScope = rawUid.length == 32 ? IdentityScope.UID2 : decodeIdentityScopeV3(rawUid[0]);
+ final DiiType diiType = rawUid.length == 32 ? DiiType.Email : decodeIdentityTypeV3(rawUid[0]);
- if (id.length > 32)
+ if (rawUid.length > 32)
{
if (identityScope != decodeIdentityScopeV3(b.getByte(0))) {
throw new ClientInputValidationException("Failed decoding advertisingTokenV3: Identity scope mismatch");
}
- if (identityType != decodeIdentityTypeV3(b.getByte(0))) {
+ if (diiType != decodeIdentityTypeV3(b.getByte(0))) {
throw new ClientInputValidationException("Failed decoding advertisingTokenV3: Identity type mismatch");
}
}
- return new AdvertisingToken(
- tokenVersion, createdAt, expiresAt, operatorIdentity, publisherIdentity,
- new UserIdentity(identityScope, identityType, id, privacyBits, establishedAt, refreshedAt)
+ return new AdvertisingTokenRequest(
+ tokenVersion, createdAt, expiresAt, operatorIdentity, sourcePublisher,
+ new RawUid(identityScope, diiType, rawUid),
+ privacyBits, establishedAt
);
}
@@ -277,22 +291,22 @@ private void recordRefreshTokenVersionCount(String siteId, TokenVersion tokenVer
.register(Metrics.globalRegistry).increment();
}
- public byte[] encode(RefreshToken t, Instant asOf) {
+ public byte[] encodeIntoRefreshToken(TokenRefreshRequest t, Instant asOf) {
final KeysetKey serviceKey = this.keyManager.getRefreshKey(asOf);
switch (t.version) {
case V2:
- recordRefreshTokenVersionCount(String.valueOf(t.publisherIdentity.siteId), TokenVersion.V2);
- return encodeV2(t, serviceKey);
+ recordRefreshTokenVersionCount(String.valueOf(t.sourcePublisher.siteId), TokenVersion.V2);
+ return encodeIntoRefreshTokenV2(t, serviceKey);
case V3:
- recordRefreshTokenVersionCount(String.valueOf(t.publisherIdentity.siteId), TokenVersion.V3);
- return encodeV3(t, serviceKey);
+ recordRefreshTokenVersionCount(String.valueOf(t.sourcePublisher.siteId), TokenVersion.V3);
+ return encodeIntoRefreshTokenV3(t, serviceKey);
default:
throw new ClientInputValidationException("RefreshToken version " + t.version + " not supported");
}
}
- public byte[] encodeV2(RefreshToken t, KeysetKey serviceKey) {
+ public byte[] encodeIntoRefreshTokenV2(TokenRefreshRequest t, KeysetKey serviceKey) {
final Buffer b = Buffer.buffer();
b.appendByte((byte) t.version.rawVersion);
b.appendLong(t.createdAt.toEpochMilli());
@@ -300,24 +314,25 @@ public byte[] encodeV2(RefreshToken t, KeysetKey serviceKey) {
// give an extra minute for clients which are trying to refresh tokens close to or at the refresh expiry timestamp
b.appendLong(t.expiresAt.plusSeconds(60).toEpochMilli());
b.appendInt(serviceKey.getId());
- final byte[] encryptedIdentity = encryptIdentityV2(t.publisherIdentity, t.userIdentity, serviceKey);
+ final byte[] encryptedIdentity = encryptIdentityV2(t.sourcePublisher, t.firstLevelHash, serviceKey,
+ t.privacyBits);
b.appendBytes(encryptedIdentity);
return b.getBytes();
}
- public byte[] encodeV3(RefreshToken t, KeysetKey serviceKey) {
+ public byte[] encodeIntoRefreshTokenV3(TokenRefreshRequest t, KeysetKey serviceKey) {
final Buffer refreshPayload = Buffer.buffer(90);
refreshPayload.appendLong(t.expiresAt.toEpochMilli());
refreshPayload.appendLong(t.createdAt.toEpochMilli());
encodeOperatorIdentityV3(refreshPayload, t.operatorIdentity);
- encodePublisherIdentityV3(refreshPayload, t.publisherIdentity);
- refreshPayload.appendInt(t.userIdentity.privacyBits);
- refreshPayload.appendLong(t.userIdentity.establishedAt.toEpochMilli());
- refreshPayload.appendByte(encodeIdentityTypeV3(t.userIdentity));
- refreshPayload.appendBytes(t.userIdentity.id);
+ encodePublisherRequesterV3(refreshPayload, t.sourcePublisher);
+ refreshPayload.appendInt(t.privacyBits.getAsInt());
+ refreshPayload.appendLong(t.firstLevelHash.establishedAt().toEpochMilli());
+ refreshPayload.appendByte(encodeIdentityTypeV3(t.firstLevelHash.identityScope(), t.firstLevelHash.diiType()));
+ refreshPayload.appendBytes(t.firstLevelHash.firstLevelHash());
final Buffer b = Buffer.buffer(124);
- b.appendByte(encodeIdentityTypeV3(t.userIdentity));
+ b.appendByte(encodeIdentityTypeV3(t.firstLevelHash.identityScope(), t.firstLevelHash.diiType()));
b.appendByte((byte) t.version.rawVersion);
b.appendInt(serviceKey.getId());
b.appendBytes(AesGcm.encrypt(refreshPayload.getBytes(), serviceKey).getPayload());
@@ -325,9 +340,10 @@ public byte[] encodeV3(RefreshToken t, KeysetKey serviceKey) {
return b.getBytes();
}
- private void encodeSiteIdentityV2(Buffer b, PublisherIdentity publisherIdentity, UserIdentity userIdentity, KeysetKey siteEncryptionKey) {
+ private void encodeSiteIdentityV2(Buffer b, SourcePublisher sourcePublisher, RawUid rawUid,
+ KeysetKey siteEncryptionKey, PrivacyBits privacyBits, Instant establishedAt) {
b.appendInt(siteEncryptionKey.getId());
- final byte[] encryptedIdentity = encryptIdentityV2(publisherIdentity, userIdentity, siteEncryptionKey);
+ final byte[] encryptedIdentity = encryptIdentityV2(sourcePublisher, rawUid, siteEncryptionKey, privacyBits, establishedAt);
b.appendBytes(encryptedIdentity);
}
@@ -337,38 +353,59 @@ public static String bytesToBase64Token(byte[] advertisingTokenBytes, TokenVersi
}
@Override
- public IdentityTokens encode(AdvertisingToken advertisingToken, RefreshToken refreshToken, Instant refreshFrom, Instant asOf) {
-
- final byte[] advertisingTokenBytes = encode(advertisingToken, asOf);
- final String base64AdvertisingToken = bytesToBase64Token(advertisingTokenBytes, advertisingToken.version);
-
- return new IdentityTokens(
- base64AdvertisingToken,
- advertisingToken.version,
- EncodingUtils.toBase64String(encode(refreshToken, asOf)),
- advertisingToken.expiresAt,
- refreshToken.expiresAt,
+ public TokenGenerateResponse encodeIntoIdentityResponse(AdvertisingTokenRequest advertisingTokenRequest, TokenRefreshRequest tokenRefreshRequest, Instant refreshFrom, Instant asOf) {
+ final String advertisingToken = generateAdvertisingTokenString(advertisingTokenRequest, asOf);
+ final String refreshToken = generateRefreshTokenString(tokenRefreshRequest, asOf);
+ return new TokenGenerateResponse(
+ advertisingToken,
+ advertisingTokenRequest.version,
+ refreshToken,
+ advertisingTokenRequest.expiresAt,
+ tokenRefreshRequest.expiresAt,
refreshFrom
);
}
- private byte[] encryptIdentityV2(PublisherIdentity publisherIdentity, UserIdentity identity, KeysetKey key) {
+ private String generateRefreshTokenString(TokenRefreshRequest tokenRefreshRequest, Instant asOf) {
+ return EncodingUtils.toBase64String(encodeIntoRefreshToken(tokenRefreshRequest, asOf));
+ }
+
+ private String generateAdvertisingTokenString(AdvertisingTokenRequest advertisingTokenRequest, Instant asOf) {
+ final byte[] advertisingTokenBytes = encodeIntoAdvertisingToken(advertisingTokenRequest, asOf);
+ return bytesToBase64Token(advertisingTokenBytes, advertisingTokenRequest.version);
+ }
+
+ private byte[] encryptIdentityV2(SourcePublisher sourcePublisher, FirstLevelHash firstLevelHash,
+ KeysetKey key, PrivacyBits privacyBits) {
+ return encryptIdentityV2(sourcePublisher, firstLevelHash.firstLevelHash(), privacyBits,
+ firstLevelHash.establishedAt(), key);
+ }
+
+ private byte[] encryptIdentityV2(SourcePublisher sourcePublisher, RawUid rawUid,
+ KeysetKey key, PrivacyBits privacyBits, Instant establishedAt) {
+ return encryptIdentityV2(sourcePublisher, rawUid.rawUid(), privacyBits,
+ establishedAt, key);
+ }
+
+
+ private byte[] encryptIdentityV2(SourcePublisher sourcePublisher, byte[] id, PrivacyBits privacyBits,
+ Instant establishedAt, KeysetKey key) {
Buffer b = Buffer.buffer();
try {
- b.appendInt(publisherIdentity.siteId);
- final byte[] identityBytes = EncodingUtils.toBase64(identity.id);
+ b.appendInt(sourcePublisher.siteId);
+ final byte[] identityBytes = EncodingUtils.toBase64(id);
b.appendInt(identityBytes.length);
b.appendBytes(identityBytes);
- b.appendInt(identity.privacyBits);
- b.appendLong(identity.establishedAt.toEpochMilli());
+ b.appendInt(privacyBits.getAsInt());
+ b.appendLong(establishedAt.toEpochMilli());
return AesCbc.encrypt(b.getBytes(), key).getPayload();
} catch (Exception e) {
throw new RuntimeException("Could not turn Identity into UTF-8", e);
}
}
- static private byte encodeIdentityTypeV3(UserIdentity userIdentity) {
- return (byte) (TokenUtils.encodeIdentityScope(userIdentity.identityScope) | (userIdentity.identityType.value << 2) | 3);
+ static private byte encodeIdentityTypeV3(IdentityScope identityScope, DiiType diiType) {
+ return (byte) (TokenUtils.encodeIdentityScope(identityScope) | (diiType.value << 2) | 3);
// "| 3" is used so that the 2nd char matches the version when V3 or higher. Eg "3" for V3 and "4" for V4
}
@@ -376,18 +413,18 @@ static private IdentityScope decodeIdentityScopeV3(byte value) {
return IdentityScope.fromValue((value & 0x10) >> 4);
}
- static private IdentityType decodeIdentityTypeV3(byte value) {
- return IdentityType.fromValue((value & 0xf) >> 2);
+ static private DiiType decodeIdentityTypeV3(byte value) {
+ return DiiType.fromValue((value & 0xf) >> 2);
}
- static void encodePublisherIdentityV3(Buffer b, PublisherIdentity publisherIdentity) {
- b.appendInt(publisherIdentity.siteId);
- b.appendLong(publisherIdentity.publisherId);
- b.appendInt(publisherIdentity.clientKeyId);
+ static void encodePublisherRequesterV3(Buffer b, SourcePublisher sourcePublisher) {
+ b.appendInt(sourcePublisher.siteId);
+ b.appendLong(sourcePublisher.publisherId);
+ b.appendInt(sourcePublisher.clientKeyId);
}
- static PublisherIdentity decodePublisherIdentityV3(Buffer b, int offset) {
- return new PublisherIdentity(b.getInt(offset), b.getInt(offset + 12), b.getLong(offset + 4));
+ static SourcePublisher decodeSourcePublisherV3(Buffer b, int offset) {
+ return new SourcePublisher(b.getInt(offset), b.getInt(offset + 12), b.getLong(offset + 4));
}
static void encodeOperatorIdentityV3(Buffer b, OperatorIdentity operatorIdentity) {
diff --git a/src/main/java/com/uid2/operator/service/ITokenEncoder.java b/src/main/java/com/uid2/operator/service/ITokenEncoder.java
index 71ab4c5f2..73fe2e70c 100644
--- a/src/main/java/com/uid2/operator/service/ITokenEncoder.java
+++ b/src/main/java/com/uid2/operator/service/ITokenEncoder.java
@@ -1,15 +1,15 @@
package com.uid2.operator.service;
-import com.uid2.operator.model.AdvertisingToken;
-import com.uid2.operator.model.IdentityTokens;
-import com.uid2.operator.model.RefreshToken;
+import com.uid2.operator.model.AdvertisingTokenRequest;
+import com.uid2.operator.model.TokenGenerateResponse;
+import com.uid2.operator.model.TokenRefreshRequest;
import java.time.Instant;
public interface ITokenEncoder {
- IdentityTokens encode(AdvertisingToken advertisingToken, RefreshToken refreshToken, Instant refreshFrom, Instant asOf);
+ TokenGenerateResponse encodeIntoIdentityResponse(AdvertisingTokenRequest advertisingTokenRequest, TokenRefreshRequest tokenRefreshRequest, Instant refreshFrom, Instant asOf);
- AdvertisingToken decodeAdvertisingToken(String base64String);
+ AdvertisingTokenRequest decodeAdvertisingToken(String base64String);
- RefreshToken decodeRefreshToken(String base64String);
+ TokenRefreshRequest decodeRefreshToken(String base64String);
}
diff --git a/src/main/java/com/uid2/operator/service/IUIDOperatorService.java b/src/main/java/com/uid2/operator/service/IUIDOperatorService.java
index e824805f1..d877940b7 100644
--- a/src/main/java/com/uid2/operator/service/IUIDOperatorService.java
+++ b/src/main/java/com/uid2/operator/service/IUIDOperatorService.java
@@ -1,6 +1,7 @@
package com.uid2.operator.service;
import com.uid2.operator.model.*;
+import com.uid2.operator.model.identities.HashedDii;
import com.uid2.shared.model.SaltEntry;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
@@ -11,20 +12,20 @@
public interface IUIDOperatorService {
- IdentityTokens generateIdentity(IdentityRequest request, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter);
+ TokenGenerateResponse generateIdentity(TokenGenerateRequest request, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter);
- RefreshResponse refreshIdentity(RefreshToken token, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter);
+ TokenRefreshResponse refreshIdentity(TokenRefreshRequest input, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter);
- MappedIdentity mapIdentity(MapRequest request);
+ IdentityMapResponseItem mapHashedDii(IdentityMapRequestItem request);
@Deprecated
- MappedIdentity map(UserIdentity userIdentity, Instant asOf);
+ IdentityMapResponseItem map(HashedDii hashedDii, Instant asOf);
List getModifiedBuckets(Instant sinceTimestamp);
- void invalidateTokensAsync(UserIdentity userIdentity, Instant asOf, String uidTraceId, Handler> handler);
+ void invalidateTokensAsync(HashedDii hashedDii, Instant asOf, String uidTraceId, Handler> handler);
- boolean advertisingTokenMatches(String advertisingToken, UserIdentity userIdentity, Instant asOf);
+ boolean advertisingTokenMatches(String advertisingToken, HashedDii hashedDii, Instant asOf);
- Instant getLatestOptoutEntry(UserIdentity userIdentity, Instant asOf);
+ Instant getLatestOptoutEntry(HashedDii hashedDii, Instant asOf);
}
diff --git a/src/main/java/com/uid2/operator/service/InputUtil.java b/src/main/java/com/uid2/operator/service/InputUtil.java
index 839e4e0f3..81ed4c6d3 100644
--- a/src/main/java/com/uid2/operator/service/InputUtil.java
+++ b/src/main/java/com/uid2/operator/service/InputUtil.java
@@ -1,10 +1,8 @@
package com.uid2.operator.service;
-import com.uid2.operator.model.IdentityScope;
-import com.uid2.operator.model.IdentityType;
-import com.uid2.operator.model.UserIdentity;
-
-import java.time.Instant;
+import com.uid2.operator.model.identities.IdentityScope;
+import com.uid2.operator.model.identities.DiiType;
+import com.uid2.operator.model.identities.HashedDii;
public class InputUtil {
@@ -169,7 +167,7 @@ public static String normalizeEmailString(String email) {
return addressPartToUse.append('@').append(domainPart).toString();
}
- public enum IdentityInputType {
+ public enum DiiInputType {
Raw,
Hash
}
@@ -185,62 +183,63 @@ private static enum EmailParsingState {
public static class InputVal {
private final String provided;
private final String normalized;
- private final IdentityType identityType;
- private final IdentityInputType inputType;
+ //Directly Identifying Information (DII) (email or phone) see https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii
+ private final DiiType diiType;
+ private final DiiInputType inputType;
private final boolean valid;
- private final byte[] identityInput;
+ private final byte[] diiInput;
- public InputVal(String provided, String normalized, IdentityType identityType, IdentityInputType inputType, boolean valid) {
+ public InputVal(String provided, String normalized, DiiType diiType, DiiInputType inputType, boolean valid) {
this.provided = provided;
this.normalized = normalized;
- this.identityType = identityType;
+ this.diiType = diiType;
this.inputType = inputType;
this.valid = valid;
if (valid) {
- if (this.inputType == IdentityInputType.Raw) {
- this.identityInput = TokenUtils.getIdentityHash(this.normalized);
+ if (this.inputType == DiiInputType.Raw) {
+ this.diiInput = TokenUtils.getHashedDii(this.normalized);
} else {
- this.identityInput = EncodingUtils.fromBase64(this.normalized);
+ this.diiInput = EncodingUtils.fromBase64(this.normalized);
}
} else {
- this.identityInput = null;
+ this.diiInput = null;
}
}
public static InputVal validEmail(String input, String normalized) {
- return new InputVal(input, normalized, IdentityType.Email, IdentityInputType.Raw, true);
+ return new InputVal(input, normalized, DiiType.Email, DiiInputType.Raw, true);
}
public static InputVal invalidEmail(String input) {
- return new InputVal(input, null, IdentityType.Email, IdentityInputType.Raw, false);
+ return new InputVal(input, null, DiiType.Email, DiiInputType.Raw, false);
}
public static InputVal validEmailHash(String input, String normalized) {
- return new InputVal(input, normalized, IdentityType.Email, IdentityInputType.Hash, true);
+ return new InputVal(input, normalized, DiiType.Email, DiiInputType.Hash, true);
}
public static InputVal invalidEmailHash(String input) {
- return new InputVal(input, null, IdentityType.Email, IdentityInputType.Hash, false);
+ return new InputVal(input, null, DiiType.Email, DiiInputType.Hash, false);
}
public static InputVal validPhone(String input, String normalized) {
- return new InputVal(input, normalized, IdentityType.Phone, IdentityInputType.Raw, true);
+ return new InputVal(input, normalized, DiiType.Phone, DiiInputType.Raw, true);
}
public static InputVal invalidPhone(String input) {
- return new InputVal(input, null, IdentityType.Phone, IdentityInputType.Raw, false);
+ return new InputVal(input, null, DiiType.Phone, DiiInputType.Raw, false);
}
public static InputVal validPhoneHash(String input, String normalized) {
- return new InputVal(input, normalized, IdentityType.Phone, IdentityInputType.Hash, true);
+ return new InputVal(input, normalized, DiiType.Phone, DiiInputType.Hash, true);
}
public static InputVal invalidPhoneHash(String input) {
- return new InputVal(input, null, IdentityType.Phone, IdentityInputType.Hash, false);
+ return new InputVal(input, null, DiiType.Phone, DiiInputType.Hash, false);
}
- public byte[] getIdentityInput() {
- return this.identityInput;
+ public byte[] getHashedDiiInput() {
+ return this.diiInput;
}
public String getProvided() {
@@ -251,24 +250,21 @@ public String getNormalized() {
return normalized;
}
- public IdentityType getIdentityType() {
- return identityType;
+ public DiiType getDiiType() {
+ return diiType;
}
- public IdentityInputType getInputType() { return inputType; }
+ public DiiInputType getInputType() { return inputType; }
public boolean isValid() {
return valid;
}
- public UserIdentity toUserIdentity(IdentityScope identityScope, int privacyBits, Instant establishedAt) {
- return new UserIdentity(
+ public HashedDii toHashedDii(IdentityScope identityScope) {
+ return new HashedDii(
identityScope,
- this.identityType,
- getIdentityInput(),
- privacyBits,
- establishedAt,
- establishedAt);
+ this.diiType,
+ getHashedDiiInput());
}
}
diff --git a/src/main/java/com/uid2/operator/service/TokenUtils.java b/src/main/java/com/uid2/operator/service/TokenUtils.java
index 2cabc641b..57d6d01dd 100644
--- a/src/main/java/com/uid2/operator/service/TokenUtils.java
+++ b/src/main/java/com/uid2/operator/service/TokenUtils.java
@@ -1,65 +1,65 @@
package com.uid2.operator.service;
-import com.uid2.operator.model.IdentityScope;
-import com.uid2.operator.model.IdentityType;
+import com.uid2.operator.model.identities.IdentityScope;
+import com.uid2.operator.model.identities.DiiType;
import java.util.HashSet;
import java.util.Set;
public class TokenUtils {
- public static byte[] getIdentityHash(String identityString) {
- return EncodingUtils.getSha256Bytes(identityString);
+ public static byte[] getHashedDii(String rawDii) {
+ return EncodingUtils.getSha256Bytes(rawDii);
}
- public static String getIdentityHashString(String identityString) {
- return EncodingUtils.toBase64String(getIdentityHash(identityString));
+ public static String getHashedDiiString(String rawDii) {
+ return EncodingUtils.toBase64String(getHashedDii(rawDii));
}
- public static byte[] getFirstLevelHash(byte[] identityHash, String firstLevelSalt) {
- return getFirstLevelHashFromIdentityHash(EncodingUtils.toBase64String(identityHash), firstLevelSalt);
+ public static byte[] getFirstLevelHashFromHashedDii(byte[] hashedDii, String firstLevelSalt) {
+ return getFirstLevelHashFromHashedDii(EncodingUtils.toBase64String(hashedDii), firstLevelSalt);
}
- public static byte[] getFirstLevelHashFromIdentity(String identityString, String firstLevelSalt) {
- return getFirstLevelHash(getIdentityHash(identityString), firstLevelSalt);
+ public static byte[] getFirstLevelHashFromRawDii(String rawDii, String firstLevelSalt) {
+ return getFirstLevelHashFromHashedDii(getHashedDii(rawDii), firstLevelSalt);
}
- public static byte[] getFirstLevelHashFromIdentityHash(String identityHash, String firstLevelSalt) {
- return EncodingUtils.getSha256Bytes(identityHash, firstLevelSalt);
+ public static byte[] getFirstLevelHashFromHashedDii(String hashedDii, String firstLevelSalt) {
+ return EncodingUtils.getSha256Bytes(hashedDii, firstLevelSalt);
}
- public static byte[] getAdvertisingIdV2(byte[] firstLevelHash, String rotatingSalt) {
+ public static byte[] getRawUidV2(byte[] firstLevelHash, String rotatingSalt) {
return EncodingUtils.getSha256Bytes(EncodingUtils.toBase64String(firstLevelHash), rotatingSalt);
}
- public static byte[] getAdvertisingIdV2FromIdentity(String identityString, String firstLevelSalt, String rotatingSalt) {
- return getAdvertisingIdV2(getFirstLevelHashFromIdentity(identityString, firstLevelSalt), rotatingSalt);
+ public static byte[] getRawUidV2FromRawDii(String rawDii, String firstLevelSalt, String rotatingSalt) {
+ return getRawUidV2(getFirstLevelHashFromRawDii(rawDii, firstLevelSalt), rotatingSalt);
}
- public static byte[] getAdvertisingIdV2FromIdentityHash(String identityString, String firstLevelSalt, String rotatingSalt) {
- return getAdvertisingIdV2(getFirstLevelHashFromIdentityHash(identityString, firstLevelSalt), rotatingSalt);
+ public static byte[] getRawUidV2FromHashedDii(String hashedDii, String firstLevelSalt, String rotatingSalt) {
+ return getRawUidV2(getFirstLevelHashFromHashedDii(hashedDii, firstLevelSalt), rotatingSalt);
}
- public static byte[] getAdvertisingIdV3(IdentityScope scope, IdentityType type, byte[] firstLevelHash, String rotatingSalt) {
+ public static byte[] getRawUidV3(IdentityScope scope, DiiType type, byte[] firstLevelHash, String rotatingSalt) {
final byte[] sha = EncodingUtils.getSha256Bytes(EncodingUtils.toBase64String(firstLevelHash), rotatingSalt);
- final byte[] id = new byte[33];
- id[0] = (byte)(encodeIdentityScope(scope) | encodeIdentityType(type));
- System.arraycopy(sha, 0, id, 1, 32);
- return id;
+ final byte[] rawUid = new byte[33];
+ rawUid[0] = (byte)(encodeIdentityScope(scope) | encodeIdentityType(type));
+ System.arraycopy(sha, 0, rawUid, 1, 32);
+ return rawUid;
}
- public static byte[] getAdvertisingIdV3FromIdentity(IdentityScope scope, IdentityType type, String identityString, String firstLevelSalt, String rotatingSalt) {
- return getAdvertisingIdV3(scope, type, getFirstLevelHashFromIdentity(identityString, firstLevelSalt), rotatingSalt);
+ public static byte[] getRawUidV3FromRawDii(IdentityScope scope, DiiType type, String rawDii, String firstLevelSalt, String rotatingSalt) {
+ return getRawUidV3(scope, type, getFirstLevelHashFromRawDii(rawDii, firstLevelSalt), rotatingSalt);
}
- public static byte[] getAdvertisingIdV3FromIdentityHash(IdentityScope scope, IdentityType type, String identityString, String firstLevelSalt, String rotatingSalt) {
- return getAdvertisingIdV3(scope, type, getFirstLevelHashFromIdentityHash(identityString, firstLevelSalt), rotatingSalt);
+ public static byte[] getRawUidV3FromHashedDii(IdentityScope scope, DiiType type, String hashedDii, String firstLevelSalt, String rotatingSalt) {
+ return getRawUidV3(scope, type, getFirstLevelHashFromHashedDii(hashedDii, firstLevelSalt), rotatingSalt);
}
public static byte encodeIdentityScope(IdentityScope identityScope) {
return (byte) (identityScope.value << 4);
}
- public static byte encodeIdentityType(IdentityType identityType) {
- return (byte) (identityType.value << 2);
+ public static byte encodeIdentityType(DiiType diiType) {
+ return (byte) (diiType.value << 2);
}
}
diff --git a/src/main/java/com/uid2/operator/service/UIDOperatorService.java b/src/main/java/com/uid2/operator/service/UIDOperatorService.java
index af422ad57..a41d95a9d 100644
--- a/src/main/java/com/uid2/operator/service/UIDOperatorService.java
+++ b/src/main/java/com/uid2/operator/service/UIDOperatorService.java
@@ -1,6 +1,7 @@
package com.uid2.operator.service;
import com.uid2.operator.model.*;
+import com.uid2.operator.model.identities.*;
import com.uid2.operator.util.PrivacyBits;
import com.uid2.shared.audit.UidInstanceIdProvider;
import com.uid2.shared.model.SaltEntry;
@@ -20,8 +21,7 @@
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
-
-import static com.uid2.operator.IdentityConst.*;
+import static com.uid2.operator.model.identities.IdentityConst.*;
import static java.time.temporal.ChronoUnit.DAYS;
public class UIDOperatorService implements IUIDOperatorService {
@@ -35,15 +35,16 @@ public class UIDOperatorService implements IUIDOperatorService {
private final ISaltProvider saltProvider;
private final IOptOutStore optOutStore;
- private final ITokenEncoder encoder;
+ private final EncryptedTokenEncoder encoder;
private final Clock clock;
private final IdentityScope identityScope;
- private final UserIdentity testOptOutIdentityForEmail;
- private final UserIdentity testOptOutIdentityForPhone;
- private final UserIdentity testValidateIdentityForEmail;
- private final UserIdentity testValidateIdentityForPhone;
- private final UserIdentity testRefreshOptOutIdentityForEmail;
- private final UserIdentity testRefreshOptOutIdentityForPhone;
+
+ private final FirstLevelHash testOptOutIdentityForEmail;
+ private final FirstLevelHash testOptOutIdentityForPhone;
+ private final FirstLevelHash testValidateIdentityForEmail;
+ private final FirstLevelHash testValidateIdentityForPhone;
+ private final FirstLevelHash testRefreshOptOutIdentityForEmail;
+ private final FirstLevelHash testRefreshOptOutIdentityForPhone;
private final OperatorIdentity operatorIdentity;
private final TokenVersion refreshTokenVersion;
@@ -53,7 +54,7 @@ public class UIDOperatorService implements IUIDOperatorService {
private final Handler saltRetrievalResponseHandler;
private final UidInstanceIdProvider uidInstanceIdProvider;
- public UIDOperatorService(IOptOutStore optOutStore, ISaltProvider saltProvider, ITokenEncoder encoder, Clock clock,
+ public UIDOperatorService(IOptOutStore optOutStore, ISaltProvider saltProvider, EncryptedTokenEncoder encoder, Clock clock,
IdentityScope identityScope, Handler saltRetrievalResponseHandler, boolean identityV3Enabled, UidInstanceIdProvider uidInstanceIdProvider) {
this.saltProvider = saltProvider;
this.encoder = encoder;
@@ -63,18 +64,18 @@ public UIDOperatorService(IOptOutStore optOutStore, ISaltProvider saltProvider,
this.saltRetrievalResponseHandler = saltRetrievalResponseHandler;
this.uidInstanceIdProvider = uidInstanceIdProvider;
- this.testOptOutIdentityForEmail = getFirstLevelHashIdentity(identityScope, IdentityType.Email,
- InputUtil.normalizeEmail(OptOutIdentityForEmail).getIdentityInput(), Instant.now());
- this.testOptOutIdentityForPhone = getFirstLevelHashIdentity(identityScope, IdentityType.Phone,
- InputUtil.normalizePhone(OptOutIdentityForPhone).getIdentityInput(), Instant.now());
- this.testValidateIdentityForEmail = getFirstLevelHashIdentity(identityScope, IdentityType.Email,
- InputUtil.normalizeEmail(ValidateIdentityForEmail).getIdentityInput(), Instant.now());
- this.testValidateIdentityForPhone = getFirstLevelHashIdentity(identityScope, IdentityType.Phone,
- InputUtil.normalizePhone(ValidateIdentityForPhone).getIdentityInput(), Instant.now());
- this.testRefreshOptOutIdentityForEmail = getFirstLevelHashIdentity(identityScope, IdentityType.Email,
- InputUtil.normalizeEmail(RefreshOptOutIdentityForEmail).getIdentityInput(), Instant.now());
- this.testRefreshOptOutIdentityForPhone = getFirstLevelHashIdentity(identityScope, IdentityType.Phone,
- InputUtil.normalizePhone(RefreshOptOutIdentityForPhone).getIdentityInput(), Instant.now());
+ this.testOptOutIdentityForEmail = getFirstLevelHashIdentity(identityScope, DiiType.Email,
+ InputUtil.normalizeEmail(OptOutIdentityForEmail).getHashedDiiInput(), Instant.now());
+ this.testOptOutIdentityForPhone = getFirstLevelHashIdentity(identityScope, DiiType.Phone,
+ InputUtil.normalizePhone(OptOutIdentityForPhone).getHashedDiiInput(), Instant.now());
+ this.testValidateIdentityForEmail = getFirstLevelHashIdentity(identityScope, DiiType.Email,
+ InputUtil.normalizeEmail(ValidateIdentityForEmail).getHashedDiiInput(), Instant.now());
+ this.testValidateIdentityForPhone = getFirstLevelHashIdentity(identityScope, DiiType.Phone,
+ InputUtil.normalizePhone(ValidateIdentityForPhone).getHashedDiiInput(), Instant.now());
+ this.testRefreshOptOutIdentityForEmail = getFirstLevelHashIdentity(identityScope, DiiType.Email,
+ InputUtil.normalizeEmail(RefreshOptOutIdentityForEmail).getHashedDiiInput(), Instant.now());
+ this.testRefreshOptOutIdentityForPhone = getFirstLevelHashIdentity(identityScope, DiiType.Phone,
+ InputUtil.normalizePhone(RefreshOptOutIdentityForPhone).getHashedDiiInput(), Instant.now());
this.operatorIdentity = new OperatorIdentity(0, OperatorType.Service, 0, 0);
@@ -95,76 +96,77 @@ private void validateTokenDurations(Duration refreshIdentityAfter, Duration refr
}
@Override
- public IdentityTokens generateIdentity(IdentityRequest request, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter) {
+ public TokenGenerateResponse generateIdentity(TokenGenerateRequest request, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter) {
this.validateTokenDurations(refreshIdentityAfter, refreshExpiresAfter, identityExpiresAfter);
final Instant now = EncodingUtils.NowUTCMillis(this.clock);
- final byte[] firstLevelHash = getFirstLevelHash(request.userIdentity.id, now);
- final UserIdentity firstLevelHashIdentity = new UserIdentity(
- request.userIdentity.identityScope, request.userIdentity.identityType, firstLevelHash, request.userIdentity.privacyBits,
- request.userIdentity.establishedAt, request.userIdentity.refreshedAt);
+ final byte[] firstLevelHash = getFirstLevelHash(request.hashedDii.hashedDii(), now);
+ final FirstLevelHash firstLevelHashIdentity = new FirstLevelHash(
+ request.hashedDii.identityScope(), request.hashedDii.diiType(), firstLevelHash,
+ request.establishedAt);
if (request.shouldCheckOptOut() && getGlobalOptOutResult(firstLevelHashIdentity, false).isOptedOut()) {
- return IdentityTokens.LogoutToken;
+ return TokenGenerateResponse.OptOutResponse;
} else {
- return this.generateIdentity(request.publisherIdentity, firstLevelHashIdentity, refreshIdentityAfter, refreshExpiresAfter, identityExpiresAfter);
+ return this.generateIdentity(request.sourcePublisher, firstLevelHashIdentity, request.privacyBits, refreshIdentityAfter, refreshExpiresAfter, identityExpiresAfter);
}
}
@Override
- public RefreshResponse refreshIdentity(RefreshToken token, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter) {
+ public TokenRefreshResponse refreshIdentity(TokenRefreshRequest input, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter) {
this.validateTokenDurations(refreshIdentityAfter, refreshExpiresAfter, identityExpiresAfter);
// should not be possible as different scopes should be using different keys, but just in case
- if (token.userIdentity.identityScope != this.identityScope) {
- return RefreshResponse.Invalid;
+ if (input.firstLevelHash.identityScope() != this.identityScope) {
+ return TokenRefreshResponse.Invalid;
}
- if (token.userIdentity.establishedAt.isBefore(REFRESH_CUTOFF)) {
- return RefreshResponse.Deprecated;
+ if (input.firstLevelHash.establishedAt().isBefore(REFRESH_CUTOFF)) {
+ return TokenRefreshResponse.Deprecated;
}
final Instant now = clock.instant();
- if (token.expiresAt.isBefore(now)) {
- return RefreshResponse.Expired;
+ if (input.expiresAt.isBefore(now)) {
+ return TokenRefreshResponse.Expired;
}
- final PrivacyBits privacyBits = PrivacyBits.fromInt(token.userIdentity.privacyBits);
- final boolean isCstg = privacyBits.isClientSideTokenGenerated();
+ final boolean isCstg = input.privacyBits.isClientSideTokenGenerated();
try {
- final GlobalOptoutResult logoutEntry = getGlobalOptOutResult(token.userIdentity, true);
+ final GlobalOptoutResult logoutEntry = getGlobalOptOutResult(input.firstLevelHash, true);
final boolean optedOut = logoutEntry.isOptedOut();
- final Duration durationSinceLastRefresh = Duration.between(token.createdAt, now);
+ final Duration durationSinceLastRefresh = Duration.between(input.createdAt, now);
if (!optedOut) {
- IdentityTokens identityTokens = this.generateIdentity(token.publisherIdentity, token.userIdentity, refreshIdentityAfter, refreshExpiresAfter, identityExpiresAfter);
-
- return RefreshResponse.createRefreshedResponse(identityTokens, durationSinceLastRefresh, isCstg);
+ TokenGenerateResponse tokenGenerateResponse = this.generateIdentity(input.sourcePublisher,
+ input.firstLevelHash,
+ input.privacyBits, refreshIdentityAfter, refreshExpiresAfter, identityExpiresAfter);
+ return TokenRefreshResponse.createRefreshedResponse(tokenGenerateResponse, durationSinceLastRefresh, isCstg);
} else {
- return RefreshResponse.Optout;
+ return TokenRefreshResponse.Optout;
}
} catch (KeyManager.NoActiveKeyException e) {
- return RefreshResponse.NoActiveKey;
+ return TokenRefreshResponse.NoActiveKey;
} catch (Exception ex) {
- return RefreshResponse.Invalid;
+ return TokenRefreshResponse.Invalid;
}
}
@Override
- public MappedIdentity mapIdentity(MapRequest request) {
- final UserIdentity firstLevelHashIdentity = getFirstLevelHashIdentity(request.userIdentity, request.asOf);
- if (request.shouldCheckOptOut() && getGlobalOptOutResult(firstLevelHashIdentity, false).isOptedOut()) {
- return MappedIdentity.LogoutIdentity;
+ public IdentityMapResponseItem mapHashedDii(IdentityMapRequestItem request) {
+ final FirstLevelHash firstLevelHash = getFirstLevelHashIdentity(request.hashedDii,
+ request.asOf);
+ if (request.shouldCheckOptOut() && getGlobalOptOutResult(firstLevelHash, false).isOptedOut()) {
+ return IdentityMapResponseItem.OptoutIdentity;
} else {
- return getMappedIdentity(firstLevelHashIdentity, request.asOf);
+ return generateRawUid(firstLevelHash, request.asOf);
}
}
@Override
- public MappedIdentity map(UserIdentity userIdentity, Instant asOf) {
- final UserIdentity firstLevelHashIdentity = getFirstLevelHashIdentity(userIdentity, asOf);
- return getMappedIdentity(firstLevelHashIdentity, asOf);
+ public IdentityMapResponseItem map(HashedDii hashedDii, Instant asOf) {
+ final FirstLevelHash firstLevelHash = getFirstLevelHashIdentity(hashedDii, asOf);
+ return generateRawUid(firstLevelHash, asOf);
}
@Override
@@ -183,11 +185,11 @@ private ISaltProvider.ISaltSnapshot getSaltProviderSnapshot(Instant asOf) {
}
@Override
- public void invalidateTokensAsync(UserIdentity userIdentity, Instant asOf, String uidTraceId, Handler> handler) {
- final UserIdentity firstLevelHashIdentity = getFirstLevelHashIdentity(userIdentity, asOf);
- final MappedIdentity mappedIdentity = getMappedIdentity(firstLevelHashIdentity, asOf);
+ public void invalidateTokensAsync(HashedDii diiIdentity, Instant asOf, String uidTraceId, Handler> handler) {
+ final FirstLevelHash firstLevelHash = getFirstLevelHashIdentity(diiIdentity, asOf);
+ final IdentityMapResponseItem identityMapResponseItem = generateRawUid(firstLevelHash, asOf);
- this.optOutStore.addEntry(firstLevelHashIdentity, mappedIdentity.advertisingId, uidTraceId, this.uidInstanceIdProvider.getInstanceId(), r -> {
+ this.optOutStore.addEntry(firstLevelHash, identityMapResponseItem.rawUid, uidTraceId, this.uidInstanceIdProvider.getInstanceId(), r -> {
if (r.succeeded()) {
handler.handle(Future.succeededFuture(r.result()));
} else {
@@ -197,59 +199,60 @@ public void invalidateTokensAsync(UserIdentity userIdentity, Instant asOf, Strin
}
@Override
- public boolean advertisingTokenMatches(String advertisingToken, UserIdentity userIdentity, Instant asOf) {
- final UserIdentity firstLevelHashIdentity = getFirstLevelHashIdentity(userIdentity, asOf);
- final MappedIdentity mappedIdentity = getMappedIdentity(firstLevelHashIdentity, asOf);
-
- final AdvertisingToken token = this.encoder.decodeAdvertisingToken(advertisingToken);
- return Arrays.equals(mappedIdentity.advertisingId, token.userIdentity.id);
+ public boolean advertisingTokenMatches(String advertisingToken, HashedDii diiIdentity, Instant asOf) {
+ final FirstLevelHash firstLevelHash = getFirstLevelHashIdentity(diiIdentity, asOf);
+ final IdentityMapResponseItem identityMapResponseItem = generateRawUid(firstLevelHash, asOf);
+ final AdvertisingTokenRequest token = this.encoder.decodeAdvertisingToken(advertisingToken);
+ return Arrays.equals(identityMapResponseItem.rawUid, token.rawUid.rawUid());
}
@Override
- public Instant getLatestOptoutEntry(UserIdentity userIdentity, Instant asOf) {
- final UserIdentity firstLevelHashIdentity = getFirstLevelHashIdentity(userIdentity, asOf);
- return this.optOutStore.getLatestEntry(firstLevelHashIdentity);
+ public Instant getLatestOptoutEntry(HashedDii hashedDii, Instant asOf) {
+ final FirstLevelHash firstLevelHash = getFirstLevelHashIdentity(hashedDii, asOf);
+ return this.optOutStore.getLatestEntry(firstLevelHash);
}
- private UserIdentity getFirstLevelHashIdentity(UserIdentity userIdentity, Instant asOf) {
- return getFirstLevelHashIdentity(userIdentity.identityScope, userIdentity.identityType, userIdentity.id, asOf);
+ private FirstLevelHash getFirstLevelHashIdentity(HashedDii hashedDii, Instant asOf) {
+ return getFirstLevelHashIdentity(hashedDii.identityScope(), hashedDii.diiType(), hashedDii.hashedDii(), asOf);
}
- private UserIdentity getFirstLevelHashIdentity(IdentityScope identityScope, IdentityType identityType, byte[] identityHash, Instant asOf) {
- final byte[] firstLevelHash = getFirstLevelHash(identityHash, asOf);
- return new UserIdentity(identityScope, identityType, firstLevelHash, 0, null, null);
+ private FirstLevelHash getFirstLevelHashIdentity(IdentityScope identityScope, DiiType diiType, byte[] hashedDii, Instant asOf) {
+ final byte[] firstLevelHash = getFirstLevelHash(hashedDii, asOf);
+ return new FirstLevelHash(identityScope, diiType, firstLevelHash, null);
}
- private byte[] getFirstLevelHash(byte[] identityHash, Instant asOf) {
- return TokenUtils.getFirstLevelHash(identityHash, getSaltProviderSnapshot(asOf).getFirstLevelSalt());
+ private byte[] getFirstLevelHash(byte[] hashedDii, Instant asOf) {
+ return TokenUtils.getFirstLevelHashFromHashedDii(hashedDii, getSaltProviderSnapshot(asOf).getFirstLevelSalt());
}
- private MappedIdentity getMappedIdentity(UserIdentity firstLevelHashIdentity, Instant asOf) {
- final SaltEntry rotatingSalt = getSaltProviderSnapshot(asOf).getRotatingSalt(firstLevelHashIdentity.id);
- final byte[] advertisingId = getAdvertisingId(firstLevelHashIdentity, rotatingSalt.currentSalt());
- final byte[] previousAdvertisingId = getPreviousAdvertisingId(firstLevelHashIdentity, rotatingSalt, asOf);
+
+ private IdentityMapResponseItem generateRawUid(FirstLevelHash firstLevelHash, Instant asOf) {
+ final SaltEntry rotatingSalt = getSaltProviderSnapshot(asOf).getRotatingSalt(firstLevelHash.firstLevelHash());
+ final byte[] advertisingId = getAdvertisingId(firstLevelHash, rotatingSalt.currentSalt());
+ final byte[] previousAdvertisingId = getPreviousAdvertisingId(firstLevelHash, rotatingSalt, asOf);
final long refreshFrom = getRefreshFrom(rotatingSalt, asOf);
- return new MappedIdentity(
+ return new IdentityMapResponseItem(
advertisingId,
rotatingSalt.hashedId(),
previousAdvertisingId,
refreshFrom);
}
- private byte[] getAdvertisingId(UserIdentity firstLevelHashIdentity, String salt) {
+ private byte[] getAdvertisingId(FirstLevelHash firstLevelHash, String salt) {
return rawUidV3Enabled
- ? TokenUtils.getAdvertisingIdV3(firstLevelHashIdentity.identityScope, firstLevelHashIdentity.identityType, firstLevelHashIdentity.id, salt)
- : TokenUtils.getAdvertisingIdV2(firstLevelHashIdentity.id, salt);
+ ? TokenUtils.getRawUidV3(firstLevelHash.identityScope(), firstLevelHash.diiType(),
+ firstLevelHash.firstLevelHash(), salt)
+ : TokenUtils.getRawUidV2(firstLevelHash.firstLevelHash(), salt);
}
- private byte[] getPreviousAdvertisingId(UserIdentity firstLevelHashIdentity, SaltEntry rotatingSalt, Instant asOf) {
+ private byte[] getPreviousAdvertisingId(FirstLevelHash firstLevelHash, SaltEntry rotatingSalt, Instant asOf) {
long age = asOf.toEpochMilli() - rotatingSalt.lastUpdated();
if (age / DAY_IN_MS < 90) {
if (rotatingSalt.previousSalt() == null || rotatingSalt.previousSalt().isBlank()) {
return null;
}
- return getAdvertisingId(firstLevelHashIdentity, rotatingSalt.previousSalt());
+ return getAdvertisingId(firstLevelHash, rotatingSalt.previousSalt());
}
return null;
}
@@ -262,33 +265,45 @@ private long getRefreshFrom(SaltEntry rotatingSalt, Instant asOf) {
return refreshFrom;
}
- private IdentityTokens generateIdentity(PublisherIdentity publisherIdentity, UserIdentity firstLevelHashIdentity, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter) {
+ private TokenGenerateResponse generateIdentity(SourcePublisher sourcePublisher,
+ FirstLevelHash firstLevelHash, PrivacyBits privacyBits,
+ Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter) {
final Instant nowUtc = EncodingUtils.NowUTCMillis(this.clock);
- final MappedIdentity mappedIdentity = getMappedIdentity(firstLevelHashIdentity, nowUtc);
- final UserIdentity advertisingIdentity = new UserIdentity(firstLevelHashIdentity.identityScope, firstLevelHashIdentity.identityType,
- mappedIdentity.advertisingId, firstLevelHashIdentity.privacyBits, firstLevelHashIdentity.establishedAt, nowUtc);
+ final IdentityMapResponseItem identityMapResponseItem = generateRawUid(firstLevelHash, nowUtc);
+ final RawUid rawUid = new RawUid(firstLevelHash.identityScope(),
+ firstLevelHash.diiType(),
+ identityMapResponseItem.rawUid);
- return this.encoder.encode(
- this.createAdvertisingToken(publisherIdentity, advertisingIdentity, nowUtc, identityExpiresAfter),
- this.createRefreshToken(publisherIdentity, firstLevelHashIdentity, nowUtc, refreshExpiresAfter),
+ return this.encoder.encodeIntoIdentityResponse(
+ this.createAdvertisingTokenRequest(sourcePublisher, rawUid, nowUtc, privacyBits,
+ firstLevelHash.establishedAt(), identityExpiresAfter),
+ this.createTokenRefreshRequest(sourcePublisher, firstLevelHash, nowUtc, privacyBits, refreshExpiresAfter),
nowUtc.plusMillis(refreshIdentityAfter.toMillis()),
nowUtc
);
}
- private RefreshToken createRefreshToken(PublisherIdentity publisherIdentity, UserIdentity userIdentity, Instant now, Duration refreshExpiresAfter) {
- return new RefreshToken(
+ private TokenRefreshRequest createTokenRefreshRequest(SourcePublisher sourcePublisher,
+ FirstLevelHash firstLevelHash,
+ Instant now,
+ PrivacyBits privacyBits, Duration refreshExpiresAfter) {
+ return new TokenRefreshRequest(
this.refreshTokenVersion,
now,
now.plusMillis(refreshExpiresAfter.toMillis()),
this.operatorIdentity,
- publisherIdentity,
- userIdentity);
+ sourcePublisher,
+ firstLevelHash,
+ privacyBits);
}
- private AdvertisingToken createAdvertisingToken(PublisherIdentity publisherIdentity, UserIdentity userIdentity, Instant now, Duration identityExpiresAfter) {
- return new AdvertisingToken(TokenVersion.V4, now, now.plusMillis(identityExpiresAfter.toMillis()), this.operatorIdentity, publisherIdentity, userIdentity);
+ private AdvertisingTokenRequest createAdvertisingTokenRequest(SourcePublisher sourcePublisher, RawUid rawUidIdentity,
+ Instant now, PrivacyBits privacyBits, Instant establishedAt, Duration identityExpiresAfter) {
+
+ return new AdvertisingTokenRequest(TokenVersion.V4, now, now.plusMillis(identityExpiresAfter.toMillis()),
+ this.operatorIdentity, sourcePublisher, rawUidIdentity,
+ privacyBits, establishedAt);
}
static protected class GlobalOptoutResult {
@@ -312,16 +327,16 @@ public Instant getTime() {
}
}
- private GlobalOptoutResult getGlobalOptOutResult(UserIdentity userIdentity, boolean forRefresh) {
- if (forRefresh && (userIdentity.matches(testRefreshOptOutIdentityForEmail) || userIdentity.matches(testRefreshOptOutIdentityForPhone))) {
+ private GlobalOptoutResult getGlobalOptOutResult(FirstLevelHash firstLevelHash, boolean forRefresh) {
+ if (forRefresh && (firstLevelHash.matches(testRefreshOptOutIdentityForEmail) || firstLevelHash.matches(testRefreshOptOutIdentityForPhone))) {
return new GlobalOptoutResult(Instant.now());
- } else if (userIdentity.matches(testValidateIdentityForEmail) || userIdentity.matches(testValidateIdentityForPhone)
- || userIdentity.matches(testRefreshOptOutIdentityForEmail) || userIdentity.matches(testRefreshOptOutIdentityForPhone)) {
+ } else if (firstLevelHash.matches(testValidateIdentityForEmail) || firstLevelHash.matches(testValidateIdentityForPhone)
+ || firstLevelHash.matches(testRefreshOptOutIdentityForEmail) || firstLevelHash.matches(testRefreshOptOutIdentityForPhone)) {
return new GlobalOptoutResult(null);
- } else if (userIdentity.matches(testOptOutIdentityForEmail) || userIdentity.matches(testOptOutIdentityForPhone)) {
+ } else if (firstLevelHash.matches(testOptOutIdentityForEmail) || firstLevelHash.matches(testOptOutIdentityForPhone)) {
return new GlobalOptoutResult(Instant.now());
}
- Instant result = this.optOutStore.getLatestEntry(userIdentity);
+ Instant result = this.optOutStore.getLatestEntry(firstLevelHash);
return new GlobalOptoutResult(result);
}
}
diff --git a/src/main/java/com/uid2/operator/service/V2RequestUtil.java b/src/main/java/com/uid2/operator/service/V2RequestUtil.java
index 0055fbf7b..77f1cd1f0 100644
--- a/src/main/java/com/uid2/operator/service/V2RequestUtil.java
+++ b/src/main/java/com/uid2/operator/service/V2RequestUtil.java
@@ -1,6 +1,6 @@
package com.uid2.operator.service;
-import com.uid2.operator.model.IdentityScope;
+import com.uid2.operator.model.identities.IdentityScope;
import com.uid2.operator.model.KeyManager;
import com.uid2.operator.util.HttpMediaType;
import com.uid2.shared.IClock;
diff --git a/src/main/java/com/uid2/operator/store/CloudSyncOptOutStore.java b/src/main/java/com/uid2/operator/store/CloudSyncOptOutStore.java
index 1b3ff936b..cc727ac03 100644
--- a/src/main/java/com/uid2/operator/store/CloudSyncOptOutStore.java
+++ b/src/main/java/com/uid2/operator/store/CloudSyncOptOutStore.java
@@ -4,7 +4,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.uid2.operator.Const;
-import com.uid2.operator.model.UserIdentity;
+import com.uid2.operator.model.identities.FirstLevelHash;
import com.uid2.operator.service.EncodingUtils;
import com.uid2.shared.Utils;
import com.uid2.shared.audit.Audit;
@@ -76,8 +76,8 @@ public CloudSyncOptOutStore(Vertx vertx, ICloudStorage fsLocal, JsonObject jsonC
}
@Override
- public Instant getLatestEntry(UserIdentity firstLevelHashIdentity) {
- long epochSecond = this.snapshot.get().getOptOutTimestamp(firstLevelHashIdentity.id);
+ public Instant getLatestEntry(FirstLevelHash firstLevelHash) {
+ long epochSecond = this.snapshot.get().getOptOutTimestamp(firstLevelHash.firstLevelHash());
Instant instant = epochSecond > 0 ? Instant.ofEpochSecond(epochSecond) : null;
return instant;
}
@@ -88,15 +88,15 @@ public long getOptOutTimestampByAdId(String adId) {
}
@Override
- public void addEntry(UserIdentity firstLevelHashIdentity, byte[] advertisingId, String uidTraceId, String uidInstanceId, Handler> handler) {
+ public void addEntry(FirstLevelHash firstLevelHash, byte[] advertisingId, String uidTraceId, String uidInstanceId, Handler> handler) {
if (remoteApiHost == null) {
handler.handle(Future.failedFuture("remote api not set"));
return;
}
HttpRequest request =this.webClient.get(remoteApiPort, remoteApiHost, remoteApiPath).
- addQueryParam("identity_hash", EncodingUtils.toBase64String(firstLevelHashIdentity.id))
- .addQueryParam("advertising_id", EncodingUtils.toBase64String(advertisingId))
+ addQueryParam("identity_hash", EncodingUtils.toBase64String(firstLevelHash.firstLevelHash()))
+ .addQueryParam("advertising_id", EncodingUtils.toBase64String(advertisingId)) // advertising id aka raw UID
.putHeader("Authorization", remoteApiBearerToken)
.putHeader(Audit.UID_INSTANCE_ID_HEADER, uidInstanceId)
.as(BodyCodec.string());
diff --git a/src/main/java/com/uid2/operator/store/IOptOutStore.java b/src/main/java/com/uid2/operator/store/IOptOutStore.java
index 4c940816e..0ed8a95c7 100644
--- a/src/main/java/com/uid2/operator/store/IOptOutStore.java
+++ b/src/main/java/com/uid2/operator/store/IOptOutStore.java
@@ -1,6 +1,6 @@
package com.uid2.operator.store;
-import com.uid2.operator.model.UserIdentity;
+import com.uid2.operator.model.identities.FirstLevelHash;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
@@ -9,13 +9,14 @@
public interface IOptOutStore {
/**
- * Get latest Opt-out record with respect to the UID (hashed identity)
- * @param firstLevelHashIdentity UID
+ * Get latest opt-out record
+ *
+ * @param firstLevelHash The first level hash of a DII Hash
* @return The timestamp of latest opt-out record. NULL if no record.
*/
- Instant getLatestEntry(UserIdentity firstLevelHashIdentity);
+ Instant getLatestEntry(FirstLevelHash firstLevelHash);
long getOptOutTimestampByAdId(String adId);
- void addEntry(UserIdentity firstLevelHashIdentity, byte[] advertisingId, String uidTraceId, String uidInstanceId, Handler> handler);
+ void addEntry(FirstLevelHash firstLevelHash, byte[] advertisingId, String uidTraceId, String uidInstanceId, Handler> handler);
}
diff --git a/src/main/java/com/uid2/operator/util/PrivacyBits.java b/src/main/java/com/uid2/operator/util/PrivacyBits.java
index f8b7edf6a..0df69d7fd 100644
--- a/src/main/java/com/uid2/operator/util/PrivacyBits.java
+++ b/src/main/java/com/uid2/operator/util/PrivacyBits.java
@@ -3,6 +3,9 @@
public class PrivacyBits {
+ // For historical reason this bit is set
+ public static final PrivacyBits DEFAULT = PrivacyBits.fromInt(1);
+
private static final int BIT_LEGACY = 0;
private static final int BIT_CSTG = 1;
private static final int BIT_CSTG_OPTOUT = 2;
@@ -16,6 +19,24 @@ public class PrivacyBits {
public PrivacyBits() {
}
+ public PrivacyBits(PrivacyBits pb) {
+ bits = pb.bits;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !obj.getClass().equals(this.getClass())) {
+ return false;
+ }
+ PrivacyBits other = (PrivacyBits)obj;
+ return this.bits == other.bits;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.bits;
+ }
+
public PrivacyBits(int bits) {
this.bits = bits;
}
@@ -37,6 +58,9 @@ public boolean isClientSideTokenOptedOut() {
public void setLegacyBit() {
setBit(BIT_LEGACY);//unknown why this bit is set in https://github.com/IABTechLab/uid2-operator/blob/dbab58346e367c9d4122ad541ff9632dc37bd410/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java#L534
}
+ public boolean isLegacyBitSet() {
+ return isBitSet(BIT_LEGACY);
+ }
private void setBit(int position) {
bits |= (1 << position);
diff --git a/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java b/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java
index 19c53343f..fb4066681 100644
--- a/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java
+++ b/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java
@@ -4,7 +4,10 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.uid2.operator.Const;
import com.uid2.operator.model.*;
-import com.uid2.operator.model.IdentityScope;
+import com.uid2.operator.model.TokenGenerateResponse;
+import com.uid2.operator.model.identities.IdentityScope;
+import com.uid2.operator.model.identities.DiiType;
+import com.uid2.operator.model.identities.HashedDii;
import com.uid2.operator.monitoring.IStatsCollectorQueue;
import com.uid2.operator.monitoring.StatsCollectorHandler;
import com.uid2.operator.monitoring.TokenResponseStatsCollector;
@@ -71,9 +74,8 @@
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
-
+import static com.uid2.operator.model.identities.IdentityConst.*;
import static com.uid2.operator.Const.Config.*;
-import static com.uid2.operator.IdentityConst.*;
import static com.uid2.operator.service.ResponseUtil.*;
import static com.uid2.operator.vertx.Endpoints.*;
@@ -98,7 +100,7 @@ public class UIDOperatorVerticle extends AbstractVerticle {
private final AuthMiddleware auth;
private final ISiteStore siteProvider;
private final IClientSideKeypairStore clientSideKeypairProvider;
- private final ITokenEncoder encoder;
+ private final EncryptedTokenEncoder encoder;
private final ISaltProvider saltProvider;
private final IOptOutStore optOutStore;
private final IClientKeyProvider clientKeyProvider;
@@ -478,13 +480,13 @@ else if(emailHash != null) {
privacyBits.setLegacyBit();
privacyBits.setClientSideTokenGenerate();
- IdentityTokens identityTokens;
+ TokenGenerateResponse tokenGenerateResponse;
try {
- identityTokens = this.idService.generateIdentity(
- new IdentityRequest(
- new PublisherIdentity(clientSideKeypair.getSiteId(), 0, 0),
- input.toUserIdentity(this.identityScope, privacyBits.getAsInt(), Instant.now()),
- OptoutCheckPolicy.RespectOptOut),
+ tokenGenerateResponse = this.idService.generateIdentity(
+ new TokenGenerateRequest(
+ new SourcePublisher(clientSideKeypair.getSiteId()),
+ input.toHashedDii(this.identityScope),
+ OptoutCheckPolicy.RespectOptOut, privacyBits, Instant.now()),
refreshIdentityAfter,
refreshExpiresAfter,
identityExpiresAfter);
@@ -495,12 +497,12 @@ else if(emailHash != null) {
JsonObject response;
TokenResponseStatsCollector.ResponseStatus responseStatus = TokenResponseStatsCollector.ResponseStatus.Success;
- if (identityTokens.isEmptyToken()) {
+ if (tokenGenerateResponse.isOptedOut()) {
response = ResponseUtil.SuccessNoBodyV2(ResponseStatus.OptOut);
responseStatus = TokenResponseStatsCollector.ResponseStatus.OptOut;
}
else { //user not opted out and already generated valid identity token
- response = ResponseUtil.SuccessV2(toTokenResponseJson(identityTokens));
+ response = ResponseUtil.SuccessV2(tokenGenerateResponse.toTokenGenerateResponseJson());
}
//if returning an optout token or a successful identity token created originally
if (responseStatus == TokenResponseStatsCollector.ResponseStatus.Success) {
@@ -508,7 +510,7 @@ else if(emailHash != null) {
}
final byte[] encryptedResponse = AesGcm.encrypt(response.toBuffer().getBytes(), sharedSecret);
rc.response().setStatusCode(200).end(Buffer.buffer(Unpooled.wrappedBuffer(Base64.getEncoder().encode(encryptedResponse))));
- recordTokenResponseStats(clientSideKeypair.getSiteId(), TokenResponseStatsCollector.Endpoint.ClientSideTokenGenerateV2, responseStatus, siteProvider, identityTokens.getAdvertisingTokenVersion(), platformType);
+ recordTokenResponseStats(clientSideKeypair.getSiteId(), TokenResponseStatsCollector.Endpoint.ClientSideTokenGenerateV2, responseStatus, siteProvider, tokenGenerateResponse.getAdvertisingTokenVersion(), platformType);
}
private boolean hasValidOriginOrAppName(RoutingContext rc, CstgRequest request, ClientSideKeypair keypair, TokenResponseStatsCollector.PlatformType platformType) {
@@ -798,7 +800,6 @@ private void handleHealthCheck(RoutingContext rc) {
private void recordOperatorServedSdkUsage(RoutingContext rc, Integer siteId, String apiContact, String clientVersion) {
if (siteId != null && apiContact != null && clientVersion != null) {
final String path = RoutingContextUtil.getPath(rc);
-
CLIENT_VERSION_COUNTERS.computeIfAbsent(
new Tuple.Tuple2<>(Integer.toString(siteId), clientVersion),
tuple -> Counter
@@ -819,7 +820,7 @@ private void handleTokenRefreshV2(RoutingContext rc) {
try {
platformType = getPlatformType(rc);
String tokenStr = (String) rc.data().get("request");
- final RefreshResponse r = this.refreshIdentity(rc, tokenStr);
+ final TokenRefreshResponse r = this.refreshIdentity(rc, tokenStr);
siteId = rc.get(Const.RoutingContextData.SiteId);
final String apiContact = RoutingContextUtil.getApiContact(rc, clientKeyProvider);
recordOperatorServedSdkUsage(rc, siteId, apiContact, rc.request().headers().get(Const.Http.ClientVersionHeader));
@@ -839,7 +840,7 @@ private void handleTokenRefreshV2(RoutingContext rc) {
ResponseUtil.LogErrorAndSendResponse(ResponseStatus.UnknownError, 500, rc, "Unknown State");
}
} else {
- ResponseUtil.SuccessV2(rc, toTokenResponseJson(r.getTokens()));
+ ResponseUtil.SuccessV2(rc, r.getIdentityResponse().toTokenGenerateResponseJson());
this.recordRefreshDurationStats(siteId, getApiContact(rc), r.getDurationSinceLastRefresh(), rc.request().headers().contains(ORIGIN_HEADER), identityExpiresAfter);
}
TokenResponseStatsCollector.recordRefresh(siteProvider, siteId, TokenResponseStatsCollector.Endpoint.RefreshV2, r, platformType);
@@ -856,13 +857,13 @@ private void handleTokenValidateV2(RoutingContext rc) {
if (!isTokenInputValid(input, rc)) {
return;
}
- if ((input.getIdentityType() == IdentityType.Email && Arrays.equals(ValidateIdentityForEmailHash, input.getIdentityInput()))
- || (input.getIdentityType() == IdentityType.Phone && Arrays.equals(ValidateIdentityForPhoneHash, input.getIdentityInput()))) {
+ if ((input.getDiiType() == DiiType.Email && Arrays.equals(ValidateIdentityForEmailHash, input.getHashedDiiInput()))
+ || (input.getDiiType() == DiiType.Phone && Arrays.equals(ValidateIdentityForPhoneHash, input.getHashedDiiInput()))) {
try {
final Instant now = Instant.now();
final String token = req.getString("token");
- if (this.idService.advertisingTokenMatches(token, input.toUserIdentity(this.identityScope, 0, now), now)) {
+ if (this.idService.advertisingTokenMatches(token, input.toHashedDii(this.identityScope), now)) {
ResponseUtil.SuccessV2(rc, Boolean.TRUE);
} else {
ResponseUtil.SuccessV2(rc, Boolean.FALSE);
@@ -923,19 +924,19 @@ private void handleTokenGenerateV2(RoutingContext rc) {
SendClientErrorResponseAndRecordStats(ResponseStatus.ClientError, 400, rc, "Required opt-out policy argument for token/generate is missing or not set to 1", siteId, TokenResponseStatsCollector.Endpoint.GenerateV2, TokenResponseStatsCollector.ResponseStatus.BadPayload, siteProvider, platformType);
return;
}
-
- final IdentityTokens t = this.idService.generateIdentity(
- new IdentityRequest(
- new PublisherIdentity(siteId, 0, 0),
- input.toUserIdentity(this.identityScope, 1, Instant.now()),
+ final TokenGenerateResponse response = this.idService.generateIdentity(
+ new TokenGenerateRequest(
+ new SourcePublisher(siteId),
+ input.toHashedDii(this.identityScope),
OptoutCheckPolicy.respectOptOut()),
refreshIdentityAfter,
refreshExpiresAfter,
identityExpiresAfter);
- if (t.isEmptyToken()) {
- if (optoutCheckPolicy.getItem1() == OptoutCheckPolicy.DoNotRespect && !this.disableOptoutToken) { // only legacy can use this policy
- final InputUtil.InputVal optOutTokenInput = input.getIdentityType() == IdentityType.Email
+ if (response.isOptedOut()) {
+ if (optoutCheckPolicy.getItem1() == OptoutCheckPolicy.DoNotRespect && !this.disableOptoutToken) { // only legacy can use
+ // this policy
+ final InputUtil.InputVal optOutTokenInput = input.getDiiType() == DiiType.Email
? InputUtil.InputVal.validEmail(OptOutTokenIdentityForEmail, OptOutTokenIdentityForEmail)
: InputUtil.InputVal.validPhone(OptOutTokenIdentityForPhone, OptOutTokenIdentityForPhone);
@@ -943,24 +944,23 @@ private void handleTokenGenerateV2(RoutingContext rc) {
pb.setLegacyBit();
pb.setClientSideTokenGenerateOptout();
- final IdentityTokens optOutTokens = this.idService.generateIdentity(
- new IdentityRequest(
- new PublisherIdentity(siteId, 0, 0),
- optOutTokenInput.toUserIdentity(this.identityScope, pb.getAsInt(), Instant.now()),
- OptoutCheckPolicy.DoNotRespect),
+ final TokenGenerateResponse optOutTokens = this.idService.generateIdentity(
+ new TokenGenerateRequest(
+ new SourcePublisher(siteId),
+ optOutTokenInput.toHashedDii(this.identityScope),
+ OptoutCheckPolicy.DoNotRespect, pb, Instant.now()),
refreshIdentityAfter,
refreshExpiresAfter,
identityExpiresAfter);
-
- ResponseUtil.SuccessV2(rc, toTokenResponseJson(optOutTokens));
+ ResponseUtil.SuccessV2(rc, optOutTokens.toTokenGenerateResponseJson());
recordTokenResponseStats(siteId, TokenResponseStatsCollector.Endpoint.GenerateV2, TokenResponseStatsCollector.ResponseStatus.Success, siteProvider, optOutTokens.getAdvertisingTokenVersion(), platformType);
} else { // new participant, or legacy specified policy/optout_check=1
ResponseUtil.SuccessNoBodyV2("optout", rc);
recordTokenResponseStats(siteId, TokenResponseStatsCollector.Endpoint.GenerateV2, TokenResponseStatsCollector.ResponseStatus.OptOut, siteProvider, null, platformType);
}
} else {
- ResponseUtil.SuccessV2(rc, toTokenResponseJson(t));
- recordTokenResponseStats(siteId, TokenResponseStatsCollector.Endpoint.GenerateV2, TokenResponseStatsCollector.ResponseStatus.Success, siteProvider, t.getAdvertisingTokenVersion(), platformType);
+ ResponseUtil.SuccessV2(rc, response.toTokenGenerateResponseJson());
+ recordTokenResponseStats(siteId, TokenResponseStatsCollector.Endpoint.GenerateV2, TokenResponseStatsCollector.ResponseStatus.Success, siteProvider, response.getAdvertisingTokenVersion(), platformType);
}
}
} catch (KeyManager.NoActiveKeyException e) {
@@ -980,7 +980,7 @@ private Future handleLogoutAsyncV2(RoutingContext rc) {
final Instant now = Instant.now();
Promise promise = Promise.promise();
- this.idService.invalidateTokensAsync(input.toUserIdentity(this.identityScope, 0, now), now, uidTraceId, ar -> {
+ this.idService.invalidateTokensAsync(input.toHashedDii(this.identityScope), now, uidTraceId, ar -> {
if (ar.succeeded()) {
JsonObject body = new JsonObject();
body.put("optout", "OK");
@@ -1086,13 +1086,13 @@ private JsonObject handleIdentityMapCommon(RoutingContext rc, InputUtil.InputVal
for (int i = 0; i < count; ++i) {
final InputUtil.InputVal input = inputList[i];
if (input != null && input.isValid()) {
- final MappedIdentity mappedIdentity = idService.mapIdentity(
- new MapRequest(
- input.toUserIdentity(this.identityScope, 0, now),
+ final IdentityMapResponseItem identityMapResponseItem = idService.mapHashedDii(
+ new IdentityMapRequestItem(
+ input.toHashedDii(this.identityScope),
OptoutCheckPolicy.respectOptOut(),
now));
- if (mappedIdentity.isOptedOut()) {
+ if (identityMapResponseItem.isOptedOut()) {
final JsonObject resp = new JsonObject();
resp.put("identifier", input.getProvided());
resp.put("reason", "optout");
@@ -1101,8 +1101,8 @@ private JsonObject handleIdentityMapCommon(RoutingContext rc, InputUtil.InputVal
} else {
final JsonObject resp = new JsonObject();
resp.put("identifier", input.getProvided());
- resp.put("advertising_id", EncodingUtils.toBase64String(mappedIdentity.advertisingId));
- resp.put("bucket_id", mappedIdentity.bucketId);
+ resp.put("advertising_id", EncodingUtils.toBase64String(identityMapResponseItem.rawUid));
+ resp.put("bucket_id", identityMapResponseItem.bucketId);
mapped.add(resp);
}
} else {
@@ -1137,19 +1137,19 @@ private JsonObject processIdentityMapV3Response(RoutingContext rc, Map getInputList = null;
final JsonArray emails = JsonParseUtils.parseArray(obj, "email", rc);
if (emails != null && !emails.isEmpty()) {
- getInputList = () -> createInputList(emails, IdentityType.Email, InputUtil.IdentityInputType.Raw);
+ getInputList = () -> createInputList(emails, DiiType.Email, InputUtil.DiiInputType.Raw);
}
final JsonArray emailHashes = JsonParseUtils.parseArray(obj, "email_hash", rc);
@@ -1209,7 +1209,7 @@ private InputUtil.InputVal[] getIdentityMapV2Input(RoutingContext rc) {
if (getInputList != null) {
return null; // only one type of input is allowed
}
- getInputList = () -> createInputList(emailHashes, IdentityType.Email, InputUtil.IdentityInputType.Hash);
+ getInputList = () -> createInputList(emailHashes, DiiType.Email, InputUtil.DiiInputType.Hash);
}
final JsonArray phones = this.phoneSupport ? JsonParseUtils.parseArray(obj,"phone", rc) : null;
@@ -1217,7 +1217,7 @@ private InputUtil.InputVal[] getIdentityMapV2Input(RoutingContext rc) {
if (getInputList != null) {
return null; // only one type of input is allowed
}
- getInputList = () -> createInputList(phones, IdentityType.Phone, InputUtil.IdentityInputType.Raw);
+ getInputList = () -> createInputList(phones, DiiType.Phone, InputUtil.DiiInputType.Raw);
}
final JsonArray phoneHashes = this.phoneSupport ? JsonParseUtils.parseArray(obj,"phone_hash", rc) : null;
@@ -1225,7 +1225,7 @@ private InputUtil.InputVal[] getIdentityMapV2Input(RoutingContext rc) {
if (getInputList != null) {
return null; // only one type of input is allowed
}
- getInputList = () -> createInputList(phoneHashes, IdentityType.Phone, InputUtil.IdentityInputType.Hash);
+ getInputList = () -> createInputList(phoneHashes, DiiType.Phone, InputUtil.DiiInputType.Hash);
}
if (emails == null && emailHashes == null && phones == null && phoneHashes == null) {
@@ -1233,7 +1233,7 @@ private InputUtil.InputVal[] getIdentityMapV2Input(RoutingContext rc) {
}
return getInputList == null ?
- createInputList(null, IdentityType.Email, InputUtil.IdentityInputType.Raw) : // handle empty array
+ createInputList(null, DiiType.Email, InputUtil.DiiInputType.Raw) : // handle empty array
getInputList.get();
}
@@ -1263,16 +1263,16 @@ private void handleIdentityMapV3(RoutingContext rc) {
private Map processIdentityMapMixedInput(RoutingContext rc, IdentityMapV3Request input) {
final Map normalizedIdentities = new HashMap<>();
- var normalizedEmails = parseIdentitiesInput(input.email(), IdentityType.Email, InputUtil.IdentityInputType.Raw, rc);
+ var normalizedEmails = parseIdentitiesInput(input.email(), DiiType.Email, InputUtil.DiiInputType.Raw, rc);
normalizedIdentities.put("email", normalizedEmails);
- var normalizedEmailHashes = parseIdentitiesInput(input.email_hash(), IdentityType.Email, InputUtil.IdentityInputType.Hash, rc);
+ var normalizedEmailHashes = parseIdentitiesInput(input.email_hash(), DiiType.Email, InputUtil.DiiInputType.Hash, rc);
normalizedIdentities.put("email_hash", normalizedEmailHashes);
- var normalizedPhones = parseIdentitiesInput(input.phone(), IdentityType.Phone, InputUtil.IdentityInputType.Raw, rc);
+ var normalizedPhones = parseIdentitiesInput(input.phone(), DiiType.Phone, InputUtil.DiiInputType.Raw, rc);
normalizedIdentities.put("phone", normalizedPhones);
- var normalizedPhoneHashes = parseIdentitiesInput(input.phone_hash(), IdentityType.Phone, InputUtil.IdentityInputType.Hash, rc);
+ var normalizedPhoneHashes = parseIdentitiesInput(input.phone_hash(), DiiType.Phone, InputUtil.DiiInputType.Hash, rc);
normalizedIdentities.put("phone_hash", normalizedPhoneHashes);
return normalizedIdentities;
@@ -1451,22 +1451,22 @@ private void recordRefreshTokenVersionCount(String siteId, TokenVersion tokenVer
}
- private RefreshResponse refreshIdentity(RoutingContext rc, String tokenStr) {
- final RefreshToken refreshToken;
+ private TokenRefreshResponse refreshIdentity(RoutingContext rc, String tokenStr) {
+ final TokenRefreshRequest tokenRefreshRequest;
try {
if (AuthMiddleware.isAuthenticated(rc)) {
rc.put(Const.RoutingContextData.SiteId, AuthMiddleware.getAuthClient(ClientKey.class, rc).getSiteId());
}
- refreshToken = this.encoder.decodeRefreshToken(tokenStr);
+ tokenRefreshRequest = this.encoder.decodeRefreshToken(tokenStr);
} catch (ClientInputValidationException cie) {
LOGGER.warn("Failed to decode refresh token for site ID: " + rc.data().get(Const.RoutingContextData.SiteId), cie);
- return RefreshResponse.Invalid;
+ return TokenRefreshResponse.Invalid;
}
- if (refreshToken == null) {
- return RefreshResponse.Invalid;
+ if (tokenRefreshRequest == null) {
+ return TokenRefreshResponse.Invalid;
}
if (!AuthMiddleware.isAuthenticated(rc)) {
- rc.put(Const.RoutingContextData.SiteId, refreshToken.publisherIdentity.siteId);
+ rc.put(Const.RoutingContextData.SiteId, tokenRefreshRequest.sourcePublisher.siteId);
}
recordRefreshTokenVersionCount(String.valueOf(rc.data().get(Const.RoutingContextData.SiteId)), this.getRefreshTokenVersion(tokenStr));
@@ -1475,7 +1475,7 @@ private RefreshResponse refreshIdentity(RoutingContext rc, String tokenStr) {
Duration refreshExpiresAfter = Duration.ofSeconds(config.getRefreshTokenExpiresAfterSeconds());
Duration identityExpiresAfter = Duration.ofSeconds(config.getIdentityTokenExpiresAfterSeconds());
- return this.idService.refreshIdentity(refreshToken, refreshIdentityAfter, refreshExpiresAfter, identityExpiresAfter);
+ return this.idService.refreshIdentity(tokenRefreshRequest, refreshIdentityAfter, refreshExpiresAfter, identityExpiresAfter);
}
public static String getSiteName(ISiteStore siteStore, Integer siteId) {
@@ -1528,7 +1528,7 @@ private void recordRefreshDurationStats(Integer siteId, String apiContact, Durat
c.increment();
}
- private InputUtil.InputVal[] createInputList(JsonArray a, IdentityType identityType, InputUtil.IdentityInputType inputType) {
+ private InputUtil.InputVal[] createInputList(JsonArray a, DiiType diiType, InputUtil.DiiInputType inputType) {
if (a == null || a.isEmpty()) {
return new InputUtil.InputVal[0];
}
@@ -1536,14 +1536,15 @@ private InputUtil.InputVal[] createInputList(JsonArray a, IdentityType identityT
final InputUtil.InputVal[] resp = new InputUtil.InputVal[size];
for (int i = 0; i < size; i++) {
- resp[i] = normalizeIdentity(a.getString(i), identityType, inputType);
+ resp[i] = normalizeIdentity(a.getString(i), diiType, inputType);
}
return resp;
}
- private InputUtil.InputVal normalizeIdentity(String identity, IdentityType identityType, InputUtil.IdentityInputType inputType) {
- return switch (identityType) {
+ private InputUtil.InputVal normalizeIdentity(String identity, DiiType diiType,
+ InputUtil.DiiInputType inputType) {
+ return switch (diiType) {
case Email -> switch (inputType) {
case Raw -> InputUtil.normalizeEmail(identity);
case Hash -> InputUtil.normalizeEmailHash(identity);
@@ -1555,7 +1556,8 @@ private InputUtil.InputVal normalizeIdentity(String identity, IdentityType ident
};
}
- private InputUtil.InputVal[] parseIdentitiesInput(String[] identities, IdentityType identityType, InputUtil.IdentityInputType inputType, RoutingContext rc) {
+ private InputUtil.InputVal[] parseIdentitiesInput(String[] identities, DiiType identityType, InputUtil.DiiInputType inputType
+ , RoutingContext rc) {
if (identities == null || identities.length == 0) {
return new InputUtil.InputVal[0];
}
@@ -1659,16 +1661,6 @@ private TransparentConsentParseResult getUserConsentV2(JsonObject req) {
}
}
- private JsonObject toTokenResponseJson(IdentityTokens t) {
- final JsonObject json = new JsonObject();
- json.put("advertising_token", t.getAdvertisingToken());
- json.put("refresh_token", t.getRefreshToken());
- json.put("identity_expires", t.getIdentityExpires().toEpochMilli());
- json.put("refresh_expires", t.getRefreshExpires().toEpochMilli());
- json.put("refresh_from", t.getRefreshFrom().toEpochMilli());
- return json;
- }
-
private static MissingAclMode getMissingAclMode(ClientKey clientKey) {
return clientKey.hasRole(Role.ID_READER) ? MissingAclMode.ALLOW_ALL : MissingAclMode.DENY_ALL;
}
diff --git a/src/main/java/com/uid2/operator/vertx/V2PayloadHandler.java b/src/main/java/com/uid2/operator/vertx/V2PayloadHandler.java
index cd15f59da..e5b336d18 100644
--- a/src/main/java/com/uid2/operator/vertx/V2PayloadHandler.java
+++ b/src/main/java/com/uid2/operator/vertx/V2PayloadHandler.java
@@ -1,6 +1,6 @@
package com.uid2.operator.vertx;
-import com.uid2.operator.model.IdentityScope;
+import com.uid2.operator.model.identities.IdentityScope;
import com.uid2.operator.model.KeyManager;
import com.uid2.operator.monitoring.TokenResponseStatsCollector;
import com.uid2.operator.service.EncodingUtils;
diff --git a/src/test/java/com/uid2/operator/EUIDOperatorVerticleTest.java b/src/test/java/com/uid2/operator/EUIDOperatorVerticleTest.java
index 7c894fba6..634d8b431 100644
--- a/src/test/java/com/uid2/operator/EUIDOperatorVerticleTest.java
+++ b/src/test/java/com/uid2/operator/EUIDOperatorVerticleTest.java
@@ -3,7 +3,7 @@
import com.uid2.shared.model.TokenVersion;
import org.junit.jupiter.api.Test;
-import com.uid2.operator.model.IdentityScope;
+import com.uid2.operator.model.identities.IdentityScope;
import com.uid2.shared.auth.Role;
import io.vertx.core.Vertx;
diff --git a/src/test/java/com/uid2/operator/InputNormalizationTest.java b/src/test/java/com/uid2/operator/InputNormalizationTest.java
index adbefbeae..6f825608a 100644
--- a/src/test/java/com/uid2/operator/InputNormalizationTest.java
+++ b/src/test/java/com/uid2/operator/InputNormalizationTest.java
@@ -71,7 +71,7 @@ public void testValidEmailNormalization() {
Assert.assertEquals(normalization.getProvided(), testCase[0]);
Assert.assertTrue(normalization.isValid());
Assert.assertEquals(testCase[1], normalization.getNormalized());
- Assert.assertEquals(testCase[2], EncodingUtils.toBase64String(normalization.getIdentityInput()));
+ Assert.assertEquals(testCase[2], EncodingUtils.toBase64String(normalization.getHashedDiiInput()));
}
}
@@ -90,7 +90,7 @@ public void testValidHashNormalization() {
Assert.assertEquals(s, normalization.getProvided());
Assert.assertTrue(normalization.isValid());
Assert.assertEquals(masterHash, normalization.getNormalized());
- Assert.assertEquals(masterHash, EncodingUtils.toBase64String(normalization.getIdentityInput()));
+ Assert.assertEquals(masterHash, EncodingUtils.toBase64String(normalization.getHashedDiiInput()));
}
}
diff --git a/src/test/java/com/uid2/operator/TokenEncodingTest.java b/src/test/java/com/uid2/operator/TokenEncodingTest.java
index 15030df98..110fa9aee 100644
--- a/src/test/java/com/uid2/operator/TokenEncodingTest.java
+++ b/src/test/java/com/uid2/operator/TokenEncodingTest.java
@@ -1,9 +1,14 @@
package com.uid2.operator;
import com.uid2.operator.model.*;
+import com.uid2.operator.model.identities.DiiType;
+import com.uid2.operator.model.identities.FirstLevelHash;
+import com.uid2.operator.model.identities.IdentityScope;
+import com.uid2.operator.model.identities.RawUid;
import com.uid2.operator.service.EncodingUtils;
import com.uid2.operator.service.EncryptedTokenEncoder;
import com.uid2.operator.service.TokenUtils;
+import com.uid2.operator.util.PrivacyBits;
import com.uid2.shared.Const.Data;
import com.uid2.shared.model.TokenVersion;
import com.uid2.shared.store.CloudPath;
@@ -51,31 +56,32 @@ public void testRefreshTokenEncoding(TokenVersion tokenVersion) {
final EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(this.keyManager);
final Instant now = EncodingUtils.NowUTCMillis();
- final byte[] firstLevelHash = TokenUtils.getFirstLevelHashFromIdentity("test@example.com", "some-salt");
+ final byte[] firstLevelHash = TokenUtils.getFirstLevelHashFromRawDii("test@example.com", "some-salt");
- final RefreshToken token = new RefreshToken(tokenVersion,
+ final TokenRefreshRequest tokenRefreshRequest = new TokenRefreshRequest(tokenVersion,
now,
now.plusSeconds(360),
new OperatorIdentity(101, OperatorType.Service, 102, 103),
- new PublisherIdentity(111, 112, 113),
- new UserIdentity(IdentityScope.UID2, IdentityType.Email, firstLevelHash, 121, now, now.minusSeconds(122))
+ new SourcePublisher(111, 112, 113),
+ new FirstLevelHash(IdentityScope.UID2, DiiType.Email, firstLevelHash, now),
+ PrivacyBits.fromInt(121)
);
if (tokenVersion == TokenVersion.V4) {
- Assert.assertThrows(Exception.class, () -> encoder.encode(token, now));
+ Assert.assertThrows(Exception.class, () -> encoder.encodeIntoRefreshToken(tokenRefreshRequest, now));
return; //V4 not supported for RefreshTokens
}
- final byte[] encodedBytes = encoder.encode(token, now);
- final RefreshToken decoded = encoder.decodeRefreshToken(EncodingUtils.toBase64String(encodedBytes));
+ final byte[] encodedBytes = encoder.encodeIntoRefreshToken(tokenRefreshRequest, now);
+ final TokenRefreshRequest decoded = encoder.decodeRefreshToken(EncodingUtils.toBase64String(encodedBytes));
assertEquals(tokenVersion, decoded.version);
- assertEquals(token.createdAt, decoded.createdAt);
+ assertEquals(tokenRefreshRequest.createdAt, decoded.createdAt);
int addSeconds = (tokenVersion == TokenVersion.V2) ? 60 : 0; //todo: why is there a 60 second buffer in encodeV2() but not in encodeV3()?
- assertEquals(token.expiresAt.plusSeconds(addSeconds), decoded.expiresAt);
- assertTrue(token.userIdentity.matches(decoded.userIdentity));
- assertEquals(token.userIdentity.privacyBits, decoded.userIdentity.privacyBits);
- assertEquals(token.userIdentity.establishedAt, decoded.userIdentity.establishedAt);
- assertEquals(token.publisherIdentity.siteId, decoded.publisherIdentity.siteId);
+ assertEquals(tokenRefreshRequest.expiresAt.plusSeconds(addSeconds), decoded.expiresAt);
+ assertTrue(tokenRefreshRequest.firstLevelHash.matches(decoded.firstLevelHash));
+ assertEquals(tokenRefreshRequest.privacyBits, decoded.privacyBits);
+ assertEquals(tokenRefreshRequest.firstLevelHash.establishedAt(), decoded.firstLevelHash.establishedAt());
+ assertEquals(tokenRefreshRequest.sourcePublisher.siteId, decoded.sourcePublisher.siteId);
Buffer b = Buffer.buffer(encodedBytes);
int keyId = b.getInt(tokenVersion == TokenVersion.V2 ? 25 : 2);
@@ -100,27 +106,29 @@ public void testAdvertisingTokenEncodings(boolean useRawUIDv3, TokenVersion adTo
final EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(this.keyManager);
final Instant now = EncodingUtils.NowUTCMillis();
- final byte[] rawUid = UIDOperatorVerticleTest.getRawUid(IdentityType.Email, "test@example.com", IdentityScope.UID2, useRawUIDv3);
+ final byte[] rawUid = UIDOperatorVerticleTest.getRawUid(DiiType.Email, "test@example.com", IdentityScope.UID2, useRawUIDv3);
- final AdvertisingToken token = new AdvertisingToken(
+ final AdvertisingTokenRequest adTokenRequest = new AdvertisingTokenRequest(
adTokenVersion,
now,
now.plusSeconds(60),
new OperatorIdentity(101, OperatorType.Service, 102, 103),
- new PublisherIdentity(111, 112, 113),
- new UserIdentity(IdentityScope.UID2, IdentityType.Email, rawUid, 121, now, now.minusSeconds(122))
+ new SourcePublisher(111, 112, 113),
+ new RawUid(IdentityScope.UID2, DiiType.Email, rawUid),
+ PrivacyBits.fromInt(121),
+ now
);
- final byte[] encodedBytes = encoder.encode(token, now);
- final AdvertisingToken decoded = encoder.decodeAdvertisingToken(EncryptedTokenEncoder.bytesToBase64Token(encodedBytes, adTokenVersion));
+ final byte[] encodedBytes = encoder.encodeIntoAdvertisingToken(adTokenRequest, now);
+ final AdvertisingTokenRequest decoded = encoder.decodeAdvertisingToken(EncryptedTokenEncoder.bytesToBase64Token(encodedBytes, adTokenVersion));
assertEquals(adTokenVersion, decoded.version);
- assertEquals(token.createdAt, decoded.createdAt);
- assertEquals(token.expiresAt, decoded.expiresAt);
- assertTrue(token.userIdentity.matches(decoded.userIdentity));
- assertEquals(token.userIdentity.privacyBits, decoded.userIdentity.privacyBits);
- assertEquals(token.userIdentity.establishedAt, decoded.userIdentity.establishedAt);
- assertEquals(token.publisherIdentity.siteId, decoded.publisherIdentity.siteId);
+ assertEquals(adTokenRequest.createdAt, decoded.createdAt);
+ assertEquals(adTokenRequest.expiresAt, decoded.expiresAt);
+ assertTrue(adTokenRequest.rawUid.matches(decoded.rawUid));
+ assertEquals(adTokenRequest.privacyBits, decoded.privacyBits);
+ assertEquals(adTokenRequest.establishedAt, decoded.establishedAt);
+ assertEquals(adTokenRequest.sourcePublisher.siteId, decoded.sourcePublisher.siteId);
Buffer b = Buffer.buffer(encodedBytes);
int keyId = b.getInt(adTokenVersion == TokenVersion.V2 ? 1 : 2); //TODO - extract master key from token should be a helper function
diff --git a/src/test/java/com/uid2/operator/UIDOperatorServiceTest.java b/src/test/java/com/uid2/operator/UIDOperatorServiceTest.java
index d3d877024..2e0014567 100644
--- a/src/test/java/com/uid2/operator/UIDOperatorServiceTest.java
+++ b/src/test/java/com/uid2/operator/UIDOperatorServiceTest.java
@@ -1,12 +1,14 @@
package com.uid2.operator;
import com.uid2.operator.model.*;
+import com.uid2.operator.model.identities.*;
+import com.uid2.operator.service.*;
import com.uid2.operator.service.EncodingUtils;
import com.uid2.operator.service.EncryptedTokenEncoder;
-import com.uid2.operator.service.ITokenEncoder;
import com.uid2.operator.service.InputUtil;
import com.uid2.operator.service.UIDOperatorService;
import com.uid2.operator.store.IOptOutStore;
+import com.uid2.operator.util.PrivacyBits;
import com.uid2.operator.vertx.OperatorShutdownHandler;
import com.uid2.shared.audit.UidInstanceIdProvider;
import com.uid2.shared.model.SaltEntry;
@@ -24,6 +26,8 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+
+import static com.uid2.operator.service.TokenUtils.getFirstLevelHashFromHashedDii;
import static com.uid2.operator.Const.Config.IdentityV3Prop;
import static java.time.temporal.ChronoUnit.DAYS;
import static org.junit.jupiter.api.Assertions.*;
@@ -54,7 +58,7 @@ public class UIDOperatorServiceTest {
ExtendedUIDOperatorService uid2Service;
ExtendedUIDOperatorService euidService;
Instant now;
-
+ RotatingSaltProvider saltProvider;
final int IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS = 600;
final int REFRESH_TOKEN_EXPIRES_AFTER_SECONDS = 900;
final int REFRESH_IDENTITY_TOKEN_AFTER_SECONDS = 300;
@@ -62,7 +66,7 @@ public class UIDOperatorServiceTest {
final String FIRST_LEVEL_SALT = "first-level-salt";
class ExtendedUIDOperatorService extends UIDOperatorService {
- public ExtendedUIDOperatorService(IOptOutStore optOutStore, ISaltProvider saltProvider, ITokenEncoder encoder, Clock clock, IdentityScope identityScope, Handler saltRetrievalResponseHandler, boolean identityV3Enabled, UidInstanceIdProvider uidInstanceIdProvider) {
+ public ExtendedUIDOperatorService(IOptOutStore optOutStore, ISaltProvider saltProvider, EncryptedTokenEncoder encoder, Clock clock, IdentityScope identityScope, Handler saltRetrievalResponseHandler, boolean identityV3Enabled, UidInstanceIdProvider uidInstanceIdProvider) {
super(optOutStore, saltProvider, encoder, clock, identityScope, saltRetrievalResponseHandler, identityV3Enabled, uidInstanceIdProvider);
}
}
@@ -83,7 +87,7 @@ void setup() throws Exception {
new GlobalScope(new CloudPath("/com.uid2.core/test/keysets/metadata.json")));
keysetProvider.loadContent();
- RotatingSaltProvider saltProvider = new RotatingSaltProvider(
+ saltProvider = new RotatingSaltProvider(
new EmbeddedResourceStorage(Main.class),
"/com.uid2.core/test/salts/metadata.json");
saltProvider.loadContent();
@@ -160,107 +164,138 @@ private RotatingSaltProvider.SaltSnapshot setUpMockSalts() {
return saltSnapshot;
}
- private UserIdentity createUserIdentity(String rawIdentityHash, IdentityScope scope, IdentityType type) {
- return new UserIdentity(
+ private HashedDii createHashedDii(String hashedDii, IdentityScope scope, DiiType type) {
+ return new HashedDii(
scope,
type,
- rawIdentityHash.getBytes(StandardCharsets.UTF_8),
- 0,
- this.now.minusSeconds(234),
- this.now.plusSeconds(12345)
+ hashedDii.getBytes(StandardCharsets.UTF_8)
);
}
- private AdvertisingToken validateAndGetToken(EncryptedTokenEncoder tokenEncoder, String advertisingTokenString, IdentityScope scope, IdentityType type, int siteId) {
+ private AdvertisingTokenRequest validateAndGetToken(EncryptedTokenEncoder tokenEncoder, String advertisingTokenString, IdentityScope scope, DiiType type, int siteId) {
UIDOperatorVerticleTest.validateAdvertisingToken(advertisingTokenString, TokenVersion.V4, scope, type);
return tokenEncoder.decodeAdvertisingToken(advertisingTokenString);
}
- private void assertIdentityScopeIdentityTypeAndEstablishedAt(UserIdentity expctedUserIdentity, UserIdentity actualUserIdentity) {
- assertEquals(expctedUserIdentity.identityScope, actualUserIdentity.identityScope);
- assertEquals(expctedUserIdentity.identityType, actualUserIdentity.identityType);
- assertEquals(expctedUserIdentity.establishedAt, actualUserIdentity.establishedAt);
+ private void assertIdentityScopeIdentityType(IdentityScope expectedScope, DiiType expectedDiiType,
+ HashedDii hashedDii) {
+ assertEquals(expectedScope, hashedDii.identityScope());
+ assertEquals(expectedDiiType, hashedDii.diiType());
+ }
+
+ private void assertIdentityScopeIdentityType(IdentityScope expectedScope, DiiType expectedDiiType,
+ RawUid rawUid) {
+ assertEquals(expectedScope, rawUid.identityScope());
+ assertEquals(expectedDiiType, rawUid.diiType());
+ }
+
+ private void assertIdentityScopeIdentityType(IdentityScope expectedScope, DiiType expectedDiiType,
+ FirstLevelHash firstLevelHash) {
+ assertEquals(expectedScope, firstLevelHash.identityScope());
+ assertEquals(expectedDiiType, firstLevelHash.diiType());
}
+
+
+
+
@ParameterizedTest
@CsvSource({"123, V4","127, V4","128, V4"})
public void testGenerateAndRefresh(int siteId, TokenVersion tokenVersion) {
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(siteId, 124, 125),
- createUserIdentity("test-email-hash", IdentityScope.UID2, IdentityType.Email),
- OptoutCheckPolicy.DoNotRespect
+ IdentityScope expectedIdentityScope = IdentityScope.UID2;
+ DiiType expectedDiiType = DiiType.Email;
+
+
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(siteId, 124, 125),
+ createHashedDii("test-email-hash", expectedIdentityScope, expectedDiiType),
+ OptoutCheckPolicy.DoNotRespect, PrivacyBits.fromInt(0),
+ this.now.minusSeconds(234)
);
- final IdentityTokens tokens = uid2Service.generateIdentity(
- identityRequest,
+ final TokenGenerateResponse tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(tokens);
+ assertNotNull(tokenGenerateResponse);
+
+ UIDOperatorVerticleTest.validateAdvertisingToken(tokenGenerateResponse.getAdvertisingToken(), tokenVersion, IdentityScope.UID2, DiiType.Email);
+ AdvertisingTokenRequest advertisingTokenRequest = tokenEncoder.decodeAdvertisingToken(tokenGenerateResponse.getAdvertisingToken());
+ assertEquals(this.now.plusSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS), advertisingTokenRequest.expiresAt);
+ assertEquals(tokenGenerateRequest.sourcePublisher.siteId, advertisingTokenRequest.sourcePublisher.siteId);
+ assertIdentityScopeIdentityType(expectedIdentityScope, expectedDiiType,
+ advertisingTokenRequest.rawUid);
+ assertEquals(tokenGenerateRequest.establishedAt, advertisingTokenRequest.establishedAt);
+ assertEquals(tokenGenerateRequest.privacyBits, advertisingTokenRequest.privacyBits);
- UIDOperatorVerticleTest.validateAdvertisingToken(tokens.getAdvertisingToken(), tokenVersion, IdentityScope.UID2, IdentityType.Email);
- AdvertisingToken advertisingToken = tokenEncoder.decodeAdvertisingToken(tokens.getAdvertisingToken());assertEquals(this.now.plusSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS), advertisingToken.expiresAt);
- assertEquals(identityRequest.publisherIdentity.siteId, advertisingToken.publisherIdentity.siteId);
- assertIdentityScopeIdentityTypeAndEstablishedAt(identityRequest.userIdentity, advertisingToken.userIdentity);
+ TokenRefreshRequest tokenRefreshRequest = tokenEncoder.decodeRefreshToken(tokenGenerateResponse.getRefreshToken());
+ assertEquals(this.now, tokenRefreshRequest.createdAt);
+ assertEquals(this.now.plusSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS), tokenRefreshRequest.expiresAt);
+ assertEquals(tokenGenerateRequest.sourcePublisher.siteId, tokenRefreshRequest.sourcePublisher.siteId);
+ assertIdentityScopeIdentityType(expectedIdentityScope, expectedDiiType, tokenRefreshRequest.firstLevelHash);
+ assertEquals(tokenGenerateRequest.establishedAt, tokenRefreshRequest.firstLevelHash.establishedAt());
+
+ final byte[] firstLevelHash = getFirstLevelHashFromHashedDii(tokenGenerateRequest.hashedDii.hashedDii(),
+ saltProvider.getSnapshot(this.now).getFirstLevelSalt() );
+ assertArrayEquals(firstLevelHash, tokenRefreshRequest.firstLevelHash.firstLevelHash());
- RefreshToken refreshToken = tokenEncoder.decodeRefreshToken(tokens.getRefreshToken());
- assertEquals(this.now, refreshToken.createdAt);
- assertEquals(this.now.plusSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS), refreshToken.expiresAt);
- assertEquals(identityRequest.publisherIdentity.siteId, refreshToken.publisherIdentity.siteId);
- assertIdentityScopeIdentityTypeAndEstablishedAt(identityRequest.userIdentity, refreshToken.userIdentity);
setNow(Instant.now().plusSeconds(200));
reset(shutdownHandler);
- final RefreshResponse refreshResponse = uid2Service.refreshIdentity(
- refreshToken,
+ final TokenRefreshResponse refreshResponse = uid2Service.refreshIdentity(tokenRefreshRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
assertNotNull(refreshResponse);
- assertEquals(RefreshResponse.Status.Refreshed, refreshResponse.getStatus());
- assertNotNull(refreshResponse.getTokens());
-
- UIDOperatorVerticleTest.validateAdvertisingToken(refreshResponse.getTokens().getAdvertisingToken(), tokenVersion, IdentityScope.UID2, IdentityType.Email);
- AdvertisingToken advertisingToken2 = tokenEncoder.decodeAdvertisingToken(refreshResponse.getTokens().getAdvertisingToken());
- assertEquals(this.now.plusSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS), advertisingToken2.expiresAt);
- assertEquals(advertisingToken.publisherIdentity.siteId, advertisingToken2.publisherIdentity.siteId);
- assertIdentityScopeIdentityTypeAndEstablishedAt(advertisingToken.userIdentity, advertisingToken2.userIdentity);
- assertArrayEquals(advertisingToken.userIdentity.id, advertisingToken2.userIdentity.id);
-
- RefreshToken refreshToken2 = tokenEncoder.decodeRefreshToken(refreshResponse.getTokens().getRefreshToken());
- assertEquals(this.now, refreshToken2.createdAt);
- assertEquals(this.now.plusSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS), refreshToken2.expiresAt);
- assertEquals(refreshToken.publisherIdentity.siteId, refreshToken2.publisherIdentity.siteId);
- assertIdentityScopeIdentityTypeAndEstablishedAt(refreshToken.userIdentity, refreshToken2.userIdentity);
- assertArrayEquals(refreshToken.userIdentity.id, refreshToken2.userIdentity.id);
+ assertEquals(TokenRefreshResponse.Status.Refreshed, refreshResponse.getStatus());
+ assertNotNull(refreshResponse.getIdentityResponse());
+
+ UIDOperatorVerticleTest.validateAdvertisingToken(refreshResponse.getIdentityResponse().getAdvertisingToken(), tokenVersion, IdentityScope.UID2, DiiType.Email);
+ AdvertisingTokenRequest advertisingTokenRequest2 = tokenEncoder.decodeAdvertisingToken(refreshResponse.getIdentityResponse().getAdvertisingToken());
+ assertEquals(this.now.plusSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS), advertisingTokenRequest2.expiresAt);
+ assertEquals(advertisingTokenRequest.sourcePublisher.siteId, advertisingTokenRequest2.sourcePublisher.siteId);
+ assertIdentityScopeIdentityType(expectedIdentityScope, expectedDiiType,
+ advertisingTokenRequest2.rawUid);
+ assertEquals(advertisingTokenRequest.establishedAt, advertisingTokenRequest2.establishedAt);
+ assertArrayEquals(advertisingTokenRequest.rawUid.rawUid(),
+ advertisingTokenRequest2.rawUid.rawUid());
+ assertEquals(tokenGenerateRequest.privacyBits, advertisingTokenRequest2.privacyBits);
+
+ TokenRefreshRequest tokenRefreshRequest2 = tokenEncoder.decodeRefreshToken(refreshResponse.getIdentityResponse().getRefreshToken());
+ assertEquals(this.now, tokenRefreshRequest2.createdAt);
+ assertEquals(this.now.plusSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS), tokenRefreshRequest2.expiresAt);
+ assertEquals(tokenRefreshRequest.sourcePublisher.siteId, tokenRefreshRequest2.sourcePublisher.siteId);
+ assertIdentityScopeIdentityType(expectedIdentityScope, expectedDiiType, tokenRefreshRequest2.firstLevelHash);
+ assertEquals(tokenRefreshRequest.firstLevelHash.establishedAt(), tokenRefreshRequest2.firstLevelHash.establishedAt());
+ assertArrayEquals(tokenRefreshRequest.firstLevelHash.firstLevelHash(), tokenRefreshRequest2.firstLevelHash.firstLevelHash());
+ assertArrayEquals(firstLevelHash, tokenRefreshRequest2.firstLevelHash.firstLevelHash());
}
@Test
public void testTestOptOutKey_DoNotRespectOptout() {
final InputUtil.InputVal inputVal = InputUtil.normalizeEmail(IdentityConst.OptOutIdentityForEmail);
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- inputVal.toUserIdentity(IdentityScope.UID2, 0, this.now),
- OptoutCheckPolicy.DoNotRespect
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ inputVal.toHashedDii(IdentityScope.UID2),
+ OptoutCheckPolicy.DoNotRespect, PrivacyBits.fromInt(0), this.now
);
- final IdentityTokens tokens = uid2Service.generateIdentity(
- identityRequest,
+
+ final TokenGenerateResponse tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(tokens);
- assertFalse(tokens.isEmptyToken());
+ assertNotNull(tokenGenerateResponse);
+ assertFalse(tokenGenerateResponse.isOptedOut());
- final RefreshToken refreshToken = this.tokenEncoder.decodeRefreshToken(tokens.getRefreshToken());
- assertEquals(RefreshResponse.Optout, uid2Service.refreshIdentity(
- refreshToken,
+ final TokenRefreshRequest tokenRefreshRequest = this.tokenEncoder.decodeRefreshToken(tokenGenerateResponse.getRefreshToken());
+ assertEquals(TokenRefreshResponse.Optout, uid2Service.refreshIdentity(tokenRefreshRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS)));
@@ -270,17 +305,17 @@ public void testTestOptOutKey_DoNotRespectOptout() {
public void testTestOptOutKey_RespectOptout() {
final InputUtil.InputVal inputVal = InputUtil.normalizeEmail(IdentityConst.OptOutIdentityForEmail);
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- inputVal.toUserIdentity(IdentityScope.UID2, 0, this.now),
- OptoutCheckPolicy.RespectOptOut
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ inputVal.toHashedDii(IdentityScope.UID2),
+ OptoutCheckPolicy.RespectOptOut, PrivacyBits.fromInt(0), this.now
);
- final IdentityTokens tokens = uid2Service.generateIdentity(
- identityRequest,
+
+ final TokenGenerateResponse tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
- assertTrue(tokens.isEmptyToken());
+ assertTrue(tokenGenerateResponse.isOptedOut());
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
}
@@ -290,24 +325,22 @@ public void testTestOptOutKeyIdentityScopeMismatch() {
final String email = "optout@example.com";
final InputUtil.InputVal inputVal = InputUtil.normalizeEmail(email);
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- inputVal.toUserIdentity(IdentityScope.EUID, 0, this.now),
- OptoutCheckPolicy.DoNotRespect
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ inputVal.toHashedDii(IdentityScope.EUID),
+ OptoutCheckPolicy.DoNotRespect, PrivacyBits.fromInt(0), this.now
);
- final IdentityTokens tokens = euidService.generateIdentity(
- identityRequest,
+ final TokenGenerateResponse tokenGenerateResponse = euidService.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(tokens);
+ assertNotNull(tokenGenerateResponse);
- final RefreshToken refreshToken = this.tokenEncoder.decodeRefreshToken(tokens.getRefreshToken());
+ final TokenRefreshRequest tokenRefreshRequest = this.tokenEncoder.decodeRefreshToken(tokenGenerateResponse.getRefreshToken());
reset(shutdownHandler);
- assertEquals(RefreshResponse.Invalid, uid2Service.refreshIdentity(
- refreshToken,
+ assertEquals(TokenRefreshResponse.Invalid, uid2Service.refreshIdentity(tokenRefreshRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS)));
@@ -319,65 +352,64 @@ public void testTestOptOutKeyIdentityScopeMismatch() {
"Email,test@example.com,EUID",
"Phone,+01010101010,UID2",
"Phone,+01010101010,EUID"})
- public void testGenerateTokenForOptOutUser(IdentityType type, String identity, IdentityScope scope) {
- final UserIdentity userIdentity = createUserIdentity(identity, scope, type);
-
- final IdentityRequest identityRequestForceGenerate = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- userIdentity,
- OptoutCheckPolicy.DoNotRespect);
-
- final IdentityRequest identityRequestRespectOptOut = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- userIdentity,
- OptoutCheckPolicy.RespectOptOut);
+ public void testGenerateTokenForOptOutUser(DiiType type, String id, IdentityScope scope) {
+ final HashedDii hashedDii = createHashedDii(TokenUtils.getHashedDiiString(id),
+ scope, type);
+
+ final TokenGenerateRequest tokenGenerateRequestForceGenerate = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ hashedDii,
+ OptoutCheckPolicy.DoNotRespect, PrivacyBits.fromInt(0),
+ this.now.minusSeconds(234));
+
+ final TokenGenerateRequest tokenGenerateRequestRespectOptOut = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ hashedDii,
+ OptoutCheckPolicy.RespectOptOut, PrivacyBits.fromInt(0),
+ this.now.minusSeconds(234));
// the clock value shouldn't matter here
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(Instant.now().minus(1, ChronoUnit.HOURS));
- final IdentityTokens tokens;
- final AdvertisingToken advertisingToken;
- final IdentityTokens tokensAfterOptOut;
+ final TokenGenerateResponse tokenGenerateResponse;
+ final AdvertisingTokenRequest advertisingTokenRequest;
+ final TokenGenerateResponse tokenGenerateResponseAfterOptOut;
if (scope == IdentityScope.UID2) {
- tokens = uid2Service.generateIdentity(
- identityRequestForceGenerate,
+ tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequestForceGenerate,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), IdentityScope.UID2, userIdentity.identityType, identityRequestRespectOptOut.publisherIdentity.siteId);
+ advertisingTokenRequest = validateAndGetToken(tokenEncoder, tokenGenerateResponse.getAdvertisingToken(), IdentityScope.UID2, hashedDii.diiType(), tokenGenerateRequestRespectOptOut.sourcePublisher.siteId);
reset(shutdownHandler);
- tokensAfterOptOut = uid2Service.generateIdentity(
- identityRequestRespectOptOut,
+ tokenGenerateResponseAfterOptOut = uid2Service.generateIdentity(tokenGenerateRequestRespectOptOut,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
} else {
- tokens = euidService.generateIdentity(
- identityRequestForceGenerate,
+ tokenGenerateResponse = euidService.generateIdentity(tokenGenerateRequestForceGenerate,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), IdentityScope.EUID, userIdentity.identityType, identityRequestRespectOptOut.publisherIdentity.siteId);
+ advertisingTokenRequest = validateAndGetToken(tokenEncoder, tokenGenerateResponse.getAdvertisingToken(), IdentityScope.EUID, hashedDii.diiType(), tokenGenerateRequestRespectOptOut.sourcePublisher.siteId);
reset(shutdownHandler);
- tokensAfterOptOut = euidService.generateIdentity(
- identityRequestRespectOptOut,
+ tokenGenerateResponseAfterOptOut = euidService.generateIdentity(tokenGenerateRequestRespectOptOut,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(tokens);
- assertNotNull(advertisingToken.userIdentity);
- assertNotNull(tokensAfterOptOut);
- assertTrue(tokensAfterOptOut.getAdvertisingToken() == null || tokensAfterOptOut.getAdvertisingToken().isEmpty());
-
+ assertNotNull(tokenGenerateResponse);
+ assertNotNull(advertisingTokenRequest.rawUid);
+ assertNotNull(tokenGenerateResponseAfterOptOut);
+ assertTrue(tokenGenerateResponseAfterOptOut.getAdvertisingToken() == null || tokenGenerateResponseAfterOptOut.getAdvertisingToken().isEmpty());
+ assertTrue(tokenGenerateResponseAfterOptOut.isOptedOut());
}
@ParameterizedTest
@@ -385,45 +417,45 @@ public void testGenerateTokenForOptOutUser(IdentityType type, String identity, I
"Email,test@example.com,EUID",
"Phone,+01010101010,UID2",
"Phone,+01010101010,EUID"})
- public void testIdentityMapForOptOutUser(IdentityType type, String identity, IdentityScope scope) {
- final UserIdentity userIdentity = createUserIdentity(identity, scope, type);
+ public void testIdentityMapForOptOutUser(DiiType type, String identity, IdentityScope scope) {
+ final HashedDii hashedDii = createHashedDii(TokenUtils.getHashedDiiString(identity), scope, type);
final Instant now = Instant.now();
- final MapRequest mapRequestForceMap = new MapRequest(
- userIdentity,
+ final IdentityMapRequestItem mapRequestForceIdentityMapItem = new IdentityMapRequestItem(
+ hashedDii,
OptoutCheckPolicy.DoNotRespect,
now);
- final MapRequest mapRequestRespectOptOut = new MapRequest(
- userIdentity,
+ final IdentityMapRequestItem identityMapRequestItemRespectOptOut = new IdentityMapRequestItem(
+ hashedDii,
OptoutCheckPolicy.RespectOptOut,
now);
// the clock value shouldn't matter here
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(Instant.now().minus(1, ChronoUnit.HOURS));
- final MappedIdentity mappedIdentity;
- final MappedIdentity mappedIdentityShouldBeOptOut;
+ final IdentityMapResponseItem identityMapResponseItem;
+ final IdentityMapResponseItem identityMapResponseItemShouldBeOptOut;
if (scope == IdentityScope.UID2) {
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- mappedIdentity = uid2Service.mapIdentity(mapRequestForceMap);
+ identityMapResponseItem = uid2Service.mapHashedDii(mapRequestForceIdentityMapItem);
reset(shutdownHandler);
- mappedIdentityShouldBeOptOut = uid2Service.mapIdentity(mapRequestRespectOptOut);
+ identityMapResponseItemShouldBeOptOut = uid2Service.mapHashedDii(identityMapRequestItemRespectOptOut);
} else {
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- mappedIdentity = euidService.mapIdentity(mapRequestForceMap);
+ identityMapResponseItem = euidService.mapHashedDii(mapRequestForceIdentityMapItem);
reset(shutdownHandler);
- mappedIdentityShouldBeOptOut = euidService.mapIdentity(mapRequestRespectOptOut);
+ identityMapResponseItemShouldBeOptOut = euidService.mapHashedDii(identityMapRequestItemRespectOptOut);
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(mappedIdentity);
- assertFalse(mappedIdentity.isOptedOut());
- assertNotNull(mappedIdentityShouldBeOptOut);
- assertTrue(mappedIdentityShouldBeOptOut.isOptedOut());
+ assertNotNull(identityMapResponseItem);
+ assertFalse(identityMapResponseItem.isOptedOut());
+ assertNotNull(identityMapResponseItemShouldBeOptOut);
+ assertTrue(identityMapResponseItemShouldBeOptOut.isOptedOut());
}
private enum TestIdentityInputType {
@@ -469,33 +501,31 @@ private InputUtil.InputVal generateInputVal(TestIdentityInputType type, String i
void testSpecialIdentityOptOutTokenGenerate(TestIdentityInputType type, String id, IdentityScope scope) {
InputUtil.InputVal inputVal = generateInputVal(type, id);
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- inputVal.toUserIdentity(scope, 0, this.now),
- OptoutCheckPolicy.RespectOptOut
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ inputVal.toHashedDii(scope),
+ OptoutCheckPolicy.RespectOptOut, PrivacyBits.fromInt(0), this.now
);
// identity has no optout record, ensure generate still returns optout
when(this.optOutStore.getLatestEntry(any())).thenReturn(null);
- IdentityTokens tokens;
+ TokenGenerateResponse tokenGenerateResponse;
if(scope == IdentityScope.EUID) {
- tokens = euidService.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = euidService.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
else {
- tokens = uid2Service.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertEquals(tokens, IdentityTokens.LogoutToken);
+ assertEquals(tokenGenerateResponse, TokenGenerateResponse.OptOutResponse);
}
@ParameterizedTest
@@ -510,25 +540,25 @@ void testSpecialIdentityOptOutTokenGenerate(TestIdentityInputType type, String i
void testSpecialIdentityOptOutIdentityMap(TestIdentityInputType type, String id, IdentityScope scope) {
InputUtil.InputVal inputVal = generateInputVal(type, id);
- final MapRequest mapRequestRespectOptOut = new MapRequest(
- inputVal.toUserIdentity(scope, 0, this.now),
+ final IdentityMapRequestItem identityMapRequestItemRespectOptOut = new IdentityMapRequestItem(
+ inputVal.toHashedDii(scope),
OptoutCheckPolicy.RespectOptOut,
now);
// identity has no optout record, ensure map still returns optout
when(this.optOutStore.getLatestEntry(any())).thenReturn(null);
- final MappedIdentity mappedIdentity;
+ final IdentityMapResponseItem identityMapResponseItem;
if(scope == IdentityScope.EUID) {
- mappedIdentity = euidService.mapIdentity(mapRequestRespectOptOut);
+ identityMapResponseItem = euidService.mapHashedDii(identityMapRequestItemRespectOptOut);
}
else {
- mappedIdentity = uid2Service.mapIdentity(mapRequestRespectOptOut);
+ identityMapResponseItem = uid2Service.mapHashedDii(identityMapRequestItemRespectOptOut);
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(mappedIdentity);
- assertTrue(mappedIdentity.isOptedOut());
+ assertNotNull(identityMapResponseItem);
+ assertTrue(identityMapResponseItem.isOptedOut());
}
@ParameterizedTest
@@ -543,39 +573,36 @@ void testSpecialIdentityOptOutIdentityMap(TestIdentityInputType type, String id,
void testSpecialIdentityOptOutTokenRefresh(TestIdentityInputType type, String id, IdentityScope scope) {
InputUtil.InputVal inputVal = generateInputVal(type, id);
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- inputVal.toUserIdentity(scope, 0, this.now),
- OptoutCheckPolicy.DoNotRespect
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ inputVal.toHashedDii(scope),
+ OptoutCheckPolicy.DoNotRespect, PrivacyBits.fromInt(0), this.now
);
- IdentityTokens tokens;
+ TokenGenerateResponse tokenGenerateResponse;
if(scope == IdentityScope.EUID) {
- tokens = euidService.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = euidService.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
else {
- tokens = uid2Service.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(tokens);
- assertNotEquals(IdentityTokens.LogoutToken, tokens);
+ assertNotNull(tokenGenerateResponse);
+ assertNotEquals(TokenGenerateResponse.OptOutResponse, tokenGenerateResponse);
// identity has no optout record, ensure refresh still returns optout
when(this.optOutStore.getLatestEntry(any())).thenReturn(null);
- final RefreshToken refreshToken = this.tokenEncoder.decodeRefreshToken(tokens.getRefreshToken());
+ final TokenRefreshRequest tokenRefreshRequest = this.tokenEncoder.decodeRefreshToken(tokenGenerateResponse.getRefreshToken());
reset(shutdownHandler);
- assertEquals(RefreshResponse.Optout, (scope == IdentityScope.EUID? euidService: uid2Service).refreshIdentity(
- refreshToken,
+ assertEquals(TokenRefreshResponse.Optout, (scope == IdentityScope.EUID? euidService: uid2Service).refreshIdentity(tokenRefreshRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS)));
@@ -594,42 +621,39 @@ void testSpecialIdentityOptOutTokenRefresh(TestIdentityInputType type, String id
void testSpecialIdentityRefreshOptOutGenerate(TestIdentityInputType type, String id, IdentityScope scope) {
InputUtil.InputVal inputVal = generateInputVal(type, id);
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- inputVal.toUserIdentity(scope, 0, this.now),
- OptoutCheckPolicy.RespectOptOut
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ inputVal.toHashedDii(scope),
+ OptoutCheckPolicy.RespectOptOut, PrivacyBits.fromInt(0), this.now
);
// identity has optout record, ensure still generates
when(this.optOutStore.getLatestEntry(any())).thenReturn(Instant.now());
- IdentityTokens tokens;
+ TokenGenerateResponse tokenGenerateResponse;
if(scope == IdentityScope.EUID) {
- tokens = euidService.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = euidService.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
else {
- tokens = uid2Service.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(tokens);
- assertNotEquals(IdentityTokens.LogoutToken, tokens);
+ assertNotNull(tokenGenerateResponse);
+ assertNotEquals(TokenGenerateResponse.OptOutResponse, tokenGenerateResponse);
// identity has no optout record, ensure refresh still returns optout
when(this.optOutStore.getLatestEntry(any())).thenReturn(null);
- final RefreshToken refreshToken = this.tokenEncoder.decodeRefreshToken(tokens.getRefreshToken());
+ final TokenRefreshRequest tokenRefreshRequest = this.tokenEncoder.decodeRefreshToken(tokenGenerateResponse.getRefreshToken());
reset(shutdownHandler);
- assertEquals(RefreshResponse.Optout, (scope == IdentityScope.EUID? euidService: uid2Service).refreshIdentity(
- refreshToken,
+ assertEquals(TokenRefreshResponse.Optout, (scope == IdentityScope.EUID? euidService: uid2Service).refreshIdentity(tokenRefreshRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS)));
@@ -648,25 +672,25 @@ void testSpecialIdentityRefreshOptOutGenerate(TestIdentityInputType type, String
void testSpecialIdentityRefreshOptOutIdentityMap(TestIdentityInputType type, String id, IdentityScope scope) {
InputUtil.InputVal inputVal = generateInputVal(type, id);
- final MapRequest mapRequestRespectOptOut = new MapRequest(
- inputVal.toUserIdentity(scope, 0, this.now),
+ final IdentityMapRequestItem identityMapRequestItemRespectOptOut = new IdentityMapRequestItem(
+ inputVal.toHashedDii(scope),
OptoutCheckPolicy.RespectOptOut,
now);
// all identities have optout records, ensure refresh-optout identities still map
when(this.optOutStore.getLatestEntry(any())).thenReturn(Instant.now());
- final MappedIdentity mappedIdentity;
+ final IdentityMapResponseItem identityMapResponseItem;
if(scope == IdentityScope.EUID) {
- mappedIdentity = euidService.mapIdentity(mapRequestRespectOptOut);
+ identityMapResponseItem = euidService.mapHashedDii(identityMapRequestItemRespectOptOut);
}
else {
- mappedIdentity = uid2Service.mapIdentity(mapRequestRespectOptOut);
+ identityMapResponseItem = uid2Service.mapHashedDii(identityMapRequestItemRespectOptOut);
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(mappedIdentity);
- assertFalse(mappedIdentity.isOptedOut());
+ assertNotNull(identityMapResponseItem);
+ assertFalse(identityMapResponseItem.isOptedOut());
}
@ParameterizedTest
@@ -681,37 +705,35 @@ void testSpecialIdentityRefreshOptOutIdentityMap(TestIdentityInputType type, Str
void testSpecialIdentityValidateGenerate(TestIdentityInputType type, String id, IdentityScope scope) {
InputUtil.InputVal inputVal = generateInputVal(type, id);
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- inputVal.toUserIdentity(scope, 0, this.now),
- OptoutCheckPolicy.RespectOptOut
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ inputVal.toHashedDii(scope),
+ OptoutCheckPolicy.RespectOptOut, PrivacyBits.fromInt(0), this.now
);
// all identities have optout records, ensure validate identities still get generated
when(this.optOutStore.getLatestEntry(any())).thenReturn(Instant.now());
- IdentityTokens tokens;
- AdvertisingToken advertisingToken;
+ TokenGenerateResponse tokenGenerateResponse;
+ AdvertisingTokenRequest advertisingTokenRequest;
if (scope == IdentityScope.EUID) {
- tokens = euidService.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = euidService.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
else {
- tokens = uid2Service.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
- advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), scope, identityRequest.userIdentity.identityType, identityRequest.publisherIdentity.siteId);
+ advertisingTokenRequest = validateAndGetToken(tokenEncoder, tokenGenerateResponse.getAdvertisingToken(), scope, tokenGenerateRequest.hashedDii.diiType(), tokenGenerateRequest.sourcePublisher.siteId);
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(tokens);
- assertNotEquals(IdentityTokens.LogoutToken, tokens);
- assertNotNull(advertisingToken.userIdentity);
+ assertNotNull(tokenGenerateResponse);
+ assertNotEquals(TokenGenerateResponse.OptOutResponse, tokenGenerateResponse);
+ assertNotNull(advertisingTokenRequest.rawUid);
}
@@ -727,25 +749,25 @@ void testSpecialIdentityValidateGenerate(TestIdentityInputType type, String id,
void testSpecialIdentityValidateIdentityMap(TestIdentityInputType type, String id, IdentityScope scope) {
InputUtil.InputVal inputVal = generateInputVal(type, id);
- final MapRequest mapRequestRespectOptOut = new MapRequest(
- inputVal.toUserIdentity(scope, 0, this.now),
+ final IdentityMapRequestItem identityMapRequestItemRespectOptOut = new IdentityMapRequestItem(
+ inputVal.toHashedDii(scope),
OptoutCheckPolicy.RespectOptOut,
now);
// all identities have optout records, ensure validate identities still get mapped
when(this.optOutStore.getLatestEntry(any())).thenReturn(Instant.now());
- final MappedIdentity mappedIdentity;
+ final IdentityMapResponseItem identityMapResponseItem;
if(scope == IdentityScope.EUID) {
- mappedIdentity = euidService.mapIdentity(mapRequestRespectOptOut);
+ identityMapResponseItem = euidService.mapHashedDii(identityMapRequestItemRespectOptOut);
}
else {
- mappedIdentity = uid2Service.mapIdentity(mapRequestRespectOptOut);
+ identityMapResponseItem = uid2Service.mapHashedDii(identityMapRequestItemRespectOptOut);
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotNull(mappedIdentity);
- assertFalse(mappedIdentity.isOptedOut());
+ assertNotNull(identityMapResponseItem);
+ assertFalse(identityMapResponseItem.isOptedOut());
}
@ParameterizedTest
@@ -757,40 +779,38 @@ void testSpecialIdentityValidateIdentityMap(TestIdentityInputType type, String i
"EmailHash,blah@unifiedid.com,EUID"})
void testNormalIdentityOptIn(TestIdentityInputType type, String id, IdentityScope scope) {
InputUtil.InputVal inputVal = generateInputVal(type, id);
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- inputVal.toUserIdentity(scope, 0, this.now),
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ inputVal.toHashedDii(scope),
OptoutCheckPolicy.DoNotRespect
);
- IdentityTokens tokens;
+ TokenGenerateResponse tokenGenerateResponse;
if(scope == IdentityScope.EUID) {
- tokens = euidService.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = euidService.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
else {
- tokens = uid2Service.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(false);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(true);
- assertNotEquals(tokens, IdentityTokens.LogoutToken);
- assertNotNull(tokens);
-
- final RefreshToken refreshToken = this.tokenEncoder.decodeRefreshToken(tokens.getRefreshToken());
- RefreshResponse refreshResponse = (scope == IdentityScope.EUID? euidService: uid2Service).refreshIdentity(
- refreshToken,
- Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
- Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
- Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
+ assertNotEquals(tokenGenerateResponse, TokenGenerateResponse.OptOutResponse);
+ assertNotNull(tokenGenerateResponse);
+
+ final TokenRefreshRequest tokenRefreshRequest = this.tokenEncoder.decodeRefreshToken(tokenGenerateResponse.getRefreshToken());
+ TokenRefreshResponse refreshResponse =
+ (scope == IdentityScope.EUID? euidService: uid2Service).refreshIdentity(tokenRefreshRequest,
+ Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
+ Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
+ Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
assertTrue(refreshResponse.isRefreshed());
- assertNotNull(refreshResponse.getTokens());
- assertNotEquals(RefreshResponse.Optout, refreshResponse);
+ assertNotNull(refreshResponse.getIdentityResponse());
+ assertNotEquals(TokenRefreshResponse.Optout, refreshResponse);
}
@ParameterizedTest
@@ -832,65 +852,62 @@ void testExpiredSaltsNotifiesShutdownHandler(TestIdentityInputType type, String
InputUtil.InputVal inputVal = generateInputVal(type, id);
- final IdentityRequest identityRequest = new IdentityRequest(
- new PublisherIdentity(123, 124, 125),
- inputVal.toUserIdentity(scope, 0, this.now),
- OptoutCheckPolicy.RespectOptOut);
+ final TokenGenerateRequest tokenGenerateRequest = new TokenGenerateRequest(
+ new SourcePublisher(123, 124, 125),
+ inputVal.toHashedDii(scope),
+ OptoutCheckPolicy.RespectOptOut, PrivacyBits.fromInt(0), this.now);
- IdentityTokens tokens;
- AdvertisingToken advertisingToken;
+ TokenGenerateResponse tokenGenerateResponse;
+ AdvertisingTokenRequest advertisingTokenRequest;
reset(shutdownHandler);
if(scope == IdentityScope.EUID) {
- tokens = euidService.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = euidService.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
- advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), IdentityScope.EUID, identityRequest.userIdentity.identityType, identityRequest.publisherIdentity.siteId);
+ advertisingTokenRequest = validateAndGetToken(tokenEncoder, tokenGenerateResponse.getAdvertisingToken(), IdentityScope.EUID, tokenGenerateRequest.hashedDii.diiType(), tokenGenerateRequest.sourcePublisher.siteId);
}
else {
- tokens = uid2Service.generateIdentity(
- identityRequest,
+ tokenGenerateResponse = uid2Service.generateIdentity(tokenGenerateRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
- advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), IdentityScope.UID2, identityRequest.userIdentity.identityType, identityRequest.publisherIdentity.siteId);
+ advertisingTokenRequest = validateAndGetToken(tokenEncoder, tokenGenerateResponse.getAdvertisingToken(), IdentityScope.UID2, tokenGenerateRequest.hashedDii.diiType(), tokenGenerateRequest.sourcePublisher.siteId);
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(true);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(false);
- assertNotNull(tokens);
- assertNotEquals(IdentityTokens.LogoutToken, tokens);
- assertNotNull(advertisingToken.userIdentity);
+ assertNotNull(tokenGenerateResponse);
+ assertNotEquals(TokenGenerateResponse.OptOutResponse, tokenGenerateResponse);
+ assertNotNull(advertisingTokenRequest.rawUid);
- final RefreshToken refreshToken = this.tokenEncoder.decodeRefreshToken(tokens.getRefreshToken());
+ final TokenRefreshRequest tokenRefreshRequest = this.tokenEncoder.decodeRefreshToken(tokenGenerateResponse.getRefreshToken());
reset(shutdownHandler);
- RefreshResponse refreshResponse = (scope == IdentityScope.EUID? euidService: uid2Service).refreshIdentity(
- refreshToken,
+ TokenRefreshResponse refreshResponse = (scope == IdentityScope.EUID? euidService: uid2Service).refreshIdentity(tokenRefreshRequest,
Duration.ofSeconds(REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS));
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(true);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(false);
assertTrue(refreshResponse.isRefreshed());
- assertNotNull(refreshResponse.getTokens());
- assertNotEquals(RefreshResponse.Optout, refreshResponse);
+ assertNotNull(refreshResponse.getIdentityResponse());
+ assertNotEquals(TokenRefreshResponse.Optout, refreshResponse);
- final MapRequest mapRequest = new MapRequest(
- inputVal.toUserIdentity(scope, 0, this.now),
+ final IdentityMapRequestItem identityMapRequestItem = new IdentityMapRequestItem(
+ inputVal.toHashedDii(scope),
OptoutCheckPolicy.RespectOptOut,
now);
- final MappedIdentity mappedIdentity;
+ final IdentityMapResponseItem identityMapResponseItem;
reset(shutdownHandler);
if(scope == IdentityScope.EUID) {
- mappedIdentity = euidService.mapIdentity(mapRequest);
+ identityMapResponseItem = euidService.mapHashedDii(identityMapRequestItem);
}
else {
- mappedIdentity = uid2Service.mapIdentity(mapRequest);
+ identityMapResponseItem = uid2Service.mapHashedDii(identityMapRequestItem);
}
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(true);
verify(shutdownHandler, never()).handleSaltRetrievalResponse(false);
- assertNotNull(mappedIdentity);
- assertFalse(mappedIdentity.isOptedOut());
+ assertNotNull(identityMapResponseItem);
+ assertFalse(identityMapResponseItem.isOptedOut());
}
@@ -906,14 +923,15 @@ void testMappedIdentityWithPreviousSaltReturnsPreviousUid() {
var email = "test@uid.com";
InputUtil.InputVal emailInput = generateInputVal(TestIdentityInputType.Email, email);
- MapRequest mapRequest = new MapRequest(emailInput.toUserIdentity(IdentityScope.UID2, 0, this.now), OptoutCheckPolicy.RespectOptOut, now);
+ IdentityMapRequestItem mapRequest = new IdentityMapRequestItem(emailInput.toHashedDii(IdentityScope.UID2),
+ OptoutCheckPolicy.RespectOptOut, now);
- MappedIdentity mappedIdentity = uid2Service.mapIdentity(mapRequest);
+ IdentityMapResponseItem mappedIdentity = uid2Service.mapHashedDii(mapRequest);
- var expectedCurrentUID = UIDOperatorVerticleTest.getRawUid(IdentityType.Email, email, FIRST_LEVEL_SALT, salt.currentSalt(), IdentityScope.UID2, uid2Config.getBoolean(IdentityV3Prop));
- var expectedPreviousUID = UIDOperatorVerticleTest.getRawUid(IdentityType.Email, email, FIRST_LEVEL_SALT, salt.previousSalt(), IdentityScope.UID2, uid2Config.getBoolean(IdentityV3Prop));
- assertArrayEquals(expectedCurrentUID, mappedIdentity.advertisingId);
- assertArrayEquals(expectedPreviousUID, mappedIdentity.previousAdvertisingId);
+ var expectedCurrentUID = UIDOperatorVerticleTest.getRawUid(DiiType.Email, email, FIRST_LEVEL_SALT, salt.currentSalt(), IdentityScope.UID2, uid2Config.getBoolean(IdentityV3Prop));
+ var expectedPreviousUID = UIDOperatorVerticleTest.getRawUid(DiiType.Email, email, FIRST_LEVEL_SALT, salt.previousSalt(), IdentityScope.UID2, uid2Config.getBoolean(IdentityV3Prop));
+ assertArrayEquals(expectedCurrentUID, mappedIdentity.rawUid);
+ assertArrayEquals(expectedPreviousUID, mappedIdentity.previousRawUid);
}
@ParameterizedTest
@@ -929,12 +947,12 @@ void testMappedIdentityWithOutdatedPreviousSaltReturnsNoPreviousUid(long extraMs
var email = "test@uid.com";
InputUtil.InputVal emailInput = generateInputVal(TestIdentityInputType.Email, email);
- MapRequest mapRequest = new MapRequest(emailInput.toUserIdentity(IdentityScope.UID2, 0, this.now), OptoutCheckPolicy.RespectOptOut, now);
+ IdentityMapRequestItem mapRequest = new IdentityMapRequestItem(emailInput.toHashedDii(IdentityScope.UID2), OptoutCheckPolicy.RespectOptOut, now);
- MappedIdentity mappedIdentity = uid2Service.mapIdentity(mapRequest);
- var expectedCurrentUID = UIDOperatorVerticleTest.getRawUid(IdentityType.Email, email, FIRST_LEVEL_SALT, salt.currentSalt(), IdentityScope.UID2, uid2Config.getBoolean(IdentityV3Prop));
- assertArrayEquals(expectedCurrentUID, mappedIdentity.advertisingId);
- assertArrayEquals(null , mappedIdentity.previousAdvertisingId);
+ IdentityMapResponseItem mappedIdentity = uid2Service.mapHashedDii(mapRequest);
+ var expectedCurrentUID = UIDOperatorVerticleTest.getRawUid(DiiType.Email, email, FIRST_LEVEL_SALT, salt.currentSalt(), IdentityScope.UID2, uid2Config.getBoolean(IdentityV3Prop));
+ assertArrayEquals(expectedCurrentUID, mappedIdentity.rawUid);
+ assertArrayEquals(null , mappedIdentity.previousRawUid);
}
@Test
@@ -949,13 +967,13 @@ void testMappedIdentityWithNoPreviousSaltReturnsNoPreviousUid() {
var email = "test@uid.com";
InputUtil.InputVal emailInput = generateInputVal(TestIdentityInputType.Email, email);
- MapRequest mapRequest = new MapRequest(emailInput.toUserIdentity(IdentityScope.UID2, 0, this.now), OptoutCheckPolicy.RespectOptOut, now);
+ IdentityMapRequestItem mapRequest = new IdentityMapRequestItem(emailInput.toHashedDii(IdentityScope.UID2), OptoutCheckPolicy.RespectOptOut, now);
- MappedIdentity mappedIdentity = uid2Service.mapIdentity(mapRequest);
+ IdentityMapResponseItem mappedIdentity = uid2Service.mapHashedDii(mapRequest);
- var expectedCurrentUID = UIDOperatorVerticleTest.getRawUid(IdentityType.Email, email, FIRST_LEVEL_SALT, salt.currentSalt(), IdentityScope.UID2, uid2Config.getBoolean(IdentityV3Prop));
- assertArrayEquals(expectedCurrentUID, mappedIdentity.advertisingId);
- assertArrayEquals(null, mappedIdentity.previousAdvertisingId);
+ var expectedCurrentUID = UIDOperatorVerticleTest.getRawUid(DiiType.Email, email, FIRST_LEVEL_SALT, salt.currentSalt(), IdentityScope.UID2, uid2Config.getBoolean(IdentityV3Prop));
+ assertArrayEquals(expectedCurrentUID, mappedIdentity.rawUid);
+ assertArrayEquals(null, mappedIdentity.previousRawUid);
}
@ParameterizedTest
@@ -971,9 +989,9 @@ void testMappedIdentityWithValidRefreshFrom(int refreshFromDays) {
var email = "test@uid.com";
InputUtil.InputVal emailInput = generateInputVal(TestIdentityInputType.Email, email);
- MapRequest mapRequest = new MapRequest(emailInput.toUserIdentity(IdentityScope.UID2, 0, this.now), OptoutCheckPolicy.RespectOptOut, now);
+ IdentityMapRequestItem mapRequest = new IdentityMapRequestItem(emailInput.toHashedDii(IdentityScope.UID2), OptoutCheckPolicy.RespectOptOut, now);
- MappedIdentity mappedIdentity = uid2Service.mapIdentity(mapRequest);
+ IdentityMapResponseItem mappedIdentity = uid2Service.mapHashedDii(mapRequest);
assertEquals(refreshFrom, mappedIdentity.refreshFrom);
}
@@ -990,9 +1008,9 @@ void testMappedIdentityWithOutdatedRefreshFrom() {
var email = "test@uid.com";
InputUtil.InputVal emailInput = generateInputVal(TestIdentityInputType.Email, email);
- MapRequest mapRequest = new MapRequest(emailInput.toUserIdentity(IdentityScope.UID2, 0, this.now), OptoutCheckPolicy.RespectOptOut, now);
+ IdentityMapRequestItem mapRequest = new IdentityMapRequestItem(emailInput.toHashedDii(IdentityScope.UID2), OptoutCheckPolicy.RespectOptOut, now);
- MappedIdentity mappedIdentity = uid2Service.mapIdentity(mapRequest);
+ IdentityMapResponseItem mappedIdentity = uid2Service.mapHashedDii(mapRequest);
long expectedRefreshFrom = this.now.truncatedTo(DAYS).plus(1, DAYS).toEpochMilli();
assertEquals(expectedRefreshFrom, mappedIdentity.refreshFrom);
diff --git a/src/test/java/com/uid2/operator/UIDOperatorVerticleTest.java b/src/test/java/com/uid2/operator/UIDOperatorVerticleTest.java
index 46163267f..30c8bee42 100644
--- a/src/test/java/com/uid2/operator/UIDOperatorVerticleTest.java
+++ b/src/test/java/com/uid2/operator/UIDOperatorVerticleTest.java
@@ -4,7 +4,9 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import com.uid2.operator.model.*;
-import com.uid2.operator.model.IdentityScope;
+import com.uid2.operator.model.identities.IdentityScope;
+import com.uid2.operator.model.identities.DiiType;
+import com.uid2.operator.model.identities.FirstLevelHash;
import com.uid2.operator.monitoring.IStatsCollectorQueue;
import com.uid2.operator.monitoring.TokenResponseStatsCollector;
import com.uid2.operator.service.*;
@@ -75,7 +77,7 @@
import java.util.stream.Stream;
import static com.uid2.operator.ClientSideTokenGenerateTestUtil.decrypt;
-import static com.uid2.operator.IdentityConst.*;
+import static com.uid2.operator.model.identities.IdentityConst.*;
import static com.uid2.operator.service.EncodingUtils.getSha256;
import static com.uid2.operator.vertx.UIDOperatorVerticle.*;
import static com.uid2.shared.Const.Data.*;
@@ -693,26 +695,27 @@ private void assertTokenStatusMetrics(Integer siteId, TokenResponseStatsCollecto
assertEquals(1, actual);
}
- private byte[] getAdvertisingIdFromIdentity(IdentityType identityType, String identityString, String firstLevelSalt, String rotatingSalt) {
- return getRawUid(identityType, identityString, firstLevelSalt, rotatingSalt, getIdentityScope(), useRawUidV3());
+ private byte[] getRawUidFromRawDii(DiiType diiType, String rawDii, String firstLevelSalt, String rotatingSalt) {
+ return getRawUid(diiType, rawDii, firstLevelSalt, rotatingSalt, getIdentityScope(), useRawUidV3());
}
- public static byte[] getRawUid(IdentityType identityType, String identityString, String firstLevelSalt, String rotatingSalt, IdentityScope identityScope, boolean useRawUidV3) {
- return !useRawUidV3
- ? TokenUtils.getAdvertisingIdV2FromIdentity(identityString, firstLevelSalt, rotatingSalt)
- : TokenUtils.getAdvertisingIdV3FromIdentity(identityScope, identityType, identityString, firstLevelSalt, rotatingSalt);
+ public static byte[] getRawUid(DiiType diiType, String rawDii, String firstLevelSalt, String rotatingSalt, IdentityScope identityScope, boolean useIdentityV3) {
+ return !useIdentityV3
+ ? TokenUtils.getRawUidV2FromRawDii(rawDii, firstLevelSalt, rotatingSalt)
+ : TokenUtils.getRawUidV3FromRawDii(identityScope, diiType, rawDii, firstLevelSalt, rotatingSalt);
}
- public static byte[] getRawUid(IdentityType identityType, String identityString, IdentityScope identityScope, boolean useRawUidV3) {
- return !useRawUidV3
- ? TokenUtils.getAdvertisingIdV2FromIdentity(identityString, firstLevelSalt, rotatingSalt123.currentSalt())
- : TokenUtils.getAdvertisingIdV3FromIdentity(identityScope, identityType, identityString, firstLevelSalt, rotatingSalt123.currentSalt());
+ public static byte[] getRawUid(DiiType diiType, String rawDii, IdentityScope identityScope, boolean useIdentityV3) {
+ return !useIdentityV3
+ ? TokenUtils.getRawUidV2FromRawDii(rawDii, firstLevelSalt, rotatingSalt123.currentSalt())
+ : TokenUtils.getRawUidV3FromRawDii(identityScope, diiType, rawDii, firstLevelSalt,rotatingSalt123.currentSalt());
}
- private byte[] getAdvertisingIdFromIdentityHash(IdentityType identityType, String identityString, String firstLevelSalt, String rotatingSalt) {
+
+ private byte[] getRawUidFromHashedDii(DiiType diiType, String hashedDii, String firstLevelSalt, String rotatingSalt) {
return !useRawUidV3()
- ? TokenUtils.getAdvertisingIdV2FromIdentityHash(identityString, firstLevelSalt, rotatingSalt)
- : TokenUtils.getAdvertisingIdV3FromIdentityHash(getIdentityScope(), identityType, identityString, firstLevelSalt, rotatingSalt);
+ ? TokenUtils.getRawUidV2FromHashedDii(hashedDii, firstLevelSalt, rotatingSalt)
+ : TokenUtils.getRawUidV3FromHashedDii(getIdentityScope(), diiType, hashedDii, firstLevelSalt, rotatingSalt);
}
private JsonObject createBatchEmailsRequestPayload() {
@@ -837,7 +840,7 @@ void keyLatestHideRefreshKey(String apiVersion, Vertx vertx, VertxTestContext te
void tokenGenerateBothEmailAndHashSpecified(String apiVersion, Vertx vertx, VertxTestContext testContext) {
final int clientSiteId = 201;
final String emailAddress = "test@uid2.com";
- final String emailHash = TokenUtils.getIdentityHashString(emailAddress);
+ final String emailHash = TokenUtils.getHashedDiiString(emailAddress);
fakeAuth(clientSiteId, Role.GENERATOR);
setupSalts();
setupKeys();
@@ -885,28 +888,27 @@ private void assertStatsCollector(String path, String referer, String apiContact
assertEquals(siteId, messageItem.getSiteId());
}
- private AdvertisingToken validateAndGetToken(EncryptedTokenEncoder encoder, JsonObject body, IdentityType identityType) { //See UID2-79+Token+and+ID+format+v3
+ private AdvertisingTokenRequest validateAndGetToken(EncryptedTokenEncoder encoder, JsonObject body, DiiType diiType) { //See UID2-79+Token+and+ID+format+v3
final String advertisingTokenString = body.getString("advertising_token");
- validateAdvertisingToken(advertisingTokenString, getTokenVersion(), getIdentityScope(), identityType);
- AdvertisingToken advertisingToken = encoder.decodeAdvertisingToken(advertisingTokenString);
-
- // without useIdentityV3() the assert will be trigger as there's no IdentityType in v4 token generated with
+ validateAdvertisingToken(advertisingTokenString, getTokenVersion(), getIdentityScope(), diiType);
+ AdvertisingTokenRequest advertisingTokenRequest = encoder.decodeAdvertisingToken(advertisingTokenString);
+ // without useRawUidV3() the assert will be trigger as there's no IdentityType in v4 token generated with
// a raw UID v2 as old raw UID format doesn't store the identity type (and scope)
if (useRawUidV3() && getTokenVersion() == TokenVersion.V4) {
- assertEquals(identityType, advertisingToken.userIdentity.identityType);
+ assertEquals(diiType, advertisingTokenRequest.rawUid.diiType());
}
- return advertisingToken;
+ return advertisingTokenRequest;
}
- public static void validateAdvertisingToken(String advertisingTokenString, TokenVersion tokenVersion, IdentityScope identityScope, IdentityType identityType) {
+ public static void validateAdvertisingToken(String advertisingTokenString, TokenVersion tokenVersion, IdentityScope identityScope, DiiType diiType) {
if (tokenVersion == TokenVersion.V2) {
assertEquals("Ag", advertisingTokenString.substring(0, 2));
} else {
String firstChar = advertisingTokenString.substring(0, 1);
if (identityScope == IdentityScope.UID2) {
- assertEquals(identityType == IdentityType.Email ? "A" : "B", firstChar);
+ assertEquals(diiType == DiiType.Email ? "A" : "B", firstChar);
} else {
- assertEquals(identityType == IdentityType.Email ? "E" : "F", firstChar);
+ assertEquals(diiType == DiiType.Email ? "E" : "F", firstChar);
}
String secondChar = advertisingTokenString.substring(1, 2);
@@ -923,14 +925,11 @@ public static void validateAdvertisingToken(String advertisingTokenString, Token
}
}
- RefreshToken decodeRefreshToken(EncryptedTokenEncoder encoder, String refreshTokenString, IdentityType identityType) {
- RefreshToken refreshToken = encoder.decodeRefreshToken(refreshTokenString);
- assertEquals(getIdentityScope(), refreshToken.userIdentity.identityScope);
- assertEquals(identityType, refreshToken.userIdentity.identityType);
- return refreshToken;
- }
- RefreshToken decodeRefreshToken(EncryptedTokenEncoder encoder, String refreshTokenString) {
- return decodeRefreshToken(encoder, refreshTokenString, IdentityType.Email);
+ TokenRefreshRequest decodeRefreshToken(EncryptedTokenEncoder encoder, String refreshTokenString, DiiType diiType) {
+ TokenRefreshRequest tokenRefreshRequest = encoder.decodeRefreshToken(refreshTokenString);
+ assertEquals(getIdentityScope(), tokenRefreshRequest.firstLevelHash.identityScope());
+ assertEquals(diiType, tokenRefreshRequest.firstLevelHash.diiType());
+ return tokenRefreshRequest;
}
@ParameterizedTest
@@ -943,7 +942,7 @@ void identityMapNewClientNoPolicySpecified(String apiVersion, String contentType
setupKeys();
// the clock value shouldn't matter here
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(now.minus(1, ChronoUnit.HOURS));
JsonObject req = new JsonObject();
@@ -970,7 +969,7 @@ void identityMapNewClientWrongPolicySpecified(String apiVersion, String policyPa
setupSalts();
setupKeys();
// the clock value shouldn't matter here
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(now.minus(1, ChronoUnit.HOURS));
JsonObject req = new JsonObject();
JsonArray emails = new JsonArray();
@@ -1091,7 +1090,7 @@ void v3IdentityMapMixedInputSuccess(String contentType, Vertx vertx, VertxTestCo
SaltEntry salt = new SaltEntry(1, "1", lastUpdated.toEpochMilli(), "salt", refreshFrom.toEpochMilli(), "previousSalt", null, null);
when(saltProviderSnapshot.getRotatingSalt(any())).thenReturn(salt);
- var phoneHash = TokenUtils.getIdentityHashString("+15555555555");
+ var phoneHash = TokenUtils.getHashedDiiString("+15555555555");
JsonObject request = new JsonObject(String.format("""
{
"email": ["test1@uid2.com", "test2@uid2.com"],
@@ -1109,15 +1108,15 @@ void v3IdentityMapMixedInputSuccess(String contentType, Vertx vertx, VertxTestCo
assertEquals(2, mappedEmails.size());
var mappedEmailExpected1 = JsonObject.of(
- "u", EncodingUtils.toBase64String(getAdvertisingIdFromIdentity(IdentityType.Email, "test1@uid2.com", firstLevelSalt, salt.currentSalt())),
- "p", EncodingUtils.toBase64String(getAdvertisingIdFromIdentity(IdentityType.Email,"test1@uid2.com", firstLevelSalt, salt.previousSalt())),
+ "u", EncodingUtils.toBase64String(getRawUidFromRawDii(DiiType.Email, "test1@uid2.com", firstLevelSalt, salt.currentSalt())),
+ "p", EncodingUtils.toBase64String(getRawUidFromRawDii(DiiType.Email,"test1@uid2.com", firstLevelSalt, salt.previousSalt())),
"r", refreshFrom.getEpochSecond()
);
assertEquals(mappedEmailExpected1, mappedEmails.getJsonObject(0));
var mappedEmailExpected2 = JsonObject.of(
- "u", EncodingUtils.toBase64String(getAdvertisingIdFromIdentity(IdentityType.Email, "test2@uid2.com", firstLevelSalt, salt.currentSalt())),
- "p", EncodingUtils.toBase64String(getAdvertisingIdFromIdentity(IdentityType.Email,"test2@uid2.com", firstLevelSalt, salt.previousSalt())),
+ "u", EncodingUtils.toBase64String(getRawUidFromRawDii(DiiType.Email, "test2@uid2.com", firstLevelSalt, salt.currentSalt())),
+ "p", EncodingUtils.toBase64String(getRawUidFromRawDii(DiiType.Email,"test2@uid2.com", firstLevelSalt, salt.previousSalt())),
"r", refreshFrom.getEpochSecond()
);
assertEquals(mappedEmailExpected2, mappedEmails.getJsonObject(1));
@@ -1129,8 +1128,8 @@ void v3IdentityMapMixedInputSuccess(String contentType, Vertx vertx, VertxTestCo
assertEquals(1, mappedPhoneHash.size());
var mappedPhoneHashExpected = JsonObject.of(
- "u", EncodingUtils.toBase64String(getAdvertisingIdFromIdentityHash(IdentityType.Phone, phoneHash, firstLevelSalt, salt.currentSalt())),
- "p", EncodingUtils.toBase64String(getAdvertisingIdFromIdentityHash(IdentityType.Phone, phoneHash, firstLevelSalt, salt.previousSalt())),
+ "u", EncodingUtils.toBase64String(getRawUidFromHashedDii(DiiType.Phone, phoneHash, firstLevelSalt, salt.currentSalt())),
+ "p", EncodingUtils.toBase64String(getRawUidFromHashedDii(DiiType.Phone, phoneHash, firstLevelSalt, salt.previousSalt())),
"r", refreshFrom.getEpochSecond()
);
assertEquals(mappedPhoneHashExpected, mappedPhoneHash.getJsonObject(0));
@@ -1263,7 +1262,7 @@ void v3IdentityMapNoPreviousAdvertisingId(String previousSalt, Vertx vertx, Vert
var mappedEmails = body.getJsonArray("email");
var expectedMappedEmails = JsonObject.of(
- "u", EncodingUtils.toBase64String(getAdvertisingIdFromIdentity(IdentityType.Email, "test1@uid2.com", firstLevelSalt, salt.currentSalt())),
+ "u", EncodingUtils.toBase64String(getRawUidFromRawDii(DiiType.Email, "test1@uid2.com", firstLevelSalt, salt.currentSalt())),
"p", null,
"r", refreshFrom.getEpochSecond()
);
@@ -1295,7 +1294,7 @@ void v3IdentityMapOutdatedRefreshFrom(Vertx vertx, VertxTestContext testContext)
var mappedEmails = body.getJsonArray("email");
var expectedMappedEmails = JsonObject.of(
- "u", EncodingUtils.toBase64String(getAdvertisingIdFromIdentity(IdentityType.Email, "test1@uid2.com", firstLevelSalt, salt.currentSalt())),
+ "u", EncodingUtils.toBase64String(getRawUidFromRawDii(DiiType.Email, "test1@uid2.com", firstLevelSalt, salt.currentSalt())),
"p", null,
"r", asOf.truncatedTo(DAYS).plus(1, DAYS).getEpochSecond()
);
@@ -1434,7 +1433,7 @@ void tokenGenerateNewClientWrongPolicySpecifiedOlderKeySuccessful(String policyP
"policy,+01234567890,Phone",
"optout_check,someoptout@example.com,Email",
"optout_check,+01234567890,Phone"})
- void tokenGenerateOptOutToken(String policyParameterKey, String identity, IdentityType identityType,
+ void tokenGenerateOptOutToken(String policyParameterKey, String identity, DiiType diiType,
Vertx vertx, VertxTestContext testContext) {
ClientKey oldClientKey = new ClientKey(
null,
@@ -1454,13 +1453,13 @@ void tokenGenerateOptOutToken(String policyParameterKey, String identity, Identi
setupKeys();
JsonObject v2Payload = new JsonObject();
- v2Payload.put(identityType.name().toLowerCase(), identity);
+ v2Payload.put(diiType.name().toLowerCase(), identity);
v2Payload.put(policyParameterKey, OptoutCheckPolicy.DoNotRespect.policy);
sendTokenGenerate("v2", vertx,
"", v2Payload, 200,
json -> {
- InputUtil.InputVal optOutTokenInput = identityType == IdentityType.Email ?
+ InputUtil.InputVal optOutTokenInput = diiType == DiiType.Email ?
InputUtil.InputVal.validEmail(OptOutTokenIdentityForEmail, OptOutTokenIdentityForEmail) :
InputUtil.InputVal.validPhone(OptOutIdentityForPhone, OptOutTokenIdentityForPhone);
@@ -1473,23 +1472,23 @@ void tokenGenerateOptOutToken(String policyParameterKey, String identity, Identi
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, body, identityType);
- RefreshToken refreshToken = encoder.decodeRefreshToken(body.getString("decrypted_refresh_token"));
- final byte[] advertisingId = getAdvertisingIdFromIdentity(identityType,
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, body, diiType);
+ TokenRefreshRequest tokenRefreshRequest = encoder.decodeRefreshToken(body.getString("decrypted_refresh_token"));
+ final byte[] rawUid = getRawUidFromRawDii(diiType,
optOutTokenInput.getNormalized(),
firstLevelSalt,
rotatingSalt123.currentSalt());
- final byte[] firstLevelHash = TokenUtils.getFirstLevelHashFromIdentity(optOutTokenInput.getNormalized(), firstLevelSalt);
- assertArrayEquals(advertisingId, advertisingToken.userIdentity.id);
- assertArrayEquals(firstLevelHash, refreshToken.userIdentity.id);
+ final byte[] firstLevelHash = TokenUtils.getFirstLevelHashFromRawDii(optOutTokenInput.getNormalized(), firstLevelSalt);
+ assertArrayEquals(rawUid, advertisingTokenRequest.rawUid.rawUid());
+ assertArrayEquals(firstLevelHash, tokenRefreshRequest.firstLevelHash.firstLevelHash());
String advertisingTokenString = body.getString("advertising_token");
final Instant now = Instant.now();
final String token = advertisingTokenString;
- final boolean matchedOptedOutIdentity = this.uidOperatorVerticle.getIdService().advertisingTokenMatches(token, optOutTokenInput.toUserIdentity(getIdentityScope(), 0, now), now);
+ final boolean matchedOptedOutIdentity = this.uidOperatorVerticle.getIdService().advertisingTokenMatches(token, optOutTokenInput.toHashedDii(getIdentityScope()), now);
assertTrue(matchedOptedOutIdentity);
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenGenerated());
- assertTrue(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenOptedOut());
+ assertFalse(advertisingTokenRequest.privacyBits.isClientSideTokenGenerated());
+ assertTrue(advertisingTokenRequest.privacyBits.isClientSideTokenOptedOut());
assertTokenStatusMetrics(
201,
@@ -1517,7 +1516,7 @@ void tokenGenerateOptOutToken(String policyParameterKey, String identity, Identi
"policy,+01234567890,Phone",
"optout_check,someoptout@example.com,Email",
"optout_check,+01234567890,Phone"})
- void tokenGenerateOptOutTokenWithDisableOptoutTokenFF(String policyParameterKey, String identity, IdentityType identityType,
+ void tokenGenerateOptOutTokenWithDisableOptoutTokenFF(String policyParameterKey, String identity, DiiType identityType,
Vertx vertx, VertxTestContext testContext) {
ClientKey oldClientKey = new ClientKey(
null,
@@ -1579,20 +1578,14 @@ void tokenGenerateForEmail(String apiVersion, String contentType, Vertx vertx, V
assertNotNull(body);
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, body, IdentityType.Email);
-
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenGenerated());
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenOptedOut());
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
- assertArrayEquals(getAdvertisingIdFromIdentity(IdentityType.Email, emailAddress, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingToken.userIdentity.id);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, body, DiiType.Email);
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"), DiiType.Email);
- RefreshToken refreshToken = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"));
- assertEquals(clientSiteId, refreshToken.publisherIdentity.siteId);
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentity(emailAddress, firstLevelSalt), refreshToken.userIdentity.id);
-
- assertEqualsClose(now.plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("identity_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("refresh_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshIdentityAfter.toMillis()), Instant.ofEpochMilli(body.getLong("refresh_from")), 10);
+ assertAdvertisingTokenRefreshTokenRequests(advertisingTokenRequest, tokenRefreshRequest, clientSiteId,
+ getRawUidFromRawDii(DiiType.Email, emailAddress, firstLevelSalt, rotatingSalt123.currentSalt()),
+ PrivacyBits.DEFAULT,
+ body,
+ TokenUtils.getFirstLevelHashFromRawDii(emailAddress, firstLevelSalt));
assertStatsCollector("/" + apiVersion + "/token/generate", null, "test-contact", clientSiteId);
@@ -1601,11 +1594,32 @@ void tokenGenerateForEmail(String apiVersion, String contentType, Vertx vertx, V
Map.of(HttpHeaders.CONTENT_TYPE.toString(), contentType));
}
+ public void assertAdvertisingTokenRefreshTokenRequests(AdvertisingTokenRequest advertisingTokenRequest, TokenRefreshRequest tokenRefreshRequest,
+ int expectedClientSiteId, byte[] expectedRawUidIdentity, PrivacyBits expectedPrivacyBits, JsonObject identityResponse, byte[] firstLevelHashIdentity) {
+
+ assertEquals(expectedClientSiteId, advertisingTokenRequest.sourcePublisher.siteId);
+ assertEquals(expectedClientSiteId, tokenRefreshRequest.sourcePublisher.siteId);
+ assertArrayEquals(expectedRawUidIdentity, advertisingTokenRequest.rawUid.rawUid());
+
+ verifyPrivacyBits(expectedPrivacyBits, advertisingTokenRequest, tokenRefreshRequest);
+ verifyFirstLevelHashIdentityAndEstablishedAt(firstLevelHashIdentity, tokenRefreshRequest, identityResponse, advertisingTokenRequest.establishedAt);
+
+ assertEqualsClose(now.plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(identityResponse.getLong("identity_expires")), 10);
+ assertEqualsClose(now.plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(identityResponse.getLong("refresh_expires")), 10);
+ assertEqualsClose(now.plusMillis(refreshIdentityAfter.toMillis()), Instant.ofEpochMilli(identityResponse.getLong("refresh_from")), 10);
+ }
+
+ public void verifyPrivacyBits(PrivacyBits expectedValue, AdvertisingTokenRequest advertisingTokenRequest,
+ TokenRefreshRequest tokenRefreshRequest) {
+ assertEquals(advertisingTokenRequest.privacyBits, expectedValue);
+ assertEquals(advertisingTokenRequest.privacyBits, tokenRefreshRequest.privacyBits);
+ }
+
@ParameterizedTest
@ValueSource(strings = {"v2"})
void tokenGenerateForEmailHash(String apiVersion, Vertx vertx, VertxTestContext testContext) {
final int clientSiteId = 201;
- final String emailHash = TokenUtils.getIdentityHashString("test@uid2.com");
+ final String emailHash = TokenUtils.getHashedDiiString("test@uid2.com");
fakeAuth(clientSiteId, Role.GENERATOR);
setupSalts();
setupKeys();
@@ -1622,20 +1636,14 @@ void tokenGenerateForEmailHash(String apiVersion, Vertx vertx, VertxTestContext
assertNotNull(body);
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, body, IdentityType.Email);
-
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenGenerated());
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenOptedOut());
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
- assertArrayEquals(getAdvertisingIdFromIdentityHash(IdentityType.Email, emailHash, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingToken.userIdentity.id);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, body, DiiType.Email);
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, apiVersion.equals("v2") ? body.getString("decrypted_refresh_token") : body.getString("refresh_token"), DiiType.Email);
- RefreshToken refreshToken = decodeRefreshToken(encoder, apiVersion.equals("v2") ? body.getString("decrypted_refresh_token") : body.getString("refresh_token"));
- assertEquals(clientSiteId, refreshToken.publisherIdentity.siteId);
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentityHash(emailHash, firstLevelSalt), refreshToken.userIdentity.id);
-
- assertEqualsClose(now.plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("identity_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("refresh_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshIdentityAfter.toMillis()), Instant.ofEpochMilli(body.getLong("refresh_from")), 10);
+ assertAdvertisingTokenRefreshTokenRequests(advertisingTokenRequest, tokenRefreshRequest, clientSiteId,
+ getRawUidFromHashedDii(DiiType.Email, emailHash, firstLevelSalt, rotatingSalt123.currentSalt()),
+ PrivacyBits.DEFAULT,
+ body,
+ TokenUtils.getFirstLevelHashFromHashedDii(emailHash, firstLevelSalt));
testContext.completeNow();
});
@@ -1660,32 +1668,64 @@ void tokenGenerateThenRefresh(String apiVersion, String contentType, Vertx vertx
assertNotNull(bodyJson);
String genRefreshToken = bodyJson.getString("refresh_token");
+ EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
+
+ AdvertisingTokenRequest firstAdvertisingTokenRequest = validateAndGetToken(encoder, bodyJson,
+ DiiType.Email);
+
+ TokenRefreshRequest firstTokenRefreshRequest = decodeRefreshToken(encoder, bodyJson.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"), DiiType.Email);
+
+ assertEquals(firstAdvertisingTokenRequest.establishedAt, firstTokenRefreshRequest.firstLevelHash.establishedAt());
when(this.optOutStore.getLatestEntry(any())).thenReturn(null);
+ byte[] expectedRawUidIdentity = getRawUidFromRawDii(DiiType.Email, emailAddress, firstLevelSalt,
+ rotatingSalt123.currentSalt());
+ byte[] expectedFirstLevelHashIdentity = TokenUtils.getFirstLevelHashFromRawDii(emailAddress, firstLevelSalt);
+
+ assertAdvertisingTokenRefreshTokenRequests(firstAdvertisingTokenRequest, firstTokenRefreshRequest, clientSiteId,
+ expectedRawUidIdentity,
+ PrivacyBits.DEFAULT,
+ bodyJson,
+ expectedFirstLevelHashIdentity);
+
+
sendTokenRefresh(apiVersion, vertx, testContext, genRefreshToken, bodyJson.getString("refresh_response_key"), 200, refreshRespJson ->
{
assertEquals("success", refreshRespJson.getString("status"));
JsonObject refreshBody = refreshRespJson.getJsonObject("body");
assertNotNull(refreshBody);
- EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
-
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, refreshBody, IdentityType.Email);
-
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenGenerated());
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenOptedOut());
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
- assertArrayEquals(getAdvertisingIdFromIdentity(IdentityType.Email, emailAddress, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingToken.userIdentity.id);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, refreshBody, DiiType.Email);
String refreshTokenStringNew = refreshBody.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token");
assertNotEquals(genRefreshToken, refreshTokenStringNew);
- RefreshToken refreshToken = decodeRefreshToken(encoder, refreshTokenStringNew);
- assertEquals(clientSiteId, refreshToken.publisherIdentity.siteId);
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentity(emailAddress, firstLevelSalt), refreshToken.userIdentity.id);
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, refreshTokenStringNew, DiiType.Email);
- assertEqualsClose(now.plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("identity_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("refresh_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshIdentityAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("refresh_from")), 10);
+ // assert if the ad/refresh tokens from original token/generate is same as the ad/refresh tokens from token/refresh
+ assertAdvertisingTokenRefreshTokenRequests(
+ advertisingTokenRequest,
+ firstTokenRefreshRequest,
+ clientSiteId,
+ expectedRawUidIdentity,
+ PrivacyBits.DEFAULT,
+ bodyJson,
+ expectedFirstLevelHashIdentity);
+ assertAdvertisingTokenRefreshTokenRequests(
+ firstAdvertisingTokenRequest,
+ tokenRefreshRequest,
+ clientSiteId,
+ expectedRawUidIdentity,
+ PrivacyBits.DEFAULT,
+ bodyJson,
+ expectedFirstLevelHashIdentity);
+ assertAdvertisingTokenRefreshTokenRequests(
+ advertisingTokenRequest,
+ tokenRefreshRequest,
+ clientSiteId,
+ expectedRawUidIdentity,
+ PrivacyBits.DEFAULT,
+ bodyJson,
+ expectedFirstLevelHashIdentity);
assertTokenStatusMetrics(
clientSiteId,
@@ -1731,18 +1771,18 @@ void tokenGenerateThenRefreshSaltsExpired(String apiVersion, Vertx vertx, VertxT
assertNotNull(refreshBody);
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, refreshBody, IdentityType.Email);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, refreshBody, DiiType.Email);
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenGenerated());
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenOptedOut());
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
- assertArrayEquals(getAdvertisingIdFromIdentity(IdentityType.Email, emailAddress, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingToken.userIdentity.id);
+ assertFalse(advertisingTokenRequest.privacyBits.isClientSideTokenGenerated());
+ assertFalse(advertisingTokenRequest.privacyBits.isClientSideTokenOptedOut());
+ assertEquals(clientSiteId, advertisingTokenRequest.sourcePublisher.siteId);
+ assertArrayEquals(getRawUidFromRawDii(DiiType.Email, emailAddress, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingTokenRequest.rawUid.rawUid());
String refreshTokenStringNew = refreshBody.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token");
assertNotEquals(genRefreshToken, refreshTokenStringNew);
- RefreshToken refreshToken = decodeRefreshToken(encoder, refreshTokenStringNew);
- assertEquals(clientSiteId, refreshToken.publisherIdentity.siteId);
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentity(emailAddress, firstLevelSalt), refreshToken.userIdentity.id);
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, refreshTokenStringNew, DiiType.Email);
+ assertEquals(clientSiteId, tokenRefreshRequest.sourcePublisher.siteId);
+ assertArrayEquals(TokenUtils.getFirstLevelHashFromRawDii(emailAddress, firstLevelSalt), tokenRefreshRequest.firstLevelHash.firstLevelHash());
assertEqualsClose(now.plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("identity_expires")), 10);
assertEqualsClose(now.plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("refresh_expires")), 10);
@@ -1909,13 +1949,13 @@ void tokenGenerateUsingCustomSiteKey(String apiVersion, Vertx vertx, VertxTestCo
assertNotNull(body);
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, body, IdentityType.Email);
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
- assertArrayEquals(getAdvertisingIdFromIdentity(IdentityType.Email, emailAddress, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingToken.userIdentity.id);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, body, DiiType.Email);
+ assertEquals(clientSiteId, advertisingTokenRequest.sourcePublisher.siteId);
+ assertArrayEquals(getRawUidFromRawDii(DiiType.Email, emailAddress, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingTokenRequest.rawUid.rawUid());
- RefreshToken refreshToken = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"));
- assertEquals(clientSiteId, refreshToken.publisherIdentity.siteId);
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentity(emailAddress, firstLevelSalt), refreshToken.userIdentity.id);
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"), DiiType.Email);
+ assertEquals(clientSiteId, tokenRefreshRequest.sourcePublisher.siteId);
+ assertArrayEquals(TokenUtils.getFirstLevelHashFromRawDii(emailAddress, firstLevelSalt), tokenRefreshRequest.firstLevelHash.firstLevelHash());
testContext.completeNow();
});
@@ -1943,16 +1983,18 @@ void tokenGenerateSaltsExpired(String apiVersion, Vertx vertx, VertxTestContext
assertNotNull(body);
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, body, IdentityType.Email);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, body, DiiType.Email);
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenGenerated());
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenOptedOut());
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
- assertArrayEquals(getAdvertisingIdFromIdentity(IdentityType.Email, emailAddress, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingToken.userIdentity.id);
+ assertTrue(advertisingTokenRequest.privacyBits.isLegacyBitSet());
+ assertEquals(advertisingTokenRequest.privacyBits, PrivacyBits.DEFAULT);
+ assertFalse(advertisingTokenRequest.privacyBits.isClientSideTokenGenerated());
+ assertFalse(advertisingTokenRequest.privacyBits.isClientSideTokenOptedOut());
+ assertEquals(clientSiteId, advertisingTokenRequest.sourcePublisher.siteId);
+ assertArrayEquals(getRawUidFromRawDii(DiiType.Email, emailAddress, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingTokenRequest.rawUid.rawUid());
- RefreshToken refreshToken = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"));
- assertEquals(clientSiteId, refreshToken.publisherIdentity.siteId);
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentity(emailAddress, firstLevelSalt), refreshToken.userIdentity.id);
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"), DiiType.Email);
+ assertEquals(clientSiteId, tokenRefreshRequest.sourcePublisher.siteId);
+ assertArrayEquals(TokenUtils.getFirstLevelHashFromRawDii(emailAddress, firstLevelSalt), tokenRefreshRequest.firstLevelHash.firstLevelHash());
assertEqualsClose(now.plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("identity_expires")), 10);
assertEqualsClose(now.plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("refresh_expires")), 10);
@@ -2217,7 +2259,6 @@ void tokenValidateWithEmailHash_Mismatch(String apiVersion, Vertx vertx, VertxTe
});
}
-
@ParameterizedTest
@ValueSource(strings = {"v2"})
void identityMapBatchBothEmailAndHashEmpty(String apiVersion, Vertx vertx, VertxTestContext testContext) {
@@ -2253,7 +2294,7 @@ void identityMapBatchBothEmailAndHashSpecified(String apiVersion, Vertx vertx, V
req.put("email_hash", emailHashes);
emails.add("test1@uid2.com");
- emailHashes.add(TokenUtils.getIdentityHashString("test2@uid2.com"));
+ emailHashes.add(TokenUtils.getHashedDiiString("test2@uid2.com"));
send(apiVersion, vertx, apiVersion + "/identity/map", false, null, req, 400, respJson -> {
assertFalse(respJson.containsKey("body"));
@@ -2392,8 +2433,8 @@ void identityMapBatchEmailHashes(String apiVersion, Vertx vertx, VertxTestContex
JsonArray hashes = new JsonArray();
req.put("email_hash", hashes);
final String[] email_hashes = {
- TokenUtils.getIdentityHashString("test1@uid2.com"),
- TokenUtils.getIdentityHashString("test2@uid2.com"),
+ TokenUtils.getHashedDiiString("test1@uid2.com"),
+ TokenUtils.getHashedDiiString("test2@uid2.com"),
};
for (String email_hash : email_hashes) {
@@ -2510,9 +2551,9 @@ void optOutStatusRequest(Map optedOutIds, int optedOutCount, Role
assertEquals(optedOutCount, optOutJsonArray.size());
for (int i = 0; i < optOutJsonArray.size(); ++i) {
JsonObject optOutObject = optOutJsonArray.getJsonObject(i);
- String advertisingId = optOutObject.getString("advertising_id");
- assertTrue(optedOutIds.containsKey(advertisingId));
- long expectedTimestamp = Instant.ofEpochSecond(optedOutIds.get(advertisingId)).toEpochMilli();
+ String rawUid = optOutObject.getString("advertising_id");
+ assertTrue(optedOutIds.containsKey(rawUid));
+ long expectedTimestamp = Instant.ofEpochSecond(optedOutIds.get(rawUid)).toEpochMilli();
assertEquals(expectedTimestamp, optOutObject.getLong("opted_out_since"));
}
testContext.completeNow();
@@ -2623,7 +2664,7 @@ void LogoutV2SaltsExpired(Vertx vertx, VertxTestContext testContext) {
void tokenGenerateBothPhoneAndHashSpecified(String apiVersion, Vertx vertx, VertxTestContext testContext) {
final int clientSiteId = 201;
final String phone = "+15555555555";
- final String phoneHash = TokenUtils.getIdentityHashString(phone);
+ final String phoneHash = TokenUtils.getHashedDiiString(phone);
fakeAuth(clientSiteId, Role.GENERATOR);
setupSalts();
setupKeys();
@@ -2669,9 +2710,9 @@ void tokenGenerateBothPhoneAndEmailSpecified(String apiVersion, Vertx vertx, Ver
void tokenGenerateBothPhoneHashAndEmailHashSpecified(String apiVersion, Vertx vertx, VertxTestContext testContext) {
final int clientSiteId = 201;
final String phone = "+15555555555";
- final String phoneHash = TokenUtils.getIdentityHashString(phone);
+ final String phoneHash = TokenUtils.getHashedDiiString(phone);
final String emailAddress = "test@uid2.com";
- final String emailHash = TokenUtils.getIdentityHashString(emailAddress);
+ final String emailHash = TokenUtils.getHashedDiiString(emailAddress);
fakeAuth(clientSiteId, Role.GENERATOR);
setupSalts();
setupKeys();
@@ -2708,31 +2749,37 @@ void tokenGenerateForPhone(String apiVersion, Vertx vertx, VertxTestContext test
assertNotNull(body);
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, body, IdentityType.Phone);
-
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenGenerated());
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenOptedOut());
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
- assertArrayEquals(getAdvertisingIdFromIdentity(IdentityType.Phone, phone, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingToken.userIdentity.id);
-
- RefreshToken refreshToken = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"), IdentityType.Phone);
- assertEquals(clientSiteId, refreshToken.publisherIdentity.siteId);
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentity(phone, firstLevelSalt), refreshToken.userIdentity.id);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, body, DiiType.Phone);
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"), DiiType.Phone);
- assertEqualsClose(now.plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("identity_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("refresh_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshIdentityAfter.toMillis()), Instant.ofEpochMilli(body.getLong("refresh_from")), 10);
+ assertAdvertisingTokenRefreshTokenRequests(advertisingTokenRequest, tokenRefreshRequest, clientSiteId,
+ getRawUidFromRawDii(DiiType.Phone, phone, firstLevelSalt, rotatingSalt123.currentSalt()),
+ PrivacyBits.DEFAULT,
+ body,
+ TokenUtils.getFirstLevelHashFromRawDii(phone, firstLevelSalt));
testContext.completeNow();
});
}
+ void verifyFirstLevelHashIdentityAndEstablishedAt(byte[] expectedFirstLevelHash,
+ TokenRefreshRequest tokenRefreshRequest,
+ JsonObject receivedJsonBody,
+ Instant expectedEstablishedTime) {
+
+ assertArrayEquals(expectedFirstLevelHash, tokenRefreshRequest.firstLevelHash.firstLevelHash());
+ assertEquals(expectedEstablishedTime, tokenRefreshRequest.firstLevelHash.establishedAt());
+ assertTrue(tokenRefreshRequest.firstLevelHash.establishedAt().toEpochMilli() < receivedJsonBody.getLong("identity_expires") );
+ assertTrue(tokenRefreshRequest.firstLevelHash.establishedAt().toEpochMilli() < receivedJsonBody.getLong("refresh_expires") );
+ assertTrue(tokenRefreshRequest.firstLevelHash.establishedAt().toEpochMilli() < receivedJsonBody.getLong("refresh_from") );
+ }
+
@ParameterizedTest
@ValueSource(strings = {"v2"})
void tokenGenerateForPhoneHash(String apiVersion, Vertx vertx, VertxTestContext testContext) {
final int clientSiteId = 201;
final String phone = "+15555555555";
- final String phoneHash = TokenUtils.getIdentityHashString(phone);
+ final String phoneHash = TokenUtils.getHashedDiiString(phone);
fakeAuth(clientSiteId, Role.GENERATOR);
setupSalts();
setupKeys();
@@ -2747,20 +2794,15 @@ void tokenGenerateForPhoneHash(String apiVersion, Vertx vertx, VertxTestContext
assertNotNull(body);
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, body, IdentityType.Phone);
-
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenGenerated());
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenOptedOut());
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
- assertArrayEquals(getAdvertisingIdFromIdentity(IdentityType.Phone, phone, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingToken.userIdentity.id);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, body, DiiType.Phone);
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"), DiiType.Phone);
- RefreshToken refreshToken = decodeRefreshToken(encoder, body.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"), IdentityType.Phone);
- assertEquals(clientSiteId, refreshToken.publisherIdentity.siteId);
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentity(phone, firstLevelSalt), refreshToken.userIdentity.id);
+ assertAdvertisingTokenRefreshTokenRequests(advertisingTokenRequest, tokenRefreshRequest, clientSiteId,
+ getRawUidFromRawDii(DiiType.Phone, phone, firstLevelSalt, rotatingSalt123.currentSalt()),
+ PrivacyBits.DEFAULT,
+ body,
+ TokenUtils.getFirstLevelHashFromRawDii(phone, firstLevelSalt));
- assertEqualsClose(now.plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("identity_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(body.getLong("refresh_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshIdentityAfter.toMillis()), Instant.ofEpochMilli(body.getLong("refresh_from")), 10);
testContext.completeNow();
});
@@ -2780,33 +2822,72 @@ void tokenGenerateThenRefreshForPhone(String apiVersion, Vertx vertx, VertxTestC
JsonObject bodyJson = genRespJson.getJsonObject("body");
assertNotNull(bodyJson);
+ EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
+ AdvertisingTokenRequest firstAdvertisingTokenRequest = validateAndGetToken(encoder, bodyJson,
+ DiiType.Phone);
String genRefreshToken = bodyJson.getString("refresh_token");
+ TokenRefreshRequest firstTokenRefreshRequest = decodeRefreshToken(encoder, bodyJson.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token"), DiiType.Phone);
when(this.optOutStore.getLatestEntry(any())).thenReturn(null);
+ byte[] expectedRawUidIdentity = getRawUidFromRawDii(DiiType.Phone, phone, firstLevelSalt, rotatingSalt123.currentSalt());
+ byte[] expectedFirstLevelHashIdentity = TokenUtils.getFirstLevelHashFromRawDii(phone, firstLevelSalt);
+
+ assertAdvertisingTokenRefreshTokenRequests(firstAdvertisingTokenRequest, firstTokenRefreshRequest, clientSiteId,
+ expectedRawUidIdentity,
+ PrivacyBits.DEFAULT,
+ bodyJson,
+ expectedFirstLevelHashIdentity);
+
sendTokenRefresh(apiVersion, vertx, testContext, genRefreshToken, bodyJson.getString("refresh_response_key"), 200, refreshRespJson ->
{
assertEquals("success", refreshRespJson.getString("status"));
JsonObject refreshBody = refreshRespJson.getJsonObject("body");
assertNotNull(refreshBody);
- EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
-
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, refreshBody, IdentityType.Phone);
-
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenGenerated());
- assertFalse(PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits).isClientSideTokenOptedOut());
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
- assertArrayEquals(getAdvertisingIdFromIdentity(IdentityType.Phone, phone, firstLevelSalt, rotatingSalt123.currentSalt()), advertisingToken.userIdentity.id);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, refreshBody, DiiType.Phone);
String refreshTokenStringNew = refreshBody.getString(apiVersion.equals("v2") ? "decrypted_refresh_token" : "refresh_token");
assertNotEquals(genRefreshToken, refreshTokenStringNew);
- RefreshToken refreshToken = decodeRefreshToken(encoder, refreshTokenStringNew, IdentityType.Phone);
- assertEquals(clientSiteId, refreshToken.publisherIdentity.siteId);
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentity(phone, firstLevelSalt), refreshToken.userIdentity.id);
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, refreshTokenStringNew, DiiType.Phone);
- assertEqualsClose(now.plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("identity_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("refresh_expires")), 10);
- assertEqualsClose(now.plusMillis(refreshIdentityAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("refresh_from")), 10);
+ // assert if the ad/refresh tokens from original token/generate is same as the ad/refresh tokens from token/refresh
+ assertAdvertisingTokenRefreshTokenRequests(
+ advertisingTokenRequest,
+ firstTokenRefreshRequest,
+ clientSiteId,
+ expectedRawUidIdentity,
+ PrivacyBits.DEFAULT,
+ bodyJson,
+ expectedFirstLevelHashIdentity);
+ assertAdvertisingTokenRefreshTokenRequests(
+ firstAdvertisingTokenRequest,
+ tokenRefreshRequest,
+ clientSiteId,
+ expectedRawUidIdentity,
+ PrivacyBits.DEFAULT,
+ bodyJson,
+ expectedFirstLevelHashIdentity);
+ assertAdvertisingTokenRefreshTokenRequests(
+ advertisingTokenRequest,
+ tokenRefreshRequest,
+ clientSiteId,
+ expectedRawUidIdentity,
+ PrivacyBits.DEFAULT,
+ bodyJson,
+ expectedFirstLevelHashIdentity);
+
+ assertTokenStatusMetrics(
+ clientSiteId,
+ TokenResponseStatsCollector.Endpoint.GenerateV2,
+ TokenResponseStatsCollector.ResponseStatus.Success,
+ //didn't set any specific header
+ TokenResponseStatsCollector.PlatformType.Other);
+ assertTokenStatusMetrics(
+ clientSiteId,
+ TokenResponseStatsCollector.Endpoint.RefreshV2,
+ TokenResponseStatsCollector.ResponseStatus.Success,
+ //didn't set any specific header
+ TokenResponseStatsCollector.PlatformType.Other);
testContext.completeNow();
});
@@ -2974,7 +3055,7 @@ void identityMapBatchBothPhoneAndHashSpecified(String apiVersion, Vertx vertx, V
req.put("phone_hash", phoneHashes);
phones.add("+15555555555");
- phoneHashes.add(TokenUtils.getIdentityHashString("+15555555555"));
+ phoneHashes.add(TokenUtils.getHashedDiiString("+15555555555"));
send(apiVersion, vertx, apiVersion + "/identity/map", false, null, req, 400, respJson -> {
assertFalse(respJson.containsKey("body"));
@@ -3016,8 +3097,8 @@ void identityMapBatchPhoneHashes(String apiVersion, Vertx vertx, VertxTestContex
JsonArray hashes = new JsonArray();
req.put("phone_hash", hashes);
final String[] email_hashes = {
- TokenUtils.getIdentityHashString("+15555555555"),
- TokenUtils.getIdentityHashString("+15555555556"),
+ TokenUtils.getHashedDiiString("+15555555555"),
+ TokenUtils.getHashedDiiString("+15555555556"),
};
for (String email_hash : email_hashes) {
@@ -3098,7 +3179,7 @@ void tokenGenerateRespectOptOutOption(String policyParameterKey, Vertx vertx, Ve
setupKeys();
// the clock value shouldn't matter here
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(now.minus(1, ChronoUnit.HOURS));
JsonObject req = new JsonObject();
@@ -3129,7 +3210,7 @@ void identityMapDefaultOption(String apiVersion, Vertx vertx, VertxTestContext t
setupKeys();
// the clock value shouldn't matter here
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(now.minus(1, ChronoUnit.HOURS));
JsonObject req = new JsonObject();
@@ -3168,7 +3249,7 @@ void identityMapRespectOptOutOption(String apiVersion, String policyParameterKey
setupKeys();
// the clock value shouldn't matter here
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(now.minus(1, ChronoUnit.HOURS));
JsonObject req = new JsonObject();
@@ -3271,9 +3352,9 @@ private void setupCstgBackend(List domainNames, List appNames)
when(siteProvider.getSite(clientSideTokenGenerateSiteId)).thenReturn(site);
}
- //if no identity is provided will get an error
+ //if no hashed dii is provided will get an error
@Test
- void cstgNoIdentityHashProvided(Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
+ void cstgNoHashedDiiProvided(Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
setupCstgBackend("cstg.co.uk");
Tuple.Tuple2 data = createClientSideTokenGenerateRequestWithNoPayload(Instant.now().toEpochMilli());
sendCstg(vertx,
@@ -3303,7 +3384,7 @@ void cstgNoIdentityHashProvided(Vertx vertx, VertxTestContext testContext) throw
})
void cstgDomainNameCheckFails(String httpOrigin, Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
setupCstgBackend();
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
sendCstg(vertx,
"v2/token/client-generate",
httpOrigin,
@@ -3332,7 +3413,7 @@ void cstgDomainNameCheckFails(String httpOrigin, Vertx vertx, VertxTestContext t
})
void cstgAppNameCheckFails(String appName, Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
setupCstgBackend(Collections.emptyList(), List.of("com.123.Game.App.android"));
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli(), appName);
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli(), appName);
sendCstg(vertx,
"v2/token/client-generate",
null,
@@ -3367,7 +3448,7 @@ void cstgDomainNameCheckFailsAndLogInvalidHttpOrigin(String httpOrigin, Vertx ve
this.uidOperatorVerticle.setLastInvalidOriginProcessTime(Instant.now().minusSeconds(3600));
setupCstgBackend();
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
sendCstg(vertx,
"v2/token/client-generate",
httpOrigin,
@@ -3398,7 +3479,7 @@ void cstgLogsInvalidAppName(String appName, Vertx vertx, VertxTestContext testCo
this.uidOperatorVerticle.setLastInvalidOriginProcessTime(Instant.now().minusSeconds(3600));
setupCstgBackend();
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli(), appName);
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli(), appName);
sendCstg(vertx,
"v2/token/client-generate",
null,
@@ -3444,7 +3525,7 @@ void cstgDisabledAsUnauthorized(Vertx vertx, VertxTestContext testContext) throw
requestJson.put("timestamp", timestamp);
requestJson.put("subscription_id", subscriptionID);
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli(), null);
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli(), null);
sendCstg(vertx,
"v2/token/client-generate",
null,
@@ -3482,7 +3563,7 @@ void cstgDomainNameCheckFailsAndLogSeveralInvalidHttpOrigin(String httpOrigin, V
setupCstgBackend();
when(siteProvider.getSite(124)).thenReturn(new Site(124, "test2", true, new HashSet<>()));
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
sendCstg(vertx,
"v2/token/client-generate",
httpOrigin,
@@ -3512,7 +3593,7 @@ void cstgDomainNameCheckFailsAndLogSeveralInvalidHttpOrigin(String httpOrigin, V
})
void cstgDomainNameCheckPasses(String httpOrigin, Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
setupCstgBackend("cstg.co.uk", "cstg2.com", "localhost");
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
sendCstg(vertx,
"v2/token/client-generate",
httpOrigin,
@@ -3526,7 +3607,7 @@ void cstgDomainNameCheckPasses(String httpOrigin, Vertx vertx, VertxTestContext
JsonObject refreshBody = respJson.getJsonObject("body");
assertNotNull(refreshBody);
var encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- validateAndGetToken(encoder, refreshBody, IdentityType.Email); //to validate token version is correct
+ validateAndGetToken(encoder, refreshBody, DiiType.Email); //to validate token version is correct
testContext.completeNow();
});
}
@@ -3539,7 +3620,7 @@ void cstgDomainNameCheckPasses(String httpOrigin, Vertx vertx, VertxTestContext
})
void cstgAppNameCheckPasses(String appName, Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
setupCstgBackend(Collections.emptyList(), List.of("com.123.Game.App.android", "123456789"));
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli(), appName);
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli(), appName);
sendCstg(vertx,
"v2/token/client-generate",
null,
@@ -3553,7 +3634,7 @@ void cstgAppNameCheckPasses(String appName, Vertx vertx, VertxTestContext testCo
JsonObject refreshBody = respJson.getJsonObject("body");
assertNotNull(refreshBody);
var encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- validateAndGetToken(encoder, refreshBody, IdentityType.Email); //to validate token version is correct
+ validateAndGetToken(encoder, refreshBody, DiiType.Email); //to validate token version is correct
assertTokenStatusMetrics(
clientSideTokenGenerateSiteId,
TokenResponseStatsCollector.Endpoint.ClientSideTokenGenerateV2,
@@ -4036,18 +4117,18 @@ private Tuple.Tuple2 createClientSideTokenGenerateRequest
return new Tuple.Tuple2<>(requestJson, secretKey);
}
- private Tuple.Tuple2 createClientSideTokenGenerateRequest(IdentityType identityType, String rawId, long timestamp) throws NoSuchAlgorithmException, InvalidKeyException {
- return createClientSideTokenGenerateRequest(identityType, rawId, timestamp, null);
+ private Tuple.Tuple2 createClientSideTokenGenerateRequest(DiiType diiType, String rawId, long timestamp) throws NoSuchAlgorithmException, InvalidKeyException {
+ return createClientSideTokenGenerateRequest(diiType, rawId, timestamp, null);
}
- private Tuple.Tuple2 createClientSideTokenGenerateRequest(IdentityType identityType, String rawId, long timestamp, String appName) throws NoSuchAlgorithmException, InvalidKeyException {
+ private Tuple.Tuple2 createClientSideTokenGenerateRequest(DiiType diiType, String rawId, long timestamp, String appName) throws NoSuchAlgorithmException, InvalidKeyException {
JsonObject identity = new JsonObject();
- if(identityType == IdentityType.Email) {
+ if(diiType == DiiType.Email) {
identity.put("email_hash", getSha256(rawId));
}
- else if(identityType == IdentityType.Phone) {
+ else if(diiType == DiiType.Phone) {
identity.put("phone_hash", getSha256(rawId));
}
else { //can't be other types
@@ -4068,17 +4149,17 @@ private Tuple.Tuple2 createClientSideTokenGenerateRequest
"test@example.com,Email",
"+61400000000,Phone"
})
- void cstgUserOptsOutAfterTokenGenerate(String id, IdentityType identityType, Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
+ void cstgUserOptsOutAfterTokenGenerate(String id, DiiType diiType, Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
setupCstgBackend("cstg.co.uk");
- final Tuple.Tuple2 data = createClientSideTokenGenerateRequest(identityType, id, Instant.now().toEpochMilli());
+ final Tuple.Tuple2 data = createClientSideTokenGenerateRequest(diiType, id, Instant.now().toEpochMilli());
// When we generate the token the user hasn't opted out.
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(null);
final EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(UserIdentity.class);
+ final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(FirstLevelHash.class);
sendCstg(vertx,
"v2/token/client-generate",
@@ -4089,19 +4170,20 @@ void cstgUserOptsOutAfterTokenGenerate(String id, IdentityType identityType, Ver
testContext,
response -> {
verify(optOutStore, times(1)).getLatestEntry(argumentCaptor.capture());
- assertArrayEquals(TokenUtils.getFirstLevelHashFromIdentity(id, firstLevelSalt), argumentCaptor.getValue().id);
+ assertArrayEquals(TokenUtils.getFirstLevelHashFromRawDii(id, firstLevelSalt),
+ argumentCaptor.getValue().firstLevelHash());
assertEquals("success", response.getString("status"));
final JsonObject genBody = response.getJsonObject("body");
- final AdvertisingToken advertisingToken = validateAndGetToken(encoder, genBody, identityType);
- final RefreshToken refreshToken = decodeRefreshToken(encoder, decodeV2RefreshToken(response), identityType);
+ final AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, genBody, diiType);
+ final TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, decodeV2RefreshToken(response), diiType);
- assertAreClientSideGeneratedTokens(advertisingToken, refreshToken, clientSideTokenGenerateSiteId, identityType, id);
+ assertAreClientSideGeneratedTokens(advertisingTokenRequest, tokenRefreshRequest, clientSideTokenGenerateSiteId, diiType, id);
// When we refresh the token the user has opted out.
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
- .thenReturn(advertisingToken.userIdentity.establishedAt.plusSeconds(1));
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
+ .thenReturn(advertisingTokenRequest.establishedAt.plusSeconds(1));
sendTokenRefresh("v2", vertx, testContext, genBody.getString("refresh_token"), genBody.getString("refresh_response_key"), 200, refreshRespJson -> {
assertEquals("optout", refreshRespJson.getString("status"));
@@ -4121,19 +4203,19 @@ void cstgUserOptsOutAfterTokenGenerate(String id, IdentityType identityType, Ver
"false,abc@abc.com,Email",
"false,+61400000000,Phone",
})
- void cstgSuccessForBothOptedAndNonOptedOutTest(boolean optOutExpected, String id, IdentityType identityType,
+ void cstgSuccessForBothOptedAndNonOptedOutTest(boolean optOutExpected, String id, DiiType diiType,
Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
setupCstgBackend("cstg.co.uk");
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(identityType, id, Instant.now().toEpochMilli());
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(diiType, id, Instant.now().toEpochMilli());
if(optOutExpected)
{
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(Instant.now().minus(1, ChronoUnit.HOURS));
}
else { //not expectedOptedOut
- when(optOutStore.getLatestEntry(any(UserIdentity.class)))
+ when(optOutStore.getLatestEntry(any(FirstLevelHash.class)))
.thenReturn(null);
}
@@ -4158,11 +4240,26 @@ void cstgSuccessForBothOptedAndNonOptedOutTest(boolean optOutExpected, String id
decodeV2RefreshToken(respJson);
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, genBody, identityType);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, genBody, diiType);
+
+ TokenRefreshRequest tokenRefreshRequest = decodeRefreshToken(encoder, genBody.getString("decrypted_refresh_token"), diiType);
+
- RefreshToken refreshToken = decodeRefreshToken(encoder, genBody.getString("decrypted_refresh_token"), identityType);
- assertAreClientSideGeneratedTokens(advertisingToken, refreshToken, clientSideTokenGenerateSiteId, identityType, id);
+ byte[] expectedRawUidIdentity = getRawUidFromRawDii(diiType, id, firstLevelSalt, rotatingSalt123.currentSalt());
+ byte[] expectedFirstLevelHashIdentity = TokenUtils.getFirstLevelHashFromRawDii(id, firstLevelSalt);
+
+ PrivacyBits expectedPrivacyBits = new PrivacyBits();
+ expectedPrivacyBits.setLegacyBit();
+ expectedPrivacyBits.setClientSideTokenGenerate();
+
+ assertAdvertisingTokenRefreshTokenRequests(advertisingTokenRequest, tokenRefreshRequest,
+ clientSideTokenGenerateSiteId,
+ expectedRawUidIdentity,
+ expectedPrivacyBits,
+ genBody,
+ expectedFirstLevelHashIdentity);
+ assertAreClientSideGeneratedTokens(advertisingTokenRequest, tokenRefreshRequest, clientSideTokenGenerateSiteId, diiType, id);
assertEqualsClose(Instant.now().plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(genBody.getLong("identity_expires")), 10);
assertEqualsClose(Instant.now().plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(genBody.getLong("refresh_expires")), 10);
assertEqualsClose(Instant.now().plusMillis(refreshIdentityAfter.toMillis()), Instant.ofEpochMilli(genBody.getLong("refresh_from")), 10);
@@ -4183,13 +4280,19 @@ void cstgSuccessForBothOptedAndNonOptedOutTest(boolean optOutExpected, String id
EncryptedTokenEncoder encoder2 = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
//make sure the new advertising token from refresh looks right
- AdvertisingToken adTokenFromRefresh = validateAndGetToken(encoder2, refreshBody, identityType);
+ AdvertisingTokenRequest adTokenFromRefresh = validateAndGetToken(encoder2, refreshBody, diiType);
String refreshTokenStringNew = refreshBody.getString("decrypted_refresh_token");
assertNotEquals(genRefreshToken, refreshTokenStringNew);
- RefreshToken refreshTokenAfterRefresh = decodeRefreshToken(encoder, refreshTokenStringNew, identityType);
+ TokenRefreshRequest refreshTokenAfterRefreshSource = decodeRefreshToken(encoder, refreshTokenStringNew, diiType);
- assertAreClientSideGeneratedTokens(adTokenFromRefresh, refreshTokenAfterRefresh, clientSideTokenGenerateSiteId, identityType, id);
+ assertAdvertisingTokenRefreshTokenRequests(adTokenFromRefresh, refreshTokenAfterRefreshSource,
+ clientSideTokenGenerateSiteId,
+ expectedRawUidIdentity,
+ expectedPrivacyBits,
+ genBody,
+ expectedFirstLevelHashIdentity);
+ assertAreClientSideGeneratedTokens(adTokenFromRefresh, refreshTokenAfterRefreshSource, clientSideTokenGenerateSiteId, diiType, id);
assertEqualsClose(Instant.now().plusMillis(identityExpiresAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("identity_expires")), 10);
assertEqualsClose(Instant.now().plusMillis(refreshExpiresAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("refresh_expires")), 10);
assertEqualsClose(Instant.now().plusMillis(refreshIdentityAfter.toMillis()), Instant.ofEpochMilli(refreshBody.getLong("refresh_from")), 10);
@@ -4214,7 +4317,7 @@ void cstgSuccessForBothOptedAndNonOptedOutTest(boolean optOutExpected, String id
void cstgSaltsExpired(String httpOrigin, Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
when(saltProviderSnapshot.getExpires()).thenReturn(Instant.now().minus(1, ChronoUnit.HOURS));
setupCstgBackend("cstg.co.uk", "cstg2.com", "localhost");
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
sendCstg(vertx,
"v2/token/client-generate",
httpOrigin,
@@ -4228,7 +4331,7 @@ void cstgSaltsExpired(String httpOrigin, Vertx vertx, VertxTestContext testConte
JsonObject refreshBody = respJson.getJsonObject("body");
assertNotNull(refreshBody);
var encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- validateAndGetToken(encoder, refreshBody, IdentityType.Email); //to validate token version is correct
+ validateAndGetToken(encoder, refreshBody, DiiType.Email); //to validate token version is correct
verify(shutdownHandler, atLeastOnce()).handleSaltRetrievalResponse(true);
@@ -4240,7 +4343,7 @@ void cstgSaltsExpired(String httpOrigin, Vertx vertx, VertxTestContext testConte
void cstgNoActiveKey(Vertx vertx, VertxTestContext testContext) throws NoSuchAlgorithmException, InvalidKeyException {
setupCstgBackend("cstg.co.uk");
setupKeys(true);
- Tuple.Tuple2 data = createClientSideTokenGenerateRequest(IdentityType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
+ Tuple.Tuple2 data = createClientSideTokenGenerateRequest(DiiType.Email, "random@unifiedid.com", Instant.now().toEpochMilli());
sendCstg(vertx,
"v2/token/client-generate",
"http://cstg.co.uk",
@@ -4283,25 +4386,25 @@ void cstgInvalidInput(String identityType, String rawUID, Vertx vertx, VertxTest
});
}
- private void assertAreClientSideGeneratedTokens(AdvertisingToken advertisingToken, RefreshToken refreshToken, int siteId, IdentityType identityType, String identity) {
- assertAreClientSideGeneratedTokens(advertisingToken,
- refreshToken,
+ private void assertAreClientSideGeneratedTokens(AdvertisingTokenRequest advertisingTokenRequest, TokenRefreshRequest tokenRefreshRequest, int siteId, DiiType diiType, String identity) {
+ assertAreClientSideGeneratedTokens(advertisingTokenRequest,
+ tokenRefreshRequest,
siteId,
- identityType,
+ diiType,
identity,
false);
}
- private void assertAreClientSideGeneratedTokens(AdvertisingToken advertisingToken, RefreshToken refreshToken, int siteId, IdentityType identityType, String identity, boolean expectedOptOut) {
- final PrivacyBits advertisingTokenPrivacyBits = PrivacyBits.fromInt(advertisingToken.userIdentity.privacyBits);
- final PrivacyBits refreshTokenPrivacyBits = PrivacyBits.fromInt(refreshToken.userIdentity.privacyBits);
+ private void assertAreClientSideGeneratedTokens(AdvertisingTokenRequest advertisingTokenRequest, TokenRefreshRequest tokenRefreshRequest, int siteId, DiiType diiType, String identity, boolean expectedOptOut) {
+ final PrivacyBits advertisingTokenPrivacyBits = advertisingTokenRequest.privacyBits;
+ final PrivacyBits refreshTokenPrivacyBits = tokenRefreshRequest.privacyBits;
- final byte[] advertisingId = getAdvertisingIdFromIdentity(identityType,
+ final byte[] rawUid = getRawUidFromRawDii(diiType,
identity,
firstLevelSalt,
rotatingSalt123.currentSalt());
- final byte[] firstLevelHash = TokenUtils.getFirstLevelHashFromIdentity(identity, firstLevelSalt);
+ final byte[] firstLevelHash = TokenUtils.getFirstLevelHashFromRawDii(identity, firstLevelSalt);
assertAll(
() -> assertTrue(advertisingTokenPrivacyBits.isClientSideTokenGenerated(), "Advertising token privacy bits CSTG flag is incorrect"),
@@ -4310,11 +4413,11 @@ private void assertAreClientSideGeneratedTokens(AdvertisingToken advertisingToke
() -> assertTrue(refreshTokenPrivacyBits.isClientSideTokenGenerated(), "Refresh token privacy bits CSTG flag is incorrect"),
() -> assertEquals(expectedOptOut, refreshTokenPrivacyBits.isClientSideTokenOptedOut(), "Refresh token privacy bits CSTG optout flag is incorrect"),
- () -> assertEquals(siteId, advertisingToken.publisherIdentity.siteId, "Advertising token site ID is incorrect"),
- () -> assertEquals(siteId, refreshToken.publisherIdentity.siteId, "Refresh token site ID is incorrect"),
+ () -> assertEquals(siteId, advertisingTokenRequest.sourcePublisher.siteId, "Advertising token site ID is incorrect"),
+ () -> assertEquals(siteId, tokenRefreshRequest.sourcePublisher.siteId, "Refresh token site ID is incorrect"),
- () -> assertArrayEquals(advertisingId, advertisingToken.userIdentity.id, "Advertising token ID is incorrect"),
- () -> assertArrayEquals(firstLevelHash, refreshToken.userIdentity.id, "Refresh token ID is incorrect")
+ () -> assertArrayEquals(rawUid, advertisingTokenRequest.rawUid.rawUid(), "Advertising token ID is incorrect"),
+ () -> assertArrayEquals(firstLevelHash, tokenRefreshRequest.firstLevelHash.firstLevelHash(), "Refresh token ID is incorrect")
);
}
@@ -4488,7 +4591,7 @@ void getActiveKeyTest() {
@ValueSource(strings = {"MultiKeysets", "AddKey", "RotateKey", "DisableActiveKey", "DisableDefaultKeyset"})
void tokenGenerateRotatingKeysets_GENERATOR(String testRun, Vertx vertx, VertxTestContext testContext) {
final int clientSiteId = 101;
- final String emailHash = TokenUtils.getIdentityHashString("test@uid2.com");
+ final String emailHash = TokenUtils.getHashedDiiString("test@uid2.com");
fakeAuth(clientSiteId, Role.GENERATOR);
MultipleKeysetsTests test = new MultipleKeysetsTests();
//To read these tests, open the MultipleKeysetsTests() constructor in another window so you can see the keyset contents and validate expectations
@@ -4544,16 +4647,16 @@ void tokenGenerateRotatingKeysets_GENERATOR(String testRun, Vertx vertx, VertxTe
assertNotNull(body);
EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(new KeyManager(keysetKeyStore, keysetProvider));
- AdvertisingToken advertisingToken = validateAndGetToken(encoder, body, IdentityType.Email);
- assertEquals(clientSiteId, advertisingToken.publisherIdentity.siteId);
+ AdvertisingTokenRequest advertisingTokenRequest = validateAndGetToken(encoder, body, DiiType.Email);
+ assertEquals(clientSiteId, advertisingTokenRequest.sourcePublisher.siteId);
//Uses a key from default keyset
int clientKeyId;
- if (advertisingToken.version == TokenVersion.V3 || advertisingToken.version == TokenVersion.V4) {
+ if (advertisingTokenRequest.version == TokenVersion.V3 || advertisingTokenRequest.version == TokenVersion.V4) {
String advertisingTokenString = body.getString("advertising_token");
byte[] bytes = null;
- if (advertisingToken.version == TokenVersion.V3) {
+ if (advertisingTokenRequest.version == TokenVersion.V3) {
bytes = EncodingUtils.fromBase64(advertisingTokenString);
- } else if (advertisingToken.version == TokenVersion.V4) {
+ } else if (advertisingTokenRequest.version == TokenVersion.V4) {
bytes = Uid2Base64UrlCoder.decode(advertisingTokenString); //same as V3 but use Base64URL encoding
}
final Buffer b = Buffer.buffer(bytes);
@@ -4563,7 +4666,7 @@ void tokenGenerateRotatingKeysets_GENERATOR(String testRun, Vertx vertx, VertxTe
final Buffer masterPayload = Buffer.buffer(masterPayloadBytes);
clientKeyId = masterPayload.getInt(29);
} else {
- clientKeyId = advertisingToken.publisherIdentity.clientKeyId;
+ clientKeyId = advertisingTokenRequest.sourcePublisher.clientKeyId;
}
switch (testRun) {
case "MultiKeysets":
diff --git a/src/test/java/com/uid2/operator/V2RequestUtilTest.java b/src/test/java/com/uid2/operator/V2RequestUtilTest.java
index d08b52389..1522e8633 100644
--- a/src/test/java/com/uid2/operator/V2RequestUtilTest.java
+++ b/src/test/java/com/uid2/operator/V2RequestUtilTest.java
@@ -3,7 +3,7 @@
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
-import com.uid2.operator.model.IdentityScope;
+import com.uid2.operator.model.identities.IdentityScope;
import com.uid2.operator.model.KeyManager;
import com.uid2.operator.service.V2RequestUtil;
import com.uid2.shared.IClock;
diff --git a/src/test/java/com/uid2/operator/benchmark/BenchmarkCommon.java b/src/test/java/com/uid2/operator/benchmark/BenchmarkCommon.java
index 10565009b..29521cee7 100644
--- a/src/test/java/com/uid2/operator/benchmark/BenchmarkCommon.java
+++ b/src/test/java/com/uid2/operator/benchmark/BenchmarkCommon.java
@@ -3,6 +3,10 @@
import com.uid2.operator.Const;
import com.uid2.operator.Main;
import com.uid2.operator.model.*;
+import com.uid2.operator.model.identities.DiiType;
+import com.uid2.operator.model.identities.FirstLevelHash;
+import com.uid2.operator.model.identities.HashedDii;
+import com.uid2.operator.model.identities.IdentityScope;
import com.uid2.operator.service.EncryptedTokenEncoder;
import com.uid2.operator.service.IUIDOperatorService;
import com.uid2.operator.service.ShutdownService;
@@ -148,18 +152,17 @@ static ICloudStorage make1mOptOutEntryStorage(String salt, List out_gene
return storage;
}
- static UserIdentity[] createUserIdentities() {
- UserIdentity[] arr = new UserIdentity[65536];
+ static HashedDii[] createHashedDiiIdentities() {
+ HashedDii[] arr = new HashedDii[65536];
for (int i = 0; i < 65536; i++) {
- final byte[] id = new byte[33];
- new Random().nextBytes(id);
- arr[i] = new UserIdentity(IdentityScope.UID2, IdentityType.Email, id, 0,
- Instant.now().minusSeconds(120), Instant.now().minusSeconds(60));
+ final byte[] diiHash = new byte[33];
+ new Random().nextBytes(diiHash);
+ arr[i] = new HashedDii(IdentityScope.UID2, DiiType.Email, diiHash);
}
return arr;
}
- static PublisherIdentity createPublisherIdentity() throws Exception {
+ static SourcePublisher createSourcePublisher() throws Exception {
RotatingClientKeyProvider clients = new RotatingClientKeyProvider(
new EmbeddedResourceStorage(Main.class),
new GlobalScope(new CloudPath("/com.uid2.core/test/clients/metadata.json")));
@@ -167,7 +170,7 @@ static PublisherIdentity createPublisherIdentity() throws Exception {
for (ClientKey client : clients.getAll()) {
if (client.hasRole(Role.GENERATOR)) {
- return new PublisherIdentity(client.getSiteId(), 0, 0);
+ return new SourcePublisher(client.getSiteId());
}
}
throw new IllegalStateException("embedded resource does not include any publisher key");
@@ -187,14 +190,14 @@ public StaticOptOutStore(ICloudStorage storage, JsonObject jsonConfig, Collectio
}
@Override
- public Instant getLatestEntry(UserIdentity firstLevelHashIdentity) {
- long epochSecond = this.snapshot.getOptOutTimestamp(firstLevelHashIdentity.id);
+ public Instant getLatestEntry(FirstLevelHash firstLevelHash) {
+ long epochSecond = this.snapshot.getOptOutTimestamp(firstLevelHash.firstLevelHash());
Instant instant = epochSecond > 0 ? Instant.ofEpochSecond(epochSecond) : null;
return instant;
}
@Override
- public void addEntry(UserIdentity firstLevelHashIdentity, byte[] advertisingId, String uidTraceId, String uidInstanceId, Handler> handler) {
+ public void addEntry(FirstLevelHash firstLevelHash, byte[] advertisingId, String uidTraceId, String uidInstanceId, Handler> handler) {
// noop
}
diff --git a/src/test/java/com/uid2/operator/benchmark/IdentityMapBenchmark.java b/src/test/java/com/uid2/operator/benchmark/IdentityMapBenchmark.java
index 6bb7102d4..f4064244d 100644
--- a/src/test/java/com/uid2/operator/benchmark/IdentityMapBenchmark.java
+++ b/src/test/java/com/uid2/operator/benchmark/IdentityMapBenchmark.java
@@ -1,6 +1,7 @@
package com.uid2.operator.benchmark;
import com.uid2.operator.model.*;
+import com.uid2.operator.model.identities.HashedDii;
import com.uid2.operator.service.IUIDOperatorService;
import com.uid2.operator.service.V2RequestUtil;
import com.uid2.operator.vertx.V2PayloadHandler;
@@ -25,13 +26,13 @@
public class IdentityMapBenchmark {
private static final IUIDOperatorService uidService;
- private static final UserIdentity[] userIdentities;
+ private static final HashedDii[] hashedDiiIdentities;
private static int idx = 0;
static {
try {
uidService = BenchmarkCommon.createUidOperatorService();
- userIdentities = BenchmarkCommon.createUserIdentities();
+ hashedDiiIdentities = BenchmarkCommon.createHashedDiiIdentities();
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -120,14 +121,14 @@ private static byte[] createEncryptedPayload(int numRecords) {
@Benchmark
@BenchmarkMode(Mode.Throughput)
- public MappedIdentity IdentityMapRawThroughput() {
- return uidService.map(userIdentities[(idx++) & 65535], Instant.now());
+ public IdentityMapResponseItem IdentityMapRawThroughput() {
+ return uidService.map(hashedDiiIdentities[(idx++) & 65535], Instant.now());
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
- public MappedIdentity IdentityMapWithOptOutThroughput() {
- return uidService.mapIdentity(new MapRequest(userIdentities[(idx++) & 65535], OptoutCheckPolicy.RespectOptOut, Instant.now()));
+ public IdentityMapResponseItem IdentityMapWithOptOutThroughput() {
+ return uidService.mapHashedDii(new IdentityMapRequestItem(hashedDiiIdentities[(idx++) & 65535], OptoutCheckPolicy.RespectOptOut, Instant.now()));
}
@Benchmark
diff --git a/src/test/java/com/uid2/operator/benchmark/TokenEndecBenchmark.java b/src/test/java/com/uid2/operator/benchmark/TokenEndecBenchmark.java
index 1dfeeb95f..5a376ea95 100644
--- a/src/test/java/com/uid2/operator/benchmark/TokenEndecBenchmark.java
+++ b/src/test/java/com/uid2/operator/benchmark/TokenEndecBenchmark.java
@@ -1,6 +1,7 @@
package com.uid2.operator.benchmark;
import com.uid2.operator.model.*;
+import com.uid2.operator.model.identities.HashedDii;
import com.uid2.operator.service.EncryptedTokenEncoder;
import com.uid2.operator.service.IUIDOperatorService;
import org.openjdk.jmh.annotations.Benchmark;
@@ -14,20 +15,20 @@
public class TokenEndecBenchmark {
private static final IUIDOperatorService uidService;
- private static final UserIdentity[] userIdentities;
- private static final PublisherIdentity publisher;
+ private static final HashedDii[] hashedDiiIdentities;
+ private static final SourcePublisher publisher;
private static final EncryptedTokenEncoder encoder;
- private static final IdentityTokens[] generatedTokens;
+ private static final TokenGenerateResponse[] generatedTokens;
private static int idx = 0;
static {
try {
uidService = BenchmarkCommon.createUidOperatorService();
- userIdentities = BenchmarkCommon.createUserIdentities();
- publisher = BenchmarkCommon.createPublisherIdentity();
+ hashedDiiIdentities = BenchmarkCommon.createHashedDiiIdentities();
+ publisher = BenchmarkCommon.createSourcePublisher();
encoder = BenchmarkCommon.createTokenEncoder();
generatedTokens = createAdvertisingTokens();
- if (generatedTokens.length < 65536 || userIdentities.length < 65536) {
+ if (generatedTokens.length < 65536 || hashedDiiIdentities.length < 65536) {
throw new IllegalStateException("must create more than 65535 test candidates.");
}
} catch (Exception e) {
@@ -35,30 +36,29 @@ public class TokenEndecBenchmark {
}
}
- static IdentityTokens[] createAdvertisingTokens() {
- List tokens = new ArrayList<>();
- for (int i = 0; i < userIdentities.length; i++) {
+ static TokenGenerateResponse[] createAdvertisingTokens() {
+ List tokens = new ArrayList<>();
+ for (int i = 0; i < hashedDiiIdentities.length; i++) {
tokens.add(
- uidService.generateIdentity(
- new IdentityRequest(
- publisher,
- userIdentities[i],
- OptoutCheckPolicy.DoNotRespect),
+ uidService.generateIdentity(new TokenGenerateRequest(
+ publisher,
+ hashedDiiIdentities[i],
+ OptoutCheckPolicy.DoNotRespect),
Duration.ofSeconds(BenchmarkCommon.REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(BenchmarkCommon.REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(BenchmarkCommon.IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS))
);
}
- return tokens.toArray(new IdentityTokens[tokens.size()]);
+ return tokens.toArray(new TokenGenerateResponse[tokens.size()]);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
- public IdentityTokens TokenGenerationBenchmark() {
- return uidService.generateIdentity(new IdentityRequest(
- publisher,
- userIdentities[(idx++) & 65535],
- OptoutCheckPolicy.DoNotRespect),
+ public TokenGenerateResponse TokenGenerationBenchmark() {
+ return uidService.generateIdentity(new TokenGenerateRequest(
+ publisher,
+ hashedDiiIdentities[(idx++) & 65535],
+ OptoutCheckPolicy.DoNotRespect),
Duration.ofSeconds(BenchmarkCommon.REFRESH_IDENTITY_TOKEN_AFTER_SECONDS),
Duration.ofSeconds(BenchmarkCommon.REFRESH_TOKEN_EXPIRES_AFTER_SECONDS),
Duration.ofSeconds(BenchmarkCommon.IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS)
@@ -67,7 +67,7 @@ public IdentityTokens TokenGenerationBenchmark() {
@Benchmark
@BenchmarkMode(Mode.Throughput)
- public RefreshResponse TokenRefreshBenchmark() {
+ public TokenRefreshResponse TokenRefreshBenchmark() {
return uidService.refreshIdentity(
encoder.decodeRefreshToken(
generatedTokens[(idx++) & 65535].getRefreshToken()),
diff --git a/src/test/java/com/uid2/operator/utilTests/IdentityMapResponseItemTest.java b/src/test/java/com/uid2/operator/utilTests/IdentityMapResponseItemTest.java
new file mode 100644
index 000000000..e24539dc6
--- /dev/null
+++ b/src/test/java/com/uid2/operator/utilTests/IdentityMapResponseItemTest.java
@@ -0,0 +1,40 @@
+package com.uid2.operator.utilTests;
+
+import com.uid2.operator.model.IdentityMapResponseItem;
+import com.uid2.operator.service.EncodingUtils;
+import org.junit.Test;
+
+import java.security.NoSuchAlgorithmException;
+import java.time.Instant;
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+
+public class IdentityMapResponseItemTest {
+ @Test
+ public void doRawUidResponseTest() throws NoSuchAlgorithmException {
+ assertEquals(IdentityMapResponseItem.OptoutIdentity.bucketId, "");
+ assertTrue(IdentityMapResponseItem.OptoutIdentity.isOptedOut());
+
+ IdentityMapResponseItem optoutResponse = new IdentityMapResponseItem(new byte[33], null, null, null);
+ assertTrue(optoutResponse.isOptedOut());
+
+ byte[] rawUid = new byte[33];
+ for(int i = 0; i < 33; i++) {
+ rawUid[i] = (byte) i;
+ }
+
+ final Long expectedRefreshFrom = EncodingUtils.NowUTCMillis().toEpochMilli();
+ byte[] expectedPreviousRawUid = new byte[33];
+ for(int i = 0; i < 33; i++) {
+ rawUid[i] = (byte) 88;
+ }
+
+ IdentityMapResponseItem generatedUid = new IdentityMapResponseItem(rawUid, "12345", expectedPreviousRawUid, expectedRefreshFrom);
+ assertFalse(generatedUid.isOptedOut());
+ assertTrue(Arrays.equals(rawUid, generatedUid.rawUid));
+ assertArrayEquals(expectedPreviousRawUid, generatedUid.previousRawUid);
+ assertEquals(expectedRefreshFrom, generatedUid.refreshFrom);
+ }
+}
diff --git a/src/test/java/com/uid2/operator/utilTests/PrivacyBitsTest.java b/src/test/java/com/uid2/operator/utilTests/PrivacyBitsTest.java
new file mode 100644
index 000000000..91750bf87
--- /dev/null
+++ b/src/test/java/com/uid2/operator/utilTests/PrivacyBitsTest.java
@@ -0,0 +1,55 @@
+package com.uid2.operator.utilTests;
+
+import com.uid2.operator.util.PrivacyBits;
+import org.junit.Test;
+import java.security.NoSuchAlgorithmException;
+import static org.junit.Assert.*;
+
+
+public class PrivacyBitsTest {
+ @Test
+ public void doPrivacyBitsTest() throws NoSuchAlgorithmException {
+ assertEquals(PrivacyBits.DEFAULT.getAsInt(), 1);
+ PrivacyBits pb1 = new PrivacyBits();
+ assertEquals(pb1.getAsInt(), 0);
+ assertEquals(pb1.hashCode(), 0);
+ assertNotEquals(pb1, PrivacyBits.fromInt(1));
+ assertNotEquals(pb1, PrivacyBits.fromInt(121));
+ assertFalse(pb1.isClientSideTokenGenerated());
+ assertFalse(pb1.isClientSideTokenOptedOut());
+
+ pb1.setLegacyBit();
+ assertEquals(pb1.getAsInt(), 0b1);
+ assertEquals(pb1.hashCode(), 0b1);
+ assertEquals(pb1, PrivacyBits.fromInt(1));
+ assertNotEquals(pb1, PrivacyBits.fromInt(121));
+ assertFalse(pb1.isClientSideTokenGenerated());
+ assertFalse(pb1.isClientSideTokenOptedOut());
+
+
+ pb1.setClientSideTokenGenerate();
+ assertEquals(pb1.getAsInt(), 0b11);
+ assertEquals(pb1.hashCode(), 0b11);
+ assertEquals(pb1, PrivacyBits.fromInt(3));
+ assertNotEquals(pb1, PrivacyBits.fromInt(121));
+ assertTrue(pb1.isClientSideTokenGenerated());
+ assertFalse(pb1.isClientSideTokenOptedOut());
+
+
+ pb1.setClientSideTokenGenerateOptout();
+ assertEquals(pb1.getAsInt(), 0b111);
+ assertEquals(pb1.hashCode(), 0b111);
+ assertEquals(pb1, PrivacyBits.fromInt(7));
+ assertNotEquals(pb1, PrivacyBits.fromInt(121));
+ assertTrue(pb1.isClientSideTokenGenerated());
+ assertTrue(pb1.isClientSideTokenOptedOut());
+
+ PrivacyBits pb2 = new PrivacyBits(pb1);
+ assertEquals(pb2.getAsInt(), 0b111);
+
+ PrivacyBits pb3 = PrivacyBits.fromInt(0b10110);
+ assertEquals(pb3.getAsInt(), 0b10110);
+ pb3.setLegacyBit();
+ assertEquals(pb3.getAsInt(), 0b10111);
+ }
+}
diff --git a/src/test/java/com/uid2/operator/utilTests/TokenGenerateResponseTest.java b/src/test/java/com/uid2/operator/utilTests/TokenGenerateResponseTest.java
new file mode 100644
index 000000000..c94c83c5c
--- /dev/null
+++ b/src/test/java/com/uid2/operator/utilTests/TokenGenerateResponseTest.java
@@ -0,0 +1,45 @@
+package com.uid2.operator.utilTests;
+
+import com.uid2.operator.model.TokenGenerateResponse;
+import com.uid2.shared.model.TokenVersion;
+import io.vertx.core.json.JsonObject;
+import org.junit.Test;
+
+import java.security.NoSuchAlgorithmException;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+
+import static org.junit.Assert.*;
+
+
+public class TokenGenerateResponseTest {
+ @Test
+ public void doIdentityResponseTest() throws NoSuchAlgorithmException {
+ assertEquals(TokenGenerateResponse.OptOutResponse.getAdvertisingToken(), "");
+ assertTrue(TokenGenerateResponse.OptOutResponse.isOptedOut());
+
+ TokenGenerateResponse nullAdTokenValue = new TokenGenerateResponse(null, TokenVersion.V4, "refreshToken", null,null,null);
+ assertTrue(nullAdTokenValue.isOptedOut());
+
+ Instant identityExpires = Instant.now();
+ Instant refreshFrom = identityExpires.plus(5, ChronoUnit.MINUTES);
+ Instant refreshExpires = identityExpires.plus(10, ChronoUnit.MINUTES);
+
+
+
+ TokenGenerateResponse response1 = new TokenGenerateResponse("adToken", TokenVersion.V3, "refreshToken", identityExpires
+ , refreshExpires, refreshFrom);
+ assertEquals(response1.getAdvertisingToken(), "adToken");
+ assertEquals(response1.getAdvertisingTokenVersion(), TokenVersion.V3);
+ assertEquals(response1.getRefreshToken(), "refreshToken");
+ assertEquals(response1.getIdentityExpires(), identityExpires);
+ assertEquals(response1.getRefreshExpires(), refreshExpires);
+ assertEquals(response1.getRefreshFrom(), refreshFrom);
+
+ JsonObject jsonV1 = response1.toTokenGenerateResponseJson();
+ assertEquals(jsonV1.getString("advertising_token"), response1.getAdvertisingToken());
+ assertEquals(jsonV1.getString("refresh_token"), response1.getRefreshToken());
+ assertEquals(jsonV1.getLong("refresh_expires").longValue(), response1.getRefreshExpires().toEpochMilli());
+ assertEquals(jsonV1.getLong("refresh_from").longValue(), response1.getRefreshFrom().toEpochMilli());
+ }
+}