diff --git a/src/main/java/org/prebid/server/auction/EidPermissionResolver.java b/src/main/java/org/prebid/server/auction/EidPermissionResolver.java new file mode 100644 index 00000000000..6a19fd7c82b --- /dev/null +++ b/src/main/java/org/prebid/server/auction/EidPermissionResolver.java @@ -0,0 +1,81 @@ +package org.prebid.server.auction; + +import com.iab.openrtb.request.Eid; +import org.apache.commons.collections4.CollectionUtils; +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 class EidPermissionResolver { + + private static final String WILDCARD_BIDDER = "*"; + + private static final ExtRequestPrebidDataEidPermissions DEFAULT_RULE = ExtRequestPrebidDataEidPermissions.builder() + .bidders(Collections.singletonList(WILDCARD_BIDDER)) + .build(); + + private static final EidPermissionResolver EMPTY = new EidPermissionResolver(Collections.emptyList()); + + private final List eidPermissions; + + private EidPermissionResolver(List eidPermissions) { + this.eidPermissions = new ArrayList<>(eidPermissions); + this.eidPermissions.add(DEFAULT_RULE); + } + + public static EidPermissionResolver of(List eidPermissions) { + return new EidPermissionResolver(eidPermissions); + } + + public static EidPermissionResolver empty() { + return EMPTY; + } + + public List resolveAllowedEids(List userEids, String bidder) { + return CollectionUtils.emptyIfNull(userEids) + .stream() + .filter(userEid -> isAllowed(userEid, bidder)) + .toList(); + } + + private boolean isAllowed(Eid eid, String bidder) { + final Map> 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())); + } + + private boolean isBidderAllowed(String bidder, List ruleBidders) { + return ruleBidders == null || ruleBidders.stream() + .anyMatch(allowedBidder -> StringUtils.equalsIgnoreCase(allowedBidder, bidder) + || WILDCARD_BIDDER.equals(allowedBidder)); + } +} diff --git a/src/main/java/org/prebid/server/auction/ExchangeService.java b/src/main/java/org/prebid/server/auction/ExchangeService.java index c4ae087d205..428510be069 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -86,7 +86,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; @@ -129,7 +128,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 BIDDER_FIELDS_EXCEPTION_LIST = Set.of( "adunitcode", "storedrequest", "options", "is_rewarded_inventory"); @@ -538,9 +536,9 @@ private Future> makeAuctionParticipation( final ExtRequest requestExt = bidRequest.getExt(); final ExtRequestPrebid prebid = requestExt != null ? requestExt.getPrebid() : null; final Map biddersToConfigs = getBiddersToConfigs(prebid); - final Map> eidPermissions = getEidPermissions(prebid); + final EidPermissionResolver eidPermissionResolver = getEidPermissions(prebid); final Map> bidderToUserAndDevice = - prepareUsersAndDevices(bidders, context, aliases, biddersToConfigs, eidPermissions); + prepareUsersAndDevices(bidders, context, aliases, biddersToConfigs, eidPermissionResolver); return privacyEnforcementService.mask(context, bidderToUserAndDevice, aliases) .map(bidderToPrivacyResult -> getAuctionParticipation( @@ -578,14 +576,12 @@ private Map getBiddersToConfigs(ExtRequestPrebid pr return bidderToConfig; } - private Map> getEidPermissions(ExtRequestPrebid prebid) { - final ExtRequestPrebidData prebidData = prebid != null ? prebid.getData() : null; - final List eidPermissions = prebidData != null - ? prebidData.getEidPermissions() - : null; - return CollectionUtils.emptyIfNull(eidPermissions).stream() - .collect(Collectors.toMap(ExtRequestPrebidDataEidPermissions::getSource, - ExtRequestPrebidDataEidPermissions::getBidders)); + private EidPermissionResolver getEidPermissions(ExtRequestPrebid prebid) { + return Optional.ofNullable(prebid) + .map(ExtRequestPrebid::getData) + .map(ExtRequestPrebidData::getEidPermissions) + .map(EidPermissionResolver::of) + .orElse(EidPermissionResolver.empty()); } private static List firstPartyDataBidders(ExtRequest requestExt) { @@ -594,11 +590,12 @@ private static List firstPartyDataBidders(ExtRequest requestExt) { return data == null ? null : data.getBidders(); } - private Map> prepareUsersAndDevices(List bidders, - AuctionContext context, - BidderAliases aliases, - Map biddersToConfigs, - Map> eidPermissions) { + private Map> prepareUsersAndDevices( + List bidders, + AuctionContext context, + BidderAliases aliases, + Map biddersToConfigs, + EidPermissionResolver eidPermissionResolver) { final BidRequest bidRequest = context.getBidRequest(); final List firstPartyDataBidders = firstPartyDataBidders(bidRequest.getExt()); @@ -610,7 +607,7 @@ private Map> prepareUsersAndDevices(List 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, eidPermissionResolver); final Device preparedDevice = prepareDevice( bidRequest.getDevice(), fpdConfig, useFirstPartyData); bidderToUserAndDevice.put(bidder, Pair.of(preparedUser, preparedDevice)); @@ -623,13 +620,13 @@ private User prepareUser(String bidder, BidderAliases aliases, boolean useFirstPartyData, ExtBidderConfigOrtb fpdConfig, - Map> eidPermissions) { + EidPermissionResolver eidPermissionResolver) { final User user = context.getBidRequest().getUser(); final ExtUser extUser = user != null ? user.getExt() : null; final UpdateResult buyerUidUpdateResult = uidUpdater.updateUid(bidder, context, aliases); final List userEids = extractUserEids(user); - final List allowedUserEids = resolveAllowedEids(userEids, bidder, eidPermissions); + final List allowedUserEids = eidPermissionResolver.resolveAllowedEids(userEids, bidder); 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; @@ -669,20 +666,6 @@ private List extractUserEids(User user) { return user != null ? user.getEids() : null; } - private List resolveAllowedEids(List userEids, String bidder, Map> eidPermissions) { - return CollectionUtils.emptyIfNull(userEids) - .stream() - .filter(userEid -> isUserEidAllowed(userEid.getSource(), eidPermissions, bidder)) - .toList(); - } - - private boolean isUserEidAllowed(String source, Map> eidPermissions, String bidder) { - final List 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 getAuctionParticipation( List bidderPrivacyResults, BidRequest bidRequest, diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebidDataEidPermissions.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebidDataEidPermissions.java index 82b2e6007a1..2373c40e886 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebidDataEidPermissions.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebidDataEidPermissions.java @@ -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 bidders; + + @Deprecated + public static ExtRequestPrebidDataEidPermissions of(String source, List bidders) { + return new ExtRequestPrebidDataEidPermissions(null, source, null, null, bidders); + } } diff --git a/src/main/java/org/prebid/server/validation/RequestValidator.java b/src/main/java/org/prebid/server/validation/RequestValidator.java index d4b9fb06726..958c59d3a5a 100644 --- a/src/main/java/org/prebid/server/validation/RequestValidator.java +++ b/src/main/java/org/prebid/server/validation/RequestValidator.java @@ -376,7 +376,7 @@ private void validateEidPermissions(List eid boolean isDebugEnabled, List warnings) throws ValidationException { - if (eidPermissions == null) { + if (CollectionUtils.isEmpty(eidPermissions)) { return; } @@ -385,14 +385,24 @@ private void validateEidPermissions(List 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()); + 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.isAllEmpty(inserter, source, matcher) && mm == null) { + 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."); } } diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Eid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Eid.groovy index 2cb344f6967..25e14e62126 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Eid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Eid.groovy @@ -25,4 +25,14 @@ class Eid { it.matchMethod = PBSUtils.randomNumber } } + + static Eid from(EidPermission eidPermission, List uids = [Uid.defaultUid]) { + new Eid().tap { + it.source = eidPermission.source + it.uids = uids + it.inserter = eidPermission.inserter + it.matcher = eidPermission.matcher + it.matchMethod = eidPermission.matchMethod + } + } } diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/EidPermission.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/EidPermission.groovy index df77f8f1fbe..5807c6f9241 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/EidPermission.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/EidPermission.groovy @@ -1,11 +1,39 @@ package org.prebid.server.functional.model.request.auction +import com.fasterxml.jackson.annotation.JsonProperty import groovy.transform.ToString import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC @ToString(includeNames = true, ignoreNulls = true) class EidPermission { String source - List bidders + String inserter + String matcher + @JsonProperty("mm") + Integer matchMethod + List bidders = [GENERIC] + + static EidPermission getDefaultEidPermission(List bidders = [GENERIC]) { + new EidPermission().tap { + it.source = PBSUtils.randomString + it.inserter = PBSUtils.randomString + it.matcher = PBSUtils.randomString + it.matchMethod = PBSUtils.randomNumber + it.bidders = bidders + } + } + + static EidPermission from(Eid eid, List bidders = [GENERIC]) { + new EidPermission().tap { + it.source = eid.source + it.inserter = eid.inserter + it.matcher = eid.matcher + it.matchMethod = eid.matchMethod + it.bidders = bidders + } + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/EidsSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/EidsSpec.groovy index 46b0ff2a5aa..6bc172f3fa4 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/EidsSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/EidsSpec.groovy @@ -13,9 +13,12 @@ import org.prebid.server.functional.model.request.auction.UserExt import org.prebid.server.functional.service.PrebidServerException import org.prebid.server.functional.util.PBSUtils +import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST import static org.prebid.server.functional.model.bidder.BidderName.ALIAS import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC_CAMEL_CASE import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.RUBICON import static org.prebid.server.functional.model.bidder.BidderName.UNKNOWN import static org.prebid.server.functional.model.bidder.BidderName.WILDCARD import static org.prebid.server.functional.model.request.auction.DebugCondition.DISABLED @@ -224,7 +227,7 @@ class EidsSpec extends BaseSpec { imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx ext.prebid.data = new ExtRequestPrebidData( eidpermissions: [new EidPermission(source: PBSUtils.randomString, bidders: eidsBidder), - new EidPermission(source: PBSUtils.randomString)]) + new EidPermission(source: PBSUtils.randomString, bidders: null)]) } when: "PBS processes auction request" @@ -338,4 +341,256 @@ class EidsSpec extends BaseSpec { and: "Bid response shouldn't contain warning" assert !bidResponse.ext.warnings } + + def "PBS should pass user.eids to all bidders when any of required eid permissions doesn't match"() { + given: "Default BidRequest with eids" + def eidPermission = EidPermission.getDefaultEidPermission([RUBICON]) + def userEid = updateEidClosure(Eid.from(eidPermission)) + + def bidRequest = BidRequest.defaultBidRequest.tap { + user = new User(eids: [userEid]) + ext.prebid.data = new ExtRequestPrebidData(eidpermissions: [eidPermission]) + } + + when: "PBS processes auction request" + def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should contain requested eids" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.user.eids == [userEid] + + and: "Bid response shouldn't contain any errors and warnings" + assert !bidResponse.ext.errors + assert !bidResponse.ext.warnings + + where: + updateEidClosure << [ + { Eid eid -> eid.tap { it.inserter = null } }, + { Eid eid -> eid.tap { it.matcher = null } }, + { Eid eid -> eid.tap { it.matchMethod = null } }, + + { Eid eid -> eid.tap { it.inserter = "" } }, + { Eid eid -> eid.tap { it.matcher = "" } }, + + { Eid eid -> eid.tap { it.source = PBSUtils.randomString } }, + { Eid eid -> eid.tap { it.inserter = PBSUtils.randomString } }, + { Eid eid -> eid.tap { it.matcher = PBSUtils.randomString } }, + { Eid eid -> eid.tap { it.matchMethod = PBSUtils.randomNumber } } + ] + } + + def "PBS shouldn't pass user.eids to unmatched bidders when eidPermissions fields match user.eids"() { + given: "Default BidRequest with eids" + def eidPermissionWithUnmatchedBidder = eidPermission.tap { + bidders = [RUBICON] + } + def userEid = updateEidClosure(eidPermissionWithUnmatchedBidder) + + def bidRequest = BidRequest.defaultBidRequest.tap { + user = new User(eids: [userEid]) + ext.prebid.data = new ExtRequestPrebidData(eidpermissions: [eidPermissionWithUnmatchedBidder]) + } + + when: "PBS processes auction request" + def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request shouldn't contain eids" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest.user.eids + + and: "Bid response shouldn't contain any errors and warnings" + assert !bidResponse.ext.errors + assert !bidResponse.ext.warnings + + where: + eidPermission | updateEidClosure + new EidPermission(source: PBSUtils.randomString) | { EidPermission eid -> Eid.from(eid) } + new EidPermission(source: PBSUtils.randomString) | { EidPermission eid -> Eid.getDefaultEid().tap { it.source = eid.source } } + new EidPermission(inserter: PBSUtils.randomString) | { EidPermission eid -> Eid.from(eid).tap { it.source = PBSUtils.randomString } } + new EidPermission(inserter: PBSUtils.randomString) | { EidPermission eid -> Eid.getDefaultEid().tap { it.inserter = eid.inserter } } + new EidPermission(matcher: PBSUtils.randomString) | { EidPermission eid -> Eid.from(eid).tap { it.source = PBSUtils.randomString } } + new EidPermission(matcher: PBSUtils.randomString) | { EidPermission eid -> Eid.getDefaultEid().tap { it.matcher = eid.matcher } } + new EidPermission(matchMethod: PBSUtils.randomNumber) | { EidPermission eid -> Eid.from(eid).tap { it.source = PBSUtils.randomString } } + new EidPermission(matchMethod: PBSUtils.randomNumber) | { EidPermission eid -> Eid.getDefaultEid().tap { it.matchMethod = eid.matchMethod } } + } + + def "PBS should filter only unauthorized eids when multiple eids with different permissions are present"() { + given: "Default BidRequest with eids" + def eidPermissionWithUnmatchedBidder = EidPermission.getDefaultEidPermission([RUBICON]) + def userEid = Eid.from(eidPermissionWithUnmatchedBidder) + def properEids = [Eid.defaultEid, Eid.defaultEid] + def bidRequest = BidRequest.defaultBidRequest.tap { + user = new User(eids: properEids + userEid) + ext.prebid.data = new ExtRequestPrebidData(eidpermissions: [eidPermissionWithUnmatchedBidder]) + } + + when: "PBS processes auction request" + def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should contain requested eids" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.user.eids == properEids + + and: "Bid response shouldn't contain any errors and warnings" + assert !bidResponse.ext.errors + assert !bidResponse.ext.warnings + } + + def "PBS should pass user.eids to matched bidders when eidPermissions fields match user.eids"() { + given: "Default BidRequest with eids" + def eidPermissionAllowingCurrentBidder = eidPermission.tap { + bidders = [[GENERIC, GENERIC_CAMEL_CASE].shuffled().first()] + } + def userEid = updateEidClosure(eidPermissionAllowingCurrentBidder) + + def bidRequest = BidRequest.defaultBidRequest.tap { + user = new User(eids: [userEid]) + ext.prebid.data = new ExtRequestPrebidData(eidpermissions: [eidPermissionAllowingCurrentBidder]) + } + + when: "PBS processes auction request" + def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should contain requested eids" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.user.eids == [userEid] + + and: "Bid response shouldn't contain any errors and warnings" + assert !bidResponse.ext.errors + assert !bidResponse.ext.warnings + + where: + eidPermission | updateEidClosure + new EidPermission(source: PBSUtils.randomString) | { EidPermission eid -> Eid.from(eid) } + new EidPermission(source: PBSUtils.randomString) | { EidPermission eid -> Eid.getDefaultEid().tap { it.source = eid.source } } + new EidPermission(inserter: PBSUtils.randomString) | { EidPermission eid -> Eid.from(eid).tap { it.source = PBSUtils.randomString } } + new EidPermission(inserter: PBSUtils.randomString) | { EidPermission eid -> Eid.getDefaultEid().tap { it.inserter = eid.inserter } } + new EidPermission(matcher: PBSUtils.randomString) | { EidPermission eid -> Eid.from(eid).tap { it.source = PBSUtils.randomString } } + new EidPermission(matcher: PBSUtils.randomString) | { EidPermission eid -> Eid.getDefaultEid().tap { it.matcher = eid.matcher } } + new EidPermission(matchMethod: PBSUtils.randomNumber) | { EidPermission eid -> Eid.from(eid).tap { it.source = PBSUtils.randomString } } + new EidPermission(matchMethod: PBSUtils.randomNumber) | { EidPermission eid -> Eid.getDefaultEid().tap { it.matchMethod = eid.matchMethod } } + } + + def "PBS should apply most specific eidPermissions rule when multiple rules match"() { + given: "Default BidRequest with eids" + def userEid = Eid.getDefaultEid() + def moreSpecificEidPermission = moreSpecificPermissionClosure(userEid).tap { + it.bidders = [RUBICON] + } + def lessSpecificEidPermission = lessSpecificPermissionClosure(userEid).tap { + it.bidders = [GENERIC] + } + def bidRequest = BidRequest.defaultBidRequest.tap { + user = new User(eids: [userEid]) + ext.prebid.data = new ExtRequestPrebidData(eidpermissions: [moreSpecificEidPermission, lessSpecificEidPermission].shuffled()) + } + + when: "PBS processes auction request" + def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request shouldn't contain eids" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest.user.eids + + and: "Bid response shouldn't contain any errors and warnings" + assert !bidResponse.ext.errors + assert !bidResponse.ext.warnings + + where: + moreSpecificPermissionClosure | lessSpecificPermissionClosure + ({ Eid eid -> EidPermission.from(eid) }) | ({ Eid eid -> EidPermission.from(eid).tap { it.source = null } }) + ({ Eid eid -> EidPermission.from(eid) }) | ({ Eid eid -> EidPermission.from(eid).tap { it.inserter = null } }) + ({ Eid eid -> EidPermission.from(eid) }) | ({ Eid eid -> EidPermission.from(eid).tap { it.matcher = null } }) + ({ Eid eid -> EidPermission.from(eid) }) | ({ Eid eid -> EidPermission.from(eid).tap { it.matchMethod = null } }) + ({ Eid eid -> EidPermission.from(eid).tap { it.source = null } }) | ({ Eid eid -> new EidPermission(inserter: eid.inserter, matcher: eid.matcher) }) + ({ Eid eid -> new EidPermission(inserter: eid.inserter, matcher: eid.matcher) }) | ({ Eid eid -> new EidPermission(matchMethod: eid.matchMethod) }) + } + + def "PBS should allow access to bidder defined in most specific rule when multiple rules match"() { + given: "Default BidRequest with eids" + def userEid = Eid.getDefaultEid() + def moreSpecificEidPermission = moreSpecificPermissionClosure(userEid).tap { + it.bidders = [GENERIC] + } + def lessSpecificEidPermission = lessSpecificPermissionClosure(userEid).tap { + it.bidders = [RUBICON] + } + def bidRequest = BidRequest.defaultBidRequest.tap { + user = new User(eids: [userEid]) + ext.prebid.data = new ExtRequestPrebidData(eidpermissions: [moreSpecificEidPermission, lessSpecificEidPermission].shuffled()) + } + + when: "PBS processes auction request" + def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should contain requested eids" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.user.eids == [userEid] + + and: "Bid response shouldn't contain any errors and warnings" + assert !bidResponse.ext.errors + assert !bidResponse.ext.warnings + + where: + moreSpecificPermissionClosure | lessSpecificPermissionClosure + ({ Eid eid -> EidPermission.from(eid) }) | ({ Eid eid -> EidPermission.from(eid).tap { it.source = null } }) + ({ Eid eid -> EidPermission.from(eid) }) | ({ Eid eid -> EidPermission.from(eid).tap { it.inserter = null } }) + ({ Eid eid -> EidPermission.from(eid) }) | ({ Eid eid -> EidPermission.from(eid).tap { it.matcher = null } }) + ({ Eid eid -> EidPermission.from(eid) }) | ({ Eid eid -> EidPermission.from(eid).tap { it.matchMethod = null } }) + ({ Eid eid -> EidPermission.from(eid).tap { it.source = null } }) | ({ Eid eid -> new EidPermission(inserter: eid.inserter, matcher: eid.matcher) }) + ({ Eid eid -> new EidPermission(inserter: eid.inserter, matcher: eid.matcher) }) | ({ Eid eid -> new EidPermission(matchMethod: eid.matchMethod) }) + } + + def "PBS should apply permissions from any matching rule when specificity is equal"() { + given: "Default BidRequest with eids" + def userEid = Eid.getDefaultEid() + def allowingRule = allowingPermissionClosure(userEid).tap { + it.bidders = [GENERIC] + } + def restrictingRule = restrictingPermissionClosure(userEid).tap { + it.bidders = [RUBICON] + } + def bidRequest = BidRequest.defaultBidRequest.tap { + user = new User(eids: [userEid]) + ext.prebid.data = new ExtRequestPrebidData(eidpermissions: [allowingRule, restrictingRule].shuffled()) + } + + when: "PBS processes auction request" + def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should contain requested eids" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.user.eids == [userEid] + + and: "Bid response shouldn't contain any errors and warnings" + assert !bidResponse.ext.errors + assert !bidResponse.ext.warnings + + where: + allowingPermissionClosure | restrictingPermissionClosure + ({ Eid eid -> new EidPermission(source: eid.source) }) | ({ Eid eid -> new EidPermission(inserter: eid.inserter) }) + ({ Eid eid -> new EidPermission(matcher: eid.matcher) }) | ({ Eid eid -> new EidPermission(source: eid.source) }) + ({ Eid eid -> new EidPermission(matchMethod: eid.matchMethod) }) | ({ Eid eid -> new EidPermission(matcher: eid.matcher) }) + ({ Eid eid -> new EidPermission(source: eid.source, matcher: eid.matcher) }) | ({ Eid eid -> new EidPermission(inserter: eid.inserter, matchMethod: eid.matchMethod) }) + ({ Eid eid -> new EidPermission(source: eid.source, inserter: eid.inserter) }) | ({ Eid eid -> new EidPermission(matcher: eid.matcher, matchMethod: eid.matchMethod) }) + ({ Eid eid -> new EidPermission(source: eid.source, matchMethod: eid.matchMethod) }) | ({ Eid eid -> new EidPermission(inserter: eid.inserter, matcher: eid.matcher) }) + ({ Eid eid -> EidPermission.from(eid).tap { matchMethod = null } }) | ({ Eid eid -> EidPermission.from(eid).tap { matcher = null } }) + } + + def "PBS should throw an error when all eidPermissions fields are empty"() { + given: "Default bid request with invalid eidPermission" + def bidRequest = BidRequest.defaultBidRequest.tap { + ext.prebid.data = new ExtRequestPrebidData(eidpermissions: [new EidPermission()]) + } + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "PBS should throw error" + def exception = thrown(PrebidServerException) + assert exception.statusCode == BAD_REQUEST.code() + assert exception.responseBody == "Invalid request format: " + + "Missing required parameter(s) in request.ext.prebid.data.eidPermissions[]. " + + "Either one or a combination of inserter, source, matcher, or mm should be defined." + } } diff --git a/src/test/java/org/prebid/server/auction/EidPermissionResolverTest.java b/src/test/java/org/prebid/server/auction/EidPermissionResolverTest.java new file mode 100644 index 00000000000..08d8627f172 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/EidPermissionResolverTest.java @@ -0,0 +1,251 @@ +package org.prebid.server.auction; + +import com.iab.openrtb.request.Eid; +import org.junit.jupiter.api.Test; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions; + +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +public class EidPermissionResolverTest { + + @Test + public void resolveShouldFilterEidsWhenBidderIsNotAllowedForSourceIgnoringCase() { + // given + final List userEids = asList( + Eid.builder().source("source1").build(), + Eid.builder().source("source2").build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .source("source1") + .bidders(singletonList("OtHeRbIdDeR")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")) + .containsExactly(Eid.builder().source("source2").build()); + } + + @Test + void resolveShouldFilterEidsWhenBidderIsNotAllowedForInserterIgnoringCase() { + // given + final List userEids = asList( + Eid.builder().inserter("inserter1").build(), + Eid.builder().inserter("inserter2").build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .inserter("inserter1") + .bidders(singletonList("OtHeRbIdDeR")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")) + .containsExactly(Eid.builder().inserter("inserter2").build()); + } + + @Test + public void resolveShouldFilterEidsWhenBidderIsNotAllowedForMatcherIgnoringCase() { + // given + final List userEids = asList( + Eid.builder().matcher("matcher1").build(), + Eid.builder().matcher("matcher2").build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .matcher("matcher1") + .bidders(singletonList("OtHeRbIdDeR")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")) + .containsExactly(Eid.builder().matcher("matcher2").build()); + } + + @Test + public void resolveShouldFilterEidsWhenBidderIsNotAllowedForMm() { + // given + final List userEids = asList(Eid.builder().mm(1).build(), Eid.builder().mm(2).build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .mm(1) + .bidders(singletonList("OtHeRbIdDeR")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")) + .containsExactly(Eid.builder().mm(2).build()); + } + + @Test + public void resolveShouldFilterEidsWhenBidderIsNotAllowedUsingMultipleCriteria() { + // given + final List userEids = asList( + Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .inserter("inserter1") + .source("source1") + .matcher("matcher1") + .mm(1) + .bidders(singletonList("OtHeRbIdDeR")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")).containsExactly( + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + } + + @Test + public void resolveShouldFilterEidsWhenEveryCriteriaMatches() { + // given + final List userEids = asList( + Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .inserter("inserter1") + .source("source2") + .matcher("matcher3") + .mm(4) + .bidders(singletonList("OtHeRbIdDeR")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")).containsExactly( + Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + } + + @Test + public void resolveShouldFilterEidsWhenBidderIsNotAllowedUsingTheMostSpecificRule() { + // given + final List userEids = asList( + Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + asList(ExtRequestPrebidDataEidPermissions.builder() + .inserter("inserter1") + .bidders(singletonList("someBidder")) + .build(), + ExtRequestPrebidDataEidPermissions.builder() + .inserter("inserter1") + .source("source1") + .matcher("matcher1") + .mm(1) + .bidders(singletonList("OtHeRbIdDeR")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")).containsExactly( + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + } + + @Test + public void resolveShouldNotFilterUserExtEidsWhenBidderIsAllowedUsingTheMostSpecificRule() { + // given + final List userEids = asList( + Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + asList(ExtRequestPrebidDataEidPermissions.builder() + .inserter("inserter1") + .bidders(singletonList("OtHeRbIdDeR")) + .build(), + ExtRequestPrebidDataEidPermissions.builder() + .inserter("inserter1") + .source("source1") + .matcher("matcher1") + .mm(1) + .bidders(singletonList("someBidder")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")).containsExactly( + Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + } + + @Test + public void resolveShouldNotFilterUserExtEidsWhenBidderIsAllowedUsingMultipleSameSpecificityRules() { + // given + final List userEids = asList( + Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + asList(ExtRequestPrebidDataEidPermissions.builder() + .inserter("inserter1") + .source("source1") + .bidders(singletonList("OtHeRbIdDeR")) + .build(), + ExtRequestPrebidDataEidPermissions.builder() + .matcher("matcher1") + .mm(1) + .bidders(singletonList("someBidder")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")).containsExactly( + Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), + Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()); + } + + @Test + public void resolveShouldNotFilterEidsWhenEidsPermissionDoesNotContainSourceIgnoringCase() { + // given + final List userEids = singletonList(Eid.builder().source("source1").build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .source("source2") + .bidders(singletonList("OtHeRbIdDeR")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")) + .containsExactly(Eid.builder().source("source1").build()); + } + + @Test + public void resolveShouldNotFilterEidsWhenSourceAllowedForAllBiddersIgnoringCase() { + // given + final List userEids = singletonList(Eid.builder().source("source1").build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .source("source1") + .bidders(singletonList("*")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")) + .containsExactly(Eid.builder().source("source1").build()); + } + + @Test + public void resolveShouldNotFilterEidsWhenSourceAllowedForBidderIgnoringCase() { + // given + final List userEids = singletonList(Eid.builder().source("source1").build()); + + final EidPermissionResolver resolver = EidPermissionResolver.of( + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .source("source1") + .bidders(singletonList("SoMeBiDdEr")) + .build())); + + // when and then + assertThat(resolver.resolveAllowedEids(userEids, "someBidder")) + .containsExactly(Eid.builder().source("source1").build()); + } +} diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index 8e5291b4c1c..84ec69ecddb 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -168,7 +168,6 @@ import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.List; @@ -1243,7 +1242,7 @@ public void shouldReturnSeparateSeatBidsForTheSameBidderIfBiddersAliasAndBidderW builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder() .auctiontimestamp(1000L) .build())))) - .originalPriceFloors(Collections.emptyMap()) + .originalPriceFloors(emptyMap()) .build()), any(), any(), @@ -1266,7 +1265,7 @@ public void shouldReturnSeparateSeatBidsForTheSameBidderIfBiddersAliasAndBidderW builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder() .auctiontimestamp(1000L) .build())))) - .originalPriceFloors(Collections.emptyMap()) + .originalPriceFloors(emptyMap()) .build()), any(), any(), @@ -1506,7 +1505,7 @@ public void shouldCallBidResponseCreatorWithExpectedParamsAndUpdateDebugErrors() final ExtRequestPrebidMultiBid multiBid5 = ExtRequestPrebidMultiBid.of("bidder6", Arrays.asList("bidder4", "bidder5"), 0, "bi6"); final ExtRequestPrebidMultiBid multiBid6 = ExtRequestPrebidMultiBid.of(null, - Collections.emptyList(), 0, "bi7"); + emptyList(), 0, "bi7"); final ExtRequestTargeting targeting = givenTargeting(true); final ObjectNode events = mapper.createObjectNode(); @@ -2136,7 +2135,7 @@ public void shouldAddMultibidInfoOnlyAboutRequestedBidder() { // given final BidRequest bidRequest = givenBidRequest(givenSingleImp(singletonMap("someBidder", 1)), builder -> builder.ext(ExtRequest.of(ExtRequestPrebid.builder() - .multibid(Collections.singletonList( + .multibid(singletonList( ExtRequestPrebidMultiBid.of(null, asList("someBidder", "anotherBidder"), 3, null))) .build()))); @@ -2230,50 +2229,6 @@ public void shouldPassUserDataAndExtDataOnlyForAllowedBidder() { tuple("keyword", "male", 133, Geo.EMPTY, eids, null, null)); } - @Test - public void shouldFilterUserExtEidsWhenBidderIsNotAllowedForSourceIgnoringCase() { - testUserEidsPermissionFiltering( - // given - asList(Eid.builder().source("source1").build(), Eid.builder().source("source2").build()), - singletonList(ExtRequestPrebidDataEidPermissions.of("source1", singletonList("OtHeRbIdDeR"))), - emptyMap(), - // expected - singletonList(Eid.builder().source("source2").build())); - } - - @Test - public void shouldNotFilterUserExtEidsWhenEidsPermissionDoesNotContainSourceIgnoringCase() { - testUserEidsPermissionFiltering( - // given - singletonList(Eid.builder().source("source1").build()), - singletonList(ExtRequestPrebidDataEidPermissions.of("source2", singletonList("OtHeRbIdDeR"))), - emptyMap(), - // expected - singletonList(Eid.builder().source("source1").build())); - } - - @Test - public void shouldNotFilterUserExtEidsWhenSourceAllowedForAllBiddersIgnoringCase() { - testUserEidsPermissionFiltering( - // given - singletonList(Eid.builder().source("source1").build()), - singletonList(ExtRequestPrebidDataEidPermissions.of("source1", singletonList("*"))), - emptyMap(), - // expected - singletonList(Eid.builder().source("source1").build())); - } - - @Test - public void shouldNotFilterUserExtEidsWhenSourceAllowedForBidderIgnoringCase() { - testUserEidsPermissionFiltering( - // given - singletonList(Eid.builder().source("source1").build()), - singletonList(ExtRequestPrebidDataEidPermissions.of("source1", singletonList("SoMeBiDdEr"))), - emptyMap(), - // expected - singletonList(Eid.builder().source("source1").build())); - } - @Test public void shouldFilterUserExtEidsWhenBidderIsNotAllowedForSourceAndSetNullIfNoEidsLeft() { // given @@ -2285,8 +2240,10 @@ public void shouldFilterUserExtEidsWhenBidderIsNotAllowedForSourceAndSetNullIfNo builder -> builder .ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(null, singletonList( - ExtRequestPrebidDataEidPermissions.of("source1", - singletonList("otherBidder"))))) + ExtRequestPrebidDataEidPermissions.builder() + .source("source1") + .bidders(singletonList("otherBidder")) + .build()))) .build())) .user(User.builder() .eids(singletonList(Eid.builder().source("source1").build())) @@ -2321,8 +2278,10 @@ public void shouldFilterUserExtEidsWhenBidderPermissionsGivenToBidderAliasOnly() .ext(ExtRequest.of(ExtRequestPrebid.builder() .aliases(singletonMap("someBidder", "someBidderAlias")) .data(ExtRequestPrebidData.of(null, singletonList( - ExtRequestPrebidDataEidPermissions.of("source1", - singletonList("someBidderAlias"))))) + ExtRequestPrebidDataEidPermissions.builder() + .source("source1") + .bidders(singletonList("someBidderAlias")) + .build()))) .build())) .user(User.builder() .eids(singletonList(Eid.builder().source("source1").build())) @@ -2357,8 +2316,10 @@ public void shouldFilterUserExtEidsWhenPermissionsGivenToBidderButNotForAlias() .ext(ExtRequest.of(ExtRequestPrebid.builder() .aliases(singletonMap("someBidder", "someBidderAlias")) .data(ExtRequestPrebidData.of(null, singletonList( - ExtRequestPrebidDataEidPermissions.of("source1", - singletonList("someBidder"))))) + ExtRequestPrebidDataEidPermissions.builder() + .source("source1") + .bidders(singletonList("someBidder")) + .build()))) .build())) .user(User.builder() .eids(singletonList(Eid.builder().source("source1").build())) @@ -4419,40 +4380,6 @@ private static BidResponse givenBidResponseWithError(Map givenUserEids, - List givenEidPermissions, - Map givenAlises, - List expectedExtUserEids) { - // given - final Bidder bidder = mock(Bidder.class); - givenBidder("someBidder", bidder, givenEmptySeatBid()); - final Map bidderToGdpr = singletonMap("someBidder", 1); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp(bidderToGdpr), - builder -> builder - .ext(ExtRequest.of(ExtRequestPrebid.builder() - .aliases(givenAlises) - .data(ExtRequestPrebidData.of(null, givenEidPermissions)) - .build())) - .user(User.builder() - .eids(givenUserEids) - .build())); - - // when - target.holdAuction(givenRequestContext(bidRequest)); - - // then - final ArgumentCaptor bidderRequestCaptor = ArgumentCaptor.forClass(BidderRequest.class); - verify(httpBidderRequester) - .requestBids(any(), bidderRequestCaptor.capture(), any(), any(), any(), any(), anyBoolean()); - final List capturedBidRequests = bidderRequestCaptor.getAllValues(); - assertThat(capturedBidRequests) - .extracting(BidderRequest::getBidRequest) - .extracting(BidRequest::getUser) - .flatExtracting(User::getEids) - .isEqualTo(expectedExtUserEids); - } - private static AppliedToImpl givenAppliedToImpl( Function appliedToImplBuilder) { return appliedToImplBuilder.apply(AppliedToImpl.builder() diff --git a/src/test/java/org/prebid/server/auction/requestfactory/AuctionRequestFactoryTest.java b/src/test/java/org/prebid/server/auction/requestfactory/AuctionRequestFactoryTest.java index 2e30e8d1bc4..59615bdbaab 100644 --- a/src/test/java/org/prebid/server/auction/requestfactory/AuctionRequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/requestfactory/AuctionRequestFactoryTest.java @@ -487,7 +487,11 @@ public void shouldReturnFailedFutureIfEidsPermissionsContainsWrongDataType() { final ObjectNode requestNode = mapper.convertValue(bidRequest, ObjectNode.class); final JsonNode eidPermissionNode = mapper.convertValue( - ExtRequestPrebidDataEidPermissions.of("source", emptyList()), JsonNode.class); + ExtRequestPrebidDataEidPermissions.builder() + .source("source") + .bidders(emptyList()) + .build(), + JsonNode.class); requestNode .putObject("ext") @@ -520,7 +524,11 @@ public void shouldReturnFailedFutureIfEidsPermissionsBiddersContainsWrongDataTyp final ObjectNode requestNode = mapper.convertValue(bidRequest, ObjectNode.class); final ObjectNode eidPermissionNode = mapper.convertValue( - ExtRequestPrebidDataEidPermissions.of("source", emptyList()), ObjectNode.class); + ExtRequestPrebidDataEidPermissions.builder() + .source("source") + .bidders(emptyList()) + .build(), + ObjectNode.class); eidPermissionNode.put("bidders", "notArrayValue"); diff --git a/src/test/java/org/prebid/server/validation/RequestValidatorTest.java b/src/test/java/org/prebid/server/validation/RequestValidatorTest.java index e4efd99feb9..653b7bf6a1d 100644 --- a/src/test/java/org/prebid/server/validation/RequestValidatorTest.java +++ b/src/test/java/org/prebid/server/validation/RequestValidatorTest.java @@ -635,7 +635,9 @@ public void validateShouldReturnValidationMessageWhenEidsPermissionsBiddersIsNul final BidRequest bidRequest = validBidRequestBuilder() .ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(null, - singletonList(ExtRequestPrebidDataEidPermissions.of("source", null)))) + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .source("source") + .build()))) .build())) .build(); @@ -654,7 +656,10 @@ public void validateShouldReturnValidationMessageWhenEidsPermissionsBiddersIsEmp final BidRequest bidRequest = validBidRequestBuilder() .ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(null, - singletonList(ExtRequestPrebidDataEidPermissions.of("source", emptyList())))) + singletonList(ExtRequestPrebidDataEidPermissions.builder() + .source("source") + .bidders(Collections.emptyList()) + .build()))) .build())) .build(); @@ -675,7 +680,10 @@ public void validateShouldReturnWarningsMessageWhenEidsPermissionsBidderIsNotRec .ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(null, singletonList( - ExtRequestPrebidDataEidPermissions.of("source", singletonList("bidder1"))))) + ExtRequestPrebidDataEidPermissions.builder() + .source("source") + .bidders(Collections.singletonList("bidder1")) + .build()))) .build())) .build(); @@ -698,7 +706,10 @@ public void validateShouldNotReturnWarningsMessageWhenEidsPermissionsBidderIsNot .ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(null, singletonList( - ExtRequestPrebidDataEidPermissions.of("source", singletonList("bidder1"))))) + ExtRequestPrebidDataEidPermissions.builder() + .source("source") + .bidders(Collections.singletonList("bidder1")) + .build()))) .build())) .build(); @@ -718,7 +729,10 @@ public void validateShouldReturnWarningMessageWhenEidsPermissionsBidderHasBlankV .ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(null, singletonList( - ExtRequestPrebidDataEidPermissions.of("source", singletonList(" "))))) + ExtRequestPrebidDataEidPermissions.builder() + .source("source") + .bidders(Collections.singletonList(" ")) + .build()))) .build())) .build(); @@ -744,7 +758,10 @@ public void validateShouldNotReturnValidationErrorWhenBidderIsAlias() { .aliases(singletonMap("bidder1Alias", "bidder1")) .data(ExtRequestPrebidData.of(null, singletonList( - ExtRequestPrebidDataEidPermissions.of("source", singletonList("bidder1"))))) + ExtRequestPrebidDataEidPermissions.builder() + .source("source") + .bidders(Collections.singletonList("bidder1")) + .build()))) .build())) .build(); @@ -762,7 +779,10 @@ public void validateShouldNotReturnValidationErrorWhenBidderIsAsterisk() { .ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(null, singletonList( - ExtRequestPrebidDataEidPermissions.of("source", singletonList("*"))))) + ExtRequestPrebidDataEidPermissions.builder() + .source("source") + .bidders(Collections.singletonList("*")) + .build()))) .build())) .build(); @@ -780,7 +800,9 @@ public void validateShouldReturnValidationMessageWhenEidsPermissionsHasMissingSo .ext(ExtRequest.of(ExtRequestPrebid.builder() .data(ExtRequestPrebidData.of(null, singletonList( - ExtRequestPrebidDataEidPermissions.of(null, singletonList("bidder1"))))) + ExtRequestPrebidDataEidPermissions.builder() + .bidders(Collections.singletonList("bidder1")) + .build()))) .build())) .build(); @@ -789,7 +811,8 @@ public void validateShouldReturnValidationMessageWhenEidsPermissionsHasMissingSo // then assertThat(result.getErrors()).hasSize(1) - .containsOnly("Missing required value request.ext.prebid.data.eidPermissions[].source"); + .containsOnly("Missing required parameter(s) in request.ext.prebid.data.eidPermissions[]. " + + "Either one or a combination of inserter, source, matcher, or mm should be defined."); } @Test