diff --git a/src/main/java/com/uid2/operator/service/UIDOperatorService.java b/src/main/java/com/uid2/operator/service/UIDOperatorService.java index 6d4ff86d0..5e66dd70c 100644 --- a/src/main/java/com/uid2/operator/service/UIDOperatorService.java +++ b/src/main/java/com/uid2/operator/service/UIDOperatorService.java @@ -47,7 +47,8 @@ public class UIDOperatorService implements IUIDOperatorService { private final OperatorIdentity operatorIdentity; private final TokenVersion refreshTokenVersion; - private final boolean identityV3Enabled; + // if we use Raw UID v3 format for the raw UID2/EUIDs generated in this operator + private final boolean rawUidV3Enabled; private final Handler saltRetrievalResponseHandler; @@ -90,7 +91,7 @@ public UIDOperatorService(JsonObject config, IOptOutStore optOutStore, ISaltProv } this.refreshTokenVersion = TokenVersion.V3; - this.identityV3Enabled = config.getBoolean("identity_v3", false); + this.rawUidV3Enabled = config.getBoolean("identity_v3", false); } @Override @@ -230,7 +231,7 @@ private MappedIdentity getAdvertisingId(UserIdentity firstLevelHashIdentity, Ins final SaltEntry rotatingSalt = getSaltProviderSnapshot(asOf).getRotatingSalt(firstLevelHashIdentity.id); return new MappedIdentity( - this.identityV3Enabled + this.rawUidV3Enabled ? TokenUtils.getAdvertisingIdV3(firstLevelHashIdentity.identityScope, firstLevelHashIdentity.identityType, firstLevelHashIdentity.id, rotatingSalt.getSalt()) : TokenUtils.getAdvertisingIdV2(firstLevelHashIdentity.id, rotatingSalt.getSalt()), rotatingSalt.getHashedId()); diff --git a/src/test/java/com/uid2/operator/EUIDOperatorVerticleTest.java b/src/test/java/com/uid2/operator/EUIDOperatorVerticleTest.java index 138e17777..7c894fba6 100644 --- a/src/test/java/com/uid2/operator/EUIDOperatorVerticleTest.java +++ b/src/test/java/com/uid2/operator/EUIDOperatorVerticleTest.java @@ -21,6 +21,8 @@ public EUIDOperatorVerticleTest() throws IOException { @Override protected IdentityScope getIdentityScope() { return IdentityScope.EUID; } @Override + protected boolean useRawUidV3() { return true; } + @Override protected void addAdditionalTokenGenerateParams(JsonObject payload) { if (payload != null && !payload.containsKey("tcf_consent_string")) { payload.put("tcf_consent_string", "CPehNtWPehNtWABAMBFRACBoALAAAEJAAIYgAKwAQAKgArABAAqAAA"); diff --git a/src/test/java/com/uid2/operator/TokenEncodingTest.java b/src/test/java/com/uid2/operator/TokenEncodingTest.java index c77c81b78..73e11309c 100644 --- a/src/test/java/com/uid2/operator/TokenEncodingTest.java +++ b/src/test/java/com/uid2/operator/TokenEncodingTest.java @@ -16,6 +16,7 @@ import io.vertx.core.json.JsonObject; import org.junit.Assert; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EnumSource; import java.time.Instant; @@ -86,15 +87,23 @@ public void testRefreshTokenEncoding(TokenVersion tokenVersion) { } @ParameterizedTest - @EnumSource(TokenVersion.class) - public void testAdvertisingTokenEncodings(TokenVersion tokenVersion) { + @CsvSource({"false, V4", //same as current UID2 prod (as at 2024-12-10) + "true, V4", //same as current EUID prod (as at 2024-12-10) + //the following combinations aren't used in any UID2/EUID environments but just testing them regardless + "false, V3", + "true, V3", + "false, V2", + "true, V2", + } + ) + public void testAdvertisingTokenEncodings(boolean useRawUIDv3, TokenVersion adTokenVersion) { final EncryptedTokenEncoder encoder = new EncryptedTokenEncoder(this.keyManager); final Instant now = EncodingUtils.NowUTCMillis(); - final byte[] rawUid = UIDOperatorVerticleTest.getRawUid(IdentityType.Email, "test@example.com", IdentityScope.UID2, tokenVersion != TokenVersion.V2); + final byte[] rawUid = UIDOperatorVerticleTest.getRawUid(IdentityType.Email, "test@example.com", IdentityScope.UID2, useRawUIDv3); final AdvertisingToken token = new AdvertisingToken( - tokenVersion, + adTokenVersion, now, now.plusSeconds(60), new OperatorIdentity(101, OperatorType.Service, 102, 103), @@ -103,9 +112,9 @@ public void testAdvertisingTokenEncodings(TokenVersion tokenVersion) { ); final byte[] encodedBytes = encoder.encode(token, now); - final AdvertisingToken decoded = encoder.decodeAdvertisingToken(EncryptedTokenEncoder.bytesToBase64Token(encodedBytes, tokenVersion)); + final AdvertisingToken decoded = encoder.decodeAdvertisingToken(EncryptedTokenEncoder.bytesToBase64Token(encodedBytes, adTokenVersion)); - assertEquals(tokenVersion, decoded.version); + assertEquals(adTokenVersion, decoded.version); assertEquals(token.createdAt, decoded.createdAt); assertEquals(token.expiresAt, decoded.expiresAt); assertTrue(token.userIdentity.matches(decoded.userIdentity)); @@ -114,7 +123,7 @@ public void testAdvertisingTokenEncodings(TokenVersion tokenVersion) { assertEquals(token.publisherIdentity.siteId, decoded.publisherIdentity.siteId); Buffer b = Buffer.buffer(encodedBytes); - int keyId = b.getInt(tokenVersion == TokenVersion.V2 ? 1 : 2); //TODO - extract master key from token should be a helper function + int keyId = b.getInt(adTokenVersion == TokenVersion.V2 ? 1 : 2); //TODO - extract master key from token should be a helper function assertEquals(Data.MasterKeySiteId, keyManager.getSiteIdFromKeyId(keyId)); } } diff --git a/src/test/java/com/uid2/operator/UIDOperatorVerticleTest.java b/src/test/java/com/uid2/operator/UIDOperatorVerticleTest.java index d9a91ae01..82ab057d0 100644 --- a/src/test/java/com/uid2/operator/UIDOperatorVerticleTest.java +++ b/src/test/java/com/uid2/operator/UIDOperatorVerticleTest.java @@ -13,7 +13,6 @@ import com.uid2.operator.util.Tuple; import com.uid2.operator.vertx.OperatorShutdownHandler; import com.uid2.operator.vertx.UIDOperatorVerticle; -import com.uid2.operator.vertx.ClientInputValidationException; import com.uid2.shared.Utils; import com.uid2.shared.auth.ClientKey; import com.uid2.shared.auth.Keyset; @@ -27,9 +26,7 @@ import com.uid2.shared.secret.KeyHasher; import com.uid2.shared.store.*; import com.uid2.shared.store.reader.RotatingKeysetProvider; -import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Metrics; -import io.micrometer.core.instrument.search.MeterNotFoundException; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.vertx.core.AsyncResult; import io.vertx.core.Future; @@ -158,7 +155,7 @@ private void setupConfig(JsonObject config) { config.put(Const.Config.SharingTokenExpiryProp, 60 * 60 * 24 * 30); config.put("identity_scope", getIdentityScope().toString()); - config.put("identity_v3", useIdentityV3()); + config.put("identity_v3", useRawUidV3()); config.put("client_side_token_generate", true); config.put("key_sharing_endpoint_provide_app_names", true); config.put("client_side_token_generate_log_invalid_http_origins", true); @@ -622,23 +619,23 @@ private void assertTokenStatusMetrics(Integer siteId, TokenResponseStatsCollecto } private byte[] getAdvertisingIdFromIdentity(IdentityType identityType, String identityString, String firstLevelSalt, String rotatingSalt) { - return getRawUid(identityType, identityString, firstLevelSalt, rotatingSalt, getIdentityScope(), useIdentityV3()); + return getRawUid(identityType, identityString, firstLevelSalt, rotatingSalt, getIdentityScope(), useRawUidV3()); } - private static byte[] getRawUid(IdentityType identityType, String identityString, String firstLevelSalt, String rotatingSalt, IdentityScope identityScope, boolean useIdentityV3) { - return !useIdentityV3 + private 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(IdentityType identityType, String identityString, IdentityScope identityScope, boolean useIdentityV3) { - return !useIdentityV3 + public static byte[] getRawUid(IdentityType identityType, String identityString, IdentityScope identityScope, boolean useRawUidV3) { + return !useRawUidV3 ? TokenUtils.getAdvertisingIdV2FromIdentity(identityString, firstLevelSalt, rotatingSalt123.getSalt()) : TokenUtils.getAdvertisingIdV3FromIdentity(identityScope, identityType, identityString, firstLevelSalt, rotatingSalt123.getSalt()); } private byte[] getAdvertisingIdFromIdentityHash(IdentityType identityType, String identityString, String firstLevelSalt, String rotatingSalt) { - return !useIdentityV3() + return !useRawUidV3() ? TokenUtils.getAdvertisingIdV2FromIdentityHash(identityString, firstLevelSalt, rotatingSalt) : TokenUtils.getAdvertisingIdV3FromIdentityHash(getIdentityScope(), identityType, identityString, firstLevelSalt, rotatingSalt); } @@ -665,7 +662,7 @@ private JsonObject setupIdentityMapServiceLinkTest() { protected TokenVersion getTokenVersion() {return TokenVersion.V4;} - final boolean useIdentityV3() { return getTokenVersion() != TokenVersion.V2; } + protected boolean useRawUidV3() { return false; } protected IdentityScope getIdentityScope() { return IdentityScope.UID2; } protected void addAdditionalTokenGenerateParams(JsonObject payload) {} @@ -816,7 +813,10 @@ private AdvertisingToken validateAndGetToken(EncryptedTokenEncoder encoder, Json final String advertisingTokenString = body.getString("advertising_token"); validateAdvertisingToken(advertisingTokenString, getTokenVersion(), getIdentityScope(), identityType); AdvertisingToken advertisingToken = encoder.decodeAdvertisingToken(advertisingTokenString); - if (getTokenVersion() == TokenVersion.V4) { + + // without useIdentityV3() 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); } return advertisingToken;