From 71b639a25d2e067f4adb23d16fe6d98f26a5c96a Mon Sep 17 00:00:00 2001 From: ilya Date: Tue, 9 Dec 2025 10:10:02 +0100 Subject: [PATCH] DATA-46374: LI EID permissions --- ...elIdentityProcessedAuctionRequestHook.java | 56 ++++++----- ...entityProcessedAuctionRequestHookTest.java | 97 +++++++++++++++++-- 2 files changed, 118 insertions(+), 35 deletions(-) diff --git a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java index 32b608ce21c..b08eb69d425 100644 --- a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java +++ b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java @@ -7,7 +7,6 @@ import com.iab.openrtb.request.User; import io.vertx.core.Future; import io.vertx.core.MultiMap; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.prebid.server.activity.Activity; import org.prebid.server.activity.ComponentType; @@ -40,18 +39,15 @@ import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions; import org.prebid.server.util.HttpUtil; import org.prebid.server.util.ListUtil; -import org.prebid.server.util.StreamUtil; import org.prebid.server.vertx.httpclient.HttpClient; import org.prebid.server.vertx.httpclient.model.HttpClientResponse; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; -import java.util.stream.Stream; public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { @@ -202,7 +198,7 @@ private AuctionRequestPayload updatedPayload(AuctionRequestPayload requestPayloa } private BidRequest updateAllowedBidders(BidRequest bidRequest, List resolvedEids) { - if (targetBidders.isEmpty()) { + if (targetBidders.isEmpty() || resolvedEids == null || resolvedEids.isEmpty()) { return bidRequest; } @@ -210,6 +206,14 @@ private BidRequest updateAllowedBidders(BidRequest bidRequest, List resolve final ExtRequestPrebid extPrebid = ext != null ? ext.getPrebid() : null; final ExtRequestPrebidData extPrebidData = extPrebid != null ? extPrebid.getData() : null; + final List existingPerms = extPrebidData != null + ? ListUtils.emptyIfNull(extPrebidData.getEidPermissions()) + : List.of(); + + if (existingPerms.isEmpty()) { + return bidRequest; + } + final ExtRequestPrebid updatedExtPrebid = Optional.ofNullable(extPrebid) .map(ExtRequestPrebid::toBuilder) .orElseGet(ExtRequestPrebid::builder) @@ -225,35 +229,37 @@ private BidRequest updateAllowedBidders(BidRequest bidRequest, List resolve } private ExtRequestPrebidData updatePrebidData(ExtRequestPrebidData extPrebidData, List resolvedEids) { - final List prebidDataBidders = extPrebidData != null ? extPrebidData.getBidders() : null; - final List updatedPrebidDataBidders = prebidDataBidders != null - ? (List) CollectionUtils.union(targetBidders, prebidDataBidders) - : targetBidders; + final List originalBidders = extPrebidData != null ? extPrebidData.getBidders() : null; - final Set resolvedSources = resolvedEids.stream().map(Eid::getSource).collect(Collectors.toSet()); + final Set resolvedSources = resolvedEids.stream() + .map(Eid::getSource) + .collect(Collectors.toSet()); final List initialPermissions = Optional.ofNullable(extPrebidData) .map(ExtRequestPrebidData::getEidPermissions) - .orElse(Collections.emptyList()); - final List updatedPermissions = Stream.concat( - initialPermissions.stream() - .map(permission -> updateEidPermission(permission, resolvedSources)), - resolvedSources.stream() - .map(source -> ExtRequestPrebidDataEidPermissions.of(source, targetBidders))) - .filter(StreamUtil.distinctBy(ExtRequestPrebidDataEidPermissions::getSource)) + .orElse(List.of()); + + final List updatedPermissions = initialPermissions.stream() + .map(permission -> restrictEidPermission(permission, resolvedSources)) + .filter(Objects::nonNull) .toList(); - return ExtRequestPrebidData.of(updatedPrebidDataBidders, updatedPermissions); + return ExtRequestPrebidData.of(originalBidders, updatedPermissions); } - private ExtRequestPrebidDataEidPermissions updateEidPermission(ExtRequestPrebidDataEidPermissions permission, - Set resolvedSources) { + private ExtRequestPrebidDataEidPermissions restrictEidPermission(ExtRequestPrebidDataEidPermissions permission, + Set resolvedSources) { + + if (!resolvedSources.contains(permission.getSource())) { + return permission; + } + + final List current = ListUtils.emptyIfNull(permission.getBidders()); + final List finalBidders = ListUtils.intersection(current, targetBidders); - return resolvedSources.contains(permission.getSource()) - ? ExtRequestPrebidDataEidPermissions.of( - permission.getSource(), - (List) CollectionUtils.union(permission.getBidders(), targetBidders)) - : permission; + return finalBidders.isEmpty() + ? null + : ExtRequestPrebidDataEidPermissions.of(permission.getSource(), finalBidders); } @Override diff --git a/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java b/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java index 7b05ec6072d..89397dda83b 100644 --- a/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java +++ b/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java @@ -375,14 +375,33 @@ public void callShouldReturnFailureWhenRequestingEidsIsFailed() { } @Test - public void biddersConfiguredRestrictionShouldBeRespected() { + public void shouldRestrictExistingEidPermissionsByIntersectionAndKeepGlobalBiddersUnchanged() { + // given final Uid givenUid = Uid.builder().id("id1").atype(2).build(); final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build(); final User givenUser = User.builder().eids(singletonList(givenEid)).build(); - final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).build(); - final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(configuredBidders, List.of( - ExtRequestPrebidDataEidPermissions.of("liveintent.com", configuredBidders))); + // existing global bidders and eid permissions including liveintent.com + final ExtRequestPrebidData givenData = ExtRequestPrebidData.of( + List.of("bidderX"), + List.of( + ExtRequestPrebidDataEidPermissions.of("some.other-source.com", List.of("bidderY")), + ExtRequestPrebidDataEidPermissions.of("liveintent.com", List.of("bidder2", "bidder3")) + )); + + final BidRequest givenBidRequest = BidRequest.builder() + .id("request") + .user(givenUser) + .ext(ExtRequest.of(ExtRequestPrebid.builder().data(givenData).build())) + .build(); + + // expected: global bidders unchanged, liveintent.com bidders intersected with configuredBidders => [bidder2] + final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of( + List.of("bidderX"), + List.of( + ExtRequestPrebidDataEidPermissions.of("some.other-source.com", List.of("bidderY")), + ExtRequestPrebidDataEidPermissions.of("liveintent.com", List.of("bidder2")) + )); final Eid expectedEid = Eid.builder().source("liveintent.com").build(); @@ -418,7 +437,7 @@ public void biddersConfiguredRestrictionShouldBeRespected() { } @Test - public void biddersConfiguredRestrictionShouldBeMergedWithProvided() { + public void shouldNotAddNewEidPermissionsOrModifyGlobalBiddersWhenSourceNotPresent() { // given final Uid givenUid = Uid.builder().id("id1").atype(2).build(); final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build(); @@ -429,12 +448,10 @@ public void biddersConfiguredRestrictionShouldBeMergedWithProvided() { ExtRequestPrebidDataEidPermissions.of("some.source.com", List.of("bidder3")))) ).build())).build(); - final List expectedBidders = List.of("bidder3", "bidder2", "bidder1"); - - final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(expectedBidders, List.of( + // expected: unchanged, because there is no existing permission for liveintent.com + final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(List.of("bidder3"), List.of( ExtRequestPrebidDataEidPermissions.of("some.other-source.com", List.of("bidder3")), - ExtRequestPrebidDataEidPermissions.of("some.source.com", List.of("bidder3")), - ExtRequestPrebidDataEidPermissions.of("liveintent.com", configuredBidders))); + ExtRequestPrebidDataEidPermissions.of("some.source.com", List.of("bidder3")))); final Eid expectedEid = Eid.builder().source("liveintent.com").build(); @@ -468,4 +485,64 @@ public void biddersConfiguredRestrictionShouldBeMergedWithProvided() { eq(MAPPER.encodeToString(givenBidRequest)), eq(5L)); } + + @Test + public void shouldRemovePermissionWhenIntersectionIsEmpty() { + // given + final Uid givenUid = Uid.builder().id("id1").atype(2).build(); + final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build(); + final User givenUser = User.builder().eids(singletonList(givenEid)).build(); + + final ExtRequestPrebidData givenData = ExtRequestPrebidData.of( + List.of("bidderGlobal"), + List.of( + ExtRequestPrebidDataEidPermissions.of("liveintent.com", List.of("not-allowed")), + ExtRequestPrebidDataEidPermissions.of("keep.com", List.of("bidderGlobal")) + )); + + final BidRequest givenBidRequest = BidRequest.builder() + .id("request") + .user(givenUser) + .ext(ExtRequest.of(ExtRequestPrebid.builder().data(givenData).build())) + .build(); + + // Respond with liveintent.com so that restriction is applied and becomes empty -> remove entry + final Eid expectedEid = Eid.builder().source("liveintent.com").build(); + final String responseBody = MAPPER.encodeToString(IdResResponse.of(List.of(expectedEid))); + given(httpClient.post(any(), any(), any(), anyLong())) + .willReturn(Future.succeededFuture(HttpClientResponse.of(200, null, responseBody))); + + given(auctionInvocationContext.auctionContext()).willReturn(auctionContext); + given(auctionContext.getActivityInfrastructure()).willReturn(activityInfrastructure); + given(activityInfrastructure.isAllowed(any(), any())).willReturn(true); + given(userFpdActivityMask.maskUser(any(), eq(false), eq(false))) + .willAnswer(invocation -> invocation.getArgument(0)); + given(userFpdActivityMask.maskDevice(any(), eq(false), eq(false))) + .willAnswer(invocation -> invocation.getArgument(0)); + + // when + final InvocationResult result = + target.call(AuctionRequestPayloadImpl.of(givenBidRequest), auctionInvocationContext).result(); + + // then + final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of( + List.of("bidderGlobal"), + List.of( + ExtRequestPrebidDataEidPermissions.of("keep.com", List.of("bidderGlobal")) + )); + + assertThat(result.status()).isEqualTo(InvocationStatus.success); + assertThat(result.payloadUpdate().apply(AuctionRequestPayloadImpl.of(givenBidRequest))) + .extracting(AuctionRequestPayload::bidRequest) + .extracting(BidRequest::getExt) + .extracting(ExtRequest::getPrebid) + .extracting(ExtRequestPrebid::getData) + .isEqualTo(expectedData); + + verify(httpClient).post( + eq("https://test.com/idres"), + argThat(headers -> headers.contains("Authorization", "Bearer auth_token", true)), + eq(MAPPER.encodeToString(givenBidRequest)), + eq(5L)); + } }