Skip to content

Commit 59fba6a

Browse files
committed
Fixed validation.
1 parent 295d988 commit 59fba6a

File tree

4 files changed

+67
-49
lines changed

4 files changed

+67
-49
lines changed

src/main/java/org/prebid/server/cookie/CookieSyncService.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.prebid.server.spring.config.bidder.model.usersync.CookieFamilySource;
4444
import org.prebid.server.util.HttpUtil;
4545
import org.prebid.server.util.ObjectUtil;
46+
import org.prebid.server.util.StreamUtil;
4647

4748
import java.util.ArrayList;
4849
import java.util.Collection;
@@ -54,7 +55,6 @@
5455
import java.util.Map;
5556
import java.util.Objects;
5657
import java.util.Set;
57-
import java.util.function.Function;
5858
import java.util.function.Predicate;
5959
import java.util.stream.Collectors;
6060

@@ -385,16 +385,11 @@ private static Set<String> allowedBiddersByPriority(CookieSyncContext cookieSync
385385

386386
private List<BidderUsersyncStatus> validStatuses(Set<String> biddersToSync, CookieSyncContext cookieSyncContext) {
387387
return biddersToSync.stream()
388-
.filter(distinctBy(bidder -> bidderCatalog.cookieFamilyName(bidder).orElseThrow()))
388+
.filter(StreamUtil.distinctBy(bidder -> bidderCatalog.cookieFamilyName(bidder).orElseThrow()))
389389
.map(bidder -> validStatus(bidder, cookieSyncContext))
390390
.toList();
391391
}
392392

393-
private static <T> Predicate<T> distinctBy(Function<? super T, ?> keyExtractor) {
394-
final Set<Object> seen = new HashSet<>();
395-
return value -> seen.add(keyExtractor.apply(value));
396-
}
397-
398393
private BidderUsersyncStatus validStatus(String bidder, CookieSyncContext cookieSyncContext) {
399394
final BiddersContext biddersContext = cookieSyncContext.getBiddersContext();
400395
final RoutingContext routingContext = cookieSyncContext.getRoutingContext();

src/main/java/org/prebid/server/handler/SetuidHandler.java

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import io.vertx.core.http.HttpServerRequest;
1111
import io.vertx.core.http.HttpServerResponse;
1212
import io.vertx.ext.web.RoutingContext;
13-
import org.apache.commons.collections4.CollectionUtils;
1413
import org.apache.commons.lang3.BooleanUtils;
1514
import org.apache.commons.lang3.ObjectUtils;
1615
import org.apache.commons.lang3.StringUtils;
@@ -55,6 +54,7 @@
5554
import org.prebid.server.settings.model.AccountGdprConfig;
5655
import org.prebid.server.settings.model.AccountPrivacyConfig;
5756
import org.prebid.server.util.HttpUtil;
57+
import org.prebid.server.util.StreamUtil;
5858
import org.prebid.server.vertx.verticles.server.HttpEndpoint;
5959
import org.prebid.server.vertx.verticles.server.application.ApplicationResource;
6060

@@ -65,9 +65,8 @@
6565
import java.util.Optional;
6666
import java.util.function.Consumer;
6767
import java.util.function.Function;
68-
import java.util.function.Supplier;
68+
import java.util.function.Predicate;
6969
import java.util.stream.Collectors;
70-
import java.util.stream.Stream;
7170

7271
public class SetuidHandler implements ApplicationResource {
7372

@@ -113,48 +112,54 @@ public SetuidHandler(long defaultTimeout,
113112
this.analyticsDelegator = Objects.requireNonNull(analyticsDelegator);
114113
this.metrics = Objects.requireNonNull(metrics);
115114
this.timeoutFactory = Objects.requireNonNull(timeoutFactory);
116-
this.cookieNameToBidderAndSyncType = collectMap(bidderCatalog);
115+
this.cookieNameToBidderAndSyncType = collectUsersyncers(bidderCatalog);
117116
}
118117

119-
private static Map<String, Pair<String, UsersyncMethodType>> collectMap(BidderCatalog bidderCatalog) {
118+
private static Map<String, Pair<String, UsersyncMethodType>> collectUsersyncers(BidderCatalog bidderCatalog) {
119+
validateUsersyncersDuplicates(bidderCatalog);
120120

121-
final Supplier<Stream<Pair<String, Usersyncer>>> usersyncers = () -> bidderCatalog.names()
122-
.stream()
123-
.filter(bidderCatalog::isActive)
124-
.filter(bidderName -> bidderCatalog.resolveBaseBidder(bidderName).equals(bidderName))
121+
return bidderCatalog.usersyncReadyBidders().stream()
122+
.filter(bidderName -> !isAliasWithRootCookieFamilyName(bidderCatalog, bidderName))
123+
.filter(StreamUtil.distinctBy(bidderCatalog::cookieFamilyName))
125124
.map(bidderName -> bidderCatalog.usersyncerByName(bidderName)
126125
.map(usersyncer -> Pair.of(bidderName, usersyncer)))
127-
.flatMap(Optional::stream);
128-
129-
validateUsersyncers(usersyncers.get().map(Pair::getRight));
130-
131-
return usersyncers.get()
126+
.flatMap(Optional::stream)
132127
.collect(Collectors.toMap(
133128
pair -> pair.getRight().getCookieFamilyName(),
134129
pair -> Pair.of(pair.getLeft(), preferredUserSyncType(pair.getRight()))));
135130
}
136131

137-
private static UsersyncMethodType preferredUserSyncType(Usersyncer usersyncer) {
138-
// when usersyncer is present, it will contain at least one method
139-
return ObjectUtils.firstNonNull(usersyncer.getIframe(), usersyncer.getRedirect()).getType();
140-
}
141-
142-
private static void validateUsersyncers(Stream<Usersyncer> usersyncers) {
143-
final List<String> cookieFamilyNameDuplicates = usersyncers.map(Usersyncer::getCookieFamilyName)
144-
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
145-
.entrySet()
146-
.stream()
147-
.filter(name -> name.getValue() > 1)
148-
.map(Map.Entry::getKey)
132+
private static void validateUsersyncersDuplicates(BidderCatalog bidderCatalog) {
133+
final List<String> duplicatedCookieFamilyNames = bidderCatalog.usersyncReadyBidders().stream()
134+
.filter(bidderName -> !isAliasWithRootCookieFamilyName(bidderCatalog, bidderName))
135+
.map(bidderCatalog::usersyncerByName)
136+
.flatMap(Optional::stream)
137+
.map(Usersyncer::getCookieFamilyName)
138+
.filter(Predicate.not(StreamUtil.distinctBy(Function.identity())))
149139
.distinct()
150140
.toList();
151-
if (CollectionUtils.isNotEmpty(cookieFamilyNameDuplicates)) {
141+
142+
if (!duplicatedCookieFamilyNames.isEmpty()) {
152143
throw new IllegalArgumentException(
153144
"Duplicated \"cookie-family-name\" found, values: "
154-
+ String.join(", ", cookieFamilyNameDuplicates));
145+
+ String.join(", ", duplicatedCookieFamilyNames));
155146
}
156147
}
157148

149+
private static boolean isAliasWithRootCookieFamilyName(BidderCatalog bidderCatalog, String bidder) {
150+
final String bidderCookieFamilyName = bidderCatalog.cookieFamilyName(bidder).orElse(StringUtils.EMPTY);
151+
final String parentCookieFamilyName =
152+
bidderCatalog.cookieFamilyName(bidderCatalog.resolveBaseBidder(bidder)).orElse(null);
153+
154+
return bidderCatalog.isAlias(bidder)
155+
&& parentCookieFamilyName != null
156+
&& parentCookieFamilyName.equals(bidderCookieFamilyName);
157+
}
158+
159+
private static UsersyncMethodType preferredUserSyncType(Usersyncer usersyncer) {
160+
return ObjectUtils.firstNonNull(usersyncer.getIframe(), usersyncer.getRedirect()).getType();
161+
}
162+
158163
@Override
159164
public List<HttpEndpoint> endpoints() {
160165
return Collections.singletonList(HttpEndpoint.of(HttpMethod.GET, Endpoint.setuid.value()));
@@ -232,9 +237,9 @@ private void handleSetuidContextResult(AsyncResult<SetuidContext> setuidContextR
232237
final String bidderName = cookieNameToBidderAndSyncType.get(bidderCookieFamily).getLeft();
233238

234239
Future.all(
235-
tcfDefinerService.isAllowedForHostVendorId(tcfContext),
236-
tcfDefinerService.resultForBidderNames(
237-
Collections.singleton(bidderName), tcfContext, accountGdprConfig))
240+
tcfDefinerService.isAllowedForHostVendorId(tcfContext),
241+
tcfDefinerService.resultForBidderNames(
242+
Collections.singleton(bidderName), tcfContext, accountGdprConfig))
238243
.onComplete(hostTcfResponseResult -> respondByTcfResponse(
239244
hostTcfResponseResult,
240245
bidderName,

src/main/java/org/prebid/server/util/StreamUtil.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package org.prebid.server.util;
22

3+
import java.util.HashSet;
34
import java.util.Iterator;
5+
import java.util.Set;
46
import java.util.Spliterator;
7+
import java.util.function.Function;
8+
import java.util.function.Predicate;
59
import java.util.stream.Stream;
610
import java.util.stream.StreamSupport;
711

@@ -17,4 +21,9 @@ public static <T> Stream<T> asStream(Spliterator<T> spliterator) {
1721
public static <T> Stream<T> asStream(Iterator<T> iterator) {
1822
return StreamSupport.stream(IterableUtil.iterable(iterator).spliterator(), false);
1923
}
24+
25+
public static <T> Predicate<T> distinctBy(Function<? super T, ?> keyExtractor) {
26+
final Set<Object> seen = new HashSet<>();
27+
return value -> seen.add(keyExtractor.apply(value));
28+
}
2029
}

src/test/java/org/prebid/server/handler/SetuidHandlerTest.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,10 @@
5252
import java.time.Instant;
5353
import java.time.ZoneId;
5454
import java.util.Base64;
55-
import java.util.HashSet;
5655
import java.util.Map;
5756
import java.util.Optional;
57+
import java.util.Set;
5858

59-
import static java.util.Arrays.asList;
6059
import static java.util.Collections.emptyMap;
6160
import static java.util.Collections.singleton;
6261
import static java.util.Collections.singletonMap;
@@ -83,7 +82,7 @@ public class SetuidHandlerTest extends VertxTest {
8382
private UidsCookieService uidsCookieService;
8483
@Mock(strictness = LENIENT)
8584
private ApplicationSettings applicationSettings;
86-
@Mock
85+
@Mock(strictness = LENIENT)
8786
private BidderCatalog bidderCatalog;
8887
@Mock(strictness = LENIENT)
8988
private SetuidPrivacyContextFactory setuidPrivacyContextFactory;
@@ -142,16 +141,20 @@ public void setUp() {
142141

143142
given(uidsCookieService.toCookie(any())).willReturn(Cookie.cookie("test", "test"));
144143

145-
given(bidderCatalog.names()).willReturn(new HashSet<>(asList(RUBICON, FACEBOOK, APPNEXUS)));
146-
given(bidderCatalog.isActive(any())).willReturn(true);
147-
given(bidderCatalog.resolveBaseBidder(any())).willAnswer(invocation -> invocation.getArgument(0));
144+
given(bidderCatalog.usersyncReadyBidders()).willReturn(Set.of(RUBICON, FACEBOOK, APPNEXUS));
145+
given(bidderCatalog.isAlias(any())).willReturn(false);
148146

149147
given(bidderCatalog.usersyncerByName(eq(RUBICON))).willReturn(
150148
Optional.of(Usersyncer.of(RUBICON, null, redirectMethod())));
149+
given(bidderCatalog.cookieFamilyName(eq(RUBICON))).willReturn(Optional.of(RUBICON));
150+
151151
given(bidderCatalog.usersyncerByName(eq(FACEBOOK))).willReturn(
152152
Optional.of(Usersyncer.of(FACEBOOK, null, redirectMethod())));
153+
given(bidderCatalog.cookieFamilyName(eq(FACEBOOK))).willReturn(Optional.of(FACEBOOK));
154+
153155
given(bidderCatalog.usersyncerByName(eq(APPNEXUS))).willReturn(
154156
Optional.of(Usersyncer.of(ADNXS, null, redirectMethod())));
157+
given(bidderCatalog.cookieFamilyName(eq(APPNEXUS))).willReturn(Optional.of(ADNXS));
155158

156159
given(activityInfrastructure.isAllowed(any(), any()))
157160
.willReturn(true);
@@ -533,7 +536,7 @@ public void shouldSendEmptyResponseWhenFParamIsEqualToBWhenTypeIsRedirect() {
533536
given(httpRequest.getParam("bidder")).willReturn(RUBICON);
534537
given(httpRequest.getParam("f")).willReturn("b");
535538
given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT");
536-
given(bidderCatalog.names()).willReturn(singleton(RUBICON));
539+
given(bidderCatalog.usersyncReadyBidders()).willReturn(singleton(RUBICON));
537540
given(bidderCatalog.usersyncerByName(any()))
538541
.willReturn(Optional.of(Usersyncer.of(RUBICON, null, redirectMethod())));
539542

@@ -619,7 +622,7 @@ public void shouldSendPixelWhenFParamNotDefinedAndTypeIsRedirect() {
619622
given(uidsCookieService.toCookie(any())).willReturn(Cookie
620623
.cookie("uids", "eyJ0ZW1wVUlEcyI6eyJydWJpY29uIjp7InVpZCI6Iko1VkxDV1FQLTI2LUNXRlQifX19"));
621624
given(httpRequest.getParam("bidder")).willReturn(RUBICON);
622-
given(bidderCatalog.names()).willReturn(singleton(RUBICON));
625+
given(bidderCatalog.usersyncReadyBidders()).willReturn(singleton(RUBICON));
623626
given(bidderCatalog.usersyncerByName(any()))
624627
.willReturn(Optional.of(Usersyncer.of(RUBICON, null, redirectMethod())));
625628
given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT");
@@ -809,17 +812,23 @@ public void shouldPassSuccessfulEventToAnalyticsReporter() {
809812
}
810813

811814
@Test
812-
public void shouldThrowExceptionInCaseOfCookieFamilyNameDuplicates() {
815+
public void shouldThrowExceptionInCaseOfBaseBidderCookieFamilyNameDuplicates() {
813816
// given
814817
final Clock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
815818
final String firstDuplicateName = "firstBidderWithDuplicate";
816819
final String secondDuplicateName = "secondBidderWithDuplicate";
817-
given(bidderCatalog.names())
818-
.willReturn(new HashSet<>(asList(RUBICON, FACEBOOK, firstDuplicateName, secondDuplicateName)));
820+
final String thirdDuplicateName = "thirdDuplicateName";
821+
822+
given(bidderCatalog.usersyncReadyBidders())
823+
.willReturn(Set.of(RUBICON, FACEBOOK, firstDuplicateName, secondDuplicateName, thirdDuplicateName));
824+
given(bidderCatalog.isAlias(thirdDuplicateName)).willReturn(true);
819825
given(bidderCatalog.usersyncerByName(eq(firstDuplicateName))).willReturn(
820826
Optional.of(Usersyncer.of(RUBICON, iframeMethod(), redirectMethod())));
821827
given(bidderCatalog.usersyncerByName(eq(secondDuplicateName))).willReturn(
822828
Optional.of(Usersyncer.of(FACEBOOK, iframeMethod(), redirectMethod())));
829+
given(bidderCatalog.usersyncerByName(eq(thirdDuplicateName))).willReturn(
830+
Optional.of(Usersyncer.of(FACEBOOK, iframeMethod(), redirectMethod())));
831+
823832
final Executable exceptionSource = () -> new SetuidHandler(
824833
2000,
825834
uidsCookieService,

0 commit comments

Comments
 (0)