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 e1c5e4240ce..96c5539eaee 100644 --- a/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java @@ -102,6 +102,7 @@ public class AmpRequestFactory { private final DebugResolver debugResolver; private final JacksonMapper mapper; private final GeoLocationServiceWrapper geoLocationServiceWrapper; + private final TcfDefinerService tcfDefinerService; public AmpRequestFactory(Ortb2RequestFactory ortb2RequestFactory, StoredRequestProcessor storedRequestProcessor, @@ -115,7 +116,8 @@ public AmpRequestFactory(Ortb2RequestFactory ortb2RequestFactory, AmpPrivacyContextFactory ampPrivacyContextFactory, DebugResolver debugResolver, JacksonMapper mapper, - GeoLocationServiceWrapper geoLocationServiceWrapper) { + GeoLocationServiceWrapper geoLocationServiceWrapper, + TcfDefinerService tcfDefinerService) { this.ortb2RequestFactory = Objects.requireNonNull(ortb2RequestFactory); this.storedRequestProcessor = Objects.requireNonNull(storedRequestProcessor); @@ -130,6 +132,7 @@ public AmpRequestFactory(Ortb2RequestFactory ortb2RequestFactory, this.ampPrivacyContextFactory = Objects.requireNonNull(ampPrivacyContextFactory); this.mapper = Objects.requireNonNull(mapper); this.geoLocationServiceWrapper = Objects.requireNonNull(geoLocationServiceWrapper); + this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService); } /** @@ -217,7 +220,7 @@ private Future parseBidRequest(AuctionContext auctionContext, HttpRe return Future.succeededFuture(bidRequest); } - private static ConsentParam consentParamFromQueryStringParams(HttpRequestContext httpRequest) { + private ConsentParam consentParamFromQueryStringParams(HttpRequestContext httpRequest) { final ConsentType specifiedConsentType = ConsentType.from(httpRequest.getQueryParams().get(CONSENT_TYPE_PARAM)); final CaseInsensitiveMultiMap queryParams = httpRequest.getQueryParams(); @@ -229,12 +232,12 @@ private static ConsentParam consentParamFromQueryStringParams(HttpRequestContext : toConsentParam(gdprConsentParam, GDPR_CONSENT_PARAM, specifiedConsentType); } - private static ConsentParam toConsentParam(String consent, String fromParam, ConsentType specifiedConsentType) { + private ConsentParam toConsentParam(String consent, String fromParam, ConsentType specifiedConsentType) { return ConsentParam.of( consent, fromParam, specifiedConsentType, - TcfDefinerService.isConsentStringValid(consent), + tcfDefinerService.isConsentStringValid(consent), Ccpa.isValid(consent)); } @@ -259,10 +262,10 @@ private static Site createSite(HttpRequestContext httpRequest) { return !StringUtils.isAllBlank(accountId, canonicalUrl, domain) ? Site.builder() - .publisher(Publisher.builder().id(accountId).build()) - .page(canonicalUrl) - .domain(domain) - .build() + .publisher(Publisher.builder().id(accountId).build()) + .page(canonicalUrl) + .domain(domain) + .build() : null; } @@ -278,9 +281,9 @@ private static User createUser(ConsentParam consentParam, String addtlConsent) { final ExtUser extUser = consentedProvidersSettings != null ? ExtUser.builder() - .deprecatedConsentedProvidersSettings(consentedProvidersSettings) - .consentedProvidersSettings(consentedProvidersSettings) - .build() + .deprecatedConsentedProvidersSettings(consentedProvidersSettings) + .consentedProvidersSettings(consentedProvidersSettings) + .build() : null; return User.builder().consent(consent).ext(extUser).build(); @@ -301,12 +304,12 @@ private static Regs createRegs(ConsentParam consentParam, return gdpr != null || usPrivacy != null || gppSid != null || gpp != null || gpc != null ? Regs.builder() - .gdpr(gdpr) - .usPrivacy(usPrivacy) - .gppSid(gppSid) - .gpp(gpp) - .ext(gpc != null ? ExtRegs.of(null, null, gpc, null) : null) - .build() + .gdpr(gdpr) + .usPrivacy(usPrivacy) + .gppSid(gppSid) + .gpp(gpp) + .ext(gpc != null ? ExtRegs.of(null, null, gpc, null) : null) + .build() : null; } @@ -359,8 +362,8 @@ private GppSidExtraction gppSidFromQueryStringParams(HttpRequestContext httpRequ try { final List gppSid = StringUtils.isNotBlank(gppSidParam) ? Arrays.stream(gppSidParam.split(",")) - .map(Integer::valueOf) - .toList() + .map(Integer::valueOf) + .toList() : null; return GppSidExtraction.success(gppSid); diff --git a/src/main/java/org/prebid/server/metric/MetricName.java b/src/main/java/org/prebid/server/metric/MetricName.java index 2f0950c5ec3..a1f46dbb564 100644 --- a/src/main/java/org/prebid/server/metric/MetricName.java +++ b/src/main/java/org/prebid/server/metric/MetricName.java @@ -104,6 +104,7 @@ public enum MetricName { specified, opt_out("opt-out"), invalid, + no_disclosed_vendors("no-disclosed-vendors"), in_geo("in-geo"), out_geo("out-geo"), unknown_geo("unknown-geo"), diff --git a/src/main/java/org/prebid/server/metric/Metrics.java b/src/main/java/org/prebid/server/metric/Metrics.java index e7b290bc14a..517fbb36bd5 100644 --- a/src/main/java/org/prebid/server/metric/Metrics.java +++ b/src/main/java/org/prebid/server/metric/Metrics.java @@ -519,6 +519,10 @@ public void updatePrivacyTcfInvalidMetric() { privacy().tcf().incCounter(MetricName.invalid); } + public void updatePrivacyTcfNoDisclosedVendorsMetric() { + privacy().tcf().incCounter(MetricName.no_disclosed_vendors); + } + public void updatePrivacyTcfRequestsMetric(int version) { final UpdatableMetrics versionMetrics = privacy().tcf().fromVersion(version); versionMetrics.incCounter(MetricName.requests); diff --git a/src/main/java/org/prebid/server/privacy/gdpr/DisclosedVendorsStrictness.java b/src/main/java/org/prebid/server/privacy/gdpr/DisclosedVendorsStrictness.java new file mode 100644 index 00000000000..13a5a5018a7 --- /dev/null +++ b/src/main/java/org/prebid/server/privacy/gdpr/DisclosedVendorsStrictness.java @@ -0,0 +1,45 @@ +package org.prebid.server.privacy.gdpr; + +import com.iabtcf.decoder.TCString; +import org.prebid.server.settings.model.GdprConfig; + +import java.time.Instant; +import java.time.Month; +import java.time.Year; +import java.time.ZoneOffset; + +public class DisclosedVendorsStrictness { + + private static final Instant TCF_2_3_ENFORCEMENT_CUTOFF_DATE = Year.of(2026) + .atMonth(Month.MARCH) + .atDay(1) + .atStartOfDay() + .toInstant(ZoneOffset.UTC); + + private final boolean strictnessEnabled; + + public DisclosedVendorsStrictness(GdprConfig gdprConfig) { + this.strictnessEnabled = gdprConfig == null || gdprConfig.isStrictDisclosedVendorsTreatment(); + } + + public boolean isValid(TCString consent) { + return !strictnessEnabled + || isCreatedBeforeTcfV2M3EnforcementCutoff(consent) + || !consent.getDisclosedVendors().isEmpty(); + } + + private boolean isCreatedBeforeTcfV2M3EnforcementCutoff(TCString consent) { + final Instant created = consent.getCreated(); + final Instant lastUpdated = consent.getLastUpdated(); + final Instant latest = lastUpdated.isAfter(created) ? lastUpdated : created; + + return latest.isBefore(TCF_2_3_ENFORCEMENT_CUTOFF_DATE); + } + + public boolean isVendorDisclosed(TCString consent, Integer vendorId) { + return !strictnessEnabled + || (vendorId != null + && (isCreatedBeforeTcfV2M3EnforcementCutoff(consent) + || consent.getDisclosedVendors().contains(vendorId))); + } +} 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 3134f33e91d..8d47b739426 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java @@ -358,17 +358,17 @@ private Purposes mergeAccountPurposes(AccountGdprConfig accountGdprConfig) { return accountPurposes != null ? Purposes.builder() - .p1(mergeItem(accountPurposes.getP1(), defaultPurposes.getP1())) - .p2(mergeItem(accountPurposes.getP2(), defaultPurposes.getP2())) - .p3(mergeItem(accountPurposes.getP3(), defaultPurposes.getP3())) - .p4(mergeItem(accountPurposes.getP4(), defaultPurposes.getP4())) - .p5(mergeItem(accountPurposes.getP5(), defaultPurposes.getP5())) - .p6(mergeItem(accountPurposes.getP6(), defaultPurposes.getP6())) - .p7(mergeItem(accountPurposes.getP7(), defaultPurposes.getP7())) - .p8(mergeItem(accountPurposes.getP8(), defaultPurposes.getP8())) - .p9(mergeItem(accountPurposes.getP9(), defaultPurposes.getP9())) - .p10(mergeItem(accountPurposes.getP10(), defaultPurposes.getP10())) - .build() + .p1(mergeItem(accountPurposes.getP1(), defaultPurposes.getP1())) + .p2(mergeItem(accountPurposes.getP2(), defaultPurposes.getP2())) + .p3(mergeItem(accountPurposes.getP3(), defaultPurposes.getP3())) + .p4(mergeItem(accountPurposes.getP4(), defaultPurposes.getP4())) + .p5(mergeItem(accountPurposes.getP5(), defaultPurposes.getP5())) + .p6(mergeItem(accountPurposes.getP6(), defaultPurposes.getP6())) + .p7(mergeItem(accountPurposes.getP7(), defaultPurposes.getP7())) + .p8(mergeItem(accountPurposes.getP8(), defaultPurposes.getP8())) + .p9(mergeItem(accountPurposes.getP9(), defaultPurposes.getP9())) + .p10(mergeItem(accountPurposes.getP10(), defaultPurposes.getP10())) + .build() : defaultPurposes; } @@ -379,9 +379,9 @@ private SpecialFeatures mergeAccountSpecialFeatures(AccountGdprConfig accountGdp return accountSpecialFeatures != null ? SpecialFeatures.builder() - .sf1(mergeItem(accountSpecialFeatures.getSf1(), defaultSpecialFeatures.getSf1())) - .sf2(mergeItem(accountSpecialFeatures.getSf2(), defaultSpecialFeatures.getSf2())) - .build() + .sf1(mergeItem(accountSpecialFeatures.getSf1(), defaultSpecialFeatures.getSf1())) + .sf2(mergeItem(accountSpecialFeatures.getSf2(), defaultSpecialFeatures.getSf2())) + .build() : defaultSpecialFeatures; } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java index 0d86272d378..91e89d0e2b3 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java @@ -60,6 +60,7 @@ public class TcfDefinerService { private final boolean gdprEnabled; private final String gdprDefaultValue; private final boolean consentStringMeansInScope; + private final DisclosedVendorsStrictness disclosedVendorsStrictness; private final Tcf2Service tcf2Service; private final Set eeaCountries; private final GeoLocationServiceWrapper geoLocationServiceWrapper; @@ -70,6 +71,7 @@ public class TcfDefinerService { public TcfDefinerService(GdprConfig gdprConfig, Set eeaCountries, + DisclosedVendorsStrictness disclosedVendorsStrictness, Tcf2Service tcf2Service, GeoLocationServiceWrapper geoLocationServiceWrapper, BidderCatalog bidderCatalog, @@ -81,6 +83,7 @@ public TcfDefinerService(GdprConfig gdprConfig, this.gdprDefaultValue = gdprConfig != null ? gdprConfig.getDefaultValue() : null; this.consentStringMeansInScope = gdprConfig != null && BooleanUtils.isTrue(gdprConfig.getConsentStringMeansInScope()); + this.disclosedVendorsStrictness = Objects.requireNonNull(disclosedVendorsStrictness); this.tcf2Service = Objects.requireNonNull(tcf2Service); this.eeaCountries = Objects.requireNonNull(eeaCountries); this.geoLocationServiceWrapper = Objects.requireNonNull(geoLocationServiceWrapper); @@ -345,6 +348,15 @@ private TCStringParsingResult parseConsentString(String consentString, RequestLo return TCStringParsingResult.of(TCStringEmpty.create(), warnings); } + if (!disclosedVendorsStrictness.isValid(tcString)) { + final String message = "Invalid TCF string: `disclosedVendors` list is empty."; + warnings.add(message); + logWarn(consentString, message, requestLogInfo); + metrics.updatePrivacyTcfNoDisclosedVendorsMetric(); + + return TCStringParsingResult.of(TCStringEmpty.create(), warnings); + } + return toValidResult(consentString, TCStringParsingResult.of(tcString, warnings)); } @@ -417,10 +429,9 @@ private static boolean isConsentValid(TCString consent) { return consent != null && !(consent instanceof TCStringEmpty); } - public static boolean isConsentStringValid(String consentString) { + public boolean isConsentStringValid(String consentString) { try { - TCString.decode(consentString); - return true; + return disclosedVendorsStrictness.isValid(TCString.decode(consentString)); } catch (RuntimeException e) { return false; } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/VendorIdResolver.java b/src/main/java/org/prebid/server/privacy/gdpr/VendorIdResolver.java index d8d360fa823..a1f32122327 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/VendorIdResolver.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/VendorIdResolver.java @@ -20,6 +20,6 @@ public static VendorIdResolver of(BidderCatalog bidderCatalog) { } public Integer resolve(String aliasOrBidder) { - return aliases != null ? aliases.resolveAliasVendorId(aliasOrBidder) : null; + return aliases.resolveAliasVendorId(aliasOrBidder); } } diff --git a/src/main/java/org/prebid/server/privacy/gdpr/model/TCStringEmpty.java b/src/main/java/org/prebid/server/privacy/gdpr/model/TCStringEmpty.java index 4225e32319f..01b77925b32 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/model/TCStringEmpty.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/model/TCStringEmpty.java @@ -22,12 +22,12 @@ public int getVersion() { @Override public Instant getCreated() { - return null; + return Instant.MAX; } @Override public Instant getLastUpdated() { - return null; + return Instant.MAX; } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01Strategy.java index 226c1910d24..269ac91aa18 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose01Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose01Strategy extends PurposeStrategy { - public Purpose01Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose01Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02Strategy.java index 8fad704d62a..4ea0e9f4b0a 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose02Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose02Strategy extends PurposeStrategy { - public Purpose02Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose02Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03Strategy.java index b6251b75af5..8eec594de89 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose03Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose03Strategy extends PurposeStrategy { - public Purpose03Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose03Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04Strategy.java index 5c2cf863b46..c28e6fcaf51 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose04Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose04Strategy extends PurposeStrategy { - public Purpose04Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose04Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05Strategy.java index 30913072c83..1cc606d6195 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose05Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose05Strategy extends PurposeStrategy { - public Purpose05Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose05Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06Strategy.java index 50945cc7b87..78e6b8f58a5 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose06Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose06Strategy extends PurposeStrategy { - public Purpose06Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose06Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07Strategy.java index 8e388c043c9..0010c87e9bd 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose07Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose07Strategy extends PurposeStrategy { - public Purpose07Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose07Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08Strategy.java index 8c084fa9d48..166fcdcf5ad 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose08Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose08Strategy extends PurposeStrategy { - public Purpose08Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose08Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09Strategy.java index 3a2704b595b..5f3f7f59b3e 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose09Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose09Strategy extends PurposeStrategy { - public Purpose09Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose09Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @Override diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10Strategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10Strategy.java index 458cd187bd4..874aca536d5 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10Strategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/Purpose10Strategy.java @@ -1,5 +1,6 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.BasicEnforcePurposeStrategy; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies.FullEnforcePurposeStrategy; @@ -8,11 +9,16 @@ public class Purpose10Strategy extends PurposeStrategy { - public Purpose10Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public Purpose10Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { - super(fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); + super( + disclosedVendorsStrictness, + fullEnforcePurposeStrategy, + basicEnforcePurposeStrategy, + noEnforcePurposeStrategy); } @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 e9e83a3ed9c..16face7bad8 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 @@ -3,6 +3,7 @@ import com.iabtcf.decoder.TCString; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -20,14 +21,17 @@ public abstract class PurposeStrategy { + private final DisclosedVendorsStrictness disclosedVendorsStrictness; private final FullEnforcePurposeStrategy fullEnforcePurposeStrategy; private final BasicEnforcePurposeStrategy basicEnforcePurposeStrategy; private final NoEnforcePurposeStrategy noEnforcePurposeStrategy; - public PurposeStrategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + public PurposeStrategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { + this.disclosedVendorsStrictness = disclosedVendorsStrictness; this.fullEnforcePurposeStrategy = fullEnforcePurposeStrategy; this.basicEnforcePurposeStrategy = basicEnforcePurposeStrategy; this.noEnforcePurposeStrategy = noEnforcePurposeStrategy; @@ -64,6 +68,8 @@ public void processTypePurposeStrategy(TCString vendorConsent, final Collection excludedVendors = excludedVendors(vendorPermissions, purpose); final Collection vendorForPurpose = vendorPermissions.stream() .filter(vendorPermission -> !excludedVendors.contains(vendorPermission)) + .filter(vendorPermission -> disclosedVendorsStrictness + .isVendorDisclosed(vendorConsent, vendorPermission.getVendor().getId())) .toList(); allowedByTypeStrategy(vendorConsent, purpose, vendorForPurpose, 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 ffb1e355276..c301c2850ec 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 @@ -2,15 +2,25 @@ import com.iabtcf.decoder.TCString; import com.iabtcf.utils.IntIterable; +import org.apache.commons.collections4.SetUtils; 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.Set; import java.util.stream.Stream; public abstract class EnforcePurposeStrategy { + protected static final Set LI_SUPPORTED_PURPOSES = SetUtils.difference( + Set.of(PurposeCode.values()), + Set.of( + PurposeCode.THREE, + PurposeCode.FOUR, + PurposeCode.FIVE, + PurposeCode.SIX)); + public abstract Stream allowedByTypeStrategy( PurposeCode purpose, TCString vendorConsent, @@ -43,6 +53,10 @@ protected boolean isAllowedByLegitimateInterest(PurposeCode purpose, boolean isEnforceVendor, TCString tcString) { + if (!LI_SUPPORTED_PURPOSES.contains(purpose)) { + return false; + } + final IntIterable purposesConsent = tcString.getPurposesLITransparency(); final IntIterable vendorConsent = tcString.getVendorLegitimateInterest(); 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 5b14f9a8a7e..092ebf8f77b 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 @@ -1,6 +1,7 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.purpose.typestrategies; import com.iabtcf.decoder.TCString; +import com.iabtcf.utils.BitSetIntIterable; import com.iabtcf.utils.IntIterable; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -18,7 +19,9 @@ public Stream allowedByTypeStrategy(PurposeCode purpose, boolean isEnforceVendors) { final IntIterable vendorConsent = tcString.getVendorConsent(); - final IntIterable vendorLIConsent = tcString.getVendorLegitimateInterest(); + final IntIterable vendorLIConsent = LI_SUPPORTED_PURPOSES.contains(purpose) + ? tcString.getVendorLegitimateInterest() + : BitSetIntIterable.EMPTY; final Stream allowedVendorPermissions = toVendorPermissions(vendorsForPurpose) .filter(vendorPermission -> vendorPermission.getVendorId() != null) diff --git a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesOneStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesOneStrategy.java index 005b10aaed3..9f7d6c45267 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesOneStrategy.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/specialfeature/SpecialFeaturesOneStrategy.java @@ -1,9 +1,14 @@ package org.prebid.server.privacy.gdpr.tcfstrategies.specialfeature; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; public class SpecialFeaturesOneStrategy extends SpecialFeaturesStrategy { + public SpecialFeaturesOneStrategy(DisclosedVendorsStrictness disclosedVendorsStrictness) { + super(disclosedVendorsStrictness); + } + @Override public int getSpecialFeatureId() { return 1; 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 5ac5494b67a..b23601cce0a 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 @@ -3,16 +3,24 @@ import com.iabtcf.decoder.TCString; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.settings.model.SpecialFeature; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.stream.Stream; public abstract class SpecialFeaturesStrategy { + private final DisclosedVendorsStrictness disclosedVendorsStrictness; + + protected SpecialFeaturesStrategy(DisclosedVendorsStrictness disclosedVendorsStrictness) { + this.disclosedVendorsStrictness = Objects.requireNonNull(disclosedVendorsStrictness); + } + public abstract int getSpecialFeatureId(); public abstract void allow(PrivacyEnforcementAction privacyEnforcementAction); @@ -22,10 +30,9 @@ public void processSpecialFeaturesStrategy(TCString vendorConsent, Collection vendorPermissions) { if (isOptIn(specialFeature, vendorConsent)) { - allowFor(vendorPermissions); - } else { - allowOnlyExcluded(vendorPermissions, specialFeature); + allowFor(disclosedVendors(vendorConsent, vendorPermissions)); } + allowFor(excludedVendors(specialFeature, vendorPermissions)); } private boolean isOptIn(SpecialFeature specialFeature, TCString vendorConsent) { @@ -33,24 +40,26 @@ private boolean isOptIn(SpecialFeature specialFeature, TCString vendorConsent) { || vendorConsent.getSpecialFeatureOptIns().contains(getSpecialFeatureId()); } - private void allowFor(Collection vendorPermissions) { - vendorPermissions.forEach(vendorPermission -> allow(vendorPermission.getPrivacyEnforcementAction())); + private Stream disclosedVendors(TCString vendorConsent, + Collection vendorPermissions) { + + return vendorPermissions.stream() + .filter(vendorPermission -> disclosedVendorsStrictness + .isVendorDisclosed(vendorConsent, vendorPermission.getVendorId())); } - private void allowOnlyExcluded(Collection vendorPermissions, SpecialFeature specialFeature) { - excludedVendors(vendorPermissions, specialFeature) - .map(VendorPermission::getPrivacyEnforcementAction) - .forEach(this::allow); + private void allowFor(Stream vendorPermissions) { + vendorPermissions.forEach(vendorPermission -> allow(vendorPermission.getPrivacyEnforcementAction())); } - private Stream excludedVendors(Collection vendorPermissions, - SpecialFeature specialFeature) { + private Stream excludedVendors(SpecialFeature specialFeature, + Collection vendorPermissions) { final List bidderNameExceptions = specialFeature.getVendorExceptions(); return CollectionUtils.isEmpty(bidderNameExceptions) ? Stream.empty() : vendorPermissions.stream() - .filter(vendorPermission -> bidderNameExceptions.contains(vendorPermission.getBidderName())); + .filter(vendorPermission -> bidderNameExceptions.contains(vendorPermission.getBidderName())); } } diff --git a/src/main/java/org/prebid/server/settings/model/GdprConfig.java b/src/main/java/org/prebid/server/settings/model/GdprConfig.java index 80d4abd9cfb..de548c2c6c8 100644 --- a/src/main/java/org/prebid/server/settings/model/GdprConfig.java +++ b/src/main/java/org/prebid/server/settings/model/GdprConfig.java @@ -17,7 +17,7 @@ public class GdprConfig { @JsonProperty("host-vendor-id") - String hostVendorId; + Integer hostVendorId; Boolean enabled; @@ -35,4 +35,7 @@ public class GdprConfig { @JsonProperty("purpose-one-treatment-interpretation") PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation; + + @JsonProperty("strict-disclosed-vendors-treatment") + boolean strictDisclosedVendorsTreatment = true; } 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 601de9e5f11..b8175828c3b 100644 --- a/src/main/java/org/prebid/server/spring/config/PrivacyServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/PrivacyServiceConfiguration.java @@ -17,6 +17,7 @@ import org.prebid.server.json.JacksonMapper; import org.prebid.server.metric.Metrics; import org.prebid.server.privacy.HostVendorTcfDefinerService; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.Tcf2Service; import org.prebid.server.privacy.gdpr.TcfDefinerService; import org.prebid.server.privacy.gdpr.tcfstrategies.purpose.Purpose01Strategy; @@ -156,10 +157,16 @@ Tcf2Service tcf2Service(GdprConfig gdprConfig, bidderCatalog); } + @Bean + DisclosedVendorsStrictness disclosedVendorsStrictness(GdprConfig gdprConfig) { + return new DisclosedVendorsStrictness(gdprConfig); + } + @Bean TcfDefinerService tcfDefinerService( GdprConfig gdprConfig, @Value("${gdpr.eea-countries}") String eeaCountriesAsString, + DisclosedVendorsStrictness disclosedVendorsStrictness, Tcf2Service tcf2Service, GeoLocationServiceWrapper geoLocationServiceWrapper, BidderCatalog bidderCatalog, @@ -172,6 +179,7 @@ TcfDefinerService tcfDefinerService( return new TcfDefinerService( gdprConfig, eeaCountries, + disclosedVendorsStrictness, tcf2Service, geoLocationServiceWrapper, bidderCatalog, @@ -181,126 +189,145 @@ TcfDefinerService tcfDefinerService( } @Bean - HostVendorTcfDefinerService hostVendorTcfDefinerService( - TcfDefinerService tcfDefinerService, - @Value("${gdpr.host-vendor-id:#{null}}") Integer hostVendorId) { + HostVendorTcfDefinerService hostVendorTcfDefinerService(TcfDefinerService tcfDefinerService, + GdprConfig gdprConfig) { - return new HostVendorTcfDefinerService(tcfDefinerService, hostVendorId); + return new HostVendorTcfDefinerService(tcfDefinerService, gdprConfig.getHostVendorId()); } @Bean - Purpose01Strategy purpose01Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose01Strategy purpose01Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose01Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - Purpose02Strategy purpose02Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose02Strategy purpose02Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, PurposeTwoBasicEnforcePurposeStrategy purposeTwoBasicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose02Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, purposeTwoBasicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - Purpose03Strategy purpose03Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose03Strategy purpose03Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose03Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - Purpose04Strategy purpose04Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose04Strategy purpose04Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose04Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - Purpose05Strategy purpose05Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose05Strategy purpose05Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose05Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - Purpose06Strategy purpose06Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose06Strategy purpose06Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose06Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - Purpose07Strategy purpose07Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose07Strategy purpose07Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose07Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - Purpose08Strategy purpose08Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose08Strategy purpose08Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose08Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - Purpose09Strategy purpose09Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose09Strategy purpose09Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose09Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - Purpose10Strategy purpose10Strategy(FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + Purpose10Strategy purpose10Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose10Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); } @Bean - SpecialFeaturesOneStrategy specialFeaturesOneStrategy() { - return new SpecialFeaturesOneStrategy(); + SpecialFeaturesOneStrategy specialFeaturesOneStrategy(DisclosedVendorsStrictness disclosedVendorsStrictness) { + return new SpecialFeaturesOneStrategy(disclosedVendorsStrictness); } @Bean 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 64a8dc7614a..337363fcf02 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -112,6 +112,7 @@ import org.prebid.server.privacy.gdpr.TcfDefinerService; import org.prebid.server.settings.ApplicationSettings; import org.prebid.server.settings.model.BidValidationEnforcement; +import org.prebid.server.settings.model.GdprConfig; import org.prebid.server.spring.config.model.CacheDefaultTtlProperties; import org.prebid.server.spring.config.model.ExternalConversionProperties; import org.prebid.server.spring.config.model.HttpClientCircuitBreakerProperties; @@ -161,17 +162,16 @@ public class ServiceConfiguration { private double logSamplingRate; @Bean - CoreCacheService cacheService( - CacheConfigurationProperties cacheConfigurationProperties, - @Value("${auction.cache.expected-request-time-ms}") long expectedCacheTimeMs, - @Value("${pbc.api.key:#{null}}") String apiKey, - @Value("${datacenter-region:#{null}}") String datacenterRegion, - VastModifier vastModifier, - EventsService eventsService, - HttpClient httpClient, - Metrics metrics, - Clock clock, - JacksonMapper mapper) { + CoreCacheService cacheService(CacheConfigurationProperties cacheConfigurationProperties, + @Value("${auction.cache.expected-request-time-ms}") long expectedCacheTimeMs, + @Value("${pbc.api.key:#{null}}") String apiKey, + @Value("${datacenter-region:#{null}}") String datacenterRegion, + VastModifier vastModifier, + EventsService eventsService, + HttpClient httpClient, + Metrics metrics, + Clock clock, + JacksonMapper mapper) { final String scheme = cacheConfigurationProperties.getScheme(); final String host = cacheConfigurationProperties.getHost(); @@ -354,8 +354,8 @@ Ortb2ImplicitParametersResolver ortb2ImplicitParametersResolver( @Value("${auction.ad-server-currency}") String adServerCurrency, @Value("${auction.blocklisted-apps}") String blocklistedAppsString, @Value("${external-url}") String externalUrl, - @Value("${gdpr.host-vendor-id:#{null}}") Integer hostVendorId, @Value("${datacenter-region}") String datacenterRegion, + GdprConfig gdprConfig, BidderCatalog bidderCatalog, ImplicitParametersExtractor implicitParametersExtractor, TimeoutResolver timeoutResolver, @@ -371,7 +371,7 @@ Ortb2ImplicitParametersResolver ortb2ImplicitParametersResolver( adServerCurrency, splitToList(blocklistedAppsString), externalUrl, - hostVendorId, + gdprConfig.getHostVendorId(), datacenterRegion, bidderCatalog, implicitParametersExtractor, @@ -534,7 +534,8 @@ AmpRequestFactory ampRequestFactory(Ortb2RequestFactory ortb2RequestFactory, AmpPrivacyContextFactory ampPrivacyContextFactory, DebugResolver debugResolver, JacksonMapper mapper, - GeoLocationServiceWrapper geoLocationServiceWrapper) { + GeoLocationServiceWrapper geoLocationServiceWrapper, + TcfDefinerService tcfDefinerService) { return new AmpRequestFactory( ortb2RequestFactory, @@ -549,7 +550,8 @@ AmpRequestFactory ampRequestFactory(Ortb2RequestFactory ortb2RequestFactory, ampPrivacyContextFactory, debugResolver, mapper, - geoLocationServiceWrapper); + geoLocationServiceWrapper, + tcfDefinerService); } @Bean @@ -1332,9 +1334,9 @@ private static > T splitToCollection(String listAsS return listAsString != null ? Stream.of(listAsString.split(",")) - .map(String::trim) - .filter(StringUtils::isNotBlank) - .collect(Collectors.toCollection(collectionFactory)) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toCollection(collectionFactory)) : collectionFactory.get(); } } diff --git a/src/main/java/org/prebid/server/spring/config/bidder/TaboolaConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/TaboolaConfiguration.java index ef917b09bc5..065c889bf8e 100644 --- a/src/main/java/org/prebid/server/spring/config/bidder/TaboolaConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/bidder/TaboolaConfiguration.java @@ -3,6 +3,7 @@ import org.prebid.server.bidder.BidderDeps; import org.prebid.server.bidder.taboola.TaboolaBidder; import org.prebid.server.json.JacksonMapper; +import org.prebid.server.settings.model.GdprConfig; 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; @@ -29,14 +30,14 @@ BidderConfigurationProperties configurationProperties() { @Bean BidderDeps taboolaBidderDeps(BidderConfigurationProperties taboolaConfigurationProperties, - @Value("${gdpr.host-vendor-id:#{null}}") Integer hostVendorId, @NotBlank @Value("${external-url}") String externalUrl, + GdprConfig gdprConfig, JacksonMapper mapper) { return BidderDepsAssembler.forBidder(BIDDER_NAME) .withConfig(taboolaConfigurationProperties) .usersyncerCreator(UsersyncerCreator.create(externalUrl)) - .bidderCreator(config -> new TaboolaBidder(config.getEndpoint(), hostVendorId, mapper)) + .bidderCreator(config -> new TaboolaBidder(config.getEndpoint(), gdprConfig.getHostVendorId(), mapper)) .assemble(); } } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountGdprConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountGdprConfig.groovy index f2f549f003a..1425f339fea 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/AccountGdprConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountGdprConfig.groovy @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.ToString import org.prebid.server.functional.model.ChannelType +import org.prebid.server.functional.model.bidder.BidderName @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy) @@ -22,7 +23,7 @@ class AccountGdprConfig { PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation @JsonProperty("purpose_one_treatment_interpretation") PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretationSnakeCase - List basicEnforcementVendors + List basicEnforcementVendors @JsonProperty("basic_enforcement_vendors") - List basicEnforcementVendorsSnakeCase + List basicEnforcementVendorsSnakeCase } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeatureConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeatureConfig.groovy index fc0d90ca6f8..483c4e81b01 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeatureConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeatureConfig.groovy @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy) @@ -12,5 +13,5 @@ class SpecialFeatureConfig { Boolean enforce List vendorExceptions @JsonProperty("vendor_exceptions") - List vendorExceptionsSnakeCase + List vendorExceptionsSnakeCase } 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 index 23e2684e51a..3b4eda25b3d 100644 --- a/src/test/groovy/org/prebid/server/functional/model/privacy/EnforcementRequirement.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/privacy/EnforcementRequirement.groovy @@ -9,6 +9,11 @@ import org.prebid.server.functional.model.config.Purpose import org.prebid.server.functional.model.config.PurposeEnforcement import org.prebid.server.functional.util.privacy.TcfConsent +import java.time.ZonedDateTime + +import static org.prebid.server.functional.util.privacy.TcfConsent.RestrictionType.REQUIRE_CONSENT +import static org.prebid.server.functional.util.privacy.TcfConsent.RestrictionType.UNDEFINED + @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy) class EnforcementRequirement { @@ -27,4 +32,32 @@ class EnforcementRequirement { List restrictionType Integer vendorIdGvl Integer vendorListVersion + List disclosedVendorsId + ZonedDateTime created = ZonedDateTime.now() + ZonedDateTime updated = ZonedDateTime.now() + + static EnforcementRequirement getDefaultBase(Integer disclosedVendorsId, Purpose purpose = Purpose.P2) { + new EnforcementRequirement().tap { + it.purpose = purpose + it.enforcePurpose = PurposeEnforcement.BASIC + it.enforceVendor = false + it.disclosedVendorsId = [disclosedVendorsId] + } + } + + static EnforcementRequirement getDefaultFull(Integer vendorId, + Integer vendorListVersion, + Purpose purpose = Purpose.P2) { + + new EnforcementRequirement().tap { + it.enforcePurpose = PurposeEnforcement.FULL + it.purpose = purpose + it.enforceVendor = true + it.vendorIdGvl = vendorId + it.restrictionType = [REQUIRE_CONSENT, UNDEFINED] + it.vendorConsentBitField = vendorId + it.vendorListVersion = vendorListVersion + it.disclosedVendorsId = [vendorId] + } + } } diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/PbsServiceFactory.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/PbsServiceFactory.groovy index e0911a2b1ca..60ca02ad762 100644 --- a/src/test/groovy/org/prebid/server/functional/testcontainers/PbsServiceFactory.groovy +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/PbsServiceFactory.groovy @@ -4,6 +4,7 @@ import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.testcontainers.container.NetworkServiceContainer import org.prebid.server.functional.testcontainers.container.PrebidServerContainer import org.prebid.server.functional.util.SystemProperties +import org.testcontainers.images.builder.Transferable import static org.prebid.server.functional.util.SystemProperties.USE_FIXED_CONTAINER_PORTS @@ -19,7 +20,7 @@ class PbsServiceFactory { this.networkServiceContainer = networkServiceContainer } - static PrebidServerService getService(Map config) { + static PrebidServerService getService(Map config, Map additionalFiles = [:]) { if (containers.containsKey(config)) { def container = containers.get(config) container.refresh() @@ -33,6 +34,9 @@ class PbsServiceFactory { remove([(container.key): container.value]) } def pbsContainer = new PrebidServerContainer(config) + if (!additionalFiles.isEmpty()) { + additionalFiles.each { k, v -> pbsContainer.withCopyToContainer(v, k) } + } pbsContainer.start() containers.put(config, pbsContainer) return new PrebidServerService(pbsContainer) diff --git a/src/test/groovy/org/prebid/server/functional/tests/CookieSyncSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/CookieSyncSpec.groovy index 29542fd8326..f8294bf7050 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/CookieSyncSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/CookieSyncSpec.groovy @@ -316,6 +316,7 @@ class CookieSyncSpec extends BaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Cookie sync request with gdpr and gdprConsent" @@ -1326,6 +1327,7 @@ class CookieSyncSpec extends BaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Cookie sync request with gdpr and gdprConsent" diff --git a/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy index 7e9eff9ebd3..a5655e0c973 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy @@ -27,6 +27,7 @@ import static org.prebid.server.functional.model.bidder.BidderName.WILDCARD import static org.prebid.server.functional.model.request.setuid.UidWithExpiry.defaultUidWithExpiry import static org.prebid.server.functional.model.response.cookiesync.UserSyncInfo.Type.REDIRECT import static org.prebid.server.functional.testcontainers.Dependencies.networkServiceContainer +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID import static org.prebid.server.functional.util.privacy.TcfConsent.RUBICON_VENDOR_ID class SetUidSpec extends BaseSpec { @@ -234,7 +235,7 @@ class SetUidSpec extends BaseSpec { def request = SetuidRequest.defaultSetuidRequest.tap { it.bidder = RUBICON gdpr = "1" - gdprConsent = new TcfConsent.Builder().build() + gdprConsent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() } def uidsCookie = UidsCookie.defaultUidsCookie.tap { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index d27d44e0fc0..bfc73c8de37 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -45,6 +45,8 @@ import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDO abstract class RuleEngineBaseSpec extends ModuleBaseSpec { protected static final List MULTI_BID_ADAPTERS = [GENERIC, OPENX, AMX].sort() + protected static final Integer OPENX_VENDOR_ID = PBSUtils.getRandomNumber(0, 65534) + protected static final Integer AMX_VENDOR_ID = PBSUtils.getRandomNumber(0, 65534) protected static final String APPLIED_FOR_ALL_IMPS = "*" protected static final String DEFAULT_CONDITIONS = "default" protected final static String CALL_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.call" @@ -75,10 +77,12 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { } protected static final Map ENABLED_DEBUG_LOG_MODE = ["logging.level.root": "debug"] - protected static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", - "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] - protected static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", - "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + protected static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", + "adapters.${OPENX}.endpoint" : "$networkServiceContainer.rootUri/auction".toString(), + "adapters.${OPENX}.meta-info.vendor-id": OPENX_VENDOR_ID as String] + protected static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", + "adapters.${AMX}.endpoint" : "$networkServiceContainer.rootUri/auction".toString(), + "adapters.${AMX}.meta-info.vendor-id": AMX_VENDOR_ID as String] protected static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] protected static final String CONFIG_DATA_CENTER = PBSUtils.randomString diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy index c4f207544b5..578d88b2130 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy @@ -799,6 +799,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { user = new User(ext: new UserExt(consent: new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID, OPENX_VENDOR_ID, AMX_VENDOR_ID]) .build())) } @@ -870,6 +871,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { user = new User(ext: new UserExt(consent: new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID, OPENX_VENDOR_ID, AMX_VENDOR_ID]) .build())) } 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 7d8ed79de7d..d92bec507cc 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 @@ -29,6 +29,7 @@ import org.prebid.server.functional.model.request.amp.ConsentType 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.Data +import org.prebid.server.functional.model.request.auction.DebugCondition import org.prebid.server.functional.model.request.auction.Device import org.prebid.server.functional.model.request.auction.DistributionChannel import org.prebid.server.functional.model.request.auction.Eid @@ -99,11 +100,6 @@ abstract class PrivacyBaseSpec extends BaseSpec { "gdpr.host-vendor-id" : GENERIC_VENDOR_ID as String, "adapters.generic.ccpa-enforced" : "true"] - protected static final int PURPOSES_ONLY_GVL_VERSION = PBSUtils.getRandomNumber(0, 4095) - protected static final int LEG_INT_PURPOSES_ONLY_GVL_VERSION = PBSUtils.getRandomNumberWithExclusion(PURPOSES_ONLY_GVL_VERSION, 0, 4095) - 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) - 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) - protected static final int EXPONENTIAL_BACKOFF_MAX_DELAY = 1 private static final Map RETRY_POLICY_EXPONENTIAL_CONFIG = [ @@ -211,6 +207,8 @@ abstract class PrivacyBaseSpec extends BaseSpec { getBidRequestWithGeo(channel).tap { regs.gdpr = 1 user = new User(consent: consentString) + test = DebugCondition.DISABLED + ext.prebid.debug = DebugCondition.DISABLED } } @@ -253,21 +251,6 @@ abstract class PrivacyBaseSpec extends BaseSpec { geo } - protected static void cacheVendorList(PrebidServerService pbsService) { - def isVendorListCachedClosure = { - def validConsentString = new TcfConsent.Builder() - .setPurposesLITransparency(BASIC_ADS) - .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) - .build() - def bidRequest = getGdprBidRequest(validConsentString) - - pbsService.sendAuctionRequest(bidRequest) - - pbsService.sendCollectedMetricsRequest()["privacy.tcf.v2.vendorlist.missing"] == 0 - } - PBSUtils.waitUntil(isVendorListCachedClosure, 10000, 1000) - } - protected static Account getAccountWithGdpr(String accountId, AccountGdprConfig gdprConfig) { getAccountWithPrivacy(accountId, new AccountPrivacyConfig(gdpr: gdprConfig)) } @@ -298,291 +281,6 @@ abstract class PrivacyBaseSpec extends BaseSpec { "/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 String generateSensitiveGpp(GppSectionId sectionId, Map fieldsMap) { Object sensitiveData Object consentBuilder @@ -650,10 +348,6 @@ abstract class PrivacyBaseSpec extends BaseSpec { } } - 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 { @@ -669,9 +363,4 @@ abstract class PrivacyBaseSpec extends BaseSpec { 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/TcfFullTransmitEidsActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/TcfFullTransmitEidsActivitiesSpec.groovy deleted file mode 100644 index d798bc4c478..00000000000 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/TcfFullTransmitEidsActivitiesSpec.groovy +++ /dev/null @@ -1,219 +0,0 @@ -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(GENERAL_PRIVACY_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/CcpaAmpSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/ccpa/CcpaAmpSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/CcpaAmpSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/ccpa/CcpaAmpSpec.groovy index 945ee175ee1..ad6a320b943 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/CcpaAmpSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/ccpa/CcpaAmpSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.ccpa import org.prebid.server.functional.model.config.AccountCcpaConfig import org.prebid.server.functional.model.config.AccountConfig @@ -7,6 +7,7 @@ import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.response.auction.ErrorType +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.privacy.BogusConsent import org.prebid.server.functional.util.privacy.CcpaConsent import org.prebid.server.functional.util.privacy.TcfConsent diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/CcpaAuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/ccpa/CcpaAuctionSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/CcpaAuctionSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/ccpa/CcpaAuctionSpec.groovy index d544c7a2788..719688ea015 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/CcpaAuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/ccpa/CcpaAuctionSpec.groovy @@ -1,8 +1,9 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.ccpa import org.prebid.server.functional.model.ChannelType import org.prebid.server.functional.model.config.AccountCcpaConfig import org.prebid.server.functional.model.request.auction.DistributionChannel +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.privacy.BogusConsent import org.prebid.server.functional.util.privacy.CcpaConsent import spock.lang.PendingFeature diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/CoppaSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/coppa/CoppaSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/CoppaSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/coppa/CoppaSpec.groovy index 134aa10c9a4..36d6ea3d13d 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/CoppaSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/coppa/CoppaSpec.groovy @@ -1,7 +1,8 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.coppa import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import spock.lang.PendingFeature import static org.prebid.server.functional.model.bidder.BidderName.GENERIC diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/dsa/DsaSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/dsa/DsaSpec.groovy index c2bf06fe50a..ddb2cd9dcfd 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/dsa/DsaSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.dsa import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.config.AccountDsaConfig @@ -12,6 +12,7 @@ import org.prebid.server.functional.model.response.auction.BidExt import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.model.response.auction.DsaResponse import org.prebid.server.functional.model.response.auction.DsaResponse as BidDsa +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.TcfConsent @@ -422,6 +423,7 @@ class DsaSpec extends PrivacyBaseSpec { def consentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() def accountId = PBSUtils.randomNumber.toString() diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAmpSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gdpr/GdprAmpSpec.groovy similarity index 98% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAmpSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gdpr/GdprAmpSpec.groovy index 717c9f32d5b..19d352208b2 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAmpSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gdpr/GdprAmpSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gdpr import org.mockserver.model.Delay import org.prebid.server.functional.model.ChannelType @@ -13,6 +13,7 @@ import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.DistributionChannel import org.prebid.server.functional.model.request.auction.Regs import org.prebid.server.functional.model.request.auction.RegsExt +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.BogusConsent import org.prebid.server.functional.util.privacy.CcpaConsent @@ -207,6 +208,7 @@ class GdprAmpSpec extends PrivacyBaseSpec { given: "Default AmpRequest with invalid consent_type" def consentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() def ampRequest = getGdprAmpRequest(consentString).tap { consentType = BOGUS @@ -254,6 +256,7 @@ class GdprAmpSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() def ampRequest = getGdprAmpRequest(validConsentString) @@ -327,6 +330,7 @@ class GdprAmpSpec extends PrivacyBaseSpec { .setTcfPolicyVersion(tcfPolicyVersion.value) .setVendorListVersion(tcfPolicyVersion.vendorListVersion) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "AMP request" @@ -369,6 +373,7 @@ class GdprAmpSpec extends PrivacyBaseSpec { .setPurposesLITransparency(BASIC_ADS) .setTcfPolicyVersion(invalidTcfPolicyVersion) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "AMP request" @@ -418,6 +423,7 @@ class GdprAmpSpec extends PrivacyBaseSpec { .setTcfPolicyVersion(tcfPolicyVersion.value) .setVendorListVersion(tcfPolicyVersion.vendorListVersion) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "AMP request" @@ -511,7 +517,7 @@ class GdprAmpSpec extends PrivacyBaseSpec { def ampStoredRequest = bidRequestWithPersonalData and: "Amp default request" - def tcfConsent = new TcfConsent.Builder().build() + def tcfConsent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() def ampRequest = getGdprAmpRequest(tcfConsent).tap { account = ampStoredRequest.accountId } @@ -591,7 +597,10 @@ class GdprAmpSpec extends PrivacyBaseSpec { def ampStoredRequest = bidRequestWithPersonalData and: "Amp default request" - def tcfConsent = new TcfConsent.Builder().setSpecialFeatureOptIns(DEVICE_ACCESS).build() + def tcfConsent = new TcfConsent.Builder() + .setSpecialFeatureOptIns(DEVICE_ACCESS) + .setDisclosedVendors([GENERIC_VENDOR_ID]) + .build() def ampRequest = getGdprAmpRequest(tcfConsent).tap { account = ampStoredRequest.accountId } @@ -670,6 +679,7 @@ class GdprAmpSpec extends PrivacyBaseSpec { .setTcfPolicyVersion(tcfPolicyVersion.value) .setVendorListVersion(tcfPolicyVersion.vendorListVersion) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "AMP request" diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gdpr/GdprAuctionSpec.groovy similarity index 96% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gdpr/GdprAuctionSpec.groovy index 299d911a398..d90bf8da9ac 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gdpr/GdprAuctionSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gdpr import org.mockserver.model.Delay import org.prebid.server.functional.model.ChannelType @@ -12,6 +12,7 @@ import org.prebid.server.functional.model.request.auction.DistributionChannel import org.prebid.server.functional.model.request.auction.Regs import org.prebid.server.functional.model.request.auction.RegsExt import org.prebid.server.functional.model.response.auction.ErrorType +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.BogusConsent import org.prebid.server.functional.util.privacy.TcfConsent @@ -131,6 +132,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() def bidRequest = getGdprBidRequest(DistributionChannel.APP, validConsentString) @@ -154,6 +156,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() def bidRequest = getGdprBidRequest(validConsentString) @@ -225,6 +228,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() def bidRequest = getGdprBidRequest(validConsentString).tap { ext.prebid.channel = new Channel().tap { @@ -289,6 +293,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { .setTcfPolicyVersion(tcfPolicyVersion.value) .setVendorListVersion(tcfPolicyVersion.vendorListVersion) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Bid request" @@ -324,6 +329,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { .setPurposesLITransparency(BASIC_ADS) .setTcfPolicyVersion(invalidTcfPolicyVersion) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Bid request" @@ -369,6 +375,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { .setTcfPolicyVersion(tcfPolicyVersion.value) .setVendorListVersion(tcfPolicyVersion.vendorListVersion) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Bid request" @@ -420,6 +427,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Gpdr bid request with override country" @@ -448,6 +456,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Gpdr bid request with override country" @@ -476,6 +485,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Gpdr bid request with override country" @@ -504,6 +514,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Gpdr bid request with override country" @@ -529,7 +540,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def "PBS auction should update activity controls fetch bids metrics when tcf requirement disallow request"() { given: "Default Generic bid requests with personal data" - def tcfConsent = new TcfConsent.Builder().build() + def tcfConsent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() def bidRequest = bidRequestWithPersonalData.tap { regs.gdpr = 1 user.ext.consent = tcfConsent @@ -559,7 +570,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def "PBS auction should update activity controls privacy metrics when tcf requirement disallow privacy fields and trace level verbosity"() { given: "Default Generic BidRequests with personal data" - def tcfConsent = new TcfConsent.Builder().build() + def tcfConsent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() def bidRequest = bidRequestWithPersonalData.tap { regs.gdpr = 1 user.ext.consent = tcfConsent @@ -637,7 +648,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def "PBS auction should update activity controls privacy metrics when tcf requirement disallow privacy fields and trace level basic"() { given: "Default Generic BidRequests with personal data" - def tcfConsent = new TcfConsent.Builder().build() + def tcfConsent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() def bidRequest = bidRequestWithPersonalData.tap { regs.gdpr = 1 user.ext.consent = tcfConsent @@ -717,7 +728,10 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def "PBS auction should not update activity controls privacy metrics when tcf requirement allow privacy fields"() { given: "Default Generic BidRequests with privacy data" - def tcfConsent = new TcfConsent.Builder().setSpecialFeatureOptIns(DEVICE_ACCESS).build() + def tcfConsent = new TcfConsent.Builder() + .setSpecialFeatureOptIns(DEVICE_ACCESS) + .setDisclosedVendors([GENERIC_VENDOR_ID]) + .build() def bidRequest = bidRequestWithPersonalData.tap { regs.gdpr = 1 user.ext.consent = tcfConsent @@ -798,6 +812,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { .setTcfPolicyVersion(tcfPolicyVersion.value) .setVendorListVersion(tcfPolicyVersion.vendorListVersion) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Bid request" @@ -827,6 +842,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Bid request with gdpr and coppa config" @@ -899,6 +915,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() and: "Bid request with gdpr and coppa config" @@ -970,7 +987,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { def bidRequest = bidRequestWithPersonalData.tap { regs.gdpr = 1 user.buyeruid = null - user.ext.consent = new TcfConsent.Builder().build() + user.ext.consent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() ext.prebid.trace = VERBOSE } @@ -1016,7 +1033,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { given: "Default bid requests with personal data" def bidRequest = bidRequestWithPersonalData.tap { regs.gdpr = 1 - user.ext.consent = new TcfConsent.Builder().build() + user.ext.consent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() ext.prebid.trace = VERBOSE } @@ -1061,7 +1078,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { given: "Default bid requests with personal data" def bidRequest = bidRequestWithPersonalData.tap { regs.gdpr = 1 - user.ext.consent = new TcfConsent.Builder().build() + user.ext.consent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() ext.prebid.trace = VERBOSE } diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprSetUidSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gdpr/GdprSetUidSpec.groovy similarity index 95% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GdprSetUidSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gdpr/GdprSetUidSpec.groovy index df46f493775..8d9ac31c3c9 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprSetUidSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gdpr/GdprSetUidSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gdpr import org.prebid.server.functional.model.UidsCookie import org.prebid.server.functional.model.config.AccountAuctionConfig @@ -11,6 +11,8 @@ import org.prebid.server.functional.model.request.setuid.SetuidRequest import org.prebid.server.functional.model.response.cookiesync.UserSyncInfo import org.prebid.server.functional.service.PrebidServerException import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.testcontainers.Dependencies +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.TcfConsent import org.prebid.server.util.ResourceUtil @@ -23,14 +25,13 @@ 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.request.setuid.UidWithExpiry.getDefaultUidWithExpiry import static org.prebid.server.functional.model.response.cookiesync.UserSyncInfo.Type.REDIRECT -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.DEVICE_ACCESS class GdprSetUidSpec extends PrivacyBaseSpec { private static final boolean CORS_SUPPORT = false - private static final String USER_SYNC_URL = "$networkServiceContainer.rootUri/generic-usersync" + private static final String USER_SYNC_URL = "$Dependencies.networkServiceContainer.rootUri/generic-usersync" private static final UserSyncInfo.Type USER_SYNC_TYPE = REDIRECT private static final Map VENDOR_GENERIC_PBS_CONFIG = GENERIC_VENDOR_CONFIG + ["gdpr.purposes.p1.enforce-purpose" : NO.value, @@ -55,6 +56,7 @@ class GdprSetUidSpec extends PrivacyBaseSpec { it.gdprConsent = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() } @@ -95,6 +97,7 @@ class GdprSetUidSpec extends PrivacyBaseSpec { it.gdprConsent = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() } @@ -133,6 +136,7 @@ class GdprSetUidSpec extends PrivacyBaseSpec { it.gdprConsent = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([PBSUtils.getRandomNumberWithExclusion(GENERIC_VENDOR_ID, 0, 65534)]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() } @@ -174,6 +178,7 @@ class GdprSetUidSpec extends PrivacyBaseSpec { it.gdprConsent = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() } @@ -219,6 +224,7 @@ class GdprSetUidSpec extends PrivacyBaseSpec { it.gdprConsent = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() } @@ -268,6 +274,7 @@ class GdprSetUidSpec extends PrivacyBaseSpec { it.gdprConsent = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() } @@ -309,6 +316,7 @@ class GdprSetUidSpec extends PrivacyBaseSpec { it.gdprConsent = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() } diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/ActivityTraceLogSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/general/ActivityTraceLogSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/ActivityTraceLogSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/general/ActivityTraceLogSpec.groovy index d9563b7fe4d..562a6fb7a4f 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/ActivityTraceLogSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/general/ActivityTraceLogSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.general import org.prebid.server.functional.model.config.AccountGppConfig import org.prebid.server.functional.model.request.auction.Activity @@ -15,6 +15,7 @@ import org.prebid.server.functional.model.response.auction.ActivityInvocationPay import org.prebid.server.functional.model.response.auction.And import org.prebid.server.functional.model.response.auction.GeoCode import org.prebid.server.functional.model.response.auction.RuleConfiguration +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.gpp.v1.UsNatV1Consent diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyCookieSyncSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/general/PrivacyCookieSyncSpec.groovy similarity index 92% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyCookieSyncSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/general/PrivacyCookieSyncSpec.groovy index 6e424bbdb48..b4b8c947103 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyCookieSyncSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/general/PrivacyCookieSyncSpec.groovy @@ -1,7 +1,8 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.general import org.prebid.server.functional.model.config.AccountCcpaConfig import org.prebid.server.functional.model.request.cookiesync.CookieSyncRequest +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.CcpaConsent diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppAmpSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppAmpSpec.groovy similarity index 97% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppAmpSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppAmpSpec.groovy index ab4d46117ab..2813af53e97 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppAmpSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppAmpSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.amp.AmpRequest @@ -6,6 +6,7 @@ import org.prebid.server.functional.model.request.amp.ConsentType import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Regs import org.prebid.server.functional.model.request.auction.RegsExt +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.gpp.v2.TcfEuV2Consent import org.prebid.server.functional.util.privacy.gpp.v1.UspV1Consent @@ -14,13 +15,14 @@ import static org.prebid.server.functional.model.request.GppSectionId.TCF_EU_V2 import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 import static org.prebid.server.functional.model.request.amp.ConsentType.GPP import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID class GppAmpSpec extends PrivacyBaseSpec { def "PBS should populate bid request with regs when consent type is GPP and consent string, gppSid are present"() { given: "Default AmpRequest with consent_type = gpp" def gppSids = "${TCF_EU_V2.value},${USP_V1.value}" as String - def consentString = new TcfEuV2Consent.Builder().build().toString() + def consentString = new TcfEuV2Consent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build().toString() def ampRequest = getGppAmpRequest(consentString, gppSids) def ampStoredRequest = BidRequest.defaultBidRequest.tap { setAccountId(ampRequest.account) diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppAuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppAuctionSpec.groovy similarity index 98% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppAuctionSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppAuctionSpec.groovy index d0282d48ddd..b40700ed59f 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppAuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppAuctionSpec.groovy @@ -1,10 +1,11 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Regs import org.prebid.server.functional.model.request.auction.RegsExt import org.prebid.server.functional.model.request.auction.User import org.prebid.server.functional.model.response.auction.ErrorType +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.CcpaConsent import org.prebid.server.functional.util.privacy.TcfConsent @@ -149,6 +150,7 @@ class GppAuctionSpec extends PrivacyBaseSpec { def validConsentString = new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() def gppSidIds = [TCF_EU_V2.intValue] def gpp = new TcfEuV2Consent.Builder().build() diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppCookieSyncSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppCookieSyncSpec.groovy similarity index 94% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppCookieSyncSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppCookieSyncSpec.groovy index f9a7a23c2b7..fdc43a841e8 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppCookieSyncSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppCookieSyncSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import io.netty.handler.codec.http.HttpResponseStatus import org.prebid.server.functional.model.config.AccountConfig @@ -11,6 +11,7 @@ import org.prebid.server.functional.model.request.cookiesync.CookieSyncRequest import org.prebid.server.functional.model.response.cookiesync.UserSyncInfo import org.prebid.server.functional.service.PrebidServerException import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.testcontainers.Dependencies import org.prebid.server.functional.tests.BaseSpec import org.prebid.server.functional.util.HttpUtil import org.prebid.server.functional.util.PBSUtils @@ -28,7 +29,6 @@ import static org.prebid.server.functional.model.request.GppSectionId.TCF_EU_V2 import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 import static org.prebid.server.functional.model.response.cookiesync.UserSyncInfo.Type.IFRAME import static org.prebid.server.functional.model.response.cookiesync.UserSyncInfo.Type.REDIRECT -import static org.prebid.server.functional.testcontainers.Dependencies.networkServiceContainer import static org.prebid.server.functional.util.privacy.CcpaConsent.Signal.ENFORCED import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.DEVICE_ACCESS @@ -37,7 +37,7 @@ class GppCookieSyncSpec extends BaseSpec { private static final UserSyncInfo.Type USER_SYNC_TYPE = REDIRECT private static final boolean CORS_SUPPORT = false - private static final String USER_SYNC_URL = "$networkServiceContainer.rootUri/generic-usersync" + private static final String USER_SYNC_URL = "$Dependencies.networkServiceContainer.rootUri/generic-usersync" private static final GppSectionId FIRST_GPP_SECTION = PBSUtils.getRandomEnum(GppSectionId.class) private static final GppSectionId SECOND_GPP_SECTION = PBSUtils.getRandomEnum(GppSectionId.class, [FIRST_GPP_SECTION]) @@ -47,7 +47,7 @@ class GppCookieSyncSpec extends BaseSpec { "adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.support-cors": CORS_SUPPORT.toString()] private static final Map GENERIC_WITH_SKIP_CONFIG = [ "adapters.${GENERIC.value}.meta-info.vendor-id" : GENERIC_VENDOR_ID as String, - "adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.url" : "$networkServiceContainer.rootUri/generic-usersync&redir={{redirect_url}}".toString(), + "adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.url" : "$Dependencies.networkServiceContainer.rootUri/generic-usersync&redir={{redirect_url}}".toString(), "adapters.${GENERIC.value}.usersync.skipwhen.gdpr" : 'true', "adapters.${GENERIC.value}.usersync.skipwhen.gpp_sid" : "${FIRST_GPP_SECTION.value}, ${SECOND_GPP_SECTION.value}".toString(), "adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.support-cors": CORS_SUPPORT.toString()] @@ -132,7 +132,9 @@ class GppCookieSyncSpec extends BaseSpec { it.gppSid = PBSUtils.getRandomNumberWithExclusion(TCF_EU_V2.intValue) it.gpp = null it.gdpr = 1 - it.gdprConsent = new TcfConsent.Builder().build() // mandatory parameter when gdpr is resolved to 1 + it.gdprConsent = new TcfConsent.Builder() + .setDisclosedVendors([GENERIC_VENDOR_ID]) + .build() // mandatory parameter when gdpr is resolved to 1 } when: "PBS processes cookie sync request" @@ -191,8 +193,10 @@ class GppCookieSyncSpec extends BaseSpec { it.gppSid = TCF_EU_V2.value it.gpp = new TcfEuV2Consent.Builder().build() it.gdpr = null - it.gdprConsent = new TcfConsent.Builder().setPurposesLITransparency(DEVICE_ACCESS) + it.gdprConsent = new TcfConsent.Builder() + .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() } @@ -229,7 +233,7 @@ class GppCookieSyncSpec extends BaseSpec { def "PBS should return empty gpp and gppSid in usersync url when gpp and gppSid is not present in request"() { given: "Pbs config with usersync.#userSyncFormat.url" - def pbsConfig = ["adapters.generic.usersync.${userSyncFormat.value}.url" : "$networkServiceContainer.rootUri/generic-usersync&redir={{redirect_url}}".toString(), + def pbsConfig = ["adapters.generic.usersync.${userSyncFormat.value}.url" : "$Dependencies.networkServiceContainer.rootUri/generic-usersync&redir={{redirect_url}}".toString(), "adapters.generic.usersync.${userSyncFormat.value}.support-cors": "false"] def prebidServerService = pbsServiceFactory.getService(pbsConfig) @@ -256,7 +260,7 @@ class GppCookieSyncSpec extends BaseSpec { def "PBS should populate gpp and gppSid in usersync url when gpp and gppSid is present in request"() { given: "Pbs config with usersync.#userSyncFormat.url" - def pbsConfig = ["adapters.generic.usersync.${userSyncFormat.value}.url" : "$networkServiceContainer.rootUri/generic-usersync&redir={{redirect_url}}".toString(), + def pbsConfig = ["adapters.generic.usersync.${userSyncFormat.value}.url" : "$Dependencies.networkServiceContainer.rootUri/generic-usersync&redir={{redirect_url}}".toString(), "adapters.generic.usersync.${userSyncFormat.value}.support-cors": "false"] def prebidServerService = pbsServiceFactory.getService(pbsConfig) @@ -288,7 +292,7 @@ class GppCookieSyncSpec extends BaseSpec { def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap { it.gppSid = TCF_EU_V2.intValue it.gdpr = 1 - it.gdprConsent = new TcfConsent.Builder().build() + it.gdprConsent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() } when: "PBS processes cookie sync request" @@ -309,7 +313,7 @@ class GppCookieSyncSpec extends BaseSpec { it.bidders = [ALIAS] it.gppSid = TCF_EU_V2.intValue it.gdpr = 1 - it.gdprConsent = new TcfConsent.Builder().build() + it.gdprConsent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() } when: "PBS processes cookie sync request" @@ -422,6 +426,7 @@ class GppCookieSyncSpec extends BaseSpec { it.gdprConsent = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() it.account = PBSUtils.randomNumber } @@ -455,6 +460,7 @@ class GppCookieSyncSpec extends BaseSpec { it.gdprConsent = new TcfConsent.Builder() .setPurposesLITransparency(DEVICE_ACCESS) .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() it.account = PBSUtils.randomNumber } @@ -503,7 +509,7 @@ class GppCookieSyncSpec extends BaseSpec { def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap { it.gppSid = PBSUtils.getRandomNumberWithExclusion(TCF_EU_V2.intValue) it.gdpr = 1 - it.gdprConsent = new TcfConsent.Builder().build() + it.gdprConsent = new TcfConsent.Builder().setDisclosedVendors([GENERIC_VENDOR_ID]).build() } when: "PBS processes cookie sync request" diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppFetchBidActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppFetchBidActivitiesSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppFetchBidActivitiesSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppFetchBidActivitiesSpec.groovy index d292bd9e6f1..7160c95c0ea 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppFetchBidActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppFetchBidActivitiesSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import org.prebid.server.functional.model.config.AccountGppConfig import org.prebid.server.functional.model.config.ActivityConfig @@ -21,6 +21,7 @@ import org.prebid.server.functional.model.request.auction.Device import org.prebid.server.functional.model.request.auction.Geo import org.prebid.server.functional.model.request.auction.RegsExt import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.gpp.v1.UsCaV1Consent import org.prebid.server.functional.util.privacy.gpp.v1.UsCoV1Consent diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSetUidSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppSetUidSpec.groovy similarity index 95% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppSetUidSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppSetUidSpec.groovy index d49565222d2..eafafeaea33 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSetUidSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppSetUidSpec.groovy @@ -1,9 +1,10 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import org.prebid.server.functional.model.UidsCookie import org.prebid.server.functional.model.request.GppSectionId import org.prebid.server.functional.model.request.setuid.SetuidRequest import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.gpp.v2.TcfEuV2Consent import org.prebid.server.functional.util.privacy.gpp.v1.UspV1Consent @@ -77,6 +78,7 @@ class GppSetUidSpec extends PrivacyBaseSpec { def setUidRequest = SetuidRequest.defaultSetuidRequest.tap { def tcfEuV2Consent = new TcfEuV2Consent.Builder() .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .setDisclosedVendors([GENERIC_VENDOR_ID]) .build() .encodeSection() it.gpp = tcfEuV2Consent diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppSyncUserActivitiesSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppSyncUserActivitiesSpec.groovy index 87d23a3d740..9cbd8671a80 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppSyncUserActivitiesSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import org.prebid.server.functional.model.UidsCookie import org.prebid.server.functional.model.config.AccountCcpaConfig @@ -23,6 +23,7 @@ import org.prebid.server.functional.model.request.auction.Condition import org.prebid.server.functional.model.request.cookiesync.CookieSyncRequest import org.prebid.server.functional.model.request.setuid.SetuidRequest import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.gpp.v1.UsCaV1Consent import org.prebid.server.functional.util.privacy.gpp.v1.UsCoV1Consent diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitEidsActivitiesSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitEidsActivitiesSpec.groovy index 1db77ef0b90..1a52cbcea86 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitEidsActivitiesSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import org.prebid.server.functional.model.config.AccountGppConfig import org.prebid.server.functional.model.config.ActivityConfig @@ -21,6 +21,7 @@ import org.prebid.server.functional.model.request.auction.Device import org.prebid.server.functional.model.request.auction.Geo import org.prebid.server.functional.model.request.auction.RegsExt import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.gpp.v1.UsCaV1Consent import org.prebid.server.functional.util.privacy.gpp.v1.UsCoV1Consent diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitPreciseGeoActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitPreciseGeoActivitiesSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitPreciseGeoActivitiesSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitPreciseGeoActivitiesSpec.groovy index e3be1b13e98..da9dd38561f 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitPreciseGeoActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitPreciseGeoActivitiesSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import org.prebid.server.functional.model.config.AccountGppConfig import org.prebid.server.functional.model.config.ActivityConfig @@ -20,6 +20,7 @@ import org.prebid.server.functional.model.request.auction.Condition import org.prebid.server.functional.model.request.auction.Geo import org.prebid.server.functional.model.request.auction.RegsExt import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.gpp.v1.UsCaV1Consent import org.prebid.server.functional.util.privacy.gpp.v1.UsCoV1Consent diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitTidActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitTidActivitiesSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitTidActivitiesSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitTidActivitiesSpec.groovy index df0d0ab531f..fd02a755fe1 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitTidActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitTidActivitiesSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.amp.AmpRequest @@ -8,6 +8,7 @@ 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.Source +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import java.time.Instant diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitUfpdActivitiesSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitUfpdActivitiesSpec.groovy index b648a4fe91c..185726ecc0c 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/gpp/GppTransmitUfpdActivitiesSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.gpp import org.prebid.server.functional.model.config.AccountGdprConfig import org.prebid.server.functional.model.config.AccountGppConfig @@ -26,6 +26,7 @@ import org.prebid.server.functional.model.request.auction.Device import org.prebid.server.functional.model.request.auction.Geo import org.prebid.server.functional.model.request.auction.RegsExt import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.gpp.v1.UsCaV1Consent import org.prebid.server.functional.util.privacy.gpp.v1.UsCoV1Consent diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/LmtSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/lmt/LmtSpec.groovy similarity index 99% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/LmtSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/lmt/LmtSpec.groovy index 4966fe43ddf..ffbd958a508 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/LmtSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/lmt/LmtSpec.groovy @@ -1,10 +1,11 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.lmt 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.Device import org.prebid.server.functional.model.request.auction.DeviceExt +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec import org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ACCOUNT_DISALLOWED_COUNT diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfBaseSpec.groovy new file mode 100644 index 00000000000..e51e71c1b7a --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfBaseSpec.groovy @@ -0,0 +1,436 @@ +package org.prebid.server.functional.tests.privacy.tcf + +import org.prebid.server.functional.model.config.AccountGdprConfig +import org.prebid.server.functional.model.config.Purpose +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.privacy.EnforcementRequirement +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.service.PrebidServerService +import org.prebid.server.functional.tests.privacy.PrivacyBaseSpec +import org.prebid.server.functional.util.PBSUtils +import org.prebid.server.functional.util.privacy.TcfUtils +import org.testcontainers.images.builder.Transferable + +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +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.request.auction.ActivityType.TRANSMIT_EIDS +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID +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 + +class TcfBaseSpec extends PrivacyBaseSpec { + + private static String prepareEncodeResponseBodyWithPurposesOnly = getVendorListContent(true, false, false) + private static String prepareEncodeResponseBodyWithLegIntPurposes = getVendorListContent(false, true, false) + private static String prepareEncodeResponseBodyWithLegIntAndFlexiblePurposes = getVendorListContent(false, true, true) + private static String prepareEncodeResponseBodyWithPurposesAndFlexiblePurposes = getVendorListContent(true, false, true) + + protected static final int PURPOSES_ONLY_GVL_VERSION = PBSUtils.getRandomNumber(0, 4095) + protected static final int LEG_INT_PURPOSES_ONLY_GVL_VERSION = PBSUtils.getRandomNumberWithExclusion(PURPOSES_ONLY_GVL_VERSION, 0, 4095) + 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) + 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) + protected static Map GLV_LISTS_FILES = [(getVendorListPath(PURPOSES_ONLY_GVL_VERSION)) : Transferable.of(prepareEncodeResponseBodyWithPurposesOnly), + (getVendorListPath(LEG_INT_PURPOSES_ONLY_GVL_VERSION)) : Transferable.of(prepareEncodeResponseBodyWithLegIntPurposes), + (getVendorListPath(LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION)): Transferable.of(prepareEncodeResponseBodyWithLegIntAndFlexiblePurposes), + (getVendorListPath(PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION)): Transferable.of(prepareEncodeResponseBodyWithPurposesAndFlexiblePurposes) + ] + + private static final Map GDPR_RESTRICTION_CONFIG = ["gdpr.strict-disclosed-vendors-treatment": 'false'] + protected static final Map VENDOR_LIST_EMPTY_CONFIG = ["gdpr.vendorlist.v2.http-endpoint-template": null, + "gdpr.vendorlist.v3.http-endpoint-template": null] + + protected static final Map TCF_BASE_CONFIG = SETTING_CONFIG + GENERIC_VENDOR_CONFIG + GENERIC_CONFIG + GDPR_RESTRICTION_CONFIG + + protected static List getBasicTcfCompanyBasedEnforcementRequirements(Purpose purpose) { + [new EnforcementRequirement(purpose: purpose, + enforcePurpose: BASIC, + enforceVendor: false, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + new EnforcementRequirement(purpose: purpose, + enforcePurpose: NO, + enforceVendor: false, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + new EnforcementRequirement(purpose: purpose, + enforcePurpose: NO, + enforceVendor: true, + vendorConsentBitField: GENERIC_VENDOR_ID, + disclosedVendorsId: [GENERIC_VENDOR_ID]) + ] + } + + protected static List getBasicTcfLegalBasedEnforcementRequirements(Purpose purpose) { + [new EnforcementRequirement(purpose: purpose, + enforcePurpose: BASIC, + enforceVendor: true, + vendorConsentBitField: GENERIC_VENDOR_ID, + disclosedVendorsId: [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, + disclosedVendorsId: [GENERIC_VENDOR_ID])] + } + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + ] + } + + protected static List getFullTcfLegalEnforcementRequirementsRandomlyWithExcludePurpose(Purpose purpose) { + getFullTcfLegalEnforcementRequirements(purpose, true) + } + + protected static List getFullTcfLegalLegitimateInterestsRequirements(Purpose purpose, boolean isPurposeExcludedAndListRandom = false) { + [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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]) + ] + } + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + restrictionType: [REQUIRE_CONSENT], + vendorIdGvl: GENERIC_VENDOR_ID, + enforcePurpose: FULL, + enforceVendor: false, + vendorListVersion: PURPOSES_ONLY_GVL_VERSION, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + enforceVendor: false, + vendorListVersion: LEG_INT_AND_FLEXIBLE_PURPOSES_GVL_VERSION, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [UNDEFINED], + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + vendorIdGvl: GENERIC_VENDOR_ID, + restrictionType: [REQUIRE_CONSENT], + enforceVendor: false, + vendorListVersion: PURPOSES_AND_LEG_INT_PURPOSES_GVL_VERSION, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + new EnforcementRequirement(purpose: isPurposeExcludedAndListRandom ? getRandomPurposeWithExclusion(purpose) : purpose, + enforcePurpose: NO, + enforceVendor: false, + disclosedVendorsId: [GENERIC_VENDOR_ID])] + } + + protected static List getFullTcfCompanyEnforcementRequirementsRandomlyWithExcludePurpose(Purpose purpose) { + getFullTcfCompanyEnforcementRequirements(purpose, true) + } + + protected static List getFullTcfCompanyLegitimateInterestsRequirements(Purpose purpose, boolean isPurposeExcludedAndListRandom = false) { + [ + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]), + + 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, + disclosedVendorsId: [GENERIC_VENDOR_ID]) + ] + } + + protected static Account generateAccountWithGdprEidsConfig(EnforcementRequirement enforcementRequirement, + String accountId, + Boolean requireConsent, + List eidsExceptions = []) { + + def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) + + generateDefaultTcfAccount(accountId, enforcementRequirement, requireConsent, eidsExceptions).tap { + config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) + } + } + + protected static Account generateDefaultTcfAccount(String accountId, + EnforcementRequirement enforcementRequirement, + Boolean requireConsent = true, + List eidsExceptions = []) { + + def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirement, requireConsent, eidsExceptions) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + + getAccountWithGdpr(accountId, accountGdprConfig) + } + + 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/tcf/TcfBasicTransmitEidsActivitiesSpec.groovy similarity index 65% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/TcfBasicTransmitEidsActivitiesSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfBasicTransmitEidsActivitiesSpec.groovy index 924ee285661..adc320b1f5e 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/TcfBasicTransmitEidsActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfBasicTransmitEidsActivitiesSpec.groovy @@ -1,11 +1,6 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.tcf 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 @@ -13,6 +8,7 @@ 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.P10 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 @@ -21,22 +17,21 @@ 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 { +class TcfBasicTransmitEidsActivitiesSpec extends TcfBaseSpec { - private static final Map PBS_CONFIG = SETTING_CONFIG + GENERIC_VENDOR_CONFIG + GENERIC_CONFIG + ["gdpr.vendorlist.v2.http-endpoint-template": null, - "gdpr.vendorlist.v3.http-endpoint-template": null] + private static PrebidServerService activityPbsServiceExcludeGvl - private static final PrebidServerService activityPbsServiceExcludeGvl = pbsServiceFactory.getService(PBS_CONFIG) + def setupSpec() { + activityPbsServiceExcludeGvl = pbsServiceFactory.getService(TCF_BASE_CONFIG + VENDOR_LIST_EMPTY_CONFIG) + } def cleanupSpec() { - pbsServiceFactory.removeContainer(PBS_CONFIG) + pbsServiceFactory.removeContainer(TCF_BASE_CONFIG + VENDOR_LIST_EMPTY_CONFIG) } - def "PBS should leave the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have any basic consent"() { + def "PBS should preserve eids from original request 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) @@ -45,12 +40,9 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { it.user.eids = userEids } - and: "Save account config with requireConsent into DB" - def purposes = TcfUtils.getPurposeConfigsForPersonalizedAdsWithSnakeCase(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) + and: "Save account GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true).tap { + it.config.privacy.gdpr.basicEnforcementVendors = [GENERIC] } accountDao.save(account) @@ -67,7 +59,7 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { getBasicTcfCompanySoftVendorExceptionsRequirements(P4) } - def "PBS should leave the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have softVendorExceptions consent"() { + def "PBS should preserve eids from original request 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) @@ -78,12 +70,9 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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) + and: "Save account GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true).tap { + it.config.privacy.gdpr.basicEnforcementVendors = [ALIAS] } accountDao.save(account) @@ -98,7 +87,7 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { enforcementRequirements << getBasicTcfCompanySoftVendorExceptionsRequirements(P4) } - def "PBS should remove the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have any basic consent"() { + def "PBS should not transmit eids from original request 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) @@ -106,12 +95,9 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { it.user.eids = userEids } - and: "Save account config with requireConsent into DB" - def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) - def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendorsSnakeCase: [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) + and: "Save account GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true).tap { + it.config.privacy.gdpr.basicEnforcementVendorsSnakeCase = [GENERIC] } accountDao.save(account) @@ -125,35 +111,37 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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(P1) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P2) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P3) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P5) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P6) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P7) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P8) + + getBasicTcfLegalPurposesLITEnforcementRequirements(P9) + getBasicTcfLegalPurposesLITEnforcementRequirements(P10) + + + getBasicTcfCompanyBasedEnforcementRequirements(P1) + + getBasicTcfCompanyBasedEnforcementRequirements(P2) + + getBasicTcfCompanyBasedEnforcementRequirements(P3) + + getBasicTcfCompanyBasedEnforcementRequirements(P5) + + getBasicTcfCompanyBasedEnforcementRequirements(P6) + + getBasicTcfCompanyBasedEnforcementRequirements(P7) + + getBasicTcfCompanyBasedEnforcementRequirements(P8) + + getBasicTcfCompanyBasedEnforcementRequirements(P9) + getBasicTcfCompanyBasedEnforcementRequirements(P10) } - def "PBS should remove the original request with eids data when requireConsent is enabled and #enforcementRequirements.purpose have softVendorExceptions consent"() { + def "PBS should not transmit eids from original request 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) @@ -164,12 +152,9 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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) + and: "Save account GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true).tap { + it.config.privacy.gdpr.basicEnforcementVendors = [ALIAS] } accountDao.save(account) @@ -193,7 +178,7 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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"() { + def "PBS should preserve eids from original request 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) @@ -201,12 +186,9 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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) + and: "Save account GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true, userEids.source).tap { + it.config.privacy.gdpr.basicEnforcementVendors = [GENERIC] } accountDao.save(account) @@ -220,17 +202,18 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { where: enforcementRequirements << getBasicTcfLegalBasedEnforcementRequirements(P2) + - getBasicTcfLegalPurposesLITEnforcementRequirements(P2) + getBasicTcfLegalBasedEnforcementRequirements(P3) + getBasicTcfLegalBasedEnforcementRequirements(P5) + getBasicTcfLegalBasedEnforcementRequirements(P6) + getBasicTcfLegalBasedEnforcementRequirements(P7) + getBasicTcfLegalBasedEnforcementRequirements(P8) + getBasicTcfLegalBasedEnforcementRequirements(P9) + - getBasicTcfLegalBasedEnforcementRequirements(P10) + getBasicTcfLegalBasedEnforcementRequirements(P10) + + + getBasicTcfLegalPurposesLITEnforcementRequirements(P2) } - def "PBS should remove the original request with eids data when requireConsent is enabled, bidder is excepted and #enforcementRequirements.purpose have unsupported basic consent"() { + def "PBS should not transmit eids from original request 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) @@ -238,12 +221,9 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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) + and: "Save account GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true, userEids.source).tap { + it.config.privacy.gdpr.basicEnforcementVendors = [GENERIC] } accountDao.save(account) @@ -257,27 +237,29 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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) + getBasicTcfLegalPurposesLITEnforcementRequirements(P10) + + + getBasicTcfCompanyBasedEnforcementRequirements(P1) + + getBasicTcfCompanyBasedEnforcementRequirements(P2) + + getBasicTcfCompanyBasedEnforcementRequirements(P3) + + getBasicTcfCompanyBasedEnforcementRequirements(P5) + + getBasicTcfCompanyBasedEnforcementRequirements(P6) + + getBasicTcfCompanyBasedEnforcementRequirements(P7) + + getBasicTcfCompanyBasedEnforcementRequirements(P8) + + getBasicTcfCompanyBasedEnforcementRequirements(P9) + + getBasicTcfCompanyBasedEnforcementRequirements(P10) } - def "PBS should leave the original request with eids data when requireConsent is disabled and #enforcementRequirements.purpose have legal basic consent"() { + def "PBS should preserve eids from original request 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) @@ -285,12 +267,9 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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) + and: "Save account GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, false).tap { + it.config.privacy.gdpr.basicEnforcementVendors = [GENERIC] } accountDao.save(account) @@ -314,7 +293,7 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { getBasicTcfLegalBasedEnforcementRequirements(P10) } - def "PBS should remove the original request with eids data when requireConsent is disabled and #enforcementRequirements.purpose have softVendorExceptions consent"() { + def "PBS should not transmit eids from original request 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) @@ -325,12 +304,9 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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) + and: "Save account GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, false).tap { + it.config.privacy.gdpr.basicEnforcementVendors = [ALIAS] } accountDao.save(account) @@ -354,7 +330,7 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { getBasicTcfCompanySoftVendorExceptionsRequirements(P10) } - def "PBS should remove the original request with eids data when requireConsent is disabled and #enforcementRequirements.purpose have unsupported basic consent"() { + def "PBS should not transmit eids from original request 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) @@ -362,12 +338,9 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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) + and: "Save account GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, false).tap { + it.config.privacy.gdpr.basicEnforcementVendors = [GENERIC] } accountDao.save(account) @@ -381,23 +354,25 @@ class TcfBasicTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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) + getBasicTcfLegalPurposesLITEnforcementRequirements(P10) + + + getBasicTcfCompanyBasedEnforcementRequirements(P1) + + getBasicTcfCompanyBasedEnforcementRequirements(P2) + + getBasicTcfCompanyBasedEnforcementRequirements(P3) + + getBasicTcfCompanyBasedEnforcementRequirements(P5) + + getBasicTcfCompanyBasedEnforcementRequirements(P6) + + getBasicTcfCompanyBasedEnforcementRequirements(P7) + + getBasicTcfCompanyBasedEnforcementRequirements(P8) + + getBasicTcfCompanyBasedEnforcementRequirements(P9) + + getBasicTcfCompanyBasedEnforcementRequirements(P10) } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfFullTransmitEidsActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfFullTransmitEidsActivitiesSpec.groovy new file mode 100644 index 00000000000..476cc206f3b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfFullTransmitEidsActivitiesSpec.groovy @@ -0,0 +1,340 @@ +package org.prebid.server.functional.tests.privacy.tcf + + +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.config.Purpose.P1 +import static org.prebid.server.functional.model.config.Purpose.P10 +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 + +class TcfFullTransmitEidsActivitiesSpec extends TcfBaseSpec { + + private static PrebidServerService privacyPbsServiceWithMultipleGvl + + def setupSpec() { + privacyPbsServiceWithMultipleGvl = pbsServiceFactory.getService(GENERAL_PRIVACY_CONFIG, GLV_LISTS_FILES) + } + + def cleanupSpec() { + pbsServiceFactory.removeContainer(GENERAL_PRIVACY_CONFIG) + } + + def "PBS should preserve eids from original request when requireConsent is enabled and #enforcementRequirements.purpose has 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 GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true) + 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 not transmit eids from original request when #enforcementRequirements.purpose have legitimate interests 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 GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true) + 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 << getFullTcfLegalLegitimateInterestsRequirements(P4) + + getFullTcfCompanyLegitimateInterestsRequirements(P4) + } + + def "PBS should not transmit eids from original request when requireConsent is enabled and #enforcementRequirements.purpose has 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 GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true) + 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) + + getFullTcfLegalEnforcementRequirements(P2) + + getFullTcfLegalEnforcementRequirements(P3) + + getFullTcfLegalEnforcementRequirements(P5) + + getFullTcfLegalEnforcementRequirements(P6) + + getFullTcfLegalEnforcementRequirements(P7) + + getFullTcfLegalEnforcementRequirements(P8) + + getFullTcfLegalEnforcementRequirements(P9) + + getFullTcfLegalEnforcementRequirements(P10) + + + getFullTcfCompanyEnforcementRequirements(P1) + + getFullTcfCompanyEnforcementRequirements(P2) + + getFullTcfCompanyEnforcementRequirements(P3) + + getFullTcfCompanyEnforcementRequirements(P5) + + getFullTcfCompanyEnforcementRequirements(P6) + + getFullTcfCompanyEnforcementRequirements(P7) + + getFullTcfCompanyEnforcementRequirements(P8) + + getFullTcfCompanyEnforcementRequirements(P9) + + getFullTcfCompanyEnforcementRequirements(P10) + + + getFullTcfLegalLegitimateInterestsRequirements(P1) + + getFullTcfLegalLegitimateInterestsRequirements(P2) + + getFullTcfLegalLegitimateInterestsRequirements(P3) + + getFullTcfLegalLegitimateInterestsRequirements(P5) + + getFullTcfLegalLegitimateInterestsRequirements(P6) + + getFullTcfLegalLegitimateInterestsRequirements(P7) + + getFullTcfLegalLegitimateInterestsRequirements(P8) + + getFullTcfLegalLegitimateInterestsRequirements(P9) + + getFullTcfLegalLegitimateInterestsRequirements(P10) + + + getFullTcfCompanyLegitimateInterestsRequirements(P1) + + getFullTcfCompanyLegitimateInterestsRequirements(P2) + + getFullTcfCompanyLegitimateInterestsRequirements(P3) + + getFullTcfCompanyLegitimateInterestsRequirements(P5) + + getFullTcfCompanyLegitimateInterestsRequirements(P6) + + getFullTcfCompanyLegitimateInterestsRequirements(P7) + + getFullTcfCompanyLegitimateInterestsRequirements(P8) + + getFullTcfCompanyLegitimateInterestsRequirements(P9) + + getFullTcfCompanyLegitimateInterestsRequirements(P10) + } + + def "PBS should preserve eids from original request when requireConsent is enabled but bidder is exempted and #enforcementRequirements.purpose has 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 GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true, userEids.source) + 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(P2) + + getFullTcfLegalEnforcementRequirements(P3) + + getFullTcfLegalEnforcementRequirements(P4) + + getFullTcfLegalEnforcementRequirements(P5) + + getFullTcfLegalEnforcementRequirements(P6) + + getFullTcfLegalEnforcementRequirements(P7) + + getFullTcfLegalEnforcementRequirements(P8) + + getFullTcfLegalEnforcementRequirements(P9) + + getFullTcfLegalEnforcementRequirements(P10) + + + getFullTcfLegalLegitimateInterestsRequirements(P2) + + getFullTcfLegalLegitimateInterestsRequirements(P7) + + getFullTcfLegalLegitimateInterestsRequirements(P8) + + getFullTcfLegalLegitimateInterestsRequirements(P9) + + getFullTcfLegalLegitimateInterestsRequirements(P10) + } + + def "PBS should not transmit eids from original request when requireConsent is enabled, bidder is exempted 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 GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, true, userEids.source) + 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) + + + getFullTcfCompanyEnforcementRequirements(P1) + + getFullTcfCompanyEnforcementRequirements(P2) + + getFullTcfCompanyEnforcementRequirements(P3) + + getFullTcfCompanyEnforcementRequirements(P5) + + getFullTcfCompanyEnforcementRequirements(P6) + + getFullTcfCompanyEnforcementRequirements(P7) + + getFullTcfCompanyEnforcementRequirements(P8) + + getFullTcfCompanyEnforcementRequirements(P9) + + getFullTcfCompanyEnforcementRequirements(P10) + + + getFullTcfCompanyLegitimateInterestsRequirements(P1) + + getFullTcfCompanyLegitimateInterestsRequirements(P2) + + getFullTcfCompanyLegitimateInterestsRequirements(P3) + + getFullTcfCompanyLegitimateInterestsRequirements(P5) + + getFullTcfCompanyLegitimateInterestsRequirements(P6) + + getFullTcfCompanyLegitimateInterestsRequirements(P7) + + getFullTcfCompanyLegitimateInterestsRequirements(P8) + + getFullTcfCompanyLegitimateInterestsRequirements(P9) + + getFullTcfCompanyLegitimateInterestsRequirements(P10) + } + + def "PBS should preserve eids from original request when requireConsent is disabled and #enforcementRequirements.purpose has 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 GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, false) + 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(P2) + + getFullTcfLegalEnforcementRequirements(P3) + + getFullTcfLegalEnforcementRequirements(P4) + + getFullTcfLegalEnforcementRequirements(P5) + + getFullTcfLegalEnforcementRequirements(P6) + + getFullTcfLegalEnforcementRequirements(P7) + + getFullTcfLegalEnforcementRequirements(P8) + + getFullTcfLegalEnforcementRequirements(P9) + + getFullTcfLegalEnforcementRequirements(P10) + + + getFullTcfLegalLegitimateInterestsRequirements(P2) + + getFullTcfLegalLegitimateInterestsRequirements(P7) + + getFullTcfLegalLegitimateInterestsRequirements(P8) + + getFullTcfLegalLegitimateInterestsRequirements(P9) + + getFullTcfLegalLegitimateInterestsRequirements(P10) + } + + def "PBS should not transmit eids from original request 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 GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, false) + 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) + + + getFullTcfCompanyEnforcementRequirements(P1) + + getFullTcfCompanyEnforcementRequirements(P2) + + getFullTcfCompanyEnforcementRequirements(P3) + + getFullTcfCompanyEnforcementRequirements(P5) + + getFullTcfCompanyEnforcementRequirements(P6) + + getFullTcfCompanyEnforcementRequirements(P7) + + getFullTcfCompanyEnforcementRequirements(P8) + + getFullTcfCompanyEnforcementRequirements(P9) + + getFullTcfCompanyEnforcementRequirements(P10) + + + getFullTcfCompanyLegitimateInterestsRequirements(P1) + + getFullTcfCompanyLegitimateInterestsRequirements(P2) + + getFullTcfCompanyLegitimateInterestsRequirements(P3) + + getFullTcfCompanyLegitimateInterestsRequirements(P5) + + getFullTcfCompanyLegitimateInterestsRequirements(P6) + + getFullTcfCompanyLegitimateInterestsRequirements(P7) + + getFullTcfCompanyLegitimateInterestsRequirements(P8) + + getFullTcfCompanyLegitimateInterestsRequirements(P9) + + getFullTcfCompanyLegitimateInterestsRequirements(P10) + } + + def "PBS should not transmit eids when only legitimate interest is present for TCF 2.3"() { + 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 GDPR config into DB" + def account = generateAccountWithGdprEidsConfig(enforcementRequirements, bidRequest.accountId, false) + 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 << + getFullTcfLegalLegitimateInterestsRequirements(P3) + + getFullTcfLegalLegitimateInterestsRequirements(P4) + + getFullTcfLegalLegitimateInterestsRequirements(P5) + + getFullTcfLegalLegitimateInterestsRequirements(P6) + + + getFullTcfCompanyLegitimateInterestsRequirements(P3) + + getFullTcfCompanyLegitimateInterestsRequirements(P4) + + getFullTcfCompanyLegitimateInterestsRequirements(P5) + + getFullTcfCompanyLegitimateInterestsRequirements(P6) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfGeneralSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfGeneralSpec.groovy new file mode 100644 index 00000000000..d0c6d258b0e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TcfGeneralSpec.groovy @@ -0,0 +1,423 @@ +package org.prebid.server.functional.tests.privacy.tcf + +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.config.Purpose +import org.prebid.server.functional.model.privacy.EnforcementRequirement +import org.prebid.server.functional.model.response.auction.NoBidResponse +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.util.PBSUtils +import org.prebid.server.functional.util.privacy.TcfUtils + +import java.time.ZoneId +import java.time.ZonedDateTime + +import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID + +class TcfGeneralSpec extends TcfBaseSpec { + + private final static ZonedDateTime TCF_2_3_ENFORCEMENT_DATE = ZonedDateTime.parse("2026-03-01T00:00:00Z") + private final static Map STRICT_DISCLOSED_VENDOR_TREATMENT_CONFIG = TCF_BASE_CONFIG + ["gdpr.strict-disclosed-vendors-treatment": 'true'] + private final static TCF_NO_DISCLOSED_VENDORS_METRIC = 'privacy.tcf.no-disclosed-vendors' + private final static DISCLOSED_VENDORS_MESSAGE = 'Invalid TCF string: `disclosedVendors` list is empty.' + + private static PrebidServerService pbsWithoutGvlVendorsWithStrictDvTreatment + private static PrebidServerService pbsWithMultipleGvlListsWithStrictDvTreatment + + def setupSpec() { + pbsWithoutGvlVendorsWithStrictDvTreatment = pbsServiceFactory.getService(STRICT_DISCLOSED_VENDOR_TREATMENT_CONFIG + VENDOR_LIST_EMPTY_CONFIG) + pbsWithMultipleGvlListsWithStrictDvTreatment = pbsServiceFactory.getService(STRICT_DISCLOSED_VENDOR_TREATMENT_CONFIG, GLV_LISTS_FILES) + } + + def cleanupSpec() { + pbsServiceFactory.removeContainer(STRICT_DISCLOSED_VENDOR_TREATMENT_CONFIG + VENDOR_LIST_EMPTY_CONFIG) + pbsServiceFactory.removeContainer(STRICT_DISCLOSED_VENDOR_TREATMENT_CONFIG) + } + + def "PBS should accept base consent when disclosedVendors includes vendor after TCF v2.3 enforcement"() { + given: "Generic BidRequests with valid tcf string" + def enforcementRequirements = EnforcementRequirement.getDefaultBase(GENERIC_VENDOR_ID).tap { + it.created = TCF_2_3_ENFORCEMENT_DATE + it.updated = TCF_2_3_ENFORCEMENT_DATE + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + and: "Flush metric" + flushMetrics(pbsWithoutGvlVendorsWithStrictDvTreatment) + + when: "PBS processes auction requests" + def response = pbsWithoutGvlVendorsWithStrictDvTreatment.sendAuctionRequest(bidRequest) + + then: "Response should contain seatBid" + assert response?.seatbid?.bid?.flatten()?.size() == 1 + + and: "PBS shouldn't emit nbr code" + assert !response.noBidResponse + + and: "Response shouldn't contain errors or warnings" + assert !response.ext.errors + assert !response.ext.warnings + + and: "PBS shouldn't emit no disclosed vendors metric" + def metrics = pbsWithoutGvlVendorsWithStrictDvTreatment.sendCollectedMetricsRequest() + assert !metrics[TCF_NO_DISCLOSED_VENDORS_METRIC] + + and: "PBS should send bid request to bidder" + assert bidder.getBidderRequests(bidRequest.id).size() == 1 + } + + def "PBS should accept full consent when disclosedVendors includes vendor after TCF v2.3 enforcement"() { + given: "Generic BidRequests with valid tcf string" + def enforcementRequirements = EnforcementRequirement.getDefaultFull(GENERIC_VENDOR_ID, PURPOSES_ONLY_GVL_VERSION).tap { + it.created = TCF_2_3_ENFORCEMENT_DATE + it.updated = TCF_2_3_ENFORCEMENT_DATE + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + and: "Flush metric" + flushMetrics(pbsWithMultipleGvlListsWithStrictDvTreatment) + + when: "PBS processes auction requests" + def response = pbsWithMultipleGvlListsWithStrictDvTreatment.sendAuctionRequest(bidRequest) + + then: "Response should contain seatBid" + assert response?.seatbid?.bid?.flatten()?.size() == 1 + + and: "PBS shouldn't emit nbr code" + assert !response.noBidResponse + + and: "Response shouldn't contain errors or warnings" + assert !response.ext.errors + assert !response.ext.warnings + + and: "PBS shouldn't emit no disclosed vendors metric" + def metrics = pbsWithMultipleGvlListsWithStrictDvTreatment.sendCollectedMetricsRequest() + assert !metrics[TCF_NO_DISCLOSED_VENDORS_METRIC] + + and: "PBS should send bid request to bidder" + assert bidder.getBidderRequests(bidRequest.id).size() == 1 + } + + def "PBS should accept base consent regardless of disclosedVendors before TCF v2.3 enforcement"() { + given: "Generic BidRequests with valid tcf string" + def enforcementRequirements = EnforcementRequirement.getDefaultBase(GENERIC_VENDOR_ID).tap { + it.created = TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1) + it.updated = TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1) + it.disclosedVendorsId = disclosedIds + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + and: "Flush metric" + flushMetrics(pbsWithoutGvlVendorsWithStrictDvTreatment) + + when: "PBS processes auction requests" + def response = pbsWithoutGvlVendorsWithStrictDvTreatment.sendAuctionRequest(bidRequest) + + then: "Response should contain seatBid" + assert response?.seatbid?.bid?.flatten()?.size() == 1 + + and: "PBS shouldn't emit nbr code" + assert !response.noBidResponse + + and: "Response shouldn't contain errors or warnings" + assert !response.ext.errors + assert !response.ext.warnings + + and: "PBS shouldn't emit no disclosed vendors metric" + def metrics = pbsWithoutGvlVendorsWithStrictDvTreatment.sendCollectedMetricsRequest() + assert !metrics[TCF_NO_DISCLOSED_VENDORS_METRIC] + + and: "PBS should send bid request to bidder" + assert bidder.getBidderRequests(bidRequest.id).size() == 1 + + where: + disclosedIds << [null, [], [PBSUtils.getRandomNumber(0, 65535)]] + } + + def "PBS should accept full consent regardless of disclosedVendors before TCF v2.3 enforcement"() { + given: "Generic BidRequests with valid tcf string" + def enforcementRequirements = EnforcementRequirement.getDefaultFull(GENERIC_VENDOR_ID, PURPOSES_ONLY_GVL_VERSION).tap { + it.created = TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1) + it.updated = TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1) + it.disclosedVendorsId = disclosedIds + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + and: "Flush metric" + flushMetrics(pbsWithMultipleGvlListsWithStrictDvTreatment) + + when: "PBS processes auction requests" + def response = pbsWithMultipleGvlListsWithStrictDvTreatment.sendAuctionRequest(bidRequest) + + then: "Response should contain seatBid" + assert response?.seatbid?.bid?.flatten()?.size() == 1 + + and: "PBS shouldn't emit nbr code" + assert !response.noBidResponse + + and: "Response shouldn't contain errors or warnings" + assert !response.ext.errors + assert !response.ext.warnings + + and: "PBS shouldn't emit no disclosed vendors metric" + def metrics = pbsWithMultipleGvlListsWithStrictDvTreatment.sendCollectedMetricsRequest() + assert !metrics[TCF_NO_DISCLOSED_VENDORS_METRIC] + + and: "PBS should send bid request to bidder" + assert bidder.getBidderRequests(bidRequest.id).size() == 1 + + where: + disclosedIds << [null, [], [PBSUtils.getRandomNumber(0, 65535)]] + } + + def "PBS should reject base consent with warning when disclosedVendors is empty after TCF v2.3 enforcement"() { + given: "Generic BidRequests with invalid tcf string" + def enforcementRequirements = EnforcementRequirement.getDefaultBase(GENERIC_VENDOR_ID).tap { + it.disclosedVendorsId = disclosedIds + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + and: "Flush metric" + flushMetrics(pbsWithoutGvlVendorsWithStrictDvTreatment) + + when: "PBS processes auction requests" + def response = pbsWithoutGvlVendorsWithStrictDvTreatment.sendAuctionRequest(bidRequest) + + then: "Response shouldn't contain seatBid" + assert response?.seatbid?.bid?.flatten()?.isEmpty() + + and: "PBS should emit proper nbr code" + assert response.noBidResponse == NoBidResponse.UNKNOWN_ERROR + + and: "PBS response shouldn't provide proper error" + assert !response.ext.errors + + and: "PBS response should include warnings" + assert response.ext.warnings[PREBID].message == [DISCLOSED_VENDORS_MESSAGE] + + and: "PBS should emit no disclosed vendors metric" + def metrics = pbsWithoutGvlVendorsWithStrictDvTreatment.sendCollectedMetricsRequest() + assert metrics[TCF_NO_DISCLOSED_VENDORS_METRIC] == 1 + + and: "PBS should not send bid request to bidder" + assert !bidder.getBidderRequests(bidRequest.id) + + where: + disclosedIds << [null, []] + } + + def "PBS should reject full consent with warning when disclosedVendors is empty after TCF v2.3 enforcement"() { + given: "Generic BidRequests with invalid tcf string" + def enforcementRequirements = EnforcementRequirement.getDefaultFull(GENERIC_VENDOR_ID, PURPOSES_ONLY_GVL_VERSION).tap { + it.disclosedVendorsId = disclosedIds + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + and: "Flush metric" + flushMetrics(pbsWithMultipleGvlListsWithStrictDvTreatment) + + when: "PBS processes auction requests" + def response = pbsWithMultipleGvlListsWithStrictDvTreatment.sendAuctionRequest(bidRequest) + + then: "Response shouldn't contain seatBid" + assert response?.seatbid?.bid?.flatten()?.isEmpty() + + and: "PBS should emit proper nbr code" + assert response.noBidResponse == NoBidResponse.UNKNOWN_ERROR + + and: "PBS response shouldn't provide proper error" + assert !response.ext.errors + + and: "PBS response should include warnings" + assert response.ext.warnings[PREBID].message == [DISCLOSED_VENDORS_MESSAGE] + + and: "PBS should emit no disclosed vendors metric" + def metrics = pbsWithMultipleGvlListsWithStrictDvTreatment.sendCollectedMetricsRequest() + assert metrics[TCF_NO_DISCLOSED_VENDORS_METRIC] == 1 + + and: "PBS should not send bid request to bidder" + assert !bidder.getBidderRequests(bidRequest.id) + + where: + disclosedIds << [null, []] + } + + def "PBS should reject base consent without warning when disclosedVendors does not match vendor after TCF v2.3 enforcement"() { + given: "Generic BidRequests with non-corresponding TCF strings" + def enforcementRequirements = EnforcementRequirement.getDefaultBase(GENERIC_VENDOR_ID).tap { + it.disclosedVendorsId = [PBSUtils.getRandomNumber(0, 65535)] + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + when: "PBS processes auction requests" + def response = pbsWithoutGvlVendorsWithStrictDvTreatment.sendAuctionRequest(bidRequest) + + then: "Response shouldn't contain seatBid" + assert response?.seatbid?.bid?.flatten()?.isEmpty() + + and: "PBS should emit proper nbr code" + assert response.noBidResponse == NoBidResponse.UNKNOWN_ERROR + + and: "No error or warning should be emitted" + assert !response.ext.errors + assert !response.ext.warnings + + and: "PBS should not send bid request to bidder" + assert !bidder.getBidderRequests(bidRequest.id) + } + + def "PBS should reject full consent without warning when disclosedVendors does not match vendor after TCF v2.3 enforcement"() { + given: "Generic BidRequests with non-corresponding TCF strings" + def enforcementRequirements = EnforcementRequirement.getDefaultFull(GENERIC_VENDOR_ID, PURPOSES_ONLY_GVL_VERSION).tap { + it.disclosedVendorsId = [PBSUtils.getRandomNumber(0, 65535)] + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + when: "PBS processes auction requests" + def response = pbsWithMultipleGvlListsWithStrictDvTreatment.sendAuctionRequest(bidRequest) + + then: "Response shouldn't contain seatBid" + assert response?.seatbid?.bid?.flatten()?.isEmpty() + + and: "PBS should emit proper nbr code" + assert response.noBidResponse == NoBidResponse.UNKNOWN_ERROR + + and: "No error or warning should be emitted" + assert !response.ext.errors + assert !response.ext.warnings + + and: "PBS should not send bid request to bidder" + assert !bidder.getBidderRequests(bidRequest.id) + } + + def "PBS should use latest UTC timestamp between created and updated for processing TCF string"() { + given: "Generic BidRequests with invalid tcf string" + def enforcementRequirements = EnforcementRequirement.getDefaultBase(GENERIC_VENDOR_ID).tap { + it.created = createDate + it.updated = updateDate + it.disclosedVendorsId = null + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + when: "PBS processes auction requests" + def response = pbsWithoutGvlVendorsWithStrictDvTreatment.sendAuctionRequest(bidRequest) + + then: "Response shouldn't contain seatBid" + assert response?.seatbid?.bid?.flatten()?.isEmpty() + + and: "PBS should emit proper nbr code" + assert response.noBidResponse == NoBidResponse.UNKNOWN_ERROR + + and: "PBS response shouldn't provide proper error" + assert !response.ext.errors + + and: "PBS response should include warnings" + assert response.ext.warnings[PREBID].message == [DISCLOSED_VENDORS_MESSAGE] + + and: "PBS should not send bid request to bidder" + assert !bidder.getBidderRequests(bidRequest.id) + + where: + createDate | updateDate + TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1) | TCF_2_3_ENFORCEMENT_DATE + TCF_2_3_ENFORCEMENT_DATE | TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1) + + TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1).withZoneSameInstant(ZoneId.of("Pacific/Honolulu")) | TCF_2_3_ENFORCEMENT_DATE + TCF_2_3_ENFORCEMENT_DATE | TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1).withZoneSameInstant(ZoneId.of("Pacific/Honolulu")) + + TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1) | TCF_2_3_ENFORCEMENT_DATE.withZoneSameInstant(ZoneId.of("Pacific/Kiritimati")) + TCF_2_3_ENFORCEMENT_DATE.withZoneSameInstant(ZoneId.of("Pacific/Kiritimati")) | TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1) + + TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1).withZoneSameInstant(ZoneId.of("Pacific/Honolulu")) | TCF_2_3_ENFORCEMENT_DATE.withZoneSameInstant(ZoneId.of("Pacific/Kiritimati")) + TCF_2_3_ENFORCEMENT_DATE.withZoneSameInstant(ZoneId.of("Pacific/Kiritimati")) | TCF_2_3_ENFORCEMENT_DATE.minusSeconds(1).withZoneSameInstant(ZoneId.of("Pacific/Honolulu")) + } + + def "PBS should allow auction when disclosed vendors are empty under strict enforcement #serviceName"() { + given: "Generic BidRequests with invalid tcf string" + def enforcementRequirements = new EnforcementRequirement().tap { + it.disclosedVendorsId = null + it.created = null + it.updated = null + it.purpose = Purpose.P2 + it.vendorExceptions = [BidderName.GENERIC] + } + def tcfConsent = TcfUtils.getConsentString(enforcementRequirements) + def bidRequest = getGdprBidRequest(tcfConsent) + + and: "Save account config with requireConsent into DB" + def account = generateDefaultTcfAccount(bidRequest.accountId, enforcementRequirements) + accountDao.save(account) + + and: "Flush metric" + flushMetrics(pbsService) + + when: "PBS processes auction requests" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain seatBid" + assert response?.seatbid?.bid?.flatten()?.size() == 1 + + and: "PBS shouldn't emit nbr code" + assert !response.noBidResponse + + and: "Response shouldn't contain errors" + assert !response.ext.errors + + and: "PBS response should include warnings" + assert response.ext.warnings[PREBID].message == [DISCLOSED_VENDORS_MESSAGE] + + and: "PBS should emit no disclosed vendors metric" + def metrics = pbsService.sendCollectedMetricsRequest() + assert metrics[TCF_NO_DISCLOSED_VENDORS_METRIC] == 1 + + where: + pbsService | serviceName + pbsWithoutGvlVendorsWithStrictDvTreatment | "without GVL vendors" + pbsWithMultipleGvlListsWithStrictDvTreatment | "with multiple GVL lists" + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/TransmitEidsOrtbConverterActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TransmitEidsOrtbConverterActivitiesSpec.groovy similarity index 77% rename from src/test/groovy/org/prebid/server/functional/tests/privacy/TransmitEidsOrtbConverterActivitiesSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TransmitEidsOrtbConverterActivitiesSpec.groovy index e5534476613..a1871f090e3 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/TransmitEidsOrtbConverterActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/tcf/TransmitEidsOrtbConverterActivitiesSpec.groovy @@ -1,4 +1,4 @@ -package org.prebid.server.functional.tests.privacy +package org.prebid.server.functional.tests.privacy.tcf import org.prebid.server.functional.model.config.AccountGdprConfig import org.prebid.server.functional.model.request.auction.Activity @@ -7,13 +7,11 @@ 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.P10 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 @@ -22,42 +20,26 @@ 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 { +class TransmitEidsOrtbConverterActivitiesSpec extends TcfBaseSpec { - private static final Map PBS_CONFIG = SETTING_CONFIG + GENERIC_VENDOR_CONFIG + GENERIC_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 final static Map ORTB_2_5_TCF_CONFIG = TCF_BASE_CONFIG + ["adapters.generic.ortb-version": "2.5"] + private static PrebidServerService activityPbsServiceExcludeGvlWithElderOrtb 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) + activityPbsServiceExcludeGvlWithElderOrtb = pbsServiceFactory.getService(ORTB_2_5_TCF_CONFIG + VENDOR_LIST_EMPTY_CONFIG) + privacyPbsServiceWithMultipleGvlWithElderOrtb = pbsServiceFactory.getService(ORTB_2_5_TCF_CONFIG, GLV_LISTS_FILES) } def cleanupSpec() { - privacyPbsContainerWithMultipleGvlWithElderOrtb.stop() - pbsServiceFactory.removeContainer(PBS_CONFIG + ["adapters.generic.ortb-version": "2.5"]) + pbsServiceFactory.removeContainer(ORTB_2_5_TCF_CONFIG + VENDOR_LIST_EMPTY_CONFIG) + pbsServiceFactory.removeContainer(ORTB_2_5_TCF_CONFIG) } - 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"() { + def "PPBS should leave ext.eids for older ORTB versions 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) @@ -68,7 +50,7 @@ class TransmitEidsOrtbConverterActivitiesSpec extends PrivacyBaseSpec { and: "Save account config with requireConsent into DB" def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) - def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC]) def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) @@ -91,7 +73,7 @@ class TransmitEidsOrtbConverterActivitiesSpec extends PrivacyBaseSpec { 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"() { + def "PBS should remove ext.eids for older ORTB versions 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) @@ -101,7 +83,7 @@ class TransmitEidsOrtbConverterActivitiesSpec extends PrivacyBaseSpec { and: "Save account config with requireConsent into DB" def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, true) - def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC]) def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) @@ -146,7 +128,7 @@ class TransmitEidsOrtbConverterActivitiesSpec extends PrivacyBaseSpec { 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"() { + def "PBS should leave ext.eids for older ORTB versions 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) @@ -156,7 +138,7 @@ class TransmitEidsOrtbConverterActivitiesSpec extends PrivacyBaseSpec { and: "Save account config with requireConsent into DB" def purposes = TcfUtils.getPurposeConfigsForPersonalizedAds(enforcementRequirements, false) - def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC.value]) + def accountGdprConfig = new AccountGdprConfig(purposes: purposes, basicEnforcementVendors: [GENERIC]) def activity = Activity.getDefaultActivity([ActivityRule.getDefaultActivityRule(Condition.baseCondition, true)]) def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap { config.privacy.allowActivities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, activity) @@ -186,7 +168,7 @@ class TransmitEidsOrtbConverterActivitiesSpec extends PrivacyBaseSpec { 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"() { + def "PBS should leave ext.eids for older ORTB versions 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) @@ -217,7 +199,7 @@ class TransmitEidsOrtbConverterActivitiesSpec extends PrivacyBaseSpec { 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"() { + def "PBS should remove ext.eids for older ORTB versions 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) @@ -249,7 +231,7 @@ class TransmitEidsOrtbConverterActivitiesSpec extends PrivacyBaseSpec { 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"() { + def "PBS should leave ext.eids for older ORTB versions 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) 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 cdb6fce665b..56953549575 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 @@ -6,6 +6,8 @@ import com.iabtcf.utils.BitSetIntIterable import org.prebid.server.functional.model.config.Purpose import org.prebid.server.functional.util.PBSUtils +import java.time.ZonedDateTime + import static org.prebid.server.functional.util.privacy.TcfConsent.TcfPolicyVersion.TCF_POLICY_V2 class TcfConsent implements ConsentString { @@ -96,6 +98,21 @@ class TcfConsent implements ConsentString { this } + Builder setDisclosedVendors(List vendorIds) { + vendorIds.forEach { id -> tcStringEncoder.addDisclosedVendors(id) } + this + } + + Builder setCreateTime(ZonedDateTime time) { + tcStringEncoder.created(time.toInstant()) + this + } + + Builder setUpdatedTime(ZonedDateTime time) { + tcStringEncoder.lastUpdated(time.toInstant()) + this + } + Builder setPublisherRestrictionEntry(PurposeId purposeId, List restrictionTypes, Integer vendorId) { restrictionTypes.each { restrictionType -> def publisherRestrictionEntry = PublisherRestrictionEntry 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 index e5101390060..5100c01fde8 100644 --- a/src/test/groovy/org/prebid/server/functional/util/privacy/TcfUtils.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/privacy/TcfUtils.groovy @@ -4,6 +4,7 @@ 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 org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.model.config.PurposeEnforcement.NO import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId @@ -11,65 +12,90 @@ import static org.prebid.server.functional.util.privacy.TcfConsent.TcfPolicyVers class TcfUtils { - static Map getPurposeConfigsForPersonalizedAds(EnforcementRequirement enforcementRequirements, boolean requireConsent = false, List eidsExceptions = []) { - def purpose = enforcementRequirements.purpose + static Map getPurposeConfigsForPersonalizedAds( + EnforcementRequirement requirement, + boolean requireConsent = false, + List eidsExceptions = []) { + + def purposes = new EnumMap(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 + purposes[Purpose.P2] = buildPurposeConfig(new EnforcementRequirement( + enforcePurpose: NO, + enforceVendor: false + )) + + def eid = buildPurposeEid(requireConsent, eidsExceptions) + def config = buildPurposeConfig(requirement) + if (requirement.purpose == Purpose.P4) { + config.eid = eid } else { - purposes[purpose] = purposeConfig - purposes[Purpose.P4] = new PurposeConfig(eid: purposeEid) + purposes[Purpose.P4] = new PurposeConfig(eid: eid) } + purposes[requirement.purpose] = config + 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 + private static PurposeConfig buildPurposeConfig(EnforcementRequirement requirement) { + new PurposeConfig().tap { + if (PBSUtils.randomBoolean) { + it.enforcePurposeSnakeCase = requirement.enforcePurpose + it.enforceVendorsSnakeCase = requirement.enforceVendor + it.vendorExceptionsSnakeCase = requirement.vendorExceptions?.value + } else { + it.enforcePurpose = requirement.enforcePurpose + it.enforceVendors = requirement.enforceVendor + it.vendorExceptions = requirement.vendorExceptions?.value + } + } + } + + private static PurposeEid buildPurposeEid(boolean requireConsent, List exceptions) { + new PurposeEid().tap { + it.exceptions = exceptions + + if (PBSUtils.randomBoolean) { + it.requireConsentKebabCase = requireConsent + } else { + it.requireConsent = requireConsent + } + } + } + + + static ConsentString getConsentString(EnforcementRequirement requirement) { + def purposeConsent = requirement.enforcePurpose != NO ? requirement.purpose : null + def purposeId = purposeConsent ? PurposeId.convertPurposeToPurposeId(purposeConsent) : null + def currentPurposeId = PurposeId.convertPurposeToPurposeId(requirement.purpose) def builder = new TcfConsent.Builder() - if (purposeConsent != null && !purposesLITransparency) { - builder.setPurposesConsent(PurposeId.convertPurposeToPurposeId(purposeConsent)) + + if (purposeConsent && !requirement.purposesLITransparency) { + builder.setPurposesConsent(purposeId) } - if (vendorConsentBitField != null) { - builder.setVendorConsent(vendorConsentBitField) + if (requirement.vendorConsentBitField) { + builder.setVendorConsent(requirement.vendorConsentBitField) } - if (purposesLITransparency) { - builder.setPurposesLITransparency(PurposeId.convertPurposeToPurposeId(enforcementRequirements.getPurpose())) + if (requirement.purposesLITransparency) { + builder.setPurposesLITransparency(currentPurposeId) } - if (purposeConsent != null && restrictionType != null && vendorIdGvl != null) { - builder.setPublisherRestrictionEntry(PurposeId.convertPurposeToPurposeId(purposeConsent), restrictionType, vendorIdGvl) + if (purposeConsent && requirement.restrictionType && requirement.vendorIdGvl) { + builder.setPublisherRestrictionEntry(purposeId, requirement.restrictionType, requirement.vendorIdGvl) } - if (vendorIdGvl != null) { - builder.setVendorLegitimateInterest(vendorIdGvl) + if (requirement.vendorIdGvl) { + builder.setVendorLegitimateInterest(requirement.vendorIdGvl) } - builder.setVendorListVersion(enforcementRequirements.vendorListVersion ?: TCF_POLICY_V2.vendorListVersion) - return builder.build() - } - - static Map getPurposeConfigsForPersonalizedAdsWithSnakeCase(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(enforcePurposeSnakeCase: NO, enforceVendors: false)] - def purposeConfig = new PurposeConfig(enforcePurposeSnakeCase: enforcementRequirements.enforcePurpose, - enforceVendorsSnakeCase: enforcementRequirements?.enforceVendor, - vendorExceptionsSnakeCase: 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) + if (requirement.disclosedVendorsId) { + builder.setDisclosedVendors(requirement.disclosedVendorsId) } - purposes + if (requirement.created) { + builder.setCreateTime(requirement.created) + } + if (requirement.updated) { + builder.setUpdatedTime(requirement.updated) + } + builder.setVendorListVersion(requirement.vendorListVersion ?: TCF_POLICY_V2.vendorListVersion) + + builder.build() } } diff --git a/src/test/groovy/org/prebid/server/functional/util/privacy/gpp/v2/TcfEuV2Consent.groovy b/src/test/groovy/org/prebid/server/functional/util/privacy/gpp/v2/TcfEuV2Consent.groovy index fe72ec0d773..cd69583e9fc 100644 --- a/src/test/groovy/org/prebid/server/functional/util/privacy/gpp/v2/TcfEuV2Consent.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/privacy/gpp/v2/TcfEuV2Consent.groovy @@ -50,6 +50,11 @@ class TcfEuV2Consent extends GppConsent { this } + Builder setDisclosedVendors(List vendorIds) { + fieldValues[TcfEuV2Field.VENDORS_DISCLOSED] = vendorIds + this + } + @Override TcfEuV2Consent build() { new TcfEuV2Consent(section, fieldValues) 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 32de14a2390..bb0420f580a 100644 --- a/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/requestfactory/AmpRequestFactoryTest.java @@ -44,6 +44,7 @@ import org.prebid.server.model.Endpoint; import org.prebid.server.model.HttpRequestContext; import org.prebid.server.privacy.ccpa.Ccpa; +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; @@ -123,6 +124,8 @@ public class AmpRequestFactoryTest extends VertxTest { private DebugResolver debugResolver; @Mock(strictness = LENIENT) private GeoLocationServiceWrapper geoLocationServiceWrapper; + @Mock(strictness = LENIENT) + private TcfDefinerService tcfDefinerService; private AmpRequestFactory target; @@ -207,7 +210,8 @@ public void setUp() { ampPrivacyContextFactory, debugResolver, jacksonMapper, - geoLocationServiceWrapper); + geoLocationServiceWrapper, + tcfDefinerService); } @Test @@ -1244,6 +1248,7 @@ public void shouldReturnBidRequestWithoutUserExtConsentWhenConsentTypeIsNotTcfAn public void shouldReturnBidRequestWithUserExtConsentWhenGdprConsentIsValidAndConsentTypeIsNotPresent() { // given routingContext.queryParams().add("gdpr_consent", "BONV8oqONXwgmADACHENAO7pqzAAppY"); + given(tcfDefinerService.isConsentStringValid(any())).willReturn(true); givenBidRequest(); diff --git a/src/test/java/org/prebid/server/it/ApplicationTest.java b/src/test/java/org/prebid/server/it/ApplicationTest.java index 3a3710c6e6e..54d402fbc4b 100644 --- a/src/test/java/org/prebid/server/it/ApplicationTest.java +++ b/src/test/java/org/prebid/server/it/ApplicationTest.java @@ -293,6 +293,7 @@ public void cookieSyncShouldReturnBidderStatusWithExpectedUsersyncInfo() { .tcfPolicyVersion(2) .addPurposesConsent(BitSetIntIterable.from(1)) .addVendorConsent(BitSetIntIterable.from(1, 32, 52)) + .addDisclosedVendors(BitSetIntIterable.from(1, 32, 52)) .encode(); // when diff --git a/src/test/java/org/prebid/server/privacy/gdpr/DisclosedVendorsStrictnessTest.java b/src/test/java/org/prebid/server/privacy/gdpr/DisclosedVendorsStrictnessTest.java new file mode 100644 index 00000000000..83730e20e0f --- /dev/null +++ b/src/test/java/org/prebid/server/privacy/gdpr/DisclosedVendorsStrictnessTest.java @@ -0,0 +1,169 @@ +package org.prebid.server.privacy.gdpr; + +import com.iabtcf.decoder.TCString; +import com.iabtcf.encoder.TCStringEncoder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.prebid.server.settings.model.GdprConfig; + +import java.time.Instant; +import java.time.Month; +import java.time.Year; +import java.time.ZoneOffset; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DisclosedVendorsStrictnessTest { + + private static final Instant CUTOFF_DATE = Year.of(2026) + .atMonth(Month.MARCH) + .atDay(1) + .atStartOfDay() + .toInstant(ZoneOffset.UTC); + + private DisclosedVendorsStrictness target; + + @BeforeEach + public void setUp() { + target = target(true); + } + + @Test + public void isValidShouldReturnTrueOnInvalidIfStrictnessDisabled() { + // given + target = target(false); + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .toTCString(); + + // when and then + assertThat(target.isValid(tcString)).isTrue(); + } + + @Test + public void isValidShouldReturnFalseIfStrictnessEnabledByDefault() { + // given + target = new DisclosedVendorsStrictness(null); + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .toTCString(); + + // when and then + assertThat(target.isValid(tcString)).isFalse(); + } + + @Test + public void isValidShouldReturnTrueOnInvalidIfLatestUpdateBeforeCutoffDate() { + // given + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .created(CUTOFF_DATE.minusNanos(1L)) + .lastUpdated(CUTOFF_DATE.minusNanos(1L)) + .toTCString(); + + // when and then + assertThat(target.isValid(tcString)).isTrue(); + } + + @Test + public void isValidShouldReturnFalseIfLatestUpdateAfterCutoffDate() { + // given + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .created(CUTOFF_DATE.minusNanos(1L)) + .lastUpdated(CUTOFF_DATE) + .toTCString(); + + // when and then + assertThat(target.isValid(tcString)).isFalse(); + } + + @Test + public void isValidShouldReturnTrueIfDisclosedVendorsPresent() { + // given + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .addDisclosedVendors(1) + .toTCString(); + + // when and then + assertThat(target.isValid(tcString)).isTrue(); + } + + @Test + public void isVendorDisclosedShouldReturnTrueOnNotDisclosedIfStrictnessDisabled() { + // given + target = target(false); + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .toTCString(); + + // when and then + assertThat(target.isVendorDisclosed(tcString, 1)).isTrue(); + } + + @Test + public void isVendorDisclosedShouldReturnFalseIfStrictnessEnabledByDefault() { + // given + target = new DisclosedVendorsStrictness(null); + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .toTCString(); + + // when and then + assertThat(target.isVendorDisclosed(tcString, 1)).isFalse(); + } + + @Test + public void isVendorDisclosedShouldReturnFalseOnNull() { + // given + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .toTCString(); + + // when and then + assertThat(target.isVendorDisclosed(tcString, null)).isFalse(); + } + + @Test + public void isVendorDisclosedShouldReturnTrueOnNotDisclosedIfLatestUpdateBeforeCutoffDate() { + // given + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .created(CUTOFF_DATE.minusNanos(1L)) + .lastUpdated(CUTOFF_DATE.minusNanos(1L)) + .toTCString(); + + // when and then + assertThat(target.isVendorDisclosed(tcString, 1)).isTrue(); + } + + @Test + public void isVendorDisclosedShouldReturnFalseIfLatestUpdateAfterCutoffDate() { + // given + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .created(CUTOFF_DATE.minusNanos(1L)) + .lastUpdated(CUTOFF_DATE) + .toTCString(); + + // when and then + assertThat(target.isVendorDisclosed(tcString, 1)).isFalse(); + } + + @Test + public void isVendorDisclosedShouldReturnTrueIfDisclosed() { + // given + final TCString tcString = TCStringEncoder.newBuilder() + .version(2) + .addDisclosedVendors(1) + .toTCString(); + + // when and then + assertThat(target.isVendorDisclosed(tcString, 1)).isTrue(); + } + + private static DisclosedVendorsStrictness target(boolean enabled) { + return new DisclosedVendorsStrictness(GdprConfig.builder().strictDisclosedVendorsTreatment(enabled).build()); + } +} 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 e63ad8f530d..bfa655c8203 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java @@ -45,6 +45,7 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -55,6 +56,8 @@ public class TcfDefinerServiceTest { private static final String EEA_COUNTRY = "ua"; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; @Mock private Tcf2Service tcf2Service; @Mock @@ -70,6 +73,8 @@ public class TcfDefinerServiceTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isValid(any())).willReturn(true); + final GdprConfig gdprConfig = GdprConfig.builder() .defaultValue("1") .enabled(true) @@ -81,6 +86,7 @@ public void setUp() { target = new TcfDefinerService( gdprConfig, singleton(EEA_COUNTRY), + disclosedVendorsStrictness, tcf2Service, geoLocationServiceWrapper, bidderCatalog, @@ -96,6 +102,7 @@ public void resolveTcfContextShouldReturnContextWhenGdprIsDisabled() { target = new TcfDefinerService( gdprConfig, singleton(EEA_COUNTRY), + disclosedVendorsStrictness, tcf2Service, geoLocationServiceWrapper, bidderCatalog, @@ -175,6 +182,7 @@ public void resolveTcfContextShouldCheckServiceConfigValueWhenRequestTypeIsUnkno target = new TcfDefinerService( gdprConfig, singleton(EEA_COUNTRY), + disclosedVendorsStrictness, tcf2Service, geoLocationServiceWrapper, bidderCatalog, @@ -208,6 +216,7 @@ public void resolveTcfContextShouldConsiderTcfVersionOneAsCorruptedVersionTwo() target = new TcfDefinerService( gdprConfig, singleton(EEA_COUNTRY), + disclosedVendorsStrictness, tcf2Service, geoLocationServiceWrapper, bidderCatalog, @@ -241,6 +250,7 @@ public void resolveTcfContextShouldEmitWarningOnTcfConsentWithTcfPolicyVersionGr target = new TcfDefinerService( gdprConfig, singleton(EEA_COUNTRY), + disclosedVendorsStrictness, tcf2Service, geoLocationServiceWrapper, bidderCatalog, @@ -285,6 +295,7 @@ public void resolveTcfContextShouldConsiderPresenceOfConsentStringAsInScope() { target = new TcfDefinerService( gdprConfig, singleton(EEA_COUNTRY), + disclosedVendorsStrictness, tcf2Service, geoLocationServiceWrapper, bidderCatalog, @@ -327,6 +338,7 @@ public void resolveTcfContextShouldUseEeaListFromAccountConfig() { target = new TcfDefinerService( gdprConfig, singleton(EEA_COUNTRY), + disclosedVendorsStrictness, tcf2Service, geoLocationServiceWrapper, bidderCatalog, @@ -447,6 +459,7 @@ public void resolveTcfContextShouldConsultDefaultValueWhenGeoLookupFailed() { target = new TcfDefinerService( gdprConfig, singleton(EEA_COUNTRY), + disclosedVendorsStrictness, tcf2Service, geoLocationServiceWrapper, bidderCatalog, @@ -478,6 +491,7 @@ public void resolveTcfContextShouldReturnTcfContextWithConsentValidAsTrue() { .gdpr("1") .consentString("CPBCa-mPBCa-mAAAAAENA0CAAEAAAAAAACiQAaQAwAAgAgABoAAAAAA") .build(); + // when final Future result = target.resolveTcfContext( privacy, null, null, MetricName.setuid, null, null); @@ -534,6 +548,32 @@ public void resolveTcfContextShouldIncrementInvalidConsentStringMetric() { verify(metrics).updatePrivacyTcfInvalidMetric(); } + @Test + public void resolveTcfContextShouldIncrementInvalidConsentStringMetricWhenDisclosedVendorsIsInvalid() { + // given + given(disclosedVendorsStrictness.isValid(any())).willReturn(false); + + final Privacy privacy = Privacy.builder() + .gdpr("1") + .consentString("CPBCa-mPBCa-mAAAAAENA0CAAEAAAAAAACiQAaQAwAAgAgABoAAAAAA") + .build(); + + // when + final Future result = target.resolveTcfContext( + privacy, null, null, MetricName.setuid, null, null); + + // then + assertThat(result).isSucceeded(); + assertThat(result.result()).satisfies(context -> { + assertThat(context.isInGdprScope()).isTrue(); + assertThat(context.getConsentString()).isEqualTo("CPBCa-mPBCa-mAAAAAENA0CAAEAAAAAAACiQAaQAwAAgAgABoAAAAAA"); + assertThat(context.isConsentValid()).isFalse(); + assertThat(context.getWarnings()).containsExactly("Invalid TCF string: `disclosedVendors` list is empty."); + }); + + verify(metrics).updatePrivacyTcfNoDisclosedVendorsMetric(); + } + @Test public void resultForVendorIdsShouldNotSetTcfRequestsAndTcfGeoMetricsWhenConsentIsNotValid() { // given @@ -645,19 +685,27 @@ public void resultForBidderNamesShouldReturnTcfResponseFromTcf2ServiceWhenConsen @Test public void isConsentStringValidShouldReturnTrueWhenStringIsValid() { // when and then - assertThat(TcfDefinerService.isConsentStringValid("CPBCa-mPBCa-mAAAAAENA0CAAEAAAAAAACiQAaQAwAAgAgABoAAAAAA")) - .isTrue(); + assertThat(target.isConsentStringValid("CPBCa-mPBCa-mAAAAAENA0CAAEAAAAAAACiQAaQAwAAgAgABoAAAAAA")).isTrue(); } @Test public void isConsentStringValidShouldReturnFalseWhenStringIsNull() { // when and then - assertThat(TcfDefinerService.isConsentStringValid(null)).isFalse(); + assertThat(target.isConsentStringValid(null)).isFalse(); } @Test public void isConsentStringValidShouldReturnFalseWhenStringNotValid() { // when and then - assertThat(TcfDefinerService.isConsentStringValid("invalid")).isFalse(); + assertThat(target.isConsentStringValid("invalid")).isFalse(); + } + + @Test + public void isConsentStringValidShouldReturnFalseWhenDisclosedVendorsIsInvalid() { + // given + given(disclosedVendorsStrictness.isValid(any())).willReturn(false); + + // when and then + assertThat(target.isConsentStringValid("CPBCa-mPBCa-mAAAAAENA0CAAEAAAAAAACiQAaQAwAAgAgABoAAAAAA")).isFalse(); } } 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 index 49a9fafe521..ba6d68937d9 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose01StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.ONE; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose01StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose01Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassEmptyListWithEnforcementsWhenAllBiddersAreExcluded() { // given 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 index 97dca14fc22..2605f2cf520 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose02StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.TWO; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose02StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose02Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { // given 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 index 092c789117d..0ab56733928 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose03StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.THREE; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose03StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose03Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { // given 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 index c93f948b6a7..800e92b9259 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose04StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.FOUR; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose04StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose04Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { // given 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 index 95ac9acea3a..e34a08de7f3 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose05StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.FIVE; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose05StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose05Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { // given 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 index 88b54b09e04..3690e5c9d07 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose06StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.SIX; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose06StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose06Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { // given 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 index 2415d3554f2..3c2508d026b 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose07StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.SEVEN; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose07StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose07Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { // given 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 index 8ea178bfe88..cbfb992b695 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose08StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.EIGHT; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose08StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose08Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { // given 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 index 8a3e2c56834..db40f9d078b 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose09StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.NINE; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose09StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose09Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { // given 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 index fb3cca61beb..0a9e24a4f1c 100644 --- 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 @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -26,7 +27,9 @@ 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.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,9 @@ public class Purpose10StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.TEN; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +57,10 @@ public class Purpose10StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose10Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); @@ -159,6 +168,45 @@ public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBi false); } + @Test + public void processTypePurposeStrategyShouldPassDisclosedListWithEnforcementsAndExcludeBiddersToBaseType() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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, + singletonList(vendorPermissionWitGvl1), + singletonList(vendorPermissionWitGvl2), + false); + } + @Test public void processTypePurposeStrategyShouldPassListWithEnforcementsAndExcludeBiddersToFullType() { // given 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 a103c61c9cd..bb98e59f911 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 @@ -11,6 +11,8 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -29,32 +31,32 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.BDDMockito.given; -import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) public class FullEnforcePurposeStrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.ONE; private FullEnforcePurposeStrategy target; - @Mock(strictness = LENIENT) + @Mock private TCString tcString; - @Mock(strictness = LENIENT) + @Mock private IntIterable allowedVendors; - @Mock(strictness = LENIENT) + @Mock private IntIterable allowedVendorsLI; - @Mock(strictness = LENIENT) + @Mock private IntIterable purposesConsent; - @Mock(strictness = LENIENT) + @Mock private IntIterable purposesLI; @Spy private IntIterable vendorIds; - @Mock(strictness = LENIENT) + @Mock private PublisherRestriction publisherRestriction; @BeforeEach @@ -351,6 +353,87 @@ public void shouldAllowWhenInGvlPurposeLIAndPurposeLI() { verify(purposesLI).contains(PURPOSE_CODE.code()); } + @Test + public void shouldAllowWhenInGvlPurposeLIAndPurposeLIAndPurposeSupportsLI() { + // given + final Vendor vendorGvl = Vendor.builder() + .legIntPurposes(EnumSet.of( + PurposeCode.ONE, + PurposeCode.TWO, + PurposeCode.SEVEN, + PurposeCode.EIGHT, + PurposeCode.NINE, + PurposeCode.TEN)) + .flexiblePurposes(EnumSet.noneOf(PurposeCode.class)) + .build(); + + final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); + final List vendorPermissions = singletonList(vendorPermissionWitGvl); + + given(purposesLI.contains(anyInt())).willReturn(true); + given(vendorIds.intIterator()).willReturn(intIterator(1)); + + // when and then + assertThat(target.allowedByTypeStrategy(PurposeCode.ONE, tcString, vendorPermissions, emptyList(), false)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.TWO, tcString, vendorPermissions, emptyList(), false)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.SEVEN, tcString, vendorPermissions, emptyList(), false)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.EIGHT, tcString, vendorPermissions, emptyList(), false)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.NINE, tcString, vendorPermissions, emptyList(), false)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.TEN, tcString, vendorPermissions, emptyList(), false)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + + verify(purposesLI).contains(PurposeCode.ONE.code()); + verify(purposesLI).contains(PurposeCode.TWO.code()); + verify(purposesLI).contains(PurposeCode.SEVEN.code()); + verify(purposesLI).contains(PurposeCode.EIGHT.code()); + verify(purposesLI).contains(PurposeCode.NINE.code()); + verify(purposesLI).contains(PurposeCode.TEN.code()); + } + + @Test + public void shouldEmptyWhenInGvlPurposeLIAndPurposeLIAndPurposeDoesNotSupportLI() { + // given + final Vendor vendorGvl = Vendor.builder() + .legIntPurposes(EnumSet.of( + PurposeCode.THREE, + PurposeCode.FOUR, + PurposeCode.FIVE, + PurposeCode.SIX)) + .flexiblePurposes(EnumSet.noneOf(PurposeCode.class)) + .build(); + + final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, vendorGvl); + final List permissions = singletonList(vendorPermissionWitGvl); + + given(purposesLI.contains(anyInt())).willReturn(true); + given(vendorIds.intIterator()).willReturn(intIterator(1)); + + // when and then + assertThat(target.allowedByTypeStrategy(PurposeCode.THREE, tcString, permissions, emptyList(), false)) + .isEmpty(); + assertThat(target.allowedByTypeStrategy(PurposeCode.FOUR, tcString, permissions, emptyList(), false)) + .isEmpty(); + assertThat(target.allowedByTypeStrategy(PurposeCode.FIVE, tcString, permissions, emptyList(), false)) + .isEmpty(); + assertThat(target.allowedByTypeStrategy(PurposeCode.SIX, tcString, permissions, emptyList(), false)) + .isEmpty(); + + verifyNoInteractions(purposesLI); + } + @Test public void shouldAllowWhenInGvlPurposeLIAndPurposeLIAndRequireLI() { // given 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 571fb93c7f0..7233f15edbf 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 @@ -32,7 +32,7 @@ public class NoEnforcePurposeStrategyTest { private NoEnforcePurposeStrategy target; - @Mock + @Mock(strictness = LENIENT) private TCString tcString; @Mock(strictness = LENIENT) private IntIterable allowedVendors; @@ -131,6 +131,56 @@ public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedA assertThat(result).usingRecursiveFieldByFieldElementComparator().containsOnly(vendorPermission); } + @Test + public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeSupportsLIAndVendorEnforced() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); + final List vendorPurposeWithGvls = singletonList(vendorPermissionWitGvl); + + given(allowedVendorsLI.contains(anyInt())).willReturn(true); + + // when and then + assertThat(target.allowedByTypeStrategy(PurposeCode.ONE, tcString, vendorPurposeWithGvls, emptyList(), true)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.TWO, tcString, vendorPurposeWithGvls, emptyList(), true)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.SEVEN, tcString, vendorPurposeWithGvls, emptyList(), true)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.EIGHT, tcString, vendorPurposeWithGvls, emptyList(), true)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.NINE, tcString, vendorPurposeWithGvls, emptyList(), true)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + assertThat(target.allowedByTypeStrategy(PurposeCode.TEN, tcString, vendorPurposeWithGvls, emptyList(), true)) + .usingRecursiveFieldByFieldElementComparator() + .containsOnly(vendorPermission); + } + + @Test + public void allowedByTypeStrategyShouldReturnExpectedValueWhenPurposeDoesNotSupportLIAndVendorEnforced() { + // given + final VendorPermission vendorPermission = VendorPermission.of(1, null, PrivacyEnforcementAction.restrictAll()); + final VendorPermissionWithGvl vendorPermissionWitGvl = withGvl(vendorPermission, Vendor.empty(1)); + final List vendorPurposeWithGvls = singletonList(vendorPermissionWitGvl); + + given(allowedVendorsLI.contains(anyInt())).willReturn(true); + + // when and then + assertThat(target.allowedByTypeStrategy(PurposeCode.THREE, tcString, vendorPurposeWithGvls, emptyList(), true)) + .isEmpty(); + assertThat(target.allowedByTypeStrategy(PurposeCode.FOUR, tcString, vendorPurposeWithGvls, emptyList(), true)) + .isEmpty(); + assertThat(target.allowedByTypeStrategy(PurposeCode.FIVE, tcString, vendorPurposeWithGvls, emptyList(), true)) + .isEmpty(); + assertThat(target.allowedByTypeStrategy(PurposeCode.SIX, tcString, vendorPurposeWithGvls, emptyList(), true)) + .isEmpty(); + } + @Test public void allowedByTypeStrategyShouldReturnExpectedValueWhenVendorLIIsAllowedAndVendorIsNotEnforced() { // given 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 11af8d78a50..7408d1e7131 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 @@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.privacy.gdpr.DisclosedVendorsStrictness; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.settings.model.SpecialFeature; @@ -17,7 +18,9 @@ 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.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.verify; @@ -28,6 +31,9 @@ public class SpecialFeaturesOneStrategyTest { private static final int SPECIAL_FEATURE_ID = 1; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + private SpecialFeaturesOneStrategy target; @Mock(strictness = LENIENT) @@ -37,10 +43,11 @@ public class SpecialFeaturesOneStrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); given(tcString.getSpecialFeatureOptIns()).willReturn(specialFeatureOptIns); given(specialFeatureOptIns.contains(anyInt())).willReturn(false); - target = new SpecialFeaturesOneStrategy(); + target = new SpecialFeaturesOneStrategy(disclosedVendorsStrictness); } @Test @@ -150,6 +157,37 @@ public void processSpecialFeaturesStrategyShouldAllowExcludedAndOptIn() { verify(specialFeatureOptIns).contains(SPECIAL_FEATURE_ID); } + @Test + public void processSpecialFeaturesStrategyShouldAllowExcludedAndDisclosedOptIn() { + // given + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(false); + given(disclosedVendorsStrictness.isVendorDisclosed(any(), eq(1))).willReturn(true); + + 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 = asList( + vendorPermission1, + vendorPermission2, + vendorPermission3); + + final SpecialFeature specialFeature = SpecialFeature.of(true, singletonList("b1")); + + given(specialFeatureOptIns.contains(SPECIAL_FEATURE_ID)).willReturn(true); + + // when + target.processSpecialFeaturesStrategy(tcString, specialFeature, vendorPermissions); + + // then + final VendorPermission vendorPermission1Changed = VendorPermission.of(1, null, allowSpecialFeature()); + final VendorPermission vendorPermission2Changed = VendorPermission.of(2, "b1", allowSpecialFeature()); + assertThat(vendorPermission1).isEqualTo(vendorPermission1Changed); + assertThat(vendorPermission2).isEqualTo(vendorPermission2Changed); + assertThat(vendorPermission3).isEqualTo(VendorPermission.of(3, "b3", PrivacyEnforcementAction.restrictAll())); + + verify(specialFeatureOptIns).contains(SPECIAL_FEATURE_ID); + } + private static PrivacyEnforcementAction allowSpecialFeature() { final PrivacyEnforcementAction privacyEnforcementAction = PrivacyEnforcementAction.restrictAll(); privacyEnforcementAction.setMaskDeviceIp(false);