diff --git a/.github/workflows/pr-functional-tests.yml b/.github/workflows/pr-functional-tests.yml index 8c28b56c0c4..610c6693193 100644 --- a/.github/workflows/pr-functional-tests.yml +++ b/.github/workflows/pr-functional-tests.yml @@ -20,7 +20,7 @@ jobs: java: [ 17 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 diff --git a/.github/workflows/pr-java-ci.yml b/.github/workflows/pr-java-ci.yml index d45b077602b..79a904c3636 100644 --- a/.github/workflows/pr-java-ci.yml +++ b/.github/workflows/pr-java-ci.yml @@ -20,7 +20,7 @@ jobs: java: [ 17 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 diff --git a/.github/workflows/pr-module-functional-tests.yml b/.github/workflows/pr-module-functional-tests.yml index 4fd6d51496a..d8f1e925a07 100644 --- a/.github/workflows/pr-module-functional-tests.yml +++ b/.github/workflows/pr-module-functional-tests.yml @@ -20,7 +20,7 @@ jobs: java: [ 17 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 @@ -33,4 +33,4 @@ jobs: run: mvn package -DskipUnitTests=true --file extra/pom.xml - name: Run module tests - run: mvn -B verify -DskipUnitTests=true -DskipFunctionalTests=true -Dtests.max-container-count=5 -DdockerfileName=Dockerfile-modules --file extra/pom.xml + run: mvn -B verify -DskipUnitTests=true -DskipFunctionalTests=true -DskipModuleFunctionalTests=false -Dtests.max-container-count=5 -DdockerfileName=Dockerfile-modules --file extra/pom.xml diff --git a/.github/workflows/release-asset-publish.yml b/.github/workflows/release-asset-publish.yml index 2c8781d9c48..1de13751c3a 100644 --- a/.github/workflows/release-asset-publish.yml +++ b/.github/workflows/release-asset-publish.yml @@ -14,7 +14,7 @@ jobs: matrix: java: [ 17 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 with: diff --git a/.github/workflows/trivy-security-check.yml b/.github/workflows/trivy-security-check.yml new file mode 100644 index 00000000000..044b7e39af6 --- /dev/null +++ b/.github/workflows/trivy-security-check.yml @@ -0,0 +1,27 @@ +name: Security Check + +on: + pull_request: + branches: [master] + +jobs: + build: + name: Trivy security check + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + ignore-unfixed: true + format: 'sarif' + output: 'trivy-results.sarif' + severity: 'CRITICAL,HIGH' + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: 'trivy-results.sarif' diff --git a/docs/application-settings.md b/docs/application-settings.md index 4999cd293a5..4abd9ac5bc2 100644 --- a/docs/application-settings.md +++ b/docs/application-settings.md @@ -54,6 +54,9 @@ Keep in mind following restrictions: to `sfN.enforce` value. - `privacy.gdpr.purpose-one-treatment-interpretation` - option that allows to skip the Purpose one enforcement workflow. Values: ignore, no-access-allowed, access-allowed. +- `privacy.gdpr.purposes.p4.eid.require_consent` - if equals to `true`, transmitting EIDs require P4 legal basis unless excepted. +- `privacy.gdpr.purposes.p4.eid.exceptions` - list of EID sources that are excepted from P4 enforcement and will be transmitted if any P2-P10 is consented. +- `privacy.gdpr.purposes.p4.eid.activity_transition` - defaults to `true`. If `true` and transmitEids is not specified, but transmitUfpd is specified, then the logic of transmitUfpd is used. This is to avoid breaking changes to existing configurations. The default value of the flag will be changed in a future release. - `metrics.verbosity-level` - defines verbosity level of metrics for this account, overrides `metrics.accounts` application settings configuration. - `analytics.auction-events.` - defines which channels are supported by analytics for this account - `analytics.modules..*` - space for `module-name` analytics module specific configuration, may be of any shape diff --git a/docs/developers/functional-tests.md b/docs/developers/functional-tests.md index 960bb7283dc..fd216eb89c5 100644 --- a/docs/developers/functional-tests.md +++ b/docs/developers/functional-tests.md @@ -46,7 +46,7 @@ You have two options for running modular tests: 1. Use `mvn verify -DdockerfileName=Dockerfile-modules` to include all previous steps (including Java tests and functional tests) because Groovy runs in the `failsafe:integration-test` phase. 2. For modular tests only, use a more granular command: -`mvn -B verify -DskipUnitTests=true -DskipFunctionalTests=true -DdockerfileName=Dockerfile-modules` +`mvn -B verify -DskipUnitTests=true -DskipFunctionalTests=true -DskipModuleFunctionalTests=false -DdockerfileName=Dockerfile-modules` ## Developing diff --git a/docs/metrics.md b/docs/metrics.md index 102e9ecc77a..41dd45cc916 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -106,8 +106,6 @@ Following metrics are collected and submitted if account is configured with `det ## /cookie_sync endpoint metrics - `cookie_sync_requests` - number of requests received -- `cookie_sync..gen` - number of times cookies was synced per bidder -- `cookie_sync..matches` - number of times cookie was already matched when synced per bidder - `cookie_sync..tcf.blocked` - number of times cookie sync was prevented by TCF per bidder ## /setuid endpoint metrics diff --git a/extra/bundle/pom.xml b/extra/bundle/pom.xml index d190470096f..b48eaf7780d 100644 --- a/extra/bundle/pom.xml +++ b/extra/bundle/pom.xml @@ -5,7 +5,7 @@ org.prebid prebid-server-aggregator - 2.12.0-SNAPSHOT + 2.13.0-SNAPSHOT ../../extra/pom.xml diff --git a/extra/modules/confiant-ad-quality/pom.xml b/extra/modules/confiant-ad-quality/pom.xml index 6e070d5e652..e04ca09ea57 100644 --- a/extra/modules/confiant-ad-quality/pom.xml +++ b/extra/modules/confiant-ad-quality/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 2.12.0-SNAPSHOT + 2.13.0-SNAPSHOT confiant-ad-quality diff --git a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/config/ConfiantAdQualityModuleConfiguration.java b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/config/ConfiantAdQualityModuleConfiguration.java index 142b6fa5139..7978153c34a 100644 --- a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/config/ConfiantAdQualityModuleConfiguration.java +++ b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/config/ConfiantAdQualityModuleConfiguration.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.vertx.core.Promise; import io.vertx.core.Vertx; -import org.prebid.server.auction.PrivacyEnforcementService; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; import org.prebid.server.hooks.modules.com.confiant.adquality.core.BidsScanner; import org.prebid.server.hooks.modules.com.confiant.adquality.core.RedisClient; import org.prebid.server.hooks.modules.com.confiant.adquality.core.RedisScanStateChecker; @@ -20,6 +20,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import java.util.Collections; import java.util.List; @ConditionalOnProperty(prefix = "hooks." + ConfiantAdQualityModule.CODE, name = "enabled", havingValue = "true") @@ -37,7 +38,7 @@ ConfiantAdQualityModule confiantAdQualityModule( RedisConfig redisConfig, RedisRetryConfig retryConfig, Vertx vertx, - PrivacyEnforcementService privacyEnforcementService, + UserFpdActivityMask userFpdActivityMask, ObjectMapper objectMapper) { final RedisConnectionConfig writeNodeConfig = redisConfig.getWriteNode(); @@ -55,14 +56,14 @@ ConfiantAdQualityModule confiantAdQualityModule( bidsScanner.start(scannerPromise); - return new ConfiantAdQualityModule(List.of( - new ConfiantAdQualityBidResponsesScanHook(bidsScanner, biddersToExcludeFromScan, privacyEnforcementService))); + return new ConfiantAdQualityModule(Collections.singletonList( + new ConfiantAdQualityBidResponsesScanHook(bidsScanner, biddersToExcludeFromScan, userFpdActivityMask))); } @Bean ObjectMapper objectMapper() { return new ObjectMapper(); - }; + } @Bean @ConfigurationProperties(prefix = "hooks.modules.confiant-ad-quality.redis-config") diff --git a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisParser.java b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisParser.java index 9ff57b14f9d..a516497146d 100644 --- a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisParser.java +++ b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisParser.java @@ -41,7 +41,7 @@ public BidsScanResult parseBidsScanResult(String redisResponse) { } catch (JsonProcessingException errorParse) { message = String.format("Error during parse redis response: %s", redisResponse); } - logger.info(message); + logger.warn(message); return BidsScanResult.builder() .bidScanResults(Collections.emptyList()) .debugMessages(List.of(message)) diff --git a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/v1/ConfiantAdQualityBidResponsesScanHook.java b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/v1/ConfiantAdQualityBidResponsesScanHook.java index 96c70c5dc9e..4cf66880bef 100644 --- a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/v1/ConfiantAdQualityBidResponsesScanHook.java +++ b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/v1/ConfiantAdQualityBidResponsesScanHook.java @@ -9,9 +9,9 @@ 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.BidRequestActivityInvocationPayload; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; import org.prebid.server.hooks.execution.v1.bidder.AllProcessedBidResponsesPayloadImpl; import org.prebid.server.hooks.modules.com.confiant.adquality.core.AnalyticsMapper; import org.prebid.server.hooks.modules.com.confiant.adquality.core.BidsMapper; @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -37,19 +38,16 @@ public class ConfiantAdQualityBidResponsesScanHook implements AllProcessedBidRes private static final String CODE = "confiant-ad-quality-bid-responses-scan-hook"; private final BidsScanner bidsScanner; - private final List biddersToExcludeFromScan; + private final UserFpdActivityMask userFpdActivityMask; - private final PrivacyEnforcementService privacyEnforcementService; - - public ConfiantAdQualityBidResponsesScanHook( - BidsScanner bidsScanner, - List biddersToExcludeFromScan, - PrivacyEnforcementService privacyEnforcementService) { + public ConfiantAdQualityBidResponsesScanHook(BidsScanner bidsScanner, + List biddersToExcludeFromScan, + UserFpdActivityMask userFpdActivityMask) { - this.bidsScanner = bidsScanner; - this.biddersToExcludeFromScan = biddersToExcludeFromScan; - this.privacyEnforcementService = privacyEnforcementService; + this.bidsScanner = Objects.requireNonNull(bidsScanner); + this.biddersToExcludeFromScan = Objects.requireNonNull(biddersToExcludeFromScan); + this.userFpdActivityMask = Objects.requireNonNull(userFpdActivityMask); } @Override @@ -60,7 +58,7 @@ public Future> call( final BidRequest bidRequest = getBidRequest(auctionInvocationContext); final List responses = allProcessedBidResponsesPayload.bidResponses(); final Map> needScanMap = responses.stream() - .collect(Collectors.groupingBy(bidderResponse -> !biddersToExcludeFromScan.contains(bidderResponse.getBidder()))); + .collect(Collectors.groupingBy(this::isScanRequired)); final List toScan = needScanMap.getOrDefault(true, Collections.emptyList()); final List avoidScan = needScanMap.getOrDefault(false, Collections.emptyList()); @@ -69,6 +67,11 @@ public Future> call( .map(scanResult -> toInvocationResult(scanResult, toScan, avoidScan, auctionInvocationContext)); } + private boolean isScanRequired(BidderResponse bidderResponse) { + return !biddersToExcludeFromScan.contains(bidderResponse.getBidder()) + && !bidderResponse.getSeatBid().getBids().isEmpty(); + } + private BidRequest getBidRequest(AuctionInvocationContext auctionInvocationContext) { final AuctionContext auctionContext = auctionInvocationContext.auctionContext(); final BidRequest bidRequest = auctionContext.getBidRequest(); @@ -78,10 +81,8 @@ private BidRequest getBidRequest(AuctionInvocationContext auctionInvocationConte final boolean disallowTransmitGeo = !auctionContext.getActivityInfrastructure() .isAllowed(Activity.TRANSMIT_GEO, activityInvocationPayload); - final User maskedUser = privacyEnforcementService - .maskUserConsideringActivityRestrictions(bidRequest.getUser(), true, disallowTransmitGeo); - final Device maskedDevice = privacyEnforcementService - .maskDeviceConsideringActivityRestrictions(bidRequest.getDevice(), true, disallowTransmitGeo); + final User maskedUser = userFpdActivityMask.maskUser(bidRequest.getUser(), true, true, disallowTransmitGeo); + final Device maskedDevice = userFpdActivityMask.maskDevice(bidRequest.getDevice(), true, disallowTransmitGeo); return bidRequest.toBuilder() .user(maskedUser) diff --git a/extra/modules/confiant-ad-quality/src/test/java/org/prebid/server/hooks/modules/com/confiant/adquality/v1/ConfiantAdQualityBidResponsesScanHookTest.java b/extra/modules/confiant-ad-quality/src/test/java/org/prebid/server/hooks/modules/com/confiant/adquality/v1/ConfiantAdQualityBidResponsesScanHookTest.java index ef8cef2c4c0..8746a3e10b2 100644 --- a/extra/modules/confiant-ad-quality/src/test/java/org/prebid/server/hooks/modules/com/confiant/adquality/v1/ConfiantAdQualityBidResponsesScanHookTest.java +++ b/extra/modules/confiant-ad-quality/src/test/java/org/prebid/server/hooks/modules/com/confiant/adquality/v1/ConfiantAdQualityBidResponsesScanHookTest.java @@ -13,9 +13,10 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.prebid.server.activity.infrastructure.ActivityInfrastructure; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidderResponse; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; +import org.prebid.server.bidder.model.BidderSeatBid; import org.prebid.server.hooks.execution.v1.bidder.AllProcessedBidResponsesPayloadImpl; import org.prebid.server.hooks.modules.com.confiant.adquality.core.BidsMapper; import org.prebid.server.hooks.modules.com.confiant.adquality.core.BidsScanResult; @@ -60,29 +61,29 @@ public class ConfiantAdQualityBidResponsesScanHookTest { private ActivityInfrastructure activityInfrastructure; @Mock - private PrivacyEnforcementService privacyEnforcementService; + private UserFpdActivityMask userFpdActivityMask; - private ConfiantAdQualityBidResponsesScanHook hook; + private ConfiantAdQualityBidResponsesScanHook target; private final RedisParser redisParser = new RedisParser(new ObjectMapper()); @Before public void setUp() { - hook = new ConfiantAdQualityBidResponsesScanHook(bidsScanner, List.of(), privacyEnforcementService); + target = new ConfiantAdQualityBidResponsesScanHook(bidsScanner, List.of(), userFpdActivityMask); } @Test - public void shouldHaveValidInitialConfigs() { + public void codeShouldHaveValidConfigsWhenInitialized() { // given // when // then - assertThat(hook.code()).isEqualTo("confiant-ad-quality-bid-responses-scan-hook"); + assertThat(target.code()).isEqualTo("confiant-ad-quality-bid-responses-scan-hook"); } @Test - public void shouldReturnResultWithNoActionWhenRedisHasNoAnswer() { + public void callShouldReturnResultWithNoActionWhenRedisHasNoAnswer() { // given final BidsScanResult bidsScanResult = BidsScanResult.builder() .bidScanResults(Collections.emptyList()) @@ -93,7 +94,7 @@ public void shouldReturnResultWithNoActionWhenRedisHasNoAnswer() { doReturn(getAuctionContext()).when(auctionInvocationContext).auctionContext(); // when - final Future> future = hook.call( + final Future> future = target.call( allProcessedBidResponsesPayload, auctionInvocationContext); // then @@ -109,7 +110,7 @@ public void shouldReturnResultWithNoActionWhenRedisHasNoAnswer() { } @Test - public void shouldReturnResultWithUpdateActionWhenRedisHasFoundSomeIssues() { + public void callShouldReturnResultWithUpdateActionWhenRedisHasFoundSomeIssues() { // given final BidsScanResult bidsScanResult = redisParser.parseBidsScanResult( "[[[{\"tag_key\": \"tag\", \"issues\":[{\"spec_name\":\"malicious_domain\",\"value\":\"ads.deceivenetworks.net\",\"first_adinstance\":\"e91e8da982bb8b7f80100426\"}]}]]]"); @@ -120,7 +121,7 @@ public void shouldReturnResultWithUpdateActionWhenRedisHasFoundSomeIssues() { .when(allProcessedBidResponsesPayload).bidResponses(); // when - final Future> future = hook.call( + final Future> future = target.call( allProcessedBidResponsesPayload, auctionInvocationContext); // then @@ -144,7 +145,7 @@ public void shouldReturnResultWithUpdateActionWhenRedisHasFoundSomeIssues() { } @Test - public void shouldSubmitBidsToScan() { + public void callShouldSubmitBidsToScanWhenBidsCome() { // given final BidsScanResult bidsScanResult = redisParser.parseBidsScanResult( "[[[{\"tag_key\": \"tag\", \"issues\":[{\"spec_name\":\"malicious_domain\",\"value\":\"ads.deceivenetworks.net\",\"first_adinstance\":\"e91e8da982bb8b7f80100426\"}]}]]]"); @@ -153,14 +154,14 @@ public void shouldSubmitBidsToScan() { doReturn(getAuctionContext()).when(auctionInvocationContext).auctionContext(); // when - hook.call(allProcessedBidResponsesPayload, auctionInvocationContext); + target.call(allProcessedBidResponsesPayload, auctionInvocationContext); // then verify(bidsScanner).submitBids(any()); } @Test - public void shouldSubmitToScanBidsWhichAreNotPartOfTheExcludeToScanConfig() { + public void callShouldSubmitToScanBidsWhichAreNotPartOfTheExcludeToScanListWhenHookIsConfiguredWithExcludeToScanList() { // given final String secureBidderName = "securebidder"; final String notSecureBadBidderName = "notsecurebadbidder"; @@ -168,9 +169,6 @@ public void shouldSubmitToScanBidsWhichAreNotPartOfTheExcludeToScanConfig() { final BidderResponse secureBidderResponse = AdQualityModuleTestUtils.getBidderResponse(secureBidderName, "imp_a", "bid_id_a"); final BidderResponse notSecureBadBidderResponse = AdQualityModuleTestUtils.getBidderResponse(notSecureBadBidderName, "imp_b", "bid_id_b"); final BidderResponse notSecureGoodBidderResponse = AdQualityModuleTestUtils.getBidderResponse(notSecureGoodBidderName, "imp_c", "bid_id_c"); - - final ConfiantAdQualityBidResponsesScanHook hookWithExcludeConfig = new ConfiantAdQualityBidResponsesScanHook( - bidsScanner, List.of(secureBidderName), privacyEnforcementService); final BidsScanResult bidsScanResult = redisParser.parseBidsScanResult( "[[[{\"tag_key\": \"tag\", \"issues\":[{\"spec_name\":\"malicious_domain\",\"value\":\"ads.deceivenetworks.net\",\"first_adinstance\":\"e91e8da982bb8b7f80100426\"}]}]],[[{\"tag_key\": \"key_b\", \"imp_id\": \"imp_b\", \"issues\": []}]]]]"); final AuctionContext auctionContext = AuctionContext.builder() @@ -178,12 +176,14 @@ public void shouldSubmitToScanBidsWhichAreNotPartOfTheExcludeToScanConfig() { .bidRequest(BidRequest.builder().cur(List.of("USD")).build()) .build(); + target = new ConfiantAdQualityBidResponsesScanHook(bidsScanner, List.of(secureBidderName), userFpdActivityMask); + doReturn(List.of(secureBidderResponse, notSecureBadBidderResponse, notSecureGoodBidderResponse)).when(allProcessedBidResponsesPayload).bidResponses(); doReturn(Future.succeededFuture(bidsScanResult)).when(bidsScanner).submitBids(any()); doReturn(auctionContext).when(auctionInvocationContext).auctionContext(); // when - final Future> invocationResult = hookWithExcludeConfig + final Future> invocationResult = target .call(allProcessedBidResponsesPayload, auctionInvocationContext); // then @@ -219,14 +219,67 @@ public void shouldSubmitToScanBidsWhichAreNotPartOfTheExcludeToScanConfig() { } @Test - public void shouldSubmitBidsWithoutMaskedGeoInfoWhenTransmitGeoIsAllowed() { + public void callShouldSubmitToScanOnlyBidsWithDataWhenSomeBiddersRespondWithEmptyResponse() { + // given + final String secureBidderName = "securebidder"; + final String notSecureBadBidderName = "notsecurebadbidder"; + final String emptyBidderName = "emptybidder"; + final BidderResponse secureBidderResponse = AdQualityModuleTestUtils.getBidderResponse(secureBidderName, "imp_a", "bid_id_a"); + final BidderResponse notSecureBadBidderResponse = AdQualityModuleTestUtils.getBidderResponse(notSecureBadBidderName, "imp_b", "bid_id_b"); + final BidderResponse emptyBidderResponse = getEmptyBidderResponse(emptyBidderName); + final BidsScanResult bidsScanResult = redisParser.parseBidsScanResult( + "[[[{\"tag_key\": \"tag\", \"issues\":[{\"spec_name\":\"malicious_domain\",\"value\":\"ads.deceivenetworks.net\",\"first_adinstance\":\"e91e8da982bb8b7f80100426\"}]}]]]"); + final AuctionContext auctionContext = AuctionContext.builder() + .activityInfrastructure(activityInfrastructure) + .bidRequest(BidRequest.builder().cur(List.of("USD")).build()) + .build(); + + target = new ConfiantAdQualityBidResponsesScanHook(bidsScanner, List.of(secureBidderName), userFpdActivityMask); + + doReturn(List.of(secureBidderResponse, notSecureBadBidderResponse, emptyBidderResponse)).when(allProcessedBidResponsesPayload).bidResponses(); + doReturn(Future.succeededFuture(bidsScanResult)).when(bidsScanner).submitBids(any()); + doReturn(auctionContext).when(auctionInvocationContext).auctionContext(); + + // when + final Future> invocationResult = target + .call(allProcessedBidResponsesPayload, auctionInvocationContext); + + // then + verify(bidsScanner).submitBids( + BidsMapper.toRedisBidsFromBidResponses(auctionContext.getBidRequest(), List.of(notSecureBadBidderResponse)) + ); + + final PayloadUpdate payloadUpdate = invocationResult.result().payloadUpdate(); + final AllProcessedBidResponsesPayloadImpl initPayloadToUpdate = AllProcessedBidResponsesPayloadImpl.of( + asList(secureBidderResponse, notSecureBadBidderResponse, emptyBidderResponse)); + final AllProcessedBidResponsesPayloadImpl resultPayloadAfterUpdate = AllProcessedBidResponsesPayloadImpl.of( + asList(secureBidderResponse, emptyBidderResponse)); + + assertThat(payloadUpdate.apply(initPayloadToUpdate)).isEqualTo(resultPayloadAfterUpdate); + assertThat(invocationResult.result().analyticsTags().activities()).isEqualTo(singletonList(ActivityImpl.of( + "ad-scan", "success", List.of( + ResultImpl.of("skipped", null, AppliedToImpl.builder() + .bidders(List.of(secureBidderName, emptyBidderName)) + .impIds(List.of("imp_a")) + .bidIds(List.of("bid_id_a")) + .build()), + ResultImpl.of("inspected-has-issue", null, AppliedToImpl.builder() + .bidders(List.of(notSecureBadBidderName)) + .impIds(List.of("imp_b")) + .bidIds(List.of("bid_id_b")) + .build())) + ))); + } + + @Test + public void callShouldSubmitBidsWithoutMaskedGeoInfoWhenTransmitGeoIsAllowed() { // given final Boolean transmitGeoIsAllowed = true; final BidsScanResult bidsScanResult = redisParser.parseBidsScanResult( "[[[{\"tag_key\": \"tag\", \"issues\":[{\"spec_name\":\"malicious_domain\",\"value\":\"ads.deceivenetworks.net\",\"first_adinstance\":\"e91e8da982bb8b7f80100426\"}]}]]]"); - final User user = privacyEnforcementService.maskUserConsideringActivityRestrictions( - getUser(), true, !transmitGeoIsAllowed); - final Device device = privacyEnforcementService.maskDeviceConsideringActivityRestrictions( + final User user = userFpdActivityMask.maskUser( + getUser(), true, true, !transmitGeoIsAllowed); + final Device device = userFpdActivityMask.maskDevice( getDevice(), true, !transmitGeoIsAllowed); bidsScanner.enableScan(); @@ -235,7 +288,7 @@ public void shouldSubmitBidsWithoutMaskedGeoInfoWhenTransmitGeoIsAllowed() { doReturn(getAuctionContext()).when(auctionInvocationContext).auctionContext(); // when - hook.call(allProcessedBidResponsesPayload, auctionInvocationContext); + target.call(allProcessedBidResponsesPayload, auctionInvocationContext); // then verify(bidsScanner).submitBids( @@ -248,14 +301,14 @@ public void shouldSubmitBidsWithoutMaskedGeoInfoWhenTransmitGeoIsAllowed() { } @Test - public void shouldSubmitBidsWithMaskedGeoInfoWhenTransmitGeoIsNotAllowed() { + public void callShouldSubmitBidsWithMaskedGeoInfoWhenTransmitGeoIsNotAllowed() { // given final Boolean transmitGeoIsAllowed = false; final BidsScanResult bidsScanResult = redisParser.parseBidsScanResult( "[[[{\"tag_key\": \"tag\", \"issues\":[{\"spec_name\":\"malicious_domain\",\"value\":\"ads.deceivenetworks.net\",\"first_adinstance\":\"e91e8da982bb8b7f80100426\"}]}]]]"); - final User user = privacyEnforcementService.maskUserConsideringActivityRestrictions( - getUser(), true, !transmitGeoIsAllowed); - final Device device = privacyEnforcementService.maskDeviceConsideringActivityRestrictions( + final User user = userFpdActivityMask.maskUser( + getUser(), true, true, !transmitGeoIsAllowed); + final Device device = userFpdActivityMask.maskDevice( getDevice(), true, !transmitGeoIsAllowed); bidsScanner.enableScan(); @@ -264,7 +317,7 @@ public void shouldSubmitBidsWithMaskedGeoInfoWhenTransmitGeoIsNotAllowed() { doReturn(getAuctionContext()).when(auctionInvocationContext).auctionContext(); // when - hook.call(allProcessedBidResponsesPayload, auctionInvocationContext); + target.call(allProcessedBidResponsesPayload, auctionInvocationContext); // then verify(bidsScanner).submitBids( @@ -277,7 +330,7 @@ public void shouldSubmitBidsWithMaskedGeoInfoWhenTransmitGeoIsNotAllowed() { } @Test - public void shouldReturnResultWithDebugInfoWhenDebugIsEnabledAndRequestIsBroken() { + public void callShouldReturnResultWithDebugInfoWhenDebugIsEnabledAndRequestIsBroken() { // given final BidsScanResult bidsScanResult = redisParser.parseBidsScanResult("[[[{\"t"); @@ -286,7 +339,7 @@ public void shouldReturnResultWithDebugInfoWhenDebugIsEnabledAndRequestIsBroken( doReturn(getAuctionContext()).when(auctionInvocationContext).auctionContext(); // when - final Future> future = hook.call( + final Future> future = target.call( allProcessedBidResponsesPayload, auctionInvocationContext); // then @@ -302,7 +355,7 @@ public void shouldReturnResultWithDebugInfoWhenDebugIsEnabledAndRequestIsBroken( } @Test - public void shouldReturnResultWithoutDebugInfoWhenDebugIsDisabledAndRequestIsBroken() { + public void callShouldReturnResultWithoutDebugInfoWhenDebugIsDisabledAndRequestIsBroken() { // given final BidsScanResult bidsScanResult = redisParser.parseBidsScanResult("[[[{\"t"); @@ -311,7 +364,7 @@ public void shouldReturnResultWithoutDebugInfoWhenDebugIsDisabledAndRequestIsBro doReturn(getAuctionContext()).when(auctionInvocationContext).auctionContext(); // when - final Future> future = hook.call( + final Future> future = target.call( allProcessedBidResponsesPayload, auctionInvocationContext); // then @@ -344,4 +397,10 @@ private static User getUser() { private static Device getDevice() { return Device.builder().geo(Geo.builder().country("country-d").region("region-d").build()).build(); } + + private static BidderResponse getEmptyBidderResponse(String bidderName) { + return BidderResponse.of(bidderName, BidderSeatBid.builder() + .bids(Collections.emptyList()) + .build(), 5); + } } diff --git a/extra/modules/ortb2-blocking/pom.xml b/extra/modules/ortb2-blocking/pom.xml index 45cbae3b016..27bdb434d58 100644 --- a/extra/modules/ortb2-blocking/pom.xml +++ b/extra/modules/ortb2-blocking/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 2.12.0-SNAPSHOT + 2.13.0-SNAPSHOT ortb2-blocking diff --git a/extra/modules/pb-richmedia-filter/pom.xml b/extra/modules/pb-richmedia-filter/pom.xml index cff942ae523..d5f8b4059b4 100644 --- a/extra/modules/pb-richmedia-filter/pom.xml +++ b/extra/modules/pb-richmedia-filter/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 2.12.0-SNAPSHOT + 2.13.0-SNAPSHOT pb-richmedia-filter diff --git a/extra/modules/pom.xml b/extra/modules/pom.xml index 623c74b038a..32f53d9ae05 100644 --- a/extra/modules/pom.xml +++ b/extra/modules/pom.xml @@ -5,7 +5,7 @@ org.prebid prebid-server-aggregator - 2.12.0-SNAPSHOT + 2.13.0-SNAPSHOT ../../extra/pom.xml @@ -43,7 +43,7 @@ org.prebid prebid-server - 2.12.0-SNAPSHOT + 2.13.0-SNAPSHOT org.projectlombok diff --git a/extra/pom.xml b/extra/pom.xml index ae0241b02a0..dd0ac70cacc 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -4,7 +4,7 @@ org.prebid prebid-server-aggregator - 2.12.0-SNAPSHOT + 2.13.0-SNAPSHOT pom diff --git a/pom.xml b/pom.xml index 269e717a44c..4cca09abf73 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.prebid prebid-server-aggregator - 2.12.0-SNAPSHOT + 2.13.0-SNAPSHOT extra/pom.xml @@ -30,7 +30,7 @@ 2.5.6 2.0.1.Final 3.9.10 - 3.12.0 + 3.14.0 4.4 1.26.0 3.6.1 @@ -86,7 +86,7 @@ 1.13.1 2.10.0 false - false + true false 1.6.2 3.0.0 diff --git a/src/main/java/com/iab/openrtb/request/User.java b/src/main/java/com/iab/openrtb/request/User.java index 5703d7505e2..c55f6b7e1af 100644 --- a/src/main/java/com/iab/openrtb/request/User.java +++ b/src/main/java/com/iab/openrtb/request/User.java @@ -17,6 +17,8 @@ @Value public class User { + public static final User EMPTY = User.builder().build(); + /** * Exchange-specific ID for the user. */ diff --git a/src/main/java/org/prebid/server/activity/Activity.java b/src/main/java/org/prebid/server/activity/Activity.java index 7492470ec28..ac4b87293bc 100644 --- a/src/main/java/org/prebid/server/activity/Activity.java +++ b/src/main/java/org/prebid/server/activity/Activity.java @@ -16,6 +16,9 @@ public enum Activity { @JsonProperty("transmitUfpd") TRANSMIT_UFPD, + @JsonProperty("transmitEids") + TRANSMIT_EIDS, + @JsonProperty("transmitPreciseGeo") TRANSMIT_GEO, diff --git a/src/main/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreator.java b/src/main/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreator.java index 290a81810d9..9339582f1e1 100644 --- a/src/main/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreator.java +++ b/src/main/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreator.java @@ -15,7 +15,12 @@ import org.prebid.server.metric.Metrics; import org.prebid.server.proto.openrtb.ext.request.TraceLevel; import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountGdprConfig; import org.prebid.server.settings.model.AccountPrivacyConfig; +import org.prebid.server.settings.model.GdprConfig; +import org.prebid.server.settings.model.Purpose; +import org.prebid.server.settings.model.PurposeEid; +import org.prebid.server.settings.model.Purposes; import org.prebid.server.settings.model.activity.AccountActivityConfiguration; import org.prebid.server.settings.model.activity.privacy.AccountPrivacyModuleConfig; @@ -27,6 +32,7 @@ import java.util.Objects; import java.util.Optional; import java.util.function.BinaryOperator; +import java.util.function.Function; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -36,14 +42,20 @@ public class ActivityInfrastructureCreator { private static final Logger logger = LoggerFactory.getLogger(ActivityInfrastructureCreator.class); private final ActivityRuleFactory activityRuleFactory; + private final Purpose defaultPurpose4; private final Metrics metrics; private final JacksonMapper jacksonMapper; public ActivityInfrastructureCreator(ActivityRuleFactory activityRuleFactory, + GdprConfig gdprConfig, Metrics metrics, JacksonMapper jacksonMapper) { this.activityRuleFactory = Objects.requireNonNull(activityRuleFactory); + this.defaultPurpose4 = Optional.ofNullable(gdprConfig) + .map(GdprConfig::getPurposes) + .map(Purposes::getP4) + .orElse(null); this.metrics = Objects.requireNonNull(metrics); this.jacksonMapper = Objects.requireNonNull(jacksonMapper); } @@ -74,12 +86,15 @@ Map parse(Account account, GppContext gppContext, return Arrays.stream(Activity.values()).collect(Collectors.toMap( UnaryOperator.identity(), - activity -> from( - activity, - activitiesConfiguration.get(activity), - modulesConfigs, - gppContext, - debug), + fallbackActivity( + activitiesConfiguration, + accountPrivacyConfig, + activity -> from( + activity, + activitiesConfiguration.get(activity), + modulesConfigs, + gppContext, + debug)), (oldValue, newValue) -> oldValue, enumMapFactory())); } @@ -94,6 +109,28 @@ private BinaryOperator takeFirstAndLogDuplicates(Str }; } + // TODO: remove this wrapper after transition period + private Function fallbackActivity( + Map activitiesConfiguration, + Optional accountPrivacyConfig, + Function activityControllerCreator) { + + final boolean imitateTransmitEids = !activitiesConfiguration.containsKey(Activity.TRANSMIT_EIDS) + && activitiesConfiguration.containsKey(Activity.TRANSMIT_UFPD) + && accountPrivacyConfig + .map(AccountPrivacyConfig::getGdpr) + .map(AccountGdprConfig::getPurposes) + .map(Purposes::getP4) + .or(() -> Optional.ofNullable(defaultPurpose4)) + .map(Purpose::getEid) + .map(PurposeEid::getActivityTransition) + .orElse(true); + + return originalActivity -> originalActivity == Activity.TRANSMIT_EIDS && imitateTransmitEids + ? activityControllerCreator.apply(Activity.TRANSMIT_UFPD) + : activityControllerCreator.apply(originalActivity); + } + private ActivityController from(Activity activity, AccountActivityConfiguration activityConfiguration, Map modulesConfigs, diff --git a/src/main/java/org/prebid/server/activity/infrastructure/privacy/usnat/USNatModule.java b/src/main/java/org/prebid/server/activity/infrastructure/privacy/usnat/USNatModule.java index b3eb1a00b8e..9208a4d1f6b 100644 --- a/src/main/java/org/prebid/server/activity/infrastructure/privacy/usnat/USNatModule.java +++ b/src/main/java/org/prebid/server/activity/infrastructure/privacy/usnat/USNatModule.java @@ -28,7 +28,7 @@ public USNatModule(Activity activity, USNatGppReader gppReader) { private static PrivacyModule innerModule(Activity activity, USNatGppReader gppReader) { return switch (activity) { case SYNC_USER, MODIFY_UFDP -> new USNatSyncUser(gppReader); - case TRANSMIT_UFPD -> new USNatTransmitUfpd(gppReader); + case TRANSMIT_UFPD, TRANSMIT_EIDS -> new USNatTransmitUfpd(gppReader); case TRANSMIT_GEO -> new USNatTransmitGeo(gppReader); case CALL_BIDDER, TRANSMIT_TID, REPORT_ANALYTICS -> USNatDefault.instance(); }; diff --git a/src/main/java/org/prebid/server/analytics/reporter/AnalyticsReporterDelegator.java b/src/main/java/org/prebid/server/analytics/reporter/AnalyticsReporterDelegator.java index a1e66811e36..9856f6e1a26 100644 --- a/src/main/java/org/prebid/server/analytics/reporter/AnalyticsReporterDelegator.java +++ b/src/main/java/org/prebid/server/analytics/reporter/AnalyticsReporterDelegator.java @@ -26,8 +26,9 @@ import org.prebid.server.analytics.model.NotificationEvent; import org.prebid.server.analytics.model.SetuidEvent; import org.prebid.server.analytics.model.VideoEvent; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.privacy.enforcement.TcfEnforcement; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.log.ConditionalLogger; import org.prebid.server.metric.MetricName; @@ -56,27 +57,29 @@ public class AnalyticsReporterDelegator { private static final ConditionalLogger UNKNOWN_ADAPTERS_LOGGER = new ConditionalLogger(logger); private static final Set ADAPTERS_PERMITTED_FOR_FULL_DATA = Collections.singleton("logAnalytics"); - private final double logSamplingRate; - - private final List delegates; private final Vertx vertx; - private final PrivacyEnforcementService privacyEnforcementService; + private final List delegates; + private final TcfEnforcement tcfEnforcement; + private final UserFpdActivityMask mask; private final Metrics metrics; + private final double logSamplingRate; private final Set reporterVendorIds; private final Set reporterNames; - public AnalyticsReporterDelegator(double logSamplingRate, + public AnalyticsReporterDelegator(Vertx vertx, List delegates, - Vertx vertx, - PrivacyEnforcementService privacyEnforcementService, - Metrics metrics) { + TcfEnforcement tcfEnforcement, + UserFpdActivityMask userFpdActivityMask, + Metrics metrics, + double logSamplingRate) { - this.logSamplingRate = logSamplingRate; - this.delegates = Objects.requireNonNull(delegates); this.vertx = Objects.requireNonNull(vertx); - this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); + this.delegates = Objects.requireNonNull(delegates); + this.tcfEnforcement = Objects.requireNonNull(tcfEnforcement); + this.mask = Objects.requireNonNull(userFpdActivityMask); this.metrics = Objects.requireNonNull(metrics); + this.logSamplingRate = logSamplingRate; reporterVendorIds = delegates.stream().map(AnalyticsReporter::vendorId).collect(Collectors.toSet()); reporterNames = delegates.stream().map(AnalyticsReporter::name).collect(Collectors.toSet()); @@ -93,7 +96,7 @@ public void processEvent(T event) { } public void processEvent(T event, TcfContext tcfContext) { - privacyEnforcementService.resultForVendorIds(reporterVendorIds, tcfContext) + tcfEnforcement.enforce(reporterVendorIds, tcfContext) .onComplete(privacyEnforcementMap -> delegateEvent(event, tcfContext, privacyEnforcementMap)); } @@ -226,22 +229,19 @@ private BidRequest updateBidRequest(BidRequest bidRequest, String adapter, ActivityInfrastructure infrastructure) { - final ActivityInvocationPayload activityInvocationPayload = BidRequestActivityInvocationPayload.of( + final ActivityInvocationPayload payload = BidRequestActivityInvocationPayload.of( activityInvocationPayload(adapter), bidRequest); - final boolean disallowTransmitUfpd = !isAllowedActivity( - infrastructure, Activity.TRANSMIT_UFPD, activityInvocationPayload); - final boolean disallowTransmitGeo = !isAllowedActivity( - infrastructure, Activity.TRANSMIT_GEO, activityInvocationPayload); + final boolean disallowTransmitUfpd = !isAllowedActivity(infrastructure, Activity.TRANSMIT_UFPD, payload); + final boolean disallowTransmitEids = !isAllowedActivity(infrastructure, Activity.TRANSMIT_EIDS, payload); + final boolean disallowTransmitGeo = !isAllowedActivity(infrastructure, Activity.TRANSMIT_GEO, payload); final User user = bidRequest != null ? bidRequest.getUser() : null; - final User resolvedUser = privacyEnforcementService - .maskUserConsideringActivityRestrictions(user, disallowTransmitUfpd, disallowTransmitGeo); + final User resolvedUser = mask.maskUser(user, disallowTransmitUfpd, disallowTransmitEids, disallowTransmitGeo); final Device device = bidRequest != null ? bidRequest.getDevice() : null; - final Device resolvedDevice = privacyEnforcementService - .maskDeviceConsideringActivityRestrictions(device, disallowTransmitUfpd, disallowTransmitGeo); + final Device resolvedDevice = mask.maskDevice(device, disallowTransmitUfpd, disallowTransmitGeo); final ExtRequest requestExt = bidRequest != null ? bidRequest.getExt() : null; final ExtRequest updatedExtRequest = updateExtRequest(requestExt, adapter); diff --git a/src/main/java/org/prebid/server/auction/ExchangeService.java b/src/main/java/org/prebid/server/auction/ExchangeService.java index 2bce6efeeeb..43b2515c2a6 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -47,6 +47,7 @@ import org.prebid.server.auction.model.BidderResponse; import org.prebid.server.auction.model.MultiBidConfig; import org.prebid.server.auction.model.StoredResponseResult; +import org.prebid.server.auction.privacy.enforcement.PrivacyEnforcementService; import org.prebid.server.auction.model.TimeoutContext; import org.prebid.server.auction.versionconverter.BidRequestOrtbVersionConversionManager; import org.prebid.server.auction.versionconverter.OrtbVersion; @@ -327,7 +328,6 @@ private Future runAuction(AuctionContext receivedContext) { .map(auctionParticipations -> validateAndAdjustBids(auctionParticipations, context, aliases)) .map(auctionParticipations -> updateResponsesMetrics(auctionParticipations, account, aliases)) .map(context::with)) - // produce response from bidder results .compose(context -> bidResponseCreator.create(context, cacheInfo, bidderToMultiBid) .map(bidResponse -> publishAuctionEvent(bidResponse, context)) @@ -602,7 +602,7 @@ private Future> makeAuctionParticipation( final Map bidderToUser = prepareUsers(bidders, context, aliases, biddersToConfigs, eidPermissions); - return privacyEnforcementService.mask(context, bidderToUser, bidders, aliases) + return privacyEnforcementService.mask(context, bidderToUser, aliases) .map(bidderToPrivacyResult -> getAuctionParticipation(bidderToPrivacyResult, bidRequest, impBidderToStoredResponse, imps, bidderToMultiBid, biddersToConfigs, aliases, context)); diff --git a/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java b/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java deleted file mode 100644 index 40391e78805..00000000000 --- a/src/main/java/org/prebid/server/auction/PrivacyEnforcementService.java +++ /dev/null @@ -1,783 +0,0 @@ -package org.prebid.server.auction; - -import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Device; -import com.iab.openrtb.request.Geo; -import com.iab.openrtb.request.Regs; -import com.iab.openrtb.request.Site; -import com.iab.openrtb.request.User; -import io.vertx.core.Future; -import io.vertx.core.MultiMap; -import io.vertx.core.http.HttpServerRequest; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.ListUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; -import org.prebid.server.activity.Activity; -import org.prebid.server.activity.ComponentType; -import org.prebid.server.activity.infrastructure.ActivityInfrastructure; -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.model.AuctionContext; -import org.prebid.server.auction.model.BidderPrivacyResult; -import org.prebid.server.auction.model.IpAddress; -import org.prebid.server.bidder.BidderCatalog; -import org.prebid.server.execution.Timeout; -import org.prebid.server.geolocation.CountryCodeMapper; -import org.prebid.server.metric.MetricName; -import org.prebid.server.metric.Metrics; -import org.prebid.server.privacy.PrivacyExtractor; -import org.prebid.server.privacy.ccpa.Ccpa; -import org.prebid.server.privacy.gdpr.TcfDefinerService; -import org.prebid.server.privacy.gdpr.VendorIdResolver; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.RequestLogInfo; -import org.prebid.server.privacy.gdpr.model.TcfContext; -import org.prebid.server.privacy.gdpr.model.TcfResponse; -import org.prebid.server.privacy.model.Privacy; -import org.prebid.server.privacy.model.PrivacyContext; -import org.prebid.server.proto.openrtb.ext.request.ExtRegs; -import org.prebid.server.proto.openrtb.ext.request.ExtRequest; -import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; -import org.prebid.server.proto.openrtb.ext.request.ExtUser; -import org.prebid.server.proto.request.CookieSyncRequest; -import org.prebid.server.settings.model.Account; -import org.prebid.server.settings.model.AccountCcpaConfig; -import org.prebid.server.settings.model.AccountGdprConfig; -import org.prebid.server.settings.model.AccountPrivacyConfig; -import org.prebid.server.settings.model.EnabledForRequestType; - -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * Service provides masking for OpenRTB client sensitive information. - */ -public class PrivacyEnforcementService { - - private static final String CATCH_ALL_BIDDERS = "*"; - private static final DecimalFormat ROUND_TWO_DECIMALS = - new DecimalFormat("###.##", DecimalFormatSymbols.getInstance(Locale.US)); - - private static final User EMPTY_USER = User.builder().build(); - - private final BidderCatalog bidderCatalog; - private final PrivacyExtractor privacyExtractor; - private final TcfDefinerService tcfDefinerService; - private final ImplicitParametersExtractor implicitParametersExtractor; - private final IpAddressHelper ipAddressHelper; - private final Metrics metrics; - private final CountryCodeMapper countryCodeMapper; - private final boolean ccpaEnforce; - private final boolean lmtEnforce; - - public PrivacyEnforcementService(BidderCatalog bidderCatalog, - PrivacyExtractor privacyExtractor, - TcfDefinerService tcfDefinerService, - ImplicitParametersExtractor implicitParametersExtractor, - IpAddressHelper ipAddressHelper, - Metrics metrics, - CountryCodeMapper countryCodeMapper, - boolean ccpaEnforce, - boolean lmtEnforce) { - - this.bidderCatalog = Objects.requireNonNull(bidderCatalog); - this.privacyExtractor = Objects.requireNonNull(privacyExtractor); - this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); - this.implicitParametersExtractor = Objects.requireNonNull(implicitParametersExtractor); - this.ipAddressHelper = Objects.requireNonNull(ipAddressHelper); - this.metrics = Objects.requireNonNull(metrics); - this.countryCodeMapper = Objects.requireNonNull(countryCodeMapper); - this.ccpaEnforce = ccpaEnforce; - this.lmtEnforce = lmtEnforce; - } - - public Future contextFromBidRequest(AuctionContext auctionContext) { - final BidRequest bidRequest = auctionContext.getBidRequest(); - final List errors = auctionContext.getPrebidErrors(); - final Account account = auctionContext.getAccount(); - final MetricName requestType = auctionContext.getRequestTypeMetric(); - final Timeout timeout = auctionContext.getTimeoutContext().getTimeout(); - - final Privacy privacy = privacyExtractor.validPrivacyFrom(bidRequest, errors); - - final Device device = bidRequest.getDevice(); - final String alpha2CountryCode = resolveAlpha2CountryCode(device); - final String effectiveIpAddress = resolveIpAddress(device, privacy); - - final AccountGdprConfig accountGdpr = accountGdprConfig(account); - final String accountId = account.getId(); - final RequestLogInfo requestLogInfo = requestLogInfo(requestType, bidRequest, accountId); - - return tcfDefinerService.resolveTcfContext( - privacy, - alpha2CountryCode, - effectiveIpAddress, - accountGdpr, - requestType, - requestLogInfo, - timeout) - .map(tcfContext -> logWarnings(auctionContext.getDebugWarnings(), tcfContext)) - .map(tcfContext -> PrivacyContext.of(privacy, tcfContext, tcfContext.getIpAddress())); - } - - private static TcfContext logWarnings(List debugWarnings, TcfContext tcfContext) { - debugWarnings.addAll(tcfContext.getWarnings()); - - return tcfContext; - } - - private String resolveAlpha2CountryCode(Device device) { - final Geo geo = device != null ? device.getGeo() : null; - final String alpha3CountryCode = geo != null ? geo.getCountry() : null; - - return countryCodeMapper.mapToAlpha2(alpha3CountryCode); - } - - private String resolveIpAddress(Device device, Privacy privacy) { - final boolean shouldBeMasked = isCoppaMaskingRequired(privacy) || isLmtEnabled(device); - - final String ipV4Address = device != null ? device.getIp() : null; - if (StringUtils.isNotBlank(ipV4Address)) { - return shouldBeMasked ? ipAddressHelper.maskIpv4(ipV4Address) : ipV4Address; - } - - final String ipV6Address = device != null ? device.getIpv6() : null; - if (StringUtils.isNotBlank(ipV6Address)) { - return shouldBeMasked ? ipAddressHelper.anonymizeIpv6(ipV6Address) : ipV6Address; - } - - return null; - } - - public Future contextFromSetuidRequest(HttpServerRequest httpRequest, - Account account, - Timeout timeout) { - - final Privacy privacy = privacyExtractor.validPrivacyFromSetuidRequest(httpRequest); - final String ipAddress = resolveIpFromRequest(httpRequest); - final AccountGdprConfig accountGdpr = accountGdprConfig(account); - final String accountId = account.getId(); - final RequestLogInfo requestLogInfo = requestLogInfo(MetricName.setuid, null, accountId); - - return tcfDefinerService.resolveTcfContext( - privacy, ipAddress, accountGdpr, MetricName.setuid, requestLogInfo, timeout) - .map(tcfContext -> PrivacyContext.of(privacy, tcfContext)); - } - - public Future contextFromCookieSyncRequest(CookieSyncRequest cookieSyncRequest, - HttpServerRequest httpRequest, - Account account, - Timeout timeout) { - - final Privacy privacy = privacyExtractor.validPrivacyFrom(cookieSyncRequest); - final String ipAddress = resolveIpFromRequest(httpRequest); - final AccountGdprConfig accountGdpr = accountGdprConfig(account); - final String accountId = account.getId(); - final RequestLogInfo requestLogInfo = requestLogInfo(MetricName.cookiesync, null, accountId); - - return tcfDefinerService.resolveTcfContext( - privacy, ipAddress, accountGdpr, MetricName.cookiesync, requestLogInfo, timeout) - .map(tcfContext -> PrivacyContext.of(privacy, tcfContext)); - } - - private String resolveIpFromRequest(HttpServerRequest request) { - final MultiMap headers = request.headers(); - final String host = request.remoteAddress().host(); - final List requestIps = implicitParametersExtractor.ipFrom(headers, host); - return requestIps.stream() - .map(ipAddressHelper::toIpAddress) - .filter(Objects::nonNull) - .map(IpAddress::getIp) - .findFirst() - .orElse(null); - } - - private static RequestLogInfo requestLogInfo(MetricName requestType, BidRequest bidRequest, String accountId) { - if (Objects.equals(requestType, MetricName.openrtb2web)) { - final Site site = bidRequest != null ? bidRequest.getSite() : null; - final String refUrl = site != null ? site.getRef() : null; - return RequestLogInfo.of(requestType, refUrl, accountId); - } - - return RequestLogInfo.of(requestType, null, accountId); - } - - Future> mask(AuctionContext auctionContext, - Map bidderToUser, - List bidders, - BidderAliases aliases) { - - final BidRequest bidRequest = auctionContext.getBidRequest(); - final Account account = auctionContext.getAccount(); - final MetricName requestType = auctionContext.getRequestTypeMetric(); - final Device device = bidRequest.getDevice(); - final PrivacyContext privacyContext = auctionContext.getPrivacyContext(); - - final Privacy privacy = privacyContext.getPrivacy(); - - // For now, COPPA masking all values, so we can omit GDPR masking. - if (isCoppaMaskingRequired(privacy)) { - return Future.succeededFuture(maskCoppa(bidderToUser, device)); - } - - updateCcpaMetrics(privacy.getCcpa()); - final Map ccpaResult = - ccpaResult(bidRequest, account, bidders, aliases, device, bidderToUser, privacy, requestType); - - final Set biddersToApplyTcf = new HashSet<>(bidders); - biddersToApplyTcf.removeAll(ccpaResult.keySet()); - - return getBidderToEnforcementAction(privacyContext.getTcfContext(), biddersToApplyTcf, aliases, account) - .map(bidderToEnforcement -> updatePrivacyMetrics( - bidderToEnforcement, aliases, requestType, bidderToUser, device)) - .map(bidderToEnforcement -> getBidderToPrivacyResult( - bidderToEnforcement, biddersToApplyTcf, bidderToUser, device)) - .map(gdprResult -> merge(ccpaResult, gdprResult)) - .map(bidderPrivacyResults -> applyActivityRestrictions(bidderPrivacyResults, auctionContext)); - } - - public Future> resultForVendorIds(Set vendorIds, - TcfContext tcfContext) { - - return tcfDefinerService.resultForVendorIds(vendorIds, tcfContext) - .map(TcfResponse::getActions); - } - - private Map ccpaResult(BidRequest bidRequest, - Account account, - List bidders, - BidderAliases aliases, - Device device, - Map bidderToUser, - Privacy privacy, - MetricName requestType) { - - return isCcpaEnforced(privacy.getCcpa(), account, requestType) - ? maskCcpa(extractCcpaEnforcedBidders(bidders, bidRequest, aliases), device, bidderToUser) - : Collections.emptyMap(); - } - - public boolean isCcpaEnforced(Ccpa ccpa, Account account) { - final boolean shouldEnforceCcpa = isCcpaEnabled(account); - return shouldEnforceCcpa && ccpa.isEnforced(); - } - - private boolean isCcpaEnforced(Ccpa ccpa, Account account, MetricName requestType) { - final boolean shouldEnforceCcpa = isCcpaEnabled(account, requestType); - return shouldEnforceCcpa && ccpa.isEnforced(); - } - - private Boolean isCcpaEnabled(Account account) { - final AccountPrivacyConfig accountPrivacyConfig = account.getPrivacy(); - final AccountCcpaConfig accountCcpaConfig = - accountPrivacyConfig != null ? accountPrivacyConfig.getCcpa() : null; - final Boolean accountCcpaEnabled = accountCcpaConfig != null ? accountCcpaConfig.getEnabled() : null; - - return ObjectUtils.defaultIfNull(accountCcpaEnabled, ccpaEnforce); - } - - private boolean isCcpaEnabled(Account account, MetricName requestType) { - final AccountPrivacyConfig accountPrivacyConfig = account.getPrivacy(); - final AccountCcpaConfig accountCcpaConfig = - accountPrivacyConfig != null ? accountPrivacyConfig.getCcpa() : null; - - final Boolean accountCcpaEnabled = accountCcpaConfig != null ? accountCcpaConfig.getEnabled() : null; - if (requestType == null) { - return ObjectUtils.defaultIfNull(accountCcpaEnabled, ccpaEnforce); - } - - final EnabledForRequestType enabledForRequestType = accountCcpaConfig != null - ? accountCcpaConfig.getEnabledForRequestType() - : null; - - final Boolean enabledForType = enabledForRequestType != null - ? enabledForRequestType.isEnabledFor(requestType) - : null; - return ObjectUtils.firstNonNull(enabledForType, accountCcpaEnabled, ccpaEnforce); - } - - private Map maskCcpa(Set biddersToMask, - Device device, - Map bidderToUser) { - - return biddersToMask.stream() - .collect(Collectors.toMap(Function.identity(), - bidder -> BidderPrivacyResult.builder() - .requestBidder(bidder) - .user(maskCcpaUser(bidderToUser.get(bidder))) - .device(maskCcpaDevice(device)) - .build())); - } - - private User maskCcpaUser(User user) { - if (user != null) { - return nullIfEmpty(user.toBuilder() - .id(null) - .buyeruid(null) - .geo(maskGeoDefault(user.getGeo())) - .eids(null) - .ext(maskUserExt(user.getExt())) - .build()); - } - return null; - } - - private Device maskCcpaDevice(Device device) { - if (device != null) { - return device.toBuilder() - .ip(ipAddressHelper.maskIpv4(device.getIp())) - .ipv6(ipAddressHelper.anonymizeIpv6(device.getIpv6())) - .geo(maskGeoDefault(device.getGeo())) - .ifa(null) - .macsha1(null).macmd5(null) - .dpidsha1(null).dpidmd5(null) - .didsha1(null).didmd5(null) - .build(); - } - return null; - } - - private static boolean isCoppaMaskingRequired(Privacy privacy) { - return privacy.getCoppa() == 1; - } - - private List maskCoppa(Map bidderToUser, Device device) { - metrics.updatePrivacyCoppaMetric(); - - return bidderToUser.entrySet().stream() - .map(bidderAndUser -> BidderPrivacyResult.builder() - .requestBidder(bidderAndUser.getKey()) - .user(maskCoppaUser(bidderAndUser.getValue())) - .device(maskCoppaDevice(device)) - .build()) - .toList(); - } - - private User maskCoppaUser(User user) { - if (user != null) { - return nullIfEmpty(user.toBuilder() - .id(null) - .yob(null) - .gender(null) - .buyeruid(null) - .geo(maskGeoForCoppa(user.getGeo())) - .eids(null) - .ext(maskUserExt(user.getExt())) - .build()); - } - return null; - } - - private Device maskCoppaDevice(Device device) { - if (device != null) { - return device.toBuilder() - .ip(ipAddressHelper.maskIpv4(device.getIp())) - .ipv6(ipAddressHelper.anonymizeIpv6(device.getIpv6())) - .geo(maskGeoForCoppa(device.getGeo())) - .ifa(null) - .macsha1(null).macmd5(null) - .dpidsha1(null).dpidmd5(null) - .didsha1(null).didmd5(null) - .build(); - } - return null; - } - - /** - * Returns masked for COPPA {@link Geo}. - */ - private static Geo maskGeoForCoppa(Geo geo) { - final Geo updatedGeo = geo != null - ? geo.toBuilder().lat(null).lon(null).metro(null).city(null).zip(null).build() - : null; - return updatedGeo == null || updatedGeo.equals(Geo.EMPTY) ? null : updatedGeo; - } - - /** - * Returns {@link Future <{@link Map}<{@link String}, {@link PrivacyEnforcementAction}>>}, - * where bidder names mapped to actions for GDPR masking for pbs server. - */ - private Future> getBidderToEnforcementAction(TcfContext tcfContext, - Set bidders, - BidderAliases aliases, - Account account) { - - return tcfDefinerService.resultForBidderNames( - Collections.unmodifiableSet(bidders), - VendorIdResolver.of(aliases, bidderCatalog), - tcfContext, - accountGdprConfig(account)) - .map(tcfResponse -> mapTcfResponseToEachBidder(tcfResponse, bidders)); - } - - private Set extractCcpaEnforcedBidders(List bidders, - BidRequest bidRequest, - BidderAliases aliases) { - - final Set ccpaEnforcedBidders = new HashSet<>(bidders); - - final ExtRequest extBidRequest = bidRequest.getExt(); - final ExtRequestPrebid extRequestPrebid = extBidRequest != null ? extBidRequest.getPrebid() : null; - final List nosaleBidders = extRequestPrebid != null - ? ListUtils.emptyIfNull(extRequestPrebid.getNosale()) - : Collections.emptyList(); - - if (nosaleBidders.size() == 1 && nosaleBidders.contains(CATCH_ALL_BIDDERS)) { - ccpaEnforcedBidders.clear(); - } else { - nosaleBidders.forEach(ccpaEnforcedBidders::remove); - } - - ccpaEnforcedBidders.removeIf(bidder -> - !bidderCatalog.bidderInfoByName(aliases.resolveBidder(bidder)).isCcpaEnforced()); - - return ccpaEnforcedBidders; - } - - private static Map mapTcfResponseToEachBidder(TcfResponse tcfResponse, - Set bidders) { - - final Map bidderNameToAction = tcfResponse.getActions(); - return bidders.stream() - .collect(Collectors.toMap(Function.identity(), bidderNameToAction::get)); - } - - private void updateCcpaMetrics(Ccpa ccpa) { - metrics.updatePrivacyCcpaMetrics(ccpa.isNotEmpty(), ccpa.isEnforced()); - } - - private Map updatePrivacyMetrics( - Map bidderToEnforcement, - BidderAliases aliases, - MetricName requestType, - Map bidderToUser, - Device device) { - - // 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 : bidderToEnforcement.entrySet()) { - final String bidder = bidderEnforcement.getKey(); - final PrivacyEnforcementAction enforcement = bidderEnforcement.getValue(); - - final boolean requestBlocked = enforcement.isBlockBidderRequest(); - - final User user = bidderToUser.get(bidder); - boolean userIdRemoved = enforcement.isRemoveUserIds(); - if (requestBlocked || (userIdRemoved && !shouldMaskUser(user))) { - userIdRemoved = false; - } - - boolean geoMasked = enforcement.isMaskGeo(); - if (requestBlocked || (geoMasked && !shouldMaskGeo(user, device))) { - geoMasked = false; - } - - final boolean analyticsBlocked = !requestBlocked && enforcement.isBlockAnalyticsReport(); - - metrics.updateAuctionTcfMetrics( - aliases.resolveBidder(bidder), - requestType, - userIdRemoved, - geoMasked, - analyticsBlocked, - requestBlocked); - } - - if (lmtEnforce && isLmtEnabled(device)) { - metrics.updatePrivacyLmtMetric(); - } - - return bidderToEnforcement; - } - - /** - * Returns true if {@link User} has sensitive privacy information that can be masked. - */ - private static boolean shouldMaskUser(User user) { - if (user == null) { - return false; - } - if (user.getId() != null || user.getBuyeruid() != null) { - return true; - } - - return CollectionUtils.isNotEmpty(user.getEids()); - } - - /** - * Returns true if {@link User} or {@link Device} has {@link Geo} information that can be masked. - */ - private static boolean shouldMaskGeo(User user, Device device) { - return (user != null && user.getGeo() != null) || (device != null && device.getGeo() != null); - } - - /** - * Returns {@link Map}<{@link String}, {@link BidderPrivacyResult}>, where bidder name mapped to masked - * {@link BidderPrivacyResult}. Masking depends on GDPR and COPPA. - */ - private List getBidderToPrivacyResult( - Map bidderToEnforcement, - Set bidders, - Map bidderToUser, - Device device) { - - final boolean isLmtEnabled = lmtEnforce && isLmtEnabled(device); - - return bidderToUser.entrySet().stream() - .filter(entry -> bidders.contains(entry.getKey())) - .map(bidderUserEntry -> createBidderPrivacyResult( - bidderUserEntry.getValue(), - device, - bidderUserEntry.getKey(), - isLmtEnabled, - bidderToEnforcement)) - .toList(); - } - - /** - * Returns {@link BidderPrivacyResult} with GDPR masking. - */ - private BidderPrivacyResult createBidderPrivacyResult(User user, - Device device, - String bidder, - boolean isLmtEnabled, - Map bidderToEnforcement) { - - final PrivacyEnforcementAction privacyEnforcementAction = bidderToEnforcement.get(bidder); - final boolean blockBidderRequest = privacyEnforcementAction.isBlockBidderRequest(); - final boolean blockAnalyticsReport = privacyEnforcementAction.isBlockAnalyticsReport(); - if (blockBidderRequest) { - return BidderPrivacyResult.builder() - .requestBidder(bidder) - .blockedRequestByTcf(true) - .blockedAnalyticsByTcf(blockAnalyticsReport) - .build(); - } - - final boolean maskGeo = privacyEnforcementAction.isMaskGeo() || isLmtEnabled; - final boolean maskUserIds = privacyEnforcementAction.isRemoveUserIds() || isLmtEnabled; - final User maskedUser = maskTcfUser(user, maskUserIds, maskGeo); - - final boolean maskIp = privacyEnforcementAction.isMaskDeviceIp() || isLmtEnabled; - final boolean maskInfo = privacyEnforcementAction.isMaskDeviceInfo() || isLmtEnabled; - final Device maskedDevice = maskTcfDevice(device, maskIp, maskGeo, maskInfo); - - return BidderPrivacyResult.builder() - .requestBidder(bidder) - .user(maskedUser) - .device(maskedDevice) - .blockedAnalyticsByTcf(blockAnalyticsReport) - .build(); - } - - /** - * Returns masked {@link User}. - */ - private User maskTcfUser(User user, boolean maskUserIds, boolean maskGeo) { - if (user != null) { - final User.UserBuilder userBuilder = user.toBuilder(); - - if (maskGeo) { - userBuilder.geo(maskGeoDefault(user.getGeo())); - } - - if (maskUserIds) { - userBuilder - .id(null) - .buyeruid(null) - .eids(null) - .ext(maskUserExt(user.getExt())); - } - - return nullIfEmpty(userBuilder.build()); - } - return null; - } - - /** - * Returns masked device accordingly for each flag. - */ - private Device maskTcfDevice(Device device, boolean maskIp, boolean maskGeo, boolean maskInfo) { - if (device != null) { - final Device.DeviceBuilder deviceBuilder = device.toBuilder(); - if (maskIp) { - deviceBuilder - .ip(ipAddressHelper.maskIpv4(device.getIp())) - .ipv6(ipAddressHelper.anonymizeIpv6(device.getIpv6())); - } - - if (maskGeo) { - deviceBuilder.geo(maskGeoDefault(device.getGeo())); - } - - if (maskInfo) { - deviceBuilder.ifa(null) - .macsha1(null).macmd5(null) - .dpidsha1(null).dpidmd5(null) - .didsha1(null).didmd5(null); - } - - return deviceBuilder.build(); - } - return null; - } - - /** - * Returns masked for GDPR {@link Geo} by rounding lon and lat properties. - */ - private static Geo maskGeoDefault(Geo geo) { - if (geo != null) { - return geo.toBuilder() - .lat(maskGeoCoordinate(geo.getLat())) - .lon(maskGeoCoordinate(geo.getLon())) - .build(); - } - return null; - } - - /** - * Returns masked geo coordinate with rounded value to two decimals. - */ - private static Float maskGeoCoordinate(Float coordinate) { - return coordinate != null ? Float.valueOf(ROUND_TWO_DECIMALS.format(coordinate)) : null; - } - - /** - * Returns masked eids of user ext. - */ - private static ExtUser maskUserExt(ExtUser userExt) { - return userExt != null - ? nullIfEmpty(userExt.toBuilder().digitrust(null).build()) - : null; - } - - /** - * Returns null if {@link ExtUser} has no data in case of masking was applied. - */ - private static ExtUser nullIfEmpty(ExtUser userExt) { - return userExt.isEmpty() ? null : userExt; - } - - /** - * Returns null if {@link User} has no data in case of masking was applied. - */ - private static User nullIfEmpty(User user) { - return Objects.equals(user, EMPTY_USER) ? null : user; - } - - private static boolean isLmtEnabled(Device device) { - return device != null && Objects.equals(device.getLmt(), 1); - } - - private List applyActivityRestrictions(List bidderPrivacyResults, - AuctionContext auctionContext) { - - return bidderPrivacyResults.stream() - .map(bidderPrivacyResult -> applyActivityRestrictions(bidderPrivacyResult, auctionContext)) - .toList(); - } - - private BidderPrivacyResult applyActivityRestrictions(BidderPrivacyResult bidderPrivacyResult, - AuctionContext auctionContext) { - - final ActivityInfrastructure activityInfrastructure = auctionContext.getActivityInfrastructure(); - - final String bidder = bidderPrivacyResult.getRequestBidder(); - final User user = bidderPrivacyResult.getUser(); - final Device device = bidderPrivacyResult.getDevice(); - - final Geo geo = device != null ? device.getGeo() : null; - final ActivityInvocationPayload activityInvocationPayload = - PrivacyEnforcementServiceActivityInvocationPayload.of( - ActivityInvocationPayloadImpl.of(ComponentType.BIDDER, bidder), - geo != null ? geo.getCountry() : null, - geo != null ? geo.getRegion() : null, - Optional.ofNullable(auctionContext.getBidRequest().getRegs()) - .map(Regs::getExt) - .map(ExtRegs::getGpc) - .orElse(null)); - - final boolean disallowTransmitUfpd = !activityInfrastructure.isAllowed( - Activity.TRANSMIT_UFPD, activityInvocationPayload); - final boolean disallowTransmitGeo = !activityInfrastructure.isAllowed( - Activity.TRANSMIT_GEO, activityInvocationPayload); - - final User resolvedUser = disallowTransmitUfpd || disallowTransmitGeo - ? maskUserConsideringActivityRestrictions(user, disallowTransmitUfpd, disallowTransmitGeo) - : user; - final Device resolvedDevice = disallowTransmitUfpd || disallowTransmitGeo - ? maskDeviceConsideringActivityRestrictions(device, disallowTransmitUfpd, disallowTransmitGeo) - : device; - - return bidderPrivacyResult.toBuilder() - .user(resolvedUser) - .device(resolvedDevice) - .build(); - } - - public User maskUserConsideringActivityRestrictions(User user, - boolean disallowTransmitUfpd, - boolean disallowTransmitGeo) { - - if (!(disallowTransmitGeo || disallowTransmitUfpd) || user == null) { - return user; - } - - final User.UserBuilder userBuilder = user.toBuilder(); - - if (disallowTransmitUfpd) { - final ExtUser extUser = user.getExt(); - userBuilder - .id(null) - .buyeruid(null) - .yob(null) - .gender(null) - .data(null) - .eids(null) - .ext(extUser != null ? nullIfEmpty(extUser.toBuilder().data(null).build()) : null); - } - - if (disallowTransmitGeo) { - userBuilder.geo(maskGeoDefault(user.getGeo())); - } - - return userBuilder.build(); - } - - public Device maskDeviceConsideringActivityRestrictions(Device device, - boolean disallowTransmitUfpd, - boolean disallowTransmitGeo) { - - if (!(disallowTransmitGeo || disallowTransmitUfpd)) { - return device; - } - - return maskTcfDevice(device, disallowTransmitGeo, disallowTransmitGeo, disallowTransmitUfpd); - } - - private static List merge( - Map ccpaResult, List gdprResult) { - - final List result = new ArrayList<>(ccpaResult.values()); - result.addAll(gdprResult); - return result; - } - - private static AccountGdprConfig accountGdprConfig(Account account) { - final AccountPrivacyConfig privacyConfig = account.getPrivacy(); - return privacyConfig != null ? privacyConfig.getGdpr() : null; - } -} diff --git a/src/main/java/org/prebid/server/auction/privacycontextfactory/AmpPrivacyContextFactory.java b/src/main/java/org/prebid/server/auction/privacy/contextfactory/AmpPrivacyContextFactory.java similarity index 77% rename from src/main/java/org/prebid/server/auction/privacycontextfactory/AmpPrivacyContextFactory.java rename to src/main/java/org/prebid/server/auction/privacy/contextfactory/AmpPrivacyContextFactory.java index a5de074b156..1f1db91f7aa 100644 --- a/src/main/java/org/prebid/server/auction/privacycontextfactory/AmpPrivacyContextFactory.java +++ b/src/main/java/org/prebid/server/auction/privacy/contextfactory/AmpPrivacyContextFactory.java @@ -1,4 +1,4 @@ -package org.prebid.server.auction.privacycontextfactory; +package org.prebid.server.auction.privacy.contextfactory; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; @@ -9,7 +9,6 @@ import org.prebid.server.auction.IpAddressHelper; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.ConsentType; -import org.prebid.server.execution.Timeout; import org.prebid.server.geolocation.CountryCodeMapper; import org.prebid.server.metric.MetricName; import org.prebid.server.privacy.PrivacyExtractor; @@ -24,6 +23,7 @@ import java.util.List; import java.util.Objects; +import java.util.Optional; public class AmpPrivacyContextFactory { @@ -47,33 +47,29 @@ public AmpPrivacyContextFactory(PrivacyExtractor privacyExtractor, public Future contextFrom(AuctionContext auctionContext) { final BidRequest bidRequest = auctionContext.getBidRequest(); - + final Account account = auctionContext.getAccount(); final MetricName requestType = auctionContext.getRequestTypeMetric(); - final Timeout timeout = auctionContext.getTimeoutContext().getTimeout(); - final List errors = auctionContext.getPrebidErrors(); - final Privacy initialPrivacy = privacyExtractor.validPrivacyFrom(bidRequest, errors); + final Privacy initialPrivacy = privacyExtractor.validPrivacyFrom(bidRequest, auctionContext.getPrebidErrors()); final Privacy strippedPrivacy = stripPrivacy(initialPrivacy, auctionContext); - final Device device = bidRequest.getDevice(); - final Account account = auctionContext.getAccount(); return tcfDefinerService.resolveTcfContext( strippedPrivacy, resolveAlpha2CountryCode(device), resolveIpAddress(device, strippedPrivacy), - extractGdprConfig(account), + accountGdprConfig(account), requestType, requestLogInfo(requestType, bidRequest, account.getId()), - timeout) + auctionContext.getTimeoutContext().getTimeout()) .map(tcfContext -> logWarnings(auctionContext.getDebugWarnings(), tcfContext)) .map(tcfContext -> PrivacyContext.of(strippedPrivacy, tcfContext, tcfContext.getIpAddress())); } private Privacy stripPrivacy(Privacy privacy, AuctionContext auctionContext) { + final String consentTypeParam = auctionContext.getHttpRequest().getQueryParams().get(CONSENT_TYPE_PARAM); final List errors = auctionContext.getPrebidErrors(); - final String consentTypeParam = auctionContext.getHttpRequest().getQueryParams().get(CONSENT_TYPE_PARAM); final ConsentType consentType = ConsentType.from(consentTypeParam); if (consentType == ConsentType.TCF_V1) { @@ -84,28 +80,16 @@ private Privacy stripPrivacy(Privacy privacy, AuctionContext auctionContext) { return privacy; } - private AccountGdprConfig extractGdprConfig(Account account) { - final AccountPrivacyConfig accountPrivacyConfig = account != null ? account.getPrivacy() : null; - - return accountPrivacyConfig != null ? accountPrivacyConfig.getGdpr() : null; - } - - private static TcfContext logWarnings(List debugWarnings, TcfContext tcfContext) { - debugWarnings.addAll(tcfContext.getWarnings()); - - return tcfContext; - } - private String resolveAlpha2CountryCode(Device device) { - final Geo geo = device != null ? device.getGeo() : null; - final String alpha3CountryCode = geo != null ? geo.getCountry() : null; - - return countryCodeMapper.mapToAlpha2(alpha3CountryCode); + return Optional.ofNullable(device) + .map(Device::getGeo) + .map(Geo::getCountry) + .map(countryCodeMapper::mapToAlpha2) + .orElse(null); } private String resolveIpAddress(Device device, Privacy privacy) { - final boolean shouldBeMasked = Objects.equals(privacy.getCoppa(), 1) - || (device != null && Objects.equals(device.getLmt(), 1)); + final boolean shouldBeMasked = isCoppaMaskingRequired(privacy) || isLmtEnabled(device); final String ipV4Address = device != null ? device.getIp() : null; if (StringUtils.isNotBlank(ipV4Address)) { @@ -120,13 +104,31 @@ private String resolveIpAddress(Device device, Privacy privacy) { return null; } + private static boolean isCoppaMaskingRequired(Privacy privacy) { + return privacy.getCoppa() == 1; + } + + private static boolean isLmtEnabled(Device device) { + return device != null && Objects.equals(device.getLmt(), 1); + } + + private static AccountGdprConfig accountGdprConfig(Account account) { + final AccountPrivacyConfig privacyConfig = account.getPrivacy(); + return privacyConfig != null ? privacyConfig.getGdpr() : null; + } + private static RequestLogInfo requestLogInfo(MetricName requestType, BidRequest bidRequest, String accountId) { - if (requestType == MetricName.openrtb2web) { - final Site site = bidRequest != null ? bidRequest.getSite() : null; - final String refUrl = site != null ? site.getRef() : null; - return RequestLogInfo.of(requestType, refUrl, accountId); - } + final String referrerUrl = MetricName.openrtb2web == requestType + ? Optional.ofNullable(bidRequest.getSite()) + .map(Site::getRef) + .orElse(null) + : null; + + return RequestLogInfo.of(requestType, referrerUrl, accountId); + } - return RequestLogInfo.of(requestType, null, accountId); + private static TcfContext logWarnings(List debugWarnings, TcfContext tcfContext) { + debugWarnings.addAll(tcfContext.getWarnings()); + return tcfContext; } } diff --git a/src/main/java/org/prebid/server/auction/privacy/contextfactory/AuctionPrivacyContextFactory.java b/src/main/java/org/prebid/server/auction/privacy/contextfactory/AuctionPrivacyContextFactory.java new file mode 100644 index 00000000000..debcefe86e4 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/contextfactory/AuctionPrivacyContextFactory.java @@ -0,0 +1,116 @@ +package org.prebid.server.auction.privacy.contextfactory; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.Site; +import io.vertx.core.Future; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.geolocation.CountryCodeMapper; +import org.prebid.server.metric.MetricName; +import org.prebid.server.privacy.PrivacyExtractor; +import org.prebid.server.privacy.gdpr.TcfDefinerService; +import org.prebid.server.privacy.gdpr.model.RequestLogInfo; +import org.prebid.server.privacy.gdpr.model.TcfContext; +import org.prebid.server.privacy.model.Privacy; +import org.prebid.server.privacy.model.PrivacyContext; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountGdprConfig; +import org.prebid.server.settings.model.AccountPrivacyConfig; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class AuctionPrivacyContextFactory { + + private final PrivacyExtractor privacyExtractor; + private final TcfDefinerService tcfDefinerService; + private final IpAddressHelper ipAddressHelper; + private final CountryCodeMapper countryCodeMapper; + + public AuctionPrivacyContextFactory(PrivacyExtractor privacyExtractor, + TcfDefinerService tcfDefinerService, + IpAddressHelper ipAddressHelper, + CountryCodeMapper countryCodeMapper) { + + this.privacyExtractor = Objects.requireNonNull(privacyExtractor); + this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); + this.ipAddressHelper = Objects.requireNonNull(ipAddressHelper); + this.countryCodeMapper = Objects.requireNonNull(countryCodeMapper); + } + + public Future contextFrom(AuctionContext auctionContext) { + final BidRequest bidRequest = auctionContext.getBidRequest(); + final Account account = auctionContext.getAccount(); + final MetricName requestType = auctionContext.getRequestTypeMetric(); + + final Privacy privacy = privacyExtractor.validPrivacyFrom(bidRequest, auctionContext.getPrebidErrors()); + final Device device = bidRequest.getDevice(); + + return tcfDefinerService.resolveTcfContext( + privacy, + resolveAlpha2CountryCode(device), + resolveIpAddress(device, privacy), + accountGdprConfig(account), + requestType, + requestLogInfo(requestType, bidRequest, account.getId()), + auctionContext.getTimeoutContext().getTimeout()) + .map(tcfContext -> logWarnings(auctionContext.getDebugWarnings(), tcfContext)) + .map(tcfContext -> PrivacyContext.of(privacy, tcfContext, tcfContext.getIpAddress())); + } + + private String resolveAlpha2CountryCode(Device device) { + return Optional.ofNullable(device) + .map(Device::getGeo) + .map(Geo::getCountry) + .map(countryCodeMapper::mapToAlpha2) + .orElse(null); + } + + private String resolveIpAddress(Device device, Privacy privacy) { + final boolean shouldBeMasked = isCoppaMaskingRequired(privacy) || isLmtEnabled(device); + + final String ipV4Address = device != null ? device.getIp() : null; + if (StringUtils.isNotBlank(ipV4Address)) { + return shouldBeMasked ? ipAddressHelper.maskIpv4(ipV4Address) : ipV4Address; + } + + final String ipV6Address = device != null ? device.getIpv6() : null; + if (StringUtils.isNotBlank(ipV6Address)) { + return shouldBeMasked ? ipAddressHelper.anonymizeIpv6(ipV6Address) : ipV6Address; + } + + return null; + } + + private static boolean isCoppaMaskingRequired(Privacy privacy) { + return privacy.getCoppa() == 1; + } + + private static boolean isLmtEnabled(Device device) { + return device != null && Objects.equals(device.getLmt(), 1); + } + + private static AccountGdprConfig accountGdprConfig(Account account) { + final AccountPrivacyConfig privacyConfig = account.getPrivacy(); + return privacyConfig != null ? privacyConfig.getGdpr() : null; + } + + private static RequestLogInfo requestLogInfo(MetricName requestType, BidRequest bidRequest, String accountId) { + final String referrerUrl = MetricName.openrtb2web == requestType + ? Optional.ofNullable(bidRequest.getSite()) + .map(Site::getRef) + .orElse(null) + : null; + + return RequestLogInfo.of(requestType, referrerUrl, accountId); + } + + private static TcfContext logWarnings(List debugWarnings, TcfContext tcfContext) { + debugWarnings.addAll(tcfContext.getWarnings()); + return tcfContext; + } +} diff --git a/src/main/java/org/prebid/server/auction/privacy/contextfactory/CookieSyncPrivacyContextFactory.java b/src/main/java/org/prebid/server/auction/privacy/contextfactory/CookieSyncPrivacyContextFactory.java new file mode 100644 index 00000000000..55e32fd1372 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/contextfactory/CookieSyncPrivacyContextFactory.java @@ -0,0 +1,74 @@ +package org.prebid.server.auction.privacy.contextfactory; + +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerRequest; +import org.prebid.server.auction.ImplicitParametersExtractor; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.auction.model.IpAddress; +import org.prebid.server.execution.Timeout; +import org.prebid.server.metric.MetricName; +import org.prebid.server.privacy.PrivacyExtractor; +import org.prebid.server.privacy.gdpr.TcfDefinerService; +import org.prebid.server.privacy.gdpr.model.RequestLogInfo; +import org.prebid.server.privacy.model.Privacy; +import org.prebid.server.privacy.model.PrivacyContext; +import org.prebid.server.proto.request.CookieSyncRequest; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountGdprConfig; +import org.prebid.server.settings.model.AccountPrivacyConfig; + +import java.util.List; +import java.util.Objects; + +public class CookieSyncPrivacyContextFactory { + + private final PrivacyExtractor privacyExtractor; + private final TcfDefinerService tcfDefinerService; + private final ImplicitParametersExtractor implicitParametersExtractor; + private final IpAddressHelper ipAddressHelper; + + public CookieSyncPrivacyContextFactory(PrivacyExtractor privacyExtractor, + TcfDefinerService tcfDefinerService, + ImplicitParametersExtractor implicitParametersExtractor, + IpAddressHelper ipAddressHelper) { + + this.privacyExtractor = Objects.requireNonNull(privacyExtractor); + this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); + this.implicitParametersExtractor = Objects.requireNonNull(implicitParametersExtractor); + this.ipAddressHelper = Objects.requireNonNull(ipAddressHelper); + } + + public Future contextFrom(CookieSyncRequest cookieSyncRequest, + HttpServerRequest httpRequest, + Account account, + Timeout timeout) { + + final Privacy privacy = privacyExtractor.validPrivacyFrom(cookieSyncRequest); + return tcfDefinerService.resolveTcfContext( + privacy, + resolveIpFromRequest(httpRequest), + accountGdprConfig(account), + MetricName.cookiesync, + RequestLogInfo.of(MetricName.cookiesync, null, account.getId()), + timeout) + .map(tcfContext -> PrivacyContext.of(privacy, tcfContext)); + } + + private String resolveIpFromRequest(HttpServerRequest request) { + final MultiMap headers = request.headers(); + final String host = request.remoteAddress().host(); + final List requestIps = implicitParametersExtractor.ipFrom(headers, host); + return requestIps.stream() + .map(ipAddressHelper::toIpAddress) + .filter(Objects::nonNull) + .map(IpAddress::getIp) + .findFirst() + .orElse(null); + } + + private static AccountGdprConfig accountGdprConfig(Account account) { + final AccountPrivacyConfig privacyConfig = account.getPrivacy(); + return privacyConfig != null ? privacyConfig.getGdpr() : null; + } +} diff --git a/src/main/java/org/prebid/server/auction/privacy/contextfactory/SetuidPrivacyContextFactory.java b/src/main/java/org/prebid/server/auction/privacy/contextfactory/SetuidPrivacyContextFactory.java new file mode 100644 index 00000000000..3e81b3fcf4f --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/contextfactory/SetuidPrivacyContextFactory.java @@ -0,0 +1,69 @@ +package org.prebid.server.auction.privacy.contextfactory; + +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerRequest; +import org.prebid.server.auction.ImplicitParametersExtractor; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.auction.model.IpAddress; +import org.prebid.server.execution.Timeout; +import org.prebid.server.metric.MetricName; +import org.prebid.server.privacy.PrivacyExtractor; +import org.prebid.server.privacy.gdpr.TcfDefinerService; +import org.prebid.server.privacy.gdpr.model.RequestLogInfo; +import org.prebid.server.privacy.model.Privacy; +import org.prebid.server.privacy.model.PrivacyContext; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountGdprConfig; +import org.prebid.server.settings.model.AccountPrivacyConfig; + +import java.util.List; +import java.util.Objects; + +public class SetuidPrivacyContextFactory { + + private final PrivacyExtractor privacyExtractor; + private final TcfDefinerService tcfDefinerService; + private final ImplicitParametersExtractor implicitParametersExtractor; + private final IpAddressHelper ipAddressHelper; + + public SetuidPrivacyContextFactory(PrivacyExtractor privacyExtractor, + TcfDefinerService tcfDefinerService, + ImplicitParametersExtractor implicitParametersExtractor, + IpAddressHelper ipAddressHelper) { + + this.privacyExtractor = Objects.requireNonNull(privacyExtractor); + this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); + this.implicitParametersExtractor = Objects.requireNonNull(implicitParametersExtractor); + this.ipAddressHelper = Objects.requireNonNull(ipAddressHelper); + } + + public Future contextFrom(HttpServerRequest httpRequest, Account account, Timeout timeout) { + final Privacy privacy = privacyExtractor.validPrivacyFromSetuidRequest(httpRequest); + return tcfDefinerService.resolveTcfContext( + privacy, + resolveIpFromRequest(httpRequest), + accountGdprConfig(account), + MetricName.setuid, + RequestLogInfo.of(MetricName.setuid, null, account.getId()), + timeout) + .map(tcfContext -> PrivacyContext.of(privacy, tcfContext)); + } + + private String resolveIpFromRequest(HttpServerRequest request) { + final MultiMap headers = request.headers(); + final String host = request.remoteAddress().host(); + final List requestIps = implicitParametersExtractor.ipFrom(headers, host); + return requestIps.stream() + .map(ipAddressHelper::toIpAddress) + .filter(Objects::nonNull) + .map(IpAddress::getIp) + .findFirst() + .orElse(null); + } + + private static AccountGdprConfig accountGdprConfig(Account account) { + final AccountPrivacyConfig privacyConfig = account.getPrivacy(); + return privacyConfig != null ? privacyConfig.getGdpr() : null; + } +} 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 new file mode 100644 index 00000000000..29e07658eb6 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcement.java @@ -0,0 +1,89 @@ +package org.prebid.server.auction.privacy.enforcement; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.Regs; +import com.iab.openrtb.request.User; +import io.vertx.core.Future; +import org.prebid.server.activity.Activity; +import org.prebid.server.activity.ComponentType; +import org.prebid.server.activity.infrastructure.ActivityInfrastructure; +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.model.AuctionContext; +import org.prebid.server.auction.model.BidderPrivacyResult; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; +import org.prebid.server.proto.openrtb.ext.request.ExtRegs; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class ActivityEnforcement { + + private final UserFpdActivityMask userFpdActivityMask; + + public ActivityEnforcement(UserFpdActivityMask userFpdActivityMask) { + this.userFpdActivityMask = Objects.requireNonNull(userFpdActivityMask); + } + + public Future> enforce(List bidderPrivacyResults, + AuctionContext auctionContext) { + + final List results = bidderPrivacyResults.stream() + .map(bidderPrivacyResult -> applyActivityRestrictions( + bidderPrivacyResult, + auctionContext.getActivityInfrastructure(), + auctionContext.getBidRequest())) + .toList(); + + return Future.succeededFuture(results); + } + + private BidderPrivacyResult applyActivityRestrictions(BidderPrivacyResult bidderPrivacyResult, + ActivityInfrastructure infrastructure, + BidRequest bidRequest) { + + final String bidder = bidderPrivacyResult.getRequestBidder(); + final User user = bidderPrivacyResult.getUser(); + final Device device = bidderPrivacyResult.getDevice(); + + final ActivityInvocationPayload payload = activityInvocationPayload( + bidder, device != null ? device.getGeo() : null, bidRequest); + + final boolean disallowTransmitUfpd = !infrastructure.isAllowed(Activity.TRANSMIT_UFPD, payload); + final boolean disallowTransmitEids = !infrastructure.isAllowed(Activity.TRANSMIT_EIDS, payload); + final boolean disallowTransmitGeo = !infrastructure.isAllowed(Activity.TRANSMIT_GEO, payload); + + final User resolvedUser = userFpdActivityMask.maskUser( + user, + disallowTransmitUfpd, + disallowTransmitEids, + disallowTransmitGeo); + final Device resolvedDevice = userFpdActivityMask.maskDevice( + device, + disallowTransmitUfpd, + disallowTransmitGeo); + + return bidderPrivacyResult.toBuilder() + .user(resolvedUser) + .device(resolvedDevice) + .build(); + } + + private static ActivityInvocationPayload activityInvocationPayload(String bidder, + Geo geo, + BidRequest bidRequest) { + + return PrivacyEnforcementServiceActivityInvocationPayload.of( + ActivityInvocationPayloadImpl.of(ComponentType.BIDDER, bidder), + geo != null ? geo.getCountry() : null, + geo != null ? geo.getRegion() : null, + Optional.ofNullable(bidRequest.getRegs()) + .map(Regs::getExt) + .map(ExtRegs::getGpc) + .orElse(null)); + } +} 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 new file mode 100644 index 00000000000..0267fbd8d3d --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcement.java @@ -0,0 +1,125 @@ +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; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdCcpaMask; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; +import org.prebid.server.privacy.ccpa.Ccpa; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountCcpaConfig; +import org.prebid.server.settings.model.AccountPrivacyConfig; + +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; + +public class CcpaEnforcement { + + private static final String CATCH_ALL_BIDDERS = "*"; + + private final UserFpdCcpaMask userFpdCcpaMask; + private final BidderCatalog bidderCatalog; + private final Metrics metrics; + private final boolean ccpaEnforce; + + public CcpaEnforcement(UserFpdCcpaMask userFpdCcpaMask, + BidderCatalog bidderCatalog, + Metrics metrics, + boolean ccpaEnforce) { + + this.userFpdCcpaMask = Objects.requireNonNull(userFpdCcpaMask); + this.bidderCatalog = Objects.requireNonNull(bidderCatalog); + this.metrics = Objects.requireNonNull(metrics); + this.ccpaEnforce = ccpaEnforce; + } + + public Future> enforce(AuctionContext auctionContext, + Map bidderToUser, + BidderAliases aliases) { + + final Ccpa ccpa = auctionContext.getPrivacyContext().getPrivacy().getCcpa(); + metrics.updatePrivacyCcpaMetrics(ccpa.isNotEmpty(), ccpa.isEnforced()); + + return Future.succeededFuture(enforce(bidderToUser, ccpa, auctionContext, aliases)); + } + + private List enforce(Map bidderToUser, + Ccpa ccpa, + AuctionContext auctionContext, + BidderAliases aliases) { + + final BidRequest bidRequest = auctionContext.getBidRequest(); + final Device device = bidRequest.getDevice(); + + return isCcpaEnforced(ccpa, auctionContext.getAccount(), auctionContext.getRequestTypeMetric()) + ? maskCcpa(bidderToUser, extractCcpaEnforcedBidders(bidderToUser.keySet(), bidRequest, aliases), device) + : Collections.emptyList(); + } + + public boolean isCcpaEnforced(Ccpa ccpa, Account account) { + return isCcpaEnforced(ccpa, account, null); + } + + private boolean isCcpaEnforced(Ccpa ccpa, Account account, MetricName requestType) { + return ccpa.isEnforced() && isCcpaEnabled(account, requestType); + } + + 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); + } + + private Set extractCcpaEnforcedBidders(Set bidders, BidRequest bidRequest, BidderAliases aliases) { + final Set ccpaEnforcedBidders = new HashSet<>(bidders); + final List nosaleBidders = Optional.ofNullable(bidRequest.getExt()) + .map(ExtRequest::getPrebid) + .map(ExtRequestPrebid::getNosale) + .orElseGet(Collections::emptyList); + + if (nosaleBidders.size() == 1 && nosaleBidders.contains(CATCH_ALL_BIDDERS)) { + ccpaEnforcedBidders.clear(); + } else { + nosaleBidders.forEach(ccpaEnforcedBidders::remove); + } + + ccpaEnforcedBidders.removeIf(bidder -> + !bidderCatalog.bidderInfoByName(aliases.resolveBidder(bidder)).isCcpaEnforced()); + + 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(); + } +} 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 new file mode 100644 index 00000000000..46b578a9ccc --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcement.java @@ -0,0 +1,44 @@ +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.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; + +public class CoppaEnforcement { + + private final UserFpdCoppaMask userFpdCoppaMask; + private final Metrics metrics; + + public CoppaEnforcement(UserFpdCoppaMask userFpdCoppaMask, Metrics metrics) { + this.userFpdCoppaMask = Objects.requireNonNull(userFpdCoppaMask); + this.metrics = Objects.requireNonNull(metrics); + } + + public boolean isApplicable(AuctionContext auctionContext) { + return auctionContext.getPrivacyContext().getPrivacy().getCoppa() == 1; + } + + public Future> enforce(AuctionContext auctionContext, Map bidderToUser) { + metrics.updatePrivacyCoppaMetric(); + return Future.succeededFuture(results(bidderToUser, auctionContext.getBidRequest().getDevice())); + } + + 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) + .build()) + .toList(); + } +} 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 new file mode 100644 index 00000000000..3f4e4055dca --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementService.java @@ -0,0 +1,62 @@ +package org.prebid.server.auction.privacy.enforcement; + +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.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; + + 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 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)); + } + + private static Set biddersToApplyTcf(Set bidders, List ccpaResult) { + final Set biddersToApplyTcf = new HashSet<>(bidders); + ccpaResult.stream() + .map(BidderPrivacyResult::getRequestBidder) + .forEach(biddersToApplyTcf::remove); + + return biddersToApplyTcf; + } +} 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 new file mode 100644 index 00000000000..afa0c83e8b1 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcement.java @@ -0,0 +1,210 @@ +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 io.vertx.core.logging.Logger; +import io.vertx.core.logging.LoggerFactory; +import org.apache.commons.collections4.CollectionUtils; +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; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdTcfMask; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; +import org.prebid.server.privacy.gdpr.TcfDefinerService; +import org.prebid.server.privacy.gdpr.VendorIdResolver; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.TcfContext; +import org.prebid.server.privacy.gdpr.model.TcfResponse; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountGdprConfig; +import org.prebid.server.settings.model.AccountPrivacyConfig; +import org.prebid.server.util.ObjectUtil; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class TcfEnforcement { + + private static final Logger logger = LoggerFactory.getLogger(TcfEnforcement.class); + + private final TcfDefinerService tcfDefinerService; + private final UserFpdTcfMask userFpdTcfMask; + private final BidderCatalog bidderCatalog; + private final Metrics metrics; + private final boolean lmtEnforce; + + public TcfEnforcement(TcfDefinerService tcfDefinerService, + UserFpdTcfMask userFpdTcfMask, + BidderCatalog bidderCatalog, + Metrics metrics, + boolean lmtEnforce) { + + this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); + this.userFpdTcfMask = Objects.requireNonNull(userFpdTcfMask); + this.bidderCatalog = Objects.requireNonNull(bidderCatalog); + this.metrics = Objects.requireNonNull(metrics); + this.lmtEnforce = lmtEnforce; + } + + public Future> enforce(Set vendorIds, TcfContext tcfContext) { + return tcfDefinerService.resultForVendorIds(vendorIds, tcfContext) + .map(TcfResponse::getActions); + } + + public Future> enforce(AuctionContext auctionContext, + Map bidderToUser, + Set bidders, + BidderAliases aliases) { + + final Device device = auctionContext.getBidRequest().getDevice(); + final AccountGdprConfig accountGdprConfig = accountGdprConfig(auctionContext.getAccount()); + final MetricName requestType = auctionContext.getRequestTypeMetric(); + + return tcfDefinerService.resultForBidderNames( + bidders, + VendorIdResolver.of(aliases, bidderCatalog), + auctionContext.getPrivacyContext().getTcfContext(), + accountGdprConfig) + .map(TcfResponse::getActions) + .map(enforcements -> updateMetrics(enforcements, aliases, requestType, bidderToUser, device)) + .map(enforcements -> bidderToPrivacyResult(enforcements, bidders, bidderToUser, device)); + } + + private static AccountGdprConfig accountGdprConfig(Account account) { + final AccountPrivacyConfig privacyConfig = account.getPrivacy(); + return privacyConfig != null ? privacyConfig.getGdpr() : null; + } + + private Map updateMetrics(Map enforcements, + BidderAliases aliases, + MetricName requestType, + Map bidderToUser, + Device device) { + + // 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); + + final boolean requestBlocked = enforcement.isBlockBidderRequest(); + final boolean ufpdRemoved = !requestBlocked + && ((enforcement.isRemoveUserFpd() && shouldRemoveUserData(user)) + || (enforcement.isMaskDeviceInfo() && shouldRemoveDeviceData(device))); + final boolean uidsRemoved = !requestBlocked && enforcement.isRemoveUserIds() && shouldRemoveUids(user); + final boolean geoMasked = !requestBlocked && enforcement.isMaskGeo() && shouldMaskGeo(user, device); + final boolean analyticsBlocked = !requestBlocked && enforcement.isBlockAnalyticsReport(); + + metrics.updateAuctionTcfMetrics( + aliases.resolveBidder(bidder), + requestType, + ufpdRemoved, + uidsRemoved, + geoMasked, + analyticsBlocked, + requestBlocked); + + if (ufpdRemoved) { + logger.warn("The UFPD fields have been removed due to a consent check."); + } + } + + if (isLmtEnforcedAndEnabled(device)) { + metrics.updatePrivacyLmtMetric(); + } + + return enforcements; + } + + private static boolean shouldRemoveUserData(User user) { + return user != null && ObjectUtils.anyNotNull( + user.getId(), + user.getBuyeruid(), + user.getYob(), + user.getGender(), + user.getKeywords(), + user.getKwarray(), + user.getData(), + ObjectUtil.getIfNotNull(user.getExt(), ExtUser::getData)); + } + + private static boolean shouldRemoveDeviceData(Device device) { + return device != null && ObjectUtils.anyNotNull( + device.getIfa(), + device.getMacsha1(), device.getMacmd5(), + device.getDpidsha1(), device.getDpidmd5(), + device.getDidsha1(), device.getDidmd5()); + } + + private static boolean shouldRemoveUids(User user) { + return user != null && CollectionUtils.isNotEmpty(user.getEids()); + } + + private static boolean shouldMaskGeo(User user, Device device) { + return (user != null && user.getGeo() != null) || (device != null && device.getGeo() != null); + } + + 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); + + return bidders.stream() + .map(bidder -> createBidderPrivacyResult( + bidder, + bidderToUser.get(bidder), + device, + bidderToEnforcement, + isLmtEnabled)) + .toList(); + } + + private BidderPrivacyResult createBidderPrivacyResult(String bidder, + User user, + Device device, + Map bidderToEnforcement, + boolean isLmtEnabled) { + + final PrivacyEnforcementAction privacyEnforcementAction = bidderToEnforcement.get(bidder); + final boolean blockBidderRequest = privacyEnforcementAction.isBlockBidderRequest(); + final boolean blockAnalyticsReport = privacyEnforcementAction.isBlockAnalyticsReport(); + if (blockBidderRequest) { + return BidderPrivacyResult.builder() + .requestBidder(bidder) + .blockedRequestByTcf(true) + .blockedAnalyticsByTcf(blockAnalyticsReport) + .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 maskedUser = userFpdTcfMask.maskUser(user, maskUserFpd, maskUserIds, maskGeo, eidExceptions); + + final boolean maskIp = privacyEnforcementAction.isMaskDeviceIp() || isLmtEnabled; + final boolean maskDeviceInfo = privacyEnforcementAction.isMaskDeviceInfo() || isLmtEnabled; + final Device maskedDevice = userFpdTcfMask.maskDevice(device, maskIp, maskGeo, maskDeviceInfo); + + return BidderPrivacyResult.builder() + .requestBidder(bidder) + .user(maskedUser) + .device(maskedDevice) + .blockedAnalyticsByTcf(blockAnalyticsReport) + .build(); + } +} diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdActivityMask.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdActivityMask.java new file mode 100644 index 00000000000..559d35b02fc --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdActivityMask.java @@ -0,0 +1,33 @@ +package org.prebid.server.auction.privacy.enforcement.mask; + +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.User; + +import java.util.Collections; +import java.util.Objects; + +public class UserFpdActivityMask { + + private final UserFpdTcfMask userFpdTcfMask; + + public UserFpdActivityMask(UserFpdTcfMask userFpdTcfMask) { + this.userFpdTcfMask = Objects.requireNonNull(userFpdTcfMask); + } + + public User maskUser(User user, + boolean disallowTransmitUfpd, + boolean disallowTransmitEids, + boolean disallowTransmitGeo) { + + return userFpdTcfMask.maskUser( + user, + disallowTransmitUfpd, + disallowTransmitEids, + disallowTransmitGeo, + Collections.emptySet()); + } + + public Device maskDevice(Device device, boolean disallowTransmitUfpd, boolean disallowTransmitGeo) { + return userFpdTcfMask.maskDevice(device, disallowTransmitGeo, disallowTransmitGeo, disallowTransmitUfpd); + } +} diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCcpaMask.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCcpaMask.java new file mode 100644 index 00000000000..4ed50a5cb58 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCcpaMask.java @@ -0,0 +1,42 @@ +package org.prebid.server.auction.privacy.enforcement.mask; + +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.User; +import org.prebid.server.auction.IpAddressHelper; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Collections; + +public class UserFpdCcpaMask extends UserFpdPrivacyMask { + + public UserFpdCcpaMask(IpAddressHelper ipAddressHelper) { + super(ipAddressHelper); + } + + public User maskUser(User user) { + return maskUser(user, true, true, true, Collections.emptySet()); + } + + public Device maskDevice(Device device) { + return maskDevice(device, true, true, true); + } + + @Override + protected Geo maskGeo(Geo geo) { + return geo.toBuilder() + .lat(maskGeoCoordinate(geo.getLat())) + .lon(maskGeoCoordinate(geo.getLon())) + .metro(null) + .city(null) + .zip(null) + .build(); + } + + private static Float maskGeoCoordinate(Float coordinate) { + return coordinate != null + ? BigDecimal.valueOf(coordinate).setScale(2, RoundingMode.HALF_UP).floatValue() + : null; + } +} diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCoppaMask.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCoppaMask.java new file mode 100644 index 00000000000..0fc8a6ad6fc --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCoppaMask.java @@ -0,0 +1,34 @@ +package org.prebid.server.auction.privacy.enforcement.mask; + +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.User; +import org.prebid.server.auction.IpAddressHelper; + +import java.util.Collections; + +public class UserFpdCoppaMask extends UserFpdPrivacyMask { + + public UserFpdCoppaMask(IpAddressHelper ipAddressHelper) { + super(ipAddressHelper); + } + + public User maskUser(User user) { + return maskUser(user, true, true, true, Collections.emptySet()); + } + + public Device maskDevice(Device device) { + return maskDevice(device, true, true, true); + } + + @Override + protected Geo maskGeo(Geo geo) { + return geo.toBuilder() + .lat(null) + .lon(null) + .metro(null) + .city(null) + .zip(null) + .build(); + } +} diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdPrivacyMask.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdPrivacyMask.java new file mode 100644 index 00000000000..4d2a04ee203 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdPrivacyMask.java @@ -0,0 +1,119 @@ +package org.prebid.server.auction.privacy.enforcement.mask; + +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Eid; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.User; +import org.apache.commons.collections4.CollectionUtils; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; + +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public abstract class UserFpdPrivacyMask { + + private final IpAddressHelper ipAddressHelper; + + protected UserFpdPrivacyMask(IpAddressHelper ipAddressHelper) { + this.ipAddressHelper = Objects.requireNonNull(ipAddressHelper); + } + + protected User maskUser(User user, + boolean maskUserFpd, + boolean maskEids, + boolean maskGeo, + Set eidExceptions) { + + if (user == null || !(maskUserFpd || maskEids || maskGeo)) { + return user; + } + + final User.UserBuilder userBuilder = user.toBuilder(); + if (maskUserFpd) { + userBuilder + .id(null) + .buyeruid(null) + .yob(null) + .gender(null) + .keywords(null) + .kwarray(null) + .data(null) + .ext(maskExtUser(user.getExt())); + } + + if (maskEids) { + userBuilder.eids(removeEids(user.getEids(), eidExceptions)); + } + + if (maskGeo) { + userBuilder.geo(maskNullableGeo(user.getGeo())); + } + + return nullIfEmpty(userBuilder.build()); + } + + private static ExtUser maskExtUser(ExtUser extUser) { + return extUser != null + ? nullIfEmpty(extUser.toBuilder().data(null).build()) + : null; + } + + private static List removeEids(List eids, Set exceptions) { + if (exceptions.isEmpty() || CollectionUtils.isEmpty(eids)) { + return null; + } + + final List clearedEids = eids.stream() + .filter(Objects::nonNull) + .filter(eid -> exceptions.contains(eid.getSource())) + .toList(); + + return clearedEids.isEmpty() ? null : clearedEids; + } + + private Geo maskNullableGeo(Geo geo) { + return geo != null ? nullIfEmpty(maskGeo(geo)) : null; + } + + protected abstract Geo maskGeo(Geo geo); + + protected Device maskDevice(Device device, boolean maskIp, boolean maskGeo, boolean maskDeviceInfo) { + if (device == null || !(maskIp || maskGeo || maskDeviceInfo)) { + return device; + } + + final Device.DeviceBuilder deviceBuilder = device.toBuilder(); + if (maskIp) { + deviceBuilder + .ip(ipAddressHelper.maskIpv4(device.getIp())) + .ipv6(ipAddressHelper.anonymizeIpv6(device.getIpv6())); + } + + if (maskGeo) { + deviceBuilder.geo(maskNullableGeo(device.getGeo())); + } + + if (maskDeviceInfo) { + deviceBuilder.ifa(null) + .macsha1(null).macmd5(null) + .dpidsha1(null).dpidmd5(null) + .didsha1(null).didmd5(null); + } + + return deviceBuilder.build(); + } + + private static User nullIfEmpty(User user) { + return user.equals(User.EMPTY) ? null : user; + } + + private static Geo nullIfEmpty(Geo geo) { + return geo.equals(Geo.EMPTY) ? null : geo; + } + + private static ExtUser nullIfEmpty(ExtUser userExt) { + return userExt.isEmpty() ? null : userExt; + } +} diff --git a/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdTcfMask.java b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdTcfMask.java new file mode 100644 index 00000000000..744b492807d --- /dev/null +++ b/src/main/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdTcfMask.java @@ -0,0 +1,41 @@ +package org.prebid.server.auction.privacy.enforcement.mask; + +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.User; +import org.prebid.server.auction.IpAddressHelper; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Set; + +public class UserFpdTcfMask extends UserFpdPrivacyMask { + + public UserFpdTcfMask(IpAddressHelper ipAddressHelper) { + super(ipAddressHelper); + } + + public User maskUser(User user, boolean maskUserFpd, boolean maskEids, boolean maskGeo, Set eidExceptions) { + return super.maskUser(user, maskUserFpd, maskEids, maskGeo, eidExceptions); + } + + public Device maskDevice(Device device, boolean maskIp, boolean maskGeo, boolean maskDeviceInfo) { + return super.maskDevice(device, maskIp, maskGeo, maskDeviceInfo); + } + + @Override + protected Geo maskGeo(Geo geo) { + return geo != null + ? geo.toBuilder() + .lat(maskGeoCoordinate(geo.getLat())) + .lon(maskGeoCoordinate(geo.getLon())) + .build() + : null; + } + + private static Float maskGeoCoordinate(Float coordinate) { + return coordinate != null + ? BigDecimal.valueOf(coordinate).setScale(2, RoundingMode.HALF_UP).floatValue() + : null; + } +} diff --git a/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java b/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java index a96c26d6776..ec9a3875a07 100644 --- a/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java @@ -26,7 +26,7 @@ import org.prebid.server.auction.gpp.AmpGppService; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.ConsentType; -import org.prebid.server.auction.privacycontextfactory.AmpPrivacyContextFactory; +import org.prebid.server.auction.privacy.contextfactory.AmpPrivacyContextFactory; import org.prebid.server.auction.versionconverter.BidRequestOrtbVersionConversionManager; import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.json.JacksonMapper; diff --git a/src/main/java/org/prebid/server/auction/requestfactory/AuctionRequestFactory.java b/src/main/java/org/prebid/server/auction/requestfactory/AuctionRequestFactory.java index 56efcd97f02..128e4b2630c 100644 --- a/src/main/java/org/prebid/server/auction/requestfactory/AuctionRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/requestfactory/AuctionRequestFactory.java @@ -10,11 +10,11 @@ import org.prebid.server.auction.ImplicitParametersExtractor; import org.prebid.server.auction.InterstitialProcessor; import org.prebid.server.auction.OrtbTypesResolver; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.StoredRequestProcessor; import org.prebid.server.auction.gpp.AuctionGppService; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.AuctionStoredResult; +import org.prebid.server.auction.privacy.contextfactory.AuctionPrivacyContextFactory; import org.prebid.server.auction.versionconverter.BidRequestOrtbVersionConversionManager; import org.prebid.server.cookie.CookieDeprecationService; import org.prebid.server.exception.InvalidRequestException; @@ -44,7 +44,7 @@ public class AuctionRequestFactory { private final ImplicitParametersExtractor paramsExtractor; private final Ortb2ImplicitParametersResolver paramsResolver; private final InterstitialProcessor interstitialProcessor; - private final PrivacyEnforcementService privacyEnforcementService; + private final AuctionPrivacyContextFactory auctionPrivacyContextFactory; private final DebugResolver debugResolver; private final JacksonMapper mapper; private final OrtbTypesResolver ortbTypesResolver; @@ -61,7 +61,7 @@ public AuctionRequestFactory(long maxRequestSize, Ortb2ImplicitParametersResolver paramsResolver, InterstitialProcessor interstitialProcessor, OrtbTypesResolver ortbTypesResolver, - PrivacyEnforcementService privacyEnforcementService, + AuctionPrivacyContextFactory auctionPrivacyContextFactory, DebugResolver debugResolver, JacksonMapper mapper) { @@ -75,7 +75,7 @@ public AuctionRequestFactory(long maxRequestSize, this.paramsResolver = Objects.requireNonNull(paramsResolver); this.interstitialProcessor = Objects.requireNonNull(interstitialProcessor); this.ortbTypesResolver = Objects.requireNonNull(ortbTypesResolver); - this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); + this.auctionPrivacyContextFactory = Objects.requireNonNull(auctionPrivacyContextFactory); this.debugResolver = Objects.requireNonNull(debugResolver); this.mapper = Objects.requireNonNull(mapper); } @@ -118,7 +118,7 @@ public Future fromRequest(RoutingContext routingContext, long st .compose(auctionContext -> updateAndValidateBidRequest(auctionContext) .map(auctionContext::with)) - .compose(auctionContext -> privacyEnforcementService.contextFromBidRequest(auctionContext) + .compose(auctionContext -> auctionPrivacyContextFactory.contextFrom(auctionContext) .map(auctionContext::with)) .map(auctionContext -> auctionContext.with( diff --git a/src/main/java/org/prebid/server/auction/requestfactory/VideoRequestFactory.java b/src/main/java/org/prebid/server/auction/requestfactory/VideoRequestFactory.java index 9f8fb7547dc..07c256b216a 100644 --- a/src/main/java/org/prebid/server/auction/requestfactory/VideoRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/requestfactory/VideoRequestFactory.java @@ -16,11 +16,11 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.DebugResolver; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.VideoStoredRequestProcessor; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.CachedDebugLog; import org.prebid.server.auction.model.WithPodErrors; +import org.prebid.server.auction.privacy.contextfactory.AuctionPrivacyContextFactory; import org.prebid.server.auction.model.debug.DebugContext; import org.prebid.server.auction.versionconverter.BidRequestOrtbVersionConversionManager; import org.prebid.server.exception.InvalidRequestException; @@ -57,7 +57,7 @@ public class VideoRequestFactory { private final VideoStoredRequestProcessor storedRequestProcessor; private final BidRequestOrtbVersionConversionManager ortbVersionConversionManager; private final Ortb2ImplicitParametersResolver paramsResolver; - private final PrivacyEnforcementService privacyEnforcementService; + private final AuctionPrivacyContextFactory auctionPrivacyContextFactory; private final DebugResolver debugResolver; private final JacksonMapper mapper; @@ -68,7 +68,7 @@ public VideoRequestFactory(int maxRequestSize, VideoStoredRequestProcessor storedRequestProcessor, BidRequestOrtbVersionConversionManager ortbVersionConversionManager, Ortb2ImplicitParametersResolver paramsResolver, - PrivacyEnforcementService privacyEnforcementService, + AuctionPrivacyContextFactory auctionPrivacyContextFactory, DebugResolver debugResolver, JacksonMapper mapper) { @@ -78,7 +78,7 @@ public VideoRequestFactory(int maxRequestSize, this.storedRequestProcessor = Objects.requireNonNull(storedRequestProcessor); this.ortbVersionConversionManager = Objects.requireNonNull(ortbVersionConversionManager); this.paramsResolver = Objects.requireNonNull(paramsResolver); - this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); + this.auctionPrivacyContextFactory = Objects.requireNonNull(auctionPrivacyContextFactory); this.debugResolver = Objects.requireNonNull(debugResolver); this.mapper = Objects.requireNonNull(mapper); @@ -108,9 +108,9 @@ public Future> fromRequest(RoutingContext routingC createBidRequest(httpRequest) .compose(bidRequest -> validateRequest( - bidRequest, - httpRequest, - initialAuctionContext.getDebugWarnings())) + bidRequest, + httpRequest, + initialAuctionContext.getDebugWarnings())) .map(bidRequestWithErrors -> populatePodErrors( bidRequestWithErrors.getPodErrors(), podErrors, bidRequestWithErrors)) @@ -126,7 +126,7 @@ public Future> fromRequest(RoutingContext routingC .compose(auctionContext -> ortb2RequestFactory.activityInfrastructureFrom(auctionContext) .map(auctionContext::with)) - .compose(auctionContext -> privacyEnforcementService.contextFromBidRequest(auctionContext) + .compose(auctionContext -> auctionPrivacyContextFactory.contextFrom(auctionContext) .map(auctionContext::with)) .map(auctionContext -> auctionContext.with( diff --git a/src/main/java/org/prebid/server/bidder/minutemedia/MinuteMediaBidder.java b/src/main/java/org/prebid/server/bidder/minutemedia/MinuteMediaBidder.java new file mode 100644 index 00000000000..544e4df12d8 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/minutemedia/MinuteMediaBidder.java @@ -0,0 +1,134 @@ +package org.prebid.server.bidder.minutemedia; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.bidder.Bidder; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderCall; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpRequest; +import org.prebid.server.bidder.model.Result; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.json.DecodeException; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.minutemedia.ExtImpMinuteMedia; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.BidderUtil; +import org.prebid.server.util.HttpUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class MinuteMediaBidder implements Bidder { + + private static final TypeReference> MINUTE_MEDIA_EXT_TYPE_REFERENCE = + new TypeReference<>() { + }; + public static final String PUBLISHER_ID_MACRO = "{{PublisherId}}"; + + private final String endpointUrl; + private final JacksonMapper mapper; + + public MinuteMediaBidder(String endpointUrl, JacksonMapper mapper) { + this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.mapper = Objects.requireNonNull(mapper); + } + + @Override + public Result>> makeHttpRequests(BidRequest bidRequest) { + final String orgId; + + try { + orgId = extractFirstImpOrdId(bidRequest.getImp()); + } catch (PreBidException e) { + return Result.withError(BidderError.badInput(e.getMessage())); + } + + return Result.withValue(BidderUtil.defaultRequest(bidRequest, resolveEndpoint(endpointUrl, orgId), mapper)); + } + + private String extractFirstImpOrdId(List imps) { + return imps.stream() + .findFirst() + .map(this::parseImpExt) + .map(ExtImpMinuteMedia::getOrg) + .map(String::strip) + .filter(StringUtils::isNotBlank) + .orElseThrow(() -> new PreBidException( + "Failed to extract bidrequest.imp[0].ext.prebid.bidder.minutemedia.org parameter")); + } + + private ExtImpMinuteMedia parseImpExt(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), MINUTE_MEDIA_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage()); + } + } + + private String resolveEndpoint(String endpointUrl, String orgId) { + return endpointUrl.replace(PUBLISHER_ID_MACRO, HttpUtil.encodeUrl(orgId)); + } + + @Override + public Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { + final List errors = new ArrayList<>(); + try { + final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + return Result.of(extractBids(bidResponse, errors), errors); + } catch (DecodeException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + } + + private static List extractBids(BidResponse bidResponse, List errors) { + if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + return Collections.emptyList(); + } + return bidsFromResponse(bidResponse, errors); + } + + private static List bidsFromResponse(BidResponse bidResponse, List errors) { + return bidResponse.getSeatbid().stream() + .filter(Objects::nonNull) + .map(SeatBid::getBid) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .map(bid -> makeBidderBid(bid, bidResponse.getCur(), errors)) + .filter(Objects::nonNull) + .toList(); + } + + private static BidderBid makeBidderBid(Bid bid, String currency, List errors) { + try { + return BidderBid.of(bid, getBidType(bid), currency); + } catch (PreBidException e) { + errors.add(BidderError.badServerResponse(e.getMessage())); + return null; + } + } + + private static BidType getBidType(Bid bid) { + final Integer markupType = bid.getMtype(); + if (markupType == null) { + throw new PreBidException("Missing mediaType for bid: %s".formatted(bid.getId())); + } + + return switch (markupType) { + case 1 -> BidType.banner; + case 2 -> BidType.video; + default -> throw new PreBidException( + "Unsupported bid mediaType: %s for impression: %s".formatted(bid.getMtype(), bid.getImpid())); + }; + } +} diff --git a/src/main/java/org/prebid/server/cookie/CookieSyncService.java b/src/main/java/org/prebid/server/cookie/CookieSyncService.java index 8f935738ae9..074fab249af 100644 --- a/src/main/java/org/prebid/server/cookie/CookieSyncService.java +++ b/src/main/java/org/prebid/server/cookie/CookieSyncService.java @@ -11,7 +11,7 @@ import org.prebid.server.activity.ComponentType; import org.prebid.server.activity.infrastructure.payload.impl.ActivityInvocationPayloadImpl; import org.prebid.server.activity.infrastructure.payload.impl.TcfContextActivityInvocationPayload; -import org.prebid.server.auction.PrivacyEnforcementService; +import org.prebid.server.auction.privacy.enforcement.CcpaEnforcement; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.bidder.BidderInfo; import org.prebid.server.bidder.UsersyncInfoBuilder; @@ -66,7 +66,7 @@ public class CookieSyncService { private final BidderCatalog bidderCatalog; private final HostVendorTcfDefinerService tcfDefinerService; - private final PrivacyEnforcementService privacyEnforcementService; + private final CcpaEnforcement ccpaEnforcement; private final UidsCookieService uidsCookieService; private final CoopSyncProvider coopSyncProvider; private final Metrics metrics; @@ -76,7 +76,7 @@ public CookieSyncService(String externalUrl, int maxLimit, BidderCatalog bidderCatalog, HostVendorTcfDefinerService tcfDefinerService, - PrivacyEnforcementService privacyEnforcementService, + CcpaEnforcement ccpaEnforcement, UidsCookieService uidsCookieService, CoopSyncProvider coopSyncProvider, Metrics metrics) { @@ -88,7 +88,7 @@ public CookieSyncService(String externalUrl, this.bidderCatalog = Objects.requireNonNull(bidderCatalog); this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); - this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); + this.ccpaEnforcement = Objects.requireNonNull(ccpaEnforcement); this.uidsCookieService = Objects.requireNonNull(uidsCookieService); this.coopSyncProvider = Objects.requireNonNull(coopSyncProvider); this.metrics = Objects.requireNonNull(metrics); @@ -311,7 +311,7 @@ private CookieSyncContext updateWithPrivacy(TcfResponse tcfResponse, Coo } private Set extractCcpaEnforcedBidders(Account account, Collection biddersToSync, Privacy privacy) { - if (!privacyEnforcementService.isCcpaEnforced(privacy.getCcpa(), account)) { + if (!ccpaEnforcement.isCcpaEnforced(privacy.getCcpa(), account)) { return Collections.emptySet(); } diff --git a/src/main/java/org/prebid/server/handler/CookieSyncHandler.java b/src/main/java/org/prebid/server/handler/CookieSyncHandler.java index 6dfbf866359..2009743ec8f 100644 --- a/src/main/java/org/prebid/server/handler/CookieSyncHandler.java +++ b/src/main/java/org/prebid/server/handler/CookieSyncHandler.java @@ -13,8 +13,8 @@ import org.prebid.server.activity.infrastructure.creator.ActivityInfrastructureCreator; import org.prebid.server.analytics.model.CookieSyncEvent; import org.prebid.server.analytics.reporter.AnalyticsReporterDelegator; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.gpp.CookieSyncGppService; +import org.prebid.server.auction.privacy.contextfactory.CookieSyncPrivacyContextFactory; import org.prebid.server.bidder.UsersyncMethodChooser; import org.prebid.server.cookie.CookieDeprecationService; import org.prebid.server.cookie.CookieSyncService; @@ -59,7 +59,7 @@ public class CookieSyncHandler implements Handler { private final ActivityInfrastructureCreator activityInfrastructureCreator; private final CookieSyncService cookieSyncService; private final ApplicationSettings applicationSettings; - private final PrivacyEnforcementService privacyEnforcementService; + private final CookieSyncPrivacyContextFactory cookieSyncPrivacyContextFactory; private final AnalyticsReporterDelegator analyticsDelegator; private final Metrics metrics; private final TimeoutFactory timeoutFactory; @@ -73,7 +73,7 @@ public CookieSyncHandler(long defaultTimeout, ActivityInfrastructureCreator activityInfrastructureCreator, CookieSyncService cookieSyncService, ApplicationSettings applicationSettings, - PrivacyEnforcementService privacyEnforcementService, + CookieSyncPrivacyContextFactory cookieSyncPrivacyContextFactory, AnalyticsReporterDelegator analyticsDelegator, Metrics metrics, TimeoutFactory timeoutFactory, @@ -87,7 +87,7 @@ public CookieSyncHandler(long defaultTimeout, this.activityInfrastructureCreator = Objects.requireNonNull(activityInfrastructureCreator); this.cookieSyncService = Objects.requireNonNull(cookieSyncService); this.applicationSettings = Objects.requireNonNull(applicationSettings); - this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); + this.cookieSyncPrivacyContextFactory = Objects.requireNonNull(cookieSyncPrivacyContextFactory); this.analyticsDelegator = Objects.requireNonNull(analyticsDelegator); this.metrics = Objects.requireNonNull(metrics); this.timeoutFactory = Objects.requireNonNull(timeoutFactory); @@ -185,7 +185,7 @@ private CookieSyncContext processGpp(CookieSyncContext cookieSyncContext) { } private Future fillWithPrivacyContext(CookieSyncContext cookieSyncContext) { - return privacyEnforcementService.contextFromCookieSyncRequest( + return cookieSyncPrivacyContextFactory.contextFrom( cookieSyncContext.getCookieSyncRequest(), cookieSyncContext.getRoutingContext().request(), cookieSyncContext.getAccount(), diff --git a/src/main/java/org/prebid/server/handler/SetuidHandler.java b/src/main/java/org/prebid/server/handler/SetuidHandler.java index 205a1292edc..5472e5a36b1 100644 --- a/src/main/java/org/prebid/server/handler/SetuidHandler.java +++ b/src/main/java/org/prebid/server/handler/SetuidHandler.java @@ -23,9 +23,9 @@ import org.prebid.server.activity.infrastructure.payload.impl.TcfContextActivityInvocationPayload; import org.prebid.server.analytics.model.SetuidEvent; import org.prebid.server.analytics.reporter.AnalyticsReporterDelegator; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.gpp.SetuidGppService; import org.prebid.server.auction.model.SetuidContext; +import org.prebid.server.auction.privacy.contextfactory.SetuidPrivacyContextFactory; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.bidder.UsersyncFormat; import org.prebid.server.bidder.UsersyncMethod; @@ -76,7 +76,7 @@ public class SetuidHandler implements Handler { private final long defaultTimeout; private final UidsCookieService uidsCookieService; private final ApplicationSettings applicationSettings; - private final PrivacyEnforcementService privacyEnforcementService; + private final SetuidPrivacyContextFactory setuidPrivacyContextFactory; private final SetuidGppService gppService; private final ActivityInfrastructureCreator activityInfrastructureCreator; private final HostVendorTcfDefinerService tcfDefinerService; @@ -89,7 +89,7 @@ public SetuidHandler(long defaultTimeout, UidsCookieService uidsCookieService, ApplicationSettings applicationSettings, BidderCatalog bidderCatalog, - PrivacyEnforcementService privacyEnforcementService, + SetuidPrivacyContextFactory setuidPrivacyContextFactory, SetuidGppService gppService, ActivityInfrastructureCreator activityInfrastructureCreator, HostVendorTcfDefinerService tcfDefinerService, @@ -100,7 +100,7 @@ public SetuidHandler(long defaultTimeout, this.defaultTimeout = defaultTimeout; this.uidsCookieService = Objects.requireNonNull(uidsCookieService); this.applicationSettings = Objects.requireNonNull(applicationSettings); - this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService); + this.setuidPrivacyContextFactory = Objects.requireNonNull(setuidPrivacyContextFactory); this.gppService = Objects.requireNonNull(gppService); this.activityInfrastructureCreator = Objects.requireNonNull(activityInfrastructureCreator); this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); @@ -164,7 +164,7 @@ private Future toSetuidContext(RoutingContext routingContext) { final Timeout timeout = timeoutFactory.create(defaultTimeout); return accountById(requestAccount, timeout) - .compose(account -> privacyEnforcementService.contextFromSetuidRequest(httpRequest, account, timeout) + .compose(account -> setuidPrivacyContextFactory.contextFrom(httpRequest, account, timeout) .map(privacyContext -> SetuidContext.builder() .routingContext(routingContext) .uidsCookie(uidsCookie) diff --git a/src/main/java/org/prebid/server/metric/ActivitiesMetrics.java b/src/main/java/org/prebid/server/metric/ActivitiesMetrics.java index 5a3df47a228..e4e52badcf6 100644 --- a/src/main/java/org/prebid/server/metric/ActivitiesMetrics.java +++ b/src/main/java/org/prebid/server/metric/ActivitiesMetrics.java @@ -35,6 +35,7 @@ private static String suffixFromActivity(Activity activity) { case CALL_BIDDER -> "fetch_bids"; case MODIFY_UFDP -> "enrich_ufpd"; case TRANSMIT_UFPD -> "transmit_ufpd"; + case TRANSMIT_EIDS -> "transmit_eids"; case TRANSMIT_GEO -> "transmit_precise_geo"; case TRANSMIT_TID -> "transmit_tid"; case REPORT_ANALYTICS -> "report_analytics"; diff --git a/src/main/java/org/prebid/server/metric/MetricName.java b/src/main/java/org/prebid/server/metric/MetricName.java index ca51fe09b73..09467050ff7 100644 --- a/src/main/java/org/prebid/server/metric/MetricName.java +++ b/src/main/java/org/prebid/server/metric/MetricName.java @@ -86,6 +86,7 @@ public enum MetricName { sizedout, // tcf + userfpd_masked, userid_removed, geo_masked, request_blocked, diff --git a/src/main/java/org/prebid/server/metric/Metrics.java b/src/main/java/org/prebid/server/metric/Metrics.java index aa5316235b5..8ce8e1b4936 100644 --- a/src/main/java/org/prebid/server/metric/Metrics.java +++ b/src/main/java/org/prebid/server/metric/Metrics.java @@ -396,14 +396,18 @@ public void updateCookieSyncTcfBlockedMetric(String bidder) { public void updateAuctionTcfMetrics(String bidder, MetricName requestType, - boolean userIdRemoved, + boolean userFpdRemoved, + boolean userIdsRemoved, boolean geoMasked, boolean analyticsBlocked, boolean requestBlocked) { final TcfMetrics tcf = forAdapter(bidder).requestType(requestType).tcf(); - if (userIdRemoved) { + if (userFpdRemoved) { + tcf.incCounter(MetricName.userfpd_masked); + } + if (userIdsRemoved) { tcf.incCounter(MetricName.userid_removed); } if (geoMasked) { diff --git a/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java b/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java index bdcdeaa8de5..f2369e6247f 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java @@ -4,6 +4,8 @@ import io.vertx.core.Future; import lombok.Value; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.SetUtils; +import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; @@ -17,6 +19,7 @@ import org.prebid.server.settings.model.EnforcePurpose; import org.prebid.server.settings.model.GdprConfig; import org.prebid.server.settings.model.Purpose; +import org.prebid.server.settings.model.PurposeEid; import org.prebid.server.settings.model.PurposeOneTreatmentInterpretation; import org.prebid.server.settings.model.Purposes; import org.prebid.server.settings.model.SpecialFeature; @@ -27,9 +30,9 @@ 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; -import java.util.stream.Stream; public class Tcf2Service { @@ -48,9 +51,9 @@ public Tcf2Service(GdprConfig gdprConfig, BidderCatalog bidderCatalog) { this.defaultPurposes = gdprConfig.getPurposes() == null ? Purposes.builder().build() : gdprConfig.getPurposes(); - this.defaultSpecialFeatures = gdprConfig.getSpecialFeatures() == null - ? SpecialFeatures.builder().build() - : gdprConfig.getSpecialFeatures(); + this.defaultSpecialFeatures = ObjectUtils.defaultIfNull( + gdprConfig.getSpecialFeatures(), + SpecialFeatures.builder().build()); this.purposeOneTreatmentInterpretation = gdprConfig.getPurposeOneTreatmentInterpretation(); this.versionedVendorListService = Objects.requireNonNull(versionedVendorListService); this.bidderCatalog = Objects.requireNonNull(bidderCatalog); @@ -75,7 +78,9 @@ private Collection vendorPermissions(Set vendorIds) { // this check only for illegal arguments... .filter(Objects::nonNull) .map(vendorId -> VendorPermission.of( - vendorId, bidderCatalog.nameByVendorId(vendorId), PrivacyEnforcementAction.restrictAll())) + vendorId, + bidderCatalog.nameByVendorId(vendorId), + PrivacyEnforcementAction.restrictAll())) .toList(); } @@ -84,7 +89,9 @@ private Collection vendorPermissions(Set bidderNames, // this check only for illegal arguments... .filter(Objects::nonNull) .map(bidderName -> VendorPermission.of( - vendorIdResolver.resolve(bidderName), bidderName, PrivacyEnforcementAction.restrictAll())) + vendorIdResolver.resolve(bidderName), + bidderName, + PrivacyEnforcementAction.restrictAll())) .toList(); } @@ -93,23 +100,26 @@ private Future> permissionsForInternal(Collection vendorPermissionsByType = toVendorPermissionsByType( - vendorPermissions, accountGdprConfig); + final VendorPermissionsByType vendorPermissionsByType = + toVendorPermissionsByType(vendorPermissions, accountGdprConfig); + // TODO: always merge account config for purpose1 with next major release return versionedVendorListService.forConsent(tcfConsent) - .map(vendorGvlPermissions -> wrapWithGVL(vendorPermissionsByType, vendorGvlPermissions)) - - .compose(gvlResult -> processSupportedPurposeStrategies(tcfConsent, gvlResult, mergedPurposes, + .compose(vendorGvlPermissions -> processSupportedPurposeStrategies( + tcfConsent, + wrapWithGVL(vendorPermissionsByType, vendorGvlPermissions), + mergedPurposes, purposeOneTreatmentInterpretation), - ignoredFailed -> processDowngradedSupportedPurposeStrategies(tcfConsent, - vendorPermissionsByType, mergedPurposes, mergedPurposeOneTreatmentInterpretation)) - - .map(changedVendorPermissions -> processSupportedSpecialFeatureStrategies(tcfConsent, - changedVendorPermissions, mergedSpecialFeatures)); + ignored -> processDowngradedSupportedPurposeStrategies( + tcfConsent, + wrapWithGVL(vendorPermissionsByType, Collections.emptyMap()), + mergedPurposes, + mergePurposeOneTreatmentInterpretation(accountGdprConfig))) + .map(ignored -> enforcePurpose4IfRequired(mergedPurposes, vendorPermissionsByType)) + .map(ignored -> processSupportedSpecialFeatureStrategies( + tcfConsent, + vendorPermissions, + mergeAccountSpecialFeatures(accountGdprConfig))); } private static VendorPermissionsByType toVendorPermissionsByType( @@ -127,13 +137,9 @@ private static VendorPermissionsByType toVendorPermissionsByTy .collect(Collectors.partitioningBy(vendorPermission -> basicEnforcedVendors.contains(vendorPermission.getBidderName()))); - final List weakPermissions = isBasicEnforcedToPermissions.getOrDefault(true, - Collections.emptyList()); - - final List standardPermissions = isBasicEnforcedToPermissions.getOrDefault(false, - Collections.emptyList()); - - return VendorPermissionsByType.of(weakPermissions, standardPermissions); + return VendorPermissionsByType.of( + isBasicEnforcedToPermissions.getOrDefault(true, Collections.emptyList()), + isBasicEnforcedToPermissions.getOrDefault(false, Collections.emptyList())); } private static VendorPermissionsByType wrapWithGVL( @@ -156,16 +162,16 @@ private static VendorPermissionWithGvl wrapWithGVL(VendorPermission vendorPermis Map vendorGvlPermissions) { final Integer vendorId = vendorPermission.getVendorId(); - final Vendor vendorGvlByVendorId = vendorId != null - ? vendorGvlPermissions.getOrDefault(vendorId, Vendor.empty(vendorId)) - : Vendor.empty(vendorId); + final Vendor vendorGvlByVendorId = Optional.ofNullable(vendorId) + .map(vendorGvlPermissions::get) + .orElseGet(() -> Vendor.empty(vendorId)); return VendorPermissionWithGvl.of(vendorPermission, vendorGvlByVendorId); } - private Future> processSupportedPurposeStrategies( + private Future processSupportedPurposeStrategies( TCString tcfConsent, - VendorPermissionsByType vendorPermissionsByType, + VendorPermissionsByType permissions, Purposes purposes, PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation) { @@ -174,59 +180,90 @@ private Future> processSupportedPurposeStrategies( final Purpose purposeById = findPurposeByTcfPurpose(tcfPurpose, purposes); final Purpose weakPurpose = weakPurpose(purposeById); - final Collection standardPermissions = vendorPermissionsByType - .getStandardPermissions(); - final Collection weakPermissions = vendorPermissionsByType.getWeakPermissions(); + final Collection standardPermissions = permissions.getStandardPermissions(); + final Collection weakPermissions = permissions.getWeakPermissions(); - processPurposeStrategy(tcfConsent, standardPermissions, purposeById, purposeStrategy, - purposeOneTreatmentInterpretation, false); - processPurposeStrategy(tcfConsent, weakPermissions, weakPurpose, purposeStrategy, - purposeOneTreatmentInterpretation, true); + processPurposeStrategy( + tcfConsent, + standardPermissions, + purposeById, + purposeStrategy, + purposeOneTreatmentInterpretation, + false); + processPurposeStrategy( + tcfConsent, + weakPermissions, + weakPurpose, + purposeStrategy, + purposeOneTreatmentInterpretation, + true); } - return Future.succeededFuture(vendorPermissionsByType.joinPermissions().stream() - .map(VendorPermissionWithGvl::getVendorPermission) - .toList()); + return Future.succeededFuture(); } - private Future> processDowngradedSupportedPurposeStrategies( + private Future processDowngradedSupportedPurposeStrategies( TCString tcfConsent, - VendorPermissionsByType vendorPermissionsByType, + VendorPermissionsByType permissions, Purposes purposes, PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation) { - final VendorPermissionsByType vendorPermissionsWithGvlByType = wrapWithGVL( - vendorPermissionsByType, Collections.emptyMap()); - for (PurposeStrategy purposeStrategy : purposeStrategies) { final PurposeCode tcfPurpose = purposeStrategy.getPurpose(); final Purpose downgradedPurposeById = downgradePurpose(findPurposeByTcfPurpose(tcfPurpose, purposes)); final Purpose weakPurpose = weakPurpose(downgradedPurposeById); - final Collection standardPermissions = vendorPermissionsWithGvlByType - .getStandardPermissions(); - final Collection weakPermissions = vendorPermissionsWithGvlByType - .getWeakPermissions(); + final Collection standardPermissions = permissions.getStandardPermissions(); + final Collection weakPermissions = permissions.getWeakPermissions(); - processPurposeStrategy(tcfConsent, standardPermissions, downgradedPurposeById, purposeStrategy, - purposeOneTreatmentInterpretation, true); - processPurposeStrategy(tcfConsent, weakPermissions, weakPurpose, purposeStrategy, - purposeOneTreatmentInterpretation, true); + processPurposeStrategy( + tcfConsent, + standardPermissions, + downgradedPurposeById, + purposeStrategy, + purposeOneTreatmentInterpretation, + true); + processPurposeStrategy( + tcfConsent, + weakPermissions, + weakPurpose, + purposeStrategy, + purposeOneTreatmentInterpretation, + true); } - return Future.succeededFuture(vendorPermissionsByType.joinPermissions()); + return Future.succeededFuture(); } - private void processPurposeStrategy(TCString tcfConsent, - Collection vendorPermissionsWithGvl, - Purpose purpose, - PurposeStrategy purposeStrategy, - PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation, - boolean wasDowngraded) { + private static Purpose downgradePurpose(Purpose purpose) { + final EnforcePurpose enforcePurpose = purpose.getEnforcePurpose(); + + return enforcePurpose == null || enforcePurpose == EnforcePurpose.full + ? Purpose.of( + EnforcePurpose.basic, + purpose.getEnforceVendors(), + purpose.getVendorExceptions(), + purpose.getEid()) + : purpose; + } + + private static Purpose weakPurpose(Purpose purpose) { + final EnforcePurpose enforcePurpose = purpose.getEnforcePurpose(); + final EnforcePurpose downgradedEnforce = enforcePurpose == null || enforcePurpose == EnforcePurpose.full + ? EnforcePurpose.basic + : enforcePurpose; + + return Purpose.of(downgradedEnforce, false, purpose.getVendorExceptions(), purpose.getEid()); + } - if (purposeStrategy.getPurpose() == PurposeCode.ONE - && tcfConsent.getPurposeOneTreatment()) { + private static void processPurposeStrategy(TCString tcfConsent, + Collection vendorPermissionsWithGvl, + Purpose purpose, + PurposeStrategy purposeStrategy, + PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation, + boolean wasDowngraded) { + if (purposeStrategy.getPurpose() == PurposeCode.ONE && tcfConsent.getPurposeOneTreatment()) { processPurposeOneTreatment( purposeOneTreatmentInterpretation, tcfConsent, @@ -239,16 +276,16 @@ private void processPurposeStrategy(TCString tcfConsent, } } - private void processPurposeOneTreatment(PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation, - TCString tcfConsent, - Purpose purposeOne, - PurposeStrategy purposeOneStrategy, - Collection vendorPermissionsWithGvl, - boolean wasDowngraded) { + private static void processPurposeOneTreatment(PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation, + TCString tcfConsent, + Purpose purposeOne, + PurposeStrategy purposeOneStrategy, + Collection vendorPermissionsWithGvl, + boolean wasDowngraded) { switch (purposeOneTreatmentInterpretation) { - case accessAllowed -> vendorPermissionsWithGvl.forEach(vendorPermission -> - purposeOneStrategy.allow(vendorPermission.getVendorPermission().getPrivacyEnforcementAction())); + case accessAllowed -> vendorPermissionsWithGvl.forEach(vendorPermissionWithGvl -> + purposeOneStrategy.allow(vendorPermissionWithGvl.getVendorPermission())); case noAccessAllowed -> { // no need for special processing of no-access-allowed since everything is disallowed from the beginning } @@ -257,22 +294,45 @@ private void processPurposeOneTreatment(PurposeOneTreatmentInterpretation purpos } } - private static Purpose downgradePurpose(Purpose purpose) { - final EnforcePurpose enforcePurpose = purpose.getEnforcePurpose(); + // TODO: remove after transition period + private static Future enforcePurpose4IfRequired(Purposes purposes, + VendorPermissionsByType permissions) { - return enforcePurpose == null || Objects.equals(enforcePurpose, EnforcePurpose.full) - ? Purpose.of(EnforcePurpose.basic, purpose.getEnforceVendors(), purpose.getVendorExceptions()) - : purpose; + final PurposeEid purpose4Eid = purposes.getP4().getEid(); + if (purpose4Eid != null && purpose4Eid.isRequireConsent()) { + final Set exceptions = SetUtils.emptyIfNull(purpose4Eid.getExceptions()); + + requireConsentForPurpose4(permissions.getStandardPermissions(), exceptions); + requireConsentForPurpose4(permissions.getWeakPermissions(), exceptions); + } + + return Future.succeededFuture(); } - private static Purpose weakPurpose(Purpose purpose) { - final EnforcePurpose enforcePurpose = purpose.getEnforcePurpose(); - final EnforcePurpose downgradedEnforce = - enforcePurpose == null || Objects.equals(enforcePurpose, EnforcePurpose.full) - ? EnforcePurpose.basic - : enforcePurpose; + private static void requireConsentForPurpose4(Collection permissions, + Set eidExceptions) { - return Purpose.of(downgradedEnforce, false, purpose.getVendorExceptions()); + for (VendorPermission vendorPermission : permissions) { + if (hasConsentForPurpose4(vendorPermission)) { + continue; + } + + final PrivacyEnforcementAction privacyEnforcementAction = vendorPermission.getPrivacyEnforcementAction(); + privacyEnforcementAction.setRemoveUserIds(true); + if (hasNaturalConsentForAnyPurposeExcept1(vendorPermission)) { + privacyEnforcementAction.setEidExceptions(eidExceptions); + } + } + } + + private static boolean hasConsentForPurpose4(VendorPermission vendorPermission) { + return vendorPermission.getConsentedPurposes().contains(PurposeCode.FOUR) + || vendorPermission.getNaturallyConsentedPurposes().contains(PurposeCode.FOUR); + } + + private static boolean hasNaturalConsentForAnyPurposeExcept1(VendorPermission vendorPermission) { + final Set purposes = vendorPermission.getNaturallyConsentedPurposes(); + return purposes.size() > 1 || (purposes.size() == 1 && !purposes.contains(PurposeCode.ONE)); } private Collection processSupportedSpecialFeatureStrategies( @@ -290,12 +350,12 @@ private Collection processSupportedSpecialFeatureStrategies( } private Purposes mergeAccountPurposes(AccountGdprConfig accountGdprConfig) { - if (accountGdprConfig == null || accountGdprConfig.getPurposes() == null) { - return defaultPurposes; - } + final Purposes accountPurposes = accountGdprConfig != null + ? accountGdprConfig.getPurposes() + : null; - final Purposes accountPurposes = accountGdprConfig.getPurposes(); - return Purposes.builder() + return accountPurposes != null + ? Purposes.builder() .p1(mergeItem(accountPurposes.getP1(), defaultPurposes.getP1())) .p2(mergeItem(accountPurposes.getP2(), defaultPurposes.getP2())) .p3(mergeItem(accountPurposes.getP3(), defaultPurposes.getP3())) @@ -306,22 +366,24 @@ private Purposes mergeAccountPurposes(AccountGdprConfig accountGdprConfig) { .p8(mergeItem(accountPurposes.getP8(), defaultPurposes.getP8())) .p9(mergeItem(accountPurposes.getP9(), defaultPurposes.getP9())) .p10(mergeItem(accountPurposes.getP10(), defaultPurposes.getP10())) - .build(); + .build() + : defaultPurposes; } private SpecialFeatures mergeAccountSpecialFeatures(AccountGdprConfig accountGdprConfig) { - if (accountGdprConfig == null || accountGdprConfig.getSpecialFeatures() == null) { - return defaultSpecialFeatures; - } + final SpecialFeatures accountSpecialFeatures = accountGdprConfig != null + ? accountGdprConfig.getSpecialFeatures() + : null; - final SpecialFeatures accountSpecialFeatures = accountGdprConfig.getSpecialFeatures(); - return SpecialFeatures.builder() + return accountSpecialFeatures != null + ? SpecialFeatures.builder() .sf1(mergeItem(accountSpecialFeatures.getSf1(), defaultSpecialFeatures.getSf1())) .sf2(mergeItem(accountSpecialFeatures.getSf2(), defaultSpecialFeatures.getSf2())) - .build(); + .build() + : defaultSpecialFeatures; } - private Purpose findPurposeByTcfPurpose(PurposeCode tcfPurpose, Purposes purposes) { + private static Purpose findPurposeByTcfPurpose(PurposeCode tcfPurpose, Purposes purposes) { return switch (tcfPurpose) { case ONE -> purposes.getP1(); case TWO -> purposes.getP2(); @@ -337,7 +399,7 @@ private Purpose findPurposeByTcfPurpose(PurposeCode tcfPurpose, Purposes purpose }; } - private SpecialFeature findSpecialFeatureById(int specialFeatureId, SpecialFeatures specialFeatures) { + private static SpecialFeature findSpecialFeatureById(int specialFeatureId, SpecialFeatures specialFeatures) { return switch (specialFeatureId) { case 1 -> specialFeatures.getSf1(); case 2 -> specialFeatures.getSf2(); @@ -348,11 +410,9 @@ private SpecialFeature findSpecialFeatureById(int specialFeatureId, SpecialFeatu private PurposeOneTreatmentInterpretation mergePurposeOneTreatmentInterpretation( AccountGdprConfig accountGdprConfig) { - if (accountGdprConfig == null || accountGdprConfig.getPurposeOneTreatmentInterpretation() == null) { - return purposeOneTreatmentInterpretation; - } - - return mergeItem(accountGdprConfig.getPurposeOneTreatmentInterpretation(), purposeOneTreatmentInterpretation); + return accountGdprConfig != null + ? mergeItem(accountGdprConfig.getPurposeOneTreatmentInterpretation(), purposeOneTreatmentInterpretation) + : purposeOneTreatmentInterpretation; } private static T mergeItem(T prioritisedItem, T item) { @@ -365,10 +425,5 @@ private static class VendorPermissionsByType { Collection weakPermissions; Collection standardPermissions; - - public Collection joinPermissions() { - return Stream.concat(weakPermissions.stream(), standardPermissions.stream()) - .toList(); - } } } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/model/PrivacyEnforcementAction.java b/src/main/java/org/prebid/server/privacy/gdpr/model/PrivacyEnforcementAction.java index 5c5b7e85e29..7785ca48a4a 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/model/PrivacyEnforcementAction.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/model/PrivacyEnforcementAction.java @@ -3,11 +3,16 @@ import lombok.Builder; import lombok.Data; +import java.util.Collections; +import java.util.Set; + @Builder(toBuilder = true) @Data public class PrivacyEnforcementAction { - boolean removeUserIds; // user.buyeruid, user.id, user.eids + boolean removeUserFpd; // user.id, user.buyeruid, user.yob, user.gender, user.keywords, user.kwarray, user.data, user.ext.data + + boolean removeUserIds; // user.eids boolean maskGeo; // user.geo, device.geo @@ -21,8 +26,11 @@ public class PrivacyEnforcementAction { boolean blockPixelSync; + Set eidExceptions; + public static PrivacyEnforcementAction restrictAll() { return PrivacyEnforcementAction.builder() + .removeUserFpd(true) .removeUserIds(true) .maskGeo(true) .maskDeviceIp(true) @@ -30,10 +38,11 @@ public static PrivacyEnforcementAction restrictAll() { .blockAnalyticsReport(true) .blockBidderRequest(true) .blockPixelSync(true) + .eidExceptions(Collections.emptySet()) .build(); } public static PrivacyEnforcementAction allowAll() { - return PrivacyEnforcementAction.builder().build(); + return PrivacyEnforcementAction.builder().eidExceptions(Collections.emptySet()).build(); } } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/model/VendorPermission.java b/src/main/java/org/prebid/server/privacy/gdpr/model/VendorPermission.java index fbc7c771aff..3686d2128b3 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/model/VendorPermission.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/model/VendorPermission.java @@ -1,6 +1,10 @@ package org.prebid.server.privacy.gdpr.model; import lombok.Value; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; + +import java.util.EnumSet; +import java.util.Set; @Value(staticConstructor = "of") public class VendorPermission { @@ -9,6 +13,18 @@ public class VendorPermission { String bidderName; + Set consentedPurposes = EnumSet.noneOf(PurposeCode.class); + + Set naturallyConsentedPurposes = EnumSet.noneOf(PurposeCode.class); + PrivacyEnforcementAction privacyEnforcementAction; + + public void consentWith(PurposeCode purposeCode) { + consentedPurposes.add(purposeCode); + } + + public void consentNaturallyWith(PurposeCode purposeCode) { + naturallyConsentedPurposes.add(purposeCode); + } } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01Strategy.java similarity index 75% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01Strategy.java index a4eb920e776..d09d864ac3f 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01Strategy.java @@ -6,11 +6,11 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeOneStrategy extends PurposeStrategy { +public class Purpose01Strategy extends PurposeStrategy { - public PurposeOneStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose01Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02Strategy.java similarity index 73% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02Strategy.java index 0268713310e..e85d912f3b8 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02Strategy.java @@ -6,11 +6,11 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeTwoStrategy extends PurposeStrategy { +public class Purpose02Strategy extends PurposeStrategy { - public PurposeTwoStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose02Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @@ -23,7 +23,6 @@ public void allow(PrivacyEnforcementAction privacyEnforcementAction) { @Override public void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03Strategy.java similarity index 71% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03Strategy.java index 324b3e4cf92..4f93951e49c 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03Strategy.java @@ -6,11 +6,11 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeThreeStrategy extends PurposeStrategy { +public class Purpose03Strategy extends PurposeStrategy { - public PurposeThreeStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose03Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @@ -22,7 +22,6 @@ public void allow(PrivacyEnforcementAction privacyEnforcementAction) { @Override public void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04Strategy.java similarity index 72% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04Strategy.java index 6f5c5036f40..b6e14d87a79 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04Strategy.java @@ -6,23 +6,26 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeFourStrategy extends PurposeStrategy { +public class Purpose04Strategy extends PurposeStrategy { - public PurposeFourStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose04Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Override public void allow(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserFpd(false); + privacyEnforcementAction.setMaskDeviceInfo(false); + + privacyEnforcementAction.setRemoveUserIds(false); } @Override public void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05Strategy.java similarity index 73% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05Strategy.java index 89266044c1b..f4258c88068 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05Strategy.java @@ -6,11 +6,11 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeFiveStrategy extends PurposeStrategy { +public class Purpose05Strategy extends PurposeStrategy { - public PurposeFiveStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose05Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @@ -22,7 +22,6 @@ public void allow(PrivacyEnforcementAction privacyEnforcementAction) { @Override public void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06Strategy.java similarity index 72% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06Strategy.java index de332a75dd3..e79bc1b0893 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06Strategy.java @@ -6,11 +6,11 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeSixStrategy extends PurposeStrategy { +public class Purpose06Strategy extends PurposeStrategy { - public PurposeSixStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose06Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @@ -22,7 +22,6 @@ public void allow(PrivacyEnforcementAction privacyEnforcementAction) { @Override public void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07Strategy.java similarity index 73% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07Strategy.java index c28c63f6311..0cd4b1784cb 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07Strategy.java @@ -6,11 +6,11 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeSevenStrategy extends PurposeStrategy { +public class Purpose07Strategy extends PurposeStrategy { - public PurposeSevenStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose07Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @@ -23,7 +23,6 @@ public void allow(PrivacyEnforcementAction privacyEnforcementAction) { @Override public void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08Strategy.java similarity index 71% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08Strategy.java index 416acfe78c8..1c101aa753b 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08Strategy.java @@ -6,11 +6,11 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeEightStrategy extends PurposeStrategy { +public class Purpose08Strategy extends PurposeStrategy { - public PurposeEightStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose08Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @@ -22,7 +22,6 @@ public void allow(PrivacyEnforcementAction privacyEnforcementAction) { @Override public void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09Strategy.java similarity index 73% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09Strategy.java index fc8922112b5..7e77e9dc24e 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09Strategy.java @@ -6,11 +6,11 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeNineStrategy extends PurposeStrategy { +public class Purpose09Strategy extends PurposeStrategy { - public PurposeNineStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose09Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @@ -22,7 +22,6 @@ public void allow(PrivacyEnforcementAction privacyEnforcementAction) { @Override public void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10Strategy.java similarity index 72% rename from src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategy.java rename to src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10Strategy.java index 834f5de800b..06a42c090fa 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10Strategy.java @@ -6,11 +6,11 @@ import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -public class PurposeTenStrategy extends PurposeStrategy { +public class Purpose10Strategy extends PurposeStrategy { - public PurposeTenStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + public Purpose10Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @@ -22,7 +22,6 @@ public void allow(PrivacyEnforcementAction privacyEnforcementAction) { @Override public void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction) { privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeStrategy.java index 1ffb98d3f42..322f7c4fc7a 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeStrategy.java @@ -16,7 +16,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Objects; +import java.util.stream.Stream; public abstract class PurposeStrategy { @@ -38,77 +38,75 @@ public PurposeStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, /** * This method is allow permission for purpose when account and server config was used. */ - public abstract void allow(PrivacyEnforcementAction privacyEnforcementAction); + protected abstract void allow(PrivacyEnforcementAction privacyEnforcementAction); + + public void allow(VendorPermission vendorPermission) { + vendorPermission.consentWith(getPurpose()); + allow(vendorPermission.getPrivacyEnforcementAction()); + } /** * This method represents allowance of permission that purpose should provide after full enforcement * (can downgrade to basic if GVL failed) despite of host company or account configuration. */ - public abstract void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction); + protected abstract void allowNaturally(PrivacyEnforcementAction privacyEnforcementAction); - public Collection processTypePurposeStrategy( - TCString vendorConsent, - Purpose purpose, - Collection vendorPermissions, - boolean wasDowngraded) { + public void allowNaturally(VendorPermission vendorPermission) { + vendorPermission.consentNaturallyWith(getPurpose()); + allowNaturally(vendorPermission.getPrivacyEnforcementAction()); + } + + public void processTypePurposeStrategy(TCString vendorConsent, + Purpose purpose, + Collection vendorPermissions, + boolean wasDowngraded) { final Collection excludedVendors = excludedVendors(vendorPermissions, purpose); final Collection vendorForPurpose = vendorPermissions.stream() .filter(vendorPermission -> !excludedVendors.contains(vendorPermission)) .toList(); - allowedByTypeStrategy(vendorConsent, purpose, vendorForPurpose, excludedVendors).stream() - .map(VendorPermission::getPrivacyEnforcementAction) + allowedByTypeStrategy(vendorConsent, purpose, vendorForPurpose, excludedVendors) .forEach(this::allow); - final Collection naturalVendorPermission = wasDowngraded + final Stream naturalVendorPermission = wasDowngraded ? allowedByBasicTypeStrategy(vendorConsent, true, vendorForPurpose, excludedVendors) : allowedByFullTypeStrategy(vendorConsent, true, vendorForPurpose, excludedVendors); - naturalVendorPermission.stream() - .map(VendorPermission::getPrivacyEnforcementAction) - .forEach(this::allowNaturally); + naturalVendorPermission.forEach(this::allowNaturally); + } - return vendorPermissions.stream() - .map(VendorPermissionWithGvl::getVendorPermission) - .toList(); + private Collection excludedVendors(Collection vendorPermissions, + Purpose purpose) { + + final List bidderNameExceptions = purpose.getVendorExceptions(); + + return CollectionUtils.isEmpty(bidderNameExceptions) + ? Collections.emptyList() + : CollectionUtils.select(vendorPermissions, vendorPermission -> + bidderNameExceptions.contains(vendorPermission.getVendorPermission().getBidderName())); } - private Collection allowedByTypeStrategy(TCString vendorConsent, - Purpose purpose, - Collection vendorForPurpose, - Collection excludedVendors) { + private Stream allowedByTypeStrategy(TCString vendorConsent, + Purpose purpose, + Collection vendorForPurpose, + Collection excludedVendors) { + final boolean isEnforceVendors = BooleanUtils.isNotFalse(purpose.getEnforceVendors()); final EnforcePurpose purposeType = purpose.getEnforcePurpose(); - if (Objects.equals(purposeType, EnforcePurpose.basic)) { - return allowedByBasicTypeStrategy(vendorConsent, isEnforceVendors, vendorForPurpose, excludedVendors); - } - - if (Objects.equals(purposeType, EnforcePurpose.no)) { + if (purposeType == EnforcePurpose.no) { return allowedByNoTypeStrategy(vendorConsent, isEnforceVendors, vendorForPurpose, excludedVendors); } - // Full by default - if (purposeType == null || purposeType.equals(EnforcePurpose.full)) { - return allowedByFullTypeStrategy(vendorConsent, isEnforceVendors, vendorForPurpose, excludedVendors); + if (purposeType == EnforcePurpose.basic) { + return allowedByBasicTypeStrategy(vendorConsent, isEnforceVendors, vendorForPurpose, excludedVendors); } - throw new IllegalArgumentException("Invalid type strategy provided. no/base/full != " + purposeType); - } - - protected Collection excludedVendors(Collection vendorPermissions, - Purpose purpose) { - - final List bidderNameExceptions = purpose.getVendorExceptions(); - - return CollectionUtils.isEmpty(bidderNameExceptions) - ? Collections.emptyList() - : CollectionUtils.select(vendorPermissions, vendorPermission -> - bidderNameExceptions.contains(vendorPermission.getVendorPermission().getBidderName())); + return allowedByFullTypeStrategy(vendorConsent, isEnforceVendors, vendorForPurpose, excludedVendors); } - protected Collection allowedByBasicTypeStrategy( + private Stream allowedByBasicTypeStrategy( TCString vendorConsent, boolean isEnforceVendors, Collection vendorForPurpose, @@ -118,7 +116,7 @@ protected Collection allowedByBasicTypeStrategy( getPurpose(), vendorConsent, vendorForPurpose, excludedVendors, isEnforceVendors); } - protected Collection allowedByNoTypeStrategy( + private Stream allowedByNoTypeStrategy( TCString vendorConsent, boolean isEnforceVendors, Collection vendorForPurpose, @@ -128,7 +126,7 @@ protected Collection allowedByNoTypeStrategy( getPurpose(), vendorConsent, vendorForPurpose, excludedVendors, isEnforceVendors); } - protected Collection allowedByFullTypeStrategy( + private Stream allowedByFullTypeStrategy( TCString vendorConsent, boolean isEnforceVendors, Collection vendorForPurpose, diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/BasicEnforcePurposeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/BasicEnforcePurposeStrategy.java index 2e746c06a20..8324d606e79 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/BasicEnforcePurposeStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/BasicEnforcePurposeStrategy.java @@ -3,33 +3,30 @@ import com.iabtcf.decoder.TCString; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; -import org.apache.commons.collections4.CollectionUtils; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; import java.util.Collection; -import java.util.List; +import java.util.stream.Stream; public class BasicEnforcePurposeStrategy extends EnforcePurposeStrategy { private static final Logger logger = LoggerFactory.getLogger(BasicEnforcePurposeStrategy.class); - public Collection allowedByTypeStrategy(PurposeCode purpose, - TCString vendorConsent, - Collection vendorsForPurpose, - Collection excludedVendors, - boolean isEnforceVendors) { + public Stream allowedByTypeStrategy(PurposeCode purpose, + TCString vendorConsent, + Collection vendorsForPurpose, + Collection excludedVendors, + boolean isEnforceVendors) { logger.debug("Basic strategy used for purpose {0}", purpose); - final List allowedVendorPermissions = vendorsForPurpose.stream() - .map(VendorPermissionWithGvl::getVendorPermission) + final Stream allowedVendorPermissions = toVendorPermissions(vendorsForPurpose) .filter(vendorPermission -> vendorPermission.getVendorId() != null) .filter(vendorPermission -> isAllowedBySimpleConsent( - purpose, vendorPermission.getVendorId(), isEnforceVendors, vendorConsent)) - .toList(); + purpose, vendorPermission.getVendorId(), isEnforceVendors, vendorConsent)); - return CollectionUtils.union(allowedVendorPermissions, toVendorPermissions(excludedVendors)); + return Stream.concat(allowedVendorPermissions, toVendorPermissions(excludedVendors)); } } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/EnforcePurposeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/EnforcePurposeStrategy.java index 6a076cd4dce..ffb1e355276 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/EnforcePurposeStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/EnforcePurposeStrategy.java @@ -7,10 +7,11 @@ import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; import java.util.Collection; +import java.util.stream.Stream; public abstract class EnforcePurposeStrategy { - public abstract Collection allowedByTypeStrategy( + public abstract Stream allowedByTypeStrategy( PurposeCode purpose, TCString vendorConsent, Collection vendorsForPurpose, @@ -59,11 +60,7 @@ private boolean isAllowedByConsents(PurposeCode purpose, return isPurposeAllowed && isVendorAllowed; } - protected static Collection toVendorPermissions( - Collection vendorPermissionWithGvls) { - - return vendorPermissionWithGvls.stream() - .map(VendorPermissionWithGvl::getVendorPermission) - .toList(); + protected static Stream toVendorPermissions(Collection permissions) { + return permissions.stream().map(VendorPermissionWithGvl::getVendorPermission); } } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/FullEnforcePurposeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/FullEnforcePurposeStrategy.java index 8254b07c163..6efa37ea524 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/FullEnforcePurposeStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/FullEnforcePurposeStrategy.java @@ -1,9 +1,9 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies; import com.iabtcf.decoder.TCString; -import com.iabtcf.v2.PublisherRestriction; import com.iabtcf.v2.RestrictionType; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.map.DefaultedMap; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; @@ -11,84 +11,68 @@ import java.util.Collection; import java.util.EnumSet; -import java.util.List; +import java.util.HashMap; import java.util.Map; -import java.util.function.Function; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.StreamSupport; +import java.util.stream.Stream; public class FullEnforcePurposeStrategy extends EnforcePurposeStrategy { - public Collection allowedByTypeStrategy(PurposeCode purpose, - TCString vendorConsent, - Collection vendorsForPurpose, - Collection excludedVendors, - boolean isEnforceVendors) { + public Stream allowedByTypeStrategy(PurposeCode purpose, + TCString vendorConsent, + Collection vendorsForPurpose, + Collection excludedVendors, + boolean isEnforceVendors) { - final List publisherRestrictions = vendorConsent.getPublisherRestrictions().stream() - .filter(publisherRestriction -> publisherRestriction.getPurposeId() == purpose.code()) - .toList(); - - final List allowedExcluded = allowedExcludedVendorPermission(excludedVendors, - publisherRestrictions); + final Map vendorToRestriction = vendorToRestriction( + purpose, vendorConsent, vendorsForPurpose, excludedVendors); - final Map vendorPermissionToRestriction = mapVendorPermission( - vendorsForPurpose, publisherRestrictions); + final Stream allowedExcluded = toVendorPermissions(excludedVendors) + .filter(vendorPermission -> isNotRestricted(vendorPermission, vendorToRestriction)); - final List allowedVendorPermissions = vendorPermissionToRestriction.entrySet().stream() - .filter(permissionAndRestriction -> - isAllowedByPublisherRestrictionAndFlexible(purpose, isEnforceVendors, - permissionAndRestriction.getKey(), vendorConsent, permissionAndRestriction.getValue())) - .map(Map.Entry::getKey) - .map(VendorPermissionWithGvl::getVendorPermission) - .toList(); - - return CollectionUtils.union(allowedExcluded, allowedVendorPermissions); - } + final Stream allowedVendorPermissions = vendorsForPurpose.stream() + .filter(vendorPermissionWithGvl -> isAllowedByPublisherRestrictionAndFlexible( + purpose, + isEnforceVendors, + vendorPermissionWithGvl, + vendorConsent, + vendorToRestriction.get(vendorPermissionWithGvl.getVendorPermission().getVendorId()))) + .map(VendorPermissionWithGvl::getVendorPermission); - private List allowedExcludedVendorPermission( - Collection excludedVendors, - Collection publisherRestrictions) { - - final List notAllowedVendorIds = publisherRestrictions.stream() - .filter(publisherRestriction -> publisherRestriction.getRestrictionType() - .equals(RestrictionType.NOT_ALLOWED)) - .map(PublisherRestriction::getVendorIds) - .flatMap(vendorIds -> StreamSupport.stream(vendorIds.spliterator(), false)) - .toList(); - - return excludedVendors.stream() - .map(VendorPermissionWithGvl::getVendorPermission) - .filter(vendorPermissionWithGvl -> isNotRestricted(notAllowedVendorIds, vendorPermissionWithGvl)) - .toList(); - } - - private boolean isNotRestricted(List notAllowedVendorIds, VendorPermission vendorPermission) { - final Integer vendorId = vendorPermission.getVendorId(); - return vendorId == null || !notAllowedVendorIds.contains(vendorId); + return Stream.concat(allowedExcluded, allowedVendorPermissions); } - private Map mapVendorPermission( + private static Map vendorToRestriction( + PurposeCode purpose, + TCString vendorConsent, Collection vendorsForPurpose, - Collection publisherRestrictions) { + Collection excludedVendors) { - return vendorsForPurpose.stream() - .collect(Collectors.toMap( - Function.identity(), - vendorPermissionWithGvl -> restrictionType(vendorPermissionWithGvl, publisherRestrictions))); + final Set participatingVendorsIds = + Stream.concat(vendorsForPurpose.stream(), excludedVendors.stream()) + .map(VendorPermissionWithGvl::getVendorPermission) + .map(VendorPermission::getVendorId) + .collect(Collectors.toSet()); + + final Map publisherRestrictions = new HashMap<>(); + vendorConsent.getPublisherRestrictions().stream() + .filter(publisherRestriction -> publisherRestriction.getPurposeId() == purpose.code()) + .forEach(publisherRestriction -> publisherRestriction.getVendorIds().toStream() + .filter(participatingVendorsIds::contains) + .forEach(vendorId -> publisherRestrictions.merge( + vendorId, + publisherRestriction.getRestrictionType(), + (first, second) -> second == RestrictionType.NOT_ALLOWED ? second : first))); + + return DefaultedMap.defaultedMap(publisherRestrictions, RestrictionType.UNDEFINED); } - private RestrictionType restrictionType(VendorPermissionWithGvl vendorPermissionWithGvl, - Collection publisherRestrictions) { + private boolean isNotRestricted(VendorPermission vendorPermission, + Map vendorToRestriction) { - final VendorPermission vendorPermission = vendorPermissionWithGvl.getVendorPermission(); final Integer vendorId = vendorPermission.getVendorId(); - - return publisherRestrictions.stream() - .filter(publisherRestriction -> publisherRestriction.getVendorIds().contains(vendorId)) - .map(PublisherRestriction::getRestrictionType) - .findFirst() - .orElse(RestrictionType.UNDEFINED); + return vendorId == null || vendorToRestriction.get(vendorId) != RestrictionType.NOT_ALLOWED; } /** @@ -112,7 +96,7 @@ private boolean isAllowedByPublisherRestrictionAndFlexible(PurposeCode purpose, TCString tcString, RestrictionType restrictionType) { - if (restrictionType.equals(RestrictionType.NOT_ALLOWED)) { + if (restrictionType == RestrictionType.NOT_ALLOWED) { return false; } @@ -133,8 +117,8 @@ private boolean isAllowedByPublisherRestrictionAndFlexible(PurposeCode purpose, if (legIntGvlPurposeCodes != null && legIntGvlPurposeCodes.contains(purpose)) { return isFlexible ? isAllowedByFlexible(purpose, vendorId, isEnforceVendor, tcString, restrictionType) - : isAllowedByNotFlexibleLegitimateInterest(purpose, vendorId, isEnforceVendor, tcString, - restrictionType); + : isAllowedByNotFlexibleLegitimateInterest( + purpose, vendorId, isEnforceVendor, tcString, restrictionType); } return false; @@ -145,10 +129,9 @@ private boolean isAllowedByNotFlexiblePurpose(PurposeCode purpose, boolean isEnforceVendor, TCString tcString, RestrictionType restrictionType) { - final boolean isSupportedRestriction = restrictionType.equals(RestrictionType.REQUIRE_CONSENT) - || restrictionType.equals(RestrictionType.UNDEFINED); - return isSupportedRestriction && isAllowedBySimpleConsent(purpose, vendorId, isEnforceVendor, tcString); + return (restrictionType == RestrictionType.REQUIRE_CONSENT || restrictionType == RestrictionType.UNDEFINED) + && isAllowedBySimpleConsent(purpose, vendorId, isEnforceVendor, tcString); } private boolean isAllowedByNotFlexibleLegitimateInterest(PurposeCode purpose, @@ -156,6 +139,7 @@ private boolean isAllowedByNotFlexibleLegitimateInterest(PurposeCode purpose, boolean isEnforceVendor, TCString tcString, RestrictionType restrictionType) { + final boolean isSupportedRestriction = restrictionType.equals(RestrictionType.REQUIRE_LEGITIMATE_INTEREST) || restrictionType.equals(RestrictionType.UNDEFINED); @@ -178,4 +162,3 @@ private boolean isAllowedByFlexible(PurposeCode purpose, }; } } - diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/NoEnforcePurposeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/NoEnforcePurposeStrategy.java index b184145fa80..5b14f9a8a7e 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/NoEnforcePurposeStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/NoEnforcePurposeStrategy.java @@ -2,33 +2,30 @@ import com.iabtcf.decoder.TCString; import com.iabtcf.utils.IntIterable; -import org.apache.commons.collections4.CollectionUtils; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; import java.util.Collection; -import java.util.List; +import java.util.stream.Stream; public class NoEnforcePurposeStrategy extends EnforcePurposeStrategy { - public Collection allowedByTypeStrategy(PurposeCode purpose, - TCString tcString, - Collection vendorsForPurpose, - Collection excludedVendors, - boolean isEnforceVendors) { + public Stream allowedByTypeStrategy(PurposeCode purpose, + TCString tcString, + Collection vendorsForPurpose, + Collection excludedVendors, + boolean isEnforceVendors) { final IntIterable vendorConsent = tcString.getVendorConsent(); final IntIterable vendorLIConsent = tcString.getVendorLegitimateInterest(); - final List allowedVendorPermissions = vendorsForPurpose.stream() - .map(VendorPermissionWithGvl::getVendorPermission) + final Stream allowedVendorPermissions = toVendorPermissions(vendorsForPurpose) .filter(vendorPermission -> vendorPermission.getVendorId() != null) - .filter(vendorPermission -> isAllowedByVendorConsent(vendorPermission.getVendorId(), isEnforceVendors, - vendorConsent, vendorLIConsent)) - .toList(); + .filter(vendorPermission -> isAllowedByVendorConsent( + vendorPermission.getVendorId(), isEnforceVendors, vendorConsent, vendorLIConsent)); - return CollectionUtils.union(allowedVendorPermissions, toVendorPermissions(excludedVendors)); + return Stream.concat(allowedVendorPermissions, toVendorPermissions(excludedVendors)); } private boolean isAllowedByVendorConsent(Integer vendorId, diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/PurposeTwoBasicEnforcePurposeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/PurposeTwoBasicEnforcePurposeStrategy.java index b34e99fb1a7..e7878be3d29 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/PurposeTwoBasicEnforcePurposeStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/PurposeTwoBasicEnforcePurposeStrategy.java @@ -1,48 +1,17 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies; import com.iabtcf.decoder.TCString; -import com.iabtcf.utils.IntIterable; -import io.vertx.core.logging.Logger; -import io.vertx.core.logging.LoggerFactory; -import org.apache.commons.collections4.CollectionUtils; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import java.util.Collection; -import java.util.List; - public class PurposeTwoBasicEnforcePurposeStrategy extends BasicEnforcePurposeStrategy { - private static final Logger logger = LoggerFactory.getLogger(PurposeTwoBasicEnforcePurposeStrategy.class); - - public Collection allowedByTypeStrategy(PurposeCode purpose, - TCString vendorConsent, - Collection vendorsForPurpose, - Collection excludedVendors, - boolean isEnforceVendors) { - - logger.debug("Basic strategy used fo purpose {0}", purpose); + @Override + protected boolean isAllowedBySimpleConsent(PurposeCode purpose, + Integer vendorId, + boolean isEnforceVendor, + TCString tcString) { - final List allowedVendorPermissions = vendorsForPurpose.stream() - .map(VendorPermissionWithGvl::getVendorPermission) - .filter(vendorPermission -> vendorPermission.getVendorId() != null) - .filter(vendorPermission -> isAllowedBySimpleConsentOrPurposeLI(purpose, - vendorPermission.getVendorId(), isEnforceVendors, vendorConsent)) - .toList(); - - return CollectionUtils.union(allowedVendorPermissions, toVendorPermissions(excludedVendors)); + return tcString.getPurposesLITransparency().contains(purpose.code()) + || super.isAllowedBySimpleConsent(purpose, vendorId, isEnforceVendor, tcString); } - - private boolean isAllowedBySimpleConsentOrPurposeLI(PurposeCode purpose, - Integer vendorId, - boolean isEnforceVendor, - TCString tcString) { - - final IntIterable purposesLIConsent = tcString.getPurposesLITransparency(); - - return isAllowedBySimpleConsent(purpose, vendorId, isEnforceVendor, tcString) - || purposesLIConsent.contains(purpose.code()); - } - } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesStrategy.java index 363138545d3..5ac5494b67a 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesStrategy.java @@ -1,7 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.specialfeature; import com.iabtcf.decoder.TCString; -import com.iabtcf.utils.IntIterable; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; @@ -9,8 +8,8 @@ import org.prebid.server.settings.model.SpecialFeature; import java.util.Collection; -import java.util.Collections; import java.util.List; +import java.util.stream.Stream; public abstract class SpecialFeaturesStrategy { @@ -18,40 +17,40 @@ public abstract class SpecialFeaturesStrategy { public abstract void allow(PrivacyEnforcementAction privacyEnforcementAction); - public Collection processSpecialFeaturesStrategy(TCString vendorConsent, - SpecialFeature specialFeature, - Collection vendorPermissions) { - // Default True - if (BooleanUtils.isFalse(specialFeature.getEnforce())) { - return allowFor(vendorPermissions); - } + public void processSpecialFeaturesStrategy(TCString vendorConsent, + SpecialFeature specialFeature, + Collection vendorPermissions) { - final IntIterable specialFeatureOptIns = vendorConsent.getSpecialFeatureOptIns(); - final boolean isSpecialFeatureIsOptIn = specialFeatureOptIns.contains(getSpecialFeatureId()); + if (isOptIn(specialFeature, vendorConsent)) { + allowFor(vendorPermissions); + } else { + allowOnlyExcluded(vendorPermissions, specialFeature); + } + } - return isSpecialFeatureIsOptIn - ? allowFor(vendorPermissions) - : allowOnlyExcluded(vendorPermissions, specialFeature); + private boolean isOptIn(SpecialFeature specialFeature, TCString vendorConsent) { + return BooleanUtils.isFalse(specialFeature.getEnforce()) + || vendorConsent.getSpecialFeatureOptIns().contains(getSpecialFeatureId()); } - private Collection allowFor(Collection vendorPermissions) { + private void allowFor(Collection vendorPermissions) { vendorPermissions.forEach(vendorPermission -> allow(vendorPermission.getPrivacyEnforcementAction())); - return vendorPermissions; } - private Collection allowOnlyExcluded(Collection vendorPermissions, - SpecialFeature specialFeature) { + private void allowOnlyExcluded(Collection vendorPermissions, SpecialFeature specialFeature) { excludedVendors(vendorPermissions, specialFeature) - .forEach(vendorPermission -> allow(vendorPermission.getPrivacyEnforcementAction())); - return vendorPermissions; + .map(VendorPermission::getPrivacyEnforcementAction) + .forEach(this::allow); } - protected Collection excludedVendors(Collection vendorPermissions, - SpecialFeature specialFeature) { + private Stream excludedVendors(Collection vendorPermissions, + SpecialFeature specialFeature) { + final List bidderNameExceptions = specialFeature.getVendorExceptions(); + return CollectionUtils.isEmpty(bidderNameExceptions) - ? Collections.emptyList() - : CollectionUtils.select(vendorPermissions, vendorPermission -> - bidderNameExceptions.contains(vendorPermission.getBidderName())); + ? Stream.empty() + : vendorPermissions.stream() + .filter(vendorPermission -> bidderNameExceptions.contains(vendorPermission.getBidderName())); } } diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUser.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUser.java index 1f80d23bacd..900a355a9fb 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUser.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUser.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Eid; import lombok.Builder; @@ -54,11 +53,6 @@ public class ExtUser extends FlexibleExtension { */ ObjectNode data; - /** - * Defines the contract for bidrequest.user.ext.digitrust - */ - JsonNode digitrust; - /** * Defines the contract for bidrequest.user.ext.ConsentedProvidersSettings */ diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/minutemedia/ExtImpMinuteMedia.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/minutemedia/ExtImpMinuteMedia.java new file mode 100644 index 00000000000..b7d122b1264 --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/minutemedia/ExtImpMinuteMedia.java @@ -0,0 +1,9 @@ +package org.prebid.server.proto.openrtb.ext.request.minutemedia; + +import lombok.Value; + +@Value(staticConstructor = "of") +public class ExtImpMinuteMedia { + + String org; +} diff --git a/src/main/java/org/prebid/server/settings/EnrichingApplicationSettings.java b/src/main/java/org/prebid/server/settings/EnrichingApplicationSettings.java index a832f27d6a4..403583ad70f 100644 --- a/src/main/java/org/prebid/server/settings/EnrichingApplicationSettings.java +++ b/src/main/java/org/prebid/server/settings/EnrichingApplicationSettings.java @@ -103,13 +103,10 @@ private Account validateAndModifyAccount(Account account) { final AccountPrivacyConfig accountPrivacyConfig = account.getPrivacy(); return account.toBuilder() - .privacy(AccountPrivacyConfig.of( - accountPrivacyConfig.getGdpr(), - accountPrivacyConfig.getCcpa(), - accountPrivacyConfig.getDsa(), - AccountActivitiesConfigurationUtils - .removeInvalidRules(accountPrivacyConfig.getActivities()), - accountPrivacyConfig.getModules())) + .privacy(accountPrivacyConfig.toBuilder() + .activities(AccountActivitiesConfigurationUtils + .removeInvalidRules(accountPrivacyConfig.getActivities())) + .build()) .build(); } diff --git a/src/main/java/org/prebid/server/settings/model/AccountPrivacyConfig.java b/src/main/java/org/prebid/server/settings/model/AccountPrivacyConfig.java index 4542114aa95..218b84a0550 100644 --- a/src/main/java/org/prebid/server/settings/model/AccountPrivacyConfig.java +++ b/src/main/java/org/prebid/server/settings/model/AccountPrivacyConfig.java @@ -1,6 +1,7 @@ package org.prebid.server.settings.model; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; import lombok.Value; import org.prebid.server.activity.Activity; import org.prebid.server.settings.model.activity.AccountActivityConfiguration; @@ -9,7 +10,8 @@ import java.util.List; import java.util.Map; -@Value(staticConstructor = "of") +@Builder(toBuilder = true) +@Value public class AccountPrivacyConfig { AccountGdprConfig gdpr; diff --git a/src/main/java/org/prebid/server/settings/model/Purpose.java b/src/main/java/org/prebid/server/settings/model/Purpose.java index 4c0ea493dab..4e67c79503c 100644 --- a/src/main/java/org/prebid/server/settings/model/Purpose.java +++ b/src/main/java/org/prebid/server/settings/model/Purpose.java @@ -20,4 +20,6 @@ public class Purpose { @JsonProperty("vendor-exceptions") List vendorExceptions; + + PurposeEid eid; } diff --git a/src/main/java/org/prebid/server/settings/model/PurposeEid.java b/src/main/java/org/prebid/server/settings/model/PurposeEid.java new file mode 100644 index 00000000000..812c2b352ea --- /dev/null +++ b/src/main/java/org/prebid/server/settings/model/PurposeEid.java @@ -0,0 +1,19 @@ +package org.prebid.server.settings.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; + +@Data +@NoArgsConstructor +@AllArgsConstructor(staticName = "of") +public class PurposeEid { + + Boolean activityTransition; + + boolean requireConsent; + + Set exceptions; +} diff --git a/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java b/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java index d54fdabb066..c36bc2a1b20 100644 --- a/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java @@ -14,6 +14,7 @@ import org.prebid.server.json.JacksonMapper; import org.prebid.server.json.JsonLogic; import org.prebid.server.metric.Metrics; +import org.prebid.server.settings.model.GdprConfig; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -87,9 +88,10 @@ ActivityRuleFactory activityRuleFactory(List> ruleCreators) { @Bean ActivityInfrastructureCreator activityInfrastructureCreator(ActivityRuleFactory activityRuleFactory, + GdprConfig gdprConfig, Metrics metrics, JacksonMapper jacksonMapper) { - return new ActivityInfrastructureCreator(activityRuleFactory, metrics, jacksonMapper); + return new ActivityInfrastructureCreator(activityRuleFactory, gdprConfig, metrics, jacksonMapper); } } diff --git a/src/main/java/org/prebid/server/spring/config/AnalyticsConfiguration.java b/src/main/java/org/prebid/server/spring/config/AnalyticsConfiguration.java index 24f6098092d..46b98597121 100644 --- a/src/main/java/org/prebid/server/spring/config/AnalyticsConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/AnalyticsConfiguration.java @@ -9,7 +9,8 @@ import org.prebid.server.analytics.reporter.log.LogAnalyticsReporter; import org.prebid.server.analytics.reporter.pubstack.PubstackAnalyticsReporter; import org.prebid.server.analytics.reporter.pubstack.model.PubstackAnalyticsProperties; -import org.prebid.server.auction.PrivacyEnforcementService; +import org.prebid.server.auction.privacy.enforcement.TcfEnforcement; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; import org.prebid.server.json.JacksonMapper; import org.prebid.server.metric.Metrics; import org.prebid.server.vertx.http.HttpClient; @@ -29,18 +30,20 @@ public class AnalyticsConfiguration { @Bean AnalyticsReporterDelegator analyticsReporterDelegator( - @Autowired(required = false) List delegates, Vertx vertx, - PrivacyEnforcementService privacyEnforcementService, + @Autowired(required = false) List delegates, + TcfEnforcement tcfEnforcement, + UserFpdActivityMask userFpdActivityMask, Metrics metrics, @Value("${logging.sampling-rate:0.01}") double logSamplingRate) { return new AnalyticsReporterDelegator( - logSamplingRate, - ListUtils.emptyIfNull(delegates), vertx, - privacyEnforcementService, - metrics); + ListUtils.emptyIfNull(delegates), + tcfEnforcement, + userFpdActivityMask, + metrics, + logSamplingRate); } @Bean diff --git a/src/main/java/org/prebid/server/spring/config/PrivacyServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/PrivacyServiceConfiguration.java index b55bfa325de..1a6a78a5c42 100644 --- a/src/main/java/org/prebid/server/spring/config/PrivacyServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/PrivacyServiceConfiguration.java @@ -4,6 +4,14 @@ import io.vertx.core.file.FileSystem; import lombok.Data; import org.prebid.server.auction.IpAddressHelper; +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.TcfEnforcement; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdCcpaMask; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdCoppaMask; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdTcfMask; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.geolocation.GeoLocationService; import org.prebid.server.json.JacksonMapper; @@ -11,17 +19,17 @@ import org.prebid.server.privacy.HostVendorTcfDefinerService; import org.prebid.server.privacy.gdpr.Tcf2Service; import org.prebid.server.privacy.gdpr.TcfDefinerService; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeEightStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeFiveStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeFourStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeNineStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeOneStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeSevenStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeSixStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose01Strategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose02Strategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose03Strategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose04Strategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose05Strategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose06Strategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose07Strategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose08Strategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose09Strategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose10Strategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeTenStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeThreeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.PurposeTwoStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; @@ -180,82 +188,112 @@ HostVendorTcfDefinerService hostVendorTcfDefinerService( } @Bean - PurposeOneStrategy purposeOneStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeOneStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, + Purpose01Strategy purpose01Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose01Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - PurposeTwoStrategy purposeTwoStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - PurposeTwoBasicEnforcePurposeStrategy purposeTwoBasicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeTwoStrategy(fullEnforcePurposeStrategy, purposeTwoBasicEnforcePurposeStrategy, + Purpose02Strategy purpose02Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + PurposeTwoBasicEnforcePurposeStrategy purposeTwoBasicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose02Strategy( + fullEnforcePurposeStrategy, + purposeTwoBasicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - PurposeThreeStrategy purposeThreeStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeThreeStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, + Purpose03Strategy purpose03Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose03Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - PurposeFourStrategy purposeFourStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeFourStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, + Purpose04Strategy purpose04Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose04Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - PurposeFiveStrategy purposeFiveStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeFiveStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, + Purpose05Strategy purpose05Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose05Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - PurposeSixStrategy purposeSixStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeSixStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, + Purpose06Strategy purpose06Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose06Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - PurposeSevenStrategy purposeSevenStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeSevenStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, + Purpose07Strategy purpose07Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose07Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - PurposeEightStrategy purposeEightStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeEightStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, + Purpose08Strategy purpose08Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose08Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - PurposeNineStrategy purposeNineStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeNineStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, + Purpose09Strategy purpose09Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose09Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - PurposeTenStrategy purposeTenStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, - BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, - NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - return new PurposeTenStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, + Purpose10Strategy purpose10Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, + NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + + return new Purpose10Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @@ -312,6 +350,55 @@ SpecialFeature specialFeature() { return new SpecialFeature(); } + @Bean + UserFpdActivityMask userFpdActivityMask(UserFpdTcfMask userFpdTcfMask) { + return new UserFpdActivityMask(userFpdTcfMask); + } + + @Bean + UserFpdCcpaMask userFpdCcpaMask(IpAddressHelper ipAddressHelper) { + return new UserFpdCcpaMask(ipAddressHelper); + } + + @Bean + UserFpdCoppaMask userFpdCoppaMask(IpAddressHelper ipAddressHelper) { + return new UserFpdCoppaMask(ipAddressHelper); + } + + @Bean + UserFpdTcfMask userFpdTcfMask(IpAddressHelper ipAddressHelper) { + return new UserFpdTcfMask(ipAddressHelper); + } + + @Bean + ActivityEnforcement activityEnforcement(UserFpdActivityMask userFpdActivityMask) { + return new ActivityEnforcement(userFpdActivityMask); + } + + @Bean + CcpaEnforcement ccpaEnforcement(UserFpdCcpaMask userFpdCcpaMask, + BidderCatalog bidderCatalog, + Metrics metrics, + @Value("${ccpa.enforce}") boolean ccpaEnforce) { + + return new CcpaEnforcement(userFpdCcpaMask, bidderCatalog, metrics, ccpaEnforce); + } + + @Bean + CoppaEnforcement coppaEnforcement(UserFpdCoppaMask userFpdCoppaMask, Metrics metrics) { + return new CoppaEnforcement(userFpdCoppaMask, metrics); + } + + @Bean + TcfEnforcement tcfEnforcement(TcfDefinerService tcfDefinerService, + UserFpdTcfMask userFpdTcfMask, + BidderCatalog bidderCatalog, + Metrics metrics, + @Value("${lmt.enforce}") boolean lmtEnforce) { + + return new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, lmtEnforce); + } + @Data @Validated public static class VendorListServiceConfigurationProperties { 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 c5bbb261f03..425396a667c 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -22,7 +22,6 @@ import org.prebid.server.auction.InterstitialProcessor; import org.prebid.server.auction.IpAddressHelper; import org.prebid.server.auction.OrtbTypesResolver; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.SecBrowsingTopicsResolver; import org.prebid.server.auction.StoredRequestProcessor; import org.prebid.server.auction.StoredResponseProcessor; @@ -48,7 +47,15 @@ import org.prebid.server.auction.mediatypeprocessor.CompositeMediaTypeProcessor; import org.prebid.server.auction.mediatypeprocessor.MediaTypeProcessor; import org.prebid.server.auction.mediatypeprocessor.MultiFormatMediaTypeProcessor; -import org.prebid.server.auction.privacycontextfactory.AmpPrivacyContextFactory; +import org.prebid.server.auction.privacy.contextfactory.AmpPrivacyContextFactory; +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.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; @@ -403,7 +410,7 @@ AuctionRequestFactory auctionRequestFactory( ImplicitParametersExtractor implicitParametersExtractor, Ortb2ImplicitParametersResolver ortb2ImplicitParametersResolver, OrtbTypesResolver ortbTypesResolver, - PrivacyEnforcementService privacyEnforcementService, + AuctionPrivacyContextFactory auctionPrivacyContextFactory, DebugResolver debugResolver, JacksonMapper mapper) { @@ -418,7 +425,7 @@ AuctionRequestFactory auctionRequestFactory( ortb2ImplicitParametersResolver, new InterstitialProcessor(), ortbTypesResolver, - privacyEnforcementService, + auctionPrivacyContextFactory, debugResolver, mapper); } @@ -476,7 +483,7 @@ VideoRequestFactory videoRequestFactory( VideoStoredRequestProcessor storedRequestProcessor, BidRequestOrtbVersionConversionManager bidRequestOrtbVersionConversionManager, Ortb2ImplicitParametersResolver ortb2ImplicitParametersResolver, - PrivacyEnforcementService privacyEnforcementService, + AuctionPrivacyContextFactory auctionPrivacyContextFactory, DebugResolver debugResolver, JacksonMapper mapper) { @@ -488,7 +495,7 @@ VideoRequestFactory videoRequestFactory( storedRequestProcessor, bidRequestOrtbVersionConversionManager, ortb2ImplicitParametersResolver, - privacyEnforcementService, + auctionPrivacyContextFactory, debugResolver, mapper); } @@ -672,7 +679,7 @@ CookieSyncService cookieSyncService( @Value("${cookie-sync.max-limit:#{null}}") Integer maxLimit, BidderCatalog bidderCatalog, HostVendorTcfDefinerService hostVendorTcfDefinerService, - PrivacyEnforcementService privacyEnforcementService, + CcpaEnforcement ccpaEnforcement, UidsCookieService uidsCookieService, CoopSyncProvider coopSyncProvider, Metrics metrics) { @@ -683,7 +690,7 @@ CookieSyncService cookieSyncService( ObjectUtils.defaultIfNull(maxLimit, Integer.MAX_VALUE), bidderCatalog, hostVendorTcfDefinerService, - privacyEnforcementService, + ccpaEnforcement, uidsCookieService, coopSyncProvider, metrics); @@ -899,27 +906,29 @@ StoredResponseProcessor storedResponseProcessor(ApplicationSettings applicationS } @Bean - PrivacyEnforcementService privacyEnforcementService( - BidderCatalog bidderCatalog, - PrivacyExtractor privacyExtractor, - TcfDefinerService tcfDefinerService, - ImplicitParametersExtractor implicitParametersExtractor, - IpAddressHelper ipAddressHelper, - Metrics metrics, - CountryCodeMapper countryCodeMapper, - @Value("${ccpa.enforce}") boolean ccpaEnforce, - @Value("${lmt.enforce}") boolean lmtEnforce) { + PrivacyEnforcementService privacyEnforcementService(CoppaEnforcement coppaEnforcement, + CcpaEnforcement ccpaEnforcement, + TcfEnforcement tcfEnforcement, + ActivityEnforcement activityEnforcement) { return new PrivacyEnforcementService( - bidderCatalog, + coppaEnforcement, + ccpaEnforcement, + tcfEnforcement, + activityEnforcement); + } + + @Bean + AuctionPrivacyContextFactory auctionPrivacyContextFactory(PrivacyExtractor privacyExtractor, + TcfDefinerService tcfDefinerService, + IpAddressHelper ipAddressHelper, + CountryCodeMapper countryCodeMapper) { + + return new AuctionPrivacyContextFactory( privacyExtractor, tcfDefinerService, - implicitParametersExtractor, ipAddressHelper, - metrics, - countryCodeMapper, - ccpaEnforce, - lmtEnforce); + countryCodeMapper); } @Bean @@ -935,6 +944,34 @@ AmpPrivacyContextFactory ampPrivacyContextFactory(PrivacyExtractor privacyExtrac countryCodeMapper); } + @Bean + CookieSyncPrivacyContextFactory cookieSyncPrivacyContextFactory( + PrivacyExtractor privacyExtractor, + TcfDefinerService tcfDefinerService, + ImplicitParametersExtractor implicitParametersExtractor, + IpAddressHelper ipAddressHelper) { + + return new CookieSyncPrivacyContextFactory( + privacyExtractor, + tcfDefinerService, + implicitParametersExtractor, + ipAddressHelper); + } + + @Bean + SetuidPrivacyContextFactory setuidPrivacyContextFactory( + PrivacyExtractor privacyExtractor, + TcfDefinerService tcfDefinerService, + ImplicitParametersExtractor implicitParametersExtractor, + IpAddressHelper ipAddressHelper) { + + return new SetuidPrivacyContextFactory( + privacyExtractor, + tcfDefinerService, + implicitParametersExtractor, + ipAddressHelper); + } + @Bean PrivacyExtractor privacyExtractor() { return new PrivacyExtractor(); diff --git a/src/main/java/org/prebid/server/spring/config/WebConfiguration.java b/src/main/java/org/prebid/server/spring/config/WebConfiguration.java index bcd8394d04e..c6244306b97 100644 --- a/src/main/java/org/prebid/server/spring/config/WebConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/WebConfiguration.java @@ -14,10 +14,11 @@ import org.prebid.server.analytics.reporter.AnalyticsReporterDelegator; import org.prebid.server.auction.AmpResponsePostProcessor; import org.prebid.server.auction.ExchangeService; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.VideoResponseFactory; import org.prebid.server.auction.gpp.CookieSyncGppService; import org.prebid.server.auction.gpp.SetuidGppService; +import org.prebid.server.auction.privacy.contextfactory.CookieSyncPrivacyContextFactory; +import org.prebid.server.auction.privacy.contextfactory.SetuidPrivacyContextFactory; import org.prebid.server.auction.requestfactory.AmpRequestFactory; import org.prebid.server.auction.requestfactory.AuctionRequestFactory; import org.prebid.server.auction.requestfactory.VideoRequestFactory; @@ -82,7 +83,8 @@ public class WebConfiguration { @Autowired private Vertx vertx; - @Bean // TODO: remove support for properties with http prefix after transition period + // TODO: remove support for properties with http prefix after transition period + @Bean HttpServerOptions httpServerOptions( @Value("#{'${http.max-headers-size:${server.max-headers-size:}}'}") int maxHeaderSize, @Value("#{'${http.max-initial-line-length:${server.max-initial-line-length:}}'}") int maxInitialLineLength, @@ -280,7 +282,7 @@ CookieSyncHandler cookieSyncHandler( ActivityInfrastructureCreator activityInfrastructureCreator, ApplicationSettings applicationSettings, CookieSyncService cookieSyncService, - PrivacyEnforcementService privacyEnforcementService, + CookieSyncPrivacyContextFactory cookieSyncPrivacyContextFactory, AnalyticsReporterDelegator analyticsReporterDelegator, Metrics metrics, TimeoutFactory timeoutFactory, @@ -295,7 +297,7 @@ CookieSyncHandler cookieSyncHandler( activityInfrastructureCreator, cookieSyncService, applicationSettings, - privacyEnforcementService, + cookieSyncPrivacyContextFactory, analyticsReporterDelegator, metrics, timeoutFactory, @@ -308,7 +310,7 @@ SetuidHandler setuidHandler( UidsCookieService uidsCookieService, ApplicationSettings applicationSettings, BidderCatalog bidderCatalog, - PrivacyEnforcementService privacyEnforcementService, + SetuidPrivacyContextFactory setuidPrivacyContextFactory, SetuidGppService setuidGppService, ActivityInfrastructureCreator activityInfrastructureCreator, HostVendorTcfDefinerService tcfDefinerService, @@ -321,7 +323,7 @@ SetuidHandler setuidHandler( uidsCookieService, applicationSettings, bidderCatalog, - privacyEnforcementService, + setuidPrivacyContextFactory, setuidGppService, activityInfrastructureCreator, tcfDefinerService, diff --git a/src/main/java/org/prebid/server/spring/config/bidder/MinuteMediaConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/MinuteMediaConfiguration.java new file mode 100644 index 00000000000..6584d277a2c --- /dev/null +++ b/src/main/java/org/prebid/server/spring/config/bidder/MinuteMediaConfiguration.java @@ -0,0 +1,41 @@ +package org.prebid.server.spring.config.bidder; + +import org.prebid.server.bidder.BidderDeps; +import org.prebid.server.bidder.minutemedia.MinuteMediaBidder; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties; +import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler; +import org.prebid.server.spring.config.bidder.util.UsersyncerCreator; +import org.prebid.server.spring.env.YamlPropertySourceFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import javax.validation.constraints.NotBlank; + +@Configuration +@PropertySource(value = "classpath:/bidder-config/minutemedia.yaml", factory = YamlPropertySourceFactory.class) +public class MinuteMediaConfiguration { + + private static final String BIDDER_NAME = "minutemedia"; + + @Bean("minutemediaConfigurationProperties") + @ConfigurationProperties("adapters.minutemedia") + BidderConfigurationProperties configurationProperties() { + return new BidderConfigurationProperties(); + } + + @Bean + BidderDeps minutemediaBidderDeps(BidderConfigurationProperties minutemediaConfigurationProperties, + @NotBlank @Value("${external-url}") String externalUrl, + JacksonMapper mapper) { + + return BidderDepsAssembler.forBidder(BIDDER_NAME) + .withConfig(minutemediaConfigurationProperties) + .usersyncerCreator(UsersyncerCreator.create(externalUrl)) + .bidderCreator(config -> new MinuteMediaBidder(config.getEndpoint(), mapper)) + .assemble(); + } +} diff --git a/src/main/java/org/prebid/server/util/ListUtil.java b/src/main/java/org/prebid/server/util/ListUtil.java new file mode 100644 index 00000000000..e31aaa453ad --- /dev/null +++ b/src/main/java/org/prebid/server/util/ListUtil.java @@ -0,0 +1,15 @@ +package org.prebid.server.util; + +import org.prebid.server.util.algorithms.ListsUnionView; + +import java.util.List; + +public class ListUtil { + + private ListUtil() { + } + + public static List union(List first, List second) { + return new ListsUnionView<>(first, second); + } +} diff --git a/src/main/java/org/prebid/server/util/algorithms/ListsUnionView.java b/src/main/java/org/prebid/server/util/algorithms/ListsUnionView.java new file mode 100644 index 00000000000..cf48a401505 --- /dev/null +++ b/src/main/java/org/prebid/server/util/algorithms/ListsUnionView.java @@ -0,0 +1,28 @@ +package org.prebid.server.util.algorithms; + +import java.util.AbstractList; +import java.util.List; +import java.util.Objects; + +public class ListsUnionView extends AbstractList implements List { + + private final List first; + private final List second; + + public ListsUnionView(List first, List second) { + this.first = Objects.requireNonNull(first); + this.second = Objects.requireNonNull(second); + } + + @Override + public T get(int index) { + return index < first.size() + ? first.get(index) + : second.get(index - first.size()); + } + + @Override + public int size() { + return first.size() + second.size(); + } +} diff --git a/src/main/java/org/prebid/server/vast/VastModifier.java b/src/main/java/org/prebid/server/vast/VastModifier.java index ce80078bfc1..d5896db41af 100644 --- a/src/main/java/org/prebid/server/vast/VastModifier.java +++ b/src/main/java/org/prebid/server/vast/VastModifier.java @@ -14,15 +14,24 @@ import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class VastModifier { - private static final String IN_LINE_TAG = ""; - private static final String IN_LINE_CLOSE_TAG = ""; - private static final String WRAPPER_TAG = ""; - private static final String WRAPPER_CLOSE_TAG = ""; - private static final String IMPRESSION_CLOSE_TAG = ""; + private static final Pattern WRAPPER_OPEN_TAG_PATTERN = + Pattern.compile("<\\s*wrapper(?:>|\\s.*?>)", Pattern.CASE_INSENSITIVE); + private static final Pattern WRAPPER_CLOSE_TAG_PATTERN = + Pattern.compile("<\\s*/\\s*wrapper(?:>|\\s.*?>)", Pattern.CASE_INSENSITIVE); + private static final Pattern INLINE_OPEN_TAG_PATTERN = + Pattern.compile("<\\s*inline(?:>|\\s.*?>)", Pattern.CASE_INSENSITIVE); + private static final Pattern INLINE_CLOSE_TAG_PATTERN = + Pattern.compile("<\\s*/\\s*inline(?:>|\\s.*?>)", Pattern.CASE_INSENSITIVE); + private static final Pattern IMPRESSION_CLOSE_TAG_PATTERN = + Pattern.compile("<\\s*/\\s*impression(?:>|\\s.*?>)", Pattern.CASE_INSENSITIVE); + private final BidderCatalog bidderCatalog; private final EventsService eventsService; private final Metrics metrics; @@ -102,45 +111,42 @@ private static String resolveVastXmlFrom(String bidAdm, String bidNurl) { : bidAdm; } - private String appendTrackingUrlToVastXml(String vastXml, String vastUrlTracking, String bidder) { - final int inLineTagIndex = StringUtils.indexOfIgnoreCase(vastXml, IN_LINE_TAG); - final int wrapperTagIndex = StringUtils.indexOfIgnoreCase(vastXml, WRAPPER_TAG); + private static String appendTrackingUrlToVastXml(String xml, String urlTracking, String bidder) { + return appendTrackingUrl(xml, urlTracking, INLINE_OPEN_TAG_PATTERN, INLINE_CLOSE_TAG_PATTERN) + .or(() -> appendTrackingUrl(xml, urlTracking, WRAPPER_OPEN_TAG_PATTERN, WRAPPER_CLOSE_TAG_PATTERN)) + .orElseThrow(() -> new PreBidException( + "VastXml does not contain neither InLine nor Wrapper for %s response".formatted(bidder))); + } + + private static Optional appendTrackingUrl(String vastXml, + String vastUrlTracking, + Pattern openTagPattern, + Pattern closeTagPattern) { - if (inLineTagIndex != -1) { - return appendTrackingUrl(vastXml, vastUrlTracking, IN_LINE_CLOSE_TAG); - } else if (wrapperTagIndex != -1) { - return appendTrackingUrl(vastXml, vastUrlTracking, WRAPPER_CLOSE_TAG); + final Matcher openTagMatcher = openTagPattern.matcher(vastXml); + if (!openTagMatcher.find()) { + return Optional.empty(); } - throw new PreBidException("VastXml does not contain neither InLine nor Wrapper for %s response" - .formatted(bidder)); - } - private static String appendTrackingUrl(String vastXml, String vastUrlTracking, String elementCloseTag) { - if (vastXml.contains(IMPRESSION_CLOSE_TAG)) { - return insertAfterExistingImpressionTag(vastXml, vastUrlTracking); + final Matcher impressionCloseTagMatcher = IMPRESSION_CLOSE_TAG_PATTERN.matcher(vastXml); + if (impressionCloseTagMatcher.find(openTagMatcher.end())) { + int replacementEnd = impressionCloseTagMatcher.end(); + while (impressionCloseTagMatcher.find(replacementEnd)) { + replacementEnd = impressionCloseTagMatcher.end(); + } + return Optional.of(insertUrlTracking(vastXml, replacementEnd, vastUrlTracking)); } - return insertBeforeElementCloseTag(vastXml, vastUrlTracking, elementCloseTag); - } - private static String insertAfterExistingImpressionTag(String vastXml, String vastUrlTracking) { - final String impressionTag = ""; - final int replacementStart = vastXml.lastIndexOf(IMPRESSION_CLOSE_TAG); + final Matcher closeTagMatcher = closeTagPattern.matcher(vastXml); + if (!closeTagMatcher.find(openTagMatcher.end())) { + return Optional.of(vastXml); + } - return vastXml.substring(0, replacementStart) + IMPRESSION_CLOSE_TAG + impressionTag - + vastXml.substring(replacementStart + IMPRESSION_CLOSE_TAG.length()); + return Optional.of(insertUrlTracking(vastXml, closeTagMatcher.start(), vastUrlTracking)); } - private static String insertBeforeElementCloseTag(String vastXml, String vastUrlTracking, String elementCloseTag) { - final int indexOfCloseTag = StringUtils.indexOfIgnoreCase(vastXml, elementCloseTag); - - if (indexOfCloseTag == -1) { - return vastXml; - } - - final String caseSpecificCloseTag = - vastXml.substring(indexOfCloseTag, indexOfCloseTag + elementCloseTag.length()); + private static String insertUrlTracking(String vastXml, int index, String vastUrlTracking) { final String impressionTag = ""; - - return vastXml.replace(caseSpecificCloseTag, impressionTag + caseSpecificCloseTag); + return vastXml.substring(0, index) + impressionTag + vastXml.substring(index); } } diff --git a/src/main/resources/bidder-config/mgidx.yaml b/src/main/resources/bidder-config/mgidx.yaml index 0228aa09cc7..7c1b74aaae3 100644 --- a/src/main/resources/bidder-config/mgidx.yaml +++ b/src/main/resources/bidder-config/mgidx.yaml @@ -1,6 +1,8 @@ adapters: mgidX: - endpoint: https://us-east-x.mgid.com/pserver + # We have the following regional endpoint domains: 'us-east-x' and 'eu' + # Please deploy this config in each of your datacenters with the appropriate regional subdomain + endpoint: https://REGION.mgid.com/pserver meta-info: maintainer-email: prebid@mgid.com app-media-types: diff --git a/src/main/resources/bidder-config/minutemedia.yaml b/src/main/resources/bidder-config/minutemedia.yaml new file mode 100644 index 00000000000..5271b51be67 --- /dev/null +++ b/src/main/resources/bidder-config/minutemedia.yaml @@ -0,0 +1,19 @@ +adapters: + minutemedia: + endpoint: https://pbs.minutemedia-prebid.com/pbs-mm?publisher_id={{PublisherId}} + modifying-vast-xml-allowed: true + meta-info: + maintainer-email: hb@minutemedia.com + app-media-types: + - banner + - video + site-media-types: + - banner + - video + vendor-id: 918 + usersync: + cookie-family-name: minutemedia + iframe: + url: https://pbs-cs.minutemedia-prebid.com/pbs-iframe?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redirect={{redirect_url}} + support-cors: false + uid-macro: '[PBS_UID]' diff --git a/src/main/resources/bidder-config/yieldlab.yaml b/src/main/resources/bidder-config/yieldlab.yaml index 537436ab41d..806d279bc26 100644 --- a/src/main/resources/bidder-config/yieldlab.yaml +++ b/src/main/resources/bidder-config/yieldlab.yaml @@ -1,6 +1,6 @@ adapters: yieldlab: - endpoint: https://ad.yieldlab.net/yp/ + endpoint: https://ad.yieldlab.net/yp meta-info: maintainer-email: solutions@yieldlab.de app-media-types: diff --git a/src/main/resources/static/bidder-params/minutemedia.json b/src/main/resources/static/bidder-params/minutemedia.json new file mode 100644 index 00000000000..5e79c6a6638 --- /dev/null +++ b/src/main/resources/static/bidder-params/minutemedia.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "MinuteMedia Adapter Params", + "description": "A schema which validates params accepted by the MinuteMedia adapter", + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "The organization ID.", + "minLength": 1 + } + }, + "required": [ + "org" + ] +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PurposeConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PurposeConfig.groovy index 08a21ae226d..0ad1a0bdf79 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/PurposeConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/PurposeConfig.groovy @@ -11,4 +11,5 @@ class PurposeConfig { PurposeEnforcement enforcePurpose Boolean enforceVendors List vendorExceptions + PurposeEid eid } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PurposeEid.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PurposeEid.groovy new file mode 100644 index 00000000000..23fb7b519b7 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/PurposeEid.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategies +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy) +class PurposeEid { + + Boolean requireConsent + List exceptions + Boolean activityTransition +} diff --git a/src/test/groovy/org/prebid/server/functional/model/privacy/EnforcementRequirement.groovy b/src/test/groovy/org/prebid/server/functional/model/privacy/EnforcementRequirement.groovy new file mode 100644 index 00000000000..2022d4f754e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/privacy/EnforcementRequirement.groovy @@ -0,0 +1,22 @@ +package org.prebid.server.functional.model.privacy + +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.config.Purpose +import org.prebid.server.functional.model.config.PurposeEnforcement +import org.prebid.server.functional.util.privacy.TcfConsent + +@ToString(includeNames = true, ignoreNulls = true) +class EnforcementRequirement { + + Purpose purpose + PurposeEnforcement enforcePurpose + Boolean enforceVendor + Integer vendorConsentBitField + Integer vendorLegitimateInterestBitField + List vendorExceptions + boolean purposesLITransparency + List restrictionType + Integer vendorIdGvl + Integer vendorListVersion +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/ActivityType.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/ActivityType.groovy index 9feb4dff72d..ef76b4023fb 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/ActivityType.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/ActivityType.groovy @@ -10,7 +10,8 @@ enum ActivityType { REPORT_ANALYTICS("reportAnalytics"), TRANSMIT_UFPD("transmitUfpd"), TRANSMIT_PRECISE_GEO("transmitPreciseGeo"), - TRANSMIT_TID("transmitTid") + TRANSMIT_TID("transmitTid"), + TRANSMIT_EIDS("transmitEids") @JsonValue final String value diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/AllowActivities.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/AllowActivities.groovy index 2b47012263d..aeafaf30fa6 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/AllowActivities.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/AllowActivities.groovy @@ -2,6 +2,7 @@ package org.prebid.server.functional.model.request.auction import groovy.transform.ToString +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_TID import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_UFPD @@ -18,6 +19,7 @@ class AllowActivities { Activity enrichUfpd Activity reportAnalytics Activity transmitUfpd + Activity transmitEids Activity transmitPreciseGeo Activity transmitTid @@ -30,6 +32,8 @@ class AllowActivities { return fetchBids = activity case ENRICH_UFPD: return enrichUfpd = activity + case TRANSMIT_EIDS: + return transmitEids = activity case REPORT_ANALYTICS: return reportAnalytics = activity case TRANSMIT_UFPD: 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 781d78156b2..1b70bd08799 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 @@ -1,9 +1,11 @@ package org.prebid.server.functional.model.request.auction +import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.prebid.server.functional.util.PBSUtils @ToString(includeNames = true, ignoreNulls = true) +@EqualsAndHashCode class Eid { String source diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Uid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Uid.groovy index ffcc6226b62..e562b9a5423 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Uid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Uid.groovy @@ -1,9 +1,11 @@ package org.prebid.server.functional.model.request.auction +import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.prebid.server.functional.util.PBSUtils @ToString(includeNames = true, ignoreNulls = true) +@EqualsAndHashCode class Uid { String id diff --git a/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy b/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy index a80cffe0c11..048c81c0c27 100644 --- a/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy +++ b/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy @@ -87,7 +87,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = postAuction(bidRequest, headers) checkResponseStatusCode(response) - response.as(BidResponse) + decode(response.body.asString(), BidResponse) } @Step("[POST RAW] /openrtb2/auction") @@ -105,7 +105,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = getAmp(ampRequest, headers) checkResponseStatusCode(response) - response.as(AmpResponse) + decode(response.body.asString(), AmpResponse) } @Step("[GET RAW] /openrtb2/amp") @@ -123,7 +123,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = postCookieSync(request) checkResponseStatusCode(response) - response.as(CookieSyncResponse) + decode(response.body.asString(), CookieSyncResponse) } @Step("[POST] /cookie_sync with headers") @@ -131,7 +131,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = postCookieSync(request, null, headers) checkResponseStatusCode(response) - response.as(CookieSyncResponse) + decode(response.body.asString(), CookieSyncResponse) } @Step("[POST] /cookie_sync with uids cookie") @@ -139,7 +139,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = postCookieSync(request, uidsCookie) checkResponseStatusCode(response) - response.as(CookieSyncResponse) + decode(response.body.asString(), CookieSyncResponse) } @Step("[POST] /cookie_sync with uids and additional cookies") @@ -149,7 +149,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = postCookieSync(request, uidsCookie, additionalCookies) checkResponseStatusCode(response) - response.as(CookieSyncResponse) + decode(response.body.asString(), CookieSyncResponse) } @Step("[POST RAW] /cookie_sync with uids cookies") @@ -201,7 +201,7 @@ class PrebidServerService implements ObjectMapperWrapper { .get(GET_UIDS_ENDPOINT) checkResponseStatusCode(response) - response.as(GetuidResponse) + decode(response.body.asString(), GetuidResponse) } @Step("[GET] /event") @@ -221,7 +221,7 @@ class PrebidServerService implements ObjectMapperWrapper { .post(VTRACK_ENDPOINT) checkResponseStatusCode(response) - response.as(PrebidCacheResponse) + decode(response.body.asString(), PrebidCacheResponse) } @Step("[GET] /status") @@ -229,7 +229,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = given(requestSpecification).get(STATUS_ENDPOINT) checkResponseStatusCode(response) - response.as(StatusResponse) + decode(response.body.asString(), StatusResponse) } @Step("[GET] /info/bidders") @@ -254,7 +254,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = given(requestSpecification).get("$INFO_BIDDERS_ENDPOINT/$bidderName.value") checkResponseStatusCode(response) - response.as(BidderInfoResponse) + decode(response.body.asString(), BidderInfoResponse) } @Step("[GET] /bidders/params") @@ -262,7 +262,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = given(requestSpecification).get(BIDDERS_PARAMS_ENDPOINT) checkResponseStatusCode(response) - response.as(BiddersParamsResponse) + decode(response.body.asString(), BiddersParamsResponse) } @Step("[GET] /currency/rates") @@ -270,7 +270,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = given(adminRequestSpecification).get(CURRENCY_RATES_ENDPOINT) checkResponseStatusCode(response) - response.as(CurrencyRatesResponse) + decode(response.body.asString(), CurrencyRatesResponse) } @Step("[GET] /logging/httpinteraction") @@ -308,7 +308,7 @@ class PrebidServerService implements ObjectMapperWrapper { def response = request.get(LINE_ITEM_STATUS_ENDPOINT) checkResponseStatusCode(response) - response.as(LineItemStatusReport) + decode(response.body.asString(), LineItemStatusReport) } @Step("[GET] /metrics") diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/NetworkScaffolding.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/NetworkScaffolding.groovy index 5aba5ecac9a..7eea7536dfb 100644 --- a/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/NetworkScaffolding.groovy +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/NetworkScaffolding.groovy @@ -156,6 +156,7 @@ abstract class NetworkScaffolding implements ObjectMapperWrapper { getRequestsHeaders(mockServerClient.retrieveRecordedRequests(getRequest(value)) as List) } + // should be used instead of mockServerClient.reset due to memory leak on library void reset(String resetEndpoint = endpoint, ClearType clearType = ALL) { mockServerClient.clear(request().withPath(resetEndpoint), clearType) } diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/VendorList.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/VendorList.groovy index e8c01b89358..8af11b1c3ca 100644 --- a/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/VendorList.groovy +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/VendorList.groovy @@ -34,15 +34,15 @@ class VendorList extends NetworkScaffolding { request().withPath(VENDOR_LIST_ENDPOINT) } + @Override void reset() { - TcfPolicyVersion.values().each { version -> super.reset("/v${version.getVendorListVersion()}/vendor-list.json") } + TcfPolicyVersion.values().each { version -> super.reset("/v${version.vendorListVersion}/vendor-list.json") } } - void setResponse( - TcfPolicyVersion tcfPolicyVersion = TCF_POLICY_V2, - Delay delay = null, - Map vendors = [(GENERIC_VENDOR_ID): Vendor.getDefaultVendor(GENERIC_VENDOR_ID)]) { - def prepareEndpoint = VENDOR_LIST_ENDPOINT.replace("{TCF_POLICY}", tcfPolicyVersion.vendorListVersion.toString()) + void setResponse(TcfPolicyVersion tcfPolicyVersion = TCF_POLICY_V2, + Delay delay = null, + Map vendors = [(GENERIC_VENDOR_ID): Vendor.getDefaultVendor(GENERIC_VENDOR_ID)]) { + def prepareEndpoint = endpoint.replace("{TCF_POLICY}", "v" + tcfPolicyVersion.vendorListVersion) def prepareEncodeResponseBody = encode(defaultVendorListResponse.tap { it.tcfPolicyVersion = tcfPolicyVersion.vendorListVersion it.vendors = vendors diff --git a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy index d495afc4e5c..cfab2bafde1 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy @@ -176,7 +176,7 @@ class AuctionSpec extends BaseSpec { then: "Bidder request shouldn't contain buyeruid from the user.ext.prebid.buyeruids" def bidderRequest = bidder.getBidderRequest(bidRequest.id) - assert !bidderRequest.user + assert !bidderRequest.user.ext } def "PBS should populate buyeruid from uids cookie when buyeruids with appropriate bidder but without value present in request"() { @@ -221,7 +221,7 @@ class AuctionSpec extends BaseSpec { then: "Bidder request shouldn't contain buyeruid from the uids cookie" def bidderRequest = bidder.getBidderRequest(bidRequest.id) - assert !bidderRequest.user + assert !bidderRequest.user.buyeruid } def "PBS should take precedence buyeruids whenever present valid uid cookie"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy index 4f3c43a985b..14467df1967 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy @@ -6,7 +6,6 @@ import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.db.StoredImp import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.amp.AmpRequest -import org.prebid.server.functional.model.request.auction.Asset import org.prebid.server.functional.model.request.auction.Banner import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Device @@ -24,7 +23,6 @@ import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.model.response.auction.ErrorType import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.CcpaConsent -import spock.lang.IgnoreRest import static org.prebid.server.functional.model.bidder.BidderName.APPNEXUS import static org.prebid.server.functional.model.bidder.BidderName.GENERIC diff --git a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy index 3f9ccc7ab43..ccd5f8b9cf0 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy @@ -1,5 +1,9 @@ package org.prebid.server.functional.tests +import org.prebid.server.functional.model.config.AccountAuctionConfig +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.config.AccountEventsConfig +import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.request.auction.Asset import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Imp @@ -192,4 +196,50 @@ class CacheSpec extends BaseSpec { false | BANNER true | VIDEO } + + def "PBS should update prebid_cache.creative_size.xml metric and adding tracking xml when xml creative contain #wrapper and impression are valid xml value"() { + given: "Current value of metric prebid_cache.requests.ok" + def initialValue = getCurrentMetricValue(defaultPbsService, "prebid_cache.requests.ok") + + and: "Create and save enabled events config in account" + def accountId = PBSUtils.randomNumber.toString() + def account = new Account().tap { + uuid = accountId + config = new AccountConfig().tap { + auction = new AccountAuctionConfig(events: new AccountEventsConfig(enabled: true)) + } + } + accountDao.save(account) + + and: "Vtrack request with custom tags" + def payload = PBSUtils.randomString + def creative = "<${wrapper}>prebid.org wrapper" + + "<![CDATA[//${payload}]]>" + + "<${impression}> <![CDATA[ ]]> " + def request = VtrackRequest.getDefaultVtrackRequest(creative) + + when: "PBS processes vtrack request" + defaultPbsService.sendVtrackRequest(request, accountId) + + then: "Vast xml is modified" + def prebidCacheRequest = prebidCache.getXmlRecordedRequestsBody(payload) + assert prebidCacheRequest.size() == 1 + assert prebidCacheRequest[0].contains("/event?t=imp&b=${request.puts[0].bidid}&a=$accountId&bidder=${request.puts[0].bidder}") + + and: "prebid_cache.creative_size.xml metric should be updated" + def metrics = defaultPbsService.sendCollectedMetricsRequest() + assert metrics["prebid_cache.requests.ok"] == initialValue + 1 + + and: "account..prebid_cache.creative_size.xml should be updated" + assert metrics["account.${accountId}.prebid_cache.requests.ok" as String] == 1 + + where: + wrapper | impression + " wrapper " | " impression " + PBSUtils.getRandomCase(" wrapper ") | PBSUtils.getRandomCase(" impression ") + " wraPPer ${PBSUtils.getRandomString()} " | " imPreSSion ${PBSUtils.getRandomString()}" + " inLine " | " ImpreSSion $PBSUtils.randomNumber" + PBSUtils.getRandomCase(" inline ") | " ${PBSUtils.getRandomCase(" impression ")} $PBSUtils.randomNumber " + " inline ${PBSUtils.getRandomString()} " | " ImpreSSion " + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy index 39a732ebdfa..d58e6e3a781 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy @@ -19,8 +19,6 @@ import org.prebid.server.functional.model.response.auction.Bid import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.util.PBSUtils -import spock.lang.IgnoreRest -import spock.lang.RepeatUntilFailure import java.math.RoundingMode diff --git a/src/test/groovy/org/prebid/server/functional/tests/TimeoutSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/TimeoutSpec.groovy index d04808f4fa2..99fa3b31a0e 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/TimeoutSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/TimeoutSpec.groovy @@ -1,11 +1,9 @@ package org.prebid.server.functional.tests -import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.amp.AmpRequest import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.PrebidStoredRequest -import org.prebid.server.functional.model.response.auction.ErrorType import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.testcontainers.container.PrebidServerContainer import org.prebid.server.functional.util.PBSUtils @@ -17,7 +15,6 @@ import static org.prebid.server.functional.testcontainers.container.PrebidServer class TimeoutSpec extends BaseSpec { private static final int DEFAULT_TIMEOUT = getRandomTimeout() - private static final int MIN_TIMEOUT_BIDDER_REQUEST = 5 private static final int MIN_TIMEOUT = PBSUtils.getRandomNumber(50, 150) private static final Map PBS_CONFIG = ["auction.biddertmax.max" : MAX_TIMEOUT as String, "auction.biddertmax.min" : MIN_TIMEOUT as String] @@ -284,76 +281,6 @@ class TimeoutSpec extends BaseSpec { assert isInternalProcessingTime(bidderRequest.tmax, MAX_TIMEOUT) } - def "PBS amp should return error when auction.biddertmax.min value not enough for bidder request"() { - given: "PBS config with biddertmax.min" - def prebidServerService = pbsServiceFactory.getService(["auction.biddertmax.min" : MIN_TIMEOUT_BIDDER_REQUEST as String]) - - and: "Default AMP request without timeout" - def ampRequest = AmpRequest.defaultAmpRequest.tap { - timeout = null - } - - and: "Default stored request tmax" - def minTmax = MIN_TIMEOUT_BIDDER_REQUEST - 1 - def ampStoredRequest = BidRequest.defaultStoredRequest.tap { - tmax = minTmax - } - - and: "Save storedRequest into DB" - def storedRequestModel = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) - storedRequestDao.save(storedRequestModel) - - when: "PBS processes amp request" - def bidResponse = prebidServerService.sendAmpRequest(ampRequest) - - then: "Bidder request timeout should correspond to the min from the stored request" - assert bidResponse?.ext?.debug?.resolvedRequest?.tmax == minTmax - - and: "PBS should send to bidder tmax form auction.biddertmax.min config" - assert bidResponse.ext.debug.httpcalls[BidderName.GENERIC.value]*.requestBody[0].contains("\"tmax\":${MIN_TIMEOUT_BIDDER_REQUEST}") - - and: "Bid response should shutdown by timeout from stored request" - def errors = bidResponse.ext?.errors - assert errors[ErrorType.GENERIC]*.code == [1] - assert errors[ErrorType.GENERIC]*.message == ["Timeout has been exceeded"] - } - - def "PBS auction should return error when auction.biddertmax.min value not enough for bidder request"() { - given: "PBS config with biddertmax.min" - def prebidServerService = pbsServiceFactory.getService(["auction.biddertmax.max" : MAX_TIMEOUT as String, - "auction.biddertmax.min" : MIN_TIMEOUT_BIDDER_REQUEST as String]) - - and: "Default BidRequest without timeout" - def bidRequest = BidRequest.defaultBidRequest.tap { - tmax = null - ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomNumber) - } - - and: "Default stored request with min tmax" - def minTmax = MIN_TIMEOUT_BIDDER_REQUEST + 4 - def storedRequest = BidRequest.defaultStoredRequest.tap { - tmax = minTmax - } - - and: "Save storedRequest into DB" - def storedRequestModel = StoredRequest.getStoredRequest(bidRequest.ext.prebid.storedRequest.id, storedRequest) - storedRequestDao.save(storedRequestModel) - - when: "PBS processes auction request" - def bidResponse = prebidServerService.sendAuctionRequest(bidRequest) - - then: "Bidder request timeout should correspond to the min from the stored request" - assert bidResponse?.ext?.debug?.resolvedRequest?.tmax == minTmax - - and: "PBS should send to bidder tmax form auction.biddertmax.min config" - assert bidResponse.ext.debug.httpcalls[BidderName.GENERIC.value]*.requestBody[0].contains("\"tmax\":${MIN_TIMEOUT_BIDDER_REQUEST}") - - and: "Bid response should shutdown by timeout from stored request" - def errors = bidResponse.ext?.errors - assert errors[ErrorType.GENERIC]*.code == [1] - assert errors[ErrorType.GENERIC]*.message == ["Timeout has been exceeded"] - } - def "PBS should choose min timeout form config for bidder request when in request value lowest that in auction.biddertmax.min"() { given: "PBS config with percent" def minBidderTmax = PBSUtils.getRandomNumber(MIN_TIMEOUT, MAX_TIMEOUT) diff --git a/src/test/groovy/org/prebid/server/functional/tests/TopicsHeaderSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/TopicsHeaderSpec.groovy index 9a473b072eb..f94ae8772c9 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/TopicsHeaderSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/TopicsHeaderSpec.groovy @@ -60,7 +60,7 @@ class TopicsHeaderSpec extends BaseSpec { then: "Bidder request shouldn't contain user.data from header" def bidderRequest = bidder.getBidderRequest(bidRequest.id) - assert !bidderRequest.user + assert !bidderRequest.user.data and: "Response should contain Observe-Browsing-Topics header" assert response.headers["Observe-Browsing-Topics"] == "?1" @@ -91,7 +91,7 @@ class TopicsHeaderSpec extends BaseSpec { then: "Bidder request shouldn't contain user.data from header" def bidderRequest = bidder.getBidderRequest(bidRequest.id) - assert !bidderRequest.user + assert !bidderRequest.user.data and: "Response should contain Observe-Browsing-Topics header" assert response.headers["Observe-Browsing-Topics"] == "?1" @@ -149,7 +149,7 @@ class TopicsHeaderSpec extends BaseSpec { then: "Bidder request shouldn't contain user.data" def bidderRequest = bidder.getBidderRequest(bidRequest.id) - assert !bidderRequest.user + assert !bidderRequest.user.data and: "Bid response should contain warning" assert response.responseBody.contains("Invalid field in Sec-Browsing-Topics header: ${header.replace(", ", "")} discarded due to limit reached.") diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy index d7e88aca716..d20d4f51dd4 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy @@ -35,7 +35,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { and: "Stored bid response in DB" def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { - it.seatbid[0].bid[0].adm = amdValue as String + it.seatbid[0].bid[0].adm = admValue as String } def storedResponse = new StoredResponse(responseId: storedResponseId, storedBidResponse: storedBidResponse) storedResponseDao.save(storedResponse) @@ -57,7 +57,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert !getAnalyticResults(response) where: - amdValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}-${PBSUtils.randomString}"] + admValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}-${PBSUtils.randomString}"] } def "PBS should reject request with error and provide analytic when adm matches with pattern name and filter set to enabled in host config"() { @@ -70,7 +70,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { and: "Stored bid response in DB" def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { - it.seatbid[0].bid[0].adm = amdValue as String + it.seatbid[0].bid[0].adm = admValue as String } def storedResponse = new StoredResponse(responseId: storedResponseId, storedBidResponse: storedBidResponse) storedResponseDao.save(storedResponse) @@ -98,10 +98,10 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert analyticResult == AnalyticResult.buildFromImp(bidRequest.imp.first()) where: - amdValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}.${PBSUtils.randomString}"] + admValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}.${PBSUtils.randomString}"] } - def "PBS should process request without analytics when adm is empty name and filter enabled in host config"() { + def "PBS should process request without analytics when adm is #admValue and filter enabled in host config"() { given: "BidRequest with stored response" def storedResponseId = PBSUtils.randomNumber def bidRequest = BidRequest.defaultBidRequest.tap { @@ -111,7 +111,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { and: "Stored bid response in DB" def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { - it.seatbid[0].bid[0].adm = amdValue as String + it.seatbid[0].bid[0].adm = admValue as String } def storedResponse = new StoredResponse(responseId: storedResponseId, storedBidResponse: storedBidResponse) storedResponseDao.save(storedResponse) @@ -135,7 +135,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert !getAnalyticResults(response) where: - amdValue << [null, '', PATTERN_NAME.substring(PBSUtils.getRandomNumber(0, PATTERN_NAME.size()))] + admValue << [null, '', PBSUtils.randomString] } def "PBS should prioritize account config and reject request with error and provide analytic when adm matches with pattern name and filter disabled in host config but enabled in account config"() { @@ -154,7 +154,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { and: "Stored bid response in DB" def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { - it.seatbid[0].bid[0].adm = amdValue as String + it.seatbid[0].bid[0].adm = admValue as String } def storedResponse = new StoredResponse(responseId: storedResponseId, storedBidResponse: storedBidResponse) storedResponseDao.save(storedResponse) @@ -178,7 +178,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert analyticResult == AnalyticResult.buildFromImp(bidRequest.imp.first()) where: - amdValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}-${PBSUtils.randomString}"] + admValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}-${PBSUtils.randomString}"] } def "PBS should prioritize account config and process request without analytics when adm matches with pattern name and filter enabled in host config but disabled in account config"() { @@ -197,7 +197,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { and: "Stored bid response in DB" def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { - it.seatbid[0].bid[0].adm = amdValue as String + it.seatbid[0].bid[0].adm = admValue as String } def storedResponse = new StoredResponse(responseId: storedResponseId, storedBidResponse: storedBidResponse) storedResponseDao.save(storedResponse) @@ -215,7 +215,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert !getAnalyticResults(response) where: - amdValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}-${PBSUtils.randomString}"] + admValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}-${PBSUtils.randomString}"] } def "PBS should prioritize account config and reject request with error and provide analytic when adm matches with account pattern and both host and account configs are enabled"() { @@ -311,7 +311,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { and: "Stored bid response in DB" def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { - it.seatbid[0].bid[0].adm = amdValue + it.seatbid[0].bid[0].adm = admValue } def storedResponse = new StoredResponse(responseId: storedResponseId, storedBidResponse: storedBidResponse) storedResponseDao.save(storedResponse) @@ -329,7 +329,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert !getAnalyticResults(response) where: - amdValue << [PATTERN_NAME, PATTERN_NAME_ACCOUNT] + admValue << [PATTERN_NAME, PATTERN_NAME_ACCOUNT] } private static List getAnalyticResults(BidResponse response) { diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy index 2dd69ccf285..4229a356033 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy @@ -1385,7 +1385,6 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { assert response.responseBody } - def "PBS setuid call when privacy regulation don't match custom requirement should respond with required UIDs cookies"() { given: "Cookie sync SetuidRequest with accountId" def accountId = PBSUtils.randomNumber as String diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy new file mode 100644 index 00000000000..ffecec814e2 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy @@ -0,0 +1,2159 @@ +package org.prebid.server.functional.tests.privacy + +import org.prebid.server.functional.model.config.AccountGppConfig +import org.prebid.server.functional.model.config.ActivityConfig +import org.prebid.server.functional.model.config.EqualityValueRule +import org.prebid.server.functional.model.config.InequalityValueRule +import org.prebid.server.functional.model.config.LogicalRestrictedRule +import org.prebid.server.functional.model.config.GppModuleConfig +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.model.request.auction.Activity +import org.prebid.server.functional.model.request.auction.ActivityRule +import org.prebid.server.functional.model.request.auction.AllowActivities +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Condition +import org.prebid.server.functional.model.request.auction.Device +import org.prebid.server.functional.model.request.auction.Eid +import org.prebid.server.functional.model.request.auction.Geo +import org.prebid.server.functional.model.request.auction.User +import org.prebid.server.functional.model.request.auction.UserExt +import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.util.PBSUtils +import org.prebid.server.functional.util.privacy.gpp.UspCaV1Consent +import org.prebid.server.functional.util.privacy.gpp.UspCoV1Consent +import org.prebid.server.functional.util.privacy.gpp.UspCtV1Consent +import org.prebid.server.functional.util.privacy.gpp.UspNatV1Consent +import org.prebid.server.functional.util.privacy.gpp.UspUtV1Consent +import org.prebid.server.functional.util.privacy.gpp.UspVaV1Consent +import org.prebid.server.functional.util.privacy.gpp.data.UsCaliforniaSensitiveData +import org.prebid.server.functional.util.privacy.gpp.data.UsNationalSensitiveData +import org.prebid.server.functional.util.privacy.gpp.data.UsUtahSensitiveData + +import java.time.Instant + +import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST +import static io.netty.handler.codec.http.HttpResponseStatus.UNAUTHORIZED +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.config.DataActivity.CONSENT +import static org.prebid.server.functional.model.config.DataActivity.NOTICE_NOT_PROVIDED +import static org.prebid.server.functional.model.config.DataActivity.NOTICE_PROVIDED +import static org.prebid.server.functional.model.config.DataActivity.NOT_APPLICABLE +import static org.prebid.server.functional.model.config.DataActivity.NO_CONSENT +import static org.prebid.server.functional.model.config.LogicalRestrictedRule.LogicalOperation.AND +import static org.prebid.server.functional.model.config.LogicalRestrictedRule.LogicalOperation.OR +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.CHILD_CONSENTS_BELOW_13 +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.CHILD_CONSENTS_FROM_13_TO_16 +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.GPC +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_ACCOUNT_INFO +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_BIOMETRIC_ID +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_CITIZENSHIP_STATUS +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_COMMUNICATION_CONTENTS +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_GENETIC_ID +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_GEOLOCATION +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_HEALTH_INFO +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_ID_NUMBERS +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_ORIENTATION +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_RACIAL_ETHNIC_ORIGIN +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SENSITIVE_DATA_RELIGIOUS_BELIEFS +import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SHARING_NOTICE +import static org.prebid.server.functional.model.pricefloors.Country.CAN +import static org.prebid.server.functional.model.pricefloors.Country.USA +import static org.prebid.server.functional.model.request.GppSectionId.USP_CA_V1 +import static org.prebid.server.functional.model.request.GppSectionId.USP_CO_V1 +import static org.prebid.server.functional.model.request.GppSectionId.USP_CT_V1 +import static org.prebid.server.functional.model.request.GppSectionId.USP_NAT_V1 +import static org.prebid.server.functional.model.request.GppSectionId.USP_UT_V1 +import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 +import static org.prebid.server.functional.model.request.GppSectionId.USP_VA_V1 +import static org.prebid.server.functional.model.request.amp.ConsentType.GPP +import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_EIDS +import static org.prebid.server.functional.model.request.auction.PrivacyModule.ALL +import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_ALL +import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_TFC_EU +import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_CUSTOM_LOGIC +import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_GENERAL +import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE +import static org.prebid.server.functional.util.privacy.model.State.ALABAMA +import static org.prebid.server.functional.util.privacy.model.State.ONTARIO + +class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { + + private static final String ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT = "account.%s.activity.processedrules.count" + private static final String DISALLOWED_COUNT_FOR_ACCOUNT = "account.%s.activity.${TRANSMIT_EIDS.metricValue}.disallowed.count" + private static final String ACTIVITY_RULES_PROCESSED_COUNT = "requests.activity.processedrules.count" + private static final String DISALLOWED_COUNT_FOR_ACTIVITY_RULE = "requests.activity.${TRANSMIT_EIDS.metricValue}.disallowed.count" + private static final String DISALLOWED_COUNT_FOR_GENERIC_ADAPTER = "adapter.${GENERIC.value}.activity.${TRANSMIT_EIDS.metricValue}.disallowed.count" + private static final String ALERT_GENERAL = "alerts.general" + + def "PBS auction call when transmit EIDS activities is allowing requests should leave EIDS fields in request and update proper metrics"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "Activities set with generic bidder allowed" + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.defaultActivity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Save account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + assert genericBidderRequest.user.eids[0].source == genericBidRequest.user.eids[0].source + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + } + + def "PBS auction call when transmit EIDS activities is rejecting requests should remove EIDS fields in request and update disallowed metrics"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "Allow activities setup" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity as Activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Save account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[DISALLOWED_COUNT_FOR_ACTIVITY_RULE] == 1 + assert metrics[DISALLOWED_COUNT_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[DISALLOWED_COUNT_FOR_GENERIC_ADAPTER] == 1 + } + + def "PBS auction call when default activity setting set to false should remove EIDS fields from request"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "Allow activities setup" + def activity = new Activity(defaultAction: false) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Save account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + } + + def "PBS auction call when bidder allowed activities have empty condition type should skip this rule and emit an error"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "Activities set for transmit EIDS with bidder allowed without type" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(conditions, isAllowed)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Save account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Response should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "Activity configuration for account ${accountId} " + + "contains conditional rule with empty array").size() == 1 + + where: + conditions | isAllowed + new Condition(componentType: []) | true + new Condition(componentType: []) | false + new Condition(componentName: []) | true + new Condition(componentName: []) | false + } + + def "PBS auction call when first rule allowing in activities should leave EIDS fields in request"() { + given: "Default Generic BidRequests with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "Activity rules with same priority" + def allowActivity = new ActivityRule(condition: Condition.baseCondition, allow: true) + def disallowActivity = new ActivityRule(condition: Condition.baseCondition, allow: false) + + and: "Activities set for bidder allowed by hierarchy structure" + def activity = Activity.getDefaultActivity([allowActivity, disallowActivity]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Save account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + assert genericBidderRequest.user.eids[0].source == genericBidRequest.user.eids[0].source + } + + def "PBS auction call when first rule disallowing in activities should remove EIDS fields in request"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "Activities set for actions with Generic bidder rejected by hierarchy setup" + def disallowActivity = new ActivityRule(condition: Condition.baseCondition, allow: false) + def allowActivity = new ActivityRule(condition: Condition.baseCondition, allow: true) + + and: "Activities set for bidder disallowing by hierarchy structure" + def activity = Activity.getDefaultActivity([disallowActivity, allowActivity]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Save account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + } + + def "PBS auction shouldn't allow rule when gppSid not intersect"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = regsGppSid + } + + and: "Setup condition" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = [PBSUtils.randomString] + it.gppSid = conditionGppSid + } + + and: "Activities set with bidder allowed" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Set up account for allow activities" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + assert genericBidderRequest.user.eids[0].source == genericBidRequest.user.eids[0].source + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + + where: + regsGppSid | conditionGppSid + null | [USP_V1.intValue] + [USP_V1.intValue] | null + } + + def "PBS auction should allow rule when gppSid intersect"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [USP_V1.intValue] + } + + and: "Setup condition" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = null + it.gppSid = [USP_V1.intValue] + } + + and: "Activities set with bidder allowed" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Set up account for allow activities" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[DISALLOWED_COUNT_FOR_ACTIVITY_RULE] == 1 + assert metrics[DISALLOWED_COUNT_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[DISALLOWED_COUNT_FOR_GENERIC_ADAPTER] == 1 + } + + def "PBS auction should process rule when device.geo doesn't intersection"() { + given: "Generic bid request with account connection" + def accountId = PBSUtils.randomNumber as String + def bidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + it.regs.gppSid = [USP_V1.intValue] + it.device = new Device(geo: deviceGeo) + } + + and: "Setup condition" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = [PBSUtils.randomString] + it.gppSid = [USP_V1.intValue] + it.geo = conditionGeo + } + + and: "Setup activities" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Set up account for allow activities" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(bidRequest.id) + assert genericBidderRequest.user.eids[0].source == bidRequest.user.eids[0].source + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + + where: + deviceGeo | conditionGeo + null | [USA.value] + new Geo(country: USA) | null + new Geo(region: ALABAMA.abbreviation) | [USA.withState(ALABAMA)] + new Geo(country: CAN, region: ALABAMA.abbreviation) | [USA.withState(ALABAMA)] + } + + def "PBS auction should disallowed rule when device.geo intersection"() { + given: "Generic bid request with account connection" + def accountId = PBSUtils.randomNumber as String + def bidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + it.setAccountId(accountId) + it.device = new Device(geo: deviceGeo) + } + + and: "Setup activity" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = null + it.gppSid = null + it.geo = conditionGeo + } + + and: "Setup activities" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Set up account for allow activities" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + + verifyAll { + !bidderRequest.user.eids + !bidderRequest?.user?.ext?.eids + } + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[DISALLOWED_COUNT_FOR_ACTIVITY_RULE] == 1 + assert metrics[DISALLOWED_COUNT_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[DISALLOWED_COUNT_FOR_GENERIC_ADAPTER] == 1 + + where: + deviceGeo | conditionGeo + new Geo(country: USA) | [USA.value] + new Geo(country: USA, region: ALABAMA.abbreviation) | [USA.withState(ALABAMA)] + new Geo(country: USA, region: ALABAMA.abbreviation) | [CAN.withState(ONTARIO), USA.withState(ALABAMA)] + } + + def "PBS auction should process rule when regs.ext.gpc doesn't intersection with condition.gpc"() { + given: "Generic bid request with account connection" + def accountId = PBSUtils.randomNumber as String + def bidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + it.regs.ext.gpc = PBSUtils.randomNumber as String + } + + and: "Setup condition" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = null + it.gpc = PBSUtils.randomNumber as String + } + + and: "Setup activities" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Set up account for allow activities" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(bidRequest.id) + assert genericBidderRequest.user.eids[0].source == bidRequest.user.eids[0].source + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + } + + def "PBS auction should disallowed rule when regs.ext.gpc intersection with condition.gpc"() { + given: "Generic bid request with account connection" + def accountId = PBSUtils.randomNumber as String + def gpc = PBSUtils.randomNumber as String + def bidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + it.setAccountId(accountId) + it.regs.ext.gpc = gpc + } + + and: "Setup activity" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = null + it.gpc = gpc + } + + and: "Setup activities" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Set up account for allow activities" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + + verifyAll { + !bidderRequest.user.eids + !bidderRequest?.user?.ext?.eids + } + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[DISALLOWED_COUNT_FOR_ACTIVITY_RULE] == 1 + assert metrics[DISALLOWED_COUNT_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[DISALLOWED_COUNT_FOR_GENERIC_ADAPTER] == 1 + } + + def "PBS auction should process rule when header gpc doesn't intersection with condition.gpc"() { + given: "Generic bid request with account connection" + def accountId = PBSUtils.randomNumber as String + def bidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + it.regs.ext.gpc = PBSUtils.randomNumber as String + } + + and: "Setup condition" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = null + it.gppSid = null + it.gpc = PBSUtils.randomNumber as String + } + + and: "Setup activities" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Set up account for allow activities" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(bidRequest, ["Sec-GPC": "1"]) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(bidRequest.id) + assert genericBidderRequest.user.eids[0].source == bidRequest.user.eids[0].source + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + } + + def "PBS auction should disallowed rule when header gpc intersection with condition.gpc"() { + given: "Generic bid request with account connection" + def accountId = PBSUtils.randomNumber as String + def bidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + it.setAccountId(accountId) + it.regs.ext.gpc = null + } + + and: "Setup activity" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = null + it.gpc = VALID_VALUE_FOR_GPC_HEADER + } + + and: "Setup activities" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Set up account for allow activities" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + when: "PBS processes auction requests with header" + activityPbsService.sendAuctionRequest(bidRequest, ["Sec-GPC": VALID_VALUE_FOR_GPC_HEADER]) + + then: "Generic bidder request should have empty EIDS fields" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + + verifyAll { + !bidderRequest.user.eids + !bidderRequest?.user?.ext?.eids + } + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[DISALLOWED_COUNT_FOR_ACTIVITY_RULE] == 1 + assert metrics[DISALLOWED_COUNT_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[DISALLOWED_COUNT_FOR_GENERIC_ADAPTER] == 1 + } + + def "PBS auction call when privacy regulation match and rejecting should remove EIDS fields in request"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [USP_NAT_V1.intValue] + regs.gpp = SIMPLE_GPC_DISALLOW_LOGIC + } + + and: "Activities set for transmitEIDS with rejecting privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [privacyAllowRegulations] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + privacyAllowRegulations << [IAB_US_GENERAL, IAB_ALL, ALL] + } + + def "PBS auction call when privacy module contain some part of disallow logic should remove EIDS fields in request"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [USP_NAT_V1.intValue] + regs.gpp = disallowGppLogic + } + + and: "Activities set for transmitEIDS with rejecting privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + disallowGppLogic << [ + SIMPLE_GPC_DISALLOW_LOGIC, + new UspNatV1Consent.Builder().setMspaServiceProviderMode(1).build(), + new UspNatV1Consent.Builder().setSaleOptOut(1).build(), + new UspNatV1Consent.Builder().setSaleOptOutNotice(2).build(), + new UspNatV1Consent.Builder().setSharingNotice(2).build(), + new UspNatV1Consent.Builder().setSaleOptOutNotice(0).setSaleOptOut(2).build(), + new UspNatV1Consent.Builder().setSharingOptOutNotice(2).build(), + new UspNatV1Consent.Builder().setSharingOptOut(1).build(), + new UspNatV1Consent.Builder().setSharingOptOutNotice(0).setSharingOptOut(2).build(), + new UspNatV1Consent.Builder().setSharingNotice(0).setSharingOptOut(2).build(), + new UspNatV1Consent.Builder().setTargetedAdvertisingOptOutNotice(2).build(), + new UspNatV1Consent.Builder().setTargetedAdvertisingOptOut(1).build(), + new UspNatV1Consent.Builder().setTargetedAdvertisingOptOutNotice(0).setTargetedAdvertisingOptOut(2).build(), + new UspNatV1Consent.Builder().setSensitiveDataProcessingOptOutNotice(2).build(), + new UspNatV1Consent.Builder().setSensitiveDataLimitUseNotice(2).build(), + new UspNatV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 1).build(), + new UspNatV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 2).build(), + new UspNatV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 1).build(), + new UspNatV1Consent.Builder().setPersonalDataConsents(2).build(), + new UspNatV1Consent.Builder().setSensitiveDataProcessing(new UsNationalSensitiveData( + racialEthnicOrigin: 1, + religiousBeliefs: 1, + healthInfo: 1, + orientation: 1, + citizenshipStatus: 1, + unionMembership: 1, + )).build(), + new UspNatV1Consent.Builder() + .setSensitiveDataLimitUseNotice(0) + .setSensitiveDataProcessing(new UsNationalSensitiveData( + racialEthnicOrigin: 2, + religiousBeliefs: 2, + healthInfo: 2, + orientation: 2, + citizenshipStatus: 2, + geneticId: 2, + biometricId: 2, + idNumbers: 2, + accountInfo: 2, + unionMembership: 2, + communicationContents: 2 + )).build(), + new UspNatV1Consent.Builder() + .setSensitiveDataProcessingOptOutNotice(0) + .setSensitiveDataProcessing(new UsNationalSensitiveData( + racialEthnicOrigin: 2, + religiousBeliefs: 2, + healthInfo: 2, + orientation: 2, + citizenshipStatus: 2, + geneticId: 2, + biometricId: 2, + idNumbers: 2, + accountInfo: 2, + unionMembership: 2, + communicationContents: 2 + )).build(), + new UspNatV1Consent.Builder().setSensitiveDataProcessing(new UsNationalSensitiveData( + geneticId: 1, + biometricId: 1, + idNumbers: 1, + accountInfo: 1, + communicationContents: 1 + )).build(), + new UspNatV1Consent.Builder().setSensitiveDataProcessing(new UsNationalSensitiveData( + geneticId: 2, + biometricId: 2, + idNumbers: 2, + accountInfo: 2, + communicationContents: 2 + )).build() + ] + } + + def "PBS auction call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [gppSid.intValue] + regs.gpp = gppConsent + } + + and: "Activities set for transmitEIDS with rejecting privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + gppConsent | gppSid + new UspNatV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_NAT_V1 + new UspCaV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_CA_V1 + new UspVaV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_VA_V1 + new UspCoV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_CO_V1 + new UspUtV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_UT_V1 + new UspCtV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_CT_V1 + } + + def "PBS auction call when privacy modules contain allowing settings should leave EIDS fields in request"() { + given: "Default basic generic BidRequest" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [USP_NAT_V1.intValue] + regs.gpp = SIMPLE_GPC_DISALLOW_LOGIC + } + + and: "Activities set for transmitEIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + assert genericBidderRequest.user.eids[0].source == genericBidRequest.user.eids[0].source + + where: + accountGppConfig << [ + new AccountGppConfig(code: IAB_US_GENERAL, enabled: false), + new AccountGppConfig(code: IAB_US_GENERAL, config: new GppModuleConfig(skipSids: [USP_NAT_V1]), enabled: true) + ] + } + + def "PBS auction call when regs.gpp in request is allowing should leave EIDS fields in request"() { + given: "Default basic generic BidRequest" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [USP_NAT_V1.intValue] + regs.gpp = regsGpp + } + + and: "Activities set for transmitEIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + assert genericBidderRequest.user.eids[0].source == genericBidRequest.user.eids[0].source + + where: + regsGpp << ["", new UspNatV1Consent.Builder().build(), new UspNatV1Consent.Builder().setGpc(false).build()] + } + + def "PBS auction call when privacy regulation have duplicate should leave EIDS fields in request and update alerts metrics"() { + given: "Default basic generic BidRequest" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [USP_NAT_V1.intValue] + } + + and: "Activities set for transmitEIDS with privacy regulation" + def ruleUsGeneric = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([ruleUsGeneric])) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Account gpp privacy regulation configs with conflict" + def accountGppUsNatAllowConfig = new AccountGppConfig(code: IAB_US_GENERAL, config: new GppModuleConfig(skipSids: [USP_NAT_V1]), enabled: false) + def accountGppUsNatRejectConfig = new AccountGppConfig(code: IAB_US_GENERAL, config: new GppModuleConfig(skipSids: []), enabled: true) + + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppUsNatAllowConfig, accountGppUsNatRejectConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + assert genericBidderRequest.user.eids[0].source == genericBidRequest.user.eids[0].source + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ALERT_GENERAL] == 1 + } + + def "PBS auction call when privacy module contain invalid property should respond with an error"() { + given: "Default basic generic BidRequest" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [USP_NAT_V1.intValue] + regs.gpp = SIMPLE_GPC_DISALLOW_LOGIC + } + + and: "Activities set for transmitEIDS with rejecting privacy regulation" + def ruleIabAll = new ActivityRule().tap { + it.privacyRegulation = [IAB_ALL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([ruleIabAll])) + + and: "Multiple account gpp privacy regulation config" + def accountGppTfcEuConfig = new AccountGppConfig(code: IAB_TFC_EU, enabled: true) + + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppTfcEuConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Response should contain error" + def error = thrown(PrebidServerException) + assert error.statusCode == UNAUTHORIZED.code() + assert error.responseBody == "Unauthorized account id: ${accountId}" + } + + def "PBS auction call when privacy regulation don't match custom requirement should leave EIDS fields in request"() { + given: "Default basic generic BidRequest" + def gppConsent = new UspNatV1Consent.Builder().setGpc(gpcValue).build() + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [USP_NAT_V1.intValue] + regs.gpp = gppConsent + } + + and: "Activities set for transmit EIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_CUSTOM_LOGIC] + } + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration with sid skip" + def accountGppConfig = new AccountGppConfig().tap { + it.code = IAB_US_CUSTOM_LOGIC + it.enabled = true + it.config = GppModuleConfig.getDefaultModuleConfig(new ActivityConfig([TRANSMIT_EIDS], accountLogic)) + } + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + assert genericBidderRequest.user.eids[0].source == genericBidRequest.user.eids[0].source + + where: + gpcValue | accountLogic + false | LogicalRestrictedRule.generateSingleRestrictedRule(OR, [new EqualityValueRule(GPC, NOTICE_PROVIDED)]) + true | LogicalRestrictedRule.generateSingleRestrictedRule(OR, [new InequalityValueRule(GPC, NOTICE_PROVIDED)]) + true | LogicalRestrictedRule.generateSingleRestrictedRule(AND, [new EqualityValueRule(GPC, NOTICE_PROVIDED), + new EqualityValueRule(SHARING_NOTICE, NOTICE_PROVIDED)]) + } + + def "PBS auction call when privacy regulation match custom requirement should remove EIDS fields in request"() { + given: "Default basic generic BidRequest" + def accountId = PBSUtils.randomNumber as String + def generalBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.gppSid = [USP_NAT_V1.intValue] + regs.gpp = gppConsent + } + + and: "Activities set for transmit EIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_CUSTOM_LOGIC] + } + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration with sid skip" + def accountLogic = LogicalRestrictedRule.generateSingleRestrictedRule(OR, valueRules) + def accountGppConfig = new AccountGppConfig().tap { + it.code = IAB_US_CUSTOM_LOGIC + it.enabled = true + it.config = GppModuleConfig.getDefaultModuleConfig(new ActivityConfig([TRANSMIT_EIDS], accountLogic)) + } + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(generalBidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(generalBidRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + gppConsent | valueRules + new UspNatV1Consent.Builder().setSharingNotice(2).build() | [new EqualityValueRule(SHARING_NOTICE, NOTICE_NOT_PROVIDED)] + new UspNatV1Consent.Builder().setGpc(true).build() | [new EqualityValueRule(GPC, NOTICE_PROVIDED)] + new UspNatV1Consent.Builder().setGpc(false).build() | [new InequalityValueRule(GPC, NOTICE_PROVIDED)] + new UspNatV1Consent.Builder().setGpc(true).build() | [new EqualityValueRule(GPC, NOTICE_PROVIDED), + new EqualityValueRule(SHARING_NOTICE, NOTICE_NOT_PROVIDED)] + new UspNatV1Consent.Builder().setSharingNotice(2).build() | [new EqualityValueRule(GPC, NOTICE_PROVIDED), + new EqualityValueRule(SHARING_NOTICE, NOTICE_NOT_PROVIDED)] + } + + def "PBS auction call when custom privacy regulation empty and normalize is disabled should respond with an error and update metric"() { + given: "Generic BidRequest with gpp and account setup" + def gppConsent = new UspNatV1Consent.Builder().setGpc(true).build() + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + ext.prebid.trace = VERBOSE + regs.gppSid = [USP_CT_V1.intValue] + regs.gpp = gppConsent + } + + and: "Activities set with privacy regulation" + def ruleUsGeneric = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_CUSTOM_LOGIC] + } + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([ruleUsGeneric])) + + and: "Account gpp configuration with empty Custom logic" + def restrictedRule = LogicalRestrictedRule.rootLogicalRestricted + def accountGppConfig = new AccountGppConfig().tap { + it.code = IAB_US_CUSTOM_LOGIC + it.enabled = true + config = GppModuleConfig.getDefaultModuleConfig(new ActivityConfig([TRANSMIT_EIDS], restrictedRule), [USP_CT_V1], false) + } + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Existed account with gpp regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Response should contain error" + def error = thrown(PrebidServerException) + assert error.statusCode == BAD_REQUEST.code() + assert error.responseBody == "JsonLogic exception: objects must have exactly 1 key defined, found 0" + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ALERT_GENERAL] == 1 + } + + def "PBS auction call when custom privacy regulation with normalizing that match custom config should have empty EIDS fields"() { + given: "Generic BidRequest with gpp and account setup" + def accountId = PBSUtils.randomNumber as String + def generalBidRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + ext.prebid.trace = VERBOSE + regs.gppSid = [gppSid.intValue] + regs.gpp = gppStateConsent.build() + } + + and: "Activities set with privacy regulation" + def ruleUsGeneric = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_CUSTOM_LOGIC] + } + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([ruleUsGeneric])) + + and: "Activity config" + def activityConfig = new ActivityConfig([TRANSMIT_EIDS], LogicalRestrictedRule.generateSingleRestrictedRule(AND, equalityValueRules)) + + and: "Account gpp configuration with enabled normalizeFlag" + def accountGppConfig = new AccountGppConfig().tap { + it.code = IAB_US_CUSTOM_LOGIC + it.enabled = true + it.config = GppModuleConfig.getDefaultModuleConfig(activityConfig, [gppSid], true) + } + + and: "Existed account with gpp regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(generalBidRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(generalBidRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + gppSid | equalityValueRules | gppStateConsent + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_ID_NUMBERS, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(idNumbers: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_ACCOUNT_INFO, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(accountInfo: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_GEOLOCATION, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(geolocation: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_RACIAL_ETHNIC_ORIGIN, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(racialEthnicOrigin: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_COMMUNICATION_CONTENTS, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(communicationContents: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_GENETIC_ID, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(geneticId: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_BIOMETRIC_ID, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(biometricId: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_HEALTH_INFO, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(healthInfo: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_ORIENTATION, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(orientation: 2)) + USP_CA_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspCaV1Consent.Builder() + .setKnownChildSensitiveDataConsents(0, 0) + USP_CA_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspCaV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(1, 2), PBSUtils.getRandomNumber(1, 2)) + + USP_VA_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspVaV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(1, 2)) + USP_VA_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspVaV1Consent.Builder().setKnownChildSensitiveDataConsents(0) + + USP_CO_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspCoV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(1, 2)) + USP_CO_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspCoV1Consent.Builder().setKnownChildSensitiveDataConsents(0) + + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_RACIAL_ETHNIC_ORIGIN, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(racialEthnicOrigin: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_RELIGIOUS_BELIEFS, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(religiousBeliefs: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_ORIENTATION, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(orientation: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_CITIZENSHIP_STATUS, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(citizenshipStatus: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_HEALTH_INFO, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(healthInfo: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_GENETIC_ID, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(geneticId: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_BIOMETRIC_ID, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(biometricId: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_GEOLOCATION, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(geolocation: 2)) + USP_UT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspUtV1Consent.Builder().setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(1, 2)) + USP_UT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspUtV1Consent.Builder().setKnownChildSensitiveDataConsents(0) + + USP_CT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspCtV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 0, 0) + USP_CT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, CONSENT)] | new UspCtV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 2, 2) + USP_CT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspCtV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(0, 2), PBSUtils.getRandomNumber(0, 2), 1) + USP_CT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspCtV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(0, 2), 1, PBSUtils.getRandomNumber(0, 2)) + } + + def "PBS amp call when transmit EIDS activities is allowing request should leave EIDS fields field in active request and update proper metrics"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + } + + and: "Activities set with bidder allowed" + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.defaultActivity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Saved account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 + } + + def "PBS amp call when transmit EIDS activities is rejecting request should remove EIDS fields field in active request and update disallowed metrics"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + } + + and: "Allow activities setup" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Saved account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[DISALLOWED_COUNT_FOR_ACTIVITY_RULE] == 1 + assert metrics[DISALLOWED_COUNT_FOR_GENERIC_ADAPTER] == 1 + } + + def "PBS amp call when default activity setting set to false should remove EIDS fields from request"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + } + + and: "Allow activities setup" + def activity = new Activity(defaultAction: false) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Saved account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + } + + def "PBS amp call when bidder allowed activities have empty condition type should skip this rule and emit an error"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + } + + and: "Activities set with have empty condition type" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(conditions, isAllowed)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Saved account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Response should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "Activity configuration for account ${accountId} " + + "contains conditional rule with empty array").size() == 1 + + where: + conditions | isAllowed + new Condition(componentType: []) | true + new Condition(componentType: []) | false + new Condition(componentName: []) | true + new Condition(componentName: []) | false + } + + def "PBS amp call when first rule allowing in activities should leave EIDS fields in request"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + } + + and: "Activity rules with same priority" + def allowActivity = new ActivityRule(condition: Condition.baseCondition, allow: true) + def disallowActivity = new ActivityRule(condition: Condition.baseCondition, allow: false) + + and: "Activities set for bidder allowed by hierarchy structure" + def activity = Activity.getDefaultActivity([allowActivity, disallowActivity]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Save account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + } + + def "PBS amp call when first rule disallowing in activities should remove EIDS fields in request"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + } + + and: "Activities set for actions with Generic bidder rejected by hierarchy setup" + def disallowActivity = new ActivityRule(condition: Condition.baseCondition, allow: false) + def allowActivity = new ActivityRule(condition: Condition.baseCondition, allow: true) + + and: "Activities set for bidder disallowing by hierarchy structure" + def activity = Activity.getDefaultActivity([disallowActivity, allowActivity]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Saved account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + } + + def "PBS amp should disallowed rule when header.gpc intersection with condition.gpc"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId).tap { + regs.ext.gpc = null + } + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + } + + and: "Allow activities setup" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = null + it.gpc = VALID_VALUE_FOR_GPC_HEADER + } + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Saved account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest, ["Sec-GPC": VALID_VALUE_FOR_GPC_HEADER]) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[DISALLOWED_COUNT_FOR_ACTIVITY_RULE] == 1 + assert metrics[DISALLOWED_COUNT_FOR_GENERIC_ADAPTER] == 1 + } + + def "PBS amp should allowed rule when gpc header doesn't intersection with condition.gpc"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + } + + and: "Allow activities setup" + def condition = Condition.baseCondition.tap { + it.componentType = null + it.componentName = null + it.gpc = PBSUtils.randomNumber as String + } + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Saved account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest, ["Sec-GPC": VALID_VALUE_FOR_GPC_HEADER]) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 + } + + def "PBS amp call when privacy regulation match and rejecting should remove EIDS fields in request"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = USP_NAT_V1.value + it.consentString = SIMPLE_GPC_DISALLOW_LOGIC + it.consentType = GPP + } + + and: "Activities set for transmitEIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [privacyAllowRegulations] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + privacyAllowRegulations << [IAB_US_GENERAL, IAB_ALL, ALL] + } + + def "PBS amp call when privacy module contain some part of disallow logic should remove EIDS fields in request"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = USP_NAT_V1.value + it.consentString = disallowGppLogic + it.consentType = GPP + } + + and: "Activities set for transmitEIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + disallowGppLogic << [ + SIMPLE_GPC_DISALLOW_LOGIC, + new UspNatV1Consent.Builder().setMspaServiceProviderMode(1).build(), + new UspNatV1Consent.Builder().setSaleOptOut(1).build(), + new UspNatV1Consent.Builder().setSaleOptOutNotice(2).build(), + new UspNatV1Consent.Builder().setSharingNotice(2).build(), + new UspNatV1Consent.Builder().setSaleOptOutNotice(0).setSaleOptOut(2).build(), + new UspNatV1Consent.Builder().setSharingOptOutNotice(2).build(), + new UspNatV1Consent.Builder().setSharingOptOut(1).build(), + new UspNatV1Consent.Builder().setSharingOptOutNotice(0).setSharingOptOut(2).build(), + new UspNatV1Consent.Builder().setSharingNotice(0).setSharingOptOut(2).build(), + new UspNatV1Consent.Builder().setTargetedAdvertisingOptOutNotice(2).build(), + new UspNatV1Consent.Builder().setTargetedAdvertisingOptOut(1).build(), + new UspNatV1Consent.Builder().setTargetedAdvertisingOptOutNotice(0).setTargetedAdvertisingOptOut(2).build(), + new UspNatV1Consent.Builder().setSensitiveDataProcessingOptOutNotice(2).build(), + new UspNatV1Consent.Builder().setSensitiveDataLimitUseNotice(2).build(), + new UspNatV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 1).build(), + new UspNatV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 2).build(), + new UspNatV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 1).build(), + new UspNatV1Consent.Builder().setPersonalDataConsents(2).build(), + new UspNatV1Consent.Builder().setSensitiveDataProcessing(new UsNationalSensitiveData( + racialEthnicOrigin: 1, + religiousBeliefs: 1, + healthInfo: 1, + orientation: 1, + citizenshipStatus: 1, + unionMembership: 1, + )).build(), + new UspNatV1Consent.Builder() + .setSensitiveDataLimitUseNotice(0) + .setSensitiveDataProcessing(new UsNationalSensitiveData( + racialEthnicOrigin: 2, + religiousBeliefs: 2, + healthInfo: 2, + orientation: 2, + citizenshipStatus: 2, + geneticId: 2, + biometricId: 2, + idNumbers: 2, + accountInfo: 2, + unionMembership: 2, + communicationContents: 2 + )).build(), + new UspNatV1Consent.Builder() + .setSensitiveDataProcessingOptOutNotice(0) + .setSensitiveDataProcessing(new UsNationalSensitiveData( + racialEthnicOrigin: 2, + religiousBeliefs: 2, + healthInfo: 2, + orientation: 2, + citizenshipStatus: 2, + geneticId: 2, + biometricId: 2, + idNumbers: 2, + accountInfo: 2, + unionMembership: 2, + communicationContents: 2 + )).build(), + new UspNatV1Consent.Builder().setSensitiveDataProcessing(new UsNationalSensitiveData( + geneticId: 1, + biometricId: 1, + idNumbers: 1, + accountInfo: 1, + communicationContents: 1 + )).build(), + new UspNatV1Consent.Builder().setSensitiveDataProcessing(new UsNationalSensitiveData( + geneticId: 2, + biometricId: 2, + idNumbers: 2, + accountInfo: 2, + communicationContents: 2 + )).build() + ] + } + + def "PBS amp call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = gppSid.value + it.consentString = gppConsent + it.consentType = GPP + } + + and: "Activities set for transmitEIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + gppConsent | gppSid + new UspNatV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_NAT_V1 + new UspCaV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_CA_V1 + new UspVaV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_VA_V1 + new UspCoV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_CO_V1 + new UspUtV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_UT_V1 + new UspCtV1Consent.Builder().setMspaServiceProviderMode(1).build() | USP_CT_V1 + } + + def "PBS amp call when privacy modules contain allowing settings should leave EIDS fields in request"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = USP_NAT_V1.value + it.consentString = SIMPLE_GPC_DISALLOW_LOGIC + it.consentType = GPP + } + + and: "Activities set for transmitEIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + where: + accountGppConfig << [ + new AccountGppConfig(code: IAB_US_GENERAL, enabled: false), + new AccountGppConfig(code: IAB_US_GENERAL, config: new GppModuleConfig(skipSids: [USP_NAT_V1]), enabled: true) + ] + } + + def "PBS amp call when regs.gpp in request is allowing should leave EIDS fields in request"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = USP_NAT_V1.value + it.consentString = regsGpp + it.consentType = GPP + } + + and: "Activities set for transmitEIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + where: + regsGpp << ["", new UspNatV1Consent.Builder().build(), new UspNatV1Consent.Builder().setGpc(false).build()] + } + + def "PBS amp call when privacy regulation have duplicate should leave EIDS fields in request and update alerts metrics"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = USP_NAT_V1.value + it.consentString = "" + it.consentType = GPP + } + + and: "Activities set for transmitEIDS with privacy regulation" + def ruleUsGeneric = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([ruleUsGeneric])) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Account gpp privacy regulation configs with conflict" + def accountGppUsNatAllowConfig = new AccountGppConfig(code: IAB_US_GENERAL, config: new GppModuleConfig(skipSids: [USP_NAT_V1]), enabled: false) + def accountGppUsNatRejectConfig = new AccountGppConfig(code: IAB_US_GENERAL, config: new GppModuleConfig(skipSids: []), enabled: true) + + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppUsNatAllowConfig, accountGppUsNatRejectConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ALERT_GENERAL] == 1 + } + + def "PBS amp call when privacy module contain invalid property should respond with an error"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = USP_NAT_V1.value + it.consentString = SIMPLE_GPC_DISALLOW_LOGIC + it.consentType = GPP + } + + def ruleIabAll = new ActivityRule().tap { + it.privacyRegulation = [IAB_ALL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([ruleIabAll])) + + and: "Multiple account gpp privacy regulation config" + def accountGppTfcEuConfig = new AccountGppConfig(code: IAB_TFC_EU, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppTfcEuConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Response should contain error" + def error = thrown(PrebidServerException) + assert error.statusCode == UNAUTHORIZED.code() + assert error.responseBody == "Unauthorized account id: ${accountId}" + } + + def "PBS amp call when privacy regulation don't match custom requirement should leave EIDS fields in request"() { + given: "Store bid request with link for account" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account and gpp" + def gppConsent = new UspNatV1Consent.Builder().setGpc(gpcValue).build() + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = USP_NAT_V1.value + it.consentString = gppConsent + it.consentType = GPP + } + + and: "Activities set for transmit EIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_CUSTOM_LOGIC] + } + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration with sid skip" + def accountGppConfig = new AccountGppConfig().tap { + it.code = IAB_US_CUSTOM_LOGIC + it.enabled = true + it.config = GppModuleConfig.getDefaultModuleConfig(new ActivityConfig([TRANSMIT_EIDS], accountLogic)) + } + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + where: + gpcValue | accountLogic + false | LogicalRestrictedRule.generateSingleRestrictedRule(OR, [new EqualityValueRule(GPC, NOTICE_PROVIDED)]) + true | LogicalRestrictedRule.generateSingleRestrictedRule(OR, [new InequalityValueRule(GPC, NOTICE_PROVIDED)]) + true | LogicalRestrictedRule.generateSingleRestrictedRule(AND, [new EqualityValueRule(GPC, NOTICE_PROVIDED), + new EqualityValueRule(SHARING_NOTICE, NOTICE_PROVIDED)]) + } + + def "PBS amp call when privacy regulation match custom requirement should remove EIDS fields from request"() { + given: "Store bid request with gpp string and link for account" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account and gppSid" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = USP_NAT_V1.value + it.consentString = gppConsent + it.consentType = GPP + } + + and: "Activities set for transmit EIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_CUSTOM_LOGIC] + } + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration with sid skip" + def accountLogic = LogicalRestrictedRule.generateSingleRestrictedRule(OR, valueRules) + def accountGppConfig = new AccountGppConfig().tap { + it.code = IAB_US_CUSTOM_LOGIC + it.enabled = true + it.config = GppModuleConfig.getDefaultModuleConfig(new ActivityConfig([TRANSMIT_EIDS], accountLogic)) + } + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + gppConsent | valueRules + new UspNatV1Consent.Builder().setSharingNotice(2).build() | [new EqualityValueRule(SHARING_NOTICE, NOTICE_NOT_PROVIDED)] + new UspNatV1Consent.Builder().setGpc(true).build() | [new EqualityValueRule(GPC, NOTICE_PROVIDED)] + new UspNatV1Consent.Builder().setGpc(false).build() | [new InequalityValueRule(GPC, NOTICE_PROVIDED)] + new UspNatV1Consent.Builder().setGpc(true).build() | [new EqualityValueRule(GPC, NOTICE_PROVIDED), + new EqualityValueRule(SHARING_NOTICE, NOTICE_NOT_PROVIDED)] + new UspNatV1Consent.Builder().setSharingNotice(2).build() | [new EqualityValueRule(GPC, NOTICE_PROVIDED), + new EqualityValueRule(SHARING_NOTICE, NOTICE_NOT_PROVIDED)] + } + + def "PBS amp call when custom privacy regulation empty and normalize is disabled should respond with an error and update metric"() { + given: "Store bid request with link for account" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account and gpp string" + def gppConsent = new UspNatV1Consent.Builder().setGpc(true).build() + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = USP_NAT_V1.intValue + it.consentString = gppConsent + it.consentType = GPP + } + + and: "Activities set with privacy regulation" + def ruleUsGeneric = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_CUSTOM_LOGIC] + } + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([ruleUsGeneric])) + + and: "Account gpp configuration with empty Custom logic" + def restrictedRule = LogicalRestrictedRule.rootLogicalRestricted + def accountGppConfig = new AccountGppConfig().tap { + it.code = IAB_US_CUSTOM_LOGIC + it.enabled = true + it.config = GppModuleConfig.getDefaultModuleConfig(new ActivityConfig([TRANSMIT_EIDS], restrictedRule), [USP_NAT_V1], false) + } + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Existed account with gpp regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp requests" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Response should contain error" + def error = thrown(PrebidServerException) + assert error.statusCode == BAD_REQUEST.code() + assert error.responseBody == "Invalid account configuration: JsonLogic exception: " + + "objects must have exactly 1 key defined, found 0" + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[ALERT_GENERAL] == 1 + } + + def "PBS amp call when custom privacy regulation with normalizing should change request consent and call to bidder"() { + given: "Store bid request with gpp string and link for account" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = givenBidRequestWithAccountAndEidsData(accountId) + + and: "amp request with link to account and gppSid" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = gppSid.intValue + it.consentString = gppStateConsent.build() + it.consentType = GPP + } + + and: "Activities set with privacy regulation" + def ruleUsGeneric = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_CUSTOM_LOGIC] + } + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([ruleUsGeneric])) + + and: "Activity config" + def activityConfig = new ActivityConfig([TRANSMIT_EIDS], LogicalRestrictedRule.generateSingleRestrictedRule(AND, equalityValueRules)) + + and: "Account gpp configuration with enabled normalizeFlag" + def accountGppConfig = new AccountGppConfig().tap { + it.code = IAB_US_CUSTOM_LOGIC + it.enabled = true + it.config = GppModuleConfig.getDefaultModuleConfig(activityConfig, [gppSid], true) + } + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Existed account with gpp regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp requests" + activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have empty EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { + !genericBidderRequest.user.eids + !genericBidderRequest.user?.ext?.eids + } + + where: + gppSid | equalityValueRules | gppStateConsent + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_ID_NUMBERS, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(idNumbers: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_ACCOUNT_INFO, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(accountInfo: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_GEOLOCATION, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(geolocation: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_RACIAL_ETHNIC_ORIGIN, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(racialEthnicOrigin: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_COMMUNICATION_CONTENTS, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(communicationContents: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_GENETIC_ID, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(geneticId: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_BIOMETRIC_ID, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(biometricId: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_HEALTH_INFO, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(healthInfo: 2)) + USP_CA_V1 | [new EqualityValueRule(SENSITIVE_DATA_ORIENTATION, CONSENT)] | new UspCaV1Consent.Builder() + .setSensitiveDataProcessing(new UsCaliforniaSensitiveData(orientation: 2)) + USP_CA_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspCaV1Consent.Builder() + .setKnownChildSensitiveDataConsents(0, 0) + USP_CA_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspCaV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(1, 2), PBSUtils.getRandomNumber(1, 2)) + + USP_VA_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspVaV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(1, 2)) + USP_VA_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspVaV1Consent.Builder().setKnownChildSensitiveDataConsents(0) + + USP_CO_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspCoV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(1, 2)) + USP_CO_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspCoV1Consent.Builder().setKnownChildSensitiveDataConsents(0) + + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_RACIAL_ETHNIC_ORIGIN, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(racialEthnicOrigin: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_RELIGIOUS_BELIEFS, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(religiousBeliefs: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_ORIENTATION, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(orientation: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_CITIZENSHIP_STATUS, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(citizenshipStatus: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_HEALTH_INFO, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(healthInfo: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_GENETIC_ID, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(geneticId: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_BIOMETRIC_ID, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(biometricId: 2)) + USP_UT_V1 | [new EqualityValueRule(SENSITIVE_DATA_GEOLOCATION, CONSENT)] | new UspUtV1Consent.Builder() + .setSensitiveDataProcessing(new UsUtahSensitiveData(geolocation: 2)) + USP_UT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspUtV1Consent.Builder().setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(1, 2)) + USP_UT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspUtV1Consent.Builder().setKnownChildSensitiveDataConsents(0) + + USP_CT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NOT_APPLICABLE), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NOT_APPLICABLE)] | new UspCtV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 0, 0) + USP_CT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, CONSENT)] | new UspCtV1Consent.Builder().setKnownChildSensitiveDataConsents(0, 2, 2) + USP_CT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspCtV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(0, 2), PBSUtils.getRandomNumber(0, 2), 1) + USP_CT_V1 | [new EqualityValueRule(CHILD_CONSENTS_BELOW_13, NO_CONSENT), + new EqualityValueRule(CHILD_CONSENTS_FROM_13_TO_16, NO_CONSENT)] | new UspCtV1Consent.Builder() + .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(0, 2), 1, PBSUtils.getRandomNumber(0, 2)) + } + + private static BidRequest givenBidRequestWithAccountAndEidsData(String accountId) { + BidRequest.getDefaultBidRequest().tap { + it.setAccountId(accountId) + it.ext.prebid.trace = VERBOSE + it.user = User.defaultUser + it.user.eids = [Eid.defaultEid] + it.user.ext = new UserExt(eids: [Eid.defaultEid]) + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy index 57cffda6ae7..5cd227ccef1 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy @@ -1,11 +1,15 @@ package org.prebid.server.functional.tests.privacy +import org.prebid.server.functional.model.config.AccountGdprConfig import org.prebid.server.functional.model.config.AccountGppConfig import org.prebid.server.functional.model.config.ActivityConfig import org.prebid.server.functional.model.config.EqualityValueRule import org.prebid.server.functional.model.config.InequalityValueRule import org.prebid.server.functional.model.config.LogicalRestrictedRule import org.prebid.server.functional.model.config.GppModuleConfig +import org.prebid.server.functional.model.config.Purpose +import org.prebid.server.functional.model.config.PurposeConfig +import org.prebid.server.functional.model.config.PurposeEid import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.auction.Activity import org.prebid.server.functional.model.request.auction.ActivityRule @@ -128,8 +132,8 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 - assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 2 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 2 } def "PBS auction call when transmit UFPD activities is rejecting requests should remove UFPD fields in request and update disallowed metrics"() { @@ -377,8 +381,8 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 - assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 2 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 2 where: regsGppSid | conditionGppSid @@ -404,7 +408,6 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(condition, false)]) def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_UFPD, activity) - and: "Flush metrics" flushMetrics(activityPbsService) @@ -446,7 +449,6 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def bidRequest = givenBidRequestWithAccountAndUfpdData(accountId).tap { it.regs.gppSid = [USP_V1.intValue] - it.ext.prebid.trace = VERBOSE it.device = new Device(geo: deviceGeo) } @@ -494,8 +496,8 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 - assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 2 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 2 where: deviceGeo | conditionGeo @@ -510,7 +512,6 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def bidRequest = givenBidRequestWithAccountAndUfpdData(accountId).tap { it.setAccountId(accountId) - it.ext.prebid.trace = VERBOSE it.device = new Device(geo: deviceGeo) } @@ -572,7 +573,6 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { given: "Generic bid request with account connection" def accountId = PBSUtils.randomNumber as String def bidRequest = givenBidRequestWithAccountAndUfpdData(accountId).tap { - it.ext.prebid.trace = VERBOSE it.regs.ext.gpc = PBSUtils.randomNumber as String } @@ -619,8 +619,8 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 - assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 2 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 2 } def "PBS auction should disallowed rule when regs.ext.gpc intersection with condition.gpc"() { @@ -629,7 +629,6 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def gpc = PBSUtils.randomNumber as String def bidRequest = givenBidRequestWithAccountAndUfpdData(accountId).tap { it.setAccountId(accountId) - it.ext.prebid.trace = VERBOSE it.regs.ext.gpc = gpc } @@ -684,7 +683,6 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { given: "Generic bid request with account connection" def accountId = PBSUtils.randomNumber as String def bidRequest = givenBidRequestWithAccountAndUfpdData(accountId).tap { - it.ext.prebid.trace = VERBOSE it.regs.ext.gpc = PBSUtils.randomNumber as String } @@ -732,8 +730,8 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 - assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 2 + assert metrics[ACTIVITY_PROCESSED_RULES_FOR_ACCOUNT.formatted(accountId)] == 2 } def "PBS auction should disallowed rule when header gpc intersection with condition.gpc"() { @@ -741,7 +739,6 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def bidRequest = givenBidRequestWithAccountAndUfpdData(accountId).tap { it.setAccountId(accountId) - it.ext.prebid.trace = VERBOSE it.regs.ext.gpc = null } @@ -1528,7 +1525,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 2 } def "PBS amp call when transmit UFPD activities is rejecting request should remove UFPD fields field in active request and update disallowed metrics"() { @@ -1885,7 +1882,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 1 + assert metrics[ACTIVITY_RULES_PROCESSED_COUNT] == 2 } def "PBS amp call when privacy regulation match and rejecting should remove UFPD fields in request"() { @@ -2674,7 +2671,56 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { .setKnownChildSensitiveDataConsents(PBSUtils.getRandomNumber(0, 2), 1, PBSUtils.getRandomNumber(0, 2)) } - private BidRequest givenBidRequestWithAccountAndUfpdData(String accountId) { + def "PBS auction call when transmit UFPD activities is rejecting requests with activityTransition false should remove only UFPD fields in request"() { + given: "Default Generic BidRequests with UFPD fields and account id" + def accountId = PBSUtils.randomNumber as String + def genericBidRequest = givenBidRequestWithAccountAndUfpdData(accountId) + + and: "Allow activities setup" + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, false)]) + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_UFPD, activity as Activity) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + and: "Save account config with allow activities into DB" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities).tap { + it.config.privacy.gdpr = new AccountGdprConfig(purposes: [(Purpose.P4): new PurposeConfig(eid: new PurposeEid(activityTransition: false))]) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsService.sendAuctionRequest(genericBidRequest) + + then: "Generic bidder request should have empty UFPD fields" + def genericBidderRequest = bidder.getBidderRequest(genericBidRequest.id) + + verifyAll { + !genericBidderRequest.device.didsha1 + !genericBidderRequest.device.didmd5 + !genericBidderRequest.device.dpidsha1 + !genericBidderRequest.device.ifa + !genericBidderRequest.device.macsha1 + !genericBidderRequest.device.macmd5 + !genericBidderRequest.device.dpidmd5 + !genericBidderRequest.user.id + !genericBidderRequest.user.buyeruid + !genericBidderRequest.user.yob + !genericBidderRequest.user.gender + !genericBidderRequest.user.data + } + + and: "Eids fields should have original data" + assert genericBidderRequest.user.eids == genericBidRequest.user.eids + + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[DISALLOWED_COUNT_FOR_ACTIVITY_RULE] == 1 + assert metrics[DISALLOWED_COUNT_FOR_ACCOUNT.formatted(accountId)] == 1 + assert metrics[DISALLOWED_COUNT_FOR_GENERIC_ADAPTER] == 1 + } + + private static BidRequest givenBidRequestWithAccountAndUfpdData(String accountId) { BidRequest.getDefaultBidRequest().tap { it.setAccountId(accountId) it.ext.prebid.trace = VERBOSE @@ -2688,6 +2734,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { dpidmd5 = PBSUtils.randomString } it.user = User.defaultUser + it.user.customdata = PBSUtils.randomString it.user.eids = [Eid.defaultEid] it.user.data = [new Data(name: PBSUtils.randomString)] it.user.buyeruid = PBSUtils.randomString diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy index 8a774d94967..686e253c93c 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy @@ -8,7 +8,10 @@ import org.prebid.server.functional.model.config.AccountDsaConfig import org.prebid.server.functional.model.config.AccountGdprConfig import org.prebid.server.functional.model.config.AccountGppConfig import org.prebid.server.functional.model.config.AccountPrivacyConfig +import org.prebid.server.functional.model.config.Purpose import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.mock.services.vendorlist.VendorListResponse +import org.prebid.server.functional.model.privacy.EnforcementRequirement import org.prebid.server.functional.model.request.amp.AmpRequest import org.prebid.server.functional.model.request.amp.ConsentType import org.prebid.server.functional.model.request.auction.AllowActivities @@ -32,6 +35,10 @@ import spock.lang.Shared import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PurposeEnforcement.BASIC +import static org.prebid.server.functional.model.config.PurposeEnforcement.FULL +import static org.prebid.server.functional.model.config.PurposeEnforcement.NO +import static org.prebid.server.functional.model.mock.services.vendorlist.VendorListResponse.getDefaultVendorListResponse import static org.prebid.server.functional.model.request.amp.ConsentType.GPP import static org.prebid.server.functional.model.request.amp.ConsentType.TCF_2 import static org.prebid.server.functional.model.request.amp.ConsentType.US_PRIVACY @@ -40,6 +47,10 @@ import static org.prebid.server.functional.model.response.cookiesync.UserSyncInf import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.BASIC_ADS +import static org.prebid.server.functional.util.privacy.TcfConsent.RestrictionType.REQUIRE_CONSENT +import static org.prebid.server.functional.util.privacy.TcfConsent.RestrictionType.REQUIRE_LEGITIMATE_INTEREST +import static org.prebid.server.functional.util.privacy.TcfConsent.RestrictionType.UNDEFINED +import static org.prebid.server.functional.util.privacy.TcfConsent.TcfPolicyVersion.TCF_POLICY_V2 abstract class PrivacyBaseSpec extends BaseSpec { @@ -51,14 +62,28 @@ abstract class PrivacyBaseSpec extends BaseSpec { "gdpr.host-vendor-id" : GENERIC_VENDOR_ID as String, "adapters.generic.ccpa-enforced" : "true"]) - private static final Map GENERIC_COOKIE_SYNC_CONFIG = ["adapters.${GENERIC.value}.usersync.${REDIRECT.value}.url" : "$networkServiceContainer.rootUri/generic-usersync".toString(), + protected static final Map GENERIC_COOKIE_SYNC_CONFIG = ["adapters.${GENERIC.value}.usersync.${REDIRECT.value}.url" : "$networkServiceContainer.rootUri/generic-usersync".toString(), "adapters.${GENERIC.value}.usersync.${REDIRECT.value}.support-cors": false.toString()] private static final Map OPENX_COOKIE_SYNC_CONFIG = ["adaptrs.${OPENX.value}.enabled" : "true", "adapters.${OPENX.value}.usersync.cookie-family-name": OPENX.value] private static final Map OPENX_CONFIG = ["adapters.${OPENX.value}.endpoint": "$networkServiceContainer.rootUri/auction".toString(), "adapters.${OPENX.value}.enabled" : 'true'] - static final Map GDPR_VENDOR_LIST_CONFIG = ["gdpr.vendorlist.v2.http-endpoint-template": "$networkServiceContainer.rootUri/v2/vendor-list.json".toString(), + protected static final Map GDPR_VENDOR_LIST_CONFIG = ["gdpr.vendorlist.v2.http-endpoint-template": "$networkServiceContainer.rootUri/v2/vendor-list.json".toString(), "gdpr.vendorlist.v3.http-endpoint-template": "$networkServiceContainer.rootUri/v3/vendor-list.json".toString()] + protected static final Map SETTING_CONFIG = ["settings.enforce-valid-account": 'true'] + protected static final Map GENERIC_VENDOR_CONFIG = ["adapters.generic.meta-info.vendor-id": GENERIC_VENDOR_ID as String, + "gdpr.host-vendor-id" : GENERIC_VENDOR_ID as String, + "adapters.generic.ccpa-enforced" : "true"] + + @Shared + protected static final int PURPOSES_ONLY_GVL_VERSION = PBSUtils.getRandomNumber(0, 4095) + @Shared + protected static final int LEG_INT_PURPOSES_ONLY_GVL_VERSION = PBSUtils.getRandomNumberWithExclusion(PURPOSES_ONLY_GVL_VERSION, 0, 4095) + @Shared + protected static final int LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION = PBSUtils.getRandomNumberWithExclusion([PURPOSES_ONLY_GVL_VERSION, LEG_INT_PURPOSES_ONLY_GVL_VERSION], 0, 4095) + @Shared + protected static final int PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION = PBSUtils.getRandomNumberWithExclusion([PURPOSES_ONLY_GVL_VERSION, LEG_INT_PURPOSES_ONLY_GVL_VERSION, LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION], 0, 4095) + static final Map RETRY_POLICY_EXPONENTIAL_CONFIG = [ "gdpr.vendorlist.v2.retry-policy.exponential-backoff.delay-millis" : 1 as String, "gdpr.vendorlist.v2.retry-policy.exponential-backoff.max-delay-millis": 1 as String, @@ -70,19 +95,23 @@ abstract class PrivacyBaseSpec extends BaseSpec { private static final PbsPgConfig pgConfig = new PbsPgConfig(networkServiceContainer) protected static final Map PBS_CONFIG = OPENX_CONFIG + OPENX_COOKIE_SYNC_CONFIG + - GENERIC_COOKIE_SYNC_CONFIG + pgConfig.properties + GDPR_VENDOR_LIST_CONFIG + SETTING_CONFIG + GENERIC_COOKIE_SYNC_CONFIG + pgConfig.properties + GDPR_VENDOR_LIST_CONFIG + SETTING_CONFIG + GENERIC_VENDOR_CONFIG protected static final String VALID_VALUE_FOR_GPC_HEADER = "1" protected static final GppConsent SIMPLE_GPC_DISALLOW_LOGIC = new UspNatV1Consent.Builder().setGpc(true).build() protected static final VendorList vendorListResponse = new VendorList(networkServiceContainer) @Shared - protected PrebidServerService activityPbsService = pbsServiceFactory.getService(PBS_CONFIG) + protected final PrebidServerService privacyPbsService = pbsServiceFactory.getService(GDPR_VENDOR_LIST_CONFIG + + GENERIC_COOKIE_SYNC_CONFIG + GENERIC_VENDOR_CONFIG) - void setup() { + @Shared + protected final PrebidServerService activityPbsService = pbsServiceFactory.getService(PBS_CONFIG) + + def setupSpec() { vendorListResponse.setResponse() } - void cleanup() { + def cleanupSpec() { vendorListResponse.reset() } @@ -188,4 +217,318 @@ abstract class PrivacyBaseSpec extends BaseSpec { def accountConfig = new AccountConfig(cookieSync: cookieSyncConfig, privacy: privacy) new Account(uuid: accountId, config: accountConfig) } + + protected static String getVendorListPath(Integer gvlVersion) { + "/app/prebid-server/data/vendorlist-v${TCF_POLICY_V2.vendorListVersion}/${gvlVersion}.json" + } + + protected static List getBasicTcfCompanyBasedEnforcementRequirements(Purpose purpose) { + [new EnforcementRequirement(purpose: purpose, enforcePurpose: BASIC, enforceVendor: false), + new EnforcementRequirement(purpose: purpose, enforcePurpose: NO, enforceVendor: false), + new EnforcementRequirement(purpose: purpose, enforcePurpose: NO, enforceVendor: true, vendorConsentBitField: GENERIC_VENDOR_ID) + ] + } + + protected static List getBasicTcfLegalBasedEnforcementRequirements(Purpose purpose) { + [new EnforcementRequirement(purpose: purpose, enforcePurpose: BASIC, enforceVendor: true, vendorConsentBitField: GENERIC_VENDOR_ID), + new EnforcementRequirement(purpose: purpose, vendorExceptions: [GENERIC]) + ] + } + + protected static List getBasicTcfCompanySoftVendorExceptionsRequirements(Purpose purpose) { + [new EnforcementRequirement(purpose: purpose, enforcePurpose: BASIC, vendorExceptions: [GENERIC]), + new EnforcementRequirement(purpose: purpose, enforcePurpose: NO, vendorExceptions: [GENERIC])] + } + + protected static List getBasicTcfLegalPurposesLITEnforcementRequirements(Purpose purpose) { + [new EnforcementRequirement(purpose: purpose, enforcePurpose: BASIC, purposesLITransparency: true)] + } + + protected static List getFullTcfLegalEnforcementRequirements(Purpose purpose, boolean isPurposeExcludedAndListRandom = false) { + [new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + restrictionType: [REQUIRE_CONSENT, UNDEFINED], + vendorIdGvl: GENERIC_VENDOR_ID, + enforcePurpose: FULL, + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_ONLY_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST, UNDEFINED], + purposesLITransparency: true, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_PURPOSES_ONLY_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST, UNDEFINED], + purposesLITransparency: true, + enforceVendor: false, + vendorListVersion: LEG_INT_PURPOSES_ONLY_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + purposesLITransparency: true, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + purposesLITransparency: true, + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST], + purposesLITransparency: true, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST], + purposesLITransparency: true, + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + purposesLITransparency: true, + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + purposesLITransparency: true, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST], + purposesLITransparency: true, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST], + purposesLITransparency: true, + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION) + ] + } + + protected static List getFullTcfLegalEnforcementRequirementsRandomlyWithExcludePurpose(Purpose purpose) { + getFullTcfLegalEnforcementRequirements(purpose, true) + } + + protected static List getFullTcfCompanyEnforcementRequirements(Purpose purpose, boolean isPurposeExcludedAndListRandom = false) { + [new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + restrictionType: [REQUIRE_CONSENT, UNDEFINED], + vendorIdGvl: GENERIC_VENDOR_ID, + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: PURPOSES_ONLY_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + restrictionType: [REQUIRE_CONSENT], + vendorIdGvl: GENERIC_VENDOR_ID, + enforcePurpose: FULL, + enforceVendor: false, + vendorListVersion: PURPOSES_ONLY_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + restrictionType: [REQUIRE_CONSENT], + vendorIdGvl: GENERIC_VENDOR_ID, + enforcePurpose: NO, + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_ONLY_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST, UNDEFINED], + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: LEG_INT_PURPOSES_ONLY_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST, UNDEFINED], + enforcePurpose: NO, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_PURPOSES_ONLY_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforcePurpose: NO, + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforcePurpose: NO, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST], + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST], + enforcePurpose: NO, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + enforcePurpose: NO, + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforcePurpose: NO, + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforcePurpose: NO, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + enforcePurpose: NO, + vendorConsentBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST], + enforcePurpose: NO, + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_LEGITIMATE_INTEREST], + enforcePurpose: NO, + vendorLegitimateInterestBitField: GENERIC_VENDOR_ID, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + enforcePurpose: NO, + enforceVendor: false)] + } + + protected static List getFullTcfCompanyEnforcementRequirementsRandomlyWithExcludePurpose(Purpose purpose) { + getFullTcfCompanyEnforcementRequirements(purpose, true) + } + + protected static String getVendorListContent(boolean includePurposes, boolean includeLegIntPurposes, boolean includeFlexiblePurposes) { + def purposeValues = TcfConsent.PurposeId.values().value + def vendor = VendorListResponse.Vendor.getDefaultVendor(GENERIC_VENDOR_ID).tap { + purposes = includePurposes ? purposeValues : [] + legIntPurposes = includeLegIntPurposes ? purposeValues : [] + flexiblePurposes = includeFlexiblePurposes ? purposeValues : [] + specialPurposes = [] + features = [] + specialFeatures = [] + } + encode(defaultVendorListResponse.tap { + it.tcfPolicyVersion = TCF_POLICY_V2.vendorListVersion + it.vendors = [(GENERIC_VENDOR_ID): vendor] + }) + } + + private static Purpose getRandomPurposeWithExclusion(Purpose excludeFromRandom) { + def availablePurposes = Purpose.values().toList() - excludeFromRandom + availablePurposes.shuffled().first() + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/TcfBasicTransmitEidsActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/TcfBasicTransmitEidsActivitiesSpec.groovy new file mode 100644 index 00000000000..64e28cf3193 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/TcfBasicTransmitEidsActivitiesSpec.groovy @@ -0,0 +1,399 @@ +package org.prebid.server.functional.tests.privacy + +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.config.AccountGdprConfig +import org.prebid.server.functional.model.request.auction.Activity +import org.prebid.server.functional.model.request.auction.ActivityRule +import org.prebid.server.functional.model.request.auction.AllowActivities +import org.prebid.server.functional.model.request.auction.Condition +import org.prebid.server.functional.model.request.auction.Eid +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.util.privacy.TcfUtils + +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.config.Purpose.P1 +import static org.prebid.server.functional.model.config.Purpose.P2 +import static org.prebid.server.functional.model.config.Purpose.P3 +import static org.prebid.server.functional.model.config.Purpose.P4 +import static org.prebid.server.functional.model.config.Purpose.P5 +import static org.prebid.server.functional.model.config.Purpose.P6 +import static org.prebid.server.functional.model.config.Purpose.P7 +import static org.prebid.server.functional.model.config.Purpose.P8 +import static org.prebid.server.functional.model.config.Purpose.P9 +import static org.prebid.server.functional.model.config.Purpose.P10 +import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_EIDS +import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE + +class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { + + private static final Map PBS_CONFIG = SETTING_CONFIG + GENERIC_VENDOR_CONFIG + GENERIC_COOKIE_SYNC_CONFIG + ["gdpr.vendorlist.v2.http-endpoint-template": null, + "gdpr.vendorlist.v3.http-endpoint-template": null] + + private final PrebidServerService activityPbsServiceExcludeGvl = pbsServiceFactory.getService(PBS_CONFIG) + + def "PBS should leave the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have any basic consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.ext.prebid.trace = VERBOSE + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.eids == userEids + + where: + enforcementRequirements << getBasicTcfCompanyBasedEnforcementRequirements(P4) + + getBasicTcfLegalBasedEnforcementRequirements(P4) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P4) + } + + def "PBS should leave the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have softVendorExceptions consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + ext.prebid.aliases = [(ALIAS.value): GENERIC] + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.alias = new Generic() + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [ALIAS.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.eids == userEids + + where: + enforcementRequirements << getBasicTcfCompanySoftVendorExceptionsRequirements(P4) + } + + def "PBS should remove the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have any basic consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest?.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getBasicTcfLegalBasedEnforcementRequirements(P1) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P1) + + getBasicTcfCompanyBasedEnforcementRequirements(P1) + + getBasicTcfLegalBasedEnforcementRequirements(P2) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P2) + + getBasicTcfCompanyBasedEnforcementRequirements(P2) + + getBasicTcfLegalBasedEnforcementRequirements(P3) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P3) + + getBasicTcfCompanyBasedEnforcementRequirements(P3) + + getBasicTcfLegalBasedEnforcementRequirements(P5) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P5) + + getBasicTcfCompanyBasedEnforcementRequirements(P5) + + getBasicTcfLegalBasedEnforcementRequirements(P6) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P6) + + getBasicTcfCompanyBasedEnforcementRequirements(P6) + + getBasicTcfLegalBasedEnforcementRequirements(P7) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P7) + + getBasicTcfCompanyBasedEnforcementRequirements(P7) + + getBasicTcfLegalBasedEnforcementRequirements(P8) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P8) + + getBasicTcfCompanyBasedEnforcementRequirements(P8) + + getBasicTcfLegalBasedEnforcementRequirements(P9) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P9) + + getBasicTcfCompanyBasedEnforcementRequirements(P9) + + getBasicTcfLegalBasedEnforcementRequirements(P10) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P10) + + getBasicTcfCompanyBasedEnforcementRequirements(P10) + } + + def "PBS should remove the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have softVendorExceptions consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + ext.prebid.aliases = [(ALIAS.value): GENERIC] + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.alias = new Generic() + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [ALIAS.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest?.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getBasicTcfCompanySoftVendorExceptionsRequirements(P1) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P2) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P3) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P5) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P6) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P7) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P8) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P9) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P10) + } + + def "PBS should leave the original request with eids data when requireConsent is enabled but bidder is excepted and #enforcementRequirements.purpose have legal basic consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true, userEids.source) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.eids == userEids + + where: + enforcementRequirements << + getBasicTcfLegalBasedEnforcementRequirements(P2) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P2) + + getBasicTcfLegalBasedEnforcementRequirements(P3) + + getBasicTcfLegalBasedEnforcementRequirements(P5) + + getBasicTcfLegalBasedEnforcementRequirements(P6) + + getBasicTcfLegalBasedEnforcementRequirements(P7) + + getBasicTcfLegalBasedEnforcementRequirements(P8) + + getBasicTcfLegalBasedEnforcementRequirements(P9) + + getBasicTcfLegalBasedEnforcementRequirements(P10) + } + + def "PBS should remove the original request with eids data when requireConsent is enabled, bidder is excepted and #enforcementRequirements.purpose have unsupported basic consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true, userEids.source) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest?.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getBasicTcfLegalBasedEnforcementRequirements(P1) + + getBasicTcfCompanyBasedEnforcementRequirements(P1) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P1) + + getBasicTcfCompanyBasedEnforcementRequirements(P2) + + getBasicTcfCompanyBasedEnforcementRequirements(P3) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P3) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P4) + + getBasicTcfCompanyBasedEnforcementRequirements(P5) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P5) + + getBasicTcfCompanyBasedEnforcementRequirements(P6) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P6) + + getBasicTcfCompanyBasedEnforcementRequirements(P7) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P7) + + getBasicTcfCompanyBasedEnforcementRequirements(P8) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P8) + + getBasicTcfCompanyBasedEnforcementRequirements(P9) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P9) + + getBasicTcfCompanyBasedEnforcementRequirements(P10) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P10) + } + + def "PBS should leave the original request with eids data when requireConsent is disabled and #enforcementRequirements.purpose have legal basic consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, false) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.eids == userEids + + where: + enforcementRequirements << getBasicTcfLegalBasedEnforcementRequirements(P2) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P2) + + getBasicTcfLegalBasedEnforcementRequirements(P3) + + getBasicTcfLegalBasedEnforcementRequirements(P4) + + getBasicTcfLegalBasedEnforcementRequirements(P5) + + getBasicTcfLegalBasedEnforcementRequirements(P6) + + getBasicTcfLegalBasedEnforcementRequirements(P7) + + getBasicTcfLegalBasedEnforcementRequirements(P8) + + getBasicTcfLegalBasedEnforcementRequirements(P9) + + getBasicTcfLegalBasedEnforcementRequirements(P10) + } + + def "PBS should remove the original request with eids data when requireConsent is disabled and #enforcementRequirements.purpose have softVendorExceptions consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + ext.prebid.aliases = [(ALIAS.value): GENERIC] + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.alias = new Generic() + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, false) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [ALIAS.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest?.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getBasicTcfCompanySoftVendorExceptionsRequirements(P1) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P2) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P3) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P5) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P6) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P7) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P8) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P9) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P10) + } + + def "PBS should remove the original request with eids data when requireConsent is disabled and #enforcementRequirements.purpose have unsupported basic consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, false) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest?.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getBasicTcfLegalBasedEnforcementRequirements(P1) + + getBasicTcfCompanyBasedEnforcementRequirements(P1) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P1) + + getBasicTcfCompanyBasedEnforcementRequirements(P2) + + getBasicTcfCompanyBasedEnforcementRequirements(P3) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P3) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P4) + + getBasicTcfCompanyBasedEnforcementRequirements(P5) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P5) + + getBasicTcfCompanyBasedEnforcementRequirements(P6) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P6) + + getBasicTcfCompanyBasedEnforcementRequirements(P7) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P7) + + getBasicTcfCompanyBasedEnforcementRequirements(P8) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P8) + + getBasicTcfCompanyBasedEnforcementRequirements(P9) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P9) + + getBasicTcfCompanyBasedEnforcementRequirements(P10) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P10) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/TcfFullTransmitEidsActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/TcfFullTransmitEidsActivitiesSpec.groovy new file mode 100644 index 00000000000..934aa69de33 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/TcfFullTransmitEidsActivitiesSpec.groovy @@ -0,0 +1,219 @@ +package org.prebid.server.functional.tests.privacy + +import org.prebid.server.functional.model.config.AccountGdprConfig +import org.prebid.server.functional.model.request.auction.Activity +import org.prebid.server.functional.model.request.auction.ActivityRule +import org.prebid.server.functional.model.request.auction.AllowActivities +import org.prebid.server.functional.model.request.auction.Condition +import org.prebid.server.functional.model.request.auction.Eid +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.testcontainers.container.PrebidServerContainer +import org.prebid.server.functional.util.privacy.TcfUtils +import org.testcontainers.images.builder.Transferable +import spock.lang.Shared + +import static org.prebid.server.functional.model.config.Purpose.P1 +import static org.prebid.server.functional.model.config.Purpose.P4 +import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_EIDS + +class TcfFullTransmitEidsActivitiesSpec extends PrivacyBaseSpec { + + @Shared + private static PrebidServerContainer privacyPbsContainerWithMultipleGvl + + @Shared + private static PrebidServerService privacyPbsServiceWithMultipleGvl + + def setupSpec() { + privacyPbsContainerWithMultipleGvl = new PrebidServerContainer(PBS_CONFIG) + def prepareEncodeResponseBodyWithPurposesOnly = getVendorListContent(true, false, false) + def prepareEncodeResponseBodyWithLegIntPurposes = getVendorListContent(false, true, false) + def prepareEncodeResponseBodyWithLegIntAndFlexiblePurposes = getVendorListContent(false, true, true) + def prepareEncodeResponseBodyWithPurposesAndFlexiblePurposes = getVendorListContent(true, false, true) + privacyPbsContainerWithMultipleGvl.withCopyToContainer(Transferable.of(prepareEncodeResponseBodyWithPurposesOnly), getVendorListPath(PURPOSES_ONLY_GVL_VERSION)) + privacyPbsContainerWithMultipleGvl.withCopyToContainer(Transferable.of(prepareEncodeResponseBodyWithLegIntPurposes), getVendorListPath(LEG_INT_PURPOSES_ONLY_GVL_VERSION)) + privacyPbsContainerWithMultipleGvl.withCopyToContainer(Transferable.of(prepareEncodeResponseBodyWithLegIntAndFlexiblePurposes), getVendorListPath(LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION)) + privacyPbsContainerWithMultipleGvl.withCopyToContainer(Transferable.of(prepareEncodeResponseBodyWithPurposesAndFlexiblePurposes), getVendorListPath(PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION)) + privacyPbsContainerWithMultipleGvl.start() + privacyPbsServiceWithMultipleGvl = new PrebidServerService(privacyPbsContainerWithMultipleGvl) + } + + def cleanupSpec() { + privacyPbsContainerWithMultipleGvl.stop() + } + + def "PBS should leave the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have full consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + privacyPbsServiceWithMultipleGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.eids == userEids + + where: + enforcementRequirements << getFullTcfLegalEnforcementRequirements(P4) + getFullTcfCompanyEnforcementRequirements(P4) + } + + def "PBS should remove the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have full consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + privacyPbsServiceWithMultipleGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest?.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getFullTcfLegalEnforcementRequirementsRandomlyWithExcludePurpose(P4) + + getFullTcfCompanyEnforcementRequirementsRandomlyWithExcludePurpose(P4) + } + + def "PBS should leave the original request with eids data when requireConsent is enabled but bidder is excepted and #enforcementRequirements.purpose have full consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true, userEids.source) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + privacyPbsServiceWithMultipleGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.eids == userEids + + where: + enforcementRequirements << getFullTcfLegalEnforcementRequirementsRandomlyWithExcludePurpose(P1) + } + + def "PBS should remove the original request with eids data when requireConsent is enabled, bidder is excepted and #enforcementRequirements.purpose have unsupported full consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true, userEids.source) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + privacyPbsServiceWithMultipleGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + + assert !bidderRequest?.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getFullTcfLegalEnforcementRequirements(P1) + + getFullTcfCompanyEnforcementRequirementsRandomlyWithExcludePurpose(P4) + } + + def "PBS should leave the original request with eids data when requireConsent is disabled and #enforcementRequirements.purpose have full consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, false) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + privacyPbsServiceWithMultipleGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.eids == userEids + + where: + enforcementRequirements << getFullTcfLegalEnforcementRequirementsRandomlyWithExcludePurpose(P1) + } + + def "PBS should remove the original request with eids data when requireConsent is disabled and #enforcementRequirements.purpose have unsupported full consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, false) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + privacyPbsServiceWithMultipleGvl.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getFullTcfLegalEnforcementRequirements(P1) + + getFullTcfCompanyEnforcementRequirementsRandomlyWithExcludePurpose(P4) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/TransmitEidsOrtbConverterActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/TransmitEidsOrtbConverterActivitiesSpec.groovy new file mode 100644 index 00000000000..8ca804717ed --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/TransmitEidsOrtbConverterActivitiesSpec.groovy @@ -0,0 +1,281 @@ +package org.prebid.server.functional.tests.privacy + +import org.prebid.server.functional.model.config.AccountGdprConfig +import org.prebid.server.functional.model.request.auction.Activity +import org.prebid.server.functional.model.request.auction.ActivityRule +import org.prebid.server.functional.model.request.auction.AllowActivities +import org.prebid.server.functional.model.request.auction.Condition +import org.prebid.server.functional.model.request.auction.Eid +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.testcontainers.container.PrebidServerContainer +import org.prebid.server.functional.util.privacy.TcfUtils +import org.testcontainers.images.builder.Transferable +import spock.lang.Shared + +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.config.Purpose.P1 +import static org.prebid.server.functional.model.config.Purpose.P2 +import static org.prebid.server.functional.model.config.Purpose.P3 +import static org.prebid.server.functional.model.config.Purpose.P4 +import static org.prebid.server.functional.model.config.Purpose.P5 +import static org.prebid.server.functional.model.config.Purpose.P6 +import static org.prebid.server.functional.model.config.Purpose.P7 +import static org.prebid.server.functional.model.config.Purpose.P8 +import static org.prebid.server.functional.model.config.Purpose.P9 +import static org.prebid.server.functional.model.config.Purpose.P10 +import static org.prebid.server.functional.model.request.auction.ActivityType.TRANSMIT_EIDS +import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE + +class TransmitEidsOrtbConverterActivitiesSpec extends PrivacyBaseSpec { + + private static final Map PBS_CONFIG = SETTING_CONFIG + GENERIC_VENDOR_CONFIG + GENERIC_COOKIE_SYNC_CONFIG + ["gdpr.vendorlist.v2.http-endpoint-template": null, + "gdpr.vendorlist.v3.http-endpoint-template": null] + private final PrebidServerService activityPbsServiceExcludeGvlWithElderOrtb = pbsServiceFactory.getService(PBS_CONFIG + ["adapters.generic.ortb-version": "2.5"]) + + @Shared + private static PrebidServerService privacyPbsServiceWithMultipleGvlWithElderOrtb + + @Shared + private static PrebidServerContainer privacyPbsContainerWithMultipleGvlWithElderOrtb + + def setupSpec() { + def prepareEncodeResponseBodyWithPurposesOnly = getVendorListContent(true, false, false) + def prepareEncodeResponseBodyWithLegIntPurposes = getVendorListContent(false, true, false) + def prepareEncodeResponseBodyWithLegIntAndFlexiblePurposes = getVendorListContent(false, true, true) + def prepareEncodeResponseBodyWithPurposesAndFlexiblePurposes = getVendorListContent(true, false, true) + privacyPbsContainerWithMultipleGvlWithElderOrtb = new PrebidServerContainer(PBS_CONFIG + ["adapters.generic.ortb-version": "2.5"]) + privacyPbsContainerWithMultipleGvlWithElderOrtb.withCopyToContainer(Transferable.of(prepareEncodeResponseBodyWithPurposesOnly), getVendorListPath(PURPOSES_ONLY_GVL_VERSION)) + privacyPbsContainerWithMultipleGvlWithElderOrtb.withCopyToContainer(Transferable.of(prepareEncodeResponseBodyWithLegIntPurposes), getVendorListPath(LEG_INT_PURPOSES_ONLY_GVL_VERSION)) + privacyPbsContainerWithMultipleGvlWithElderOrtb.withCopyToContainer(Transferable.of(prepareEncodeResponseBodyWithLegIntAndFlexiblePurposes), getVendorListPath(LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION)) + privacyPbsContainerWithMultipleGvlWithElderOrtb.withCopyToContainer(Transferable.of(prepareEncodeResponseBodyWithPurposesAndFlexiblePurposes), getVendorListPath(PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION)) + privacyPbsContainerWithMultipleGvlWithElderOrtb.start() + privacyPbsServiceWithMultipleGvlWithElderOrtb = new PrebidServerService(privacyPbsContainerWithMultipleGvlWithElderOrtb) + } + + def cleanupSpec() { + privacyPbsContainerWithMultipleGvlWithElderOrtb.stop() + } + + def "PBS should leave the original request with ext.eids data for elder ortb when requireConsent is enabled and #enforcementRequirements.purpose have any basic consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.ext.prebid.trace = VERBOSE + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvlWithElderOrtb.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in user.ext.eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.ext?.eids == userEids + + and: "should be empty at user.eids" + assert !bidderRequest?.user?.eids + + where: + enforcementRequirements << getBasicTcfCompanyBasedEnforcementRequirements(P4) + + getBasicTcfLegalBasedEnforcementRequirements(P4) + + getBasicTcfCompanySoftVendorExceptionsRequirements(P4) + } + + def "PBS should remove the original request with ext.eids data for elder ortb when requireConsent is enabled and #enforcementRequirements.purpose have any basic consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvlWithElderOrtb.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest?.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getBasicTcfLegalBasedEnforcementRequirements(P1) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P1) + + getBasicTcfCompanyBasedEnforcementRequirements(P1) + + getBasicTcfLegalBasedEnforcementRequirements(P2) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P2) + + getBasicTcfCompanyBasedEnforcementRequirements(P2) + + getBasicTcfLegalBasedEnforcementRequirements(P3) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P3) + + getBasicTcfCompanyBasedEnforcementRequirements(P3) + + getBasicTcfLegalBasedEnforcementRequirements(P5) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P5) + + getBasicTcfCompanyBasedEnforcementRequirements(P5) + + getBasicTcfLegalBasedEnforcementRequirements(P6) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P6) + + getBasicTcfCompanyBasedEnforcementRequirements(P6) + + getBasicTcfLegalBasedEnforcementRequirements(P7) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P7) + + getBasicTcfCompanyBasedEnforcementRequirements(P7) + + getBasicTcfLegalBasedEnforcementRequirements(P8) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P8) + + getBasicTcfCompanyBasedEnforcementRequirements(P8) + + getBasicTcfLegalBasedEnforcementRequirements(P9) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P9) + + getBasicTcfCompanyBasedEnforcementRequirements(P9) + + getBasicTcfLegalBasedEnforcementRequirements(P10) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P10) + + getBasicTcfCompanyBasedEnforcementRequirements(P10) + } + + def "PBS should leave the original request with ext.eids data for elder ortb when requireConsent is disabled and #enforcementRequirements.purpose have legal basic consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, false) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + activityPbsServiceExcludeGvlWithElderOrtb.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.ext?.eids == userEids + + and: "should be empty at user.eids" + assert !bidderRequest?.user?.eids + + where: + enforcementRequirements << getBasicTcfLegalBasedEnforcementRequirements(P2) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P2) + + getBasicTcfLegalBasedEnforcementRequirements(P3) + + getBasicTcfLegalBasedEnforcementRequirements(P4) + + getBasicTcfLegalBasedEnforcementRequirements(P5) + + getBasicTcfLegalBasedEnforcementRequirements(P6) + + getBasicTcfLegalBasedEnforcementRequirements(P7) + + getBasicTcfLegalBasedEnforcementRequirements(P8) + + getBasicTcfLegalBasedEnforcementRequirements(P9) + + getBasicTcfLegalBasedEnforcementRequirements(P10) + } + + def "PBS should leave the original request with ext.eids data for elder ortb when requireConsent is enabled and #enforcementRequirements.purpose have full consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + privacyPbsServiceWithMultipleGvlWithElderOrtb.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.ext?.eids == userEids + + and: "should be empty at user.eids" + assert !bidderRequest?.user?.eids + + where: + enforcementRequirements << getFullTcfLegalEnforcementRequirements(P4) + getFullTcfCompanyEnforcementRequirements(P4) + } + + def "PBS should remove the original request with ext.eids data for elder ortb when requireConsent is enabled and #enforcementRequirements.purpose have full consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + privacyPbsServiceWithMultipleGvlWithElderOrtb.sendAuctionRequest(bidRequest) + + then: "Generic bidder request shouldn't have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest?.user?.eids + assert !bidderRequest.user?.ext?.eids + + where: + enforcementRequirements << getFullTcfLegalEnforcementRequirements(P1) + + getFullTcfCompanyEnforcementRequirements(P1) + + getFullTcfLegalEnforcementRequirementsRandomlyWithExcludePurpose(P4) + + getFullTcfCompanyEnforcementRequirementsRandomlyWithExcludePurpose(P4) + } + + def "PBS should leave the original request with ext.eids data for elder ortb when requireConsent is disabled and #enforcementRequirements.purpose have full consent"() { + given: "Default Generic BidRequests with Eid field" + def userEids = [Eid.defaultEid] + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent).tap { + it.user.eids = userEids + } + + and: "Save account config with requireConsent into DB" + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, false) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + accountDao.save(account) + + when: "PBS processes auction requests" + privacyPbsServiceWithMultipleGvlWithElderOrtb.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in Eid field" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest?.user?.ext?.eids == userEids + + and: "should be empty at user.eids" + assert !bidderRequest?.user?.eids + + where: + enforcementRequirements << getFullTcfLegalEnforcementRequirementsRandomlyWithExcludePurpose(P1) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy index 1bbcf6fe0ec..0604aa81fbe 100644 --- a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy @@ -28,6 +28,11 @@ class PBSUtils implements ObjectMapperWrapper { value == excludedValue ? getRandomNumberWithExclusion(excludedValue, min, max) : value } + static int getRandomNumberWithExclusion(List excludedValues, int min = 0, int max = MAX_VALUE) { + def value = getRandomNumber(min, max) + excludedValues.contains(value) ? getRandomNumberWithExclusion(excludedValues, min, max) : value + } + static int getRandomNegativeNumber(int min = MIN_VALUE + 1, int max = 0) { getRandomNumber(max, min * -1) * -1 } diff --git a/src/test/groovy/org/prebid/server/functional/util/privacy/TcfConsent.groovy b/src/test/groovy/org/prebid/server/functional/util/privacy/TcfConsent.groovy index e2b616b8190..90a4def9604 100644 --- a/src/test/groovy/org/prebid/server/functional/util/privacy/TcfConsent.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/privacy/TcfConsent.groovy @@ -1,7 +1,10 @@ package org.prebid.server.functional.util.privacy +import com.iabtcf.encoder.PublisherRestrictionEntry import com.iabtcf.encoder.TCStringEncoder import com.iabtcf.utils.BitSetIntIterable +import com.iabtcf.v2.RestrictionType +import org.prebid.server.functional.model.config.Purpose import org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.util.privacy.TcfConsent.TcfPolicyVersion.TCF_POLICY_V2 @@ -54,8 +57,18 @@ class TcfConsent implements ConsentString { this } - Builder setPurposesConsent(List purposesConsent) { - tcStringEncoder.addPurposesConsent(BitSetIntIterable.from(purposesConsent)) + Builder setPurposesConsent(PurposeId purposeConsent) { + tcStringEncoder.addPurposesConsent(purposeConsent.value) + this + } + + Builder setPurposesConsent(List purposesConsent) { + tcStringEncoder.addPurposesConsent(BitSetIntIterable.from(purposesConsent.collect { it.value })) + this + } + + Builder setVendorConsent(Integer vendorConsent) { + tcStringEncoder.addVendorConsent(vendorConsent) this } @@ -64,6 +77,11 @@ class TcfConsent implements ConsentString { this } + Builder setVendorLegitimateInterest(Integer vendorLegitimateInterest) { + tcStringEncoder.addVendorLegitimateInterest(vendorLegitimateInterest) + this + } + Builder setVendorLegitimateInterest(List vendorLegitimateInterest) { tcStringEncoder.addVendorLegitimateInterest(BitSetIntIterable.from(vendorLegitimateInterest)) this @@ -74,6 +92,19 @@ class TcfConsent implements ConsentString { this } + Builder setPublisherRestrictionEntry(PurposeId purposeId, List restrictionTypes, Integer vendorId) { + restrictionTypes.each { restrictionType -> + def publisherRestrictionEntry = PublisherRestrictionEntry + .newBuilder() + .purposeId(purposeId.value) + .restrictionType(com.iabtcf.v2.RestrictionType.from(restrictionType.value)) + .addVendor(vendorId) + .build() + tcStringEncoder.addPublisherRestrictionEntry(publisherRestrictionEntry) + } + this + } + TcfConsent build() { new TcfConsent(this) } @@ -83,14 +114,25 @@ class TcfConsent implements ConsentString { DEVICE_ACCESS(1), BASIC_ADS(2), + PERSONALIZED_ADS_PROFILE(3), PERSONALIZED_ADS(4), - MEASURE_AD_PERFORMANCE(7) + PERSONALIZED_CONTENT_PROFILE(5), + PERSONALIZED_CONTENT(6), + MEASURE_AD_PERFORMANCE(7), + MEASURE_CONTENT_PERFORMANCE(8), + AUDIENCE_MARKET_RESEARCH(9), + DEVELOPMENT_IMPROVE_PRODUCTS(10) final int value PurposeId(int value) { this.value = value } + + static PurposeId convertPurposeToPurposeId(Purpose purpose) { + int purposeValue = purpose.ordinal() + 1 + values().find { it.value == purposeValue } + } } enum TcfPolicyVersion { @@ -112,4 +154,17 @@ class TcfConsent implements ConsentString { (this == TCF_POLICY_V3) ? 2 : 3 } } + + enum RestrictionType { + NOT_ALLOWED(0), + REQUIRE_CONSENT(1), + REQUIRE_LEGITIMATE_INTEREST(2), + UNDEFINED(3) + + final int value + + RestrictionType(int value) { + this.value = value + } + } } diff --git a/src/test/groovy/org/prebid/server/functional/util/privacy/TcfUtils.groovy b/src/test/groovy/org/prebid/server/functional/util/privacy/TcfUtils.groovy new file mode 100644 index 00000000000..2b4a32fb2c9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/util/privacy/TcfUtils.groovy @@ -0,0 +1,58 @@ +package org.prebid.server.functional.util.privacy + +import org.prebid.server.functional.model.config.Purpose +import org.prebid.server.functional.model.config.PurposeConfig +import org.prebid.server.functional.model.config.PurposeEid +import org.prebid.server.functional.model.privacy.EnforcementRequirement + +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.util.privacy.TcfConsent.PurposeId +import static org.prebid.server.functional.util.privacy.TcfConsent.TcfPolicyVersion.TCF_POLICY_V2 + +class TcfUtils { + + static Map getPurposeConfigsForPersonalizedAds(EnforcementRequirement enforcementRequirements, boolean requireConsent = false, List eidsExceptions = []) { + def purpose = enforcementRequirements.purpose + // Basic Ads required for any bidder call, should be present at least as company consent + def purposes = [(Purpose.P2): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)] + def purposeConfig = new PurposeConfig(enforcePurpose: enforcementRequirements.enforcePurpose, + enforceVendors: enforcementRequirements?.enforceVendor, + vendorExceptions: enforcementRequirements?.vendorExceptions?.value) + def purposeEid = new PurposeEid(requireConsent: requireConsent, exceptions: eidsExceptions) + if (purpose == Purpose.P4) { + purposeConfig.eid = purposeEid + purposes[Purpose.P4] = purposeConfig + } else { + purposes[purpose] = purposeConfig + purposes[Purpose.P4] = new PurposeConfig(eid: purposeEid) + } + purposes + } + + static ConsentString getConsentString(EnforcementRequirement enforcementRequirements) { + def purposeConsent = enforcementRequirements.enforcePurpose != NO ? enforcementRequirements.getPurpose() : null + def vendorConsentBitField = enforcementRequirements.getVendorConsentBitField() + def purposesLITransparency = enforcementRequirements.getPurposesLITransparency() + def restrictionType = enforcementRequirements.restrictionType + def vendorIdGvl = enforcementRequirements.vendorIdGvl + def builder = new TcfConsent.Builder() + if (purposeConsent != null && !purposesLITransparency) { + builder.setPurposesConsent(PurposeId.convertPurposeToPurposeId(purposeConsent)) + } + if (vendorConsentBitField != null) { + builder.setVendorConsent(vendorConsentBitField) + } + if (purposesLITransparency) { + builder.setPurposesLITransparency(PurposeId.convertPurposeToPurposeId(enforcementRequirements.getPurpose())) + } + if (purposeConsent != null && restrictionType != null && vendorIdGvl != null) { + builder.setPublisherRestrictionEntry(PurposeId.convertPurposeToPurposeId(purposeConsent), restrictionType, vendorIdGvl) + } + if (vendorIdGvl != null) { + builder.setVendorLegitimateInterest(vendorIdGvl) + } + builder.setVendorListVersion(enforcementRequirements.vendorListVersion ?: TCF_POLICY_V2.vendorListVersion) + return builder.build() + } +} diff --git a/src/test/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreatorTest.java b/src/test/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreatorTest.java index ddef30e4900..0927f596a41 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreatorTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreatorTest.java @@ -17,7 +17,11 @@ import org.prebid.server.metric.MetricName; import org.prebid.server.metric.Metrics; import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountGdprConfig; import org.prebid.server.settings.model.AccountPrivacyConfig; +import org.prebid.server.settings.model.Purpose; +import org.prebid.server.settings.model.PurposeEid; +import org.prebid.server.settings.model.Purposes; import org.prebid.server.settings.model.activity.AccountActivityConfiguration; import org.prebid.server.settings.model.activity.privacy.AccountUSNatModuleConfig; import org.prebid.server.settings.model.activity.rule.AccountActivityComponentRuleConfig; @@ -55,7 +59,7 @@ public class ActivityInfrastructureCreatorTest { @Before public void setUp() { - creator = new ActivityInfrastructureCreator(activityRuleFactory, metrics, jacksonMapper); + creator = new ActivityInfrastructureCreator(activityRuleFactory, null, metrics, jacksonMapper); } @Test @@ -73,13 +77,7 @@ public void parseShouldReturnExpectedResultIfAccountPrivacyNull() { @Test public void parseShouldReturnExpectedResultIfAccountPrivacyActivitiesNull() { // given - final Account account = Account.builder().privacy(AccountPrivacyConfig.of( - null, - null, - null, - null, - null)) - .build(); + final Account account = Account.builder().privacy(AccountPrivacyConfig.builder().build()).build(); // when final Map controllers = creator.parse(account, null, debug); @@ -92,15 +90,13 @@ public void parseShouldReturnExpectedResultIfAccountPrivacyActivitiesNull() { public void parseShouldSkipPrivacyModulesDuplicatesAndEmitWarnings() { // given final Account account = Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - null, - null, - Map.of(Activity.SYNC_USER, AccountActivityConfiguration.of( - null, singletonList(AccountActivityComponentRuleConfig.of(null, null)))), - asList( + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of(Activity.SYNC_USER, AccountActivityConfiguration.of( + null, singletonList(AccountActivityComponentRuleConfig.of(null, null))))) + .modules(asList( AccountUSNatModuleConfig.of(null, null), - AccountUSNatModuleConfig.of(null, null)))) + AccountUSNatModuleConfig.of(null, null))) + .build()) .build(); // when @@ -115,17 +111,14 @@ null, singletonList(AccountActivityComponentRuleConfig.of(null, null)))), public void parseShouldReturnExpectedResult() { // given final Account account = Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - null, - null, - Map.of( + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of( Activity.SYNC_USER, AccountActivityConfiguration.of(null, null), Activity.CALL_BIDDER, AccountActivityConfiguration.of(false, null), Activity.MODIFY_UFDP, AccountActivityConfiguration.of(true, null), Activity.TRANSMIT_UFPD, AccountActivityConfiguration.of(true, singletonList( - AccountActivityComponentRuleConfig.of(null, null)))), - null)) + AccountActivityComponentRuleConfig.of(null, null))))) + .build()) .build(); final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); @@ -146,4 +139,51 @@ public void parseShouldReturnExpectedResult() { assertThat(controllers.get(Activity.MODIFY_UFDP).isAllowed(null)).isEqualTo(true); assertThat(controllers.get(Activity.TRANSMIT_UFPD).isAllowed(null)).isEqualTo(false); } + + @Test + public void parseShouldReturnImitatedTransmitEidsActivity() { + // given + final Account account = Account.builder() + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of(Activity.TRANSMIT_UFPD, AccountActivityConfiguration.of(false, null))) + .build()) + .build(); + final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); + + // when + final Map controllers = creator.parse(account, gppContext, debug); + + // then + assertThat(controllers.keySet()).containsExactlyInAnyOrder(Activity.values()); + + assertThat(controllers.get(Activity.CALL_BIDDER).isAllowed(null)).isEqualTo(true); + assertThat(controllers.get(Activity.TRANSMIT_UFPD).isAllowed(null)).isEqualTo(false); + assertThat(controllers.get(Activity.TRANSMIT_EIDS).isAllowed(null)).isEqualTo(false); + } + + @Test + public void parseShouldReturnOriginalTransmitEidsActivity() { + // given + final Account account = Account.builder() + .privacy(AccountPrivacyConfig.builder() + .gdpr(AccountGdprConfig.builder() + .purposes(Purposes.builder() + .p4(Purpose.of(null, null, null, PurposeEid.of(false, false, null))) + .build()) + .build()) + .activities(Map.of(Activity.TRANSMIT_UFPD, AccountActivityConfiguration.of(false, null))) + .build()) + .build(); + final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); + + // when + final Map controllers = creator.parse(account, gppContext, debug); + + // then + assertThat(controllers.keySet()).containsExactlyInAnyOrder(Activity.values()); + + assertThat(controllers.get(Activity.CALL_BIDDER).isAllowed(null)).isEqualTo(true); + assertThat(controllers.get(Activity.TRANSMIT_UFPD).isAllowed(null)).isEqualTo(false); + assertThat(controllers.get(Activity.TRANSMIT_EIDS).isAllowed(null)).isEqualTo(true); + } } diff --git a/src/test/java/org/prebid/server/activity/utils/AccountActivitiesConfigurationUtilsTest.java b/src/test/java/org/prebid/server/activity/utils/AccountActivitiesConfigurationUtilsTest.java index 38e85b72616..de7632f7cc3 100644 --- a/src/test/java/org/prebid/server/activity/utils/AccountActivitiesConfigurationUtilsTest.java +++ b/src/test/java/org/prebid/server/activity/utils/AccountActivitiesConfigurationUtilsTest.java @@ -42,13 +42,7 @@ public void isInvalidActivitiesConfigurationShouldReturnFalseIfAccountPrivacyNul @Test public void isInvalidActivitiesConfigurationShouldReturnFalseIfAccountPrivacyActivitiesNull() { // given - final Account account = Account.builder().privacy(AccountPrivacyConfig.of( - null, - null, - null, - null, - null)) - .build(); + final Account account = Account.builder().privacy(AccountPrivacyConfig.builder().build()).build(); // when final boolean result = AccountActivitiesConfigurationUtils.isInvalidActivitiesConfiguration(account); @@ -61,11 +55,8 @@ public void isInvalidActivitiesConfigurationShouldReturnFalseIfAccountPrivacyAct public void isInvalidActivitiesConfigurationShouldReturnFalseIfConfigurationValid() { // given final Account account = Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - null, - null, - Map.of( + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of( Activity.SYNC_USER, AccountActivityConfiguration.of(null, null), Activity.CALL_BIDDER, AccountActivityConfiguration.of(null, asList( null, @@ -89,8 +80,8 @@ public void isInvalidActivitiesConfigurationShouldReturnFalseIfConfigurationVali null, null, null), - null)))), - null)) + null))))) + .build()) .build(); // when @@ -104,15 +95,12 @@ public void isInvalidActivitiesConfigurationShouldReturnFalseIfConfigurationVali public void isInvalidActivitiesConfigurationShouldReturnTrueOnInvalidComponentRule() { // given final Account account = Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - null, - null, - Map.of(Activity.CALL_BIDDER, AccountActivityConfiguration.of(null, singletonList( + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of(Activity.CALL_BIDDER, AccountActivityConfiguration.of(null, singletonList( AccountActivityComponentRuleConfig.of( AccountActivityComponentRuleConfig.Condition.of(emptyList(), emptyList()), - null)))), - null)) + null))))) + .build()) .build(); // when @@ -126,16 +114,13 @@ public void isInvalidActivitiesConfigurationShouldReturnTrueOnInvalidComponentRu public void isInvalidActivitiesConfigurationShouldReturnTrueOnInvalidGeoRule() { // given final Account account = Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - null, - null, - Map.of(Activity.CALL_BIDDER, AccountActivityConfiguration.of(null, singletonList( + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of(Activity.CALL_BIDDER, AccountActivityConfiguration.of(null, singletonList( AccountActivityGeoRuleConfig.of( AccountActivityGeoRuleConfig.Condition.of( emptyList(), emptyList(), null, null, null), - null)))), - null)) + null))))) + .build()) .build(); // when diff --git a/src/test/java/org/prebid/server/analytics/reporter/AnalyticsReporterDelegatorTest.java b/src/test/java/org/prebid/server/analytics/reporter/AnalyticsReporterDelegatorTest.java index 57ca9574b29..10c53f7ba06 100644 --- a/src/test/java/org/prebid/server/analytics/reporter/AnalyticsReporterDelegatorTest.java +++ b/src/test/java/org/prebid/server/analytics/reporter/AnalyticsReporterDelegatorTest.java @@ -27,8 +27,9 @@ import org.prebid.server.analytics.model.AmpEvent; import org.prebid.server.analytics.model.AuctionEvent; import org.prebid.server.analytics.model.NotificationEvent; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.privacy.enforcement.TcfEnforcement; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.metric.MetricName; import org.prebid.server.metric.Metrics; @@ -69,7 +70,9 @@ public class AnalyticsReporterDelegatorTest { @Mock private Vertx vertx; @Mock - private PrivacyEnforcementService privacyEnforcementService; + private TcfEnforcement tcfEnforcement; + @Mock + private UserFpdActivityMask userFpdActivityMask; @Mock private Metrics metrics; @@ -98,11 +101,11 @@ public void setUp() { final Map enforcementActionMap = new HashMap<>(); enforcementActionMap.put(SECOND_REPORTER_ID, PrivacyEnforcementAction.allowAll()); enforcementActionMap.put(FIRST_REPORTER_ID, PrivacyEnforcementAction.allowAll()); - given(privacyEnforcementService.resultForVendorIds(any(), any())) + given(tcfEnforcement.enforce(any(), any())) .willReturn(Future.succeededFuture(enforcementActionMap)); target = new AnalyticsReporterDelegator( - 0.01, List.of(firstReporter, secondReporter), vertx, privacyEnforcementService, metrics); + vertx, List.of(firstReporter, secondReporter), tcfEnforcement, userFpdActivityMask, metrics, 0.01); } @Test @@ -251,7 +254,7 @@ public void shouldPassEventToAllowedDelegatesWhenSomeVendorIdWasAllowed() { enforcementActionMap.put(FIRST_REPORTER_ID, PrivacyEnforcementAction.restrictAll()); enforcementActionMap.put(SECOND_REPORTER_ID, PrivacyEnforcementAction.allowAll()); - given(privacyEnforcementService.resultForVendorIds(any(), any())) + given(tcfEnforcement.enforce(any(), any())) .willReturn(Future.succeededFuture(enforcementActionMap)); willAnswer(withNullAndInvokeHandler()).given(vertx).runOnContext(any()); @@ -271,7 +274,7 @@ public void shouldNotPassEventToDelegatesWhenAllVendorIdsWasBlocked() { enforcementActionMap.put(FIRST_REPORTER_ID, PrivacyEnforcementAction.restrictAll()); enforcementActionMap.put(SECOND_REPORTER_ID, PrivacyEnforcementAction.restrictAll()); - given(privacyEnforcementService.resultForVendorIds(any(), any())) + given(tcfEnforcement.enforce(any(), any())) .willReturn(Future.succeededFuture(enforcementActionMap)); willAnswer(withNullAndInvokeHandler()).given(vertx).runOnContext(any()); @@ -349,11 +352,12 @@ public void shouldUpdateAuctionEventToConsideringActivitiesRestrictions() { // given given(activityInfrastructure.isAllowed(eq(Activity.REPORT_ANALYTICS), any())).willReturn(true); given(activityInfrastructure.isAllowed(eq(Activity.TRANSMIT_UFPD), any())).willReturn(false); + given(activityInfrastructure.isAllowed(eq(Activity.TRANSMIT_EIDS), any())).willReturn(false); given(activityInfrastructure.isAllowed(eq(Activity.TRANSMIT_GEO), any())).willReturn(false); - given(privacyEnforcementService.maskUserConsideringActivityRestrictions(any(), eq(true), eq(true))) + given(userFpdActivityMask.maskUser(any(), eq(true), eq(true), eq(true))) .willReturn(User.builder().id("masked").build()); - given(privacyEnforcementService.maskDeviceConsideringActivityRestrictions(any(), eq(true), eq(true))) + given(userFpdActivityMask.maskDevice(any(), eq(true), eq(true))) .willReturn(Device.builder().model("masked").build()); final AuctionEvent auctionEvent = AuctionEvent.builder() diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index e0aa389eb79..8477f0fa112 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -58,6 +58,7 @@ import org.prebid.server.auction.model.StoredResponseResult; import org.prebid.server.auction.model.TimeoutContext; import org.prebid.server.auction.model.debug.DebugContext; +import org.prebid.server.auction.privacy.enforcement.PrivacyEnforcementService; import org.prebid.server.auction.versionconverter.BidRequestOrtbVersionConversionManager; import org.prebid.server.bidder.Bidder; import org.prebid.server.bidder.BidderCatalog; @@ -336,7 +337,7 @@ public void setUp() { CompressionType.NONE, Ortb.of(false))); - given(privacyEnforcementService.mask(any(), argThat(MapUtils::isNotEmpty), any(), any())) + given(privacyEnforcementService.mask(any(), argThat(MapUtils::isNotEmpty), any())) .willAnswer(inv -> Future.succeededFuture(((Map) inv.getArgument(1)).entrySet().stream() .map(bidderAndUser -> BidderPrivacyResult.builder() @@ -345,7 +346,7 @@ public void setUp() { .build()) .toList())); - given(privacyEnforcementService.mask(any(), argThat(MapUtils::isEmpty), any(), any())) + given(privacyEnforcementService.mask(any(), argThat(MapUtils::isEmpty), any())) .willReturn(Future.succeededFuture(emptyList())); given(fpdResolver.resolveUser(any(), any())).willAnswer(invocation -> invocation.getArgument(0)); @@ -1096,7 +1097,7 @@ public void shouldReturnFailedFutureWithUnchangedMessageWhenPrivacyEnforcementSe final Bidder bidder = mock(Bidder.class); givenBidder("someBidder", bidder, givenEmptySeatBid()); - given(privacyEnforcementService.mask(any(), any(), any(), any())) + given(privacyEnforcementService.mask(any(), any(), any())) .willReturn(Future.failedFuture("Error when retrieving allowed purpose ids")); final BidRequest bidRequest = givenBidRequest(givenSingleImp(singletonMap("someBidder", 1)), @@ -1121,7 +1122,7 @@ public void shouldNotCreateRequestForBidderRestrictedByPrivacyEnforcement() { .requestBidder("bidderAlias") .blockedRequestByTcf(true) .build(); - given(privacyEnforcementService.mask(any(), any(), any(), any())) + given(privacyEnforcementService.mask(any(), any(), any())) .willReturn(Future.succeededFuture(singletonList(restrictedPrivacy))); final BidRequest bidRequest = givenBidRequest(singletonList( @@ -1153,7 +1154,7 @@ public void shouldReturnProperStoredResponseIfAvailableOnlySingleImpRequests() { final BidderPrivacyResult bidderPrivacyResult = BidderPrivacyResult.builder() .requestBidder("bidder-test") .build(); - given(privacyEnforcementService.mask(any(), any(), any(), any())) + given(privacyEnforcementService.mask(any(), any(), any())) .willReturn(Future.succeededFuture(singletonList(bidderPrivacyResult))); final BidRequest bidRequest = givenBidRequest(singletonList( @@ -4545,7 +4546,7 @@ public void shouldProperPopulateImpExtPrebidEvenIfInExtImpPrebidContainNotCorrec final BidRequest bidRequest = givenBidRequest(singletonList(imp), identity()); final AuctionContext auctionContext = givenRequestContext(bidRequest); - given(privacyEnforcementService.mask(any(), anyMap(), any(), any())) + given(privacyEnforcementService.mask(any(), anyMap(), any())) .willReturn(Future.succeededFuture(singletonList(BidderPrivacyResult.builder() .requestBidder("bidderName") .build()))); @@ -4579,7 +4580,7 @@ public void shouldThrowErrorIfCannotBeParsedImpExtPrebid() { final BidRequest bidRequest = givenBidRequest(singletonList(imp), identity()); final AuctionContext auctionContext = givenRequestContext(bidRequest); - given(privacyEnforcementService.mask(any(), anyMap(), any(), any())) + given(privacyEnforcementService.mask(any(), anyMap(), any())) .willReturn(Future.succeededFuture(singletonList(BidderPrivacyResult.builder() .requestBidder(bidderName) .build()))); @@ -4605,7 +4606,7 @@ public void shouldReturnsSourceWithCorrespondingRequestExtPrebidSchainsIfSchainI given(supplyChainResolver.resolveForBidder(any(), any())) .willReturn(givenSourceSchain); - given(privacyEnforcementService.mask(any(), anyMap(), any(), any())) + given(privacyEnforcementService.mask(any(), anyMap(), any())) .willReturn(Future.succeededFuture(singletonList(BidderPrivacyResult.builder() .requestBidder("bidderName") .build()))); diff --git a/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java b/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java deleted file mode 100644 index ce543c0b2b4..00000000000 --- a/src/test/java/org/prebid/server/auction/PrivacyEnforcementServiceTest.java +++ /dev/null @@ -1,1965 +0,0 @@ -package org.prebid.server.auction; - -import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Device; -import com.iab.openrtb.request.Eid; -import com.iab.openrtb.request.Geo; -import com.iab.openrtb.request.Imp; -import com.iab.openrtb.request.Regs; -import com.iab.openrtb.request.Site; -import com.iab.openrtb.request.User; -import io.vertx.core.Future; -import io.vertx.core.MultiMap; -import io.vertx.core.http.HttpServerRequest; -import io.vertx.core.net.impl.SocketAddressImpl; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.VertxTest; -import org.prebid.server.activity.Activity; -import org.prebid.server.activity.infrastructure.ActivityInfrastructure; -import org.prebid.server.assertion.FutureAssertion; -import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.auction.model.BidderPrivacyResult; -import org.prebid.server.auction.model.IpAddress; -import org.prebid.server.auction.model.TimeoutContext; -import org.prebid.server.bidder.BidderCatalog; -import org.prebid.server.bidder.BidderInfo; -import org.prebid.server.exception.InvalidRequestException; -import org.prebid.server.execution.Timeout; -import org.prebid.server.execution.TimeoutFactory; -import org.prebid.server.geolocation.CountryCodeMapper; -import org.prebid.server.geolocation.model.GeoInfo; -import org.prebid.server.metric.MetricName; -import org.prebid.server.metric.Metrics; -import org.prebid.server.model.CaseInsensitiveMultiMap; -import org.prebid.server.privacy.PrivacyExtractor; -import org.prebid.server.privacy.ccpa.Ccpa; -import org.prebid.server.privacy.gdpr.TcfDefinerService; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.RequestLogInfo; -import org.prebid.server.privacy.gdpr.model.TCStringEmpty; -import org.prebid.server.privacy.gdpr.model.TcfContext; -import org.prebid.server.privacy.gdpr.model.TcfResponse; -import org.prebid.server.privacy.model.Privacy; -import org.prebid.server.privacy.model.PrivacyContext; -import org.prebid.server.proto.openrtb.ext.request.ExtRequest; -import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; -import org.prebid.server.proto.openrtb.ext.request.ExtUser; -import org.prebid.server.proto.openrtb.ext.request.ExtUserPrebid; -import org.prebid.server.proto.request.CookieSyncRequest; -import org.prebid.server.settings.model.Account; -import org.prebid.server.settings.model.AccountCcpaConfig; -import org.prebid.server.settings.model.AccountPrivacyConfig; -import org.prebid.server.settings.model.EnabledForRequestType; -import org.prebid.server.spring.config.bidder.model.CompressionType; -import org.prebid.server.spring.config.bidder.model.Ortb; - -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.UnaryOperator; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; -import static java.util.function.UnaryOperator.identity; -import static org.apache.commons.lang3.StringUtils.EMPTY; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anySet; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -public class PrivacyEnforcementServiceTest extends VertxTest { - - private static final String BIDDER_NAME = "someBidder"; - private static final String BUYER_UID = "uidval"; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private BidderCatalog bidderCatalog; - private PrivacyExtractor privacyExtractor; - @Mock - private TcfDefinerService tcfDefinerService; - @Mock - private ImplicitParametersExtractor implicitParametersExtractor; - @Mock - private IpAddressHelper ipAddressHelper; - @Mock - private Metrics metrics; - @Mock - private CountryCodeMapper countryCodeMapper; - @Mock - private ActivityInfrastructure activityInfrastructure; - - private PrivacyEnforcementService privacyEnforcementService; - - @Mock - private BidderAliases aliases; - private Timeout timeout; - - @Before - public void setUp() { - given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, restrictDeviceAndUser()), null))); - given(aliases.resolveBidder(anyString())).willAnswer(invocation -> invocation.getArgument(0)); - given(ipAddressHelper.maskIpv4(anyString())).willReturn("192.168.0.0"); - given(ipAddressHelper.anonymizeIpv6(eq("2001:0db8:85a3:0000:0000:8a2e:0370:7334"))) - .willReturn("2001:0db8:85a3:0000::"); - - given(activityInfrastructure.isAllowed(any(), any())) - .willReturn(true); - - timeout = new TimeoutFactory(Clock.fixed(Instant.now(), ZoneId.systemDefault())).create(500); - - privacyExtractor = new PrivacyExtractor(); - - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, false, false); - } - - @Test - public void contextFromBidRequestShouldReturnTcfContextForCoppa() { - // given - final BidRequest bidRequest = BidRequest.builder() - .regs(Regs.builder().coppa(1).build()) - .build(); - - final TcfContext tcfContext = TcfContext.builder() - .inGdprScope(true) - .consentString("consent") - .consent(TCStringEmpty.create()) - .warnings(emptyList()) - .build(); - given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) - .willReturn(Future.succeededFuture(tcfContext)); - - final AuctionContext auctionContext = AuctionContext.builder() - .bidRequest(bidRequest) - .account(Account.empty("account")) - .timeoutContext(TimeoutContext.of(0, null, 0)) - .prebidErrors(new ArrayList<>()) - .debugWarnings(new ArrayList<>()) - .build(); - - // when - final Future privacyContext = privacyEnforcementService.contextFromBidRequest(auctionContext); - - // then - final Privacy privacy = Privacy.builder() - .gdpr(EMPTY) - .consentString(EMPTY) - .ccpa(Ccpa.EMPTY) - .coppa(1) - .gpp(EMPTY) - .gppSid(emptyList()) - .build(); - - FutureAssertion.assertThat(privacyContext).succeededWith(PrivacyContext.of(privacy, tcfContext)); - } - - @Test - public void contextFromBidRequestShouldReturnTcfContext() { - // given - final String referer = "Referer"; - final BidRequest bidRequest = BidRequest.builder() - .regs(Regs.builder().gdpr(1).usPrivacy("1YYY").build()) - .user(User.builder() - .consent("consent") - .build()) - .site(Site.builder().ref(referer).build()) - .build(); - - final TcfContext tcfContext = TcfContext.builder() - .inGdprScope(true) - .consentString("consent") - .consent(TCStringEmpty.create()) - .warnings(emptyList()) - .build(); - given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) - .willReturn(Future.succeededFuture(tcfContext)); - - final String accountId = "account"; - final MetricName requestType = MetricName.openrtb2web; - - final AuctionContext auctionContext = AuctionContext.builder() - .bidRequest(bidRequest) - .account(Account.empty(accountId)) - .requestTypeMetric(requestType) - .timeoutContext(TimeoutContext.of(0, null, 0)) - .prebidErrors(new ArrayList<>()) - .debugWarnings(new ArrayList<>()) - .build(); - - // when - final Future privacyContext = privacyEnforcementService.contextFromBidRequest(auctionContext); - - // then - final Privacy privacy = Privacy.builder() - .gdpr("1") - .consentString("consent") - .ccpa(Ccpa.of("1YYY")) - .coppa(0) - .gpp(EMPTY) - .gppSid(emptyList()) - .build(); - FutureAssertion.assertThat(privacyContext).succeededWith(PrivacyContext.of(privacy, tcfContext)); - - final RequestLogInfo expectedRequestLogInfo = RequestLogInfo.of(requestType, referer, accountId); - verify(tcfDefinerService).resolveTcfContext( - eq(privacy), isNull(), isNull(), isNull(), same(requestType), - eq(expectedRequestLogInfo), isNull()); - } - - @Test - public void contextFromBidRequestShouldReturnTcfContextWithIpMasked() { - // given - final BidRequest bidRequest = BidRequest.builder() - .regs(Regs.builder().gdpr(1).usPrivacy("1YYY").build()) - .user(User.builder() - .consent("consent") - .build()) - .device(Device.builder() - .lmt(1) - .ip("ip") - .build()) - .build(); - - given(ipAddressHelper.maskIpv4(any())).willReturn("ip-masked"); - - final TcfContext tcfContext = TcfContext.builder() - .inGdprScope(true) - .consentString("consent") - .consent(TCStringEmpty.create()) - .ipAddress("ip-masked") - .inEea(false) - .geoInfo(GeoInfo.builder().vendor("v").country("ua").build()) - .warnings(emptyList()) - .build(); - given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) - .willReturn(Future.succeededFuture(tcfContext)); - - final AuctionContext auctionContext = AuctionContext.builder() - .bidRequest(bidRequest) - .account(Account.empty("account")) - .requestTypeMetric(MetricName.openrtb2web) - .timeoutContext(TimeoutContext.of(0, null, 0)) - .prebidErrors(new ArrayList<>()) - .debugWarnings(new ArrayList<>()) - .build(); - - // when - final Future privacyContext = privacyEnforcementService.contextFromBidRequest(auctionContext); - - // then - final Privacy privacy = Privacy.builder() - .gdpr("1") - .consentString("consent") - .ccpa(Ccpa.of("1YYY")) - .coppa(0) - .gpp(EMPTY) - .gppSid(emptyList()) - .build(); - FutureAssertion.assertThat(privacyContext).succeededWith(PrivacyContext.of(privacy, tcfContext, "ip-masked")); - - verify(tcfDefinerService).resolveTcfContext( - eq(privacy), isNull(), eq("ip-masked"), isNull(), same(MetricName.openrtb2web), any(), isNull()); - } - - @Test - public void contextFromBidRequestShouldCallResolveTcfContextWithIpv6AnonymizedWhenIpNotPresentAndLmtIsOne() { - // given - final BidRequest bidRequest = BidRequest.builder() - .device(Device.builder() - .lmt(1) - .ipv6("ipv6") - .build()) - .build(); - given(ipAddressHelper.anonymizeIpv6(any())).willReturn("ip-masked"); - given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) - .willReturn(Future.succeededFuture(TcfContext.builder().build())); - final AuctionContext auctionContext = AuctionContext.builder() - .bidRequest(bidRequest) - .account(Account.empty("account")) - .timeoutContext(TimeoutContext.of(0, null, 0)) - .prebidErrors(new ArrayList<>()) - .build(); - - // when - privacyEnforcementService.contextFromBidRequest(auctionContext); - - // then - verify(tcfDefinerService).resolveTcfContext(any(), any(), eq("ip-masked"), any(), any(), any(), any()); - } - - @Test - public void contextFromBidRequestShouldCallResolveTcfContextWithIpv6WhenIpv4NotPresent() { - // given - final BidRequest bidRequest = BidRequest.builder() - .device(Device.builder() - .ipv6("ipv6") - .build()) - .build(); - given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) - .willReturn(Future.succeededFuture(TcfContext.builder().build())); - final AuctionContext auctionContext = AuctionContext.builder() - .bidRequest(bidRequest) - .account(Account.empty("account")) - .timeoutContext(TimeoutContext.of(0, null, 0)) - .prebidErrors(new ArrayList<>()) - .build(); - - // when - privacyEnforcementService.contextFromBidRequest(auctionContext); - - // then - verify(tcfDefinerService).resolveTcfContext(any(), any(), eq("ipv6"), any(), any(), any(), any()); - } - - @Test - public void contextFromSetuidRequestShouldReturnContext() { - // given - final HttpServerRequest httpRequest = mock(HttpServerRequest.class); - given(httpRequest.getParam("gdpr")).willReturn("1"); - given(httpRequest.getParam("gdpr_consent")).willReturn("consent"); - final MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - given(httpRequest.headers()).willReturn(headers); - given(httpRequest.remoteAddress()).willReturn(new SocketAddressImpl(1234, "host")); - - given(implicitParametersExtractor.ipFrom(eq(headers), eq("host"))).willReturn(singletonList("ip")); - given(implicitParametersExtractor - .ipFrom(any(CaseInsensitiveMultiMap.class), any())).willReturn(singletonList("ip")); - given(ipAddressHelper.toIpAddress(anyString())).willReturn(IpAddress.of("ip", IpAddress.IP.v4)); - - final TcfContext tcfContext = TcfContext.builder() - .inGdprScope(true) - .consentString("consent") - .consent(TCStringEmpty.create()) - .build(); - given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any())) - .willReturn(Future.succeededFuture(tcfContext)); - - final String accountId = "account"; - - // when - final Future privacyContext = privacyEnforcementService.contextFromSetuidRequest( - httpRequest, Account.empty(accountId), null); - - // then - final Privacy privacy = Privacy.builder() - .gdpr("1") - .consentString("consent") - .ccpa(Ccpa.EMPTY) - .coppa(0) - .gpp(EMPTY) - .gppSid(emptyList()) - .build(); - FutureAssertion.assertThat(privacyContext).succeededWith(PrivacyContext.of(privacy, tcfContext)); - - final RequestLogInfo expectedRequestLogInfo = RequestLogInfo.of(MetricName.setuid, null, accountId); - verify(tcfDefinerService).resolveTcfContext( - eq(privacy), eq("ip"), isNull(), eq(MetricName.setuid), eq(expectedRequestLogInfo), isNull()); - } - - @Test - public void contextFromCookieSyncRequestShouldReturnContext() { - // given - final HttpServerRequest httpRequest = mock(HttpServerRequest.class); - final MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - given(httpRequest.headers()).willReturn(headers); - given(httpRequest.remoteAddress()).willReturn(new SocketAddressImpl(1234, "host")); - - given(implicitParametersExtractor.ipFrom(eq(headers), eq("host"))).willReturn(singletonList("ip")); - given(ipAddressHelper.toIpAddress(anyString())).willReturn(IpAddress.of("ip", IpAddress.IP.v4)); - - final CookieSyncRequest cookieSyncRequest = CookieSyncRequest.builder() - .gdpr(1) - .gdprConsent("consent") - .usPrivacy("1YYY") - .build(); - - final TcfContext tcfContext = TcfContext.builder() - .inGdprScope(true) - .consentString("consent") - .consent(TCStringEmpty.create()) - .build(); - given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any())) - .willReturn(Future.succeededFuture(tcfContext)); - - final String accountId = "account"; - - // when - final Future privacyContext = privacyEnforcementService.contextFromCookieSyncRequest( - cookieSyncRequest, httpRequest, Account.empty(accountId), null); - - // then - final Privacy privacy = Privacy.builder() - .gdpr("1") - .consentString("consent") - .ccpa(Ccpa.of("1YYY")) - .coppa(0) - .gpp(EMPTY) - .gppSid(emptyList()) - .build(); - FutureAssertion.assertThat(privacyContext).succeededWith(PrivacyContext.of(privacy, tcfContext)); - - final RequestLogInfo expectedRequestLogInfo = RequestLogInfo.of(MetricName.cookiesync, null, accountId); - verify(tcfDefinerService).resolveTcfContext( - eq(privacy), eq("ip"), isNull(), eq(MetricName.cookiesync), eq(expectedRequestLogInfo), isNull()); - } - - @Test - public void shouldMaskForCoppaWhenDeviceLmtIsEnforceAndOneAndRegsCoppaIsOneAndDoesNotCallTcfServices() { - // given - final User user = notMaskedUser(notMaskedExtUser()); - final Device device = givenNotMaskedDevice(deviceBuilder -> deviceBuilder.lmt(1)); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.EMPTY, 1); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expected = BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .user(userCoppaMasked(extUserIdsMasked())) - .device(givenCoppaMaskedDevice(deviceBuilder -> deviceBuilder.lmt(1))) - .build(); - assertThat(result).isEqualTo(singletonList(expected)); - - verifyNoInteractions(tcfDefinerService); - } - - @Test - public void shouldMaskForCcpaWhenUsPolicyIsValidAndCoppaIsZero() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, true, false); - - given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) - .willReturn(Future.succeededFuture(TcfResponse.of(true, emptyMap(), null))); - - given(bidderCatalog.bidderInfoByName(BIDDER_NAME)).willReturn(givenBidderInfo(1, true)); - - final User user = notMaskedUser(notMaskedExtUser()); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.of("1YYY"), 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expected = BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .user(userTcfMasked(extUserIdsMasked())) - .device(deviceTcfMasked()) - .build(); - assertThat(result).isEqualTo(singletonList(expected)); - } - - @Test - public void shouldMaskForCcpaWhenAccountHasCppaConfigEnabledForRequestType() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, false, true); - - given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) - .willReturn(Future.succeededFuture(TcfResponse.of(true, emptyMap(), null))); - - given(bidderCatalog.bidderInfoByName(BIDDER_NAME)).willReturn(givenBidderInfo(1, true)); - - final User user = notMaskedUser(notMaskedExtUser()); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.of("1YYY"), 0); - - final AuctionContext context = AuctionContext.builder() - .account(Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - AccountCcpaConfig.builder() - .enabledForRequestType( - EnabledForRequestType.of(false, false, true, false, false)) - .build(), - null, - null, - null)) - .build()) - .requestTypeMetric(MetricName.openrtb2app) - .bidRequest(bidRequest) - .timeoutContext(TimeoutContext.of(0, timeout, 0)) - .privacyContext(privacyContext) - .activityInfrastructure(activityInfrastructure) - .build(); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expected = BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .user(userTcfMasked(extUserIdsMasked())) - .device(deviceTcfMasked()) - .build(); - assertThat(result).isEqualTo(singletonList(expected)); - } - - @Test - public void shouldMaskForCcpaWhenAccountHasCppaEnforcedTrue() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, false, true); - - given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) - .willReturn(Future.succeededFuture(TcfResponse.of(true, emptyMap(), null))); - - given(bidderCatalog.bidderInfoByName(BIDDER_NAME)).willReturn(givenBidderInfo(1, true)); - - final User user = notMaskedUser(notMaskedExtUser()); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.of("1YYY"), 0); - - final AuctionContext context = AuctionContext.builder() - .account(Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - AccountCcpaConfig.builder().enabled(true).build(), - null, - null, - null)) - .build()) - .requestTypeMetric(MetricName.openrtb2app) - .bidRequest(bidRequest) - .timeoutContext(TimeoutContext.of(0, timeout, 0)) - .privacyContext(privacyContext) - .activityInfrastructure(activityInfrastructure) - .build(); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expected = BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .user(userTcfMasked(extUserIdsMasked())) - .device(deviceTcfMasked()) - .build(); - assertThat(result).isEqualTo(singletonList(expected)); - } - - @Test - public void shouldMaskForCcpaWhenAccountHasCcpaConfigEnabled() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, false, true); - - given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) - .willReturn(Future.succeededFuture(TcfResponse.of(true, emptyMap(), null))); - - given(bidderCatalog.bidderInfoByName(BIDDER_NAME)).willReturn(givenBidderInfo(1, true)); - - final User user = notMaskedUser(notMaskedExtUser()); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.of("1YYY"), 0); - - final AuctionContext context = AuctionContext.builder() - .account(Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - AccountCcpaConfig.builder().enabled(true).build(), - null, - null, - null)) - .build()) - .requestTypeMetric(null) - .bidRequest(bidRequest) - .timeoutContext(TimeoutContext.of(0, timeout, 0)) - .privacyContext(privacyContext) - .activityInfrastructure(activityInfrastructure) - .build(); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expected = BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .user(userTcfMasked(extUserIdsMasked())) - .device(deviceTcfMasked()) - .build(); - assertThat(result).isEqualTo(singletonList(expected)); - } - - @Test - public void shouldTolerateEmptyBidderToBidderPrivacyResultList() { - // given - final BidRequest bidRequest = givenBidRequest(emptyList(), - bidRequestBuilder -> bidRequestBuilder - .user(null) - .device(null)); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, emptyMap(), singletonList(BIDDER_NAME), aliases) - .result(); - - // then - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - verifyNoMoreInteractions(tcfDefinerService); - - assertThat(result).isEqualTo(emptyList()); - } - - @Test - public void shouldNotMaskWhenDeviceLmtIsNullAndGdprNotEnforced() { - // given - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, PrivacyEnforcementAction.allowAll()), null))); - - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest( - givenSingleImp(singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .user(notMaskedUser()) - .device(notMaskedDevice()) - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - } - - @Test - public void shouldNotMaskWhenDeviceLmtIsZeroAndCoppaIsZeroAndGdprIsZeroAndTcfDefinerServiceAllowAll() { - // given - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, PrivacyEnforcementAction.allowAll()), null))); - - final User user = notMaskedUser(); - final Device device = givenNotMaskedDevice(deviceBuilder -> deviceBuilder.lmt(0)); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .user(notMaskedUser()) - .device(givenNotMaskedDevice(deviceBuilder -> deviceBuilder.lmt(0))) - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - } - - @Test - public void shouldMaskForTcfWhenTcfServiceAllowAllAndDeviceLmtIsOneAndLmtIsEnforced() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, false, true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, PrivacyEnforcementAction.allowAll()), null))); - - final User user = notMaskedUser(); - final Device device = givenNotMaskedDevice(deviceBuilder -> deviceBuilder.lmt(1)); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .user(userTcfMasked()) - .device(givenTcfMaskedDevice(deviceBuilder -> deviceBuilder.lmt(1))) - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - } - - @Test - public void shouldNotMaskForTcfWhenTcfServiceAllowAllAndDeviceLmtIsOneAndLmtIsNotEnforced() { - // given - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, PrivacyEnforcementAction.allowAll()), null))); - - final User user = notMaskedUser(); - final Device device = givenNotMaskedDevice(deviceBuilder -> deviceBuilder.lmt(1)); - final Regs regs = Regs.builder().coppa(0).build(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device) - .regs(regs)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), BidderAliases.of(null, null, bidderCatalog)) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .user(user) - .device(device) - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - } - - @Test - public void shouldMaskForTcfWhenTcfDefinerServiceRestrictDeviceAndUser() { - // given - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .user(userTcfMasked()) - .device(deviceTcfMasked()) - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - } - - @Test - public void shouldMaskUserIdsWhenTcfDefinerServiceRestrictUserIds() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll(); - privacyEnforcementAction.setRemoveUserIds(true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null))); - - final User user = notMaskedUser(notMaskedExtUser()); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .user(givenNotMaskedUser(userBuilder -> userBuilder - .id(null) - .buyeruid(null) - .consent(null) - .ext(extUserIdsMasked()))) - .device(notMaskedDevice()) - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - - verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), eq(true), anyBoolean(), anyBoolean(), - anyBoolean()); - } - - @Test - public void shouldMaskUserIdsWhenTcfDefinerServiceRestrictUserIdsAndReturnNullWhenAllValuesMasked() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll(); - privacyEnforcementAction.setRemoveUserIds(true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null))); - - final User user = User.builder() - .buyeruid(BUYER_UID) - .eids(singletonList(Eid.of("Test", emptyList(), null))) - .build(); - - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - } - - @Test - public void shouldMaskGeoWhenTcfDefinerServiceRestrictGeo() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll(); - privacyEnforcementAction.setMaskGeo(true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null))); - - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .user(givenNotMaskedUser(userBuilder -> userBuilder.geo(userTcfMasked().getGeo()))) - .device(givenNotMaskedDevice(deviceBuilder -> deviceBuilder.geo(deviceTcfMasked().getGeo()))) - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - - verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), anyBoolean(), eq(true), anyBoolean(), - anyBoolean()); - } - - @Test - public void shouldMaskDeviceIpWhenTcfDefinerServiceRestrictDeviceIp() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll(); - privacyEnforcementAction.setMaskDeviceIp(true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null))); - - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final Device deviceTcfMasked = deviceTcfMasked(); - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .user(notMaskedUser()) - .device(givenNotMaskedDevice( - deviceBuilder -> deviceBuilder.ip(deviceTcfMasked.getIp()).ipv6(deviceTcfMasked.getIpv6()))) - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - } - - @Test - public void shouldMaskDeviceInfoWhenTcfDefinerServiceRestrictDeviceInfo() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll(); - privacyEnforcementAction.setMaskDeviceInfo(true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null))); - - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final Device deviceInfoMasked = givenNotMaskedDevice(deviceBuilder -> deviceBuilder - .ifa(null) - .macsha1(null).macmd5(null) - .dpidsha1(null).dpidmd5(null) - .didsha1(null).didmd5(null)); - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .user(notMaskedUser()) - .device(deviceInfoMasked) - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - } - - @Test - public void shouldSendAnalyticsBlockedMetricIfRestrictedByPrivacyEnforcement() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll(); - privacyEnforcementAction.setBlockAnalyticsReport(true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null))); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp(singletonMap(BIDDER_NAME, 1)), identity()); - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - privacyEnforcementService.mask(context, emptyMap(), singletonList(BIDDER_NAME), aliases); - - // then - verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), anyBoolean(), anyBoolean(), eq(true), - anyBoolean()); - } - - @Test - public void shouldNotSendRelatedMetricsIfBlockBidderRequestEnforcementIsPresent() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll(); - privacyEnforcementAction.setBlockBidderRequest(true); // has highest priority - privacyEnforcementAction.setRemoveUserIds(true); - privacyEnforcementAction.setMaskGeo(true); - privacyEnforcementAction.setBlockAnalyticsReport(true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null))); - - final ExtUser extUser = notMaskedExtUser(); - final User user = notMaskedUser(extUser); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(notMaskedDevice())); - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - privacyEnforcementService.mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases); - - // then - verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), eq(false), eq(false), eq(false), - eq(true)); - } - - @Test - public void shouldNotSendUserIdRemovedMetricIfNoPrivateUserInformation() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll(); - privacyEnforcementAction.setRemoveUserIds(true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null))); - - final User user = User.builder().gender("gender").consent("consent").build(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user)); - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - privacyEnforcementService.mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases); - - // then - verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), eq(false), anyBoolean(), anyBoolean(), - anyBoolean()); - } - - @Test - public void shouldNotSendGeoMaskedMetricIfNoPrivateGeoInformation() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.allowAll(); - privacyEnforcementAction.setMaskGeo(true); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, privacyEnforcementAction), null))); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .device(Device.builder().model("blackberry").build())); - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - privacyEnforcementService.mask(context, emptyMap(), singletonList(BIDDER_NAME), aliases); - - // then - verify(metrics).updateAuctionTcfMetrics(eq(BIDDER_NAME), any(), eq(false), anyBoolean(), anyBoolean(), - anyBoolean()); - } - - @Test - public void shouldRerunEmptyResultWhenTcfDefinerServiceRestrictRequest() { - // given - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, PrivacyEnforcementAction.restrictAll()), null))); - - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .blockedRequestByTcf(true) - .blockedAnalyticsByTcf(true) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - } - - @Test - public void shouldResolveBidderNameAndVendorIdsByAliases() { - // given - final String requestBidder1Name = "bidder1"; - final String requestBidder1Alias = "bidder1Alias"; - final String bidder2Name = "bidder2NotInRequest"; - final String bidder2Alias = "bidder2Alias"; - final Integer bidder2AliasVendorId = 220; - final String requestBidder3Name = "bidder3"; - - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - final HashMap bidderToId = new HashMap<>(); - bidderToId.put(requestBidder1Name, 1); - bidderToId.put(requestBidder1Alias, 2); - bidderToId.put(bidder2Alias, 3); - bidderToId.put(requestBidder3Name, 4); - final BidRequest bidRequest = givenBidRequest( - givenSingleImp(bidderToId), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - final Map bidderToUser = new HashMap<>(); - bidderToUser.put(requestBidder1Name, notMaskedUser()); - bidderToUser.put(requestBidder1Alias, notMaskedUser()); - bidderToUser.put(bidder2Alias, notMaskedUser()); - bidderToUser.put(requestBidder3Name, notMaskedUser()); - - final Map bidderNameToTcfEnforcement = new HashMap<>(); - bidderNameToTcfEnforcement.put(requestBidder1Name, PrivacyEnforcementAction.restrictAll()); - bidderNameToTcfEnforcement.put(requestBidder1Alias, PrivacyEnforcementAction.restrictAll()); - bidderNameToTcfEnforcement.put(bidder2Alias, restrictDeviceAndUser()); - bidderNameToTcfEnforcement.put(requestBidder3Name, PrivacyEnforcementAction.allowAll()); - - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture(TcfResponse.of(true, bidderNameToTcfEnforcement, null))); - - given(aliases.resolveBidder(eq(requestBidder1Alias))).willReturn(requestBidder1Name); - given(aliases.resolveBidder(eq(bidder2Alias))).willReturn(bidder2Name); - given(aliases.resolveAliasVendorId(eq(bidder2Alias))).willReturn(bidder2AliasVendorId); - - // when - final List bidders = - asList(requestBidder1Name, requestBidder1Alias, bidder2Alias, requestBidder3Name); - final List result = privacyEnforcementService - .mask(context, bidderToUser, bidders, aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidder1Masked = BidderPrivacyResult.builder() - .requestBidder(requestBidder1Name) - .blockedAnalyticsByTcf(true) - .blockedRequestByTcf(true) - .build(); - final BidderPrivacyResult expectedBidderAlias1Masked = BidderPrivacyResult.builder() - .requestBidder(requestBidder1Alias) - .blockedAnalyticsByTcf(true) - .blockedRequestByTcf(true) - .build(); - final BidderPrivacyResult expectedBidderAlias2Masked = BidderPrivacyResult.builder() - .requestBidder(bidder2Alias) - .user(userTcfMasked()) - .device(deviceTcfMasked()) - .build(); - final BidderPrivacyResult expectedBidder3Masked = BidderPrivacyResult.builder() - .requestBidder(requestBidder3Name) - .user(notMaskedUser()) - .device(notMaskedDevice()) - .build(); - assertThat(result).containsOnly( - expectedBidder1Masked, expectedBidderAlias1Masked, expectedBidderAlias2Masked, expectedBidder3Masked); - - final Set bidderNames = new HashSet<>(asList( - requestBidder1Name, requestBidder1Alias, bidder2Alias, requestBidder3Name)); - verify(tcfDefinerService).resultForBidderNames(eq(bidderNames), any(), any(), any()); - } - - @Test - public void shouldNotReturnUserIfMaskingAppliedAndUserBecameEmptyObject() { - // given - final User user = User.builder() - .buyeruid("buyeruid") - .eids(singletonList(Eid.of("Test", emptyList(), null))) - .build(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 1); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidderPrivacy = BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .build(); - assertThat(result).containsOnly(expectedBidderPrivacy); - } - - @Test - public void shouldReturnFailedFutureWhenTcfServiceIsReturnFailedFuture() { - // given - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.failedFuture(new InvalidRequestException( - "Error when retrieving allowed purpose ids in a reason of invalid consent string"))); - - final User user = notMaskedUser(); - final Device device = givenNotMaskedDevice(deviceBuilder -> deviceBuilder.lmt(1)); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("0", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final Future> firstFuture = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases); - - // then - assertThat(firstFuture.failed()).isTrue(); - assertThat(firstFuture.cause().getMessage()) - .isEqualTo("Error when retrieving allowed purpose ids in a reason of invalid consent string"); - - verify(tcfDefinerService).resultForBidderNames(eq(singleton(BIDDER_NAME)), any(), any(), any()); - verifyNoMoreInteractions(tcfDefinerService); - } - - @Test - public void shouldMaskForCcpaAndTcfWhenUsPolicyIsValidAndGdprIsEnforcedAndCOPPAIsZero() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, true, false); - - final String bidder1Name = "bidder1Name"; - final String bidder2Name = "bidder2Name"; - final String bidder3Name = "bidder3Name"; - - given(bidderCatalog.bidderInfoByName(bidder1Name)).willReturn(givenBidderInfo(1, true)); - given(bidderCatalog.bidderInfoByName(bidder2Name)).willReturn(givenBidderInfo(2, true)); - given(bidderCatalog.bidderInfoByName(bidder3Name)).willReturn(givenBidderInfo(3, false)); - - final Map bidderNameToAction = new HashMap<>(); - bidderNameToAction.put(bidder2Name, PrivacyEnforcementAction.restrictAll()); - bidderNameToAction.put(bidder3Name, PrivacyEnforcementAction.restrictAll()); - given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) - .willReturn(Future.succeededFuture(TcfResponse.of(true, bidderNameToAction, null))); - - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - - final Map bidderToUser = new HashMap<>(); - bidderToUser.put(bidder1Name, notMaskedUser()); - bidderToUser.put(bidder2Name, notMaskedUser()); - bidderToUser.put(bidder3Name, notMaskedUser()); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device) - .ext(ExtRequest.of(ExtRequestPrebid.builder() - .nosale(singletonList(bidder2Name)) - .build()))); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.of("1YYY"), 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService.mask( - context, - bidderToUser, - asList(bidder1Name, bidder2Name, bidder3Name), - BidderAliases.of(null, null, bidderCatalog)) - .result(); - - // then - assertThat(result).containsOnly( - BidderPrivacyResult.builder() - .requestBidder(bidder1Name) - .device(deviceTcfMasked()) - .user(userTcfMasked()) - .blockedRequestByTcf(false) - .blockedAnalyticsByTcf(false) - .build(), - BidderPrivacyResult.builder() - .requestBidder(bidder2Name) - .blockedRequestByTcf(true) - .blockedAnalyticsByTcf(true) - .build(), - BidderPrivacyResult.builder() - .requestBidder(bidder3Name) - .blockedRequestByTcf(true) - .blockedAnalyticsByTcf(true) - .build()); - } - - @Test - public void shouldNotMaskForCcpaWhenCatchAllWildcardIsPresentInNosaleList() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, true, false); - - given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap(BIDDER_NAME, PrivacyEnforcementAction.allowAll()), null))); - - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - - final Map bidderToUser = singletonMap(BIDDER_NAME, notMaskedUser()); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device) - .ext(ExtRequest.of(ExtRequestPrebid.builder() - .nosale(singletonList("*")) - .build()))); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.of("1YYY"), 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), BidderAliases.of(null, null, bidderCatalog)) - .result(); - - // then - assertThat(result).containsOnly( - BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .user(notMaskedUser()) - .device(notMaskedDevice()) - .blockedRequestByTcf(false) - .blockedAnalyticsByTcf(false) - .build()); - } - - @Test - public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsFalseInConfigurationAndNullInAccount() { - // given - final Ccpa ccpa = Ccpa.of("1YYY"); - final Account account = Account.builder().build(); - - // when and then - assertThat(privacyEnforcementService.isCcpaEnforced(ccpa, account)).isFalse(); - } - - @Test - public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrueInConfigurationAndFalseInAccount() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, true, false); - - final Ccpa ccpa = Ccpa.of("1YYY"); - final Account account = Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - AccountCcpaConfig.builder().enabled(false).build(), - null, - null, - null)) - .build(); - - // when and then - assertThat(privacyEnforcementService.isCcpaEnforced(ccpa, account)).isFalse(); - } - - @Test - public void isCcpaEnforcedShouldReturnFalseWhenEnforcedPropertyIsTrue() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, true, false); - - final Ccpa ccpa = Ccpa.of("1YNY"); - final Account account = Account.builder().build(); - - // when and then - assertThat(privacyEnforcementService.isCcpaEnforced(ccpa, account)).isFalse(); - } - - @Test - public void isCcpaEnforcedShouldReturnFalseWhenAccountCcpaConfigHasEnabledTrue() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, false, true); - - final Ccpa ccpa = Ccpa.of("1YYY"); - final Account account = Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - AccountCcpaConfig.builder().enabled(true).build(), - null, - null, - null)) - .build(); - - // when and then - assertThat(privacyEnforcementService.isCcpaEnforced(ccpa, account)).isTrue(); - } - - @Test - public void isCcpaEnforcedShouldReturnTrueWhenEnforcedPropertyIsTrueAndCcpaReturnsTrue() { - // given - privacyEnforcementService = new PrivacyEnforcementService( - bidderCatalog, privacyExtractor, tcfDefinerService, implicitParametersExtractor, ipAddressHelper, - metrics, countryCodeMapper, true, false); - - final Ccpa ccpa = Ccpa.of("1YYY"); - final Account account = Account.builder().build(); - - // when and then - assertThat(privacyEnforcementService.isCcpaEnforced(ccpa, account)).isTrue(); - } - - @Test - public void shouldReturnCorrectMaskedForMultipleBidders() { - // given - final String bidder1Name = "bidder1"; - final String bidder2Name = "bidder2"; - final String bidder3Name = "bidder3"; - - final Map vendorIdToTcfEnforcement = new HashMap<>(); - vendorIdToTcfEnforcement.put(bidder1Name, PrivacyEnforcementAction.restrictAll()); - vendorIdToTcfEnforcement.put(bidder2Name, restrictDeviceAndUser()); - vendorIdToTcfEnforcement.put(bidder3Name, PrivacyEnforcementAction.allowAll()); - given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) - .willReturn(Future.succeededFuture(TcfResponse.of(true, vendorIdToTcfEnforcement, null))); - - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - final Map bidderToUser = new HashMap<>(); - bidderToUser.put(bidder1Name, notMaskedUser()); - bidderToUser.put(bidder2Name, notMaskedUser()); - bidderToUser.put(bidder3Name, notMaskedUser()); - final List bidders = asList(bidder1Name, bidder2Name, bidder3Name); - - final HashMap bidderToId = new HashMap<>(); - bidderToId.put(bidder1Name, 1); - bidderToId.put(bidder2Name, 2); - bidderToId.put(bidder3Name, 3); - final BidRequest bidRequest = givenBidRequest( - givenSingleImp(bidderToId), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, bidders, aliases) - .result(); - - // then - final BidderPrivacyResult expectedBidder1Masked = BidderPrivacyResult.builder() - .requestBidder(bidder1Name) - .blockedAnalyticsByTcf(true) - .blockedRequestByTcf(true) - .build(); - final BidderPrivacyResult expectedBidder2Masked = BidderPrivacyResult.builder() - .requestBidder(bidder2Name) - .user(userTcfMasked()) - .device(deviceTcfMasked()) - .build(); - final BidderPrivacyResult expectedBidder3Masked = BidderPrivacyResult.builder() - .requestBidder(bidder3Name) - .user(notMaskedUser()) - .device(notMaskedDevice()) - .build(); - assertThat(result).hasSize(3) - .containsOnly(expectedBidder1Masked, expectedBidder2Masked, expectedBidder3Masked); - - final HashSet bidderNames = new HashSet<>(asList(bidder1Name, bidder2Name, bidder3Name)); - verify(tcfDefinerService).resultForBidderNames(eq(bidderNames), any(), any(), any()); - } - - @Test - public void shouldIncrementCcpaAndAuctionTcfMetrics() { - // given - final User user = notMaskedUser(); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap("someAlias", user); - - final BidRequest bidRequest = givenBidRequest(givenSingleImp( - singletonMap("someAlias", 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext("1", Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - given(tcfDefinerService.resultForBidderNames(anySet(), any(), any(), any())) - .willReturn(Future.succeededFuture( - TcfResponse.of(true, singletonMap("someAlias", restrictDeviceAndUser()), null))); - - given(aliases.resolveBidder(eq("someAlias"))).willReturn(BIDDER_NAME); - - // when - privacyEnforcementService.mask(context, bidderToUser, singletonList("someAlias"), aliases); - - // then - verify(metrics).updatePrivacyCcpaMetrics(eq(false), eq(false)); - verify(metrics).updateAuctionTcfMetrics( - eq(BIDDER_NAME), eq(MetricName.openrtb2web), eq(true), eq(true), eq(false), eq(false)); - } - - @Test - public void shouldMaskCorrespondingToActivitiesRestrictions() { - // given - given(activityInfrastructure.isAllowed(eq(Activity.TRANSMIT_UFPD), any())).willReturn(false); - given(activityInfrastructure.isAllowed(eq(Activity.TRANSMIT_GEO), any())).willReturn(false); - given(ipAddressHelper.anonymizeIpv6(eq("2001:0db8:85a3:0000::"))).willReturn("2001:0db8:85a3:0000::"); - - final User user = User.builder() - .id("id") - .buyeruid("buyeruid") - .yob(1) - .gender("gender") - .data(emptyList()) - .eids(emptyList()) - .geo(Geo.builder().lon(-85.34321F).lat(189.342323F).build()) - .ext(ExtUser.builder().data(mapper.createObjectNode()).build()) - .build(); - final Device device = notMaskedDevice(); - final Map bidderToUser = singletonMap(BIDDER_NAME, user); - - final BidRequest bidRequest = givenBidRequest( - givenSingleImp(singletonMap(BIDDER_NAME, 1)), - bidRequestBuilder -> bidRequestBuilder - .user(user) - .device(device)); - - final PrivacyContext privacyContext = givenPrivacyContext(null, Ccpa.EMPTY, 0); - - final AuctionContext context = auctionContext(bidRequest, privacyContext); - - // when - final List result = privacyEnforcementService - .mask(context, bidderToUser, singletonList(BIDDER_NAME), aliases) - .result(); - - // then - final BidderPrivacyResult expected = BidderPrivacyResult.builder() - .requestBidder(BIDDER_NAME) - .user(User.builder() - .id(null) - .buyeruid(null) - .yob(null) - .gender(null) - .data(null) - .eids(null) - .geo(Geo.builder().lon(-85.34F).lat(189.34F).build()) - .ext(null) - .build()) - .device(deviceTcfMasked()) - .build(); - assertThat(result).isEqualTo(singletonList(expected)); - } - - @Test - public void maskUserConsideringActivityRestrictionsShouldReturnSameIfNoRestrictionsApplied() { - // given - final User user = User.builder().build(); - - // when - final User result = privacyEnforcementService.maskUserConsideringActivityRestrictions(user, false, false); - - // then - assertThat(result).isSameAs(user); - } - - @Test - public void maskUserConsideringActivityRestrictionsShouldReturnMaskedUser() { - // given - final User user = User.builder() - .id("id") - .buyeruid("buyeruid") - .yob(1) - .gender("gender") - .data(emptyList()) - .eids(emptyList()) - .geo(Geo.builder().lon(-85.34321F).lat(189.342323F).build()) - .ext(ExtUser.builder().data(mapper.createObjectNode()).build()) - .build(); - - // when - final User result = privacyEnforcementService.maskUserConsideringActivityRestrictions(user, true, true); - - // then - assertThat(result).isEqualTo(User.builder() - .id(null) - .buyeruid(null) - .yob(null) - .gender(null) - .data(null) - .eids(null) - .geo(Geo.builder().lon(-85.34F).lat(189.34F).build()) - .ext(null) - .build()); - } - - @Test - public void maskDeviceConsideringActivityRestrictionsShouldReturnSameIfNoRestrictionsApplied() { - // given - final Device device = Device.builder().build(); - - // when - final Device result = privacyEnforcementService.maskDeviceConsideringActivityRestrictions(device, false, false); - - // then - assertThat(result).isSameAs(device); - } - - @Test - public void maskDeviceConsideringActivityRestrictionsShouldReturnMaskedUser() { - // given - final Device device = notMaskedDevice(); - - // when - final Device result = privacyEnforcementService.maskDeviceConsideringActivityRestrictions(device, true, true); - - // then - assertThat(result).isEqualTo(deviceTcfMasked()); - } - - private AuctionContext auctionContext(BidRequest bidRequest, PrivacyContext privacyContext) { - return AuctionContext.builder() - .account(Account.builder().build()) - .requestTypeMetric(MetricName.openrtb2web) - .bidRequest(bidRequest) - .timeoutContext(TimeoutContext.of(0, timeout, 0)) - .privacyContext(privacyContext) - .activityInfrastructure(activityInfrastructure) - .build(); - } - - private static Device notMaskedDevice() { - return Device.builder() - .ip("192.168.0.10") - .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") - .geo(Geo.builder().lon(-85.34321F).lat(189.342323F).country("US").build()) - .ifa("ifa") - .macsha1("macsha1") - .macmd5("macmd5") - .didsha1("didsha1") - .didmd5("didmd5") - .dpidsha1("dpidsha1") - .dpidmd5("dpidmd5") - .build(); - } - - private static User notMaskedUser() { - return User.builder() - .id("id") - .buyeruid(BUYER_UID) - .geo(Geo.builder().lon(-85.1245F).lat(189.9531F).country("US").build()) - .consent("consent") - .build(); - } - - private static User notMaskedUser(ExtUser extUser) { - return User.builder() - .id("id") - .buyeruid(BUYER_UID) - .geo(Geo.builder().lon(-85.1245F).lat(189.9531F).country("US").build()) - .eids(singletonList(Eid.of("Test", emptyList(), null))) - .ext(extUser) - .build(); - } - - private static ExtUser notMaskedExtUser() { - return ExtUser.builder() - .digitrust(mapper.createObjectNode()) - .prebid(ExtUserPrebid.of(singletonMap("key", "value"))) - .build(); - } - - private static ExtUser extUserIdsMasked() { - return ExtUser.builder() - .prebid(ExtUserPrebid.of(singletonMap("key", "value"))) - .build(); - } - - private static Device deviceCoppaMasked() { - return Device.builder() - .ip("192.168.0.0") - .ipv6("2001:0db8:85a3:0000::") - .geo(Geo.builder().country("US").build()) - .build(); - } - - private static User userCoppaMasked(ExtUser extUser) { - return User.builder() - .geo(Geo.builder().country("US").build()) - .ext(extUser) - .build(); - } - - private static Device deviceTcfMasked() { - return Device.builder() - .ip("192.168.0.0") - .ipv6("2001:0db8:85a3:0000::") - .geo(Geo.builder().lon(-85.34F).lat(189.34F).country("US").build()) - .build(); - } - - private static User userTcfMasked() { - return User.builder() - .buyeruid(null) - .geo(Geo.builder().lon(-85.12F).lat(189.95F).country("US").build()) - .consent("consent") - .build(); - } - - private static User userTcfMasked(ExtUser extUser) { - return User.builder() - .buyeruid(null) - .geo(Geo.builder().lon(-85.12F).lat(189.95F).country("US").build()) - .ext(extUser) - .build(); - } - - private static BidRequest givenBidRequest(List imp, - UnaryOperator bidRequestBuilderCustomizer) { - return bidRequestBuilderCustomizer.apply(BidRequest.builder().cur(singletonList("USD")).imp(imp)).build(); - } - - private static Device givenNotMaskedDevice(UnaryOperator deviceBuilderCustomizer) { - return deviceBuilderCustomizer.apply(notMaskedDevice().toBuilder()).build(); - } - - private static User givenNotMaskedUser(UnaryOperator deviceBuilderCustomizer) { - return deviceBuilderCustomizer.apply(notMaskedUser().toBuilder()).build(); - } - - private static Device givenTcfMaskedDevice(UnaryOperator deviceBuilderCustomizer) { - return deviceBuilderCustomizer.apply(deviceTcfMasked().toBuilder()).build(); - } - - private static Device givenCoppaMaskedDevice(UnaryOperator deviceBuilderCustomizer) { - return deviceBuilderCustomizer.apply(deviceCoppaMasked().toBuilder()).build(); - } - - private static List givenSingleImp(T ext) { - return singletonList(givenImp(ext, identity())); - } - - private static Imp givenImp(T ext, UnaryOperator impBuilderCustomizer) { - return impBuilderCustomizer.apply(Imp.builder().ext(mapper.valueToTree(ext))).build(); - } - - private PrivacyContext givenPrivacyContext(String gdpr, Ccpa ccpa, Integer coppa) { - return PrivacyContext.of( - Privacy.builder().gdpr(gdpr).consentString(EMPTY).ccpa(ccpa).coppa(coppa).build(), - TcfContext.empty()); - } - - private static PrivacyEnforcementAction restrictDeviceAndUser() { - return PrivacyEnforcementAction.builder() - .maskDeviceInfo(true) - .maskDeviceIp(true) - .maskGeo(true) - .removeUserIds(true) - .build(); - } - - private static BidderInfo givenBidderInfo(int gdprVendorId, boolean enforceCcpa) { - return BidderInfo.create( - true, - null, - true, - null, - null, - null, - null, - null, - null, - null, - gdprVendorId, - enforceCcpa, - false, - CompressionType.NONE, - Ortb.of(false)); - } -} diff --git a/src/test/java/org/prebid/server/auction/privacycontextfactory/AmpPrivacyContextFactoryTest.java b/src/test/java/org/prebid/server/auction/privacy/contextfactory/AmpPrivacyContextFactoryTest.java similarity index 68% rename from src/test/java/org/prebid/server/auction/privacycontextfactory/AmpPrivacyContextFactoryTest.java rename to src/test/java/org/prebid/server/auction/privacy/contextfactory/AmpPrivacyContextFactoryTest.java index fc6d366dc15..f1175e02748 100644 --- a/src/test/java/org/prebid/server/auction/privacycontextfactory/AmpPrivacyContextFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/privacy/contextfactory/AmpPrivacyContextFactoryTest.java @@ -1,4 +1,4 @@ -package org.prebid.server.auction.privacycontextfactory; +package org.prebid.server.auction.privacy.contextfactory; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; @@ -55,12 +55,15 @@ public class AmpPrivacyContextFactoryTest extends VertxTest { @Mock private CountryCodeMapper countryCodeMapper; - private AmpPrivacyContextFactory ampPrivacyContextFactory; + private AmpPrivacyContextFactory target; @Before public void setUp() { - ampPrivacyContextFactory = new AmpPrivacyContextFactory( - privacyExtractor, tcfDefinerService, ipAddressHelper, countryCodeMapper); + target = new AmpPrivacyContextFactory( + privacyExtractor, + tcfDefinerService, + ipAddressHelper, + countryCodeMapper); } @Test @@ -70,20 +73,18 @@ public void contextFromShouldExtractInitialPrivacy() { .gdpr("") .consentString("") .ccpa(Ccpa.EMPTY) + .coppa(0) .build(); - given(privacyExtractor.validPrivacyFrom(any(), any())) - .willReturn(emptyPrivacy); - given(privacyExtractor.toValidPrivacy(any(), any(), any(), any(), any(), any(), any())) - .willReturn(emptyPrivacy); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(emptyPrivacy); given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) .willReturn(Future.succeededFuture(TcfContext.empty())); final AuctionContext auctionContext = givenAuctionContext( - contextBuilder -> contextBuilder.httpRequest(givenHttpRequestContext("invalid"))); + context -> context.httpRequest(givenHttpRequestContext("invalid"))); // when - ampPrivacyContextFactory.contextFrom(auctionContext); + target.contextFrom(auctionContext); // then verify(privacyExtractor).validPrivacyFrom(any(), anyList()); @@ -96,20 +97,18 @@ public void contextFromShouldAddTcfExtractionWarningsToAuctionDebugWarningsWhenI .gdpr("") .consentString("") .ccpa(Ccpa.EMPTY) + .coppa(0) .build(); - given(privacyExtractor.validPrivacyFrom(any(), any())) - .willReturn(emptyPrivacy); - given(privacyExtractor.toValidPrivacy(any(), any(), any(), any(), any(), any(), any())) - .willReturn(emptyPrivacy); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(emptyPrivacy); given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) .willReturn(Future.succeededFuture(TcfContext.builder().warnings(singletonList("Error")).build())); final AuctionContext auctionContext = givenAuctionContext( - contextBuilder -> contextBuilder.httpRequest(givenHttpRequestContext(null))); + context -> context.httpRequest(givenHttpRequestContext(null))); // when - ampPrivacyContextFactory.contextFrom(auctionContext); + target.contextFrom(auctionContext); // then assertThat(auctionContext.getDebugWarnings()).containsExactly("Error"); @@ -122,6 +121,7 @@ public void contextFromShouldNotRemoveConsentStringOnEmptyConsentTypeParam() { .gdpr("1") .consentString("consent_string") .ccpa(Ccpa.EMPTY) + .coppa(0) .build(); given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(privacy); @@ -129,13 +129,13 @@ public void contextFromShouldNotRemoveConsentStringOnEmptyConsentTypeParam() { .willReturn(Future.succeededFuture(TcfContext.empty())); final AuctionContext auctionContext = givenAuctionContext( - contextBuilder -> contextBuilder.httpRequest(givenHttpRequestContext(null))); + context -> context.httpRequest(givenHttpRequestContext(null))); // when - final Future result = ampPrivacyContextFactory.contextFrom(auctionContext); + final PrivacyContext result = target.contextFrom(auctionContext).result(); // then - assertThat(result.result().getPrivacy()).isEqualTo(privacy); + assertThat(result.getPrivacy()).isEqualTo(privacy); assertThat(auctionContext.getPrebidErrors()).isEmpty(); } @@ -146,39 +146,24 @@ public void contextFromShouldRemoveConsentStringAndEmitErrorOnTcf1ConsentTypePar .gdpr("1") .consentString("consent_string") .ccpa(Ccpa.EMPTY) + .coppa(0) .build(); - given(privacyExtractor.validPrivacyFrom(any(), any())) - .willReturn(privacy); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(privacy); given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) .willReturn(Future.succeededFuture(TcfContext.empty())); final AuctionContext auctionContext = givenAuctionContext( - contextBuilder -> contextBuilder.httpRequest(givenHttpRequestContext("1"))); + context -> context.httpRequest(givenHttpRequestContext("1"))); // when - final Future result = ampPrivacyContextFactory.contextFrom(auctionContext); + final PrivacyContext result = target.contextFrom(auctionContext).result(); // then - assertThat(result.result().getPrivacy()).isEqualTo(privacy.withoutConsent()); + assertThat(result.getPrivacy()).isEqualTo(privacy.withoutConsent()); assertThat(auctionContext.getPrebidErrors()).containsExactly("Consent type tcfV1 is no longer supported"); } - private static AuctionContext givenAuctionContext( - UnaryOperator auctionContextCustomizer) { - - final AuctionContext.AuctionContextBuilder defaultAuctionContextBuilder = - AuctionContext.builder() - .httpRequest(givenHttpRequestContext(null)) - .debugWarnings(new ArrayList<>()) - .account(Account.builder().build()) - .prebidErrors(new ArrayList<>()) - .bidRequest(givenBidRequest(identity())) - .timeoutContext(TimeoutContext.of(0, null, 0)); - - return auctionContextCustomizer.apply(defaultAuctionContextBuilder).build(); - } - @Test public void contextFromShouldMaskIpV4WhenCoppaEqualsToOneAndIpV4Present() { // given @@ -188,27 +173,21 @@ public void contextFromShouldMaskIpV4WhenCoppaEqualsToOneAndIpV4Present() { .ccpa(Ccpa.of("1YYY")) .coppa(1) .build(); - given(privacyExtractor.validPrivacyFrom(any(), any())) - .willReturn(privacy); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(privacy); - given(ipAddressHelper.maskIpv4(anyString())) - .willReturn("maskedIpV4"); - given(ipAddressHelper.anonymizeIpv6(anyString())) - .willReturn("maskedIpV6"); + given(ipAddressHelper.maskIpv4(anyString())).willReturn("maskedIpV4"); + given(ipAddressHelper.anonymizeIpv6(anyString())).willReturn("maskedIpV6"); given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) .willReturn(Future.succeededFuture(TcfContext.empty())); - final BidRequest bidRequest = givenBidRequest(bidRequestBuilder -> - bidRequestBuilder.device(Device.builder().ip("ip").build())); - - final AuctionContext auctionContext = givenAuctionContext(auctionContextBuilder -> - auctionContextBuilder - .httpRequest(givenHttpRequestContext("invalid")) - .bidRequest(bidRequest)); + final BidRequest bidRequest = givenBidRequest(request -> request.device(Device.builder().ip("ip").build())); + final AuctionContext auctionContext = givenAuctionContext(context -> context + .httpRequest(givenHttpRequestContext("invalid")) + .bidRequest(bidRequest)); // when - ampPrivacyContextFactory.contextFrom(auctionContext); + target.contextFrom(auctionContext); // then verify(ipAddressHelper).maskIpv4(anyString()); @@ -223,27 +202,21 @@ public void contextFromShouldMaskIpV6WhenCoppaEqualsToOneAndIpV6Present() { .ccpa(Ccpa.of("1YYY")) .coppa(1) .build(); - given(privacyExtractor.validPrivacyFrom(any(), any())) - .willReturn(privacy); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(privacy); - given(ipAddressHelper.maskIpv4(anyString())) - .willReturn("maskedIpV4"); - given(ipAddressHelper.anonymizeIpv6(anyString())) - .willReturn("maskedIpV6"); + given(ipAddressHelper.maskIpv4(anyString())).willReturn("maskedIpV4"); + given(ipAddressHelper.anonymizeIpv6(anyString())).willReturn("maskedIpV6"); given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) .willReturn(Future.succeededFuture(TcfContext.empty())); - final BidRequest bidRequest = givenBidRequest(bidRequestBuilder -> - bidRequestBuilder.device(Device.builder().ipv6("ipV6").build())); - - final AuctionContext auctionContext = givenAuctionContext(auctionContextBuilder -> - auctionContextBuilder - .httpRequest(givenHttpRequestContext("invalid")) - .bidRequest(bidRequest)); + final BidRequest bidRequest = givenBidRequest(request -> request.device(Device.builder().ipv6("ipV6").build())); + final AuctionContext auctionContext = givenAuctionContext(context -> context + .httpRequest(givenHttpRequestContext("invalid")) + .bidRequest(bidRequest)); // when - ampPrivacyContextFactory.contextFrom(auctionContext); + target.contextFrom(auctionContext); // then verify(ipAddressHelper).anonymizeIpv6(anyString()); @@ -258,23 +231,19 @@ public void contextFromShouldAddRefUrlWhenPresentAndRequestTypeIsWeb() { .ccpa(Ccpa.EMPTY) .coppa(0) .build(); - given(privacyExtractor.validPrivacyFrom(any(), any())) - .willReturn(privacy); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(privacy); given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) .willReturn(Future.succeededFuture(TcfContext.empty())); - final BidRequest bidRequest = givenBidRequest(bidRequestBuilder -> - bidRequestBuilder.site(Site.builder().ref("refUrl").build())); - - final AuctionContext auctionContext = givenAuctionContext(auctionContextBuilder -> - auctionContextBuilder - .requestTypeMetric(MetricName.openrtb2web) - .httpRequest(givenHttpRequestContext("invalid")) - .bidRequest(bidRequest)); + final BidRequest bidRequest = givenBidRequest(request -> request.site(Site.builder().ref("refUrl").build())); + final AuctionContext auctionContext = givenAuctionContext(context -> context + .requestTypeMetric(MetricName.openrtb2web) + .httpRequest(givenHttpRequestContext("invalid")) + .bidRequest(bidRequest)); // when - ampPrivacyContextFactory.contextFrom(auctionContext); + target.contextFrom(auctionContext); // then final RequestLogInfo expectedRequestLogInfo = RequestLogInfo.of(MetricName.openrtb2web, "refUrl", null); @@ -282,6 +251,21 @@ public void contextFromShouldAddRefUrlWhenPresentAndRequestTypeIsWeb() { .resolveTcfContext(any(), any(), any(), any(), any(), eq(expectedRequestLogInfo), any()); } + private static AuctionContext givenAuctionContext( + UnaryOperator auctionContextCustomizer) { + + final AuctionContext.AuctionContextBuilder defaultAuctionContextBuilder = + AuctionContext.builder() + .httpRequest(givenHttpRequestContext(null)) + .debugWarnings(new ArrayList<>()) + .account(Account.builder().build()) + .prebidErrors(new ArrayList<>()) + .bidRequest(givenBidRequest(identity())) + .timeoutContext(TimeoutContext.of(0L, null, 0)); + + return auctionContextCustomizer.apply(defaultAuctionContextBuilder).build(); + } + private static BidRequest givenBidRequest(UnaryOperator bidRequestCustomizer) { return bidRequestCustomizer.apply(BidRequest.builder()).build(); } diff --git a/src/test/java/org/prebid/server/auction/privacy/contextfactory/AuctionPrivacyContextFactoryTest.java b/src/test/java/org/prebid/server/auction/privacy/contextfactory/AuctionPrivacyContextFactoryTest.java new file mode 100644 index 00000000000..d656ce757cf --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/contextfactory/AuctionPrivacyContextFactoryTest.java @@ -0,0 +1,234 @@ +package org.prebid.server.auction.privacy.contextfactory; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Site; +import io.vertx.core.Future; +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.VertxTest; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.TimeoutContext; +import org.prebid.server.geolocation.CountryCodeMapper; +import org.prebid.server.metric.MetricName; +import org.prebid.server.model.CaseInsensitiveMultiMap; +import org.prebid.server.model.HttpRequestContext; +import org.prebid.server.privacy.PrivacyExtractor; +import org.prebid.server.privacy.ccpa.Ccpa; +import org.prebid.server.privacy.gdpr.TcfDefinerService; +import org.prebid.server.privacy.gdpr.model.RequestLogInfo; +import org.prebid.server.privacy.gdpr.model.TcfContext; +import org.prebid.server.privacy.model.Privacy; +import org.prebid.server.settings.model.Account; + +import java.util.ArrayList; +import java.util.function.UnaryOperator; + +import static java.util.Collections.singletonList; +import static java.util.function.UnaryOperator.identity; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +public class AuctionPrivacyContextFactoryTest extends VertxTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private PrivacyExtractor privacyExtractor; + @Mock + private TcfDefinerService tcfDefinerService; + @Mock + private IpAddressHelper ipAddressHelper; + @Mock + private CountryCodeMapper countryCodeMapper; + + private AuctionPrivacyContextFactory target; + + @Before + public void setUp() { + target = new AuctionPrivacyContextFactory( + privacyExtractor, + tcfDefinerService, + ipAddressHelper, + countryCodeMapper); + } + + @Test + public void contextFromShouldExtractInitialPrivacy() { + // given + final Privacy emptyPrivacy = Privacy.builder() + .gdpr("") + .consentString("") + .ccpa(Ccpa.EMPTY) + .coppa(0) + .build(); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(emptyPrivacy); + + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.empty())); + + final AuctionContext auctionContext = givenAuctionContext( + context -> context.httpRequest(givenHttpRequestContext("invalid"))); + + // when + target.contextFrom(auctionContext); + + // then + verify(privacyExtractor).validPrivacyFrom(any(), anyList()); + } + + @Test + public void contextFromShouldAddTcfExtractionWarningsToAuctionDebugWarningsWhenInGdprScope() { + // given + final Privacy emptyPrivacy = Privacy.builder() + .gdpr("") + .consentString("") + .ccpa(Ccpa.EMPTY) + .coppa(0) + .build(); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(emptyPrivacy); + + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.builder().warnings(singletonList("Error")).build())); + + final AuctionContext auctionContext = givenAuctionContext( + context -> context.httpRequest(givenHttpRequestContext(null))); + + // when + target.contextFrom(auctionContext); + + // then + assertThat(auctionContext.getDebugWarnings()).containsExactly("Error"); + } + + @Test + public void contextFromShouldMaskIpV4WhenCoppaEqualsToOneAndIpV4Present() { + // given + final Privacy privacy = Privacy.builder() + .gdpr("1") + .consentString("consent_string") + .ccpa(Ccpa.of("1YYY")) + .coppa(1) + .build(); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(privacy); + + given(ipAddressHelper.maskIpv4(anyString())).willReturn("maskedIpV4"); + given(ipAddressHelper.anonymizeIpv6(anyString())).willReturn("maskedIpV6"); + + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.empty())); + + final BidRequest bidRequest = givenBidRequest(request -> request.device(Device.builder().ip("ip").build())); + final AuctionContext auctionContext = givenAuctionContext(context -> context + .httpRequest(givenHttpRequestContext("invalid")) + .bidRequest(bidRequest)); + + // when + target.contextFrom(auctionContext); + + // then + verify(ipAddressHelper).maskIpv4(anyString()); + } + + @Test + public void contextFromShouldMaskIpV6WhenCoppaEqualsToOneAndIpV6Present() { + // given + final Privacy privacy = Privacy.builder() + .gdpr("1") + .consentString("consent_string") + .ccpa(Ccpa.of("1YYY")) + .coppa(1) + .build(); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(privacy); + + given(ipAddressHelper.maskIpv4(anyString())).willReturn("maskedIpV4"); + given(ipAddressHelper.anonymizeIpv6(anyString())).willReturn("maskedIpV6"); + + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.empty())); + + final BidRequest bidRequest = givenBidRequest(request -> request.device(Device.builder().ipv6("ipV6").build())); + final AuctionContext auctionContext = givenAuctionContext(context -> context + .httpRequest(givenHttpRequestContext("invalid")) + .bidRequest(bidRequest)); + + // when + target.contextFrom(auctionContext); + + // then + verify(ipAddressHelper).anonymizeIpv6(anyString()); + } + + @Test + public void contextFromShouldAddRefUrlWhenPresentAndRequestTypeIsWeb() { + // given + final Privacy privacy = Privacy.builder() + .gdpr("1") + .consentString("consent_string") + .ccpa(Ccpa.EMPTY) + .coppa(0) + .build(); + given(privacyExtractor.validPrivacyFrom(any(), any())).willReturn(privacy); + + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.empty())); + + final BidRequest bidRequest = givenBidRequest(request -> request.site(Site.builder().ref("refUrl").build())); + final AuctionContext auctionContext = givenAuctionContext(context -> context + .requestTypeMetric(MetricName.openrtb2web) + .httpRequest(givenHttpRequestContext("invalid")) + .bidRequest(bidRequest)); + + // when + target.contextFrom(auctionContext); + + // then + final RequestLogInfo expectedRequestLogInfo = RequestLogInfo.of(MetricName.openrtb2web, "refUrl", null); + verify(tcfDefinerService) + .resolveTcfContext(any(), any(), any(), any(), any(), eq(expectedRequestLogInfo), any()); + } + + private static AuctionContext givenAuctionContext( + UnaryOperator auctionContextCustomizer) { + + final AuctionContext.AuctionContextBuilder defaultAuctionContextBuilder = + AuctionContext.builder() + .httpRequest(givenHttpRequestContext(null)) + .debugWarnings(new ArrayList<>()) + .account(Account.builder().build()) + .prebidErrors(new ArrayList<>()) + .bidRequest(givenBidRequest(identity())) + .timeoutContext(TimeoutContext.of(0L, null, 0)); + + return auctionContextCustomizer.apply(defaultAuctionContextBuilder).build(); + } + + private static BidRequest givenBidRequest(UnaryOperator bidRequestCustomizer) { + return bidRequestCustomizer.apply(BidRequest.builder()).build(); + } + + private static HttpRequestContext givenHttpRequestContext(String consentType) { + final CaseInsensitiveMultiMap.Builder queryParamBuilder = + CaseInsensitiveMultiMap.builder(); + + if (StringUtils.isNotEmpty(consentType)) { + queryParamBuilder.add("consent_type", consentType); + } + + return HttpRequestContext.builder() + .queryParams(queryParamBuilder.build()) + .build(); + } +} diff --git a/src/test/java/org/prebid/server/auction/privacy/contextfactory/CookieSyncPrivacyContextFactoryTest.java b/src/test/java/org/prebid/server/auction/privacy/contextfactory/CookieSyncPrivacyContextFactoryTest.java new file mode 100644 index 00000000000..1e5de65c586 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/contextfactory/CookieSyncPrivacyContextFactoryTest.java @@ -0,0 +1,130 @@ +package org.prebid.server.auction.privacy.contextfactory; + +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.net.SocketAddress; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.VertxTest; +import org.prebid.server.auction.ImplicitParametersExtractor; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.auction.model.IpAddress; +import org.prebid.server.execution.Timeout; +import org.prebid.server.privacy.PrivacyExtractor; +import org.prebid.server.privacy.gdpr.TcfDefinerService; +import org.prebid.server.privacy.gdpr.model.TcfContext; +import org.prebid.server.privacy.model.Privacy; +import org.prebid.server.privacy.model.PrivacyContext; +import org.prebid.server.settings.model.Account; + +import java.util.List; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +public class CookieSyncPrivacyContextFactoryTest extends VertxTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private PrivacyExtractor privacyExtractor; + @Mock + private TcfDefinerService tcfDefinerService; + @Mock + private ImplicitParametersExtractor implicitParametersExtractor; + @Mock + private IpAddressHelper ipAddressHelper; + + private CookieSyncPrivacyContextFactory target; + + @Mock + private HttpServerRequest httpRequest; + @Mock + private Timeout timeout; + + @Before + public void setUp() { + target = new CookieSyncPrivacyContextFactory( + privacyExtractor, + tcfDefinerService, + implicitParametersExtractor, + ipAddressHelper); + } + + @Test + public void contextFromShouldReturnExpectedPrivacy() { + // given + givenImplicitParametersResolver(emptyList()); + + final Privacy privacy = Privacy.builder().build(); + given(privacyExtractor.validPrivacyFrom(any())).willReturn(privacy); + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.empty())); + + final Account account = Account.builder().build(); + + // when + final PrivacyContext result = target.contextFrom(null, httpRequest, account, timeout).result(); + + // then + assertThat(result) + .extracting(PrivacyContext::getPrivacy) + .isSameAs(privacy); + } + + @Test + public void contextFromShouldUseRequestIp() { + // given + givenImplicitParametersResolver(singletonList("0.0.0.0")); + + given(ipAddressHelper.toIpAddress(eq("0.0.0.0"))) + .willReturn(IpAddress.of("ip", IpAddress.IP.v4)); + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.empty())); + + final Account account = Account.builder().build(); + + // when + target.contextFrom(null, httpRequest, account, timeout); + + // then + verify(tcfDefinerService).resolveTcfContext(any(), eq("ip"), any(), any(), any(), any()); + } + + @Test + public void contextFromShouldReturnExpectedTcfContext() { + // given + givenImplicitParametersResolver(emptyList()); + + final TcfContext tcfContext = TcfContext.empty(); + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(tcfContext)); + + final Account account = Account.builder().build(); + + // when + final PrivacyContext result = target.contextFrom(null, httpRequest, account, timeout).result(); + + // then + assertThat(result) + .extracting(PrivacyContext::getTcfContext) + .isSameAs(tcfContext); + } + + private void givenImplicitParametersResolver(List ips) { + given(httpRequest.remoteAddress()).willReturn(SocketAddress.inetSocketAddress(0, "host")); + given(implicitParametersExtractor.ipFrom(Mockito.any(), eq("host"))).willReturn(ips); + } +} diff --git a/src/test/java/org/prebid/server/auction/privacy/contextfactory/SetuidPrivacyContextFactoryTest.java b/src/test/java/org/prebid/server/auction/privacy/contextfactory/SetuidPrivacyContextFactoryTest.java new file mode 100644 index 00000000000..a33baae02b6 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/contextfactory/SetuidPrivacyContextFactoryTest.java @@ -0,0 +1,130 @@ +package org.prebid.server.auction.privacy.contextfactory; + +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.net.SocketAddress; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.VertxTest; +import org.prebid.server.auction.ImplicitParametersExtractor; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.auction.model.IpAddress; +import org.prebid.server.execution.Timeout; +import org.prebid.server.privacy.PrivacyExtractor; +import org.prebid.server.privacy.gdpr.TcfDefinerService; +import org.prebid.server.privacy.gdpr.model.TcfContext; +import org.prebid.server.privacy.model.Privacy; +import org.prebid.server.privacy.model.PrivacyContext; +import org.prebid.server.settings.model.Account; + +import java.util.List; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +public class SetuidPrivacyContextFactoryTest extends VertxTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private PrivacyExtractor privacyExtractor; + @Mock + private TcfDefinerService tcfDefinerService; + @Mock + private ImplicitParametersExtractor implicitParametersExtractor; + @Mock + private IpAddressHelper ipAddressHelper; + + private SetuidPrivacyContextFactory target; + + @Mock + private HttpServerRequest httpRequest; + @Mock + private Timeout timeout; + + @Before + public void setUp() { + target = new SetuidPrivacyContextFactory( + privacyExtractor, + tcfDefinerService, + implicitParametersExtractor, + ipAddressHelper); + } + + @Test + public void contextFromShouldReturnExpectedPrivacy() { + // given + givenImplicitParametersResolver(emptyList()); + + final Privacy privacy = Privacy.builder().build(); + given(privacyExtractor.validPrivacyFromSetuidRequest(any())).willReturn(privacy); + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.empty())); + + final Account account = Account.builder().build(); + + // when + final PrivacyContext result = target.contextFrom(httpRequest, account, timeout).result(); + + // then + assertThat(result) + .extracting(PrivacyContext::getPrivacy) + .isSameAs(privacy); + } + + @Test + public void contextFromShouldUseRequestIp() { + // given + givenImplicitParametersResolver(singletonList("0.0.0.0")); + + given(ipAddressHelper.toIpAddress(eq("0.0.0.0"))) + .willReturn(IpAddress.of("ip", IpAddress.IP.v4)); + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfContext.empty())); + + final Account account = Account.builder().build(); + + // when + target.contextFrom(httpRequest, account, timeout); + + // then + verify(tcfDefinerService).resolveTcfContext(any(), eq("ip"), any(), any(), any(), any()); + } + + @Test + public void contextFromShouldReturnExpectedTcfContext() { + // given + givenImplicitParametersResolver(emptyList()); + + final TcfContext tcfContext = TcfContext.empty(); + given(tcfDefinerService.resolveTcfContext(any(), any(), any(), any(), any(), any())) + .willReturn(Future.succeededFuture(tcfContext)); + + final Account account = Account.builder().build(); + + // when + final PrivacyContext result = target.contextFrom(httpRequest, account, timeout).result(); + + // then + assertThat(result) + .extracting(PrivacyContext::getTcfContext) + .isSameAs(tcfContext); + } + + private void givenImplicitParametersResolver(List ips) { + given(httpRequest.remoteAddress()).willReturn(SocketAddress.inetSocketAddress(0, "host")); + given(implicitParametersExtractor.ipFrom(Mockito.any(), eq("host"))).willReturn(ips); + } +} 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 new file mode 100644 index 00000000000..78512a06f41 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/ActivityEnforcementTest.java @@ -0,0 +1,79 @@ +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 org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.activity.infrastructure.ActivityInfrastructure; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidderPrivacyResult; +import org.prebid.server.auction.privacy.enforcement.mask.UserFpdActivityMask; + +import java.util.List; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; + +public class ActivityEnforcementTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private UserFpdActivityMask userFpdActivityMask; + + private ActivityEnforcement target; + + @Mock + private ActivityInfrastructure activityInfrastructure; + + @Before + public void setUp() { + target = new ActivityEnforcement(userFpdActivityMask); + } + + @Test + public void enforceShouldReturnExpectedResult() { + // given + final User maskedUser = User.builder().id("maskedUser").build(); + final Device maskedDevice = Device.builder().ip("maskedDevice").build(); + + given(activityInfrastructure.isAllowed(any(), any())).willReturn(false); + given(userFpdActivityMask.maskUser(any(), anyBoolean(), anyBoolean(), anyBoolean())) + .willReturn(maskedUser); + given(userFpdActivityMask.maskDevice(any(), anyBoolean(), anyBoolean())) + .willReturn(maskedDevice); + + final BidderPrivacyResult bidderPrivacyResult = BidderPrivacyResult.builder() + .requestBidder("bidder") + .user(User.builder().id("originalUser").build()) + .device(Device.builder().ip("originalDevice").build()) + .build(); + + final AuctionContext context = givenAuctionContext(); + + // when + final List result = target.enforce(singletonList(bidderPrivacyResult), context).result(); + + //then + assertThat(result).allSatisfy(privacyResult -> { + assertThat(privacyResult.getUser()).isSameAs(maskedUser); + assertThat(privacyResult.getDevice()).isSameAs(maskedDevice); + }); + } + + private AuctionContext givenAuctionContext() { + return AuctionContext.builder() + .bidRequest(BidRequest.builder().build()) + .activityInfrastructure(activityInfrastructure) + .build(); + } +} 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 new file mode 100644 index 00000000000..54612c8cc7c --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/CcpaEnforcementTest.java @@ -0,0 +1,258 @@ +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 org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +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.UserFpdCcpaMask; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.bidder.BidderInfo; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; +import org.prebid.server.privacy.ccpa.Ccpa; +import org.prebid.server.privacy.model.Privacy; +import org.prebid.server.privacy.model.PrivacyContext; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountCcpaConfig; +import org.prebid.server.settings.model.AccountPrivacyConfig; +import org.prebid.server.settings.model.EnabledForRequestType; +import org.prebid.server.spring.config.bidder.model.Ortb; + +import java.util.List; +import java.util.Map; +import java.util.function.UnaryOperator; + +import static java.util.Collections.singletonList; +import static java.util.function.UnaryOperator.identity; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +public class CcpaEnforcementTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private UserFpdCcpaMask userFpdCcpaMask; + @Mock + private BidderCatalog bidderCatalog; + @Mock + private Metrics metrics; + + private CcpaEnforcement target; + + @Mock + private BidderAliases aliases; + + @Before + public void setUp() { + given(bidderCatalog.bidderInfoByName("bidder")) + .willReturn(BidderInfo.create( + true, + null, + false, + null, + null, + null, + null, + null, + null, + null, + 0, + true, + false, + null, + Ortb.of(false))); + + target = new CcpaEnforcement(userFpdCcpaMask, bidderCatalog, metrics, true); + + given(aliases.resolveBidder(anyString())) + .willAnswer(invocation -> invocation.getArgument(0)); + } + + @Test + public void enforceShouldReturnEmptyListWhenCcpaNotEnforced() { + // given + final AuctionContext auctionContext = givenAuctionContext(context -> context + .privacyContext(PrivacyContext.of(Privacy.builder().ccpa(Ccpa.of("1YN-")).build(), null, null))); + + // when + final List result = target.enforce(auctionContext, null, aliases).result(); + + // then + assertThat(result).isEmpty(); + verify(metrics).updatePrivacyCcpaMetrics(eq(true), eq(false)); + } + + @Test + public void enforceShouldConsiderEnforceCcpaConfigurationProperty() { + // given + final AuctionContext auctionContext = givenAuctionContext(context -> context.account(Account.empty("id"))); + + target = new CcpaEnforcement(userFpdCcpaMask, bidderCatalog, metrics, false); + + // when + final List result = target.enforce(auctionContext, null, aliases).result(); + + // then + assertThat(result).isEmpty(); + verify(metrics).updatePrivacyCcpaMetrics(eq(true), eq(true)); + } + + @Test + public void enforceShouldConsiderAccountCcpaEnabledProperty() { + // given + final AuctionContext auctionContext = givenAuctionContext(context -> context + .account(Account.builder() + .privacy(AccountPrivacyConfig.builder() + .ccpa(AccountCcpaConfig.builder().enabled(false).build()) + .build()) + .build())); + + // when + final List result = target.enforce(auctionContext, null, aliases).result(); + + // then + assertThat(result).isEmpty(); + } + + @Test + public void enforceShouldConsiderAccountCcpaEnabledForRequestTypeProperty() { + // given + final AuctionContext auctionContext = givenAuctionContext(context -> context + .requestTypeMetric(MetricName.openrtb2app)); + + // when + final List result = target.enforce(auctionContext, null, aliases).result(); + + // then + assertThat(result).isEmpty(); + } + + @Test + public void enforceShouldTreatAllBiddersAsNoSale() { + // given + final AuctionContext auctionContext = givenAuctionContext(context -> context + .bidRequest(BidRequest.builder() + .device(Device.builder().ip("originalDevice").build()) + .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()); + + // when + final List result = target.enforce(auctionContext, bidderToUser, aliases).result(); + + // then + assertThat(result).isEmpty(); + } + + @Test + public void enforceShouldSkipNoSaleBiddersAndNotEnforcedByBidderConfig() { + // given + given(aliases.resolveBidder("bidderAlias")).willReturn("bidder"); + given(bidderCatalog.bidderInfoByName("bidder")) + .willReturn(BidderInfo.create( + true, + null, + false, + null, + null, + null, + null, + null, + null, + null, + 0, + false, + false, + null, + Ortb.of(false))); + + final AuctionContext auctionContext = givenAuctionContext(identity()); + + final Map bidderToUser = Map.of( + "bidderAlias", User.builder().id("originalUser").build(), + "noSale", User.builder().id("originalUser").build()); + + // when + final List result = target.enforce(auctionContext, bidderToUser, aliases).result(); + + // then + assertThat(result).isEmpty(); + } + + @Test + public void enforceShouldReturnExpectedResult() { + // given + final User maskedUser = User.builder().id("maskedUser").build(); + final Device maskedDevice = Device.builder().ip("maskedDevice").build(); + + given(userFpdCcpaMask.maskUser(any())).willReturn(maskedUser); + given(userFpdCcpaMask.maskDevice(any())).willReturn(maskedDevice); + + final AuctionContext auctionContext = givenAuctionContext(identity()); + + final Map bidderToUser = Map.of( + "bidder", User.builder().id("originalUser").build(), + "noSale", User.builder().id("originalUser").build()); + + // when + final List result = target.enforce(auctionContext, bidderToUser, aliases).result(); + + // then + assertThat(result) + .hasSize(1) + .allSatisfy(privacyResult -> { + assertThat(privacyResult.getUser()).isSameAs(maskedUser); + assertThat(privacyResult.getDevice()).isSameAs(maskedDevice); + }); + } + + private static AuctionContext givenAuctionContext( + UnaryOperator auctionContextCustomizer) { + + final AuctionContext.AuctionContextBuilder initialContext = AuctionContext.builder() + .bidRequest(BidRequest.builder() + .device(Device.builder().ip("originalDevice").build()) + .ext(ExtRequest.of(ExtRequestPrebid.builder() + .nosale(singletonList("noSale")) + .build())) + .build()) + .requestTypeMetric(MetricName.openrtb2web) + .account(Account.builder() + .privacy(AccountPrivacyConfig.builder() + .ccpa(AccountCcpaConfig.builder() + .enabled(true) + .enabledForRequestType(EnabledForRequestType.of( + true, + false, + false, + false, + false)) + .build()) + .build()) + .build()) + .privacyContext(PrivacyContext.of(Privacy.builder().ccpa(Ccpa.of("1YY-")).build(), null, null)); + + return auctionContextCustomizer.apply(initialContext).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 new file mode 100644 index 00000000000..b3a343139c9 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/CoppaEnforcementTest.java @@ -0,0 +1,90 @@ +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 org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +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 org.prebid.server.privacy.model.Privacy; +import org.prebid.server.privacy.model.PrivacyContext; + +import java.util.List; +import java.util.Map; + +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; + +public class CoppaEnforcementTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private UserFpdCoppaMask userFpdCoppaMask; + @Mock + private Metrics metrics; + + private CoppaEnforcement target; + + @Before + public void setUp() { + target = new CoppaEnforcement(userFpdCoppaMask, metrics); + } + + @Test + public void isApplicableShouldReturnFalse() { + // given + final AuctionContext auctionContext = AuctionContext.builder() + .privacyContext(PrivacyContext.of(Privacy.builder().coppa(0).build(), null, null)) + .build(); + + // when and then + assertThat(target.isApplicable(auctionContext)).isFalse(); + } + + @Test + public void isApplicableShouldReturnTrue() { + // given + final AuctionContext auctionContext = AuctionContext.builder() + .privacyContext(PrivacyContext.of(Privacy.builder().coppa(1).build(), null, null)) + .build(); + + // when and then + assertThat(target.isApplicable(auctionContext)).isTrue(); + } + + @Test + public void enforceShouldReturnExpectedResultAndEmitMetrics() { + // given + final User maskedUser = User.builder().id("maskedUser").build(); + final Device maskedDevice = Device.builder().ip("maskedDevice").build(); + + given(userFpdCoppaMask.maskUser(any())).willReturn(maskedUser); + given(userFpdCoppaMask.maskDevice(any())).willReturn(maskedDevice); + + final AuctionContext auctionContext = AuctionContext.builder() + .bidRequest(BidRequest.builder().device(Device.builder().ip("originalDevice").build()).build()) + .build(); + final Map bidderToUser = Map.of("bidder", User.builder().id("originalUser").build()); + + // when + final List result = target.enforce(auctionContext, bidderToUser).result(); + + // then + assertThat(result).allSatisfy(privacyResult -> { + assertThat(privacyResult.getUser()).isSameAs(maskedUser); + assertThat(privacyResult.getDevice()).isSameAs(maskedDevice); + }); + verify(metrics).updatePrivacyCoppaMetric(); + } +} 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 new file mode 100644 index 00000000000..1e429550a99 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/PrivacyEnforcementServiceTest.java @@ -0,0 +1,101 @@ +package org.prebid.server.auction.privacy.enforcement; + +import com.iab.openrtb.request.User; +import io.vertx.core.Future; +import org.apache.commons.collections4.ListUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +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 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; + +public class PrivacyEnforcementServiceTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private CoppaEnforcement coppaEnforcement; + @Mock + private CcpaEnforcement ccpaEnforcement; + @Mock + private TcfEnforcement tcfEnforcement; + @Mock + private ActivityEnforcement activityEnforcement; + + private PrivacyEnforcementService target; + + @Before + 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(); + + // then + assertThat(result).isSameAs(bidderPrivacyResults); + verifyNoInteractions(ccpaEnforcement); + verifyNoInteractions(tcfEnforcement); + verifyNoInteractions(activityEnforcement); + } + + @Test + public void maskShouldReturnExpectedResult() { + // given + given(coppaEnforcement.isApplicable(any())).willReturn(false); + + given(ccpaEnforcement.enforce(any(), any(), any())).willReturn(Future.succeededFuture( + singletonList(BidderPrivacyResult.builder().requestBidder("bidder1").build()))); + + given(tcfEnforcement.enforce(any(), any(), eq(singleton("bidder0")), any())) + .willReturn(Future.succeededFuture( + singletonList(BidderPrivacyResult.builder().requestBidder("bidder0").build()))); + + given(activityEnforcement.enforce(any(), any())) + .willAnswer(invocation -> Future.succeededFuture(ListUtils.union( + invocation.getArgument(0), + singletonList(BidderPrivacyResult.builder().requestBidder("bidder2").build())))); + + final Map bidderToUser = Map.of( + "bidder0", User.builder().build(), + "bidder1", User.builder().build()); + + // when + final List result = target.mask(null, bidderToUser, null).result(); + + // 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()); + } +} 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 new file mode 100644 index 00000000000..e0b2c288673 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcementTest.java @@ -0,0 +1,431 @@ +package org.prebid.server.auction.privacy.enforcement; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Eid; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.User; +import io.vertx.core.Future; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +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.UserFpdTcfMask; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; +import org.prebid.server.privacy.gdpr.TcfDefinerService; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.TcfContext; +import org.prebid.server.privacy.gdpr.model.TcfResponse; +import org.prebid.server.privacy.model.PrivacyContext; +import org.prebid.server.settings.model.Account; +import org.prebid.server.settings.model.AccountPrivacyConfig; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; + +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class TcfEnforcementTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private TcfDefinerService tcfDefinerService; + @Mock + private UserFpdTcfMask userFpdTcfMask; + @Mock + private BidderCatalog bidderCatalog; + @Mock + private Metrics metrics; + + private TcfEnforcement target; + + @Mock + private BidderAliases aliases; + + @Before + public void setUp() { + given(userFpdTcfMask.maskUser(any(), anyBoolean(), anyBoolean(), anyBoolean(), anySet())) + .willAnswer(invocation -> invocation.getArgument(0)); + given(userFpdTcfMask.maskDevice(any(), anyBoolean(), anyBoolean(), anyBoolean())) + .willAnswer(invocation -> invocation.getArgument(0)); + + target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, true); + + given(aliases.resolveBidder(anyString())) + .willAnswer(invocation -> invocation.getArgument(0)); + } + + @Test + public void enforceShouldReturnResultForVendorsIds() { + // given + final Set vendorsIds = Collections.emptySet(); + final TcfContext tcfContext = TcfContext.empty(); + given(tcfDefinerService.resultForVendorIds(same(vendorsIds), same(tcfContext))) + .willReturn(Future.succeededFuture(TcfResponse.of( + null, + Map.of(1, PrivacyEnforcementAction.allowAll()), + null))); + + // when + final Map result = target.enforce(vendorsIds, tcfContext).result(); + + // then + assertThat(result).containsAllEntriesOf(Map.of(1, PrivacyEnforcementAction.allowAll())); + } + + @Test + public void enforceShouldEmitExpectedMetricsWhenUserAndDeviceHavePrivacyData() { + // give + given(aliases.resolveBidder(eq("bidder1Alias"))).willReturn("bidder1"); + + givenPrivacyEnforcementActions(Map.of( + "bidder0", givenEnforcementAction(), + "bidder1Alias", givenEnforcementAction(), + "bidder2", givenEnforcementAction( + PrivacyEnforcementAction::setBlockBidderRequest, + PrivacyEnforcementAction::setRemoveUserFpd, + PrivacyEnforcementAction::setMaskDeviceInfo, + PrivacyEnforcementAction::setRemoveUserIds, + PrivacyEnforcementAction::setMaskGeo, + PrivacyEnforcementAction::setBlockAnalyticsReport), + + "bidder3", givenEnforcementAction(PrivacyEnforcementAction::setBlockAnalyticsReport), + "bidder4", givenEnforcementAction(PrivacyEnforcementAction::setMaskGeo), + "bidder5", givenEnforcementAction(PrivacyEnforcementAction::setRemoveUserIds), + "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(); + + // when + target.enforce(auctionContext, bidderToUser, bidders, aliases); + + // then + verifyMetric("bidder0", false, false, false, false, false); + verifyMetric("bidder1", false, false, false, false, false); + + verifyMetric("bidder2", false, false, false, false, true); + verifyMetric("bidder3", false, false, false, true, false); + verifyMetric("bidder4", false, false, true, false, false); + verifyMetric("bidder5", false, true, false, false, false); + verifyMetric("bidder6", true, false, false, false, false); + verifyMetric("bidder7", true, false, false, false, false); + } + + @Test + public void enforceShouldEmitExpectedMetricsWhenUserHasPrivacyData() { + // give + givenPrivacyEnforcementActions(Map.of( + "bidder0", givenEnforcementAction(PrivacyEnforcementAction::setMaskGeo), + "bidder1", givenEnforcementAction(PrivacyEnforcementAction::setMaskDeviceInfo), + "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(); + + // when + target.enforce(auctionContext, bidderToUser, bidders, aliases); + + // then + verifyMetric("bidder0", false, false, true, false, false); + verifyMetric("bidder1", false, false, false, false, false); + verifyMetric("bidder2", true, false, false, false, false); + } + + @Test + public void enforceShouldEmitExpectedMetricsWhenDeviceHavePrivacyData() { + // give + givenPrivacyEnforcementActions(Map.of( + "bidder0", givenEnforcementAction(PrivacyEnforcementAction::setMaskGeo), + "bidder1", givenEnforcementAction(PrivacyEnforcementAction::setRemoveUserIds), + "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(); + + // when + target.enforce(auctionContext, bidderToUser, bidders, aliases); + + // then + verifyMetric("bidder0", false, false, true, false, false); + verifyMetric("bidder1", false, false, false, false, false); + verifyMetric("bidder2", true, false, false, false, false); + verifyMetric("bidder3", false, false, false, false, false); + } + + @Test + public void enforceShouldEmitExpectedMetricsWhenUserAndDeviceDoNotHavePrivacyData() { + // give + givenPrivacyEnforcementActions(Map.of( + "bidder0", givenEnforcementAction(PrivacyEnforcementAction::setMaskGeo), + "bidder1", givenEnforcementAction( + PrivacyEnforcementAction::setRemoveUserFpd, + PrivacyEnforcementAction::setMaskDeviceInfo))); + + final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithNoPrivacyData()); + final Map bidderToUser = Map.of( + "bidder0", givenUserWithNoPrivacyData(), + "bidder1", givenUserWithNoPrivacyData()); + final Set bidders = Set.of(); + + // when + target.enforce(auctionContext, bidderToUser, bidders, aliases); + + // then + verifyMetric("bidder0", false, false, false, false, false); + verifyMetric("bidder1", false, false, false, false, false); + } + + @Test + 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(); + + // when + target.enforce(auctionContext, bidderToUser, bidders, aliases); + + // then + verify(metrics).updatePrivacyLmtMetric(); + } + + @Test + 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(); + + // when + target.enforce(auctionContext, bidderToUser, bidders, aliases); + + // then + verify(metrics, times(0)).updatePrivacyLmtMetric(); + } + + @Test + 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(); + + target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, false); + + // when + target.enforce(auctionContext, bidderToUser, bidders, aliases); + + // then + verify(metrics, times(0)).updatePrivacyLmtMetric(); + } + + @Test + public void enforceShouldMaskUserAndDeviceWhenRestrictionsEnforcedAndLmtNotEnabled() { + // give + final User maskedUser = User.builder().id("maskedUser").build(); + final Device maskedDevice = Device.builder().ip("maskedDevice").build(); + + given(userFpdTcfMask.maskUser(any(), eq(true), eq(true), eq(true), eq(singleton("eidException")))) + .willReturn(maskedUser); + given(userFpdTcfMask.maskDevice(any(), eq(true), eq(true), eq(true))) + .willReturn(maskedDevice); + + givenPrivacyEnforcementActions(Map.of( + "bidder0", givenEnforcementAction( + PrivacyEnforcementAction::setBlockBidderRequest, + PrivacyEnforcementAction::setBlockAnalyticsReport), + "bidder1", givenEnforcementAction( + PrivacyEnforcementAction::setRemoveUserFpd, + PrivacyEnforcementAction::setMaskDeviceInfo, + PrivacyEnforcementAction::setRemoveUserIds, + PrivacyEnforcementAction::setMaskDeviceIp, + PrivacyEnforcementAction::setMaskGeo, + 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"); + + // when + final List result = target.enforce(context, bidderToUser, bidders, aliases).result(); + + // then + assertThat(result).containsExactlyInAnyOrder( + BidderPrivacyResult.builder() + .requestBidder("bidder0") + .blockedRequestByTcf(true) + .blockedAnalyticsByTcf(true) + .build(), + BidderPrivacyResult.builder() + .requestBidder("bidder1") + .blockedRequestByTcf(false) + .blockedAnalyticsByTcf(true) + .user(maskedUser) + .device(maskedDevice) + .build(), + BidderPrivacyResult.builder() + .requestBidder("bidder2") + .blockedRequestByTcf(false) + .blockedAnalyticsByTcf(false) + .user(givenUserWithPrivacyData()) + .device(givenDeviceWithNoPrivacyData()) + .build()); + } + + @Test + public void enforceShouldMaskUserAndDeviceWhenRestrictionsNotEnforcedAndLmtEnabled() { + // give + final User maskedUser = User.builder().id("maskedUser").build(); + final Device maskedDevice = Device.builder().ip("maskedDevice").build(); + + given(userFpdTcfMask.maskUser(any(), eq(true), eq(true), eq(true), eq(singleton("eidException")))) + .willReturn(maskedUser); + given(userFpdTcfMask.maskDevice(any(), eq(true), eq(true), eq(true))) + .willReturn(maskedDevice); + + givenPrivacyEnforcementActions(Map.of("bidder", givenEnforcementAction())); + + final AuctionContext context = givenAuctionContext(givenDeviceWithPrivacyData()); + final Map bidderToUser = Map.of("bidder", givenUserWithPrivacyData()); + final Set bidders = Set.of("bidder"); + + // when + final List result = target.enforce(context, bidderToUser, bidders, aliases).result(); + + // then + assertThat(result).containsExactly( + BidderPrivacyResult.builder() + .requestBidder("bidder") + .blockedRequestByTcf(false) + .blockedAnalyticsByTcf(false) + .user(maskedUser) + .device(maskedDevice) + .build()); + } + + private void givenPrivacyEnforcementActions(Map actions) { + given(tcfDefinerService.resultForBidderNames(any(), any(), any(), any())) + .willReturn(Future.succeededFuture(TcfResponse.of(null, actions, null))); + } + + private static AuctionContext givenAuctionContext(Device device) { + return AuctionContext.builder() + .bidRequest(BidRequest.builder().device(device).build()) + .requestTypeMetric(MetricName.openrtb2web) + .account(Account.builder() + .privacy(AccountPrivacyConfig.builder().build()) + .build()) + .privacyContext(PrivacyContext.of(null, TcfContext.empty(), null)) + .build(); + } + + private static Device givenDeviceWithPrivacyData() { + return Device.builder() + .ip("originalDevice") + .ifa("ifa") + .geo(Geo.builder().build()) + .lmt(1) + .build(); + } + + private static Device givenDeviceWithNoPrivacyData() { + return Device.builder().ip("originalDevice").build(); + } + + private static User givenUserWithPrivacyData() { + return User.builder() + .id("originalUser") + .eids(singletonList(Eid.of(null, null, null))) + .geo(Geo.builder().build()) + .build(); + } + + private static User givenUserWithNoPrivacyData() { + return User.builder().build(); + } + + @SafeVarargs + private static PrivacyEnforcementAction givenEnforcementAction( + BiConsumer... restrictions) { + + final PrivacyEnforcementAction action = PrivacyEnforcementAction.allowAll(); + for (BiConsumer restriction : restrictions) { + restriction.accept(action, true); + } + action.setEidExceptions(singleton("eidException")); + + return action; + } + + private void verifyMetric(String bidder, + boolean userFpdRemoved, + boolean userIdsRemoved, + boolean geoMasked, + boolean analyticsBlocked, + boolean requestBlocked) { + + verify(metrics).updateAuctionTcfMetrics( + eq(bidder), + any(), + eq(userFpdRemoved), + eq(userIdsRemoved), + eq(geoMasked), + eq(analyticsBlocked), + eq(requestBlocked)); + } +} diff --git a/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdActivityMaskTest.java b/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdActivityMaskTest.java new file mode 100644 index 00000000000..bcf2ee5f7d7 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdActivityMaskTest.java @@ -0,0 +1,91 @@ +package org.prebid.server.auction.privacy.enforcement.mask; + +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.User; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static java.util.Collections.emptySet; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.verify; + +public class UserFpdActivityMaskTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private UserFpdTcfMask userFpdTcfMask; + + private UserFpdActivityMask target; + + @Before + public void setUp() { + target = new UserFpdActivityMask(userFpdTcfMask); + } + + @Test + public void maskUserShouldProperlyDelegateUfpdParameter() { + // given + final User user = User.builder().build(); + + // when + target.maskUser(user, true, false, false); + + // then + verify(userFpdTcfMask).maskUser(same(user), eq(true), eq(false), eq(false), eq(emptySet())); + } + + @Test + public void maskUserShouldProperlyDelegateEidsParameter() { + // given + final User user = User.builder().build(); + + // when + target.maskUser(user, false, true, false); + + // then + verify(userFpdTcfMask).maskUser(same(user), eq(false), eq(true), eq(false), eq(emptySet())); + } + + @Test + public void maskUserShouldProperlyDelegateGeoParameter() { + // given + final User user = User.builder().build(); + + // when + target.maskUser(user, false, false, true); + + // then + verify(userFpdTcfMask).maskUser(same(user), eq(false), eq(false), eq(true), eq(emptySet())); + } + + @Test + public void maskDeviceShouldProperlyDelegateUfpdParameter() { + // given + final Device device = Device.builder().build(); + + // when + target.maskDevice(device, true, false); + + // then + verify(userFpdTcfMask).maskDevice(same(device), eq(false), eq(false), eq(true)); + } + + @Test + public void maskDeviceShouldProperlyDelegateGeoParameter() { + // given + final Device device = Device.builder().build(); + + // when + target.maskDevice(device, false, true); + + // then + verify(userFpdTcfMask).maskDevice(same(device), eq(true), eq(true), eq(false)); + } +} diff --git a/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCcpaMaskTest.java b/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCcpaMaskTest.java new file mode 100644 index 00000000000..53d4a1571df --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCcpaMaskTest.java @@ -0,0 +1,104 @@ +package org.prebid.server.auction.privacy.enforcement.mask; + +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.User; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.VertxTest; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; + +import static java.util.Collections.emptyList; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +public class UserFpdCcpaMaskTest extends VertxTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private IpAddressHelper ipAddressHelper; + + private UserFpdCcpaMask target; + + @Before + public void setUp() { + target = new UserFpdCcpaMask(ipAddressHelper); + } + + @Test + public void maskUserShouldReturnExpectedResult() { + // given + final User user = User.builder() + .id("id") + .buyeruid("buyeruid") + .yob(1) + .gender("gender") + .keywords("keywords") + .kwarray(emptyList()) + .data(emptyList()) + .eids(emptyList()) + .geo(Geo.builder() + .lon(-85.34321F) + .lat(189.342323F) + .metro("metro") + .city("city") + .zip("zip") + .build()) + .ext(ExtUser.builder().data(mapper.createObjectNode()).build()) + .build(); + + // when + final User result = target.maskUser(user); + + // then + assertThat(result).isEqualTo( + User.builder() + .geo(Geo.builder().lon(-85.34F).lat(189.34F).build()) + .build()); + } + + @Test + public void maskDeviceShouldReturnExpectedResult() { + // given + given(ipAddressHelper.maskIpv4(any())).willReturn("ip4"); + given(ipAddressHelper.anonymizeIpv6(any())).willReturn("ip6"); + + final Device device = Device.builder() + .ip("192.168.0.10") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + .geo(Geo.builder() + .lon(-85.34321F) + .lat(189.342323F) + .metro("metro") + .city("city") + .zip("zip") + .build()) + .ifa("ifa") + .macsha1("macsha1") + .macmd5("macmd5") + .didsha1("didsha1") + .didmd5("didmd5") + .dpidsha1("dpidsha1") + .dpidmd5("dpidmd5") + .build(); + + // when + final Device result = target.maskDevice(device); + + // then + assertThat(result).isEqualTo( + Device.builder() + .ip("ip4") + .ipv6("ip6") + .geo(Geo.builder().lon(-85.34F).lat(189.34F).build()) + .build()); + } +} diff --git a/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCoppaMaskTest.java b/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCoppaMaskTest.java new file mode 100644 index 00000000000..056b36f3a84 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdCoppaMaskTest.java @@ -0,0 +1,100 @@ +package org.prebid.server.auction.privacy.enforcement.mask; + +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.User; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.VertxTest; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; + +import static java.util.Collections.emptyList; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +public class UserFpdCoppaMaskTest extends VertxTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private IpAddressHelper ipAddressHelper; + + private UserFpdCoppaMask target; + + @Before + public void setUp() { + target = new UserFpdCoppaMask(ipAddressHelper); + } + + @Test + public void maskUserShouldReturnExpectedResult() { + // given + final User user = User.builder() + .id("id") + .buyeruid("buyeruid") + .yob(1) + .gender("gender") + .keywords("keywords") + .kwarray(emptyList()) + .data(emptyList()) + .eids(emptyList()) + .geo(Geo.builder() + .lon(-85.34321F) + .lat(189.342323F) + .metro("metro") + .city("city") + .zip("zip") + .build()) + .ext(ExtUser.builder().data(mapper.createObjectNode()).build()) + .build(); + + // when + final User result = target.maskUser(user); + + // then + assertThat(result).isNull(); + } + + @Test + public void maskDeviceShouldReturnExpectedResult() { + // given + given(ipAddressHelper.maskIpv4(any())).willReturn("ip4"); + given(ipAddressHelper.anonymizeIpv6(any())).willReturn("ip6"); + + final Device device = Device.builder() + .ip("192.168.0.10") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + .geo(Geo.builder() + .lon(-85.34321F) + .lat(189.342323F) + .metro("metro") + .city("city") + .zip("zip") + .build()) + .ifa("ifa") + .macsha1("macsha1") + .macmd5("macmd5") + .didsha1("didsha1") + .didmd5("didmd5") + .dpidsha1("dpidsha1") + .dpidmd5("dpidmd5") + .build(); + + // when + final Device result = target.maskDevice(device); + + // then + assertThat(result).isEqualTo( + Device.builder() + .ip("ip4") + .ipv6("ip6") + .build()); + } +} diff --git a/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdTcfMaskTest.java b/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdTcfMaskTest.java new file mode 100644 index 00000000000..444fd839aa7 --- /dev/null +++ b/src/test/java/org/prebid/server/auction/privacy/enforcement/mask/UserFpdTcfMaskTest.java @@ -0,0 +1,265 @@ +package org.prebid.server.auction.privacy.enforcement.mask; + +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Eid; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.User; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.VertxTest; +import org.prebid.server.auction.IpAddressHelper; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +public class UserFpdTcfMaskTest extends VertxTest { + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private IpAddressHelper ipAddressHelper; + + private UserFpdTcfMask target; + + @Before + public void setUp() { + target = new UserFpdTcfMask(ipAddressHelper); + } + + @Test + public void maskUserShouldReturnExpectedResultWhenFpdMasked() { + // given + final User user = User.builder() + .id("id") + .buyeruid("buyeruid") + .yob(1) + .gender("gender") + .keywords("keywords") + .kwarray(emptyList()) + .data(emptyList()) + .eids(emptyList()) + .geo(Geo.builder().lon(-85.34321F).lat(189.342323F).build()) + .ext(ExtUser.builder().data(mapper.createObjectNode()).build()) + .build(); + + // when + final User result = target.maskUser(user, true, false, false, emptySet()); + + // then + assertThat(result).isEqualTo( + User.builder() + .eids(emptyList()) + .geo(Geo.builder().lon(-85.34321F).lat(189.342323F).build()) + .build()); + } + + @Test + public void maskUserShouldReturnExpectedResultWhenEidsMasked() { + // given + final User user = User.builder() + .id("id") + .buyeruid("buyeruid") + .yob(1) + .gender("gender") + .keywords("keywords") + .kwarray(emptyList()) + .data(emptyList()) + .eids(asList( + Eid.of("1", null, null), + Eid.of("2", null, null), + Eid.of("3", null, null))) + .geo(Geo.builder().lon(-85.34321F).lat(189.342323F).build()) + .ext(ExtUser.builder().data(mapper.createObjectNode()).build()) + .build(); + + // when + final User result = target.maskUser(user, false, true, false, singleton("2")); + + // then + assertThat(result).isEqualTo( + User.builder() + .id("id") + .buyeruid("buyeruid") + .yob(1) + .gender("gender") + .keywords("keywords") + .kwarray(emptyList()) + .data(emptyList()) + .eids(singletonList(Eid.of("2", null, null))) + .geo(Geo.builder().lon(-85.34321F).lat(189.342323F).build()) + .ext(ExtUser.builder().data(mapper.createObjectNode()).build()) + .build()); + } + + @Test + public void maskUserShouldReturnExpectedResultWhenGeoMasked() { + // given + final User user = User.builder() + .id("id") + .buyeruid("buyeruid") + .yob(1) + .gender("gender") + .keywords("keywords") + .kwarray(emptyList()) + .data(emptyList()) + .eids(emptyList()) + .geo(Geo.builder().lon(-85.34321F).lat(189.342323F).build()) + .ext(ExtUser.builder().data(mapper.createObjectNode()).build()) + .build(); + + // when + final User result = target.maskUser(user, false, false, true, emptySet()); + + // then + assertThat(result).isEqualTo( + User.builder() + .id("id") + .buyeruid("buyeruid") + .yob(1) + .gender("gender") + .keywords("keywords") + .kwarray(emptyList()) + .data(emptyList()) + .eids(emptyList()) + .geo(Geo.builder().lon(-85.34F).lat(189.34F).build()) + .ext(ExtUser.builder().data(mapper.createObjectNode()).build()) + .build()); + } + + @Test + public void maskDeviceShouldReturnExpectedResultWhenIpMasked() { + // given + given(ipAddressHelper.maskIpv4(any())).willReturn("ip4"); + given(ipAddressHelper.anonymizeIpv6(any())).willReturn("ip6"); + + final Device device = Device.builder() + .ip("192.168.0.10") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + .geo(Geo.builder() + .lon(-85.34321F) + .lat(189.342323F) + .build()) + .ifa("ifa") + .macsha1("macsha1") + .macmd5("macmd5") + .didsha1("didsha1") + .didmd5("didmd5") + .dpidsha1("dpidsha1") + .dpidmd5("dpidmd5") + .build(); + + // when + final Device result = target.maskDevice(device, true, false, false); + + // then + assertThat(result).isEqualTo( + Device.builder() + .ip("ip4") + .ipv6("ip6") + .geo(Geo.builder() + .lon(-85.34321F) + .lat(189.342323F) + .build()) + .ifa("ifa") + .macsha1("macsha1") + .macmd5("macmd5") + .didsha1("didsha1") + .didmd5("didmd5") + .dpidsha1("dpidsha1") + .dpidmd5("dpidmd5") + .build()); + } + + @Test + public void maskDeviceShouldReturnExpectedResultWhenGeoMasked() { + // given + given(ipAddressHelper.maskIpv4(any())).willReturn("ip4"); + given(ipAddressHelper.anonymizeIpv6(any())).willReturn("ip6"); + + final Device device = Device.builder() + .ip("192.168.0.10") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + .geo(Geo.builder() + .lon(-85.34321F) + .lat(189.342323F) + .build()) + .ifa("ifa") + .macsha1("macsha1") + .macmd5("macmd5") + .didsha1("didsha1") + .didmd5("didmd5") + .dpidsha1("dpidsha1") + .dpidmd5("dpidmd5") + .build(); + + // when + final Device result = target.maskDevice(device, false, true, false); + + // then + assertThat(result).isEqualTo( + Device.builder() + .ip("192.168.0.10") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + .geo(Geo.builder() + .lon(-85.34F) + .lat(189.34F) + .build()) + .ifa("ifa") + .macsha1("macsha1") + .macmd5("macmd5") + .didsha1("didsha1") + .didmd5("didmd5") + .dpidsha1("dpidsha1") + .dpidmd5("dpidmd5") + .build()); + } + + @Test + public void maskDeviceShouldReturnExpectedResultWhenDeviceInfoMasked() { + // given + given(ipAddressHelper.maskIpv4(any())).willReturn("ip4"); + given(ipAddressHelper.anonymizeIpv6(any())).willReturn("ip6"); + + final Device device = Device.builder() + .ip("192.168.0.10") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + .geo(Geo.builder() + .lon(-85.34321F) + .lat(189.342323F) + .build()) + .ifa("ifa") + .macsha1("macsha1") + .macmd5("macmd5") + .didsha1("didsha1") + .didmd5("didmd5") + .dpidsha1("dpidsha1") + .dpidmd5("dpidmd5") + .build(); + + // when + final Device result = target.maskDevice(device, false, false, true); + + // then + assertThat(result).isEqualTo( + Device.builder() + .ip("192.168.0.10") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + .geo(Geo.builder() + .lon(-85.34321F) + .lat(189.342323F) + .build()) + .build()); + } +} diff --git a/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java b/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java index a81e971a019..45440a5b40a 100644 --- a/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java @@ -33,7 +33,7 @@ import org.prebid.server.auction.gpp.AmpGppService; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.debug.DebugContext; -import org.prebid.server.auction.privacycontextfactory.AmpPrivacyContextFactory; +import org.prebid.server.auction.privacy.contextfactory.AmpPrivacyContextFactory; import org.prebid.server.auction.versionconverter.BidRequestOrtbVersionConversionManager; import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.geolocation.model.GeoInfo; diff --git a/src/test/java/org/prebid/server/auction/requestfactory/AuctionRequestFactoryTest.java b/src/test/java/org/prebid/server/auction/requestfactory/AuctionRequestFactoryTest.java index 60dc83c3438..0b6d47cee58 100644 --- a/src/test/java/org/prebid/server/auction/requestfactory/AuctionRequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/requestfactory/AuctionRequestFactoryTest.java @@ -29,12 +29,12 @@ import org.prebid.server.auction.ImplicitParametersExtractor; import org.prebid.server.auction.InterstitialProcessor; import org.prebid.server.auction.OrtbTypesResolver; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.StoredRequestProcessor; import org.prebid.server.auction.gpp.AuctionGppService; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.AuctionStoredResult; import org.prebid.server.auction.model.debug.DebugContext; +import org.prebid.server.auction.privacy.contextfactory.AuctionPrivacyContextFactory; import org.prebid.server.auction.versionconverter.BidRequestOrtbVersionConversionManager; import org.prebid.server.cookie.CookieDeprecationService; import org.prebid.server.exception.InvalidRequestException; @@ -98,7 +98,7 @@ public class AuctionRequestFactoryTest extends VertxTest { @Mock private OrtbTypesResolver ortbTypesResolver; @Mock - private PrivacyEnforcementService privacyEnforcementService; + private AuctionPrivacyContextFactory auctionPrivacyContextFactory; @Mock private DebugResolver debugResolver; @@ -167,7 +167,7 @@ public void setUp() { given(interstitialProcessor.process(any())) .will(invocationOnMock -> invocationOnMock.getArgument(0)); - given(privacyEnforcementService.contextFromBidRequest(any())) + given(auctionPrivacyContextFactory.contextFrom(any())) .willReturn(Future.succeededFuture(defaultPrivacyContext)); given(ortb2RequestFactory.enrichBidRequestWithAccountAndPrivacyData(any())) @@ -195,7 +195,7 @@ public void setUp() { paramsResolver, interstitialProcessor, ortbTypesResolver, - privacyEnforcementService, + auctionPrivacyContextFactory, debugResolver, jacksonMapper); } @@ -229,7 +229,7 @@ public void shouldReturnFailedFutureIfRequestBodyExceedsMaxRequestSize() { paramsResolver, interstitialProcessor, ortbTypesResolver, - privacyEnforcementService, + auctionPrivacyContextFactory, debugResolver, jacksonMapper); @@ -696,7 +696,7 @@ public void shouldReturnPopulatedPrivacyContextAndGetWhenPrivacyEnforcementRetur .coppa(0) .build(), TcfContext.builder().geoInfo(geoInfo).build()); - given(privacyEnforcementService.contextFromBidRequest(any())) + given(auctionPrivacyContextFactory.contextFrom(any())) .willReturn(Future.succeededFuture(privacyContext)); // when diff --git a/src/test/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactoryTest.java b/src/test/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactoryTest.java index c57edaf4d02..bfb5bec8bca 100644 --- a/src/test/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactoryTest.java @@ -1445,16 +1445,10 @@ public void enrichBidRequestWithAccountAndPrivacyDataShouldSetDsaFromAccountWhen final Account account = Account.builder() .id(accountId) - .privacy(AccountPrivacyConfig.of(null, - null, - AccountDsaConfig.of(DefaultDsa.of(0, - 1, - 2, - List.of(DsaTransparency.of("", - List.of(0)))), - null), - null, - null)) + .privacy(AccountPrivacyConfig.builder() + .dsa(AccountDsaConfig.of( + DefaultDsa.of(0, 1, 2, List.of(DsaTransparency.of("", List.of(0)))), null)) + .build()) .build(); given(applicationSettings.getAccountById(any(), any())).willReturn(Future.succeededFuture(account)); @@ -1506,12 +1500,7 @@ public void enrichBidRequestWithAccountAndPrivacyDataShouldNotSetDsaFromAccountW final Account account = Account.builder() .id(accountId) - .privacy(AccountPrivacyConfig.of(null, - null, - AccountDsaConfig.of(null, - null), - null, - null)) + .privacy(AccountPrivacyConfig.builder().dsa(AccountDsaConfig.of(null, null)).build()) .build(); given(applicationSettings.getAccountById(any(), any())).willReturn(Future.succeededFuture(account)); @@ -1565,16 +1554,10 @@ public void enrichBidRequestWithAccountAndPrivacyDataShouldNotSetDsaFromAccountW final Account account = Account.builder() .id(accountId) - .privacy(AccountPrivacyConfig.of(null, - null, - AccountDsaConfig.of(DefaultDsa.of(3, - 4, - 5, - List.of(DsaTransparency.of("domain", - List.of(1)))), - null), - null, - null)) + .privacy(AccountPrivacyConfig.builder() + .dsa(AccountDsaConfig.of( + DefaultDsa.of(3, 4, 5, List.of(DsaTransparency.of("domain", List.of(1)))), null)) + .build()) .build(); given(applicationSettings.getAccountById(any(), any())).willReturn(Future.succeededFuture(account)); @@ -1624,16 +1607,10 @@ public void enrichBidRequestWithAccountAndPrivacyDataShouldSetDsaFromAccountWhen final Account account = Account.builder() .id(accountId) - .privacy(AccountPrivacyConfig.of(null, - null, - AccountDsaConfig.of(DefaultDsa.of(0, - 1, - 2, - List.of(DsaTransparency.of("", - List.of(0)))), - true), - null, - null)) + .privacy(AccountPrivacyConfig.builder() + .dsa(AccountDsaConfig.of( + DefaultDsa.of(0, 1, 2, List.of(DsaTransparency.of("", List.of(0)))), true)) + .build()) .build(); given(applicationSettings.getAccountById(any(), any())).willReturn(Future.succeededFuture(account)); @@ -1685,16 +1662,10 @@ public void enrichBidRequestWithAccountAndPrivacyDataShouldNotSetDsaFromAccountW final Account account = Account.builder() .id(accountId) - .privacy(AccountPrivacyConfig.of(null, - null, - AccountDsaConfig.of(DefaultDsa.of(0, - 1, - 2, - List.of(DsaTransparency.of("", - List.of(0)))), - true), - null, - null)) + .privacy(AccountPrivacyConfig.builder() + .dsa(AccountDsaConfig.of( + DefaultDsa.of(0, 1, 2, List.of(DsaTransparency.of("", List.of(0)))), true)) + .build()) .build(); given(applicationSettings.getAccountById(any(), any())).willReturn(Future.succeededFuture(account)); diff --git a/src/test/java/org/prebid/server/auction/requestfactory/VideoRequestFactoryTest.java b/src/test/java/org/prebid/server/auction/requestfactory/VideoRequestFactoryTest.java index 71eb233b2f4..f9b4f23cc79 100644 --- a/src/test/java/org/prebid/server/auction/requestfactory/VideoRequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/requestfactory/VideoRequestFactoryTest.java @@ -25,11 +25,11 @@ import org.prebid.server.VertxTest; import org.prebid.server.auction.DebugResolver; import org.prebid.server.auction.PriceGranularity; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.VideoStoredRequestProcessor; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.WithPodErrors; import org.prebid.server.auction.model.debug.DebugContext; +import org.prebid.server.auction.privacy.contextfactory.AuctionPrivacyContextFactory; import org.prebid.server.auction.versionconverter.BidRequestOrtbVersionConversionManager; import org.prebid.server.exception.InvalidRequestException; import org.prebid.server.metric.MetricName; @@ -81,7 +81,7 @@ public class VideoRequestFactoryTest extends VertxTest { @Mock private Ortb2ImplicitParametersResolver paramsResolver; @Mock - private PrivacyEnforcementService privacyEnforcementService; + private AuctionPrivacyContextFactory auctionPrivacyContextFactory; private VideoRequestFactory target; @@ -124,7 +124,7 @@ public void setUp() { .coppa(0) .build(), TcfContext.empty()); - given(privacyEnforcementService.contextFromBidRequest(any())) + given(auctionPrivacyContextFactory.contextFrom(any())) .willReturn(Future.succeededFuture(defaultPrivacyContext)); given(ortb2RequestFactory.populateUserAdditionalInfo(any())) @@ -138,7 +138,7 @@ public void setUp() { videoStoredRequestProcessor, ortbVersionConversionManager, paramsResolver, - privacyEnforcementService, + auctionPrivacyContextFactory, debugResolver, jacksonMapper); } @@ -173,7 +173,7 @@ public void shouldReturnFailedFutureIfStoredRequestIsEnforcedAndIdIsNotProvided( videoStoredRequestProcessor, ortbVersionConversionManager, paramsResolver, - privacyEnforcementService, + auctionPrivacyContextFactory, debugResolver, jacksonMapper); @@ -198,7 +198,7 @@ public void shouldReturnFailedFutureIfRequestBodyExceedsMaxRequestSize() { videoStoredRequestProcessor, ortbVersionConversionManager, paramsResolver, - privacyEnforcementService, + auctionPrivacyContextFactory, debugResolver, jacksonMapper); diff --git a/src/test/java/org/prebid/server/bidder/minutemedia/MinuteMediaBidderTest.java b/src/test/java/org/prebid/server/bidder/minutemedia/MinuteMediaBidderTest.java new file mode 100644 index 00000000000..6a1460149ea --- /dev/null +++ b/src/test/java/org/prebid/server/bidder/minutemedia/MinuteMediaBidderTest.java @@ -0,0 +1,258 @@ +package org.prebid.server.bidder.minutemedia; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import org.junit.Test; +import org.prebid.server.VertxTest; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderCall; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpRequest; +import org.prebid.server.bidder.model.HttpResponse; +import org.prebid.server.bidder.model.Result; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.minutemedia.ExtImpMinuteMedia; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import static java.util.Collections.singletonList; +import static java.util.function.UnaryOperator.identity; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; +import static org.prebid.server.proto.openrtb.ext.response.BidType.video; + +public class MinuteMediaBidderTest extends VertxTest { + + private static final String ENDPOINT_URL = "https://randomurl.com/exchange?publisherId={{PublisherId}}"; + + private final MinuteMediaBidder target = new MinuteMediaBidder(ENDPOINT_URL, jacksonMapper); + + @Test + public void creationShouldFailOnInvalidEndpointUrl() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new MinuteMediaBidder("invalid_url", jacksonMapper)); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { + // given + final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.ext( + mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()).allSatisfy(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(error.getMessage()).startsWith("Cannot deserialize value"); + }); + } + + @Test + public void makeHttpRequestsShouldResolveEndpointUrlUsingFirstImp() { + // given + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder.ext(givenImpExt("123")), + impBuilder -> impBuilder.ext(givenImpExt("456"))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()) + .extracting(HttpRequest::getUri) + .containsExactly("https://randomurl.com/exchange?publisherId=123"); + assertThat(result.getErrors()).isEmpty(); + } + + @Test + public void makeHttpRequestsShouldReturnErrorOnAbsentImpExtBidderOrg() { + // given + final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.ext(givenImpExt(null))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()).allSatisfy(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(error.getMessage()) + .startsWith("Failed to extract bidrequest.imp[0].ext.prebid.bidder.minutemedia.org parameter"); + }); + } + + @Test + public void makeHttpRequestsShouldReturnErrorOnEmptyImpExtBidderOrg() { + // given + final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.ext(givenImpExt(""))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()).allSatisfy(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(error.getMessage()) + .startsWith("Failed to extract bidrequest.imp[0].ext.prebid.bidder.minutemedia.org parameter"); + }); + } + + @Test + public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { + // given + final BidderCall httpCall = givenHttpCall(null, "invalid"); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).hasSize(1) + .allSatisfy(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response); + assertThat(error.getMessage()).startsWith("Failed to decode: Unrecognized token"); + }); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall(null, mapper.writeValueAsString(null)); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall(null, + mapper.writeValueAsString(BidResponse.builder().build())); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnBannerBidIfMtypeIs1() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall( + givenBidRequest(identity()), + mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(1)))); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .containsExactly(BidderBid.of(givenBid(bidBuilder -> bidBuilder.mtype(1)), banner, null)); + } + + @Test + public void makeBidsShouldReturnVideoBidIfMtypeIs2() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall( + givenBidRequest(identity()), + mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(2)))); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .containsExactly(BidderBid.of(givenBid(bidBuilder -> bidBuilder.mtype(2)), video, null)); + } + + @Test + public void makeBidsShouldReturnErrorIfMtypeIs3() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall( + givenBidRequest(identity()), + mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(3)))); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()) + .containsExactly(BidderError.badServerResponse("Unsupported bid mediaType: 3 for impression: 123")); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnErrorIfMtypeIs4() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall( + givenBidRequest(identity()), + mapper.writeValueAsString(givenBidResponse(bidBuilder -> bidBuilder.mtype(4)))); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()) + .containsExactly(BidderError.badServerResponse("Unsupported bid mediaType: 4 for impression: 123")); + assertThat(result.getValue()).isEmpty(); + } + + @SafeVarargs + private static BidRequest givenBidRequest(UnaryOperator... impCustomizers) { + return BidRequest.builder() + .imp(Arrays.stream(impCustomizers).map(MinuteMediaBidderTest::givenImp).toList()) + .build(); + } + + private static Imp givenImp(UnaryOperator impCustomizer) { + return impCustomizer.apply(Imp.builder() + .id("imp_id") + .ext(givenImpExt("123"))) + .build(); + } + + private static ObjectNode givenImpExt(String org) { + return mapper.valueToTree(ExtPrebid.of(null, + ExtImpMinuteMedia.of(org))); + } + + private static BidResponse givenBidResponse(Function bidCustomizer) { + return BidResponse.builder() + .seatbid(singletonList(SeatBid.builder().bid( + singletonList(givenBid(bidCustomizer))) + .build())) + .build(); + } + + private static Bid givenBid(Function bidCustomizer) { + return bidCustomizer.apply(Bid.builder().impid("123")).build(); + } + + private static BidderCall givenHttpCall(BidRequest bidRequest, String body) { + return BidderCall.succeededHttp( + HttpRequest.builder().payload(bidRequest).build(), + HttpResponse.of(200, null, body), + null); + } +} diff --git a/src/test/java/org/prebid/server/cookie/CookieSyncServiceTest.java b/src/test/java/org/prebid/server/cookie/CookieSyncServiceTest.java index a0ee29d837d..a587620365a 100644 --- a/src/test/java/org/prebid/server/cookie/CookieSyncServiceTest.java +++ b/src/test/java/org/prebid/server/cookie/CookieSyncServiceTest.java @@ -12,7 +12,7 @@ import org.prebid.server.activity.Activity; import org.prebid.server.activity.ComponentType; import org.prebid.server.activity.infrastructure.ActivityInfrastructure; -import org.prebid.server.auction.PrivacyEnforcementService; +import org.prebid.server.auction.privacy.enforcement.CcpaEnforcement; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.bidder.UsersyncMethod; import org.prebid.server.bidder.UsersyncMethodChooser; @@ -74,7 +74,7 @@ public class CookieSyncServiceTest extends VertxTest { @Mock private HostVendorTcfDefinerService hostVendorTcfDefinerService; @Mock - private PrivacyEnforcementService privacyEnforcementService; + private CcpaEnforcement ccpaEnforcement; @Mock private UidsCookieService uidsCookieService; @Mock @@ -1171,7 +1171,7 @@ private void givenCookieSyncService(int limit, int maxLimit) { maxLimit, bidderCatalog, hostVendorTcfDefinerService, - privacyEnforcementService, + ccpaEnforcement, uidsCookieService, coopSyncProvider, metrics); diff --git a/src/test/java/org/prebid/server/handler/CookieSyncHandlerTest.java b/src/test/java/org/prebid/server/handler/CookieSyncHandlerTest.java index 778a17c256d..e4a305e1f04 100644 --- a/src/test/java/org/prebid/server/handler/CookieSyncHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/CookieSyncHandlerTest.java @@ -20,8 +20,8 @@ import org.prebid.server.activity.infrastructure.creator.ActivityInfrastructureCreator; import org.prebid.server.analytics.model.CookieSyncEvent; import org.prebid.server.analytics.reporter.AnalyticsReporterDelegator; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.gpp.CookieSyncGppService; +import org.prebid.server.auction.privacy.contextfactory.CookieSyncPrivacyContextFactory; import org.prebid.server.cookie.CookieDeprecationService; import org.prebid.server.cookie.CookieSyncService; import org.prebid.server.cookie.UidsCookie; @@ -91,7 +91,7 @@ public class CookieSyncHandlerTest extends VertxTest { @Mock private ApplicationSettings applicationSettings; @Mock - private PrivacyEnforcementService privacyEnforcementService; + private CookieSyncPrivacyContextFactory cookieSyncPrivacyContextFactory; @Mock private AnalyticsReporterDelegator analyticsReporterDelegator; @Mock @@ -115,7 +115,7 @@ public void setUp() { given(httpResponse.setStatusCode(anyInt())).willReturn(httpResponse); given(httpResponse.putHeader(any(CharSequence.class), any(AsciiString.class))).willReturn(httpResponse); - given(privacyEnforcementService.contextFromCookieSyncRequest(any(), any(), any(), any())) + given(cookieSyncPrivacyContextFactory.contextFrom(any(), any(), any(), any())) .willReturn(Future.succeededFuture(PrivacyContext.of( Privacy.builder() .gdpr("") @@ -134,7 +134,7 @@ public void setUp() { activityInfrastructureCreator, cookieSyncService, applicationSettings, - privacyEnforcementService, + cookieSyncPrivacyContextFactory, analyticsReporterDelegator, metrics, timeoutFactory, @@ -240,7 +240,7 @@ public void shouldRespondWithBadRequestStatusIfGdprConsentIsInvalid() { .gdprConsent("invalid") .build())); - given(privacyEnforcementService.contextFromCookieSyncRequest(any(), any(), any(), any())) + given(cookieSyncPrivacyContextFactory.contextFrom(any(), any(), any(), any())) .willReturn(Future.succeededFuture(PrivacyContext.of(null, TcfContext.builder().inGdprScope(true).consentValid(false).build()))); @@ -265,7 +265,7 @@ public void shouldRespondWithBadRequestStatusOnInvalidAccountConfigException() { .gdprConsent("valid") .build())); - given(privacyEnforcementService.contextFromCookieSyncRequest(any(), any(), any(), any())) + given(cookieSyncPrivacyContextFactory.contextFrom(any(), any(), any(), any())) .willReturn(Future.succeededFuture(PrivacyContext.of(null, TcfContext.builder().inGdprScope(true).consentValid(true).build()))); @@ -353,11 +353,11 @@ public void shouldPassAccountToPrivacyEnforcementServiceWhenAccountIsFound() { final AccountGdprConfig accountGdprConfig = AccountGdprConfig.builder() .enabledForRequestType(EnabledForRequestType.of(true, true, true, true, true)).build(); final Account account = Account.builder() - .privacy(AccountPrivacyConfig.of(accountGdprConfig, null, null, null, null)) + .privacy(AccountPrivacyConfig.builder().gdpr(accountGdprConfig).build()) .build(); given(applicationSettings.getAccountById(any(), any())).willReturn(Future.succeededFuture(account)); - given(privacyEnforcementService.contextFromCookieSyncRequest(any(), any(), any(), any())) + given(cookieSyncPrivacyContextFactory.contextFrom(any(), any(), any(), any())) .willReturn(Future.failedFuture("fail")); // when @@ -365,8 +365,7 @@ public void shouldPassAccountToPrivacyEnforcementServiceWhenAccountIsFound() { // then verify(applicationSettings).getAccountById(eq("account"), any()); - - verify(privacyEnforcementService).contextFromCookieSyncRequest(any(), any(), eq(account), any()); + verify(cookieSyncPrivacyContextFactory).contextFrom(any(), any(), eq(account), any()); } @Test @@ -377,7 +376,7 @@ public void shouldPassAccountToPrivacyEnforcementServiceWhenAccountIsNotFound() given(applicationSettings.getAccountById(any(), any())).willReturn(Future.failedFuture("bad")); - given(privacyEnforcementService.contextFromCookieSyncRequest(any(), any(), any(), any())) + given(cookieSyncPrivacyContextFactory.contextFrom(any(), any(), any(), any())) .willReturn(Future.failedFuture("fail")); givenDefaultCookieSyncServicePipelineResult(); @@ -386,9 +385,7 @@ public void shouldPassAccountToPrivacyEnforcementServiceWhenAccountIsNotFound() // then verify(applicationSettings).getAccountById(eq("account"), any()); - - verify(privacyEnforcementService) - .contextFromCookieSyncRequest(any(), any(), eq(Account.empty("account")), any()); + verify(cookieSyncPrivacyContextFactory).contextFrom(any(), any(), eq(Account.empty("account")), any()); } @Test diff --git a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java index 0f99716f44b..001f4e93099 100644 --- a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java @@ -21,8 +21,8 @@ import org.prebid.server.activity.infrastructure.creator.ActivityInfrastructureCreator; import org.prebid.server.analytics.model.SetuidEvent; import org.prebid.server.analytics.reporter.AnalyticsReporterDelegator; -import org.prebid.server.auction.PrivacyEnforcementService; import org.prebid.server.auction.gpp.SetuidGppService; +import org.prebid.server.auction.privacy.contextfactory.SetuidPrivacyContextFactory; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.bidder.UsersyncMethod; import org.prebid.server.bidder.UsersyncMethodType; @@ -87,7 +87,7 @@ public class SetuidHandlerTest extends VertxTest { @Mock private BidderCatalog bidderCatalog; @Mock - private PrivacyEnforcementService privacyEnforcementService; + private SetuidPrivacyContextFactory setuidPrivacyContextFactory; @Mock private SetuidGppService gppService; @Mock @@ -117,7 +117,7 @@ public void setUp() { PrivacyEnforcementAction.allowAll()); tcfContext = TcfContext.builder().inGdprScope(false).build(); - given(privacyEnforcementService.contextFromSetuidRequest(any(), any(), any())) + given(setuidPrivacyContextFactory.contextFrom(any(), any(), any())) .willReturn(Future.succeededFuture(PrivacyContext.of(null, tcfContext))); given(gppService.contextFrom(any())).willReturn(Future.succeededFuture()); given(gppService.updateSetuidContext(any())) @@ -158,7 +158,7 @@ public void setUp() { uidsCookieService, applicationSettings, bidderCatalog, - privacyEnforcementService, + setuidPrivacyContextFactory, gppService, activityInfrastructureCreator, tcfDefinerService, @@ -230,7 +230,7 @@ public void shouldRespondWithBadRequestStatusIfGdprConsentIsInvalid() { .willReturn(new UidsCookie(Uids.builder().uids(emptyMap()).build(), jacksonMapper)); tcfContext = TcfContext.builder().inGdprScope(true).consentValid(false).build(); - given(privacyEnforcementService.contextFromSetuidRequest(any(), any(), any())) + given(setuidPrivacyContextFactory.contextFrom(any(), any(), any())) .willReturn(Future.succeededFuture(PrivacyContext.of(null, tcfContext))); // when @@ -394,7 +394,7 @@ public void shouldPassAccountToPrivacyEnforcementServiceWhenAccountIsFound() { .enabledForRequestType(EnabledForRequestType.of(true, true, true, true, true)) .build(); final Account account = Account.builder() - .privacy(AccountPrivacyConfig.of(accountGdprConfig, null, null, null, null)) + .privacy(AccountPrivacyConfig.builder().gdpr(accountGdprConfig).build()) .build(); final Future accountFuture = Future.succeededFuture(account); given(applicationSettings.getAccountById(any(), any())).willReturn(accountFuture); @@ -404,7 +404,7 @@ public void shouldPassAccountToPrivacyEnforcementServiceWhenAccountIsFound() { // then verify(applicationSettings).getAccountById(eq("accId"), any()); - verify(privacyEnforcementService).contextFromSetuidRequest(any(), eq(account), any()); + verify(setuidPrivacyContextFactory).contextFrom(any(), eq(account), any()); } @Test @@ -426,7 +426,7 @@ public void shouldPassAccountToPrivacyEnforcementServiceWhenAccountIsNotFound() // then verify(applicationSettings).getAccountById(eq("accId"), any()); - verify(privacyEnforcementService).contextFromSetuidRequest(any(), eq(Account.empty("accId")), any()); + verify(setuidPrivacyContextFactory).contextFrom(any(), eq(Account.empty("accId")), any()); } @Test @@ -507,7 +507,7 @@ public void shouldSendEmptyResponseWhenFParamIsEqualToBWhenTypeIsRedirect() { uidsCookieService, applicationSettings, bidderCatalog, - privacyEnforcementService, + setuidPrivacyContextFactory, gppService, activityInfrastructureCreator, tcfDefinerService, @@ -551,7 +551,7 @@ public void shouldSendEmptyResponseWhenFParamNotDefinedAndTypeIsIframe() { uidsCookieService, applicationSettings, bidderCatalog, - privacyEnforcementService, + setuidPrivacyContextFactory, gppService, activityInfrastructureCreator, tcfDefinerService, @@ -594,7 +594,7 @@ public void shouldSendPixelWhenFParamNotDefinedAndTypeIsRedirect() { uidsCookieService, applicationSettings, bidderCatalog, - privacyEnforcementService, + setuidPrivacyContextFactory, gppService, activityInfrastructureCreator, tcfDefinerService, @@ -687,7 +687,7 @@ public void shouldSkipTcfChecksAndRespondWithCookieIfHostVendorIdNotDefined() th uidsCookieService, applicationSettings, bidderCatalog, - privacyEnforcementService, + setuidPrivacyContextFactory, gppService, activityInfrastructureCreator, tcfDefinerService, @@ -790,7 +790,7 @@ public void shouldThrowExceptionInCaseOfCookieFamilyNameDuplicates() { uidsCookieService, applicationSettings, bidderCatalog, - privacyEnforcementService, + setuidPrivacyContextFactory, gppService, activityInfrastructureCreator, tcfDefinerService, diff --git a/src/test/java/org/prebid/server/it/MinuteMediaTest.java b/src/test/java/org/prebid/server/it/MinuteMediaTest.java new file mode 100644 index 00000000000..b6d79986eb1 --- /dev/null +++ b/src/test/java/org/prebid/server/it/MinuteMediaTest.java @@ -0,0 +1,39 @@ +package org.prebid.server.it; + +import io.restassured.response.Response; +import org.json.JSONException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.prebid.server.model.Endpoint; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.util.Collections.singletonList; + +@RunWith(SpringRunner.class) +public class MinuteMediaTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromMinuteMedia() throws IOException, JSONException { + // given + + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/minutemedia-exchange")) + .withQueryParam("publisherId", equalTo("123")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/minutemedia/test-minutemedia-bid-request.json"))) + .willReturn(aResponse().withBody(jsonFrom("openrtb2/minutemedia/test-minutemedia-bid-response.json")))); + + // when + final Response response = responseFor("openrtb2/minutemedia/test-auction-minutemedia-request.json", + Endpoint.openrtb2_auction); + + // then + assertJsonEquals("openrtb2/minutemedia/test-auction-minutemedia-response.json", response, + singletonList("minutemedia")); + } +} diff --git a/src/test/java/org/prebid/server/metric/MetricsTest.java b/src/test/java/org/prebid/server/metric/MetricsTest.java index 8a6518666d5..c428db441c2 100644 --- a/src/test/java/org/prebid/server/metric/MetricsTest.java +++ b/src/test/java/org/prebid/server/metric/MetricsTest.java @@ -809,19 +809,21 @@ public void updateDeliveryRequestTimeShouldLogTime() { @Test public void updateAuctionTcfMetricsShouldIncrementMetrics() { // when - metrics.updateAuctionTcfMetrics(RUBICON, MetricName.openrtb2web, true, true, true, true); - metrics.updateAuctionTcfMetrics(CONVERSANT, MetricName.openrtb2web, false, true, true, false); - metrics.updateAuctionTcfMetrics(CONVERSANT, MetricName.openrtb2app, true, false, false, true); + metrics.updateAuctionTcfMetrics(RUBICON, MetricName.openrtb2web, true, true, true, true, true); + metrics.updateAuctionTcfMetrics(CONVERSANT, MetricName.openrtb2web, true, false, true, false, true); + metrics.updateAuctionTcfMetrics(CONVERSANT, MetricName.openrtb2app, false, true, false, true, false); // then + assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.userfpd_masked").getCount()).isOne(); assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.userid_removed").getCount()).isOne(); assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.geo_masked").getCount()).isOne(); assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.analytics_blocked").getCount()).isOne(); assertThat(metricRegistry.counter("adapter.rubicon.openrtb2-web.tcf.request_blocked").getCount()).isOne(); - assertThat(metricRegistry.counter("adapter.conversant.openrtb2-web.tcf.geo_masked").getCount()).isOne(); - assertThat(metricRegistry.counter("adapter.conversant.openrtb2-web.tcf.analytics_blocked").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.conversant.openrtb2-web.tcf.userfpd_masked").getCount()).isOne(); assertThat(metricRegistry.counter("adapter.conversant.openrtb2-app.tcf.userid_removed").getCount()).isOne(); - assertThat(metricRegistry.counter("adapter.conversant.openrtb2-app.tcf.request_blocked").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.conversant.openrtb2-web.tcf.geo_masked").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.conversant.openrtb2-app.tcf.analytics_blocked").getCount()).isOne(); + assertThat(metricRegistry.counter("adapter.conversant.openrtb2-web.tcf.request_blocked").getCount()).isOne(); } @Test diff --git a/src/test/java/org/prebid/server/privacy/gdpr/Tcf2ServiceTest.java b/src/test/java/org/prebid/server/privacy/gdpr/Tcf2ServiceTest.java index ebe1a847ce2..ae0645fc030 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/Tcf2ServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/Tcf2ServiceTest.java @@ -8,6 +8,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import org.mockito.stubbing.Answer; import org.prebid.server.VertxTest; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; @@ -21,34 +22,36 @@ import org.prebid.server.settings.model.EnforcePurpose; import org.prebid.server.settings.model.GdprConfig; import org.prebid.server.settings.model.Purpose; +import org.prebid.server.settings.model.PurposeEid; import org.prebid.server.settings.model.PurposeOneTreatmentInterpretation; import org.prebid.server.settings.model.Purposes; import org.prebid.server.settings.model.SpecialFeature; import org.prebid.server.settings.model.SpecialFeatures; import java.util.Collection; -import java.util.HashSet; import java.util.List; +import java.util.TreeSet; +import java.util.function.Consumer; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; +import static org.apache.commons.collections4.SetUtils.hashSet; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyCollection; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.prebid.server.assertion.FutureAssertion.assertThat; +import static org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction.restrictAll; import static org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode.FOUR; import static org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode.ONE; import static org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode.SEVEN; @@ -75,6 +78,8 @@ public class Tcf2ServiceTest extends VertxTest { private SpecialFeaturesStrategy specialFeaturesStrategyOne; @Mock private TCString tcString; + @Mock + private VendorIdResolver vendorIdResolver; private Tcf2Service target; @@ -100,7 +105,8 @@ public class Tcf2ServiceTest extends VertxTest { @Before public void setUp() { - given(tcString.getVendorListVersion()).willReturn(10); + given(vendorListService.forConsent(any())).willReturn(Future.succeededFuture(emptyMap())); + given(purposeStrategyOne.getPurpose()).willReturn(ONE); given(purposeStrategyTwo.getPurpose()).willReturn(TWO); given(purposeStrategyFour.getPurpose()).willReturn(FOUR); @@ -110,20 +116,30 @@ public void setUp() { given(specialFeaturesStrategyOne.getSpecialFeatureId()).willReturn(1); specialFeaturesStrategies = singletonList(specialFeaturesStrategyOne); - given(vendorListService.forConsent(any())).willReturn(Future.succeededFuture(emptyMap())); + given(tcString.getVendorListVersion()).willReturn(10); + given(vendorIdResolver.resolve(anyString())).willReturn(null); + + initTcf2Service(PurposeOneTreatmentInterpretation.ignore); + } + private void initTcf2Service(PurposeOneTreatmentInterpretation p1TI) { initPurposes(); initSpecialFeatures(); - initGdpr(); - target = new Tcf2Service(gdprConfig, purposeStrategies, specialFeaturesStrategies, vendorListService, + initGdpr(p1TI); + + target = new Tcf2Service( + gdprConfig, + purposeStrategies, + specialFeaturesStrategies, + vendorListService, bidderCatalog); } private void initPurposes() { - purpose1 = Purpose.of(EnforcePurpose.basic, true, emptyList()); - purpose2 = Purpose.of(EnforcePurpose.no, true, emptyList()); - purpose4 = Purpose.of(EnforcePurpose.no, false, emptyList()); - purpose7 = Purpose.of(EnforcePurpose.full, false, emptyList()); + purpose1 = Purpose.of(EnforcePurpose.basic, true, emptyList(), null); + purpose2 = Purpose.of(EnforcePurpose.no, true, emptyList(), null); + purpose4 = Purpose.of(EnforcePurpose.no, false, emptyList(), null); + purpose7 = Purpose.of(EnforcePurpose.full, false, emptyList(), null); purposes = Purposes.builder() .p1(purpose1) .p2(purpose2) @@ -131,10 +147,10 @@ private void initPurposes() { .p7(purpose7) .build(); - weakPurpose1 = Purpose.of(EnforcePurpose.basic, false, emptyList()); - weakPurpose2 = Purpose.of(EnforcePurpose.no, false, emptyList()); - weakPurpose4 = Purpose.of(EnforcePurpose.no, false, emptyList()); - weakPurpose7 = Purpose.of(EnforcePurpose.basic, false, emptyList()); + weakPurpose1 = Purpose.of(EnforcePurpose.basic, false, emptyList(), null); + weakPurpose2 = Purpose.of(EnforcePurpose.no, false, emptyList(), null); + weakPurpose4 = Purpose.of(EnforcePurpose.no, false, emptyList(), null); + weakPurpose7 = Purpose.of(EnforcePurpose.basic, false, emptyList(), null); } private void initSpecialFeatures() { @@ -144,13 +160,13 @@ private void initSpecialFeatures() { .build(); } - private void initGdpr() { + private void initGdpr(PurposeOneTreatmentInterpretation p1TI) { gdprConfig = GdprConfig.builder() .defaultValue("1") .enabled(true) .purposes(purposes) .specialFeatures(specialFeatures) - .purposeOneTreatmentInterpretation(PurposeOneTreatmentInterpretation.ignore) + .purposeOneTreatmentInterpretation(p1TI) .build(); } @@ -163,17 +179,12 @@ public void permissionsForShouldReturnByGdprPurpose() { final Future> result = target.permissionsFor(singleton(1), tcString); // then - final VendorPermission expectedVendorPermission = - VendorPermission.of(1, "rubicon", PrivacyEnforcementAction.restrictAll()); - + final VendorPermission expectedVendorPermission = VendorPermission.of(1, "rubicon", restrictAll()); assertThat(result).succeededWith(singletonList(expectedVendorPermission)); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl = VendorPermissionWithGvl.of( - expectedVendorPermission, Vendor.empty(1)); - verifyEachPurposeStrategyReceive(singletonList(expectedVendorPermissionWitGvl)); + verifyEachPurposeStrategyReceive(singletonList(withGvl(expectedVendorPermission, 1))); verifyEachSpecialFeatureStrategyReceive(singletonList(expectedVendorPermission)); - verify(bidderCatalog).nameByVendorId(1); verify(vendorListService).forConsent(argThat(tcString -> tcString.getVendorListVersion() == 10)); } @@ -187,54 +198,48 @@ public void permissionsForShouldReturnByGdprPurposeAndDowngradeToBasicTypeWhenVe final Future> result = target.permissionsFor(singleton(1), tcString); // then - final VendorPermission expectedVendorPermission = VendorPermission.of(1, "rubicon", - PrivacyEnforcementAction.restrictAll()); + final VendorPermission expectedVendorPermission = VendorPermission.of(1, "rubicon", restrictAll()); assertThat(result).succeededWith(singletonList(expectedVendorPermission)); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl = VendorPermissionWithGvl.of( - expectedVendorPermission, Vendor.empty(1)); - final List vendorPermissionWithGvls = singletonList(expectedVendorPermissionWitGvl); - verify(purposeStrategyOne).processTypePurposeStrategy(tcString, purpose1, vendorPermissionWithGvls, true); - verify(purposeStrategyTwo).processTypePurposeStrategy(tcString, purpose2, vendorPermissionWithGvls, true); - verify(purposeStrategyFour).processTypePurposeStrategy(tcString, purpose4, vendorPermissionWithGvls, true); - - final Purpose expectedDowngradedPurpose = Purpose.of(EnforcePurpose.basic, purpose7.getEnforceVendors(), - purpose1.getVendorExceptions()); - verify(purposeStrategySeven).processTypePurposeStrategy(tcString, expectedDowngradedPurpose, - vendorPermissionWithGvls, true); + final Purpose downgradedPurpose7 = Purpose.of( + EnforcePurpose.basic, + purpose7.getEnforceVendors(), + purpose7.getVendorExceptions(), + purpose7.getEid()); + final List permissionsWithGvl = singletonList(withGvl(expectedVendorPermission, 1)); + verify(purposeStrategyOne).processTypePurposeStrategy(tcString, purpose1, permissionsWithGvl, true); + verify(purposeStrategyTwo).processTypePurposeStrategy(tcString, purpose2, permissionsWithGvl, true); + verify(purposeStrategyFour).processTypePurposeStrategy(tcString, purpose4, permissionsWithGvl, true); + verify(purposeStrategySeven) + .processTypePurposeStrategy(tcString, downgradedPurpose7, permissionsWithGvl, true); verifyEachSpecialFeatureStrategyReceive(singletonList(expectedVendorPermission)); - verify(bidderCatalog).nameByVendorId(1); verify(vendorListService).forConsent(any()); } @Test public void permissionsForShouldMergeAccountPurposes() { // given - final Purpose accountPurposeOne = Purpose.of(EnforcePurpose.full, false, singletonList("test")); - final Purposes accountPurposes = Purposes.builder() - .p1(accountPurposeOne) + final Purpose accountPurposeOne = Purpose.of(EnforcePurpose.full, false, singletonList("test"), null); + final AccountGdprConfig accountGdprConfig = AccountGdprConfig.builder() + .purposes(Purposes.builder().p1(accountPurposeOne).build()) .build(); - final AccountGdprConfig accountGdprConfig = AccountGdprConfig.builder().purposes(accountPurposes).build(); - - final VendorIdResolver vendorIdResolver = mock(VendorIdResolver.class); - given(vendorIdResolver.resolve(anyString())).willReturn(null); - // when - final Future> result = - target.permissionsFor(singleton("b1"), vendorIdResolver, tcString, accountGdprConfig); + final Future> result = target.permissionsFor( + singleton("b1"), vendorIdResolver, tcString, accountGdprConfig); // then - final VendorPermission expectedVendorPermission = - VendorPermission.of(null, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission expectedVendorPermission = VendorPermission.of(null, "b1", restrictAll()); assertThat(result).succeededWith(singletonList(expectedVendorPermission)); verify(purposeStrategyOne).processTypePurposeStrategy( tcString, accountPurposeOne, - singletonList(VendorPermissionWithGvl.of(expectedVendorPermission, Vendor.empty(null))), + singletonList(withGvl(expectedVendorPermission, null)), false); + + verify(vendorIdResolver).resolve(anyString()); verify(vendorListService).forConsent(argThat(tcString -> tcString.getVendorListVersion() == 10)); } @@ -242,24 +247,16 @@ public void permissionsForShouldMergeAccountPurposes() { public void permissionsForShouldMergeAccountSpecialFeatures() { // given final SpecialFeature accountSpecialFeatureOne = SpecialFeature.of(false, emptyList()); - final SpecialFeatures specialFeatures = SpecialFeatures.builder() - .sf1(accountSpecialFeatureOne) - .build(); - final AccountGdprConfig accountGdprConfig = AccountGdprConfig.builder() - .specialFeatures(specialFeatures) + .specialFeatures(SpecialFeatures.builder().sf1(accountSpecialFeatureOne).build()) .build(); - final VendorIdResolver vendorIdResolver = mock(VendorIdResolver.class); - given(vendorIdResolver.resolve(anyString())).willReturn(null); - // when - final Future> result = - target.permissionsFor(singleton("b1"), vendorIdResolver, tcString, accountGdprConfig); + final Future> result = target.permissionsFor( + singleton("b1"), vendorIdResolver, tcString, accountGdprConfig); // then - final VendorPermission expectedVendorPermission = - VendorPermission.of(null, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission expectedVendorPermission = VendorPermission.of(null, "b1", restrictAll()); assertThat(result).succeededWith(singletonList(expectedVendorPermission)); verify(specialFeaturesStrategyOne).processSpecialFeaturesStrategy( @@ -271,61 +268,46 @@ public void permissionsForShouldMergeAccountSpecialFeatures() { @Test public void permissionsForShouldSplitIntoWeakPurposesWhenAccountHaveBasicEnforcementBidders() { // given - final VendorIdResolver vendorIdResolver = mock(VendorIdResolver.class); given(vendorIdResolver.resolve(eq("b1"))).willReturn(1); given(vendorIdResolver.resolve(eq("b2"))).willReturn(2); - final AccountGdprConfig accountGdprConfig = AccountGdprConfig.builder() .basicEnforcementVendors(singletonList("b2")) .build(); // when - final Future> result = - target.permissionsFor(new HashSet<>(asList("b1", "b2")), vendorIdResolver, tcString, accountGdprConfig); + final Future> result = target.permissionsFor( + hashSet("b1", "b2"), vendorIdResolver, tcString, accountGdprConfig); // then - final VendorPermission expectedVendorPermission1 = - VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission expectedVendorPermission2 = - VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl1 = - VendorPermissionWithGvl.of(expectedVendorPermission1, Vendor.empty(1)); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl2 = - VendorPermissionWithGvl.of(expectedVendorPermission2, Vendor.empty(2)); - - verifyEachPurposeStrategyReceive(singletonList(expectedVendorPermissionWitGvl1)); - verifyEachPurposeStrategyReceiveWeak(singletonList(expectedVendorPermissionWitGvl2)); + final VendorPermission expectedVendorPermission1 = VendorPermission.of(1, "b1", restrictAll()); + final VendorPermission expectedVendorPermission2 = VendorPermission.of(2, "b2", restrictAll()); + assertThat(result).succeededWith(asList(expectedVendorPermission2, expectedVendorPermission1)); + + verifyEachPurposeStrategyReceive(singletonList(withGvl(expectedVendorPermission1, 1))); + verifyEachPurposeStrategyReceiveWeak(singletonList(withGvl(expectedVendorPermission2, 2))); verifyEachSpecialFeatureStrategyReceive(asList(expectedVendorPermission2, expectedVendorPermission1)); verify(vendorIdResolver, times(2)).resolve(anyString()); verify(vendorListService).forConsent(any()); - - assertThat(result).succeededWith(asList(expectedVendorPermission2, expectedVendorPermission1)); } @Test public void permissionsForShouldReturnBidderNamesResult() { // given - final VendorIdResolver vendorIdResolver = mock(VendorIdResolver.class); given(vendorIdResolver.resolve(eq("b1"))).willReturn(1); - given(vendorIdResolver.resolve(eq("b2"))).willReturn(null); // when - final Future> result = - target.permissionsFor(new HashSet<>(asList("b1", "b2")), vendorIdResolver, tcString, null); + final Future> result = target.permissionsFor( + hashSet("b1", "b2"), vendorIdResolver, tcString, null); // then - final VendorPermission expectedVendorPermission1 = - VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission expectedVendorPermission2 = - VendorPermission.of(null, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl1 = - VendorPermissionWithGvl.of(expectedVendorPermission1, Vendor.empty(1)); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl2 = - VendorPermissionWithGvl.of(expectedVendorPermission2, Vendor.empty(null)); + final VendorPermission expectedVendorPermission1 = VendorPermission.of(1, "b1", restrictAll()); + final VendorPermission expectedVendorPermission2 = VendorPermission.of(null, "b2", restrictAll()); assertThat(result).succeededWith(asList(expectedVendorPermission2, expectedVendorPermission1)); - verifyEachPurposeStrategyReceive(asList(expectedVendorPermissionWitGvl2, expectedVendorPermissionWitGvl1)); + verifyEachPurposeStrategyReceive(asList( + withGvl(expectedVendorPermission2, null), + withGvl(expectedVendorPermission1, 1))); verifyEachSpecialFeatureStrategyReceive(asList(expectedVendorPermission2, expectedVendorPermission1)); verify(vendorIdResolver, times(2)).resolve(anyString()); @@ -338,150 +320,302 @@ public void permissionsForShouldReturnVendorIdsResult() { given(bidderCatalog.nameByVendorId(eq(1))).willReturn("b1"); // when - final Future> result = - target.permissionsFor(new HashSet<>(asList(1, 2)), tcString); + final Future> result = target.permissionsFor(hashSet(1, 2), tcString); // then - final VendorPermission expectedVendorPermission1 = VendorPermission.of(1, "b1", - PrivacyEnforcementAction.restrictAll()); - final VendorPermission expectedVendorPermission2 = VendorPermission.of(2, null, - PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl1 = VendorPermissionWithGvl.of( - expectedVendorPermission1, Vendor.empty(1)); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl2 = VendorPermissionWithGvl.of( - expectedVendorPermission2, Vendor.empty(2)); + final VendorPermission expectedVendorPermission1 = VendorPermission.of(1, "b1", restrictAll()); + final VendorPermission expectedVendorPermission2 = VendorPermission.of(2, null, restrictAll()); assertThat(result).succeededWith(asList(expectedVendorPermission1, expectedVendorPermission2)); - verifyEachPurposeStrategyReceive(asList(expectedVendorPermissionWitGvl1, expectedVendorPermissionWitGvl2)); + + verifyEachPurposeStrategyReceive(asList( + withGvl(expectedVendorPermission1, 1), + withGvl(expectedVendorPermission2, 2))); verifyEachSpecialFeatureStrategyReceive(asList(expectedVendorPermission1, expectedVendorPermission2)); - verify(bidderCatalog, times(2)).nameByVendorId(anyInt()); verify(vendorListService).forConsent(any()); - - verifyNoMoreInteractions(bidderCatalog); } @Test public void permissionsForShouldReturnAllDeniedWhenP1TIIsNoAccessAllowed() { // given given(bidderCatalog.nameByVendorId(any())).willReturn("rubicon"); - given(tcString.getPurposeOneTreatment()).willReturn(true); - - final GdprConfig gdprConfig = GdprConfig.builder() - .purposes(purposes) - .purposeOneTreatmentInterpretation(PurposeOneTreatmentInterpretation.noAccessAllowed) - .build(); - target = new Tcf2Service(gdprConfig, purposeStrategies, specialFeaturesStrategies, vendorListService, - bidderCatalog); + initTcf2Service(PurposeOneTreatmentInterpretation.noAccessAllowed); // when final Future> result = target.permissionsFor(singleton(1), tcString); // then - assertThat(result).succeededWith( - singletonList(VendorPermission.of(1, "rubicon", PrivacyEnforcementAction.restrictAll()))); - - final VendorPermission expectedVendorPermission1 = VendorPermission.of(1, "rubicon", - PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl1 = VendorPermissionWithGvl.of( - expectedVendorPermission1, Vendor.empty(1)); - final List standardPermissions = singletonList(expectedVendorPermissionWitGvl1); - - verify(purposeStrategyOne, never()).processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); - verify(purposeStrategyTwo).processTypePurposeStrategy(any(), any(), eq(standardPermissions), eq(false)); - verify(purposeStrategySeven).processTypePurposeStrategy(any(), any(), eq(standardPermissions), eq(false)); - verify(purposeStrategyFour).processTypePurposeStrategy(any(), any(), eq(standardPermissions), eq(false)); + final VendorPermission expectedVendorPermission = VendorPermission.of(1, "rubicon", restrictAll()); + assertThat(result).succeededWith(singletonList(expectedVendorPermission)); + final List permissions = singletonList(withGvl(expectedVendorPermission, 1)); + verify(purposeStrategyOne, never()) + .processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); + verify(purposeStrategyTwo).processTypePurposeStrategy(any(), any(), eq(permissions), eq(false)); + verify(purposeStrategySeven).processTypePurposeStrategy(any(), any(), eq(permissions), eq(false)); + verify(purposeStrategyFour).processTypePurposeStrategy(any(), any(), eq(permissions), eq(false)); verify(purposeStrategyTwo).processTypePurposeStrategy(any(), any(), eq(emptyList()), eq(true)); verify(purposeStrategySeven).processTypePurposeStrategy(any(), any(), eq(emptyList()), eq(true)); verify(purposeStrategyFour).processTypePurposeStrategy(any(), any(), eq(emptyList()), eq(true)); + verifyEachSpecialFeatureStrategyReceive(singletonList(expectedVendorPermission)); - verify(specialFeaturesStrategyOne).processSpecialFeaturesStrategy(any(), any(), anyCollection()); + verify(vendorListService).forConsent(any()); } @Test public void permissionsForShouldAllowAllWhenP1TIIsAccessAllowed() { // given given(bidderCatalog.nameByVendorId(any())).willReturn("rubicon"); - given(tcString.getPurposeOneTreatment()).willReturn(true); - - final GdprConfig gdprConfig = GdprConfig.builder() - .purposes(purposes) - .purposeOneTreatmentInterpretation(PurposeOneTreatmentInterpretation.accessAllowed) - .build(); - target = new Tcf2Service(gdprConfig, purposeStrategies, specialFeaturesStrategies, vendorListService, - bidderCatalog); + initTcf2Service(PurposeOneTreatmentInterpretation.accessAllowed); // when - target.permissionsFor(singleton(1), tcString); + final Future> result = target.permissionsFor(singleton(1), tcString); // then - final VendorPermission expectedVendorPermission1 = VendorPermission.of(1, "rubicon", - PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl1 = VendorPermissionWithGvl.of( - expectedVendorPermission1, Vendor.empty(1)); - final List standardPermissions = singletonList(expectedVendorPermissionWitGvl1); + final VendorPermission expectedVendorPermission = VendorPermission.of(1, "rubicon", restrictAll()); + assertThat(result).succeededWith(singletonList(expectedVendorPermission)); - verify(purposeStrategyOne, never()).processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); + final List permissions = singletonList(withGvl(expectedVendorPermission, 1)); + verify(purposeStrategyOne, never()) + .processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); verify(purposeStrategyOne).allow(any()); - verify(purposeStrategyTwo).processTypePurposeStrategy(any(), any(), eq(standardPermissions), eq(false)); - verify(purposeStrategySeven).processTypePurposeStrategy(any(), any(), eq(standardPermissions), eq(false)); - verify(purposeStrategyFour).processTypePurposeStrategy(any(), any(), eq(standardPermissions), eq(false)); - + verify(purposeStrategyTwo).processTypePurposeStrategy(any(), any(), eq(permissions), eq(false)); + verify(purposeStrategySeven).processTypePurposeStrategy(any(), any(), eq(permissions), eq(false)); + verify(purposeStrategyFour).processTypePurposeStrategy(any(), any(), eq(permissions), eq(false)); verify(purposeStrategyTwo).processTypePurposeStrategy(any(), any(), eq(emptyList()), eq(true)); verify(purposeStrategySeven).processTypePurposeStrategy(any(), any(), eq(emptyList()), eq(true)); verify(purposeStrategyFour).processTypePurposeStrategy(any(), any(), eq(emptyList()), eq(true)); + verifyEachSpecialFeatureStrategyReceive(singletonList(expectedVendorPermission)); - verify(specialFeaturesStrategyOne).processSpecialFeaturesStrategy(any(), any(), anyCollection()); + verify(vendorListService).forConsent(any()); } @Test public void permissionsForShouldNotAllowAllWhenP1TIsFalseAndP1TIIsAccessAllowed() { // given given(bidderCatalog.nameByVendorId(any())).willReturn("rubicon"); - given(tcString.getPurposeOneTreatment()).willReturn(false); - - final GdprConfig gdprConfig = GdprConfig.builder() - .purposes(purposes) - .purposeOneTreatmentInterpretation(PurposeOneTreatmentInterpretation.accessAllowed) - .build(); - target = new Tcf2Service(gdprConfig, purposeStrategies, specialFeaturesStrategies, vendorListService, - bidderCatalog); + initTcf2Service(PurposeOneTreatmentInterpretation.accessAllowed); // when - target.permissionsFor(singleton(1), tcString); + final Future> result = target.permissionsFor(singleton(1), tcString); // then - final VendorPermission expectedVendorPermission1 = VendorPermission.of(1, "rubicon", - PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl expectedVendorPermissionWitGvl1 = VendorPermissionWithGvl.of( - expectedVendorPermission1, Vendor.empty(1)); - final List standardPermissions = singletonList(expectedVendorPermissionWitGvl1); + final VendorPermission expectedVendorPermission = VendorPermission.of(1, "rubicon", restrictAll()); + assertThat(result).succeededWith(singletonList(expectedVendorPermission)); + final List standardPermissions = singletonList(withGvl(expectedVendorPermission, 1)); verify(purposeStrategyOne, never()).allow(any()); verifyEachPurposeStrategyReceive(standardPermissions); verifyEachPurposeStrategyReceiveWeak(emptyList()); + verifyEachSpecialFeatureStrategyReceive(singletonList(expectedVendorPermission)); + + verify(vendorListService).forConsent(any()); + } + + @Test + public void permissionsForShouldRequirePurpose4ConsentIfConfiguredAndPassEidExceptionsWhereAllowed() { + // given + given(vendorIdResolver.resolve(eq("b1"))).willReturn(1); + given(vendorIdResolver.resolve(eq("b2"))).willReturn(2); + given(vendorIdResolver.resolve(eq("b3"))).willReturn(3); + given(vendorIdResolver.resolve(eq("b4"))).willReturn(4); + + doAnswer(answer( + vendorPermission -> vendorPermission.consentNaturallyWith(ONE), + doNothing(), + vendorPermission -> vendorPermission.consentNaturallyWith(ONE), + doNothing())) + .when(purposeStrategyOne) + .processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); + + doAnswer(answer( + doNothing(), + vendorPermission -> { + vendorPermission.consentWith(TWO); + vendorPermission.getPrivacyEnforcementAction().setRemoveUserIds(false); + }, + vendorPermission -> { + vendorPermission.consentNaturallyWith(TWO); + vendorPermission.getPrivacyEnforcementAction().setRemoveUserIds(false); + }, + doNothing())) + .when(purposeStrategyTwo) + .processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); + + doAnswer(answer( + doNothing(), + doNothing(), + doNothing(), + vendorPermission -> { + vendorPermission.consentWith(FOUR); + vendorPermission.getPrivacyEnforcementAction().setRemoveUserIds(false); + })) + .when(purposeStrategyFour) + .processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); + + final Purpose purposeFour = Purpose.of( + purpose4.getEnforcePurpose(), + purpose4.getEnforceVendors(), + purpose4.getVendorExceptions(), + PurposeEid.of(null, true, singleton("eidException"))); + final AccountGdprConfig accountGdprConfig = AccountGdprConfig.builder() + .purposes(Purposes.builder().p4(purposeFour).build()) + .build(); + + // when + final Future> result = target.permissionsFor( + new TreeSet<>(asList("b1", "b2", "b3", "b4")), vendorIdResolver, tcString, accountGdprConfig); + + // then + final PrivacyEnforcementAction privacyEnforcementAction1 = restrictAll(); + final VendorPermission expectedVendorPermission1 = VendorPermission.of(1, "b1", privacyEnforcementAction1); + expectedVendorPermission1.consentNaturallyWith(ONE); + + final PrivacyEnforcementAction privacyEnforcementAction2 = restrictAll(); + final VendorPermission expectedVendorPermission2 = VendorPermission.of(2, "b2", privacyEnforcementAction2); + expectedVendorPermission2.consentWith(TWO); + + final PrivacyEnforcementAction privacyEnforcementAction3 = restrictAll(); + privacyEnforcementAction3.setEidExceptions(singleton("eidException")); + final VendorPermission expectedVendorPermission3 = VendorPermission.of(3, "b3", privacyEnforcementAction3); + expectedVendorPermission3.consentNaturallyWith(ONE); + expectedVendorPermission3.consentNaturallyWith(TWO); + + final PrivacyEnforcementAction privacyEnforcementAction4 = restrictAll(); + privacyEnforcementAction4.setRemoveUserIds(false); + final VendorPermission expectedVendorPermission4 = VendorPermission.of(4, "b4", privacyEnforcementAction4); + expectedVendorPermission4.consentWith(FOUR); + + assertThat(result).succeededWith(asList( + expectedVendorPermission1, + expectedVendorPermission2, + expectedVendorPermission3, + expectedVendorPermission4)); + } + + @Test + public void permissionsForShouldNotRequirePurpose4ConsentIfNotConfigured() { + // given + given(vendorIdResolver.resolve(eq("b1"))).willReturn(1); + given(vendorIdResolver.resolve(eq("b2"))).willReturn(2); + given(vendorIdResolver.resolve(eq("b3"))).willReturn(3); + given(vendorIdResolver.resolve(eq("b4"))).willReturn(4); + + doAnswer(answer( + vendorPermission -> vendorPermission.consentNaturallyWith(ONE), + doNothing(), + vendorPermission -> vendorPermission.consentNaturallyWith(ONE), + doNothing())) + .when(purposeStrategyOne) + .processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); + + doAnswer(answer( + doNothing(), + vendorPermission -> { + vendorPermission.consentWith(TWO); + vendorPermission.getPrivacyEnforcementAction().setRemoveUserIds(false); + }, + vendorPermission -> { + vendorPermission.consentNaturallyWith(TWO); + vendorPermission.getPrivacyEnforcementAction().setRemoveUserIds(false); + }, + doNothing())) + .when(purposeStrategyTwo) + .processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); + + doAnswer(answer( + doNothing(), + doNothing(), + doNothing(), + vendorPermission -> { + vendorPermission.consentWith(FOUR); + vendorPermission.getPrivacyEnforcementAction().setRemoveUserIds(false); + })) + .when(purposeStrategyFour) + .processTypePurposeStrategy(any(), any(), anyCollection(), anyBoolean()); + + final Purpose purposeFour = Purpose.of( + purpose4.getEnforcePurpose(), + purpose4.getEnforceVendors(), + purpose4.getVendorExceptions(), + PurposeEid.of(null, false, singleton("eidException"))); + final AccountGdprConfig accountGdprConfig = AccountGdprConfig.builder() + .purposes(Purposes.builder().p4(purposeFour).build()) + .build(); - verify(specialFeaturesStrategyOne).processSpecialFeaturesStrategy(any(), any(), anyCollection()); + // when + final Future> result = target.permissionsFor( + new TreeSet<>(asList("b1", "b2", "b3", "b4")), vendorIdResolver, tcString, accountGdprConfig); + + // then + final PrivacyEnforcementAction privacyEnforcementAction1 = restrictAll(); + final VendorPermission expectedVendorPermission1 = VendorPermission.of(1, "b1", privacyEnforcementAction1); + expectedVendorPermission1.consentNaturallyWith(ONE); + + final PrivacyEnforcementAction privacyEnforcementAction2 = restrictAll(); + privacyEnforcementAction2.setRemoveUserIds(false); + final VendorPermission expectedVendorPermission2 = VendorPermission.of(2, "b2", privacyEnforcementAction2); + expectedVendorPermission2.consentWith(TWO); + + final PrivacyEnforcementAction privacyEnforcementAction3 = restrictAll(); + privacyEnforcementAction3.setRemoveUserIds(false); + final VendorPermission expectedVendorPermission3 = VendorPermission.of(3, "b3", privacyEnforcementAction3); + expectedVendorPermission3.consentNaturallyWith(ONE); + expectedVendorPermission3.consentNaturallyWith(TWO); + + final PrivacyEnforcementAction privacyEnforcementAction4 = restrictAll(); + privacyEnforcementAction4.setRemoveUserIds(false); + final VendorPermission expectedVendorPermission4 = VendorPermission.of(4, "b4", privacyEnforcementAction4); + expectedVendorPermission4.consentWith(FOUR); + + assertThat(result).succeededWith(asList( + expectedVendorPermission1, + expectedVendorPermission2, + expectedVendorPermission3, + expectedVendorPermission4)); } - public void verifyEachPurposeStrategyReceive(List vendorPermissionWithGvls) { - verify(purposeStrategyOne).processTypePurposeStrategy(tcString, purpose1, vendorPermissionWithGvls, false); - verify(purposeStrategyTwo).processTypePurposeStrategy(tcString, purpose2, vendorPermissionWithGvls, false); - verify(purposeStrategyFour).processTypePurposeStrategy(tcString, purpose4, vendorPermissionWithGvls, false); - verify(purposeStrategySeven).processTypePurposeStrategy(tcString, purpose7, vendorPermissionWithGvls, false); + public void verifyEachPurposeStrategyReceive(List permissions) { + verify(purposeStrategyOne).processTypePurposeStrategy(tcString, purpose1, permissions, false); + verify(purposeStrategyTwo).processTypePurposeStrategy(tcString, purpose2, permissions, false); + verify(purposeStrategyFour).processTypePurposeStrategy(tcString, purpose4, permissions, false); + verify(purposeStrategySeven).processTypePurposeStrategy(tcString, purpose7, permissions, false); } - public void verifyEachPurposeStrategyReceiveWeak(List vendorPermissionWithGvls) { - verify(purposeStrategyOne).processTypePurposeStrategy(tcString, weakPurpose1, vendorPermissionWithGvls, true); - verify(purposeStrategyTwo).processTypePurposeStrategy(tcString, weakPurpose2, vendorPermissionWithGvls, true); - verify(purposeStrategyFour).processTypePurposeStrategy(tcString, weakPurpose4, vendorPermissionWithGvls, true); - verify(purposeStrategySeven).processTypePurposeStrategy(tcString, weakPurpose7, vendorPermissionWithGvls, true); + public void verifyEachPurposeStrategyReceiveWeak(List permissions) { + verify(purposeStrategyOne).processTypePurposeStrategy(tcString, weakPurpose1, permissions, true); + verify(purposeStrategyTwo).processTypePurposeStrategy(tcString, weakPurpose2, permissions, true); + verify(purposeStrategyFour).processTypePurposeStrategy(tcString, weakPurpose4, permissions, true); + verify(purposeStrategySeven).processTypePurposeStrategy(tcString, weakPurpose7, permissions, true); } public void verifyEachSpecialFeatureStrategyReceive(List vendorPermission) { verify(specialFeaturesStrategyOne).processSpecialFeaturesStrategy(tcString, specialFeature1, vendorPermission); } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Integer vendorId) { + return VendorPermissionWithGvl.of(vendorPermission, Vendor.empty(vendorId)); + } + + @SafeVarargs + private static Answer answer(Consumer... actionOnVendorPermission) { + return invocation -> { + final Collection vendorPermissions = invocation.getArgument(2); + int currentAction = 0; + + for (VendorPermissionWithGvl vendorPermission : vendorPermissions) { + actionOnVendorPermission[currentAction++].accept(vendorPermission.getVendorPermission()); + } + + return null; + }; + } + + private static Consumer doNothing() { + return ignore -> { + }; + } } diff --git a/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java index 2aa0b6423c8..450b989ab6d 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java @@ -80,7 +80,7 @@ public void setUp() { .defaultValue("1") .enabled(true) .purposes(Purposes.builder() - .p1(Purpose.of(EnforcePurpose.basic, true, emptyList())) + .p1(Purpose.of(EnforcePurpose.basic, true, emptyList(), null)) .build()) .build(); diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01StrategyTest.java new file mode 100644 index 00000000000..7203e3f559e --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01StrategyTest.java @@ -0,0 +1,382 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose01StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.ONE; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose01Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose01Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithEnforcementsWhenAllBiddersAreExcluded() { + // given + final List vendorExceptions = asList("b1", "b2", "b3", "b5", "b7"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + privacyEnforcementAction.setBlockPixelSync(false); + return privacyEnforcementAction; + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02StrategyTest.java new file mode 100644 index 00000000000..4f374616045 --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02StrategyTest.java @@ -0,0 +1,382 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose02StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.TWO; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose02Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose02Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + privacyEnforcementAction.setBlockBidderRequest(false); + return privacyEnforcementAction; + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03StrategyTest.java new file mode 100644 index 00000000000..dfc3e0e11f9 --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03StrategyTest.java @@ -0,0 +1,380 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose03StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.THREE; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose03Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose03Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + return PrivacyEnforcementAction.restrictAll(); + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04StrategyTest.java new file mode 100644 index 00000000000..0dc182e38af --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04StrategyTest.java @@ -0,0 +1,384 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose04StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.FOUR; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose04Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose04Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + privacyEnforcementAction.setRemoveUserFpd(false); + privacyEnforcementAction.setMaskDeviceInfo(false); + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05StrategyTest.java new file mode 100644 index 00000000000..d9c880ea3ad --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05StrategyTest.java @@ -0,0 +1,380 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose05StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.FIVE; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose05Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose05Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + return PrivacyEnforcementAction.restrictAll(); + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06StrategyTest.java new file mode 100644 index 00000000000..4a4f1955083 --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06StrategyTest.java @@ -0,0 +1,380 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose06StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.SIX; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose06Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose06Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + return PrivacyEnforcementAction.restrictAll(); + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07StrategyTest.java new file mode 100644 index 00000000000..fd426f9ccf6 --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07StrategyTest.java @@ -0,0 +1,382 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose07StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.SEVEN; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose07Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose07Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); + privacyEnforcementAction.setBlockAnalyticsReport(false); + return privacyEnforcementAction; + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08StrategyTest.java new file mode 100644 index 00000000000..373c9b76ca9 --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08StrategyTest.java @@ -0,0 +1,380 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose08StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.EIGHT; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose08Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose08Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + return PrivacyEnforcementAction.restrictAll(); + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09StrategyTest.java new file mode 100644 index 00000000000..8af82408271 --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09StrategyTest.java @@ -0,0 +1,380 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose09StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.NINE; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose09Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose09Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + return PrivacyEnforcementAction.restrictAll(); + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10StrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10StrategyTest.java new file mode 100644 index 00000000000..1aca90d1078 --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10StrategyTest.java @@ -0,0 +1,380 @@ +package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; + +import com.iabtcf.decoder.TCString; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; +import org.prebid.server.privacy.gdpr.model.VendorPermission; +import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; +import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; +import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; +import org.prebid.server.settings.model.EnforcePurpose; +import org.prebid.server.settings.model.Purpose; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class Purpose10StrategyTest { + + private static final PurposeCode PURPOSE_CODE = PurposeCode.TEN; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; + + @Mock + private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; + + @Mock + private NoEnforcePurposeStrategy noEnforcePurposeStrategy; + + private Purpose10Strategy target; + + @Mock + private TCString tcString; + + @Before + public void setUp() { + target = new Purpose10Strategy( + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); + } + + @Test + public void allowShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allow(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(vendorPermissionResult(1, "b1")); + } + + @Test + public void allowNaturallyShouldReturnExpectedValue() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + + // when + target.allowNaturally(vendorPermission); + + // then + assertThat(vendorPermission).isEqualTo(naturalVendorPermissionResult(1, "b1")); + } + + @Test + public void getPurposeIdShouldReturnExpectedValue() { + // when and then + assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + final List vendorExceptions = asList("b1", "b3"); + final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions, null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, null)); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b1")); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll())); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), + singletonList(vendorPermissionWitGvl2), + false); + } + + @Test + public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(vendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(vendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2", "b3", "b5", "b7"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willAnswer(invocation -> Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(allVendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), vendorPermissionsWithGvl, true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowNaturally() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.full, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission3)) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, false); + + // then + assertThat(vendorPermission1).isEqualTo(naturalVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(naturalVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(fullEnforcePurposeStrategy, times(2)) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + @Test + public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { + // given + final Purpose purpose = Purpose.of(EnforcePurpose.no, null, asList("b1", "b2"), null); + final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); + final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl3 = withGvl(vendorPermission3, Vendor.empty(3)); + final List vendorPermissionsWithGvl = asList( + vendorPermissionWitGvl1, + vendorPermissionWitGvl2, + vendorPermissionWitGvl3); + + given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2, vendorPermission3)); + given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) + .willReturn(Stream.of(vendorPermission1, vendorPermission2)); + + // when + target.processTypePurposeStrategy(tcString, purpose, vendorPermissionsWithGvl, true); + + // then + assertThat(vendorPermission1).isEqualTo(allVendorPermissionResult(1, "b1")); + assertThat(vendorPermission2).isEqualTo(allVendorPermissionResult(2, "b2")); + assertThat(vendorPermission3).isEqualTo(vendorPermissionResult(3, "b3")); + + verify(noEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + verify(basicEnforcePurposeStrategy) + .allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl3), + asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2), + true); + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } + + private static VendorPermission vendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurpose()); + vendorPermission.consentWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission naturalVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowNatural()); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static VendorPermission allVendorPermissionResult(Integer vendorId, String bidderName) { + final VendorPermission vendorPermission = VendorPermission.of(vendorId, bidderName, allowPurposeAndNaturally()); + vendorPermission.consentWith(PURPOSE_CODE); + vendorPermission.consentNaturallyWith(PURPOSE_CODE); + return vendorPermission; + } + + private static PrivacyEnforcementAction allowPurposeAndNaturally() { + return allowNatural(allowPurpose()); + } + + private static PrivacyEnforcementAction allowPurpose() { + return PrivacyEnforcementAction.restrictAll(); + } + + private static PrivacyEnforcementAction allowNatural() { + return allowNatural(PrivacyEnforcementAction.restrictAll()); + } + + private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { + privacyEnforcementAction.setRemoveUserIds(false); + return privacyEnforcementAction; + } +} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategyTest.java deleted file mode 100644 index c440f8aa6fb..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeEightStrategyTest.java +++ /dev/null @@ -1,342 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeEightStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.EIGHT; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeEightStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeEightStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowNatural()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowNatural(allowPurpose()); - } - - private static PrivacyEnforcementAction allowPurpose() { - return PrivacyEnforcementAction.restrictAll(); - } - - private static PrivacyEnforcementAction allowNatural() { - return allowNatural(PrivacyEnforcementAction.restrictAll()); - } - - private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategyTest.java deleted file mode 100644 index ca028362b86..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFiveStrategyTest.java +++ /dev/null @@ -1,342 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeFiveStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.FIVE; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeFiveStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeFiveStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowNatural()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowNatural(allowPurpose()); - } - - private static PrivacyEnforcementAction allowPurpose() { - return PrivacyEnforcementAction.restrictAll(); - } - - private static PrivacyEnforcementAction allowNatural() { - return allowNatural(PrivacyEnforcementAction.restrictAll()); - } - - private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategyTest.java deleted file mode 100644 index f56dfea0bd5..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeFourStrategyTest.java +++ /dev/null @@ -1,344 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeFourStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.FOUR; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeFourStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeFourStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowNatural()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), - singletonList(vendorPermissionWitGvl2), false); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithEnforcementsWhenAllBiddersAreExcluded() { - // given - final List vendorExceptions = Arrays.asList("b1", "b2", "b3", "b5", "b7"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowNatural(allowPurpose()); - } - - private static PrivacyEnforcementAction allowPurpose() { - return PrivacyEnforcementAction.restrictAll(); - } - - private static PrivacyEnforcementAction allowNatural() { - return allowNatural(PrivacyEnforcementAction.restrictAll()); - } - - private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategyTest.java deleted file mode 100644 index 73813c86982..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeNineStrategyTest.java +++ /dev/null @@ -1,341 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeNineStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.NINE; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeNineStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeNineStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowNatural()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowNatural(allowPurpose()); - } - - private static PrivacyEnforcementAction allowPurpose() { - return PrivacyEnforcementAction.restrictAll(); - } - - private static PrivacyEnforcementAction allowNatural() { - return allowNatural(PrivacyEnforcementAction.restrictAll()); - } - - private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategyTest.java deleted file mode 100644 index c519de62f25..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeOneStrategyTest.java +++ /dev/null @@ -1,338 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeOneStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.ONE; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeOneStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeOneStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction) - .usingRecursiveComparison() - .isEqualTo(PrivacyEnforcementAction.restrictAll()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithEnforcementsWhenAllBiddersAreExcluded() { - // given - final List vendorExceptions = Arrays.asList("b1", "b2", "b3", "b5", "b7"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator() - .isEqualTo(Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setBlockPixelSync(false); - return privacyEnforcementAction; - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowPurpose(); - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategyTest.java deleted file mode 100644 index 2e5e55b86d2..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSevenStrategyTest.java +++ /dev/null @@ -1,346 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeSevenStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.SEVEN; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeSevenStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeSevenStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowNatural()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithEnforcementsWhenAllBiddersAreExcluded() { - // given - final List vendorExceptions = Arrays.asList("b1", "b2", "b3", "b5", "b7"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowNatural(allowPurpose()); - } - - private static PrivacyEnforcementAction allowPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setBlockAnalyticsReport(false); - return privacyEnforcementAction; - } - - private static PrivacyEnforcementAction allowNatural() { - return allowNatural(PrivacyEnforcementAction.restrictAll()); - } - - private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategyTest.java deleted file mode 100644 index f3a90234fc6..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeSixStrategyTest.java +++ /dev/null @@ -1,342 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeSixStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.SIX; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeSixStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeSixStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowNatural()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowNatural(allowPurpose()); - } - - private static PrivacyEnforcementAction allowPurpose() { - return PrivacyEnforcementAction.restrictAll(); - } - - private static PrivacyEnforcementAction allowNatural() { - return allowNatural(PrivacyEnforcementAction.restrictAll()); - } - - private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategyTest.java deleted file mode 100644 index 43ff7f5bba3..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTenStrategyTest.java +++ /dev/null @@ -1,341 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeTenStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.TEN; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeTenStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeTenStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowNatural()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowNatural(allowPurpose()); - } - - private static PrivacyEnforcementAction allowPurpose() { - return PrivacyEnforcementAction.restrictAll(); - } - - private static PrivacyEnforcementAction allowNatural() { - return allowNatural(PrivacyEnforcementAction.restrictAll()); - } - - private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategyTest.java deleted file mode 100644 index 2aa8f2d4306..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeThreeStrategyTest.java +++ /dev/null @@ -1,342 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeThreeStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.THREE; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeThreeStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeThreeStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowNatural()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowNatural(allowPurpose()); - } - - private static PrivacyEnforcementAction allowPurpose() { - return PrivacyEnforcementAction.restrictAll(); - } - - private static PrivacyEnforcementAction allowNatural() { - return allowNatural(PrivacyEnforcementAction.restrictAll()); - } - - private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategyTest.java deleted file mode 100644 index 8b7f129e948..00000000000 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeTwoStrategyTest.java +++ /dev/null @@ -1,344 +0,0 @@ -package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; - -import com.iabtcf.decoder.TCString; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; -import org.prebid.server.privacy.gdpr.model.VendorPermission; -import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.NoEnforcePurposeStrategy; -import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; -import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import org.prebid.server.settings.model.EnforcePurpose; -import org.prebid.server.settings.model.Purpose; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class PurposeTwoStrategyTest { - - private static final PurposeCode PURPOSE_CODE = - PurposeCode.TWO; - - @Rule - public final MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock - private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; - - @Mock - private BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; - - @Mock - private NoEnforcePurposeStrategy noEnforcePurposeStrategy; - - private PurposeTwoStrategy target; - - @Mock - private TCString tcString; - - @Before - public void setUp() { - target = new PurposeTwoStrategy(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, - noEnforcePurposeStrategy); - } - - @Test - public void allowShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allow(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowPurpose()); - } - - @Test - public void allowNaturallyShouldReturnExpectedValue() { - // given - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - - // when - target.allowNaturally(privacyEnforcementAction); - - // then - assertThat(privacyEnforcementAction).usingRecursiveComparison().isEqualTo(allowNatural()); - } - - @Test - public void getPurposeIdShouldReturnExpectedValue() { - // when and then - assertThat(target.getPurpose()).isEqualTo(PURPOSE_CODE); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToNoType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.no, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToBaseType() { - // given - final List vendorExceptions = Arrays.asList("b1", "b3"); - final Purpose purpose = Purpose.of(EnforcePurpose.basic, false, vendorExceptions); - final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(Arrays.asList(vendorPermission1, vendorPermission2)); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, null, - PrivacyEnforcementAction.restrictAll()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(vendorPermissionWitGvl1, vendorPermissionWitGvl3), singletonList(vendorPermissionWitGvl2), - false); - } - - @Test - public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.basic, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurpose()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurpose()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldPassEmptyListWithFullEnforcementsWhenAllBiddersAreExcluded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2", "b3", "b5", "b7")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurposeAndNaturally()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, emptyList(), - vendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyVendorExceptions() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.full, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(fullEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, false); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(fullEnforcePurposeStrategy, times(2)).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - @Test - public void processTypePurposeStrategyShouldAllowPurposeAndNaturallyWhenVendorPermissionsReturnedForDowngraded() { - // given - final Purpose purpose = Purpose.of(EnforcePurpose.no, null, Arrays.asList("b1", "b2")); - final VendorPermission vendorPermission1 = VendorPermission.of(1, "b1", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission2 = VendorPermission.of(2, "b2", PrivacyEnforcementAction.restrictAll()); - final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl vendorPermissionWitGvl3 = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, - vendorPermission3); - - final List excludedVendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); - - given(noEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(vendorPermissions); - given(basicEnforcePurposeStrategy.allowedByTypeStrategy(any(), any(), any(), any(), anyBoolean())) - .willReturn(excludedVendorPermissions); - - final List vendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2, vendorPermissionWitGvl3); - - // when - final Collection result = target.processTypePurposeStrategy(tcString, purpose, - vendorPermissionsWithGvl, true); - - // then - final List excludedVendorPermissionsWithGvl = Arrays.asList(vendorPermissionWitGvl1, - vendorPermissionWitGvl2); - - final VendorPermission vendorPermission1Changed = VendorPermission.of(1, "b1", allowPurposeAndNaturally()); - final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b2", allowPurposeAndNaturally()); - final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowPurpose()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo( - Arrays.asList(vendorPermission1Changed, vendorPermission2Changed, vendorPermission3Changed)); - - verify(noEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - verify(basicEnforcePurposeStrategy).allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl3), excludedVendorPermissionsWithGvl, true); - } - - private static PrivacyEnforcementAction allowPurposeAndNaturally() { - return allowNatural(allowPurpose()); - } - - private static PrivacyEnforcementAction allowPurpose() { - final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); - privacyEnforcementAction.setBlockBidderRequest(false); - return privacyEnforcementAction; - } - - private static PrivacyEnforcementAction allowNatural() { - return allowNatural(PrivacyEnforcementAction.restrictAll()); - } - - private static PrivacyEnforcementAction allowNatural(PrivacyEnforcementAction privacyEnforcementAction) { - privacyEnforcementAction.setRemoveUserIds(false); - privacyEnforcementAction.setMaskDeviceInfo(false); - return privacyEnforcementAction; - } -} diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/BasicEnforcePurposeStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/BasicEnforcePurposeStrategyTest.java index 0d1aba302bf..5d8fedfc2e8 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/BasicEnforcePurposeStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/BasicEnforcePurposeStrategyTest.java @@ -14,10 +14,10 @@ import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import java.util.Arrays; -import java.util.Collection; import java.util.List; +import java.util.stream.Stream; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; @@ -64,13 +64,12 @@ public void setUp() { public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -80,13 +79,12 @@ public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndV public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -96,15 +94,14 @@ public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndV public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -114,15 +111,14 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAnd public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -132,15 +128,14 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAnd public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -150,15 +145,14 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedA public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -168,16 +162,15 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedA public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeLIAndPurposeIsAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesConsent.contains(anyInt())).willReturn(true); given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -187,16 +180,15 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeLIAndPurpos public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeLIAndPurposeIsAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesConsent.contains(anyInt())).willReturn(true); given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -207,19 +199,18 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeLIAndVendor // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final List vendorPermissionWithGvls = Arrays.asList(vendorPermissionWitGvl1, + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final List vendorPermissionWithGvls = asList( + vendorPermissionWitGvl1, vendorPermissionWitGvl2); given(allowedVendors.contains(anyInt())).willReturn(true); given(purposesLI.contains(PURPOSE_CODE.code())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -230,19 +221,18 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeAndVendorLI // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final List vendorPermissionWithGvls = Arrays.asList(vendorPermissionWitGvl1, + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final List vendorPermissionWithGvls = asList( + vendorPermissionWitGvl1, vendorPermissionWitGvl2); given(allowedVendorsLI.contains(anyInt())).willReturn(true); given(purposesConsent.contains(PURPOSE_CODE.code())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result) @@ -255,16 +245,22 @@ public void allowedByTypeStrategyShouldReturnExcludedVendors() { // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - singleton(vendorPermissionWitGvl1), singleton(vendorPermissionWitGvl2), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singleton(vendorPermissionWitGvl1), + singleton(vendorPermissionWitGvl2), + true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission2); } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } } diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/FullEnforcePurposeStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/FullEnforcePurposeStrategyTest.java index 42a6b78086f..73d875542cf 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/FullEnforcePurposeStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/FullEnforcePurposeStrategyTest.java @@ -2,12 +2,14 @@ import com.iabtcf.decoder.TCString; import com.iabtcf.utils.IntIterable; +import com.iabtcf.utils.IntIterator; import com.iabtcf.v2.PublisherRestriction; import com.iabtcf.v2.RestrictionType; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; @@ -16,11 +18,12 @@ import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import java.util.Arrays; -import java.util.Collection; import java.util.EnumSet; +import java.util.Iterator; import java.util.List; +import java.util.stream.Stream; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; @@ -28,7 +31,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -51,7 +54,7 @@ public class FullEnforcePurposeStrategyTest { private IntIterable purposesConsent; @Mock private IntIterable purposesLI; - @Mock + @Spy private IntIterable vendorIds; @Mock @@ -74,7 +77,8 @@ public void setUp() { given(allowedVendorsLI.contains(anyInt())).willReturn(false); given(purposesConsent.contains(anyInt())).willReturn(false); given(purposesLI.contains(anyInt())).willReturn(false); - given(vendorIds.contains(anyInt())).willReturn(false); + + given(vendorIds.intIterator()).willReturn(intIterator(1)); target = new FullEnforcePurposeStrategy(); } @@ -82,39 +86,36 @@ public void setUp() { @Test public void shouldReturnOnlyExcludedAllowedWhenMultiplePublisherRestrictionsProvided() { // given - final IntIterable requireConsentIterable = mock(IntIterable.class); - final PublisherRestriction publisherRestriction1 = new PublisherRestriction(PURPOSE_CODE.code(), - RestrictionType.REQUIRE_CONSENT, requireConsentIterable); - given(requireConsentIterable.spliterator()).willReturn(singletonList(1).spliterator()); + final IntIterable requireConsentIterable = spy(IntIterable.class); + final PublisherRestriction publisherRestriction1 = new PublisherRestriction( + PURPOSE_CODE.code(), RestrictionType.REQUIRE_CONSENT, requireConsentIterable); + given(requireConsentIterable.intIterator()).willReturn(intIterator(1)); - final IntIterable notAllowedIterable = mock(IntIterable.class); - final PublisherRestriction publisherRestriction2 = new PublisherRestriction(PURPOSE_CODE.code(), - RestrictionType.NOT_ALLOWED, notAllowedIterable); - given(notAllowedIterable.spliterator()).willReturn(Arrays.asList(4, 2).spliterator()); + final IntIterable notAllowedIterable = spy(IntIterable.class); + final PublisherRestriction publisherRestriction2 = new PublisherRestriction( + PURPOSE_CODE.code(), RestrictionType.NOT_ALLOWED, notAllowedIterable); + given(notAllowedIterable.intIterator()).willReturn(intIterator(4, 2)); - given(tcString.getPublisherRestrictions()).willReturn( - Arrays.asList(publisherRestriction1, publisherRestriction2)); + given(tcString.getPublisherRestrictions()).willReturn(asList(publisherRestriction1, publisherRestriction2)); final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission4 = VendorPermission.of(4, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission5 = VendorPermission.of(5, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl requireConsentPermission = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl notAllowedPermission = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final VendorPermissionWithGvl excludedNotMentionedPermission = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.empty(3)); - final VendorPermissionWithGvl excludedNotAllowedPermission = VendorPermissionWithGvl.of(vendorPermission4, - Vendor.empty(4)); - final VendorPermissionWithGvl notMentionedPermission = VendorPermissionWithGvl.of(vendorPermission5, - Vendor.empty(5)); + final VendorPermissionWithGvl requireConsentPermission = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl notAllowedPermission = withGvl(vendorPermission2, Vendor.empty(2)); + final VendorPermissionWithGvl excludedNotMentionedPermission = withGvl(vendorPermission3, Vendor.empty(3)); + final VendorPermissionWithGvl excludedNotAllowedPermission = withGvl(vendorPermission4, Vendor.empty(4)); + final VendorPermissionWithGvl notMentionedPermission = withGvl(vendorPermission5, Vendor.empty(5)); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(requireConsentPermission, notAllowedPermission, notMentionedPermission), - Arrays.asList(excludedNotMentionedPermission, excludedNotAllowedPermission), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(requireConsentPermission, notAllowedPermission, notMentionedPermission), + asList(excludedNotMentionedPermission, excludedNotAllowedPermission), + true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission3); @@ -123,44 +124,46 @@ public void shouldReturnOnlyExcludedAllowedWhenMultiplePublisherRestrictionsProv @Test public void shouldReturnExpectedWhenMultiplePublisherRestrictionsProvided() { // given - final IntIterable requireConsentIterable = mock(IntIterable.class); - final PublisherRestriction publisherRestriction1 = new PublisherRestriction(PURPOSE_CODE.code(), - RestrictionType.REQUIRE_CONSENT, requireConsentIterable); - given(requireConsentIterable.spliterator()).willReturn(singletonList(1).spliterator()); + final IntIterable requireConsentIterable = spy(IntIterable.class); + final PublisherRestriction publisherRestriction1 = new PublisherRestriction( + PURPOSE_CODE.code(), RestrictionType.REQUIRE_CONSENT, requireConsentIterable); + given(requireConsentIterable.intIterator()).willReturn(intIterator(1)); given(requireConsentIterable.contains(eq(1))).willReturn(true); - final IntIterable notAllowedIterable = mock(IntIterable.class); - final PublisherRestriction publisherRestriction2 = new PublisherRestriction(PURPOSE_CODE.code(), - RestrictionType.NOT_ALLOWED, notAllowedIterable); - given(notAllowedIterable.spliterator()).willReturn(Arrays.asList(4, 2).spliterator()); + final IntIterable notAllowedIterable = spy(IntIterable.class); + final PublisherRestriction publisherRestriction2 = new PublisherRestriction( + PURPOSE_CODE.code(), RestrictionType.NOT_ALLOWED, notAllowedIterable); + given(notAllowedIterable.intIterator()).willReturn(intIterator(4, 2)); given(notAllowedIterable.contains(eq(4))).willReturn(true); given(notAllowedIterable.contains(eq(2))).willReturn(true); - given(tcString.getPublisherRestrictions()).willReturn( - Arrays.asList(publisherRestriction1, publisherRestriction2)); + given(tcString.getPublisherRestrictions()).willReturn(asList(publisherRestriction1, publisherRestriction2)); final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission4 = VendorPermission.of(4, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission5 = VendorPermission.of(5, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl requireConsentPermission = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.builder().id(1).purposes(EnumSet.of(PURPOSE_CODE)).build()); - final VendorPermissionWithGvl notAllowedPermission = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.builder().id(2).purposes(EnumSet.of(PURPOSE_CODE)).build()); - final VendorPermissionWithGvl excludedNotMentionedPermission = VendorPermissionWithGvl.of(vendorPermission3, - Vendor.builder().id(3).purposes(EnumSet.of(PURPOSE_CODE)).build()); - final VendorPermissionWithGvl excludedNotAllowedPermission = VendorPermissionWithGvl.of(vendorPermission4, - Vendor.builder().id(4).purposes(EnumSet.of(PURPOSE_CODE)).build()); - final VendorPermissionWithGvl notMentionedPermission = VendorPermissionWithGvl.of(vendorPermission5, - Vendor.builder().id(5).purposes(EnumSet.of(PURPOSE_CODE)).build()); + final VendorPermissionWithGvl requireConsentPermission = withGvl( + vendorPermission1, Vendor.builder().id(1).purposes(EnumSet.of(PURPOSE_CODE)).build()); + final VendorPermissionWithGvl notAllowedPermission = withGvl( + vendorPermission2, Vendor.builder().id(2).purposes(EnumSet.of(PURPOSE_CODE)).build()); + final VendorPermissionWithGvl excludedNotMentionedPermission = withGvl( + vendorPermission3, Vendor.builder().id(3).purposes(EnumSet.of(PURPOSE_CODE)).build()); + final VendorPermissionWithGvl excludedNotAllowedPermission = withGvl( + vendorPermission4, Vendor.builder().id(4).purposes(EnumSet.of(PURPOSE_CODE)).build()); + final VendorPermissionWithGvl notMentionedPermission = withGvl( + vendorPermission5, Vendor.builder().id(5).purposes(EnumSet.of(PURPOSE_CODE)).build()); given(purposesConsent.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - Arrays.asList(requireConsentPermission, notAllowedPermission, notMentionedPermission), - Arrays.asList(excludedNotMentionedPermission, excludedNotAllowedPermission), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + asList(requireConsentPermission, notAllowedPermission, notMentionedPermission), + asList(excludedNotMentionedPermission, excludedNotAllowedPermission), + false); // then assertThat(result) @@ -179,14 +182,14 @@ public void shouldAllowWhenInGvlPurposeAndPurposeConsentAllowed() { .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesConsent.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -203,7 +206,7 @@ public void shouldAllowWhenInGvlPurposeAndPurposeConsentAllowedAndRequireConsent .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -211,8 +214,8 @@ public void shouldAllowWhenInGvlPurposeAndPurposeConsentAllowedAndRequireConsent given(purposesConsent.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -229,7 +232,7 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAllowedAndRequireLI() { .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -237,8 +240,8 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAllowedAndRequireLI() { given(purposesConsent.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -255,14 +258,14 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeLIAllowed() { .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -280,15 +283,15 @@ public void shouldAllowWhenInGvlPurposeAndPurposeConsentAllowedAndVendorConsentA .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesConsent.contains(anyInt())).willReturn(true); given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -306,15 +309,15 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAllowedAndVendorLIAllowe .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesConsent.contains(anyInt())).willReturn(true); given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -334,14 +337,14 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeLI() { .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -358,7 +361,7 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeLIAndRequireLI() { .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -366,8 +369,8 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeLIAndRequireLI() { given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -384,7 +387,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAndRequireConsent() { .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); final RestrictionType requireConsent = RestrictionType.REQUIRE_CONSENT; @@ -393,8 +396,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAndRequireConsent() { given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -411,15 +414,15 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeLIAndVendorLIAllowedAndEnforc .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesLI.contains(anyInt())).willReturn(true); given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -437,7 +440,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAllowedAndVendorConsen .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesLI.contains(anyInt())).willReturn(true); @@ -445,8 +448,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAllowedAndVendorConsen given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -468,7 +471,7 @@ public void shouldAllowWhenInGvlPurposeAndPurposeConsentAllowedAndFlexibleAndReq .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -476,8 +479,8 @@ public void shouldAllowWhenInGvlPurposeAndPurposeConsentAllowedAndFlexibleAndReq given(purposesConsent.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -494,7 +497,7 @@ public void shouldAllowWhenInGvlPurposeAndPurposeConsentAndVendorConsentAndEnfor .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -503,8 +506,8 @@ public void shouldAllowWhenInGvlPurposeAndPurposeConsentAndVendorConsentAndEnfor given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -522,7 +525,7 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeLIAllowedAndFlexibleAndRequireC .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -530,8 +533,8 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeLIAllowedAndFlexibleAndRequireC given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -548,7 +551,7 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeLIAndVendorLIAllowedAndEnforced .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -557,8 +560,8 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeLIAndVendorLIAllowedAndEnforced given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -576,7 +579,7 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAndVendorLIAndEnforcedAn .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -585,8 +588,8 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAndVendorLIAndEnforcedAn given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -603,7 +606,7 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeLIAndVendorConsentAndEnforcedAn .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -612,8 +615,8 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeLIAndVendorConsentAndEnforcedAn given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -630,7 +633,7 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAllowedAndFlexibleAndReq .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -638,8 +641,8 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAllowedAndFlexibleAndReq given(purposesConsent.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -656,7 +659,7 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAndVendorConsentAndEnfor .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -665,8 +668,8 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAndVendorConsentAndEnfor given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -684,7 +687,7 @@ public void shouldAllowWhenInGvlPurposeAndPurposeLIAllowedAndFlexibleAndRequireL .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -692,8 +695,8 @@ public void shouldAllowWhenInGvlPurposeAndPurposeLIAllowedAndFlexibleAndRequireL given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -710,7 +713,7 @@ public void shouldAllowWhenInGvlPurposeAndPurposeLIAndVendorLIAllowedAndEnforced .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -719,8 +722,8 @@ public void shouldAllowWhenInGvlPurposeAndPurposeLIAndVendorLIAllowedAndEnforced given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -738,7 +741,7 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAndVendorLIAllowedAndEnf .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -747,8 +750,8 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeConsentAndVendorLIAllowedAndEnf given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -766,7 +769,7 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeLIAndVendorConsentAllowedAndEnf .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -775,8 +778,8 @@ public void shouldEmptyWhenInGvlPurposeAndPurposeLIAndVendorConsentAllowedAndEnf given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -798,7 +801,7 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeConsentAllowedAndFlexibleAndR .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -806,8 +809,8 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeConsentAllowedAndFlexibleAndR given(purposesConsent.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -824,7 +827,7 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeAndVendorConsentAndEnforcedAn .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -833,8 +836,8 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeAndVendorConsentAndEnforcedAn given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -852,7 +855,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAllowedAndFlexibleAndRequir .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -860,8 +863,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAllowedAndFlexibleAndRequir given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -878,7 +881,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAndVendorLIAllowedAndEnforc .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -887,8 +890,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAndVendorLIAllowedAndEnforc given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -906,7 +909,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAndVendorLIAndEnforced .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -915,8 +918,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAndVendorLIAndEnforced given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -934,7 +937,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAndVendorConsentAndEnforced .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_CONSENT); @@ -943,8 +946,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAndVendorConsentAndEnforced given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -964,7 +967,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAllowedAndFlexibleAndR .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -972,8 +975,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAllowedAndFlexibleAndR given(purposesConsent.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -990,7 +993,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAndVendorConsentAndEnf .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -999,8 +1002,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAndVendorConsentAndEnf given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -1018,7 +1021,7 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeLIAllowedAndFlexibleAndRequir .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -1026,8 +1029,8 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeLIAllowedAndFlexibleAndRequir given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -1044,7 +1047,7 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeLIAndVendorLIAllowedAndEnforc .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -1053,8 +1056,8 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeLIAndVendorLIAllowedAndEnforc given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -1072,7 +1075,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAndVendorLIAllowedAndE .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -1081,8 +1084,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeConsentAndVendorLIAllowedAndE given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -1100,7 +1103,7 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAndVendorConsentAllowedAndE .build(); final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, vendorGvl); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); setRestriction(RestrictionType.REQUIRE_LEGITIMATE_INTEREST); @@ -1109,8 +1112,8 @@ public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAndVendorConsentAllowedAndE given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -1124,14 +1127,16 @@ public void shouldReturnExcludedVendors() { // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - singleton(vendorPermissionWitGvl1), singleton(vendorPermissionWitGvl2), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singleton(vendorPermissionWitGvl1), + singleton(vendorPermissionWitGvl2), + true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission2); @@ -1139,6 +1144,26 @@ public void shouldReturnExcludedVendors() { private void setRestriction(RestrictionType requireConsent) { given(publisherRestriction.getRestrictionType()).willReturn(requireConsent); - given(vendorIds.contains(anyInt())).willReturn(true); + } + + private static IntIterator intIterator(Integer... ints) { + return new IntIterator() { + + private final Iterator iterator = asList(ints).iterator(); + + @Override + public int nextInt() { + return iterator.next(); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + }; + } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); } } diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/NoEnforcePurposeStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/NoEnforcePurposeStrategyTest.java index 5f9a22fe70c..ed38eb6c44d 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/NoEnforcePurposeStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/NoEnforcePurposeStrategyTest.java @@ -14,10 +14,10 @@ import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import java.util.Arrays; -import java.util.Collection; import java.util.List; +import java.util.stream.Stream; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -56,13 +56,12 @@ public void setUp() { public void allowedByTypeStrategyShouldReturnExpectedListWhenVendorIsNotAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -72,13 +71,12 @@ public void allowedByTypeStrategyShouldReturnExpectedListWhenVendorIsNotAllowedA public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -88,15 +86,14 @@ public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndV public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -106,15 +103,14 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAnd public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -124,15 +120,14 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAnd public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPurposeWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPurposeWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPurposeWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -142,15 +137,14 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedA public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPurposeWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPurposeWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPurposeWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -161,19 +155,17 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedFor // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(1)); - final List vendorPurposeWithGvls = Arrays.asList(vendorPermissionWitGvl1, + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(1)); + final List vendorPurposeWithGvls = asList(vendorPermissionWitGvl1, vendorPermissionWitGvl2); given(allowedVendors.contains(eq(1))).willReturn(true); given(allowedVendors.contains(eq(2))).willReturn(false); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPurposeWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPurposeWithGvls, emptyList(), false); // then assertThat(result) @@ -186,18 +178,17 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedFor // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(1)); - final List vendorPurposeWithGvls = Arrays.asList(vendorPermissionWitGvl1, + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(1)); + final List vendorPurposeWithGvls = asList( + vendorPermissionWitGvl1, vendorPermissionWitGvl2); given(allowedVendors.contains(eq(1))).willReturn(true); given(allowedVendors.contains(eq(2))).willReturn(false); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPurposeWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPurposeWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission1); @@ -208,19 +199,18 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeAndVendorLI // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(1)); - final List vendorPurposeWithGvls = Arrays.asList(vendorPermissionWitGvl1, + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(1)); + final List vendorPurposeWithGvls = asList( + vendorPermissionWitGvl1, vendorPermissionWitGvl2); given(allowedVendorsLI.contains(eq(1))).willReturn(true); given(allowedVendorsLI.contains(eq(2))).willReturn(false); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPurposeWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPurposeWithGvls, emptyList(), false); // then assertThat(result) @@ -233,19 +223,18 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeAndVendorLI // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(1)); - final List vendorPurposeWithGvls = Arrays.asList(vendorPermissionWitGvl1, + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(1)); + final List vendorPurposeWithGvls = asList( + vendorPermissionWitGvl1, vendorPermissionWitGvl2); given(allowedVendorsLI.contains(eq(1))).willReturn(true); given(allowedVendorsLI.contains(eq(2))).willReturn(false); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPurposeWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPurposeWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission1); @@ -256,16 +245,22 @@ public void allowedByTypeStrategyShouldReturnExcludedVendors() { // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(1)); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - singletonList(vendorPermissionWitGvl1), singletonList(vendorPermissionWitGvl2), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission2); } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } } diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/PurposeTwoBasicEnforcePurposeStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/PurposeTwoBasicEnforcePurposeStrategyTest.java index c2904916f89..5b38ea212d4 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/PurposeTwoBasicEnforcePurposeStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/PurposeTwoBasicEnforcePurposeStrategyTest.java @@ -14,10 +14,10 @@ import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode; import org.prebid.server.privacy.gdpr.vendorlist.proto.Vendor; -import java.util.Arrays; -import java.util.Collection; import java.util.List; +import java.util.stream.Stream; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; @@ -64,13 +64,12 @@ public void setUp() { public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -80,13 +79,12 @@ public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndV public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -96,15 +94,14 @@ public void allowedByTypeStrategyShouldReturnEmptyListWhenVendorIsNotAllowedAndV public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -114,15 +111,14 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAnd public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendors.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -132,15 +128,14 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorIsAllowedAnd public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).isEmpty(); @@ -150,15 +145,14 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedA public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(allowedVendorsLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).isEmpty(); @@ -168,16 +162,15 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedA public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeLIAndPurposeIsAllowedAndVendorIsNotEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesConsent.contains(anyInt())).willReturn(true); given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -187,16 +180,15 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeLIAndPurpos public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeLIAndPurposeIsAllowedAndVendorEnforced() { // given final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl = VendorPermissionWithGvl.of(vendorPermission, - Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); final List vendorPermissionWithGvls = singletonList(vendorPermissionWitGvl); given(purposesConsent.contains(anyInt())).willReturn(true); given(purposesLI.contains(anyInt())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); @@ -207,19 +199,22 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeLIAndVendor // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final List vendorPermissionWithGvls = Arrays.asList(vendorPermissionWitGvl1, + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final List vendorPermissionWithGvls = asList( + vendorPermissionWitGvl1, vendorPermissionWitGvl2); given(allowedVendors.contains(anyInt())).willReturn(true); given(purposesLI.contains(PURPOSE_CODE.code())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + vendorPermissionWithGvls, + emptyList(), + false); // then assertThat(result) @@ -232,19 +227,18 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeAndVendorLI // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); - final List vendorPermissionWithGvls = Arrays.asList(vendorPermissionWitGvl1, + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); + final List vendorPermissionWithGvls = asList( + vendorPermissionWitGvl1, vendorPermissionWitGvl2); given(allowedVendorsLI.contains(anyInt())).willReturn(true); given(purposesConsent.contains(PURPOSE_CODE.code())).willReturn(true); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - vendorPermissionWithGvls, emptyList(), false); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, tcString, vendorPermissionWithGvls, emptyList(), false); // then assertThat(result) @@ -257,16 +251,22 @@ public void allowedByTypeStrategyShouldReturnExcludedVendors() { // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, null, PrivacyEnforcementAction.restrictAll()); - final VendorPermissionWithGvl vendorPermissionWitGvl1 = VendorPermissionWithGvl.of(vendorPermission1, - Vendor.empty(1)); - final VendorPermissionWithGvl vendorPermissionWitGvl2 = VendorPermissionWithGvl.of(vendorPermission2, - Vendor.empty(2)); + final VendorPermissionWithGvl vendorPermissionWitGvl1 = withGvl(vendorPermission1, Vendor.empty(1)); + final VendorPermissionWithGvl vendorPermissionWitGvl2 = withGvl(vendorPermission2, Vendor.empty(2)); // when - final Collection result = target.allowedByTypeStrategy(PURPOSE_CODE, tcString, - singleton(vendorPermissionWitGvl1), singleton(vendorPermissionWitGvl2), true); + final Stream result = target.allowedByTypeStrategy( + PURPOSE_CODE, + tcString, + singleton(vendorPermissionWitGvl1), + singleton(vendorPermissionWitGvl2), + true); // then assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission2); } + + private static VendorPermissionWithGvl withGvl(VendorPermission vendorPermission, Vendor vendor) { + return VendorPermissionWithGvl.of(vendorPermission, vendor); + } } diff --git a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesOneStrategyTest.java b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesOneStrategyTest.java index 0c2806aec73..65223078ca3 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesOneStrategyTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesOneStrategyTest.java @@ -12,10 +12,9 @@ import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.settings.model.SpecialFeature; -import java.util.Arrays; -import java.util.Collection; import java.util.List; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -69,19 +68,18 @@ public void processSpecialFeaturesStrategyShouldAllowForAllWhenIsNotEnforced() { // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); + final List vendorPermissions = asList(vendorPermission1, vendorPermission2); final SpecialFeature specialFeature = SpecialFeature.of(false, emptyList()); // when - final Collection result = target.processSpecialFeaturesStrategy(tcString, specialFeature, - vendorPermissions); + target.processSpecialFeaturesStrategy(tcString, specialFeature, vendorPermissions); // then final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowSpecialFeature()); final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowSpecialFeature()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission1Changed, - vendorPermission2Changed); + assertThat(vendorPermission1).isEqualTo(vendorPermission1Changed); + assertThat(vendorPermission2).isEqualTo(vendorPermission2Changed); verifyNoInteractions(specialFeatureOptIns); } @@ -91,18 +89,16 @@ public void processSpecialFeaturesStrategyShouldAllowEmptyListWhenAllOptOutAndNo // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); + final List vendorPermissions = asList(vendorPermission1, vendorPermission2); final SpecialFeature specialFeature = SpecialFeature.of(true, emptyList()); // when - final Collection result = target.processSpecialFeaturesStrategy(tcString, specialFeature, - vendorPermissions); + target.processSpecialFeaturesStrategy(tcString, specialFeature, vendorPermissions); // then - assertThat(result) - .usingRecursiveFieldByFieldElementComparator() - .containsOnly(vendorPermission1, vendorPermission2); + assertThat(vendorPermission1).isEqualTo(VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll())); + assertThat(vendorPermission2).isEqualTo(VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll())); verify(specialFeatureOptIns).contains(SPECIAL_FEATURE_ID); } @@ -112,18 +108,17 @@ public void processSpecialFeaturesStrategyShouldAllowOnlyExcludedWhenAllOptOutAn // given final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2); + final List vendorPermissions = asList(vendorPermission1, vendorPermission2); final SpecialFeature specialFeature = SpecialFeature.of(true, singletonList("b1")); // when - final Collection result = target.processSpecialFeaturesStrategy(tcString, specialFeature, - vendorPermissions); + target.processSpecialFeaturesStrategy(tcString, specialFeature, vendorPermissions); // then final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowSpecialFeature()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission1, - vendorPermission2Changed); + assertThat(vendorPermission1).isEqualTo(VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll())); + assertThat(vendorPermission2).isEqualTo(vendorPermission2Changed); verify(specialFeatureOptIns).contains(SPECIAL_FEATURE_ID); } @@ -134,7 +129,9 @@ public void processSpecialFeaturesStrategyShouldAllowExcludedAndOptIn() { final VendorPermission vendorPermission1 = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission2 = VendorPermission.of(2, "b1", PrivacyEnforcementAction.restrictAll()); final VendorPermission vendorPermission3 = VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll()); - final List vendorPermissions = Arrays.asList(vendorPermission1, vendorPermission2, + final List vendorPermissions = asList( + vendorPermission1, + vendorPermission2, vendorPermission3); final SpecialFeature specialFeature = SpecialFeature.of(true, singletonList("b1")); @@ -142,15 +139,15 @@ public void processSpecialFeaturesStrategyShouldAllowExcludedAndOptIn() { given(specialFeatureOptIns.contains(SPECIAL_FEATURE_ID)).willReturn(true); // when - final Collection result = target.processSpecialFeaturesStrategy(tcString, specialFeature, - vendorPermissions); + target.processSpecialFeaturesStrategy(tcString, specialFeature, vendorPermissions); // then final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowSpecialFeature()); final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowSpecialFeature()); final VendorPermission vendorPermission3Changed = VendorPermission.of(3, "b3", allowSpecialFeature()); - assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission1Changed, - vendorPermission2Changed, vendorPermission3Changed); + assertThat(vendorPermission1).isEqualTo(vendorPermission1Changed); + assertThat(vendorPermission2).isEqualTo(vendorPermission2Changed); + assertThat(vendorPermission3).isEqualTo(vendorPermission3Changed); verify(specialFeatureOptIns).contains(SPECIAL_FEATURE_ID); } diff --git a/src/test/java/org/prebid/server/settings/EnrichingApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/EnrichingApplicationSettingsTest.java index 4c1f01f7de3..045e9e906c5 100644 --- a/src/test/java/org/prebid/server/settings/EnrichingApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/EnrichingApplicationSettingsTest.java @@ -116,7 +116,7 @@ public void getAccountByIdShouldMergeAccountWithDefaultAccount() { 0, Account.builder() .auction(AccountAuctionConfig.builder().bannerCacheTtl(100).build()) - .privacy(AccountPrivacyConfig.of(gdprConfig, null, null, null, null)) + .privacy(AccountPrivacyConfig.builder().gdpr(gdprConfig).build()) .build(), delegate, priceFloorsConfigResolver, @@ -127,14 +127,11 @@ public void getAccountByIdShouldMergeAccountWithDefaultAccount() { .auction(AccountAuctionConfig.builder() .videoCacheTtl(200) .build()) - .privacy(AccountPrivacyConfig.of( - AccountGdprConfig.builder() + .privacy(AccountPrivacyConfig.builder() + .gdpr(AccountGdprConfig.builder() .enabledForRequestType(EnabledForRequestType.of(true, null, null, null, null)) - .build(), - null, - null, - null, - null)) + .build()) + .build()) .build())); // when @@ -147,15 +144,12 @@ public void getAccountByIdShouldMergeAccountWithDefaultAccount() { .bannerCacheTtl(100) .videoCacheTtl(200) .build()) - .privacy(AccountPrivacyConfig.of( - AccountGdprConfig.builder() + .privacy(AccountPrivacyConfig.builder() + .gdpr(AccountGdprConfig.builder() .enabled(true) .enabledForRequestType(EnabledForRequestType.of(true, null, null, null, null)) - .build(), - null, - null, - null, - null)) + .build()) + .build()) .build()); } @@ -236,11 +230,8 @@ public void getAccountByIdShouldRemoveInvalidRulesFromAccountActivitiesConfigura jsonMerger); given(delegate.getAccountById(anyString(), any())).willReturn(Future.succeededFuture(Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - null, - null, - Map.of( + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of( Activity.SYNC_USER, AccountActivityConfiguration.of(null, null), Activity.CALL_BIDDER, AccountActivityConfiguration.of(null, asList( AccountActivityComponentRuleConfig.of(null, null), @@ -255,8 +246,8 @@ public void getAccountByIdShouldRemoveInvalidRulesFromAccountActivitiesConfigura AccountActivityComponentRuleConfig.of( AccountActivityComponentRuleConfig.Condition.of( singletonList(ComponentType.BIDDER), singletonList("bidder")), - null)))), - null)) + null))))) + .build()) .build())); // when @@ -264,11 +255,8 @@ public void getAccountByIdShouldRemoveInvalidRulesFromAccountActivitiesConfigura // then assertThat(accountFuture).succeededWith(Account.builder() - .privacy(AccountPrivacyConfig.of( - null, - null, - null, - Map.of( + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of( Activity.SYNC_USER, AccountActivityConfiguration.of(null, null), Activity.CALL_BIDDER, AccountActivityConfiguration.of(null, asList( AccountActivityComponentRuleConfig.of(null, null), @@ -278,8 +266,8 @@ public void getAccountByIdShouldRemoveInvalidRulesFromAccountActivitiesConfigura AccountActivityComponentRuleConfig.of( AccountActivityComponentRuleConfig.Condition.of( singletonList(ComponentType.BIDDER), singletonList("bidder")), - null)))), - null)) + null))))) + .build()) .build()); } } diff --git a/src/test/java/org/prebid/server/settings/FileApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/FileApplicationSettingsTest.java index 711442be41e..ebbd2abc0e8 100644 --- a/src/test/java/org/prebid/server/settings/FileApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/FileApplicationSettingsTest.java @@ -157,24 +157,25 @@ public void getAccountByIdShouldReturnPresentAccount() { .bidValidations(AccountBidValidationConfig.of(BidValidationEnforcement.enforce)) .events(AccountEventsConfig.of(true)) .build()) - .privacy(AccountPrivacyConfig.of( - AccountGdprConfig.builder() + .privacy(AccountPrivacyConfig.builder() + .gdpr(AccountGdprConfig.builder() .enabled(true) .enabledForRequestType(EnabledForRequestType.of(true, true, true, true, true)) .purposes(Purposes.builder() - .p1(Purpose.of(EnforcePurpose.basic, false, asList("rubicon", "appnexus"))) - .p2(Purpose.of(EnforcePurpose.full, true, singletonList("openx"))) + .p1(Purpose.of( + EnforcePurpose.basic, + false, + asList("rubicon", "appnexus"), + null)) + .p2(Purpose.of(EnforcePurpose.full, true, singletonList("openx"), null)) .build()) .specialFeatures(SpecialFeatures.builder() .sf1(SpecialFeature.of(true, asList("rubicon", "appnexus"))) .sf2(SpecialFeature.of(false, singletonList("openx"))) .build()) .purposeOneTreatmentInterpretation(PurposeOneTreatmentInterpretation.accessAllowed) - .build(), - null, - null, - null, - null)) + .build()) + .build()) .analytics(AccountAnalyticsConfig.of( expectedEventsConfig, singletonMap( diff --git a/src/test/java/org/prebid/server/settings/HttpApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/HttpApplicationSettingsTest.java index cd4d0c557c2..2b90e48b926 100644 --- a/src/test/java/org/prebid/server/settings/HttpApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/HttpApplicationSettingsTest.java @@ -107,7 +107,7 @@ public void getAccountByIdShouldReturnFetchedAccount() throws JsonProcessingExce .auction(AccountAuctionConfig.builder() .priceGranularity("testPriceGranularity") .build()) - .privacy(AccountPrivacyConfig.of(null, null, null, null, null)) + .privacy(AccountPrivacyConfig.builder().build()) .build(); final HttpAccountsResponse response = HttpAccountsResponse.of(Collections.singletonMap("someId", account)); givenHttpClientReturnsResponse(200, mapper.writeValueAsString(response)); diff --git a/src/test/java/org/prebid/server/settings/JdbcApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/JdbcApplicationSettingsTest.java index 53b9e518605..b53b6943324 100644 --- a/src/test/java/org/prebid/server/settings/JdbcApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/JdbcApplicationSettingsTest.java @@ -244,15 +244,12 @@ public void getAccountByIdShouldReturnAccountWithAllFieldsPopulated(TestContext .bidValidations(AccountBidValidationConfig.of(BidValidationEnforcement.enforce)) .events(AccountEventsConfig.of(true)) .build()) - .privacy(AccountPrivacyConfig.of( - AccountGdprConfig.builder() + .privacy(AccountPrivacyConfig.builder() + .gdpr(AccountGdprConfig.builder() .enabled(true) .enabledForRequestType(EnabledForRequestType.of(true, true, true, true, true)) - .build(), - null, - null, - null, - null)) + .build()) + .build()) .analytics(AccountAnalyticsConfig.of( expectedEventsConfig, singletonMap( diff --git a/src/test/java/org/prebid/server/util/algorithms/ListsUnionViewTest.java b/src/test/java/org/prebid/server/util/algorithms/ListsUnionViewTest.java new file mode 100644 index 00000000000..8747521d37b --- /dev/null +++ b/src/test/java/org/prebid/server/util/algorithms/ListsUnionViewTest.java @@ -0,0 +1,32 @@ +package org.prebid.server.util.algorithms; + +import org.junit.Test; + +import java.util.ArrayList; +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 ListsUnionViewTest { + + @Test + public void unionViewShouldActProperly() { + // given + final List list1 = new ArrayList<>(asList(1, 2)); + final List list2 = singletonList(3); + + // when + final List result = new ListsUnionView<>(list1, list2); + + // then + assertThat(result).containsExactly(1, 2, 3); + + // when + list1.add(1, 4); + + // then + assertThat(result).containsExactly(1, 4, 2, 3); + } +} diff --git a/src/test/java/org/prebid/server/vast/VastModifierTest.java b/src/test/java/org/prebid/server/vast/VastModifierTest.java index 5593a535028..89dce72913d 100644 --- a/src/test/java/org/prebid/server/vast/VastModifierTest.java +++ b/src/test/java/org/prebid/server/vast/VastModifierTest.java @@ -179,6 +179,21 @@ public void createBidVastXmlShouldBeModifiedWithNewImpressionVastUrlWhenEventsEn + ""); } + @Test + public void createBidVastXmlShouldBeModifiedWithNewImpressionVastUrlWhenEventsEnabledAndNoEmptyTag2() { + // when + final String bidAdm = "< impreSSion garbage >http:/test.com< /ImPression garbage >"; + final String result = target + .createBidVastXml(BIDDER, bidAdm, BID_NURL, BID_ID, ACCOUNT_ID, eventsContext(), emptyList(), + LINEITEM_ID); + + // then + verify(eventsService).vastUrlTracking(BID_ID, BIDDER, ACCOUNT_ID, LINEITEM_ID, eventsContext()); + + assertThat(result).isEqualTo("< impreSSion garbage >http:/test.com< /ImPression garbage >" + + ""); + } + @Test public void createBidVastXmlShouldBeModifiedWithNewImpressionAfterExistingImpressionTags() { // when @@ -196,6 +211,23 @@ public void createBidVastXmlShouldBeModifiedWithNewImpressionAfterExistingImpres + ""); } + @Test + public void createBidVastXmlShouldBeModifiedWithNewImpressionAfterExistingImpressionTags2() { + // when + final String bidAdm = "< Impression >http:/test.com< /Impression >" + + "http:/test2.com< /ImPRession garbage>"; + final String result = target + .createBidVastXml(BIDDER, bidAdm, BID_NURL, BID_ID, ACCOUNT_ID, eventsContext(), emptyList(), + LINEITEM_ID); + + // then + verify(eventsService).vastUrlTracking(BID_ID, BIDDER, ACCOUNT_ID, LINEITEM_ID, eventsContext()); + + assertThat(result).isEqualTo("< Impression >http:/test.com< /Impression >" + + "http:/test2.com< /ImPRession garbage>" + + ""); + } + @Test public void createBidVastXmlShouldInsertImpressionTagForEmptyInLine() { // when @@ -239,11 +271,27 @@ public void createBidVastXmlShouldModifyWrapperTagInCaseInsensitiveMode() { + ""); } + @Test + public void createBidVastXmlShouldModifyWrapperTagInCaseInsensitiveMode2() { + // when + final String bidAdm = "< wraPPer garbage>http:/test.com< / wraPPer garbage>"; + final String result = target + .createBidVastXml(BIDDER, bidAdm, BID_NURL, BID_ID, ACCOUNT_ID, eventsContext(), emptyList(), + LINEITEM_ID); + + // then + verify(eventsService).vastUrlTracking(BID_ID, BIDDER, ACCOUNT_ID, LINEITEM_ID, eventsContext()); + + assertThat(result).isEqualTo("< wraPPer garbage>http:/test.com" + + "< / wraPPer garbage>"); + } + @Test public void createBidVastXmlShouldInsertImpressionTagForEmptyWrapper() { // when + final String bidAdm = ""; final String result = target - .createBidVastXml(BIDDER, "", BID_NURL, + .createBidVastXml(BIDDER, bidAdm, BID_NURL, BID_ID, ACCOUNT_ID, eventsContext(), emptyList(), LINEITEM_ID); @@ -254,6 +302,23 @@ BID_ID, ACCOUNT_ID, eventsContext(), emptyList(), .isEqualTo(""); } + @Test + public void createBidVastXmlShouldInsertImpressionTagForEmptyWrapper2() { + // when + final String bidAdm = "< wraPPer garbage>< / wrapPer garbage>"; + final String result = target + .createBidVastXml(BIDDER, bidAdm, BID_NURL, + BID_ID, ACCOUNT_ID, eventsContext(), emptyList(), + LINEITEM_ID); + + // then + verify(eventsService).vastUrlTracking(BID_ID, BIDDER, ACCOUNT_ID, LINEITEM_ID, eventsContext()); + + assertThat(result) + .isEqualTo("< wraPPer garbage>< / wrapPer garbage>"); + } + @Test public void createBidVastXmlShouldNotInsertImpressionTagForNoWrapperCloseTag() { // when @@ -283,6 +348,21 @@ public void createBidVastXmlShouldModifyInlineTagInCaseInsensitiveMode() { + ""); } + @Test + public void createBidVastXmlShouldModifyInlineTagInCaseInsensitiveMode2() { + // when + final String bidAdm = "< InLIne garbage >http:/test.com"; + final String result = target + .createBidVastXml(BIDDER, bidAdm, BID_NURL, BID_ID, ACCOUNT_ID, eventsContext(), emptyList(), + LINEITEM_ID); + + // then + verify(eventsService).vastUrlTracking(BID_ID, BIDDER, ACCOUNT_ID, LINEITEM_ID, eventsContext()); + + assertThat(result).isEqualTo("< InLIne garbage >http:/test.com" + + ""); + } + @Test public void createBidVastXmlShouldBeModifiedIfInLineHasNoImpressionTags() { // when @@ -297,6 +377,21 @@ public void createBidVastXmlShouldBeModifiedIfInLineHasNoImpressionTags() { assertThat(result).isEqualTo(""); } + @Test + public void createBidVastXmlShouldBeModifiedIfInLineHasNoImpressionTags2() { + // when + final String bidAdm = "< InLIne garbage >< / InLIne garbage >"; + final String result = target + .createBidVastXml(BIDDER, bidAdm, BID_NURL, BID_ID, ACCOUNT_ID, eventsContext(), emptyList(), + LINEITEM_ID); + + // then + verify(eventsService).vastUrlTracking(BID_ID, BIDDER, ACCOUNT_ID, LINEITEM_ID, eventsContext()); + + assertThat(result).isEqualTo("< InLIne garbage >< / InLIne garbage >"); + } + @Test public void createBidVastXmlShouldNotBeModifiedIfNoParentTagsPresent() { // when @@ -312,6 +407,36 @@ public void createBidVastXmlShouldNotBeModifiedIfNoParentTagsPresent() { verify(metrics).updateAdapterRequestErrorMetric(BIDDER, MetricName.badserverresponse); } + @Test + public void createBidVastXmlShouldNotBeModifiedIfWrapperTagIsInvalid() { + // when + final String adm = ""; + final List warnings = new ArrayList<>(); + final String result = target + .createBidVastXml(BIDDER, adm, BID_NURL, BID_ID, ACCOUNT_ID, eventsContext(), warnings, LINEITEM_ID); + + // then + verify(eventsService).vastUrlTracking(BID_ID, BIDDER, ACCOUNT_ID, LINEITEM_ID, eventsContext()); + assertThat(result).isEqualTo(adm); + assertThat(warnings).containsExactly("VastXml does not contain neither InLine nor Wrapper for bidder response"); + verify(metrics).updateAdapterRequestErrorMetric(BIDDER, MetricName.badserverresponse); + } + + @Test + public void createBidVastXmlShouldNotBeModifiedIfInlineTagIsInvalid() { + // when + final String adm = ""; + final List warnings = new ArrayList<>(); + final String result = target + .createBidVastXml(BIDDER, adm, BID_NURL, BID_ID, ACCOUNT_ID, eventsContext(), warnings, LINEITEM_ID); + + // then + verify(eventsService).vastUrlTracking(BID_ID, BIDDER, ACCOUNT_ID, LINEITEM_ID, eventsContext()); + assertThat(result).isEqualTo(adm); + assertThat(warnings).containsExactly("VastXml does not contain neither InLine nor Wrapper for bidder response"); + verify(metrics).updateAdapterRequestErrorMetric(BIDDER, MetricName.badserverresponse); + } + @Test public void createBidVastXmlShouldNotModifyWhenEventsEnabledAndAdmHaveNoImpression() { // when diff --git a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-first-and-second-response.json b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-first-and-second-response.json index 19264d25845..67635c5327b 100644 --- a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-first-and-second-response.json +++ b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-first-and-second-response.json @@ -396,6 +396,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "rubicon" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-first-bid-only-response.json b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-first-bid-only-response.json index 297f0518a8e..b6eb2ef21fa 100644 --- a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-first-bid-only-response.json +++ b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-first-bid-only-response.json @@ -393,6 +393,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "rubicon" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-in-order-response.json b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-in-order-response.json index 297f0518a8e..b6eb2ef21fa 100644 --- a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-in-order-response.json +++ b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-in-order-response.json @@ -393,6 +393,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "rubicon" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-in-reverse-order-response.json b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-in-reverse-order-response.json index 6b0db95f32e..0888ea6f0ce 100644 --- a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-in-reverse-order-response.json +++ b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-in-reverse-order-response.json @@ -399,6 +399,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "rubicon" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-second-bid-only-response.json b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-second-bid-only-response.json index a0ccf324d82..4855226c745 100644 --- a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-second-bid-only-response.json +++ b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-second-bid-only-response.json @@ -397,6 +397,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "rubicon" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-third-and-second-response.json b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-third-and-second-response.json index 3fbc3594c12..fe231c9b49c 100644 --- a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-third-and-second-response.json +++ b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-third-and-second-response.json @@ -398,6 +398,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "rubicon" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-third-bid-only-response.json b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-third-bid-only-response.json index 7bea97d3c61..50b88237167 100644 --- a/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-third-bid-only-response.json +++ b/src/test/resources/org/prebid/server/it/deals/premature/responses/test-auction-third-bid-only-response.json @@ -397,6 +397,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "rubicon" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/deals/simulation/test-auction-response-1.json b/src/test/resources/org/prebid/server/it/deals/simulation/test-auction-response-1.json index 6d8a30fe28b..4a51433c278 100644 --- a/src/test/resources/org/prebid/server/it/deals/simulation/test-auction-response-1.json +++ b/src/test/resources/org/prebid/server/it/deals/simulation/test-auction-response-1.json @@ -301,6 +301,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "rubicon" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/deals/simulation/test-auction-response-2.json b/src/test/resources/org/prebid/server/it/deals/simulation/test-auction-response-2.json index cb8373269fd..85591873f73 100644 --- a/src/test/resources/org/prebid/server/it/deals/simulation/test-auction-response-2.json +++ b/src/test/resources/org/prebid/server/it/deals/simulation/test-auction-response-2.json @@ -285,6 +285,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "rubicon" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/deals/test-auction-response.json b/src/test/resources/org/prebid/server/it/deals/test-auction-response.json index 23703e850eb..d4e5d0690eb 100644 --- a/src/test/resources/org/prebid/server/it/deals/test-auction-response.json +++ b/src/test/resources/org/prebid/server/it/deals/test-auction-response.json @@ -803,6 +803,23 @@ "activity": "transmitUfpd", "allowed": true }, + { + "description": "Invocation of Activity Infrastructure.", + "activity": "transmitEids", + "activity_invocation_payload": { + "component_type": "BIDDER", + "component_name": "generic" + } + }, + { + "description": "Setting the default invocation result.", + "allow_by_default": true + }, + { + "description": "Activity Infrastructure invocation result.", + "activity": "transmitEids", + "allowed": true + }, { "description": "Invocation of Activity Infrastructure.", "activity": "transmitPreciseGeo", diff --git a/src/test/resources/org/prebid/server/it/openrtb2/appnexus/test-video-appnexus-bid-request-1.json b/src/test/resources/org/prebid/server/it/openrtb2/appnexus/test-video-appnexus-bid-request-1.json index cf3471cc49d..5e19435fdc2 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/appnexus/test-video-appnexus-bid-request-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/appnexus/test-video-appnexus-bid-request-1.json @@ -3,6 +3,7 @@ "imp": [ { "id": "1_0", + "secure": 1, "video": { "mimes": [ "video/mp4" @@ -258,19 +259,9 @@ "devicetype": 1, "os": "mac os", "h": 480, - "w": 640, - "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", - "didsha1": "didsha1", - "didmd5": "didmd5", - "dpidsha1": "dpidsha1", - "dpidmd5": "dpidmd5", - "macsha1": "macsha1", - "macmd5": "macmd5" + "w": 640 }, "user": { - "yob": 1991, - "gender": "F", - "keywords": "Hotels, Travelling", "ext": { "consent": "CPBCa-mPBCa-mAAAAAENA0CAAEAAAAAAACiQAaQAwAAgAgABoAAAAAA" } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/appnexus/test-video-appnexus-bid-request-2.json b/src/test/resources/org/prebid/server/it/openrtb2/appnexus/test-video-appnexus-bid-request-2.json index 1daa345a422..1b09479f3ea 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/appnexus/test-video-appnexus-bid-request-2.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/appnexus/test-video-appnexus-bid-request-2.json @@ -3,6 +3,7 @@ "imp": [ { "id": "2_4", + "secure": 1, "video": { "mimes": [ "video/mp4" @@ -51,19 +52,9 @@ "devicetype": 1, "os": "mac os", "h": 480, - "w": 640, - "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", - "didsha1": "didsha1", - "didmd5": "didmd5", - "dpidsha1": "dpidsha1", - "dpidmd5": "dpidmd5", - "macsha1": "macsha1", - "macmd5": "macmd5" + "w": 640 }, "user": { - "yob": 1991, - "gender": "F", - "keywords": "Hotels, Travelling", "ext": { "consent": "CPBCa-mPBCa-mAAAAAENA0CAAEAAAAAAACiQAaQAwAAgAgABoAAAAAA" } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_ch_endpoint/test-huaweiads-auction-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_ch_endpoint/test-huaweiads-auction-request.json index 5cacc4b8894..874f8fec466 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_ch_endpoint/test-huaweiads-auction-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_ch_endpoint/test-huaweiads-auction-request.json @@ -68,7 +68,7 @@ } }, "regs": { - "coppa": 1 + "coppa": 0 }, "ext": { } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_ch_endpoint/test-huaweiads-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_ch_endpoint/test-huaweiads-bid-request.json index 6485df19779..2415e64247f 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_ch_endpoint/test-huaweiads-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_ch_endpoint/test-huaweiads-bid-request.json @@ -47,7 +47,7 @@ "type": 0 }, "regs": { - "coppa": 1 + "coppa": 0 }, "consent": "CPaYLJBPaYLJBIPAAAENCSCgAPAAAAAAAAAAGsQAQGsAAAAA.YAAAAAAAAAA", "version": "3.4", diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_eu_endpoint/test-huaweiads-auction-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_eu_endpoint/test-huaweiads-auction-request.json index aed8a0cf12c..e41aea624d5 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_eu_endpoint/test-huaweiads-auction-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_eu_endpoint/test-huaweiads-auction-request.json @@ -65,7 +65,7 @@ } }, "regs": { - "coppa": 1 + "coppa": 0 }, "ext": { } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_eu_endpoint/test-huaweiads-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_eu_endpoint/test-huaweiads-bid-request.json index d5c7b214df6..72ddba10eff 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_eu_endpoint/test-huaweiads-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_eu_endpoint/test-huaweiads-bid-request.json @@ -27,7 +27,7 @@ "oaid": "oaid", "os": "android", "type": 4, - "ip" : "193.168.244.0", + "ip" : "193.168.244.1", "localeCountry": "PT", "pxratio": 23.01, "width": 1080, @@ -46,7 +46,7 @@ "cellInfo" : [ ] }, "regs": { - "coppa": 1 + "coppa": 0 }, "version": "3.4", "clientAdRequestId": "test-req-id" diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_imei/test-huaweiads-auction-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_imei/test-huaweiads-auction-request.json index e417955a04e..e4eaacaff19 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_imei/test-huaweiads-auction-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_imei/test-huaweiads-auction-request.json @@ -71,7 +71,7 @@ } }, "regs": { - "coppa": 1 + "coppa": 0 }, "ext": { } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_imei/test-huaweiads-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_imei/test-huaweiads-bid-request.json index 1c171ce0aad..3a68caedd22 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_imei/test-huaweiads-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_imei/test-huaweiads-bid-request.json @@ -49,7 +49,7 @@ "type": 0 }, "regs": { - "coppa": 1 + "coppa": 0 }, "consent": "CPaYLJBPaYLJBIPAAAENCSCgAPAAAAAAAAAAGsQAQGsAAAAA.YAAAAAAAAAA", "version": "3.4", diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_interstitial_type/test-huaweiads-auction-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_interstitial_type/test-huaweiads-auction-request.json index 9174a20bc08..e93327589c1 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_interstitial_type/test-huaweiads-auction-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_interstitial_type/test-huaweiads-auction-request.json @@ -65,7 +65,7 @@ } }, "regs": { - "coppa": 1 + "coppa": 0 }, "ext": { } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_interstitial_type/test-huaweiads-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_interstitial_type/test-huaweiads-bid-request.json index 9e32425bcac..57dabbdc4fa 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_interstitial_type/test-huaweiads-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/banner_interstitial_type/test-huaweiads-bid-request.json @@ -27,7 +27,7 @@ "oaid": "oaid", "os": "android", "type": 4, - "ip": "193.168.244.0", + "ip": "193.168.244.1", "localeCountry": "FR", "pxratio": 23.01, "width": 1080, @@ -46,7 +46,7 @@ "type": 0 }, "regs": { - "coppa": 1 + "coppa": 0 }, "version": "3.4", "clientAdRequestId": "test-req-id" diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/test-huaweiads-auction-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/test-huaweiads-auction-request.json index 61a37d903d6..3e2eed35706 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/test-huaweiads-auction-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/test-huaweiads-auction-request.json @@ -68,7 +68,7 @@ } }, "regs": { - "coppa": 1 + "coppa": 0 }, "ext": { } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/test-huaweiads-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/test-huaweiads-bid-request.json index 6485df19779..2415e64247f 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/test-huaweiads-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/huaweiads/test-huaweiads-bid-request.json @@ -47,7 +47,7 @@ "type": 0 }, "regs": { - "coppa": 1 + "coppa": 0 }, "consent": "CPaYLJBPaYLJBIPAAAENCSCgAPAAAAAAAAAAGsQAQGsAAAAA.YAAAAAAAAAA", "version": "3.4", diff --git a/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-auction-minutemedia-request.json b/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-auction-minutemedia-request.json new file mode 100644 index 00000000000..30bc6846987 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-auction-minutemedia-request.json @@ -0,0 +1,94 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "banner_imp_id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "prebid": { + "bidder": { + "minutemedia": { + "org": "123", + "path": "mvo", + "zone": "1r" + } + } + } + } + }, + { + "id": "video_imp_id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 1, + "maxduration": 2, + "protocols": [ + 1, + 2, + 5 + ], + "w": 1020, + "h": 780, + "startdelay": 1, + "placement": 1, + "playbackmethod": [ + 2 + ], + "delivery": [ + 1 + ], + "api": [ + 1, + 2, + 3, + 4 + ] + }, + "ext": { + "prebid": { + "bidder": { + "minutemedia": { + "org": "123", + "path": "mvo", + "zone": "1r" + } + } + } + } + } + ], + "site": { + "id": "999", + "cat": [ + "IAB3-1" + ], + "domain": "www.foobar.com", + "page": "http://www.foobar.com/1234.html ", + "publisher": { + "id": "111", + "name": "foobar.com", + "cat": [ + "IAB3-1" + ], + "domain": "foobar.com" + } + }, + "device": { + "ua": "Fake Test User Agent/6.0", + "ip": "123.45.67.89" + }, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-auction-minutemedia-response.json b/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-auction-minutemedia-response.json new file mode 100644 index 00000000000..898532edc4b --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-auction-minutemedia-response.json @@ -0,0 +1,43 @@ +{ + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "banner_imp_id", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29681110", + "adomain": [ + "test.com" + ], + "cid": "123", + "crid": "29681110", + "w": 300, + "h": 250, + "mtype": 1, + "ext": { + "origbidcpm": 0.5, + "origbidcur": "USD", + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "minutemedia", + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "minutemedia": "{{ minutemedia.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 0 + }, + "tmaxrequest": 5000 + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-minutemedia-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-minutemedia-bid-request.json new file mode 100644 index 00000000000..b42b2c736a0 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-minutemedia-bid-request.json @@ -0,0 +1,114 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "banner_imp_id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "secure": 1, + "ext": { + "tid": "${json-unit.any-string}", + "bidder": { + "org": "123", + "path": "mvo", + "zone": "1r" + } + } + }, + { + "id": "video_imp_id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 1, + "maxduration": 2, + "startdelay": 1, + "protocols": [ + 1, + 2, + 5 + ], + "w": 1020, + "h": 780, + "placement": 1, + "playbackmethod": [ + 2 + ], + "delivery": [ + 1 + ], + "api": [ + 1, + 2, + 3, + 4 + ] + }, + "secure": 1, + "ext": { + "tid": "${json-unit.any-string}", + "bidder": { + "org": "123", + "path": "mvo", + "zone": "1r" + } + } + } + ], + "site": { + "id": "999", + "domain": "www.foobar.com", + "cat": [ + "IAB3-1" + ], + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "111", + "name": "foobar.com", + "cat": [ + "IAB3-1" + ], + "domain": "foobar.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "Fake Test User Agent/6.0", + "ip": "123.45.67.89" + }, + "at": 1, + "tmax": "${json-unit.any-number}", + "cur": [ + "USD" + ], + "source": { + "tid": "${json-unit.any-string}" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "ext": { + "prebid": { + "channel": { + "name": "web" + }, + "server": { + "externalurl": "http://localhost:8080", + "gvlid": 1, + "datacenter": "local", + "endpoint": "/openrtb2/auction" + } + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-minutemedia-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-minutemedia-bid-response.json new file mode 100644 index 00000000000..f8251aacaf7 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/minutemedia/test-minutemedia-bid-response.json @@ -0,0 +1,27 @@ +{ + "id": "request_id", + "seatbid": [ + { + "seat": "123", + "bid": [ + { + "id": "bid_id", + "impid": "banner_imp_id", + "price": 0.500000, + "adid": "29681110", + "adm": "some-test-ad", + "adomain": [ + "test.com" + ], + "cid": "123", + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + } + ] + } + ], + "bidid": "global_bid_id", + "cur": "USD" +} diff --git a/src/test/resources/org/prebid/server/it/test-application.properties b/src/test/resources/org/prebid/server/it/test-application.properties index 2bef5e71ca2..1f0a9689ece 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -447,6 +447,8 @@ adapters.zeta_global_ssp.enabled=true adapters.zeta_global_ssp.endpoint=http://localhost:8090/zeta_global_ssp-exchange adapters.yearxero.enabled=true adapters.yearxero.endpoint=http://localhost:8090/yearxero-exchange +adapters.minutemedia.enabled=true +adapters.minutemedia.endpoint=http://localhost:8090/minutemedia-exchange?publisherId={{PublisherId}} http-client.circuit-breaker.enabled=true http-client.circuit-breaker.idle-expire-hours=24 http-client.circuit-breaker.opening-threshold=1000