Skip to content

Commit a78f44f

Browse files
committed
Fixed code
1 parent e019c90 commit a78f44f

File tree

3 files changed

+176
-8
lines changed

3 files changed

+176
-8
lines changed

src/main/java/com/uid2/admin/vertx/RequestUtil.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,17 @@ public static Duration[] getDurations(RoutingContext rc, String paramName) {
207207
}
208208
}
209209

210+
public static Optional<String> getString(RoutingContext rc, String paramName) {
211+
final List<String> values = rc.queryParam(paramName);
212+
if (values.isEmpty()) return Optional.empty();
213+
return Optional.of(values.get(0));
214+
}
215+
210216
public static Optional<Boolean> getBoolean(RoutingContext rc, String paramName, boolean defaultValue) {
211217
final List<String> values = rc.queryParam(paramName);
212-
if (values.isEmpty()) return Optional.of(defaultValue);
218+
if (values.isEmpty()) {
219+
return Optional.of(defaultValue);
220+
}
213221
try {
214222
return Optional.of(Boolean.valueOf(values.get(0)));
215223
} catch (Exception ex) {

src/main/java/com/uid2/admin/vertx/service/SaltService.java

Lines changed: 112 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.uid2.admin.vertx.RequestUtil;
1010
import com.uid2.admin.vertx.ResponseUtil;
1111
import com.uid2.admin.vertx.WriteLock;
12+
import com.uid2.client.Uid2Helper;
1213
import com.uid2.shared.audit.AuditParams;
1314
import com.uid2.shared.auth.Role;
1415
import com.uid2.shared.model.SaltEntry;
@@ -76,7 +77,7 @@ public class SaltService implements IService {
7677
private static final String OPERATOR_URL = "http://proxy:80";
7778
private static final Map<String, String> OPERATOR_HEADERS = Map.of("Authorization", String.format("Bearer %s", CLIENT_API_KEY));
7879

79-
private static final int IDENTITY_COUNT = 1_000;
80+
private static final int IDENTITY_COUNT = 10_000;
8081
private static final int TIMESTAMP_LENGTH = 8;
8182
private static final int IV_LENGTH = 12;
8283

@@ -115,6 +116,12 @@ public void setupRoutes(Router router) {
115116
}
116117
}, new AuditParams(List.of("fraction"), Collections.emptyList()), Role.MAINTAINER, Role.SECRET_ROTATION));
117118

119+
router.post("/api/salt/compare").blockingHandler(auth.handle(ctx -> {
120+
synchronized (writeLock) {
121+
this.handleSaltCompare(ctx);
122+
}
123+
}, new AuditParams(List.of(), Collections.emptyList()), Role.MAINTAINER, Role.SECRET_ROTATION));
124+
118125
router.post("/api/salt/simulateToken").blockingHandler(auth.handle(ctx -> {
119126
synchronized (writeLock) {
120127
this.handleSaltSimulateToken(ctx);
@@ -260,6 +267,99 @@ private void handleSaltFastForward(RoutingContext rc) {
260267
}
261268
}
262269

270+
private void handleSaltCompare(RoutingContext rc) {
271+
try {
272+
final String testOperatorUrl = RequestUtil.getString(rc, "test_operator_url").orElse("");
273+
final String testApiKey = RequestUtil.getString(rc, "test_api_key").orElse("");
274+
final String testApiSecret = RequestUtil.getString(rc, "test_api_secret").orElse("");
275+
final String integOperatorUrl = RequestUtil.getString(rc, "integ_operator_url").orElse("");
276+
final String integApiKey = RequestUtil.getString(rc, "integ_api_key").orElse("");
277+
final String integApiSecret = RequestUtil.getString(rc, "integ_api_secret").orElse("");
278+
279+
List<String> emails = new ArrayList<>();
280+
Map<String, Boolean> emailToSaltMap = new HashMap<>();
281+
RotatingSaltProvider.SaltSnapshot snapshot = saltProvider.getSnapshots().getLast();
282+
for (int i = 0; i < IDENTITY_COUNT; i++) {
283+
String email = randomEmail();
284+
emails.add(email);
285+
286+
SaltEntry salt = getSalt(email, snapshot);
287+
boolean isV4 = salt.currentKeySalt() != null && salt.currentKeySalt().key() != null && salt.currentKeySalt().salt() != null;
288+
emailToSaltMap.put(email, isV4);
289+
}
290+
291+
// Construct identity map args
292+
JsonNode testResponse = v3IdentityMap(emails, testOperatorUrl, testApiKey, testApiSecret);
293+
JsonNode integResponse = v3IdentityMap(emails, integOperatorUrl, integApiKey, integApiSecret);
294+
295+
JsonNode testMappings = testResponse.at("/body/email");
296+
JsonNode integMappings = integResponse.at("/body/email");
297+
298+
int testV4UidCount = 0;
299+
int testV2UidCount = 0;
300+
int testInvalidV4UidCount = 0;
301+
int testInvalidV2UidCount = 0;
302+
int testNullUidCount = 0;
303+
int testIntegMatchCount = 0;
304+
int testIntegMismatchCount = 0;
305+
for (int i = 0; i < IDENTITY_COUNT; i++) {
306+
String email = emails.get(i);
307+
boolean isV4 = emailToSaltMap.get(email);
308+
309+
String testUid = testMappings.get(i).at("/u").asText();
310+
String integUid = integMappings.get(i).at("/u").asText();
311+
312+
byte[] testUidBytes = testUid == null ? null : Base64.getDecoder().decode(testUid);
313+
byte[] integUidBytes = integUid == null ? null : Base64.getDecoder().decode(integUid);
314+
315+
// First, check test UID is valid
316+
if (testUidBytes == null) {
317+
LOGGER.error("TEST - UID is null");
318+
testNullUidCount++;
319+
} else if (isV4) {
320+
if (testUidBytes.length != 33) {
321+
LOGGER.error("TEST - salt is v4 but UID length is {}", testUidBytes.length);
322+
testInvalidV4UidCount++;
323+
} else {
324+
testV4UidCount++;
325+
}
326+
} else {
327+
if (testUidBytes.length != 32) {
328+
LOGGER.error("TEST - salt is v2 but UID length is {}", testUidBytes.length);
329+
testInvalidV2UidCount++;
330+
} else {
331+
testV2UidCount++;
332+
}
333+
}
334+
335+
// Then, check if test and integ match
336+
if (!isV4) {
337+
if (!Arrays.equals(testUidBytes, integUidBytes)) {
338+
LOGGER.error("TEST and INTEG UIDs do not match for {} - TEST={} | INTEG={}", email, testUid, integUid);
339+
testIntegMismatchCount++;
340+
} else {
341+
testIntegMatchCount++;
342+
}
343+
}
344+
}
345+
346+
LOGGER.info("UID Consistency between Test and Integ: " +
347+
"test_v4_uid_count={} test_v2_uid_count={} " +
348+
"test_v4_invalid_uid_count={} test_v2_invalid_uid_count={} test_null_uid_count={} " +
349+
"test_integ_match_count={} test_integ_mismatch_count={}",
350+
testV4UidCount, testV2UidCount,
351+
testInvalidV4UidCount, testInvalidV2UidCount, testNullUidCount,
352+
testIntegMatchCount, testIntegMismatchCount);
353+
354+
rc.response()
355+
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
356+
.end();
357+
} catch (Exception e) {
358+
LOGGER.error(e.getMessage(), e);
359+
rc.fail(500, e);
360+
}
361+
}
362+
263363
private void handleSaltSimulateToken(RoutingContext rc) {
264364
try {
265365
final double fraction = RequestUtil.getDouble(rc, "fraction").orElse(0.002740);
@@ -329,8 +429,8 @@ private void handleSaltSimulateToken(RoutingContext rc) {
329429
}
330430

331431
LOGGER.info(
332-
"UID token simulation: success_count={}, failure_count={}, " +
333-
"v2_to_v4_count={}, v4_to_v4_count={}, v4_to_v2_count={}, v2_to_v2_count={}",
432+
"UID token simulation: success_count={} failure_count={} " +
433+
"v2_to_v4_count={} v4_to_v4_count={} v4_to_v2_count={} v2_to_v2_count={}",
334434
emailToRefreshSuccessMap.values().stream().filter(x -> x).count(),
335435
emailToRefreshSuccessMap.values().stream().filter(x -> !x).count(),
336436
emails.stream().filter(email -> !preRotationEmailToSaltMap.get(email) && postRotationEmailToSaltMap.get(email)).count(),
@@ -782,7 +882,7 @@ private void v2LoadSalts() throws Exception {
782882
HTTP_CLIENT.post(String.format("%s/v2/salts/load", OPERATOR_URL), "", OPERATOR_HEADERS);
783883
}
784884

785-
private JsonNode v3IdentityMap(List<String> emails) throws Exception {
885+
private JsonNode v3IdentityMap(List<String> emails, String baseUrl, String key, String secret) throws Exception {
786886
StringBuilder reqBody = new StringBuilder("{ \"email\": [");
787887
for (String email : emails) {
788888
reqBody.append(String.format("\"%s\"", email));
@@ -792,9 +892,14 @@ private JsonNode v3IdentityMap(List<String> emails) throws Exception {
792892
}
793893
reqBody.append("] }");
794894

795-
V2Envelope envelope = v2CreateEnvelope(reqBody.toString(), CLIENT_API_SECRET);
796-
HttpResponse<String> response = HTTP_CLIENT.post(String.format("%s/v3/identity/map", OPERATOR_URL), envelope.envelope(), OPERATOR_HEADERS);
797-
return v2DecryptEncryptedResponse(response.body(), envelope.nonce(), CLIENT_API_SECRET);
895+
V2Envelope envelope = v2CreateEnvelope(reqBody.toString(), secret);
896+
Map<String, String> headers = Map.of("Authorization", String.format("Bearer %s", key));
897+
HttpResponse<String> response = HTTP_CLIENT.post(String.format("%s/v3/identity/map", baseUrl), envelope.envelope(), headers);
898+
return v2DecryptEncryptedResponse(response.body(), envelope.nonce(), secret);
899+
}
900+
901+
private JsonNode v3IdentityMap(List<String> emails) throws Exception {
902+
return v3IdentityMap(emails, OPERATOR_URL, CLIENT_API_KEY, CLIENT_API_SECRET);
798903
}
799904

800905
private JsonNode v2TokenGenerate(String email) throws Exception {

webroot/adm/salt.html

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,36 @@ <h1>UID2 Env - Salt Management</h1>
5656
defaultValue: true
5757
}
5858

59+
const testOperatorUrlInput = {
60+
name: 'testOperatorUrl',
61+
label: 'Test Operator URL'
62+
}
63+
64+
const testApiKeyInput = {
65+
name: 'testApiKey',
66+
label: 'Test API Key'
67+
}
68+
69+
const testApiSecretInput = {
70+
name: 'testApiSecret',
71+
label: 'Test API Secret'
72+
}
73+
74+
const integOperatorUrlInput = {
75+
name: 'integOperatorUrl',
76+
label: 'Integ Operator URL'
77+
}
78+
79+
const integApiKeyInput = {
80+
name: 'integApiKey',
81+
label: 'Integ API Key'
82+
}
83+
84+
const integApiSecretInput = {
85+
name: 'integApiSecret',
86+
label: 'Integ API Secret'
87+
}
88+
5989
const tokenIterationsInput = {
6090
name: 'tokenIterations',
6191
label: 'Iterations',
@@ -140,6 +170,31 @@ <h1>UID2 Env - Salt Management</h1>
140170
}
141171
},
142172
},
173+
{
174+
id: 'compare',
175+
title: 'Compare Test and Integ',
176+
role: 'maintainer',
177+
inputs: [
178+
testOperatorUrlInput,
179+
testApiKeyInput,
180+
testApiSecretInput,
181+
integOperatorUrlInput,
182+
integApiKeyInput,
183+
integApiSecretInput
184+
],
185+
apiCall: {
186+
method: 'POST',
187+
getUrl: (inputs) => {
188+
const testOperatorUrl = encodeURIComponent(inputs.testOperatorUrl);
189+
const testApiKey = encodeURIComponent(inputs.testApiKey);
190+
const testApiSecret = encodeURIComponent(inputs.testApiSecret);
191+
const integOperatorUrl = encodeURIComponent(inputs.integOperatorUrl);
192+
const integApiKey = encodeURIComponent(inputs.integApiKey);
193+
const integApiSecret = encodeURIComponent(inputs.integApiSecret);
194+
return `/api/salt/compare?test_operator_url=${testOperatorUrl}&test_api_key=${testApiKey}&test_api_secret=${testApiSecret}&integ_operator_url=${integOperatorUrl}&integ_api_key=${integApiKey}&integ_api_secret=${integApiSecret}`;
195+
}
196+
},
197+
},
143198
{
144199
id: 'simulateToken',
145200
title: 'Simulate Token',

0 commit comments

Comments
 (0)