Skip to content

Commit 68a47ae

Browse files
authored
UID2-2112 Allow generating v4 tokens by percentage (fall back to default token version otherwise) (#289)
1 parent e0f67fb commit 68a47ae

File tree

4 files changed

+38
-56
lines changed

4 files changed

+38
-56
lines changed

conf/local-config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"refresh_token_expires_after_seconds": 86400,
1414
"refresh_identity_token_after_seconds": 900,
1515
"advertising_token_v3": false,
16-
"advertising_token_v4": false,
16+
"advertising_token_v4_percentage": 0,
1717
"refresh_token_v3": false,
1818
"identity_v3": false,
1919
"identity_scope": "uid2",
@@ -35,4 +35,4 @@
3535
"client_side_token_generate": true,
3636
"client_side_token_generate_domain_name_check_enabled": true,
3737
"key_sharing_endpoint_provide_site_domain_names": true
38-
}
38+
}

src/main/java/com/uid2/operator/service/UIDOperatorService.java

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package com.uid2.operator.service;
22

3-
import com.uid2.operator.IdentityConst;
43
import com.uid2.operator.model.*;
54
import com.uid2.operator.util.PrivacyBits;
6-
import com.uid2.operator.vertx.UIDOperatorVerticle;
75
import com.uid2.shared.model.SaltEntry;
86
import com.uid2.operator.store.IOptOutStore;
97
import com.uid2.shared.store.ISaltProvider;
@@ -22,6 +20,7 @@
2220
import java.time.ZoneOffset;
2321
import java.time.format.DateTimeFormatter;
2422
import java.util.*;
23+
import java.util.concurrent.ThreadLocalRandom;
2524

2625
import static com.uid2.operator.IdentityConst.*;
2726

@@ -48,7 +47,8 @@ public class UIDOperatorService implements IUIDOperatorService {
4847
private final Duration refreshIdentityAfter;
4948

5049
private final OperatorIdentity operatorIdentity;
51-
private final TokenVersion advertisingTokenVersion;
50+
private final TokenVersion tokenVersionToUseIfNotV4;
51+
private final int advertisingTokenV4Percentage;
5252
private final TokenVersion refreshTokenVersion;
5353
private final boolean identityV3Enabled;
5454

@@ -88,11 +88,9 @@ public UIDOperatorService(JsonObject config, IOptOutStore optOutStore, ISaltProv
8888
throw new IllegalStateException(REFRESH_TOKEN_EXPIRES_AFTER_SECONDS + " must be >= " + REFRESH_IDENTITY_TOKEN_AFTER_SECONDS);
8989
}
9090

91-
if (config.getBoolean("advertising_token_v4", false)) {
92-
this.advertisingTokenVersion = TokenVersion.V4;
93-
} else {
94-
this.advertisingTokenVersion = config.getBoolean("advertising_token_v3", false) ? TokenVersion.V3 : TokenVersion.V2;
95-
}
91+
this.advertisingTokenV4Percentage = config.getInteger("advertising_token_v4_percentage", 0); //0 indicates token v4 will not be used
92+
this.tokenVersionToUseIfNotV4 = config.getBoolean("advertising_token_v3", false) ? TokenVersion.V3 : TokenVersion.V2;
93+
9694
this.refreshTokenVersion = TokenVersion.V3;
9795
this.identityV3Enabled = config.getBoolean("identity_v3", false);
9896
}
@@ -295,16 +293,13 @@ private RefreshToken createRefreshToken(PublisherIdentity publisherIdentity, Use
295293
}
296294

297295
private AdvertisingToken createAdvertisingToken(PublisherIdentity publisherIdentity, UserIdentity userIdentity, Instant now) {
298-
return new AdvertisingToken(
299-
this.advertisingTokenVersion,
300-
now,
301-
now.plusMillis(identityExpiresAfter.toMillis()),
302-
this.operatorIdentity,
303-
publisherIdentity,
304-
userIdentity);
296+
int randomNum = ThreadLocalRandom.current().nextInt(1, 101);
297+
var tokenVersion = (randomNum <= this.advertisingTokenV4Percentage) ? TokenVersion.V4 : this.tokenVersionToUseIfNotV4;
298+
299+
return new AdvertisingToken(tokenVersion, now, now.plusMillis(identityExpiresAfter.toMillis()), this.operatorIdentity, publisherIdentity, userIdentity);
305300
}
306301

307-
protected class GlobalOptoutResult {
302+
static protected class GlobalOptoutResult {
308303
private final boolean isOptedOut;
309304
//can be null if isOptedOut is false!
310305
private final Instant time;
@@ -338,8 +333,8 @@ private GlobalOptoutResult getGlobalOptOutResult(UserIdentity userIdentity, bool
338333
return new GlobalOptoutResult(result);
339334
}
340335

341-
public TokenVersion getAdvertisingTokenVersion() {
342-
return advertisingTokenVersion;
336+
public TokenVersion getAdvertisingTokenVersionForTests() {
337+
assert this.advertisingTokenV4Percentage == 0 || this.advertisingTokenV4Percentage == 100; //we want tests to be deterministic
338+
return this.advertisingTokenV4Percentage == 100 ? TokenVersion.V4 : this.tokenVersionToUseIfNotV4;
343339
}
344-
345340
}

src/test/java/com/uid2/operator/UIDOperatorServiceTest.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ void setup() throws Exception {
7575
uid2Config.put(UIDOperatorService.IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS, IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS);
7676
uid2Config.put(UIDOperatorService.REFRESH_TOKEN_EXPIRES_AFTER_SECONDS, REFRESH_TOKEN_EXPIRES_AFTER_SECONDS);
7777
uid2Config.put(UIDOperatorService.REFRESH_IDENTITY_TOKEN_AFTER_SECONDS, REFRESH_IDENTITY_TOKEN_AFTER_SECONDS);
78-
uid2Config.put("advertising_token_v4", false);
78+
uid2Config.put("advertising_token_v4_percentage", 0);
7979
uid2Config.put("advertising_token_v3", false); // prod is using v2 token version for now
8080
uid2Config.put("identity_v3", false);
8181

@@ -92,7 +92,7 @@ void setup() throws Exception {
9292
euidConfig.put(UIDOperatorService.IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS, IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS);
9393
euidConfig.put(UIDOperatorService.REFRESH_TOKEN_EXPIRES_AFTER_SECONDS, REFRESH_TOKEN_EXPIRES_AFTER_SECONDS);
9494
euidConfig.put(UIDOperatorService.REFRESH_IDENTITY_TOKEN_AFTER_SECONDS, REFRESH_IDENTITY_TOKEN_AFTER_SECONDS);
95-
euidConfig.put("advertising_token_v4", false);
95+
euidConfig.put("advertising_token_v4_percentage", 0);
9696
euidConfig.put("advertising_token_v3", true);
9797
euidConfig.put("identity_v3", true);
9898

@@ -127,7 +127,8 @@ private UserIdentity createUserIdentity(String rawIdentityHash, IdentityScope sc
127127
);
128128
}
129129

130-
private AdvertisingToken validateAndGetToken(EncryptedTokenEncoder tokenEncoder, String advertisingTokenString, TokenVersion tokenVersion, IdentityScope scope, IdentityType type) {
130+
private AdvertisingToken validateAndGetToken(EncryptedTokenEncoder tokenEncoder, String advertisingTokenString, IdentityScope scope, IdentityType type) {
131+
TokenVersion tokenVersion = (scope == IdentityScope.UID2) ? uid2Service.getAdvertisingTokenVersionForTests() : euidService.getAdvertisingTokenVersionForTests();
131132
UIDOperatorVerticleTest.validateAdvertisingToken(advertisingTokenString, tokenVersion, scope, type);
132133
return tokenEncoder.decodeAdvertisingToken(advertisingTokenString);
133134
}
@@ -142,7 +143,7 @@ public void testGenerateAndRefresh() {
142143
final IdentityTokens tokens = uid2Service.generateIdentity(identityRequest);
143144
assertNotNull(tokens);
144145

145-
AdvertisingToken advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), uid2Service.getAdvertisingTokenVersion(), IdentityScope.UID2, IdentityType.Email);
146+
AdvertisingToken advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), IdentityScope.UID2, IdentityType.Email);
146147
assertEquals(this.now.plusSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS), advertisingToken.expiresAt);
147148
assertEquals(identityRequest.publisherIdentity.siteId, advertisingToken.publisherIdentity.siteId);
148149
assertEquals(identityRequest.userIdentity.identityScope, advertisingToken.userIdentity.identityScope);
@@ -164,7 +165,7 @@ public void testGenerateAndRefresh() {
164165
assertEquals(RefreshResponse.Status.Refreshed, refreshResponse.getStatus());
165166
assertNotNull(refreshResponse.getTokens());
166167

167-
AdvertisingToken advertisingToken2 = validateAndGetToken(tokenEncoder, refreshResponse.getTokens().getAdvertisingToken(), uid2Service.getAdvertisingTokenVersion(), IdentityScope.UID2, IdentityType.Email);
168+
AdvertisingToken advertisingToken2 = validateAndGetToken(tokenEncoder, refreshResponse.getTokens().getAdvertisingToken(), IdentityScope.UID2, IdentityType.Email);
168169
assertEquals(this.now.plusSeconds(IDENTITY_TOKEN_EXPIRES_AFTER_SECONDS), advertisingToken2.expiresAt);
169170
assertEquals(advertisingToken.publisherIdentity.siteId, advertisingToken2.publisherIdentity.siteId);
170171
assertEquals(advertisingToken.userIdentity.identityScope, advertisingToken2.userIdentity.identityScope);
@@ -242,12 +243,12 @@ public void testGenerateTokenForOptOutUser(IdentityType type, String identity, I
242243
final IdentityTokens tokensAfterOptOut;
243244
if (scope == IdentityScope.UID2) {
244245
tokens = uid2Service.generateIdentity(identityRequestForceGenerate);
245-
advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), uid2Service.getAdvertisingTokenVersion(), IdentityScope.UID2, userIdentity.identityType);
246+
advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), IdentityScope.UID2, userIdentity.identityType);
246247
tokensAfterOptOut = uid2Service.generateIdentity(identityRequestRespectOptOut);
247248

248249
} else {
249250
tokens = euidService.generateIdentity(identityRequestForceGenerate);
250-
advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), euidService.getAdvertisingTokenVersion(), IdentityScope.EUID, userIdentity.identityType);
251+
advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), IdentityScope.EUID, userIdentity.identityType);
251252
tokensAfterOptOut = euidService.generateIdentity(identityRequestRespectOptOut);
252253
}
253254
assertNotNull(tokens);
@@ -335,7 +336,7 @@ private InputUtil.InputVal generateInputVal(TestIdentityInputType type, String i
335336
"PhoneHash,+00000000000,UID2",
336337
"Phone,+00000000000,EUID",
337338
"PhoneHash,+00000000000,EUID"})
338-
public void testSpecialIdentityOptOutTokenGenerate(TestIdentityInputType type, String id, IdentityScope scope) {
339+
void testSpecialIdentityOptOutTokenGenerate(TestIdentityInputType type, String id, IdentityScope scope) {
339340
InputUtil.InputVal inputVal = generateInputVal(type, id);
340341

341342
final IdentityRequest identityRequest = new IdentityRequest(
@@ -366,7 +367,7 @@ public void testSpecialIdentityOptOutTokenGenerate(TestIdentityInputType type, S
366367
"PhoneHash,+00000000000,UID2",
367368
"Phone,+00000000000,EUID",
368369
"PhoneHash,+00000000000,EUID"})
369-
public void testSpecialIdentityOptOutIdentityMap(TestIdentityInputType type, String id, IdentityScope scope) {
370+
void testSpecialIdentityOptOutIdentityMap(TestIdentityInputType type, String id, IdentityScope scope) {
370371
InputUtil.InputVal inputVal = generateInputVal(type, id);
371372

372373
final MapRequest mapRequestRespectOptOut = new MapRequest(
@@ -397,7 +398,7 @@ public void testSpecialIdentityOptOutIdentityMap(TestIdentityInputType type, Str
397398
"PhoneHash,+00000000000,UID2",
398399
"Phone,+00000000000,EUID",
399400
"PhoneHash,+00000000000,EUID"})
400-
public void testSpecialIdentityOptOutTokenRefresh(TestIdentityInputType type, String id, IdentityScope scope) {
401+
void testSpecialIdentityOptOutTokenRefresh(TestIdentityInputType type, String id, IdentityScope scope) {
401402
InputUtil.InputVal inputVal = generateInputVal(type, id);
402403

403404
final IdentityRequest identityRequest = new IdentityRequest(
@@ -432,7 +433,7 @@ public void testSpecialIdentityOptOutTokenRefresh(TestIdentityInputType type, St
432433
"PhoneHash,+00000000002,UID2",
433434
"Phone,+00000000002,EUID",
434435
"PhoneHash,+00000000002,EUID"})
435-
public void testSpecialIdentityRefreshOptOutGenerate(TestIdentityInputType type, String id, IdentityScope scope) {
436+
void testSpecialIdentityRefreshOptOutGenerate(TestIdentityInputType type, String id, IdentityScope scope) {
436437
InputUtil.InputVal inputVal = generateInputVal(type, id);
437438

438439
final IdentityRequest identityRequest = new IdentityRequest(
@@ -470,7 +471,7 @@ public void testSpecialIdentityRefreshOptOutGenerate(TestIdentityInputType type,
470471
"PhoneHash,+00000000002,UID2",
471472
"Phone,+00000000002,EUID",
472473
"PhoneHash,+00000000002,EUID"})
473-
public void testSpecialIdentityRefreshOptOutIdentityMap(TestIdentityInputType type, String id, IdentityScope scope) {
474+
void testSpecialIdentityRefreshOptOutIdentityMap(TestIdentityInputType type, String id, IdentityScope scope) {
474475
InputUtil.InputVal inputVal = generateInputVal(type, id);
475476

476477
final MapRequest mapRequestRespectOptOut = new MapRequest(
@@ -501,7 +502,7 @@ public void testSpecialIdentityRefreshOptOutIdentityMap(TestIdentityInputType ty
501502
"PhoneHash,+12345678901,UID2",
502503
"Phone,+12345678901,EUID",
503504
"PhoneHash,+12345678901,EUID"})
504-
public void testSpecialIdentityValidateGenerate(TestIdentityInputType type, String id, IdentityScope scope) {
505+
void testSpecialIdentityValidateGenerate(TestIdentityInputType type, String id, IdentityScope scope) {
505506
InputUtil.InputVal inputVal = generateInputVal(type, id);
506507

507508
final IdentityRequest identityRequest = new IdentityRequest(
@@ -517,11 +518,11 @@ public void testSpecialIdentityValidateGenerate(TestIdentityInputType type, Stri
517518
AdvertisingToken advertisingToken;
518519
if(scope == IdentityScope.EUID) {
519520
tokens = euidService.generateIdentity(identityRequest);
520-
advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), euidService.getAdvertisingTokenVersion(), scope, identityRequest.userIdentity.identityType);
521+
advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), scope, identityRequest.userIdentity.identityType);
521522
}
522523
else {
523524
tokens = uid2Service.generateIdentity(identityRequest);
524-
advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), uid2Service.getAdvertisingTokenVersion(), scope, identityRequest.userIdentity.identityType);
525+
advertisingToken = validateAndGetToken(tokenEncoder, tokens.getAdvertisingToken(), scope, identityRequest.userIdentity.identityType);
525526
}
526527
assertNotNull(tokens);
527528
assertNotEquals(IdentityTokens.LogoutToken, tokens);
@@ -538,7 +539,7 @@ public void testSpecialIdentityValidateGenerate(TestIdentityInputType type, Stri
538539
"PhoneHash,+12345678901,UID2",
539540
"Phone,+12345678901,EUID",
540541
"PhoneHash,+12345678901,EUID"})
541-
public void testSpecialIdentityValidateIdentityMap(TestIdentityInputType type, String id, IdentityScope scope) {
542+
void testSpecialIdentityValidateIdentityMap(TestIdentityInputType type, String id, IdentityScope scope) {
542543
InputUtil.InputVal inputVal = generateInputVal(type, id);
543544

544545
final MapRequest mapRequestRespectOptOut = new MapRequest(
@@ -567,7 +568,7 @@ public void testSpecialIdentityValidateIdentityMap(TestIdentityInputType type, S
567568
"PhoneHash,+61401234567,EUID",
568569
"Email,[email protected],EUID",
569570
"EmailHash,[email protected],EUID"})
570-
public void testNormalIdentityOptIn(TestIdentityInputType type, String id, IdentityScope scope) {
571+
void testNormalIdentityOptIn(TestIdentityInputType type, String id, IdentityScope scope) {
571572
InputUtil.InputVal inputVal = generateInputVal(type, id);
572573
final IdentityRequest identityRequest = new IdentityRequest(
573574
new PublisherIdentity(123, 124, 125),

0 commit comments

Comments
 (0)