diff --git a/src/main/java/org/prebid/server/auction/model/EidPermissionHolder.java b/src/main/java/org/prebid/server/auction/EidPermissionResolver.java similarity index 84% rename from src/main/java/org/prebid/server/auction/model/EidPermissionHolder.java rename to src/main/java/org/prebid/server/auction/EidPermissionResolver.java index 8abaaf6a592..6a19fd7c82b 100644 --- a/src/main/java/org/prebid/server/auction/model/EidPermissionHolder.java +++ b/src/main/java/org/prebid/server/auction/EidPermissionResolver.java @@ -1,4 +1,4 @@ -package org.prebid.server.auction.model; +package org.prebid.server.auction; import com.iab.openrtb.request.Eid; import org.apache.commons.collections4.CollectionUtils; @@ -13,7 +13,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public final class EidPermissionHolder { +public class EidPermissionResolver { private static final String WILDCARD_BIDDER = "*"; @@ -21,20 +21,20 @@ public final class EidPermissionHolder { .bidders(Collections.singletonList(WILDCARD_BIDDER)) .build(); - private static final EidPermissionHolder EMPTY = new EidPermissionHolder(Collections.emptyList()); + private static final EidPermissionResolver EMPTY = new EidPermissionResolver(Collections.emptyList()); private final List eidPermissions; - private EidPermissionHolder(List eidPermissions) { + private EidPermissionResolver(List eidPermissions) { this.eidPermissions = new ArrayList<>(eidPermissions); this.eidPermissions.add(DEFAULT_RULE); } - public static EidPermissionHolder of(List eidPermissions) { - return new EidPermissionHolder(eidPermissions); + public static EidPermissionResolver of(List eidPermissions) { + return new EidPermissionResolver(eidPermissions); } - public static EidPermissionHolder empty() { + public static EidPermissionResolver empty() { return EMPTY; } @@ -74,7 +74,7 @@ private boolean isRuleMatched(Eid eid, ExtRequestPrebidDataEidPermissions eidPer } private boolean isBidderAllowed(String bidder, List ruleBidders) { - return CollectionUtils.emptyIfNull(ruleBidders).stream() + 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 42aab13359e..428510be069 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -42,7 +42,6 @@ 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; @@ -537,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 EidPermissionHolder eidPermissionHolder = getEidPermissions(prebid); + final EidPermissionResolver eidPermissionResolver = getEidPermissions(prebid); final Map> bidderToUserAndDevice = - prepareUsersAndDevices(bidders, context, aliases, biddersToConfigs, eidPermissionHolder); + prepareUsersAndDevices(bidders, context, aliases, biddersToConfigs, eidPermissionResolver); return privacyEnforcementService.mask(context, bidderToUserAndDevice, aliases) .map(bidderToPrivacyResult -> getAuctionParticipation( @@ -577,12 +576,12 @@ private Map getBiddersToConfigs(ExtRequestPrebid pr return bidderToConfig; } - private EidPermissionHolder getEidPermissions(ExtRequestPrebid prebid) { + private EidPermissionResolver getEidPermissions(ExtRequestPrebid prebid) { return Optional.ofNullable(prebid) .map(ExtRequestPrebid::getData) .map(ExtRequestPrebidData::getEidPermissions) - .map(EidPermissionHolder::of) - .orElse(EidPermissionHolder.empty()); + .map(EidPermissionResolver::of) + .orElse(EidPermissionResolver.empty()); } private static List firstPartyDataBidders(ExtRequest requestExt) { @@ -596,7 +595,7 @@ private Map> prepareUsersAndDevices( AuctionContext context, BidderAliases aliases, Map biddersToConfigs, - EidPermissionHolder eidPermissionHolder) { + EidPermissionResolver eidPermissionResolver) { final BidRequest bidRequest = context.getBidRequest(); final List firstPartyDataBidders = firstPartyDataBidders(bidRequest.getExt()); @@ -608,7 +607,7 @@ private Map> prepareUsersAndDevices( final boolean useFirstPartyData = firstPartyDataBidders == null || firstPartyDataBidders.stream() .anyMatch(fpdBidder -> StringUtils.equalsIgnoreCase(fpdBidder, bidder)); final User preparedUser = prepareUser( - bidder, context, aliases, useFirstPartyData, fpdConfig, eidPermissionHolder); + bidder, context, aliases, useFirstPartyData, fpdConfig, eidPermissionResolver); final Device preparedDevice = prepareDevice( bidRequest.getDevice(), fpdConfig, useFirstPartyData); bidderToUserAndDevice.put(bidder, Pair.of(preparedUser, preparedDevice)); @@ -621,13 +620,13 @@ private User prepareUser(String bidder, BidderAliases aliases, boolean useFirstPartyData, ExtBidderConfigOrtb fpdConfig, - EidPermissionHolder eidPermissionHolder) { + 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 = eidPermissionHolder.resolveAllowedEids(userEids, bidder); + 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; 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..65ccf55fa69 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 @@ -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 808cf5781b5..84ec69ecddb 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -2229,208 +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.builder() - .source("source1") - .bidders(singletonList("OtHeRbIdDeR")) - .build()), - emptyMap(), - // expected - singletonList(Eid.builder().source("source2").build())); - } - - @Test - public void shouldFilterUserExtEidsWhenBidderIsNotAllowedForInserterIgnoringCase() { - testUserEidsPermissionFiltering( - // given - asList(Eid.builder().inserter("inserter1").build(), Eid.builder().inserter("inserter2").build()), - singletonList(ExtRequestPrebidDataEidPermissions.builder() - .inserter("inserter1") - .bidders(singletonList("OtHeRbIdDeR")) - .build()), - emptyMap(), - // expected - singletonList(Eid.builder().inserter("inserter2").build())); - } - - @Test - public void shouldFilterUserExtEidsWhenBidderIsNotAllowedForMatcherIgnoringCase() { - testUserEidsPermissionFiltering( - // given - asList(Eid.builder().matcher("matcher1").build(), Eid.builder().matcher("matcher2").build()), - singletonList(ExtRequestPrebidDataEidPermissions.builder() - .matcher("matcher1") - .bidders(singletonList("OtHeRbIdDeR")) - .build()), - emptyMap(), - // expected - singletonList(Eid.builder().matcher("matcher2").build())); - } - - @Test - public void shouldFilterUserExtEidsWhenBidderIsNotAllowedForMm() { - testUserEidsPermissionFiltering( - // given - asList(Eid.builder().mm(1).build(), Eid.builder().mm(2).build()), - singletonList(ExtRequestPrebidDataEidPermissions.builder() - .mm(1) - .bidders(singletonList("OtHeRbIdDeR")) - .build()), - emptyMap(), - // expected - singletonList(Eid.builder().mm(2).build())); - } - - @Test - public void shouldFilterUserExtEidsWhenBidderIsNotAllowedUsingMultipleCriteria() { - testUserEidsPermissionFiltering( - // given - asList(Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), - Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()), - singletonList(ExtRequestPrebidDataEidPermissions.builder() - .inserter("inserter1") - .source("source1") - .matcher("matcher1") - .mm(1) - .bidders(singletonList("OtHeRbIdDeR")) - .build()), - emptyMap(), - // expected - singletonList(Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build())); - } - - @Test - public void shouldFilterUserExtEidsWhenEveryCriteriaMatches() { - testUserEidsPermissionFiltering( - // given - asList(Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), - Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()), - singletonList(ExtRequestPrebidDataEidPermissions.builder() - .inserter("inserter1") - .source("source2") - .matcher("matcher3") - .mm(4) - .bidders(singletonList("OtHeRbIdDeR")) - .build()), - emptyMap(), - // expected - asList(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 shouldFilterUserExtEidsWhenBidderIsNotAllowedUsingTheMostSpecificRule() { - testUserEidsPermissionFiltering( - // given - asList(Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), - Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()), - asList(ExtRequestPrebidDataEidPermissions.builder() - .inserter("inserter1") - .bidders(singletonList("someBidder")) - .build(), - ExtRequestPrebidDataEidPermissions.builder() - .inserter("inserter1") - .source("source1") - .matcher("matcher1") - .mm(1) - .bidders(singletonList("OtHeRbIdDeR")) - .build()), - emptyMap(), - // expected - singletonList(Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build())); - } - - @Test - public void shouldNotFilterUserExtEidsWhenBidderIsAllowedUsingTheMostSpecificRule() { - testUserEidsPermissionFiltering( - // given - asList(Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), - Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()), - asList(ExtRequestPrebidDataEidPermissions.builder() - .inserter("inserter1") - .bidders(singletonList("OtHeRbIdDeR")) - .build(), - ExtRequestPrebidDataEidPermissions.builder() - .inserter("inserter1") - .source("source1") - .matcher("matcher1") - .mm(1) - .bidders(singletonList("someBidder")) - .build()), - emptyMap(), - // expected - asList(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 shouldNotFilterUserExtEidsWhenBidderIsAllowedUsingMultipleSameSpecificityRules() { - testUserEidsPermissionFiltering( - // given - asList(Eid.builder().inserter("inserter1").source("source1").matcher("matcher1").mm(1).build(), - Eid.builder().inserter("inserter2").source("source2").matcher("matcher2").mm(2).build()), - asList(ExtRequestPrebidDataEidPermissions.builder() - .inserter("inserter1") - .source("source1") - .bidders(singletonList("OtHeRbIdDeR")) - .build(), - ExtRequestPrebidDataEidPermissions.builder() - .matcher("matcher1") - .mm(1) - .bidders(singletonList("someBidder")) - .build()), - emptyMap(), - // expected - asList(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 shouldNotFilterUserExtEidsWhenEidsPermissionDoesNotContainSourceIgnoringCase() { - testUserEidsPermissionFiltering( - // given - singletonList(Eid.builder().source("source1").build()), - singletonList(ExtRequestPrebidDataEidPermissions.builder() - .source("source2") - .bidders(singletonList("OtHeRbIdDeR")) - .build()), - emptyMap(), - // expected - singletonList(Eid.builder().source("source1").build())); - } - - @Test - public void shouldNotFilterUserExtEidsWhenSourceAllowedForAllBiddersIgnoringCase() { - testUserEidsPermissionFiltering( - // given - singletonList(Eid.builder().source("source1").build()), - singletonList(ExtRequestPrebidDataEidPermissions.builder() - .source("source1") - .bidders(singletonList("*")) - .build()), - emptyMap(), - // expected - singletonList(Eid.builder().source("source1").build())); - } - - @Test - public void shouldNotFilterUserExtEidsWhenSourceAllowedForBidderIgnoringCase() { - testUserEidsPermissionFiltering( - // given - singletonList(Eid.builder().source("source1").build()), - singletonList(ExtRequestPrebidDataEidPermissions.builder() - .source("source1") - .bidders(singletonList("SoMeBiDdEr")) - .build()), - emptyMap(), - // expected - singletonList(Eid.builder().source("source1").build())); - } - @Test public void shouldFilterUserExtEidsWhenBidderIsNotAllowedForSourceAndSetNullIfNoEidsLeft() { // given @@ -4582,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()