Skip to content

Commit f035228

Browse files
committed
Testing Identity Map with octet stream and without
1 parent 3bf54cc commit f035228

File tree

1 file changed

+211
-0
lines changed

1 file changed

+211
-0
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package suite.operator;
2+
3+
import app.AppsMap;
4+
import app.component.Operator;
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.uid2.shared.util.Mapper;
8+
import common.Const;
9+
import common.EnvUtil;
10+
import common.HttpClient;
11+
import okhttp3.*;
12+
import org.junit.jupiter.api.Test;
13+
14+
import java.lang.reflect.Constructor;
15+
import java.lang.reflect.Method;
16+
import java.nio.ByteBuffer;
17+
import java.nio.charset.StandardCharsets;
18+
import java.security.SecureRandom;
19+
import java.time.Instant;
20+
import java.util.*;
21+
22+
import static org.assertj.core.api.Assertions.assertThat;
23+
24+
25+
public class IdentityMapDirectHttpTest {
26+
27+
private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance();
28+
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
29+
private static final String CLIENT_API_KEY = EnvUtil.getEnv(Const.Config.Operator.CLIENT_API_KEY);
30+
private static final String CLIENT_API_SECRET = EnvUtil.getEnv(Const.Config.Operator.CLIENT_API_SECRET);
31+
private static final int TIMESTAMP_LENGTH = 8;
32+
33+
private record V2Envelope(String envelope, byte[] nonce) {}
34+
35+
/**
36+
* Test 1: JSON content type with base64 encoding (standard approach)
37+
*/
38+
@Test
39+
public void test1_JsonWithBase64() throws Exception {
40+
System.out.println("🧪 Test 1: JSON content type + Base64 encoding");
41+
42+
Operator operator = getFirstOperator();
43+
String jsonPayload = createTestPayload();
44+
45+
// Create base64-encoded envelope
46+
V2Envelope envelope = createV2Envelope(jsonPayload, CLIENT_API_SECRET);
47+
48+
// Send with JSON content type
49+
JsonNode response = sendWithJsonContentType(operator, envelope.envelope(), envelope.nonce());
50+
51+
validateSuccessResponse(response, "Test 1");
52+
System.out.println("✅ Test 1 completed successfully\n");
53+
}
54+
55+
/**
56+
* Test 2: Octet-stream content type with base64 encoding
57+
*/
58+
@Test
59+
public void test2_OctetStreamWithBase64() throws Exception {
60+
System.out.println("🧪 Test 2: Octet-stream content type + Base64 encoding");
61+
62+
Operator operator = getFirstOperator();
63+
String jsonPayload = createTestPayload();
64+
65+
// Create base64-encoded envelope
66+
V2Envelope envelope = createV2Envelope(jsonPayload, CLIENT_API_SECRET);
67+
68+
// Send with octet-stream content type
69+
JsonNode response = sendWithOctetStreamContentType(operator, envelope.envelope().getBytes(StandardCharsets.UTF_8), envelope.nonce());
70+
71+
validateSuccessResponse(response, "Test 2");
72+
System.out.println("✅ Test 2 completed successfully\n");
73+
}
74+
75+
// Helper methods
76+
77+
private Operator getFirstOperator() {
78+
return AppsMap.getApps(Operator.class).iterator().next();
79+
}
80+
81+
private String createTestPayload() throws Exception {
82+
Map<String, Object> payload = new HashMap<>();
83+
payload.put("email", List.of("[email protected]", "[email protected]"));
84+
payload.put("policy", 1);
85+
86+
String jsonPayload = OBJECT_MAPPER.writeValueAsString(payload);
87+
System.out.println("Request payload: " + jsonPayload);
88+
return jsonPayload;
89+
}
90+
91+
/**
92+
* Send request with JSON content type (string body)
93+
*/
94+
private JsonNode sendWithJsonContentType(Operator operator, String body, byte[] nonce) throws Exception {
95+
String encryptedResponse = HttpClient.post(
96+
operator.getBaseUrl() + "/v2/identity/map",
97+
body,
98+
CLIENT_API_KEY
99+
);
100+
101+
return decryptV2Response(encryptedResponse, nonce);
102+
}
103+
104+
/**
105+
* Send request with octet-stream content type
106+
*/
107+
private JsonNode sendWithOctetStreamContentType(Operator operator, byte[] bodyBytes, byte[] nonce) throws Exception {
108+
Request request = new Request.Builder()
109+
.url(operator.getBaseUrl() + "/v2/identity/map")
110+
.addHeader("Authorization", "Bearer " + CLIENT_API_KEY)
111+
.post(RequestBody.create(bodyBytes, MediaType.get("application/octet-stream")))
112+
.build();
113+
114+
String encryptedResponse = executeRequest(request);
115+
return decryptV2Response(encryptedResponse, nonce);
116+
}
117+
118+
/**
119+
* Execute HTTP request and return response body
120+
*/
121+
private String executeRequest(Request request) throws Exception {
122+
try (Response response = HttpClient.RAW_CLIENT.newCall(request).execute()) {
123+
if (!response.isSuccessful()) {
124+
throw new RuntimeException("HTTP request failed: " + response.code() + " " + response.message());
125+
}
126+
return Objects.requireNonNull(response.body()).string();
127+
}
128+
}
129+
130+
/**
131+
* Creates encrypted V2 envelope with base64 encoding
132+
*/
133+
private V2Envelope createV2Envelope(String payload, String secret) throws Exception {
134+
// Create unencrypted envelope: timestamp + nonce + payload
135+
Instant timestamp = Instant.now();
136+
137+
byte[] nonce = new byte[8];
138+
SECURE_RANDOM.nextBytes(nonce);
139+
140+
byte[] payloadBytes = payload.getBytes(StandardCharsets.UTF_8);
141+
142+
ByteBuffer writer = ByteBuffer.allocate(TIMESTAMP_LENGTH + nonce.length + payloadBytes.length);
143+
writer.putLong(timestamp.toEpochMilli());
144+
writer.put(nonce);
145+
writer.put(payloadBytes);
146+
147+
// Encrypt the envelope
148+
byte envelopeVersion = 1;
149+
byte[] encrypted = encryptGCM(writer.array(), Base64.getDecoder().decode(secret));
150+
151+
ByteBuffer envelopeBuffer = ByteBuffer.allocate(1 + encrypted.length);
152+
envelopeBuffer.put(envelopeVersion);
153+
envelopeBuffer.put(encrypted);
154+
155+
return new V2Envelope(Base64.getEncoder().encodeToString(envelopeBuffer.array()), nonce);
156+
}
157+
158+
/**
159+
* Decrypts V2 response using UID2 internal methods
160+
*/
161+
private JsonNode decryptV2Response(String encryptedResponse, byte[] nonce) throws Exception {
162+
Class<?> uid2HelperClass = Class.forName("com.uid2.client.Uid2Helper");
163+
Constructor<?> constructor = uid2HelperClass.getDeclaredConstructor(String.class);
164+
constructor.setAccessible(true);
165+
Object uid2Helper = constructor.newInstance(CLIENT_API_SECRET);
166+
167+
Method decryptMethod = uid2HelperClass.getDeclaredMethod("decrypt", String.class, byte[].class);
168+
decryptMethod.setAccessible(true);
169+
String decryptedResponse = (String) decryptMethod.invoke(uid2Helper, encryptedResponse, nonce);
170+
171+
return OBJECT_MAPPER.readTree(decryptedResponse);
172+
}
173+
174+
/**
175+
* Encrypts data using GCM encryption via UID2 internal methods
176+
*/
177+
private byte[] encryptGCM(byte[] data, byte[] secretBytes) throws Exception {
178+
Class<?> encryptionClass = Class.forName("com.uid2.client.Uid2Encryption");
179+
Method encryptMethod = encryptionClass.getDeclaredMethod("encryptGCM", byte[].class, byte[].class, byte[].class);
180+
encryptMethod.setAccessible(true);
181+
return (byte[]) encryptMethod.invoke(encryptionClass, data, null, secretBytes);
182+
}
183+
184+
/**
185+
* Validates successful response structure
186+
*/
187+
private void validateSuccessResponse(JsonNode response, String testName) {
188+
System.out.println(testName + " - Response status: " + response.get("status").asText());
189+
190+
// Validate successful response
191+
assertThat(response.get("status").asText()).isEqualTo("success");
192+
assertThat(response.has("body")).isTrue();
193+
194+
JsonNode body = response.get("body");
195+
assertThat(body.has("mapped")).isTrue();
196+
197+
JsonNode mapped = body.get("mapped");
198+
assertThat(mapped.isArray()).isTrue();
199+
assertThat(mapped.size()).isEqualTo(2);
200+
201+
// Validate each mapped identity has required fields
202+
for (JsonNode mappedIdentity : mapped) {
203+
assertThat(mappedIdentity.has("advertising_id")).isTrue();
204+
assertThat(mappedIdentity.has("bucket_id")).isTrue();
205+
assertThat(mappedIdentity.get("advertising_id").asText()).isNotBlank();
206+
assertThat(mappedIdentity.get("bucket_id").asText()).isNotBlank();
207+
}
208+
209+
System.out.println(testName + " - Successfully mapped 2 identities");
210+
}
211+
}

0 commit comments

Comments
 (0)