Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .trivyignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ CVE-2025-65018 exp:2026-06-05
CVE-2025-66293 exp:2026-06-15

# UID2-6481
CVE-2025-68973 exp:2026-06-15
CVE-2025-68973 exp:2026-06-15

# Libpng for testing
CVE-2026-22695
CVE-2026-22801
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ RUN adduser -D uid2-operator && mkdir -p /opt/uid2 && chmod 777 -R /opt/uid2 &&
USER uid2-operator

CMD java \
-XX:MaxRAMPercentage=95 -XX:-UseCompressedOops -XX:+PrintFlagsFinal -XX:-OmitStackTraceInFastThrow \
-XX:+UseZGC -XX:+ZGenerational \
-XX:MaxRAMPercentage=85 -XX:-UseCompressedOops -XX:+PrintFlagsFinal -XX:-OmitStackTraceInFastThrow \
-Djava.security.egd=file:/dev/./urandom \
-Dvertx.logger-delegate-factory-class-name=io.vertx.core.logging.SLF4JLogDelegateFactory \
-Dlogback.configurationFile=/app/conf/logback.xml \
Expand Down
4 changes: 3 additions & 1 deletion conf/default-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@
"sharing_token_expiry_seconds": 2592000,
"operator_type": "public",
"enable_remote_config": true,
"uid_instance_id_prefix": "local-operator"
"uid_instance_id_prefix": "local-operator",
"enable_async_batch_request": true,
"compute_pool_thread_count": 12
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.uid2</groupId>
<artifactId>uid2-operator</artifactId>
<version>5.63.30</version>
<version>5.63.35-alpha-294-SNAPSHOT</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/uid2/operator/Const.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@ public class Config extends com.uid2.shared.Const.Config {
public static final String RuntimeConfigMetadataPathProp = "runtime_config_metadata_path";

public static final String IdentityEnvironmentProp = "identity_environment";

public static final String ComputePoolThreadCountProp = "compute_pool_thread_count";
public static final String EnableAsyncBatchRequestProp = "enable_async_batch_request";
}
}
22 changes: 19 additions & 3 deletions src/main/java/com/uid2/operator/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,13 @@ private void run() throws Exception {
this.createVertxInstancesMetric();
this.createVertxEventLoopsMetric();

// Create shared compute pool for CPU-intensive operations
final int computePoolSize = config.getInteger(Const.Config.ComputePoolThreadCountProp, Math.max(1, Runtime.getRuntime().availableProcessors() - 2));
final WorkerExecutor computeWorkerPool = vertx.createSharedWorkerExecutor("compute", computePoolSize);
LOGGER.info("Created compute worker pool with size: {}", computePoolSize);

Supplier<Verticle> operatorVerticleSupplier = () -> {
UIDOperatorVerticle verticle = new UIDOperatorVerticle(configStore, config, this.clientSideTokenGenerate, siteProvider, clientKeyProvider, clientSideKeypairProvider, getKeyManager(), saltProvider, optOutStore, Clock.systemUTC(), _statsCollectorQueue, new SecureLinkValidatorService(this.serviceLinkProvider, this.serviceProvider), this.shutdownHandler::handleSaltRetrievalResponse, this.uidInstanceIdProvider);
UIDOperatorVerticle verticle = new UIDOperatorVerticle(configStore, config, this.clientSideTokenGenerate, siteProvider, clientKeyProvider, clientSideKeypairProvider, getKeyManager(), saltProvider, optOutStore, Clock.systemUTC(), _statsCollectorQueue, new SecureLinkValidatorService(this.serviceLinkProvider, this.serviceProvider), this.shutdownHandler::handleSaltRetrievalResponse, this.uidInstanceIdProvider, computeWorkerPool);
return verticle;
};

Expand Down Expand Up @@ -371,6 +376,9 @@ private void run() throws Exception {
})
.onFailure(t -> {
LOGGER.error("Failed to bootstrap operator: " + t.getMessage(), new Exception(t));
if (computeWorkerPool != null) {
computeWorkerPool.close();
}
vertx.close();
System.exit(1);
});
Expand Down Expand Up @@ -488,7 +496,7 @@ private static Vertx createVertx() {

MicrometerMetricsOptions metricOptions = new MicrometerMetricsOptions()
.setPrometheusOptions(prometheusOptions)
.setLabels(EnumSet.of(Label.HTTP_METHOD, Label.HTTP_CODE, Label.HTTP_PATH))
.setLabels(EnumSet.of(Label.HTTP_METHOD, Label.HTTP_CODE, Label.HTTP_PATH, Label.POOL_NAME))
.setJvmMetricsEnabled(true)
.setEnabled(true);
setupMetrics(metricOptions);
Expand All @@ -499,7 +507,8 @@ private static Vertx createVertx() {

VertxOptions vertxOptions = new VertxOptions()
.setMetricsOptions(metricOptions)
.setBlockedThreadCheckInterval(threadBlockedCheckInterval);
.setBlockedThreadCheckInterval(threadBlockedCheckInterval)
.setWorkerPoolSize(12);

return Vertx.vertx(vertxOptions);
}
Expand All @@ -524,6 +533,7 @@ private static void setupMetrics(MicrometerMetricsOptions metricOptions) {
Objects.equals(id.getTag(Label.HTTP_CODE.toString()), "404")))
.meterFilter(new MeterFilter() {
private final String httpServerResponseTime = MetricsDomain.HTTP_SERVER.getPrefix() + MetricsNaming.v4Names().getHttpResponseTime();
private final String poolQueueTime = MetricsDomain.NAMED_POOLS.getPrefix() + MetricsNaming.v4Names().getPoolQueueTime();

@Override
public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
Expand All @@ -533,6 +543,12 @@ public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticC
.build()
.merge(config);
}
if (id.getName().equals(poolQueueTime)) {
return DistributionStatisticConfig.builder()
.percentiles(0.50, 0.90, 0.95, 0.99)
.build()
.merge(config);
}
return config;
}
})
Expand Down
52 changes: 45 additions & 7 deletions src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.WorkerExecutor;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerResponse;
Expand Down Expand Up @@ -133,13 +134,16 @@ public class UIDOperatorVerticle extends AbstractVerticle {
public static final long OPT_OUT_CHECK_CUTOFF_DATE = Instant.parse("2023-09-01T00:00:00.00Z").getEpochSecond();
private final Handler<Boolean> saltRetrievalResponseHandler;
private final int allowClockSkewSeconds;
private final WorkerExecutor computeWorkerPool;
protected Map<Integer, Set<String>> siteIdToInvalidOriginsAndAppNames = new HashMap<>();
protected boolean keySharingEndpointProvideAppNames;
protected Instant lastInvalidOriginProcessTime = Instant.now();

private final int optOutStatusMaxRequestSize;
private final boolean optOutStatusApiEnabled;

private final boolean isAsyncBatchRequestsEnabled;

//"Android" is from https://github.com/IABTechLab/uid2-android-sdk/blob/ff93ebf597f5de7d440a84f7015a334ba4138ede/sdk/src/main/java/com/uid2/UID2Client.kt#L46
//"ios"/"tvos" is from https://github.com/IABTechLab/uid2-ios-sdk/blob/91c290d29a7093cfc209eca493d1fee80c17e16a/Sources/UID2/UID2Client.swift#L36-L38
private static final List<String> SUPPORTED_IN_APP = Arrays.asList("Android", "ios", "tvos");
Expand All @@ -164,7 +168,8 @@ public UIDOperatorVerticle(IConfigStore configStore,
IStatsCollectorQueue statsCollectorQueue,
SecureLinkValidatorService secureLinkValidatorService,
Handler<Boolean> saltRetrievalResponseHandler,
UidInstanceIdProvider uidInstanceIdProvider) {
UidInstanceIdProvider uidInstanceIdProvider,
WorkerExecutor computeWorkerPool) {
this.keyManager = keyManager;
this.secureLinkValidatorService = secureLinkValidatorService;
try {
Expand Down Expand Up @@ -198,6 +203,8 @@ public UIDOperatorVerticle(IConfigStore configStore,
this.identityV3Enabled = config.getBoolean(IdentityV3Prop, false);
this.disableOptoutToken = config.getBoolean(DisableOptoutTokenProp, false);
this.uidInstanceIdProvider = uidInstanceIdProvider;
this.computeWorkerPool = computeWorkerPool;
this.isAsyncBatchRequestsEnabled = config.getBoolean(EnableAsyncBatchRequestProp, false);
}

@Override
Expand Down Expand Up @@ -282,10 +289,6 @@ private void setUpEncryptedRoutes(Router mainRouter, BodyHandler bodyHandler) {
rc -> encryptedPayloadHandler.handleTokenRefresh(rc, this::handleTokenRefreshV2)));
mainRouter.post(V2_TOKEN_VALIDATE.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handle(rc, this::handleTokenValidateV2), Role.GENERATOR));
mainRouter.post(V2_IDENTITY_BUCKETS.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handle(rc, this::handleBucketsV2), Role.MAPPER));
mainRouter.post(V2_IDENTITY_MAP.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handle(rc, this::handleIdentityMapV2), Role.MAPPER));
mainRouter.post(V2_KEY_LATEST.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handle(rc, this::handleKeysRequestV2), Role.ID_READER));
mainRouter.post(V2_KEY_SHARING.toString()).handler(bodyHandler).handler(auth.handleV1(
Expand All @@ -303,8 +306,22 @@ private void setUpEncryptedRoutes(Router mainRouter, BodyHandler bodyHandler) {
if (this.clientSideTokenGenerate)
mainRouter.post(V2_TOKEN_CLIENTGENERATE.toString()).handler(bodyHandler).handler(this::handleClientSideTokenGenerate);

mainRouter.post(V3_IDENTITY_MAP.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handle(rc, this::handleIdentityMapV3), Role.MAPPER));
if (isAsyncBatchRequestsEnabled) {
LOGGER.info("Async batch requests enabled");
mainRouter.post(V2_IDENTITY_BUCKETS.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handleAsync(rc, this::handleBucketsV2Async), Role.MAPPER));
mainRouter.post(V2_IDENTITY_MAP.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handleAsync(rc, this::handleIdentityMapV2Async), Role.MAPPER));
mainRouter.post(V3_IDENTITY_MAP.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handleAsync(rc, this::handleIdentityMapV3Async), Role.MAPPER));
} else {
mainRouter.post(V2_IDENTITY_BUCKETS.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handle(rc, this::handleBucketsV2), Role.MAPPER));
mainRouter.post(V2_IDENTITY_MAP.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handle(rc, this::handleIdentityMapV2), Role.MAPPER));
mainRouter.post(V3_IDENTITY_MAP.toString()).handler(bodyHandler).handler(auth.handleV1(
rc -> encryptedPayloadHandler.handle(rc, this::handleIdentityMapV3), Role.MAPPER));
}
}

private void handleClientSideTokenGenerate(RoutingContext rc) {
Expand Down Expand Up @@ -1037,6 +1054,13 @@ private Future handleLogoutAsyncV2(RoutingContext rc) {
}
}

private Future<Void> handleBucketsV2Async(RoutingContext rc) {
return computeWorkerPool.executeBlocking(() -> {
handleBucketsV2(rc);
return null;
});
}

private void handleBucketsV2(RoutingContext rc) {
final JsonObject req = (JsonObject) rc.data().get("request");
final String qp = req.getString("since_timestamp");
Expand Down Expand Up @@ -1222,6 +1246,13 @@ private boolean validateServiceLink(RoutingContext rc) {
return false;
}

private Future<Void> handleIdentityMapV2Async(RoutingContext rc) {
return computeWorkerPool.executeBlocking(() -> {
handleIdentityMapV2(rc);
return null;
});
}

private void handleIdentityMapV2(RoutingContext rc) {
try {
final Integer siteId = RoutingContextUtil.getSiteId(rc);
Expand Down Expand Up @@ -1285,6 +1316,13 @@ private InputUtil.InputVal[] getIdentityMapV2Input(RoutingContext rc) {
getInputList.get();
}

private Future<Void> handleIdentityMapV3Async(RoutingContext rc) {
return computeWorkerPool.executeBlocking(() -> {
handleIdentityMapV3(rc);
return null;
});
}

private void handleIdentityMapV3(RoutingContext rc) {
try {
JsonObject jsonInput = (JsonObject) rc.data().get("request");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.uid2.shared.store.*;
import com.uid2.shared.store.salt.ISaltProvider;
import io.vertx.core.Handler;
import io.vertx.core.WorkerExecutor;
import io.vertx.core.json.JsonObject;

import java.time.Clock;
Expand All @@ -33,8 +34,9 @@ public ExtendedUIDOperatorVerticle(IConfigStore configStore,
IStatsCollectorQueue statsCollectorQueue,
SecureLinkValidatorService secureLinkValidationService,
Handler<Boolean> saltRetrievalResponseHandler,
UidInstanceIdProvider uidInstanceIdProvider) {
super(configStore, config, clientSideTokenGenerate, siteProvider, clientKeyProvider, clientSideKeypairProvider, keyManager, saltProvider, optOutStore, clock, statsCollectorQueue, secureLinkValidationService, saltRetrievalResponseHandler, uidInstanceIdProvider);
UidInstanceIdProvider uidInstanceIdProvider,
WorkerExecutor computeWorkerPool) {
super(configStore, config, clientSideTokenGenerate, siteProvider, clientKeyProvider, clientSideKeypairProvider, keyManager, saltProvider, optOutStore, clock, statsCollectorQueue, secureLinkValidationService, saltRetrievalResponseHandler, uidInstanceIdProvider, computeWorkerPool);
}

public IUIDOperatorService getIdService() {
Expand Down
8 changes: 7 additions & 1 deletion src/test/java/com/uid2/operator/UIDOperatorVerticleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.WorkerExecutor;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.json.JsonArray;
Expand Down Expand Up @@ -142,6 +143,7 @@ public class UIDOperatorVerticleTest {
private ExtendedUIDOperatorVerticle uidOperatorVerticle;
private RuntimeConfig runtimeConfig;
private EncryptedTokenEncoder encoder;
private WorkerExecutor computeWorkerPool;

@BeforeEach
void deployVerticle(Vertx vertx, VertxTestContext testContext, TestInfo testInfo) {
Expand All @@ -165,7 +167,8 @@ void deployVerticle(Vertx vertx, VertxTestContext testContext, TestInfo testInfo

this.uidInstanceIdProvider = new UidInstanceIdProvider("test-instance", "id");

this.uidOperatorVerticle = new ExtendedUIDOperatorVerticle(configStore, config, config.getBoolean("client_side_token_generate"), siteProvider, clientKeyProvider, clientSideKeypairProvider, new KeyManager(keysetKeyStore, keysetProvider), saltProvider, optOutStore, clock, statsCollectorQueue, secureLinkValidatorService, shutdownHandler::handleSaltRetrievalResponse, uidInstanceIdProvider);
this.computeWorkerPool = vertx.createSharedWorkerExecutor("compute", 4);
this.uidOperatorVerticle = new ExtendedUIDOperatorVerticle(configStore, config, config.getBoolean("client_side_token_generate"), siteProvider, clientKeyProvider, clientSideKeypairProvider, new KeyManager(keysetKeyStore, keysetProvider), saltProvider, optOutStore, clock, statsCollectorQueue, secureLinkValidatorService, shutdownHandler::handleSaltRetrievalResponse, uidInstanceIdProvider, this.computeWorkerPool);
vertx.deployVerticle(uidOperatorVerticle, testContext.succeeding(id -> testContext.completeNow()));

this.registry = new SimpleMeterRegistry();
Expand All @@ -177,6 +180,9 @@ void deployVerticle(Vertx vertx, VertxTestContext testContext, TestInfo testInfo
@AfterEach
void teardown() {
Metrics.globalRegistry.remove(registry);
if (computeWorkerPool != null) {
computeWorkerPool.close();
}
}

private RuntimeConfig setupRuntimeConfig(JsonObject config) {
Expand Down