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
49 changes: 20 additions & 29 deletions src/main/java/org/prebid/server/auction/ExchangeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.prebid.server.auction.model.BidderPrivacyResult;
import org.prebid.server.auction.model.BidderRequest;
import org.prebid.server.auction.model.BidderResponse;
import org.prebid.server.auction.model.EidPermissionHolder;
import org.prebid.server.auction.model.MultiBidConfig;
import org.prebid.server.auction.model.StoredResponseResult;
import org.prebid.server.auction.model.TimeoutContext;
Expand Down Expand Up @@ -86,7 +87,6 @@
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidBidderConfig;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidCache;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidData;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidMultiBid;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidSchain;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting;
Expand Down Expand Up @@ -129,7 +129,6 @@ public class ExchangeService {
private static final String ALL_BIDDERS_CONFIG = "*";
private static final Integer DEFAULT_MULTIBID_LIMIT_MIN = 1;
private static final Integer DEFAULT_MULTIBID_LIMIT_MAX = 9;
private static final String EID_ALLOWED_FOR_ALL_BIDDERS = "*";
private static final BigDecimal THOUSAND = BigDecimal.valueOf(1000);
private static final Set<String> BIDDER_FIELDS_EXCEPTION_LIST = Set.of(
"adunitcode", "storedrequest", "options", "is_rewarded_inventory");
Expand Down Expand Up @@ -538,9 +537,9 @@ private Future<List<AuctionParticipation>> makeAuctionParticipation(
final ExtRequest requestExt = bidRequest.getExt();
final ExtRequestPrebid prebid = requestExt != null ? requestExt.getPrebid() : null;
final Map<String, ExtBidderConfigOrtb> biddersToConfigs = getBiddersToConfigs(prebid);
final Map<String, List<String>> eidPermissions = getEidPermissions(prebid);
final EidPermissionHolder eidPermissionHolder = getEidPermissions(prebid);
final Map<String, Pair<User, Device>> bidderToUserAndDevice =
prepareUsersAndDevices(bidders, context, aliases, biddersToConfigs, eidPermissions);
prepareUsersAndDevices(bidders, context, aliases, biddersToConfigs, eidPermissionHolder);

return privacyEnforcementService.mask(context, bidderToUserAndDevice, aliases)
.map(bidderToPrivacyResult -> getAuctionParticipation(
Expand Down Expand Up @@ -578,14 +577,12 @@ private Map<String, ExtBidderConfigOrtb> getBiddersToConfigs(ExtRequestPrebid pr
return bidderToConfig;
}

private Map<String, List<String>> getEidPermissions(ExtRequestPrebid prebid) {
final ExtRequestPrebidData prebidData = prebid != null ? prebid.getData() : null;
final List<ExtRequestPrebidDataEidPermissions> eidPermissions = prebidData != null
? prebidData.getEidPermissions()
: null;
return CollectionUtils.emptyIfNull(eidPermissions).stream()
.collect(Collectors.toMap(ExtRequestPrebidDataEidPermissions::getSource,
ExtRequestPrebidDataEidPermissions::getBidders));
private EidPermissionHolder getEidPermissions(ExtRequestPrebid prebid) {
return Optional.ofNullable(prebid)
.map(ExtRequestPrebid::getData)
.map(ExtRequestPrebidData::getEidPermissions)
.map(EidPermissionHolder::of)
.orElse(EidPermissionHolder.empty());
}

private static List<String> firstPartyDataBidders(ExtRequest requestExt) {
Expand All @@ -594,11 +591,12 @@ private static List<String> firstPartyDataBidders(ExtRequest requestExt) {
return data == null ? null : data.getBidders();
}

private Map<String, Pair<User, Device>> prepareUsersAndDevices(List<String> bidders,
AuctionContext context,
BidderAliases aliases,
Map<String, ExtBidderConfigOrtb> biddersToConfigs,
Map<String, List<String>> eidPermissions) {
private Map<String, Pair<User, Device>> prepareUsersAndDevices(
List<String> bidders,
AuctionContext context,
BidderAliases aliases,
Map<String, ExtBidderConfigOrtb> biddersToConfigs,
EidPermissionHolder eidPermissionHolder) {

final BidRequest bidRequest = context.getBidRequest();
final List<String> firstPartyDataBidders = firstPartyDataBidders(bidRequest.getExt());
Expand All @@ -610,7 +608,7 @@ private Map<String, Pair<User, Device>> prepareUsersAndDevices(List<String> bidd
final boolean useFirstPartyData = firstPartyDataBidders == null || firstPartyDataBidders.stream()
.anyMatch(fpdBidder -> StringUtils.equalsIgnoreCase(fpdBidder, bidder));
final User preparedUser = prepareUser(
bidder, context, aliases, useFirstPartyData, fpdConfig, eidPermissions);
bidder, context, aliases, useFirstPartyData, fpdConfig, eidPermissionHolder);
final Device preparedDevice = prepareDevice(
bidRequest.getDevice(), fpdConfig, useFirstPartyData);
bidderToUserAndDevice.put(bidder, Pair.of(preparedUser, preparedDevice));
Expand All @@ -623,13 +621,13 @@ private User prepareUser(String bidder,
BidderAliases aliases,
boolean useFirstPartyData,
ExtBidderConfigOrtb fpdConfig,
Map<String, List<String>> eidPermissions) {
EidPermissionHolder eidPermissionHolder) {

final User user = context.getBidRequest().getUser();
final ExtUser extUser = user != null ? user.getExt() : null;
final UpdateResult<String> buyerUidUpdateResult = uidUpdater.updateUid(bidder, context, aliases);
final List<Eid> userEids = extractUserEids(user);
final List<Eid> allowedUserEids = resolveAllowedEids(userEids, bidder, eidPermissions);
final List<Eid> allowedUserEids = resolveAllowedEids(userEids, bidder, eidPermissionHolder);
final boolean shouldUpdateUserEids = allowedUserEids.size() != CollectionUtils.emptyIfNull(userEids).size();
final boolean shouldCleanExtPrebid = extUser != null && extUser.getPrebid() != null;
final boolean shouldCleanExtData = extUser != null && extUser.getData() != null && !useFirstPartyData;
Expand Down Expand Up @@ -669,20 +667,13 @@ private List<Eid> extractUserEids(User user) {
return user != null ? user.getEids() : null;
}

private List<Eid> resolveAllowedEids(List<Eid> userEids, String bidder, Map<String, List<String>> eidPermissions) {
private List<Eid> resolveAllowedEids(List<Eid> userEids, String bidder, EidPermissionHolder eidPermissionHolder) {
return CollectionUtils.emptyIfNull(userEids)
.stream()
.filter(userEid -> isUserEidAllowed(userEid.getSource(), eidPermissions, bidder))
.filter(userEid -> eidPermissionHolder.isAllowed(userEid, bidder))
.toList();
}
Comment on lines +670 to 675
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can also be moved to EidPermissionHolder.


private boolean isUserEidAllowed(String source, Map<String, List<String>> eidPermissions, String bidder) {
final List<String> allowedBidders = eidPermissions.get(source);
return CollectionUtils.isEmpty(allowedBidders) || allowedBidders.stream()
.anyMatch(allowedBidder -> StringUtils.equalsIgnoreCase(allowedBidder, bidder)
|| EID_ALLOWED_FOR_ALL_BIDDERS.equals(allowedBidder));
}

private List<AuctionParticipation> getAuctionParticipation(
List<BidderPrivacyResult> bidderPrivacyResults,
BidRequest bidRequest,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.prebid.server.auction.model;

import com.iab.openrtb.request.Eid;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class EidPermissionHolder {

private static final String WILDCARD_BIDDER = "*";

private static final ExtRequestPrebidDataEidPermissions DEFAULT_RULE = ExtRequestPrebidDataEidPermissions.builder()
.bidders(Collections.singletonList(WILDCARD_BIDDER))
.build();

private static final EidPermissionHolder EMPTY = new EidPermissionHolder(Collections.emptyList());

private final List<ExtRequestPrebidDataEidPermissions> eidPermissions;

private EidPermissionHolder(List<ExtRequestPrebidDataEidPermissions> eidPermissions) {
this.eidPermissions = new ArrayList<>(eidPermissions);
this.eidPermissions.add(DEFAULT_RULE);
}

public static EidPermissionHolder of(List<ExtRequestPrebidDataEidPermissions> eidPermissions) {
return new EidPermissionHolder(eidPermissions);
}

public static EidPermissionHolder empty() {
return EMPTY;
}

public boolean isAllowed(Eid eid, String bidder) {
final Map<Integer, List<ExtRequestPrebidDataEidPermissions>> matchingRulesBySpecificity = eidPermissions
.stream()
.filter(rule -> isRuleMatched(eid, rule))
.collect(Collectors.groupingBy(this::getRuleSpecificity));

final int highestSpecificityMatchingRules = Collections.max(matchingRulesBySpecificity.keySet());
return matchingRulesBySpecificity.get(highestSpecificityMatchingRules).stream()
.anyMatch(eidPermission -> isBidderAllowed(bidder, eidPermission.getBidders()));
}

private int getRuleSpecificity(ExtRequestPrebidDataEidPermissions eidPermission) {
return (int) Stream.of(eidPermission.getInserter(),
eidPermission.getSource(),
eidPermission.getMatcher(),
eidPermission.getMm())
.filter(Objects::nonNull)
.count();
}

private boolean isRuleMatched(Eid eid, ExtRequestPrebidDataEidPermissions eidPermission) {
return (eidPermission.getInserter() == null || eidPermission.getInserter().equals(eid.getInserter()))
&& (eidPermission.getSource() == null || eidPermission.getSource().equals(eid.getSource()))
&& (eidPermission.getMatcher() == null || eidPermission.getMatcher().equals(eid.getMatcher()))
&& (eidPermission.getMm() == null || eidPermission.getMm().equals(eid.getMm()));
Comment on lines +61 to +64
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add method nullOrEquals(T permission, T eid)

}

private boolean isBidderAllowed(String bidder, List<String> ruleBidders) {
return ruleBidders == null || ruleBidders.stream()
.anyMatch(allowedBidder -> StringUtils.equalsIgnoreCase(allowedBidder, bidder)
|| WILDCARD_BIDDER.equals(allowedBidder));
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
package org.prebid.server.proto.openrtb.ext.request;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Builder;
import lombok.Value;

import java.util.List;

/**
* Defines the contract for bidrequest.ext.prebid.data.eidPermissions
*/
@Value(staticConstructor = "of")
@Value
@Builder
public class ExtRequestPrebidDataEidPermissions {

/**
* Defines the contract for bidrequest.ext.prebid.data.eidPermissions.inserter
*/
String inserter;

/**
* Defines the contract for bidrequest.ext.prebid.data.eidPermissions.source
*/
String source;

/**
* Defines the contract for bidrequest.ext.prebid.data.eidPermissions.matcher
*/
String matcher;

/**
* Defines the contract for bidrequest.ext.prebid.data.eidPermissions.mm
*/
Integer mm;

/**
* Defines the contract for bidrequest.ext.prebid.data.eidPermissions.bidders
*/
@JsonFormat(without = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
List<String> bidders;

@Deprecated
public static ExtRequestPrebidDataEidPermissions of(String source, List<String> bidders) {
return new ExtRequestPrebidDataEidPermissions(null, source, null, null, bidders);
}
Comment on lines +39 to +42
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the idea to keep it

}
20 changes: 15 additions & 5 deletions src/main/java/org/prebid/server/validation/RequestValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ private void validateEidPermissions(List<ExtRequestPrebidDataEidPermissions> eid
boolean isDebugEnabled,
List<String> warnings) throws ValidationException {

if (eidPermissions == null) {
if (ObjectUtils.isEmpty(eidPermissions)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CollectionUtils

return;
}

Expand All @@ -385,14 +385,24 @@ private void validateEidPermissions(List<ExtRequestPrebidDataEidPermissions> eid
throw new ValidationException("request.ext.prebid.data.eidpermissions[i] can't be null");
}

validateEidPermissionSource(eidPermission.getSource());
validateEidPermissionCriteria(eidPermission.getInserter(),
eidPermission.getSource(),
eidPermission.getMatcher(),
eidPermission.getMm());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

            validateEidPermissionCriteria(
                    eidPermission.getInserter(),
                    eidPermission.getSource(),
                    eidPermission.getMatcher(),
                    eidPermission.getMm());

validateEidPermissionBidders(eidPermission.getBidders(), aliases, isDebugEnabled, warnings);
}
}

private void validateEidPermissionSource(String source) throws ValidationException {
if (StringUtils.isEmpty(source)) {
throw new ValidationException("Missing required value request.ext.prebid.data.eidPermissions[].source");
private void validateEidPermissionCriteria(String inserter,
String source,
String matcher,
Integer mm) throws ValidationException {
if (StringUtils.isEmpty(inserter)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add empty line after multi-line declaration

&& StringUtils.isEmpty(source)
&& StringUtils.isEmpty(matcher)
&& mm == null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about to use StringUtils.isAllEmpty?

throw new ValidationException("Missing required parameter(s) in request.ext.prebid.data.eidPermissions[]. "
+ "Either one or a combination of inserter, source, matcher, or mm should be defined.");
}
}

Expand Down
Loading
Loading