diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcement.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcement.java index df7603048ee..395dff73802 100644 --- a/src/main/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcement.java +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcement.java @@ -12,6 +12,7 @@ import org.prebid.server.activity.infrastructure.payload.ActivityInvocationPayload; import org.prebid.server.activity.infrastructure.payload.impl.ActivityInvocationPayloadImpl; import org.prebid.server.activity.infrastructure.payload.impl.PrivacyEnforcementServiceActivityInvocationPayload; +import org.prebid.server.auction.BidderAliases; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderPrivacyResult; import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; @@ -21,7 +22,7 @@ import java.util.Objects; import java.util.Optional; -public class ActivityEnforcement { +public class ActivityEnforcement implements PrivacyEnforcement { private final UserFpdActivityMask userFpdActivityMask; @@ -29,17 +30,19 @@ public ActivityEnforcement(UserFpdActivityMask userFpdActivityMask) { this.userFpdActivityMask = Objects.requireNonNull(userFpdActivityMask); } - public Future> enforce(List bidderPrivacyResults, - AuctionContext auctionContext) { + @Override + public Future> enforce(AuctionContext auctionContext, + BidderAliases aliases, + List results) { - final List results = bidderPrivacyResults.stream() + final List enforcedResults = results.stream() .map(bidderPrivacyResult -> applyActivityRestrictions( bidderPrivacyResult, auctionContext.getActivityInfrastructure(), auctionContext.getBidRequest())) .toList(); - return Future.succeededFuture(results); + return Future.succeededFuture(enforcedResults); } private BidderPrivacyResult applyActivityRestrictions(BidderPrivacyResult bidderPrivacyResult, diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcement.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcement.java index 10fe183801d..51a8d8f4622 100644 --- a/src/main/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcement.java +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcement.java @@ -1,10 +1,7 @@ package org.prebid.server.auction.privacy.enforcement; import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Device; -import com.iab.openrtb.request.User; import io.vertx.core.Future; -import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.auction.BidderAliases; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderPrivacyResult; @@ -22,12 +19,12 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; -public class CcpaEnforcement { +public class CcpaEnforcement implements PrivacyEnforcement { private static final String CATCH_ALL_BIDDERS = "*"; @@ -47,9 +44,10 @@ public CcpaEnforcement(UserFpdCcpaMask userFpdCcpaMask, this.ccpaEnforce = ccpaEnforce; } + @Override public Future> enforce(AuctionContext auctionContext, - Map bidderToUser, - BidderAliases aliases) { + BidderAliases aliases, + List results) { final Ccpa ccpa = auctionContext.getPrivacyContext().getPrivacy().getCcpa(); final BidRequest bidRequest = auctionContext.getBidRequest(); @@ -58,7 +56,7 @@ public Future> enforce(AuctionContext auctionContext, final boolean isCcpaEnabled = isCcpaEnabled(auctionContext.getAccount(), auctionContext.getRequestTypeMetric()); final Set enforcedBidders = isCcpaEnabled && isCcpaEnforced - ? extractCcpaEnforcedBidders(bidderToUser.keySet(), bidRequest, aliases) + ? extractCcpaEnforcedBidders(results, bidRequest, aliases) : Collections.emptySet(); metrics.updatePrivacyCcpaMetrics( @@ -68,7 +66,11 @@ public Future> enforce(AuctionContext auctionContext, isCcpaEnabled, enforcedBidders); - return Future.succeededFuture(maskCcpa(bidderToUser, enforcedBidders, bidRequest.getDevice())); + final List enforcedResults = results.stream() + .map(result -> enforcedBidders.contains(result.getRequestBidder()) ? maskCcpa(result) : result) + .toList(); + + return Future.succeededFuture(enforcedResults); } public boolean isCcpaEnforced(Ccpa ccpa, Account account) { @@ -79,19 +81,21 @@ private boolean isCcpaEnabled(Account account, MetricName requestType) { final Optional accountCcpaConfig = Optional.ofNullable(account.getPrivacy()) .map(AccountPrivacyConfig::getCcpa); - return ObjectUtils.firstNonNull( - accountCcpaConfig - .map(AccountCcpaConfig::getEnabledForRequestType) - .map(enabledForRequestType -> enabledForRequestType.isEnabledFor(requestType)) - .orElse(null), - accountCcpaConfig - .map(AccountCcpaConfig::getEnabled) - .orElse(null), - ccpaEnforce); + return accountCcpaConfig + .map(AccountCcpaConfig::getEnabledForRequestType) + .map(enabledForRequestType -> enabledForRequestType.isEnabledFor(requestType)) + .or(() -> accountCcpaConfig.map(AccountCcpaConfig::getEnabled)) + .orElse(ccpaEnforce); } - private Set extractCcpaEnforcedBidders(Set bidders, BidRequest bidRequest, BidderAliases aliases) { - final Set ccpaEnforcedBidders = new HashSet<>(bidders); + private Set extractCcpaEnforcedBidders(List results, + BidRequest bidRequest, + BidderAliases aliases) { + + final Set ccpaEnforcedBidders = results.stream() + .map(BidderPrivacyResult::getRequestBidder) + .collect(Collectors.toCollection(HashSet::new)); + final List nosaleBidders = Optional.ofNullable(bidRequest.getExt()) .map(ExtRequest::getPrebid) .map(ExtRequestPrebid::getNosale) @@ -109,14 +113,11 @@ private Set extractCcpaEnforcedBidders(Set bidders, BidRequest b return ccpaEnforcedBidders; } - private List maskCcpa(Map bidderToUser, Set bidders, Device device) { - final Device maskedDevice = userFpdCcpaMask.maskDevice(device); - return bidders.stream() - .map(bidder -> BidderPrivacyResult.builder() - .requestBidder(bidder) - .user(userFpdCcpaMask.maskUser(bidderToUser.get(bidder))) - .device(maskedDevice) - .build()) - .toList(); + private BidderPrivacyResult maskCcpa(BidderPrivacyResult result) { + return BidderPrivacyResult.builder() + .requestBidder(result.getRequestBidder()) + .user(userFpdCcpaMask.maskUser(result.getUser())) + .device(userFpdCcpaMask.maskDevice(result.getDevice())) + .build(); } } diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcement.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcement.java index 92471e85ec2..9ebe0c8d044 100644 --- a/src/main/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcement.java +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcement.java @@ -1,18 +1,18 @@ package org.prebid.server.auction.privacy.enforcement; -import com.iab.openrtb.request.Device; -import com.iab.openrtb.request.User; import io.vertx.core.Future; +import org.prebid.server.auction.BidderAliases; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderPrivacyResult; import org.prebid.server.auction.privacy.enforcement.mask.UserFpdCoppaMask; import org.prebid.server.metric.Metrics; import java.util.List; -import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; -public class CoppaEnforcement { +public class CoppaEnforcement implements PrivacyEnforcement { private final UserFpdCoppaMask userFpdCoppaMask; private final Metrics metrics; @@ -22,23 +22,34 @@ public CoppaEnforcement(UserFpdCoppaMask userFpdCoppaMask, Metrics metrics) { this.metrics = Objects.requireNonNull(metrics); } - public boolean isApplicable(AuctionContext auctionContext) { - return auctionContext.getPrivacyContext().getPrivacy().getCoppa() == 1; - } + @Override + public Future> enforce(AuctionContext auctionContext, + BidderAliases aliases, + List results) { + + if (!isApplicable(auctionContext)) { + return Future.succeededFuture(results); + } + + final Set bidders = results.stream() + .map(BidderPrivacyResult::getRequestBidder) + .collect(Collectors.toSet()); - public Future> enforce(AuctionContext auctionContext, Map bidderToUser) { - metrics.updatePrivacyCoppaMetric(auctionContext.getActivityInfrastructure(), bidderToUser.keySet()); - return Future.succeededFuture(results(bidderToUser, auctionContext.getBidRequest().getDevice())); + metrics.updatePrivacyCoppaMetric(auctionContext.getActivityInfrastructure(), bidders); + return Future.succeededFuture(enforce(results)); } - private List results(Map bidderToUser, Device device) { - final Device maskedDevice = userFpdCoppaMask.maskDevice(device); - return bidderToUser.entrySet().stream() - .map(bidderAndUser -> BidderPrivacyResult.builder() - .requestBidder(bidderAndUser.getKey()) - .user(userFpdCoppaMask.maskUser(bidderAndUser.getValue())) - .device(maskedDevice) + private List enforce(List results) { + return results.stream() + .map(result -> BidderPrivacyResult.builder() + .requestBidder(result.getRequestBidder()) + .user(userFpdCoppaMask.maskUser(result.getUser())) + .device(userFpdCoppaMask.maskDevice(result.getDevice())) .build()) .toList(); } + + private static boolean isApplicable(AuctionContext auctionContext) { + return auctionContext.getPrivacyContext().getPrivacy().getCoppa() == 1; + } } diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcement.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcement.java new file mode 100644 index 00000000000..d12e290fb2e --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcement.java @@ -0,0 +1,15 @@ +package org.prebid.server.auction.privacy.enforcement; + +import io.vertx.core.Future; +import org.prebid.server.auction.BidderAliases; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidderPrivacyResult; + +import java.util.List; + +public interface PrivacyEnforcement { + + Future> enforce(AuctionContext auctionContext, + BidderAliases aliases, + List results); +} diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementService.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementService.java index 3f4e4055dca..f50a7c637f6 100644 --- a/src/main/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementService.java +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementService.java @@ -5,58 +5,41 @@ import org.prebid.server.auction.BidderAliases; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderPrivacyResult; -import org.prebid.server.util.ListUtil; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; /** * Service provides masking for OpenRTB client sensitive information. */ public class PrivacyEnforcementService { - private final CoppaEnforcement coppaEnforcement; - private final CcpaEnforcement ccpaEnforcement; - private final TcfEnforcement tcfEnforcement; - private final ActivityEnforcement activityEnforcement; + private final List enforcements; - public PrivacyEnforcementService(CoppaEnforcement coppaEnforcement, - CcpaEnforcement ccpaEnforcement, - TcfEnforcement tcfEnforcement, - ActivityEnforcement activityEnforcement) { - - this.coppaEnforcement = Objects.requireNonNull(coppaEnforcement); - this.ccpaEnforcement = Objects.requireNonNull(ccpaEnforcement); - this.tcfEnforcement = Objects.requireNonNull(tcfEnforcement); - this.activityEnforcement = Objects.requireNonNull(activityEnforcement); + public PrivacyEnforcementService(final List enforcements) { + this.enforcements = Objects.requireNonNull(enforcements); } public Future> mask(AuctionContext auctionContext, Map bidderToUser, BidderAliases aliases) { - // For now, COPPA masking all values, so we can omit TCF masking. - return coppaEnforcement.isApplicable(auctionContext) - ? coppaEnforcement.enforce(auctionContext, bidderToUser) - : ccpaEnforcement.enforce(auctionContext, bidderToUser, aliases) - .compose(ccpaResult -> tcfEnforcement.enforce( - auctionContext, - bidderToUser, - biddersToApplyTcf(bidderToUser.keySet(), ccpaResult), - aliases) - .map(tcfResult -> ListUtil.union(ccpaResult, tcfResult))) - .compose(bidderPrivacyResults -> activityEnforcement.enforce(bidderPrivacyResults, auctionContext)); - } + final List initialResults = bidderToUser.entrySet().stream() + .map(entry -> BidderPrivacyResult.builder() + .requestBidder(entry.getKey()) + .user(entry.getValue()) + .device(auctionContext.getBidRequest().getDevice()) + .build()) + .toList(); + + Future> composedResult = Future.succeededFuture(initialResults); - private static Set biddersToApplyTcf(Set bidders, List ccpaResult) { - final Set biddersToApplyTcf = new HashSet<>(bidders); - ccpaResult.stream() - .map(BidderPrivacyResult::getRequestBidder) - .forEach(biddersToApplyTcf::remove); + for (PrivacyEnforcement enforcement : enforcements) { + composedResult = composedResult.compose( + results -> enforcement.enforce(auctionContext, aliases, results)); + } - return biddersToApplyTcf; + return composedResult; } } diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcement.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcement.java index 48e098f63a6..86602099401 100644 --- a/src/main/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcement.java +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcement.java @@ -30,8 +30,9 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; -public class TcfEnforcement { +public class TcfEnforcement implements PrivacyEnforcement { private static final Logger logger = LoggerFactory.getLogger(TcfEnforcement.class); @@ -59,30 +60,25 @@ public Future> enforce(Set vendo .map(TcfResponse::getActions); } + @Override public Future> enforce(AuctionContext auctionContext, - Map bidderToUser, - Set bidders, - BidderAliases aliases) { + BidderAliases aliases, + List results) { - final Device device = auctionContext.getBidRequest().getDevice(); - final AccountGdprConfig accountGdprConfig = accountGdprConfig(auctionContext.getAccount()); final MetricName requestType = auctionContext.getRequestTypeMetric(); final ActivityInfrastructure activityInfrastructure = auctionContext.getActivityInfrastructure(); + final Set bidders = results.stream() + .map(BidderPrivacyResult::getRequestBidder) + .collect(Collectors.toSet()); return tcfDefinerService.resultForBidderNames( bidders, VendorIdResolver.of(aliases, bidderCatalog), auctionContext.getPrivacyContext().getTcfContext(), - accountGdprConfig) + accountGdprConfig(auctionContext.getAccount())) .map(TcfResponse::getActions) - .map(enforcements -> updateMetrics( - activityInfrastructure, - enforcements, - aliases, - requestType, - bidderToUser, - device)) - .map(enforcements -> bidderToPrivacyResult(enforcements, bidders, bidderToUser, device)); + .map(enforcements -> updateMetrics(activityInfrastructure, enforcements, aliases, requestType, results)) + .map(enforcements -> applyEnforcements(enforcements, results)); } private static AccountGdprConfig accountGdprConfig(Account account) { @@ -94,22 +90,21 @@ private Map updateMetrics(ActivityInfrastructu Map enforcements, BidderAliases aliases, MetricName requestType, - Map bidderToUser, - Device device) { - - final boolean isLmtEnforcedAndEnabled = isLmtEnforcedAndEnabled(device); + List results) { // Metrics should represent real picture of the bidding process, so if bidder request is blocked // by privacy then no reason to increment another metrics, like geo masked, etc. - for (final Map.Entry bidderEnforcement : enforcements.entrySet()) { - final String bidder = bidderEnforcement.getKey(); - final PrivacyEnforcementAction enforcement = bidderEnforcement.getValue(); - final User user = bidderToUser.get(bidder); + for (BidderPrivacyResult result : results) { + final String bidder = result.getRequestBidder(); + final User user = result.getUser(); + final Device device = result.getDevice(); + final PrivacyEnforcementAction enforcement = enforcements.get(bidder); final boolean requestBlocked = enforcement.isBlockBidderRequest(); final boolean ufpdRemoved = !requestBlocked && ((enforcement.isRemoveUserFpd() && shouldRemoveUserData(user)) || (enforcement.isMaskDeviceInfo() && shouldRemoveDeviceData(device))); + final boolean isLmtEnforcedAndEnabled = isLmtEnforcedAndEnabled(device); final boolean uidsRemoved = !requestBlocked && enforcement.isRemoveUserIds() && shouldRemoveUids(user); final boolean geoMasked = !requestBlocked && enforcement.isMaskGeo() && shouldMaskGeo(user, device); final boolean analyticsBlocked = !requestBlocked && enforcement.isBlockAnalyticsReport(); @@ -165,32 +160,19 @@ private boolean isLmtEnforcedAndEnabled(Device device) { return lmtEnforce && device != null && Objects.equals(device.getLmt(), 1); } - private List bidderToPrivacyResult(Map bidderToEnforcement, - Set bidders, - Map bidderToUser, - Device device) { - - final boolean isLmtEnabled = isLmtEnforcedAndEnabled(device); + private List applyEnforcements(Map enforcements, + List results) { - return bidders.stream() - .map(bidder -> createBidderPrivacyResult( - bidder, - bidderToUser.get(bidder), - device, - bidderToEnforcement, - isLmtEnabled)) + return results.stream() + .map(result -> applyEnforcement(enforcements.get(result.getRequestBidder()), result)) .toList(); } - private BidderPrivacyResult createBidderPrivacyResult(String bidder, - User user, - Device device, - Map bidderToEnforcement, - boolean isLmtEnabled) { + private BidderPrivacyResult applyEnforcement(PrivacyEnforcementAction enforcement, BidderPrivacyResult result) { + final String bidder = result.getRequestBidder(); - final PrivacyEnforcementAction privacyEnforcementAction = bidderToEnforcement.get(bidder); - final boolean blockBidderRequest = privacyEnforcementAction.isBlockBidderRequest(); - final boolean blockAnalyticsReport = privacyEnforcementAction.isBlockAnalyticsReport(); + final boolean blockBidderRequest = enforcement.isBlockBidderRequest(); + final boolean blockAnalyticsReport = enforcement.isBlockAnalyticsReport(); if (blockBidderRequest) { return BidderPrivacyResult.builder() @@ -200,14 +182,18 @@ private BidderPrivacyResult createBidderPrivacyResult(String bidder, .build(); } - final boolean maskUserFpd = privacyEnforcementAction.isRemoveUserFpd() || isLmtEnabled; - final boolean maskUserIds = privacyEnforcementAction.isRemoveUserIds() || isLmtEnabled; - final boolean maskGeo = privacyEnforcementAction.isMaskGeo() || isLmtEnabled; - final Set eidExceptions = privacyEnforcementAction.getEidExceptions(); + final User user = result.getUser(); + final Device device = result.getDevice(); + + final boolean isLmtEnabled = isLmtEnforcedAndEnabled(device); + final boolean maskUserFpd = enforcement.isRemoveUserFpd() || isLmtEnabled; + final boolean maskUserIds = enforcement.isRemoveUserIds() || isLmtEnabled; + final boolean maskGeo = enforcement.isMaskGeo() || isLmtEnabled; + final Set eidExceptions = enforcement.getEidExceptions(); final User maskedUser = userFpdTcfMask.maskUser(user, maskUserFpd, maskUserIds, eidExceptions); - final boolean maskIp = privacyEnforcementAction.isMaskDeviceIp() || isLmtEnabled; - final boolean maskDeviceInfo = privacyEnforcementAction.isMaskDeviceInfo() || isLmtEnabled; + final boolean maskIp = enforcement.isMaskDeviceIp() || isLmtEnabled; + final boolean maskDeviceInfo = enforcement.isMaskDeviceInfo() || isLmtEnabled; final Device maskedDevice = userFpdTcfMask.maskDevice(device, maskIp, maskGeo, maskDeviceInfo); return BidderPrivacyResult.builder() diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java index 8cc22f7be13..deaa768320c 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -54,11 +54,9 @@ import org.prebid.server.auction.privacy.contextfactory.AuctionPrivacyContextFactory; import org.prebid.server.auction.privacy.contextfactory.CookieSyncPrivacyContextFactory; import org.prebid.server.auction.privacy.contextfactory.SetuidPrivacyContextFactory; -import org.prebid.server.auction.privacy.enforcement.ActivityEnforcement; import org.prebid.server.auction.privacy.enforcement.CcpaEnforcement; -import org.prebid.server.auction.privacy.enforcement.CoppaEnforcement; +import org.prebid.server.auction.privacy.enforcement.PrivacyEnforcement; import org.prebid.server.auction.privacy.enforcement.PrivacyEnforcementService; -import org.prebid.server.auction.privacy.enforcement.TcfEnforcement; import org.prebid.server.auction.requestfactory.AmpRequestFactory; import org.prebid.server.auction.requestfactory.AuctionRequestFactory; import org.prebid.server.auction.requestfactory.Ortb2ImplicitParametersResolver; @@ -946,16 +944,8 @@ StoredResponseProcessor storedResponseProcessor(ApplicationSettings applicationS } @Bean - PrivacyEnforcementService privacyEnforcementService(CoppaEnforcement coppaEnforcement, - CcpaEnforcement ccpaEnforcement, - TcfEnforcement tcfEnforcement, - ActivityEnforcement activityEnforcement) { - - return new PrivacyEnforcementService( - coppaEnforcement, - ccpaEnforcement, - tcfEnforcement, - activityEnforcement); + PrivacyEnforcementService privacyEnforcementService(List enforcements) { + return new PrivacyEnforcementService(enforcements); } @Bean diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/PublicCountryIp.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/PublicCountryIp.groovy index 59fb0b34c25..9bdb94f007d 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/PublicCountryIp.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/PublicCountryIp.groovy @@ -4,7 +4,8 @@ enum PublicCountryIp { USA_IP("209.232.44.21", "d646:2414:17b2:f371:9b62:f176:b4c0:51cd"), UKR_IP("193.238.111.14", "3080:f30f:e4bc:0f56:41be:6aab:9d0a:58e2"), - CAN_IP("70.71.245.39", "f9b2:c742:1922:7d4b:7122:c7fc:8b75:98c8") + CAN_IP("70.71.245.39", "f9b2:c742:1922:7d4b:7122:c7fc:8b75:98c8"), + BGR_IP("31.211.128.0", "2002:1fd3:8000:0000:0000:0000:0000:0000") final String v4 final String v6 diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/RegsExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/RegsExt.groovy index 9a9c62fde81..d7e9cb2242e 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/RegsExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/RegsExt.groovy @@ -10,6 +10,7 @@ class RegsExt { @Deprecated(since = "enabling support of ortb 2.6") Integer gdpr + Integer coppa @Deprecated(since = "enabling support of ortb 2.6") String usPrivacy String gpc diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAmpSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAmpSpec.groovy index d4cfc7bbda9..34242d43cec 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAmpSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAmpSpec.groovy @@ -8,7 +8,11 @@ import org.prebid.server.functional.model.config.AccountPrivacyConfig import org.prebid.server.functional.model.config.PurposeConfig import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.pricefloors.Country import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.DistributionChannel +import org.prebid.server.functional.model.request.auction.Regs +import org.prebid.server.functional.model.request.auction.RegsExt import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.testcontainers.container.PrebidServerContainer import org.prebid.server.functional.util.PBSUtils @@ -27,6 +31,7 @@ import static org.prebid.server.functional.model.config.Purpose.P4 import static org.prebid.server.functional.model.config.PurposeEnforcement.BASIC import static org.prebid.server.functional.model.config.PurposeEnforcement.NO import static org.prebid.server.functional.model.mock.services.vendorlist.GvlSpecificationVersion.V3 +import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT import static org.prebid.server.functional.model.request.amp.ConsentType.BOGUS @@ -36,6 +41,7 @@ import static org.prebid.server.functional.model.request.auction.ActivityType.FE import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_EIDS import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_PRECISE_GEO import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_UFPD +import static org.prebid.server.functional.model.request.auction.PublicCountryIp.BGR_IP import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID import static org.prebid.server.functional.util.privacy.CcpaConsent.Signal.ENFORCED import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID @@ -686,4 +692,162 @@ class GdprAmpSpec extends PrivacyBaseSpec { where: tcfPolicyVersion << [TCF_POLICY_V4, TCF_POLICY_V5] } + + def "PBS should process with GDPR enforcement when GDPR and COPPA configurations are present in request"() { + given: "Valid consent string without basic ads" + def validConsentString = new TcfConsent.Builder() + .setPurposesLITransparency(DEVICE_ACCESS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build() + + and: "Amp default request" + def ampRequest = getGdprAmpRequest(validConsentString) + + and: "Bid request with gdpr and coppa config" + def ampStoredRequest = getGdprBidRequest(DistributionChannel.SITE, validConsentString).tap { + regs = new Regs(gdpr: gdpr, coppa: coppa, ext: new RegsExt(gdpr: extGdpr, coppa: extCoppa)) + setAccountId(ampRequest.account) + } + + and: "Save account config without eea countries into DB" + def accountGdprConfig = new AccountGdprConfig(enabled: true, eeaCountries: PBSUtils.getRandomEnum(Country.class, [BULGARIA])) + def account = getAccountWithGdpr(ampRequest.account, accountGdprConfig) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + and: "Flush metrics" + flushMetrics(privacyPbsService) + + when: "PBS processes amp request" + privacyPbsService.sendAmpRequest(ampRequest) + + then: "Bidder shouldn't be called" + assert !bidder.getBidderRequests(ampStoredRequest.id) + + then: "Metrics processed across activities should be updated" + def metrics = privacyPbsService.sendCollectedMetricsRequest() + assert metrics[TEMPLATE_ADAPTER_DISALLOWED_COUNT.getValue(ampStoredRequest, FETCH_BIDS)] == 1 + assert metrics[TEMPLATE_REQUEST_DISALLOWED_COUNT.getValue(ampStoredRequest, FETCH_BIDS)] == 1 + + where: + gdpr | coppa | extGdpr | extCoppa + 1 | 1 | 1 | 1 + 1 | 1 | 1 | 0 + 1 | 1 | 1 | null + 1 | 1 | 0 | 1 + 1 | 1 | 0 | 0 + 1 | 1 | 0 | null + 1 | 1 | null | 1 + 1 | 1 | null | 0 + 1 | 1 | null | null + 1 | 0 | 1 | 1 + 1 | 0 | 1 | 0 + 1 | 0 | 1 | null + 1 | 0 | 0 | 1 + 1 | 0 | 0 | 0 + 1 | 0 | 0 | null + 1 | 0 | null | 1 + 1 | 0 | null | 0 + 1 | 0 | null | null + 1 | null | 1 | 1 + 1 | null | 1 | 0 + 1 | null | 1 | null + 1 | null | 0 | 1 + 1 | null | 0 | 0 + 1 | null | 0 | null + 1 | null | null | 1 + 1 | null | null | 0 + 1 | null | null | null + + null | 1 | 1 | 1 + null | 1 | 1 | 0 + null | 1 | 1 | null + null | 0 | 1 | 1 + null | 0 | 1 | 0 + null | 0 | 1 | null + null | null | 1 | 1 + null | null | 1 | 0 + null | null | 1 | null + } + + def "PBS should process with GDPR enforcement when request comes from EEA IP with COPPA enabled"() { + given: "Valid consent string without basic ads" + def validConsentString = new TcfConsent.Builder() + .setPurposesLITransparency(DEVICE_ACCESS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build() + + and: "Amp default request" + def ampRequest = getGdprAmpRequest(validConsentString) + + and: "Bid request with gdpr and coppa config" + def ampStoredRequest = getGdprBidRequest(DistributionChannel.SITE, validConsentString).tap { + regs = new Regs(gdpr: 1, coppa: 1, ext: new RegsExt(gdpr: 1, coppa: 1)) + device.geo.country = requestCountry + device.geo.region = null + device.ip = requestIpV4 + device.ipv6 = requestIpV6 + } + + and: "Save account config without eea countries into DB" + def accountGdprConfig = new AccountGdprConfig(enabled: true, eeaCountries: accountCountry) + def account = getAccountWithGdpr(ampRequest.account, accountGdprConfig) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + and: "Flush metrics" + flushMetrics(privacyPbsService) + + when: "PBS processes amp request" + privacyPbsService.sendAmpRequest(ampRequest, header) + + then: "Bidder shouldn't be called" + assert !bidder.getBidderRequests(ampStoredRequest.id) + + then: "Metrics processed across activities should be updated" + def metrics = privacyPbsService.sendCollectedMetricsRequest() + assert metrics[TEMPLATE_ADAPTER_DISALLOWED_COUNT.getValue(ampStoredRequest, FETCH_BIDS)] == 1 + assert metrics[TEMPLATE_REQUEST_DISALLOWED_COUNT.getValue(ampStoredRequest, FETCH_BIDS)] == 1 + + where: + requestCountry | accountCountry | requestIpV4 | requestIpV6 | header + BULGARIA | BULGARIA | BGR_IP.v4 | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | null | BGR_IP.v4 | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | BULGARIA | BGR_IP.v4 | null | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | null | BGR_IP.v4 | null | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | BULGARIA | null | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | null | null | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | BULGARIA | null | null | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | null | null | null | ["X-Forwarded-For": BGR_IP.v4] + null | BULGARIA | BGR_IP.v4 | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + null | null | BGR_IP.v4 | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + null | BULGARIA | BGR_IP.v4 | null | ["X-Forwarded-For": BGR_IP.v4] + null | null | BGR_IP.v4 | null | ["X-Forwarded-For": BGR_IP.v4] + null | BULGARIA | null | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + null | null | null | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + null | BULGARIA | null | null | ["X-Forwarded-For": BGR_IP.v4] + null | null | null | null | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | BULGARIA | BGR_IP.v4 | BGR_IP.v6 | [:] + BULGARIA | null | BGR_IP.v4 | BGR_IP.v6 | [:] + BULGARIA | BULGARIA | BGR_IP.v4 | null | [:] + BULGARIA | null | BGR_IP.v4 | null | [:] + BULGARIA | BULGARIA | null | BGR_IP.v6 | [:] + BULGARIA | null | null | BGR_IP.v6 | [:] + BULGARIA | BULGARIA | null | null | [:] + BULGARIA | null | null | null | [:] + null | BULGARIA | BGR_IP.v4 | BGR_IP.v6 | [:] + null | null | BGR_IP.v4 | BGR_IP.v6 | [:] + null | BULGARIA | BGR_IP.v4 | null | [:] + null | null | BGR_IP.v4 | null | [:] + null | BULGARIA | null | BGR_IP.v6 | [:] + null | null | null | BGR_IP.v6 | [:] + null | BULGARIA | null | null | [:] + null | null | null | null | [:] + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy index c4a21342ab9..66003fa2716 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy @@ -5,7 +5,10 @@ import org.prebid.server.functional.model.ChannelType import org.prebid.server.functional.model.config.AccountGdprConfig import org.prebid.server.functional.model.config.PurposeConfig import org.prebid.server.functional.model.config.PurposeEnforcement +import org.prebid.server.functional.model.pricefloors.Country import org.prebid.server.functional.model.request.auction.DistributionChannel +import org.prebid.server.functional.model.request.auction.Regs +import org.prebid.server.functional.model.request.auction.RegsExt import org.prebid.server.functional.model.response.auction.ErrorType import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.testcontainers.container.PrebidServerContainer @@ -36,6 +39,7 @@ import static org.prebid.server.functional.model.request.auction.ActivityType.TR import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_PRECISE_GEO import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_UFPD import static org.prebid.server.functional.model.request.auction.Prebid.Channel +import static org.prebid.server.functional.model.request.auction.PublicCountryIp.BGR_IP import static org.prebid.server.functional.model.request.auction.TraceLevel.BASIC import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BLOCKED_PRIVACY @@ -805,4 +809,147 @@ class GdprAuctionSpec extends PrivacyBaseSpec { where: tcfPolicyVersion << [TCF_POLICY_V4, TCF_POLICY_V5] } + + def "PBS should process with GDPR enforcement when GDPR and COPPA configurations are present in request"() { + given: "Valid consent string without basic ads" + def validConsentString = new TcfConsent.Builder() + .setPurposesLITransparency(DEVICE_ACCESS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build() + + and: "Bid request with gdpr and coppa config" + def bidRequest = getGdprBidRequest(DistributionChannel.APP, validConsentString).tap { + regs = new Regs(gdpr: gdpr, coppa: coppa, ext: new RegsExt(gdpr: extGdpr, coppa: extCoppa)) + } + + and: "Save account config without eea countries into DB" + def accountGdprConfig = new AccountGdprConfig(enabled: true, eeaCountries: PBSUtils.getRandomEnum(Country.class, [BULGARIA])) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig) + accountDao.save(account) + + and: "Flush metrics" + flushMetrics(privacyPbsService) + + when: "PBS processes auction request" + privacyPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder shouldn't be called" + assert !bidder.getBidderRequests(bidRequest.id) + + then: "Metrics processed across activities should be updated" + def metrics = privacyPbsService.sendCollectedMetricsRequest() + assert metrics[TEMPLATE_ADAPTER_DISALLOWED_COUNT.getValue(bidRequest, FETCH_BIDS)] == 1 + assert metrics[TEMPLATE_REQUEST_DISALLOWED_COUNT.getValue(bidRequest, FETCH_BIDS)] == 1 + + where: + gdpr | coppa | extGdpr | extCoppa + 1 | 1 | 1 | 1 + 1 | 1 | 1 | 0 + 1 | 1 | 1 | null + 1 | 1 | 0 | 1 + 1 | 1 | 0 | 0 + 1 | 1 | 0 | null + 1 | 1 | null | 1 + 1 | 1 | null | 0 + 1 | 1 | null | null + 1 | 0 | 1 | 1 + 1 | 0 | 1 | 0 + 1 | 0 | 1 | null + 1 | 0 | 0 | 1 + 1 | 0 | 0 | 0 + 1 | 0 | 0 | null + 1 | 0 | null | 1 + 1 | 0 | null | 0 + 1 | 0 | null | null + 1 | null | 1 | 1 + 1 | null | 1 | 0 + 1 | null | 1 | null + 1 | null | 0 | 1 + 1 | null | 0 | 0 + 1 | null | 0 | null + 1 | null | null | 1 + 1 | null | null | 0 + 1 | null | null | null + + null | 1 | 1 | 1 + null | 1 | 1 | 0 + null | 1 | 1 | null + null | 0 | 1 | 1 + null | 0 | 1 | 0 + null | 0 | 1 | null + null | null | 1 | 1 + null | null | 1 | 0 + null | null | 1 | null + } + + def "PBS should process with GDPR enforcement when request comes from EEA IP with COPPA enabled"() { + given: "Valid consent string without basic ads" + def validConsentString = new TcfConsent.Builder() + .setPurposesLITransparency(DEVICE_ACCESS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build() + + and: "Bid request with gdpr and coppa config" + def bidRequest = getGdprBidRequest(DistributionChannel.APP, validConsentString).tap { + regs = new Regs(gdpr: 1, coppa: 1, ext: new RegsExt(gdpr: 1, coppa: 1)) + device.geo.country = requestCountry + device.geo.region = null + device.ip = requestIpV4 + device.ipv6 = requestIpV6 + } + + and: "Save account config without eea countries into DB" + def accountGdprConfig = new AccountGdprConfig(enabled: true, eeaCountries: accountCountry) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig) + accountDao.save(account) + + and: "Flush metrics" + flushMetrics(privacyPbsService) + + when: "PBS processes auction request" + privacyPbsService.sendAuctionRequest(bidRequest, header) + + then: "Bidder shouldn't be called" + assert !bidder.getBidderRequests(bidRequest.id) + + then: "Metrics processed across activities should be updated" + def metrics = privacyPbsService.sendCollectedMetricsRequest() + assert metrics[TEMPLATE_ADAPTER_DISALLOWED_COUNT.getValue(bidRequest, FETCH_BIDS)] == 1 + assert metrics[TEMPLATE_REQUEST_DISALLOWED_COUNT.getValue(bidRequest, FETCH_BIDS)] == 1 + + where: + requestCountry | accountCountry | requestIpV4 | requestIpV6 | header + BULGARIA | BULGARIA | BGR_IP.v4 | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | null | BGR_IP.v4 | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | BULGARIA | BGR_IP.v4 | null | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | null | BGR_IP.v4 | null | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | BULGARIA | null | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | null | null | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | BULGARIA | null | null | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | null | null | null | ["X-Forwarded-For": BGR_IP.v4] + null | BULGARIA | BGR_IP.v4 | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + null | null | BGR_IP.v4 | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + null | BULGARIA | BGR_IP.v4 | null | ["X-Forwarded-For": BGR_IP.v4] + null | null | BGR_IP.v4 | null | ["X-Forwarded-For": BGR_IP.v4] + null | BULGARIA | null | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + null | null | null | BGR_IP.v6 | ["X-Forwarded-For": BGR_IP.v4] + null | BULGARIA | null | null | ["X-Forwarded-For": BGR_IP.v4] + null | null | null | null | ["X-Forwarded-For": BGR_IP.v4] + BULGARIA | BULGARIA | BGR_IP.v4 | BGR_IP.v6 | [:] + BULGARIA | null | BGR_IP.v4 | BGR_IP.v6 | [:] + BULGARIA | BULGARIA | BGR_IP.v4 | null | [:] + BULGARIA | null | BGR_IP.v4 | null | [:] + BULGARIA | BULGARIA | null | BGR_IP.v6 | [:] + BULGARIA | null | null | BGR_IP.v6 | [:] + BULGARIA | BULGARIA | null | null | [:] + BULGARIA | null | null | null | [:] + null | BULGARIA | BGR_IP.v4 | BGR_IP.v6 | [:] + null | null | BGR_IP.v4 | BGR_IP.v6 | [:] + null | BULGARIA | BGR_IP.v4 | null | [:] + null | null | BGR_IP.v4 | null | [:] + null | BULGARIA | null | BGR_IP.v6 | [:] + null | null | null | BGR_IP.v6 | [:] + null | BULGARIA | null | null | [:] + null | null | null | null | [:] + } } diff --git a/src/test/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcementTest.java b/src/test/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcementTest.java index a6c2e3f3ae9..6e4662a501d 100644 --- a/src/test/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcementTest.java +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcementTest.java @@ -9,6 +9,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.activity.infrastructure.ActivityInfrastructure; +import org.prebid.server.auction.BidderAliases; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderPrivacyResult; import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; @@ -32,6 +33,9 @@ public class ActivityEnforcementTest { @Mock private ActivityInfrastructure activityInfrastructure; + @Mock + private BidderAliases bidderAliases; + @BeforeEach public void setUp() { target = new ActivityEnforcement(userFpdActivityMask); @@ -58,7 +62,8 @@ public void enforceShouldReturnExpectedResult() { final AuctionContext context = givenAuctionContext(); // when - final List result = target.enforce(singletonList(bidderPrivacyResult), context).result(); + final List result = + target.enforce(context, bidderAliases, singletonList(bidderPrivacyResult)).result(); //then assertThat(result).allSatisfy(privacyResult -> { diff --git a/src/test/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcementTest.java b/src/test/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcementTest.java index 5459e232386..9bf76d427f9 100644 --- a/src/test/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcementTest.java +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcementTest.java @@ -30,7 +30,6 @@ import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.UnaryOperator; @@ -89,16 +88,17 @@ public void setUp() { } @Test - public void enforceShouldReturnEmptyListWhenCcpaNotEnforced() { + public void enforceShouldNotModifyListWhenCcpaIsNotEnforced() { // given final AuctionContext auctionContext = givenAuctionContext(context -> context .privacyContext(PrivacyContext.of(Privacy.builder().ccpa(Ccpa.of("1YN-")).build(), null, null))); + final List initialResults = givenPrivacyResults(givenUser(), givenDevice()); // when - final List result = target.enforce(auctionContext, null, aliases).result(); + final List result = target.enforce(auctionContext, aliases, initialResults).result(); // then - assertThat(result).isEmpty(); + assertThat(result).containsExactlyInAnyOrderElementsOf(initialResults); verify(metrics).updatePrivacyCcpaMetrics( eq(activityInfrastructure), eq(true), @@ -112,13 +112,15 @@ public void enforceShouldConsiderEnforceCcpaConfigurationProperty() { // given final AuctionContext auctionContext = givenAuctionContext(context -> context.account(Account.empty("id"))); + final List initialResults = givenPrivacyResults(givenUser(), givenDevice()); + target = new CcpaEnforcement(userFpdCcpaMask, bidderCatalog, metrics, false); // when - final List result = target.enforce(auctionContext, null, aliases).result(); + final List result = target.enforce(auctionContext, aliases, initialResults).result(); // then - assertThat(result).isEmpty(); + assertThat(result).containsExactlyInAnyOrderElementsOf(initialResults); verify(metrics).updatePrivacyCcpaMetrics( eq(activityInfrastructure), eq(true), @@ -136,12 +138,13 @@ public void enforceShouldConsiderAccountCcpaEnabledProperty() { .ccpa(AccountCcpaConfig.builder().enabled(false).build()) .build()) .build())); + final List initialResults = givenPrivacyResults(givenUser(), givenDevice()); // when - final List result = target.enforce(auctionContext, null, aliases).result(); + final List result = target.enforce(auctionContext, aliases, initialResults).result(); // then - assertThat(result).isEmpty(); + assertThat(result).containsExactlyInAnyOrderElementsOf(initialResults); verify(metrics).updatePrivacyCcpaMetrics( eq(activityInfrastructure), eq(true), @@ -155,12 +158,13 @@ public void enforceShouldConsiderAccountCcpaEnabledForRequestTypeProperty() { // given final AuctionContext auctionContext = givenAuctionContext(context -> context .requestTypeMetric(MetricName.openrtb2app)); + final List initialResults = givenPrivacyResults(givenUser(), givenDevice()); // when - final List result = target.enforce(auctionContext, null, aliases).result(); + final List result = target.enforce(auctionContext, aliases, initialResults).result(); // then - assertThat(result).isEmpty(); + assertThat(result).containsExactlyInAnyOrderElementsOf(initialResults); verify(metrics).updatePrivacyCcpaMetrics( eq(activityInfrastructure), eq(true), @@ -174,21 +178,21 @@ public void enforceShouldTreatAllBiddersAsNoSale() { // given final AuctionContext auctionContext = givenAuctionContext(context -> context .bidRequest(BidRequest.builder() - .device(Device.builder().ip("originalDevice").build()) + .device(givenDevice()) .ext(ExtRequest.of(ExtRequestPrebid.builder() .nosale(singletonList("*")) .build())) .build())); - final Map bidderToUser = Map.of( - "bidder", User.builder().id("originalUser").build(), - "noSale", User.builder().id("originalUser").build()); + final List initialResults = List.of( + BidderPrivacyResult.builder().requestBidder("bidder").user(givenUser()).device(givenDevice()).build(), + BidderPrivacyResult.builder().requestBidder("noSale").user(givenUser()).device(givenDevice()).build()); // when - final List result = target.enforce(auctionContext, bidderToUser, aliases).result(); + final List result = target.enforce(auctionContext, aliases, initialResults).result(); // then - assertThat(result).isEmpty(); + assertThat(result).containsExactlyInAnyOrderElementsOf(initialResults); verify(metrics).updatePrivacyCcpaMetrics( eq(activityInfrastructure), eq(true), @@ -222,15 +226,23 @@ public void enforceShouldSkipNoSaleBiddersAndNotEnforcedByBidderConfig() { final AuctionContext auctionContext = givenAuctionContext(identity()); - final Map bidderToUser = Map.of( - "bidderAlias", User.builder().id("originalUser").build(), - "noSale", User.builder().id("originalUser").build()); + final List initialResults = List.of( + BidderPrivacyResult.builder() + .requestBidder("bidderAlias") + .user(givenUser()) + .device(givenDevice()) + .build(), + BidderPrivacyResult.builder() + .requestBidder("noSale") + .user(givenUser()) + .device(givenDevice()) + .build()); // when - final List result = target.enforce(auctionContext, bidderToUser, aliases).result(); + final List result = target.enforce(auctionContext, aliases, initialResults).result(); // then - assertThat(result).isEmpty(); + assertThat(result).containsExactlyInAnyOrderElementsOf(initialResults); verify(metrics).updatePrivacyCcpaMetrics( eq(activityInfrastructure), eq(true), @@ -250,20 +262,18 @@ public void enforceShouldReturnExpectedResult() { final AuctionContext auctionContext = givenAuctionContext(identity()); - final Map bidderToUser = Map.of( - "bidder", User.builder().id("originalUser").build(), - "noSale", User.builder().id("originalUser").build()); + final List initialResults = List.of( + BidderPrivacyResult.builder().requestBidder("bidder").user(givenUser()).device(givenDevice()).build(), + BidderPrivacyResult.builder().requestBidder("noSale").user(givenUser()).device(givenDevice()).build()); // when - final List result = target.enforce(auctionContext, bidderToUser, aliases).result(); + final List result = target.enforce(auctionContext, aliases, initialResults).result(); // then - assertThat(result) - .hasSize(1) - .allSatisfy(privacyResult -> { - assertThat(privacyResult.getUser()).isSameAs(maskedUser); - assertThat(privacyResult.getDevice()).isSameAs(maskedDevice); - }); + assertThat(result).containsExactlyInAnyOrder( + BidderPrivacyResult.builder().requestBidder("bidder").user(maskedUser).device(maskedDevice).build(), + BidderPrivacyResult.builder().requestBidder("noSale").user(givenUser()).device(givenDevice()).build()); + verify(metrics).updatePrivacyCcpaMetrics( eq(activityInfrastructure), eq(true), @@ -278,7 +288,7 @@ private AuctionContext givenAuctionContext( final AuctionContext.AuctionContextBuilder initialContext = AuctionContext.builder() .activityInfrastructure(activityInfrastructure) .bidRequest(BidRequest.builder() - .device(Device.builder().ip("originalDevice").build()) + .device(givenDevice()) .ext(ExtRequest.of(ExtRequestPrebid.builder() .nosale(singletonList("noSale")) .build())) @@ -301,4 +311,16 @@ private AuctionContext givenAuctionContext( return auctionContextCustomizer.apply(initialContext).build(); } + + private static List givenPrivacyResults(User user, Device device) { + return singletonList(BidderPrivacyResult.builder().requestBidder("bidder").user(user).device(device).build()); + } + + private static User givenUser() { + return User.builder().id("originalUser").build(); + } + + private static Device givenDevice() { + return Device.builder().ip("originalDevice").build(); + } } diff --git a/src/test/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcementTest.java b/src/test/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcementTest.java index 4b7e7de8628..9e9d47d0488 100644 --- a/src/test/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcementTest.java +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcementTest.java @@ -9,6 +9,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.activity.infrastructure.ActivityInfrastructure; +import org.prebid.server.auction.BidderAliases; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderPrivacyResult; import org.prebid.server.auction.privacy.enforcement.mask.UserFpdCoppaMask; @@ -17,13 +18,14 @@ import org.prebid.server.privacy.model.PrivacyContext; import java.util.List; -import java.util.Map; import java.util.Set; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; @ExtendWith(MockitoExtension.class) public class CoppaEnforcementTest { @@ -34,6 +36,8 @@ public class CoppaEnforcementTest { private Metrics metrics; @Mock private ActivityInfrastructure activityInfrastructure; + @Mock + private BidderAliases bidderAliases; private CoppaEnforcement target; @@ -43,29 +47,33 @@ public void setUp() { } @Test - public void isApplicableShouldReturnFalse() { + public void enforceShouldNotMaskDataWhenNotApplicable() { // given final AuctionContext auctionContext = AuctionContext.builder() + .activityInfrastructure(activityInfrastructure) .privacyContext(PrivacyContext.of(Privacy.builder().coppa(0).build(), null, null)) + .bidRequest(BidRequest.builder().build()) .build(); - // when and then - assertThat(target.isApplicable(auctionContext)).isFalse(); - } + final List initialResults = singletonList( + BidderPrivacyResult.builder() + .requestBidder("bidder") + .user(User.builder().id("originalUser").build()) + .device(Device.builder().ip("originalDevice").build()) + .build()); - @Test - public void isApplicableShouldReturnTrue() { - // given - final AuctionContext auctionContext = AuctionContext.builder() - .privacyContext(PrivacyContext.of(Privacy.builder().coppa(1).build(), null, null)) - .build(); + // when + final List results = + target.enforce(auctionContext, bidderAliases, initialResults).result(); - // when and then - assertThat(target.isApplicable(auctionContext)).isTrue(); + // then + assertThat(results).containsExactlyInAnyOrderElementsOf(initialResults); + verifyNoInteractions(userFpdCoppaMask); + verifyNoInteractions(metrics); } @Test - public void enforceShouldReturnExpectedResultAndEmitMetrics() { + public void enforceShouldMaskDataAndEmitMetricsWhenApplicable() { // given final User maskedUser = User.builder().id("maskedUser").build(); final Device maskedDevice = Device.builder().ip("maskedDevice").build(); @@ -75,12 +83,19 @@ public void enforceShouldReturnExpectedResultAndEmitMetrics() { final AuctionContext auctionContext = AuctionContext.builder() .activityInfrastructure(activityInfrastructure) - .bidRequest(BidRequest.builder().device(Device.builder().ip("originalDevice").build()).build()) + .privacyContext(PrivacyContext.of(Privacy.builder().coppa(1).build(), null, null)) + .bidRequest(BidRequest.builder().build()) .build(); - final Map bidderToUser = Map.of("bidder", User.builder().id("originalUser").build()); + + final List initialResults = singletonList( + BidderPrivacyResult.builder() + .requestBidder("bidder") + .user(User.builder().id("originalUser").build()) + .device(Device.builder().ip("originalDevice").build()) + .build()); // when - final List result = target.enforce(auctionContext, bidderToUser).result(); + final List result = target.enforce(auctionContext, bidderAliases, initialResults).result(); // then assertThat(result).allSatisfy(privacyResult -> { diff --git a/src/test/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementServiceTest.java b/src/test/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementServiceTest.java index f1a49f91738..4333eebb5d8 100644 --- a/src/test/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementServiceTest.java +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementServiceTest.java @@ -1,98 +1,70 @@ package org.prebid.server.auction.privacy.enforcement; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; import com.iab.openrtb.request.User; import io.vertx.core.Future; -import org.apache.commons.collections4.ListUtils; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.auction.BidderAliases; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderPrivacyResult; import java.util.List; import java.util.Map; -import static java.util.Collections.singleton; import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; +import static java.util.Collections.singletonMap; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; +import static org.prebid.server.assertion.FutureAssertion.assertThat; @ExtendWith(MockitoExtension.class) public class PrivacyEnforcementServiceTest { @Mock - private CoppaEnforcement coppaEnforcement; - @Mock - private CcpaEnforcement ccpaEnforcement; - @Mock - private TcfEnforcement tcfEnforcement; - @Mock - private ActivityEnforcement activityEnforcement; - - private PrivacyEnforcementService target; + private PrivacyEnforcement firstEnforcement; - @BeforeEach - public void setUp() { - target = new PrivacyEnforcementService( - coppaEnforcement, - ccpaEnforcement, - tcfEnforcement, - activityEnforcement); - } - - @Test - public void maskShouldUseCoppaEnforcementIfApplicable() { - // given - given(coppaEnforcement.isApplicable(any())).willReturn(true); - - final List bidderPrivacyResults = singletonList(null); - given(coppaEnforcement.enforce(any(), any())).willReturn(Future.succeededFuture(bidderPrivacyResults)); - - // when - final List result = target.mask(null, null, null).result(); + @Mock + private PrivacyEnforcement secondEnforcement; - // then - assertThat(result).isSameAs(bidderPrivacyResults); - verifyNoInteractions(ccpaEnforcement); - verifyNoInteractions(tcfEnforcement); - verifyNoInteractions(activityEnforcement); - } + @Mock + private BidderAliases bidderAliases; @Test - public void maskShouldReturnExpectedResult() { + public void maskShouldPassBidderPrivacyThroughAllEnforcements() { // given - given(coppaEnforcement.isApplicable(any())).willReturn(false); + final BidderPrivacyResult expectedResult = BidderPrivacyResult.builder() + .requestBidder("bidder") + .user(User.EMPTY) + .device(Device.builder().build()) + .build(); - given(ccpaEnforcement.enforce(any(), any(), any())).willReturn(Future.succeededFuture( - singletonList(BidderPrivacyResult.builder().requestBidder("bidder1").build()))); + given(firstEnforcement.enforce(any(), any(), any())) + .willReturn(Future.succeededFuture(singletonList(expectedResult))); + given(secondEnforcement.enforce(any(), any(), any())) + .willReturn(Future.succeededFuture(singletonList(expectedResult))); - given(tcfEnforcement.enforce(any(), any(), eq(singleton("bidder0")), any())) - .willReturn(Future.succeededFuture( - singletonList(BidderPrivacyResult.builder().requestBidder("bidder0").build()))); + final PrivacyEnforcementService target = new PrivacyEnforcementService( + List.of(firstEnforcement, secondEnforcement)); - given(activityEnforcement.enforce(any(), any())) - .willAnswer(invocation -> Future.succeededFuture(ListUtils.union( - invocation.getArgument(0), - singletonList(BidderPrivacyResult.builder().requestBidder("bidder2").build())))); + final AuctionContext auctionContext = AuctionContext.builder() + .bidRequest(BidRequest.builder().device(Device.builder().build()).build()) + .build(); - final Map bidderToUser = Map.of( - "bidder0", User.builder().build(), - "bidder1", User.builder().build()); + final User user = User.builder().id("originalUser").build(); + final Map bidderToUser = singletonMap("bidder", user); // when - final List result = target.mask(null, bidderToUser, null).result(); + final Future> result = target.mask(auctionContext, bidderToUser, bidderAliases); // then - assertThat(result).containsExactly( - BidderPrivacyResult.builder().requestBidder("bidder1").build(), - BidderPrivacyResult.builder().requestBidder("bidder0").build(), - BidderPrivacyResult.builder().requestBidder("bidder2").build()); - verify(coppaEnforcement, times(0)).enforce(any(), any()); + assertThat(result) + .isSucceeded() + .unwrap() + .asList() + .containsExactlyInAnyOrder(expectedResult); } } diff --git a/src/test/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcementTest.java b/src/test/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcementTest.java index 0dad839fc87..0ed33b4cce3 100644 --- a/src/test/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcementTest.java +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcementTest.java @@ -119,20 +119,20 @@ public void enforceShouldEmitExpectedMetricsWhenUserAndDeviceHavePrivacyData() { "bidder6", givenEnforcementAction(PrivacyEnforcementAction::setMaskDeviceInfo), "bidder7", givenEnforcementAction(PrivacyEnforcementAction::setRemoveUserFpd))); - final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithPrivacyData()); - final Map bidderToUser = Map.of( - "bidder0", givenUserWithPrivacyData(), - "bidder1Alias", givenUserWithPrivacyData(), - "bidder2", givenUserWithPrivacyData(), - "bidder3", givenUserWithPrivacyData(), - "bidder4", givenUserWithPrivacyData(), - "bidder5", givenUserWithPrivacyData(), - "bidder6", givenUserWithPrivacyData(), - "bidder7", givenUserWithPrivacyData()); - final Set bidders = Set.of(); + final Device device = givenDeviceWithPrivacyData(); + final AuctionContext auctionContext = givenAuctionContext(device); + final List initialResults = List.of( + givenBidderPrivacyResult("bidder0", givenUserWithPrivacyData(), device), + givenBidderPrivacyResult("bidder1Alias", givenUserWithPrivacyData(), device), + givenBidderPrivacyResult("bidder2", givenUserWithPrivacyData(), device), + givenBidderPrivacyResult("bidder3", givenUserWithPrivacyData(), device), + givenBidderPrivacyResult("bidder4", givenUserWithPrivacyData(), device), + givenBidderPrivacyResult("bidder5", givenUserWithPrivacyData(), device), + givenBidderPrivacyResult("bidder6", givenUserWithPrivacyData(), device), + givenBidderPrivacyResult("bidder7", givenUserWithPrivacyData(), device)); // when - target.enforce(auctionContext, bidderToUser, bidders, aliases); + target.enforce(auctionContext, aliases, initialResults); // then verifyMetric("bidder0", false, false, false, false, false, true); @@ -155,14 +155,13 @@ public void enforceShouldEmitExpectedMetricsWhenUserHasPrivacyData() { "bidder2", givenEnforcementAction(PrivacyEnforcementAction::setRemoveUserFpd))); final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithNoPrivacyData()); - final Map bidderToUser = Map.of( - "bidder0", givenUserWithPrivacyData(), - "bidder1", givenUserWithPrivacyData(), - "bidder2", givenUserWithPrivacyData()); - final Set bidders = Set.of(); + final List initialResults = List.of( + givenBidderPrivacyResult("bidder0", givenUserWithPrivacyData(), givenDeviceWithNoPrivacyData()), + givenBidderPrivacyResult("bidder1", givenUserWithPrivacyData(), givenDeviceWithNoPrivacyData()), + givenBidderPrivacyResult("bidder2", givenUserWithPrivacyData(), givenDeviceWithNoPrivacyData())); // when - target.enforce(auctionContext, bidderToUser, bidders, aliases); + target.enforce(auctionContext, aliases, initialResults); // then verifyMetric("bidder0", false, false, true, false, false, false); @@ -179,17 +178,17 @@ public void enforceShouldEmitExpectedMetricsWhenDeviceHavePrivacyData() { "bidder2", givenEnforcementAction(PrivacyEnforcementAction::setMaskDeviceInfo), "bidder3", givenEnforcementAction(PrivacyEnforcementAction::setRemoveUserFpd))); - final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithPrivacyData()); - final Map bidderToUser = Map.of( - "bidder0", givenUserWithNoPrivacyData(), - "bidder1", givenUserWithNoPrivacyData(), - "bidder2", givenUserWithNoPrivacyData(), - "bidder3", givenUserWithNoPrivacyData(), - "bidder4", givenUserWithNoPrivacyData()); - final Set bidders = Set.of(); + final Device device = givenDeviceWithPrivacyData(); + final AuctionContext auctionContext = givenAuctionContext(device); + final List initialResults = List.of( + givenBidderPrivacyResult("bidder0", givenUserWithNoPrivacyData(), device), + givenBidderPrivacyResult("bidder1", givenUserWithNoPrivacyData(), device), + givenBidderPrivacyResult("bidder2", givenUserWithNoPrivacyData(), device), + givenBidderPrivacyResult("bidder3", givenUserWithNoPrivacyData(), device), + givenBidderPrivacyResult("bidder4", givenUserWithNoPrivacyData(), device)); // when - target.enforce(auctionContext, bidderToUser, bidders, aliases); + target.enforce(auctionContext, aliases, initialResults); // then verifyMetric("bidder0", false, false, true, false, false, true); @@ -207,14 +206,14 @@ public void enforceShouldEmitExpectedMetricsWhenUserAndDeviceDoNotHavePrivacyDat PrivacyEnforcementAction::setRemoveUserFpd, PrivacyEnforcementAction::setMaskDeviceInfo))); - final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithNoPrivacyData()); - final Map bidderToUser = Map.of( - "bidder0", givenUserWithNoPrivacyData(), - "bidder1", givenUserWithNoPrivacyData()); - final Set bidders = Set.of(); + final Device device = givenDeviceWithNoPrivacyData(); + final AuctionContext auctionContext = givenAuctionContext(device); + final List initialResults = List.of( + givenBidderPrivacyResult("bidder0", givenUserWithNoPrivacyData(), device), + givenBidderPrivacyResult("bidder1", givenUserWithNoPrivacyData(), device)); // when - target.enforce(auctionContext, bidderToUser, bidders, aliases); + target.enforce(auctionContext, aliases, initialResults); // then verifyMetric("bidder0", false, false, false, false, false, false); @@ -226,12 +225,13 @@ public void enforceShouldEmitPrivacyLmtMetric() { // give givenPrivacyEnforcementActions(Map.of("bidder", givenEnforcementAction())); - final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithPrivacyData()); - final Map bidderToUser = Map.of("bidder", givenUserWithPrivacyData()); - final Set bidders = Set.of(); + final Device device = givenDeviceWithPrivacyData(); + final AuctionContext auctionContext = givenAuctionContext(device); + final List initialResults = List.of( + givenBidderPrivacyResult("bidder", givenUserWithPrivacyData(), device)); // when - target.enforce(auctionContext, bidderToUser, bidders, aliases); + target.enforce(auctionContext, aliases, initialResults); // then verifyMetric("bidder", false, false, false, false, false, true); @@ -242,12 +242,13 @@ public void enforceShouldNotEmitPrivacyLmtMetricWhenLmtNot1() { // give givenPrivacyEnforcementActions(Map.of("bidder", givenEnforcementAction())); - final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithNoPrivacyData()); - final Map bidderToUser = Map.of("bidder", givenUserWithPrivacyData()); - final Set bidders = Set.of(); + final Device device = givenDeviceWithNoPrivacyData(); + final AuctionContext auctionContext = givenAuctionContext(device); + final List initialResults = List.of( + givenBidderPrivacyResult("bidder", givenUserWithPrivacyData(), device)); // when - target.enforce(auctionContext, bidderToUser, bidders, aliases); + target.enforce(auctionContext, aliases, initialResults); // then verifyMetric("bidder", false, false, false, false, false, false); @@ -259,14 +260,15 @@ public void enforceShouldNotEmitPrivacyLmtMetricWhenLmtNotEnforced() { // give givenPrivacyEnforcementActions(Map.of("bidder", givenEnforcementAction())); - final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithPrivacyData()); - final Map bidderToUser = Map.of("bidder", givenUserWithPrivacyData()); - final Set bidders = Set.of(); + final Device device = givenDeviceWithPrivacyData(); + final AuctionContext auctionContext = givenAuctionContext(device); + final List initialResults = List.of( + givenBidderPrivacyResult("bidder", givenUserWithPrivacyData(), device)); target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, false); // when - target.enforce(auctionContext, bidderToUser, bidders, aliases); + target.enforce(auctionContext, aliases, initialResults); // then verifyMetric("bidder", false, false, false, false, false, false); @@ -297,15 +299,15 @@ public void enforceShouldMaskUserAndDeviceWhenRestrictionsEnforcedAndLmtNotEnabl PrivacyEnforcementAction::setBlockAnalyticsReport), "bidder2", givenEnforcementAction())); - final AuctionContext context = givenAuctionContext(givenDeviceWithNoPrivacyData()); - final Map bidderToUser = Map.of( - "bidder0", givenUserWithPrivacyData(), - "bidder1", givenUserWithPrivacyData(), - "bidder2", givenUserWithPrivacyData()); - final Set bidders = Set.of("bidder0", "bidder1", "bidder2"); + final Device device = givenDeviceWithNoPrivacyData(); + final AuctionContext context = givenAuctionContext(device); + final List initialResults = List.of( + givenBidderPrivacyResult("bidder0", givenUserWithPrivacyData(), device), + givenBidderPrivacyResult("bidder1", givenUserWithPrivacyData(), device), + givenBidderPrivacyResult("bidder2", givenUserWithPrivacyData(), device)); // when - final List result = target.enforce(context, bidderToUser, bidders, aliases).result(); + final List result = target.enforce(context, aliases, initialResults).result(); // then assertThat(result).containsExactlyInAnyOrder( @@ -343,12 +345,13 @@ public void enforceShouldMaskUserAndDeviceWhenRestrictionsNotEnforcedAndLmtEnabl givenPrivacyEnforcementActions(Map.of("bidder", givenEnforcementAction())); - final AuctionContext context = givenAuctionContext(givenDeviceWithPrivacyData()); - final Map bidderToUser = Map.of("bidder", givenUserWithPrivacyData()); - final Set bidders = Set.of("bidder"); + final Device device = givenDeviceWithPrivacyData(); + final AuctionContext context = givenAuctionContext(device); + final List initialResults = List.of( + givenBidderPrivacyResult("bidder", givenUserWithPrivacyData(), device)); // when - final List result = target.enforce(context, bidderToUser, bidders, aliases).result(); + final List result = target.enforce(context, aliases, initialResults).result(); // then assertThat(result).containsExactly( @@ -378,6 +381,10 @@ private AuctionContext givenAuctionContext(Device device) { .build(); } + private static BidderPrivacyResult givenBidderPrivacyResult(String bidder, User user, Device device) { + return BidderPrivacyResult.builder().requestBidder(bidder).user(user).device(device).build(); + } + private static Device givenDeviceWithPrivacyData() { return Device.builder() .ip("originalDevice")