From 8c78ce54e39d6d8f7ff82f2a7f83b2e27b341081 Mon Sep 17 00:00:00 2001 From: Danylo Date: Thu, 26 Mar 2026 13:17:21 +0100 Subject: [PATCH 01/12] Ignore LI for purposes 3, 4, 5 and 6. --- .../purpose/typestrategies/EnforcePurposeStrategy.java | 4 ++++ 1 file changed, 4 insertions(+) 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..fa64ccdf447 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 @@ -43,6 +43,10 @@ protected boolean isAllowedByLegitimateInterest(PurposeCode purpose, boolean isEnforceVendor, TCString tcString) { + switch (purpose) { + case THREE, FOUR, FIVE, SIX: return false; + } + final IntIterable purposesConsent = tcString.getPurposesLITransparency(); final IntIterable vendorConsent = tcString.getVendorLegitimateInterest(); From 36cf442d8cd3a66faea06dc4375699cd521589a9 Mon Sep 17 00:00:00 2001 From: Danylo Date: Thu, 26 Mar 2026 13:34:37 +0100 Subject: [PATCH 02/12] Ignore LI for purposes 3, 4, 5 and 6 in `NoEnforcePurposeStrategy` --- .../typestrategies/EnforcePurposeStrategy.java | 15 +++++++++++++-- .../typestrategies/NoEnforcePurposeStrategy.java | 5 ++++- 2 files changed, 17 insertions(+), 3 deletions(-) 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 fa64ccdf447..d9729d991d6 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,26 @@ 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, + PurposeCode.UNKNOWN)); + public abstract Stream allowedByTypeStrategy( PurposeCode purpose, TCString vendorConsent, @@ -43,8 +54,8 @@ protected boolean isAllowedByLegitimateInterest(PurposeCode purpose, boolean isEnforceVendor, TCString tcString) { - switch (purpose) { - case THREE, FOUR, FIVE, SIX: return false; + if (!LI_SUPPORTED_PURPOSES.contains(purpose)) { + return false; } final IntIterable purposesConsent = tcString.getPurposesLITransparency(); 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) From 574aed7bbcb63f6f09b3fddf1b1292720f34fe77 Mon Sep 17 00:00:00 2001 From: Danylo Date: Fri, 27 Mar 2026 01:15:50 +0100 Subject: [PATCH 03/12] Refactor --- .../server/settings/model/GdprConfig.java | 2 +- .../config/PrivacyServiceConfiguration.java | 7 +++-- .../spring/config/ServiceConfiguration.java | 26 +++++++++---------- .../config/bidder/TaboolaConfiguration.java | 5 ++-- 4 files changed, 20 insertions(+), 20 deletions(-) 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..10487677d82 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; 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..0cdc87ae7d3 100644 --- a/src/main/java/org/prebid/server/spring/config/PrivacyServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/PrivacyServiceConfiguration.java @@ -181,11 +181,10 @@ 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 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..f86547199a0 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, 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(); } } From 0e352a24e7f5cc68d45c39cba62a45035268e6a1 Mon Sep 17 00:00:00 2001 From: Danylo Date: Fri, 27 Mar 2026 02:20:54 +0100 Subject: [PATCH 04/12] Add support for `disclosedVendors` property. --- .../server/privacy/gdpr/Tcf2Service.java | 14 ++++++-- .../privacy/gdpr/TcfDefinerService.java | 34 +++++++++++++++++-- .../server/privacy/gdpr/VendorIdResolver.java | 2 +- .../EnforcePurposeStrategy.java | 3 +- 4 files changed, 45 insertions(+), 8 deletions(-) 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..409e178867a 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java @@ -99,12 +99,19 @@ private Future> permissionsForInternal(Collection disclosedVendors = vendorPermissions.stream() + .filter(permission -> TcfDefinerService.isVendorDisclosed(tcfConsent, permission.getVendorId())) + .toList(); + if (disclosedVendors.isEmpty()) { + return Future.succeededFuture(vendorPermissions); + } + final Purposes mergedPurposes = mergeAccountPurposes(accountGdprConfig); final PurposeOneTreatmentInterpretation mergedPurposeOneTreatmentInterpretation = mergePurposeOneTreatmentInterpretation(accountGdprConfig); final VendorPermissionsByType vendorPermissionsByType = - toVendorPermissionsByType(vendorPermissions, accountGdprConfig); + toVendorPermissionsByType(disclosedVendors, accountGdprConfig); return versionedVendorListService.forConsent(tcfConsent) .compose(vendorGvlPermissions -> processSupportedPurposeStrategies( @@ -120,8 +127,9 @@ private Future> permissionsForInternal(Collection enforcePurpose4IfRequired(mergedPurposes, vendorPermissionsByType)) .map(ignored -> processSupportedSpecialFeatureStrategies( tcfConsent, - vendorPermissions, - mergeAccountSpecialFeatures(accountGdprConfig))); + disclosedVendors, + mergeAccountSpecialFeatures(accountGdprConfig))) + .map(vendorPermissions); } private static VendorPermissionsByType toVendorPermissionsByType( 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..2776957f362 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java @@ -29,6 +29,10 @@ import org.prebid.server.settings.model.GdprConfig; import org.prebid.server.util.ObjectUtil; +import java.time.Instant; +import java.time.Month; +import java.time.Year; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -56,6 +60,11 @@ public class TcfDefinerService { new ConditionalLogger("undefined_corrupt_consent", logger); private static final String GDPR_ENABLED = "1"; + private static final Instant MARCH_01_2026 = Year.of(2026) + .atMonth(Month.MARCH) + .atDay(1) + .atStartOfDay() + .toInstant(ZoneOffset.UTC); private final boolean gdprEnabled; private final String gdprDefaultValue; @@ -345,6 +354,11 @@ private TCStringParsingResult parseConsentString(String consentString, RequestLo return TCStringParsingResult.of(TCStringEmpty.create(), warnings); } + if (!isDisclosedVendorsValid(tcString)) { + warnings.add("Invalid TCF string: `disclosedVendors` list is empty."); + return TCStringParsingResult.of(TCStringEmpty.create(), warnings); + } + return toValidResult(consentString, TCStringParsingResult.of(tcString, warnings)); } @@ -417,10 +431,26 @@ private static boolean isConsentValid(TCString consent) { return consent != null && !(consent instanceof TCStringEmpty); } + private static boolean isDisclosedVendorsValid(TCString consent) { + return isCreatedBeforeMarch01Y2026(consent) || !consent.getDisclosedVendors().isEmpty(); + } + + private static boolean isCreatedBeforeMarch01Y2026(TCString consent) { + final Instant created = consent.getCreated(); + final Instant lastUpdated = consent.getLastUpdated(); + final Instant latest = lastUpdated.isAfter(created) ? lastUpdated : created; + + return latest.isBefore(MARCH_01_2026); + } + + public static boolean isVendorDisclosed(TCString consent, Integer vendorId) { + return vendorId != null + && (isCreatedBeforeMarch01Y2026(consent) || consent.getDisclosedVendors().contains(vendorId)); + } + public static boolean isConsentStringValid(String consentString) { try { - TCString.decode(consentString); - return true; + return isDisclosedVendorsValid(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/tcfstrategies/purpose/typestrategies/EnforcePurposeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/typestrategies/EnforcePurposeStrategy.java index d9729d991d6..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 @@ -19,8 +19,7 @@ public abstract class EnforcePurposeStrategy { PurposeCode.THREE, PurposeCode.FOUR, PurposeCode.FIVE, - PurposeCode.SIX, - PurposeCode.UNKNOWN)); + PurposeCode.SIX)); public abstract Stream allowedByTypeStrategy( PurposeCode purpose, From 8cbecc75be3d17fba9ec9591136dee84a7a49f0e Mon Sep 17 00:00:00 2001 From: Danylo Date: Fri, 27 Mar 2026 12:09:21 +0100 Subject: [PATCH 05/12] Fix NPE --- .../org/prebid/server/privacy/gdpr/model/TCStringEmpty.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 9e49557fe85e82b1d2057a8cd13f0da0803ff503 Mon Sep 17 00:00:00 2001 From: Danylo Date: Mon, 27 Apr 2026 20:48:25 +0200 Subject: [PATCH 06/12] Rename constant --- .../prebid/server/privacy/gdpr/TcfDefinerService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 2776957f362..3c2ca14a18b 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java @@ -60,7 +60,7 @@ public class TcfDefinerService { new ConditionalLogger("undefined_corrupt_consent", logger); private static final String GDPR_ENABLED = "1"; - private static final Instant MARCH_01_2026 = Year.of(2026) + private static final Instant TCF_2_3_ENFORCEMENT_CUTOFF_DATE = Year.of(2026) .atMonth(Month.MARCH) .atDay(1) .atStartOfDay() @@ -432,20 +432,20 @@ private static boolean isConsentValid(TCString consent) { } private static boolean isDisclosedVendorsValid(TCString consent) { - return isCreatedBeforeMarch01Y2026(consent) || !consent.getDisclosedVendors().isEmpty(); + return isCreatedBeforeTcfV2M3EnforcementCutoff(consent) || !consent.getDisclosedVendors().isEmpty(); } - private static boolean isCreatedBeforeMarch01Y2026(TCString consent) { + private static 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(MARCH_01_2026); + return latest.isBefore(TCF_2_3_ENFORCEMENT_CUTOFF_DATE); } public static boolean isVendorDisclosed(TCString consent, Integer vendorId) { return vendorId != null - && (isCreatedBeforeMarch01Y2026(consent) || consent.getDisclosedVendors().contains(vendorId)); + && (isCreatedBeforeTcfV2M3EnforcementCutoff(consent) || consent.getDisclosedVendors().contains(vendorId)); } public static boolean isConsentStringValid(String consentString) { From 0a8dbdef1a6bc7d844f332df6a7dc81ca72d6034 Mon Sep 17 00:00:00 2001 From: Danylo Date: Thu, 30 Apr 2026 18:49:22 +0200 Subject: [PATCH 07/12] Update enforcement logic --- .../server/privacy/gdpr/Tcf2Service.java | 42 ++++++++----------- .../purpose/PurposeStrategy.java | 4 +- .../SpecialFeaturesStrategy.java | 26 ++++++------ 3 files changed, 34 insertions(+), 38 deletions(-) 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 409e178867a..8d47b739426 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java @@ -99,19 +99,12 @@ private Future> permissionsForInternal(Collection disclosedVendors = vendorPermissions.stream() - .filter(permission -> TcfDefinerService.isVendorDisclosed(tcfConsent, permission.getVendorId())) - .toList(); - if (disclosedVendors.isEmpty()) { - return Future.succeededFuture(vendorPermissions); - } - final Purposes mergedPurposes = mergeAccountPurposes(accountGdprConfig); final PurposeOneTreatmentInterpretation mergedPurposeOneTreatmentInterpretation = mergePurposeOneTreatmentInterpretation(accountGdprConfig); final VendorPermissionsByType vendorPermissionsByType = - toVendorPermissionsByType(disclosedVendors, accountGdprConfig); + toVendorPermissionsByType(vendorPermissions, accountGdprConfig); return versionedVendorListService.forConsent(tcfConsent) .compose(vendorGvlPermissions -> processSupportedPurposeStrategies( @@ -127,9 +120,8 @@ private Future> permissionsForInternal(Collection enforcePurpose4IfRequired(mergedPurposes, vendorPermissionsByType)) .map(ignored -> processSupportedSpecialFeatureStrategies( tcfConsent, - disclosedVendors, - mergeAccountSpecialFeatures(accountGdprConfig))) - .map(vendorPermissions); + vendorPermissions, + mergeAccountSpecialFeatures(accountGdprConfig))); } private static VendorPermissionsByType toVendorPermissionsByType( @@ -366,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; } @@ -387,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/tcfstrategies/purpose/PurposeStrategy.java b/src/main/java/org/prebid/server/privacy/gdpr/tcfstrategies/purpose/PurposeStrategy.java index e9e83a3ed9c..6a70bff4567 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.TcfDefinerService; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl; @@ -63,7 +64,8 @@ public void processTypePurposeStrategy(TCString vendorConsent, final Collection excludedVendors = excludedVendors(vendorPermissions, purpose); final Collection vendorForPurpose = vendorPermissions.stream() - .filter(vendorPermission -> !excludedVendors.contains(vendorPermission)) + .filter(vendorPermission -> !excludedVendors.contains(vendorPermission) + || TcfDefinerService.isVendorDisclosed(vendorConsent, vendorPermission.getVendor().getId())) .toList(); allowedByTypeStrategy(vendorConsent, purpose, vendorForPurpose, excludedVendors) 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..d7290d353e3 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,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.TcfDefinerService; import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction; import org.prebid.server.privacy.gdpr.model.VendorPermission; import org.prebid.server.settings.model.SpecialFeature; @@ -22,10 +23,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 +33,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 -> + TcfDefinerService.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())); } } From 1fe683480932baa00f6aae087bb59d6d98c879af Mon Sep 17 00:00:00 2001 From: Danylo Date: Thu, 30 Apr 2026 20:21:47 +0200 Subject: [PATCH 08/12] Add `strict-disclosed-vendors-treatment` property --- .../requestfactory/AmpRequestFactory.java | 41 +++++++++-------- .../gdpr/DisclosedVendorsStrictness.java | 45 +++++++++++++++++++ .../privacy/gdpr/TcfDefinerService.java | 40 +++++------------ .../purpose/Purpose01Strategy.java | 10 ++++- .../purpose/Purpose02Strategy.java | 10 ++++- .../purpose/Purpose03Strategy.java | 10 ++++- .../purpose/Purpose04Strategy.java | 10 ++++- .../purpose/Purpose05Strategy.java | 10 ++++- .../purpose/Purpose06Strategy.java | 10 ++++- .../purpose/Purpose07Strategy.java | 10 ++++- .../purpose/Purpose08Strategy.java | 10 ++++- .../purpose/Purpose09Strategy.java | 10 ++++- .../purpose/Purpose10Strategy.java | 10 ++++- .../purpose/PurposeStrategy.java | 12 +++-- .../SpecialFeaturesOneStrategy.java | 5 +++ .../SpecialFeaturesStrategy.java | 13 ++++-- .../server/settings/model/GdprConfig.java | 3 ++ 17 files changed, 183 insertions(+), 76 deletions(-) create mode 100644 src/main/java/org/prebid/server/privacy/gdpr/DisclosedVendorsStrictness.java 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/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/TcfDefinerService.java b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java index 3c2ca14a18b..8fcd6050e83 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java @@ -29,10 +29,6 @@ import org.prebid.server.settings.model.GdprConfig; import org.prebid.server.util.ObjectUtil; -import java.time.Instant; -import java.time.Month; -import java.time.Year; -import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -60,15 +56,11 @@ public class TcfDefinerService { new ConditionalLogger("undefined_corrupt_consent", logger); private static final String GDPR_ENABLED = "1"; - 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 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; @@ -79,6 +71,7 @@ public class TcfDefinerService { public TcfDefinerService(GdprConfig gdprConfig, Set eeaCountries, + DisclosedVendorsStrictness disclosedVendorsStrictness, Tcf2Service tcf2Service, GeoLocationServiceWrapper geoLocationServiceWrapper, BidderCatalog bidderCatalog, @@ -90,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); @@ -354,8 +348,11 @@ private TCStringParsingResult parseConsentString(String consentString, RequestLo return TCStringParsingResult.of(TCStringEmpty.create(), warnings); } - if (!isDisclosedVendorsValid(tcString)) { - warnings.add("Invalid TCF string: `disclosedVendors` list is empty."); + if (!disclosedVendorsStrictness.isValid(tcString)) { + final String message = "Invalid TCF string: `disclosedVendors` list is empty."; + warnings.add(message); + logWarn(consentString, message, requestLogInfo); + return TCStringParsingResult.of(TCStringEmpty.create(), warnings); } @@ -431,26 +428,9 @@ private static boolean isConsentValid(TCString consent) { return consent != null && !(consent instanceof TCStringEmpty); } - private static boolean isDisclosedVendorsValid(TCString consent) { - return isCreatedBeforeTcfV2M3EnforcementCutoff(consent) || !consent.getDisclosedVendors().isEmpty(); - } - - private static 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 static boolean isVendorDisclosed(TCString consent, Integer vendorId) { - return vendorId != null - && (isCreatedBeforeTcfV2M3EnforcementCutoff(consent) || consent.getDisclosedVendors().contains(vendorId)); - } - - public static boolean isConsentStringValid(String consentString) { + public boolean isConsentStringValid(String consentString) { try { - return isDisclosedVendorsValid(TCString.decode(consentString)); + return disclosedVendorsStrictness.isValid(TCString.decode(consentString)); } catch (RuntimeException e) { return false; } 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 6a70bff4567..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,7 +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.TcfDefinerService; +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; @@ -21,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,8 +67,9 @@ public void processTypePurposeStrategy(TCString vendorConsent, final Collection excludedVendors = excludedVendors(vendorPermissions, purpose); final Collection vendorForPurpose = vendorPermissions.stream() - .filter(vendorPermission -> !excludedVendors.contains(vendorPermission) - || TcfDefinerService.isVendorDisclosed(vendorConsent, vendorPermission.getVendor().getId())) + .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/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 d7290d353e3..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,17 +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.TcfDefinerService; +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); @@ -37,8 +44,8 @@ private Stream disclosedVendors(TCString vendorConsent, Collection vendorPermissions) { return vendorPermissions.stream() - .filter(vendorPermission -> - TcfDefinerService.isVendorDisclosed(vendorConsent, vendorPermission.getVendorId())); + .filter(vendorPermission -> disclosedVendorsStrictness + .isVendorDisclosed(vendorConsent, vendorPermission.getVendorId())); } private void allowFor(Stream vendorPermissions) { 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 10487677d82..de548c2c6c8 100644 --- a/src/main/java/org/prebid/server/settings/model/GdprConfig.java +++ b/src/main/java/org/prebid/server/settings/model/GdprConfig.java @@ -35,4 +35,7 @@ public class GdprConfig { @JsonProperty("purpose-one-treatment-interpretation") PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation; + + @JsonProperty("strict-disclosed-vendors-treatment") + boolean strictDisclosedVendorsTreatment = true; } From fc35aa38d7727fdd81f1109f3a4a2dee1b14e334 Mon Sep 17 00:00:00 2001 From: Danylo Date: Thu, 30 Apr 2026 20:55:03 +0200 Subject: [PATCH 09/12] Fix build --- .../config/PrivacyServiceConfiguration.java | 56 ++++++++++++++----- .../spring/config/ServiceConfiguration.java | 12 ++-- .../requestfactory/AmpRequestFactoryTest.java | 7 ++- .../org/prebid/server/it/ApplicationTest.java | 1 + .../privacy/gdpr/TcfDefinerServiceTest.java | 20 +++++-- .../purpose/Purpose01StrategyTest.java | 8 +++ .../purpose/Purpose02StrategyTest.java | 8 +++ .../purpose/Purpose03StrategyTest.java | 8 +++ .../purpose/Purpose04StrategyTest.java | 8 +++ .../purpose/Purpose05StrategyTest.java | 8 +++ .../purpose/Purpose06StrategyTest.java | 8 +++ .../purpose/Purpose07StrategyTest.java | 8 +++ .../purpose/Purpose08StrategyTest.java | 8 +++ .../purpose/Purpose09StrategyTest.java | 8 +++ .../purpose/Purpose10StrategyTest.java | 8 +++ .../SpecialFeaturesOneStrategyTest.java | 8 ++- 16 files changed, 159 insertions(+), 25 deletions(-) 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 0cdc87ae7d3..a6fde491970 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, @@ -188,118 +196,138 @@ HostVendorTcfDefinerService hostVendorTcfDefinerService(TcfDefinerService tcfDef } @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, - PurposeTwoBasicEnforcePurposeStrategy purposeTwoBasicEnforcePurposeStrategy, + Purpose02Strategy purpose02Strategy(DisclosedVendorsStrictness disclosedVendorsStrictness, + FullEnforcePurposeStrategy fullEnforcePurposeStrategy, + BasicEnforcePurposeStrategy basicEnforcePurposeStrategy, NoEnforcePurposeStrategy noEnforcePurposeStrategy) { return new Purpose02Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, - purposeTwoBasicEnforcePurposeStrategy, + basicEnforcePurposeStrategy, 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 f86547199a0..337363fcf02 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -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/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/TcfDefinerServiceTest.java b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java index e63ad8f530d..f8c2927c1f6 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, @@ -645,19 +658,18 @@ 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(); } } 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..576b52833da 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose01StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.ONE; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose01StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose01Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..4078e8ea3b8 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose02StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.TWO; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose02StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose02Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..156f8294adf 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose03StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.THREE; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose03StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose03Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..60137eeb660 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose04StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.FOUR; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose04StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose04Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..d57324add12 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose05StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.FIVE; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose05StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose05Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..528e1f4f9f4 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose06StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.SIX; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose06StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose06Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..6f008dbd116 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose07StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.SEVEN; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose07StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose07Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..932201d6f2a 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose08StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.EIGHT; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose08StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose08Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..f7241844c5d 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose09StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.NINE; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose09StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose09Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..93faa7273ac 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; @@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; 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 +37,9 @@ public class Purpose10StrategyTest { private static final PurposeCode PURPOSE_CODE = PurposeCode.TEN; + @Mock(strictness = LENIENT) + private DisclosedVendorsStrictness disclosedVendorsStrictness; + @Mock private FullEnforcePurposeStrategy fullEnforcePurposeStrategy; @@ -51,7 +56,10 @@ public class Purpose10StrategyTest { @BeforeEach public void setUp() { + given(disclosedVendorsStrictness.isVendorDisclosed(any(), any())).willReturn(true); + target = new Purpose10Strategy( + disclosedVendorsStrictness, fullEnforcePurposeStrategy, basicEnforcePurposeStrategy, noEnforcePurposeStrategy); 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..f7f6c7b60b2 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,6 +18,7 @@ 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.BDDMockito.given; import static org.mockito.Mock.Strictness.LENIENT; @@ -28,6 +30,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 +42,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 From 4dd80cbcf32bab77c2518de4755e7a76e63d00fc Mon Sep 17 00:00:00 2001 From: Danylo Date: Thu, 30 Apr 2026 23:39:01 +0200 Subject: [PATCH 10/12] Add units --- .../privacy/gdpr/TcfDefinerService.java | 1 + .../privacy/gdpr/TcfDefinerServiceTest.java | 36 +++++++ .../purpose/Purpose01StrategyTest.java | 40 ++++++++ .../purpose/Purpose02StrategyTest.java | 40 ++++++++ .../purpose/Purpose03StrategyTest.java | 40 ++++++++ .../purpose/Purpose04StrategyTest.java | 40 ++++++++ .../purpose/Purpose05StrategyTest.java | 40 ++++++++ .../purpose/Purpose06StrategyTest.java | 40 ++++++++ .../purpose/Purpose07StrategyTest.java | 40 ++++++++ .../purpose/Purpose08StrategyTest.java | 40 ++++++++ .../purpose/Purpose09StrategyTest.java | 40 ++++++++ .../purpose/Purpose10StrategyTest.java | 40 ++++++++ .../FullEnforcePurposeStrategyTest.java | 97 +++++++++++++++++-- .../NoEnforcePurposeStrategyTest.java | 52 +++++++++- .../SpecialFeaturesOneStrategyTest.java | 32 ++++++ 15 files changed, 610 insertions(+), 8 deletions(-) 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 8fcd6050e83..5a9545b6fab 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java @@ -352,6 +352,7 @@ private TCStringParsingResult parseConsentString(String consentString, RequestLo final String message = "Invalid TCF string: `disclosedVendors` list is empty."; warnings.add(message); logWarn(consentString, message, requestLogInfo); + metrics.updatePrivacyTcfInvalidMetric(); return TCStringParsingResult.of(TCStringEmpty.create(), warnings); } 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 f8c2927c1f6..b7058eb0689 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java @@ -491,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); @@ -547,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).updatePrivacyTcfInvalidMetric(); + } + @Test public void resultForVendorIdsShouldNotSetTcfRequestsAndTcfGeoMetricsWhenConsentIsNotValid() { // given @@ -672,4 +699,13 @@ public void isConsentStringValidShouldReturnFalseWhenStringNotValid() { // when and then 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 576b52833da..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 4078e8ea3b8..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 156f8294adf..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 60137eeb660..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 d57324add12..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 528e1f4f9f4..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 6f008dbd116..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 932201d6f2a..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 f7241844c5d..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 93faa7273ac..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 @@ -27,6 +27,7 @@ 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; @@ -167,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 f7f6c7b60b2..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 @@ -20,6 +20,7 @@ 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; @@ -156,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); From 3544c0254486c6f8ca85ff5ed8df16529cb55c63 Mon Sep 17 00:00:00 2001 From: Danylo Date: Fri, 1 May 2026 13:40:48 +0200 Subject: [PATCH 11/12] Add `no_disclosed_vendors` metric --- src/main/java/org/prebid/server/metric/MetricName.java | 1 + src/main/java/org/prebid/server/metric/Metrics.java | 4 ++++ .../org/prebid/server/privacy/gdpr/TcfDefinerService.java | 2 +- .../org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) 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/TcfDefinerService.java b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java index 5a9545b6fab..91e89d0e2b3 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/TcfDefinerService.java @@ -352,7 +352,7 @@ private TCStringParsingResult parseConsentString(String consentString, RequestLo final String message = "Invalid TCF string: `disclosedVendors` list is empty."; warnings.add(message); logWarn(consentString, message, requestLogInfo); - metrics.updatePrivacyTcfInvalidMetric(); + metrics.updatePrivacyTcfNoDisclosedVendorsMetric(); return TCStringParsingResult.of(TCStringEmpty.create(), warnings); } 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 b7058eb0689..bfa655c8203 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/TcfDefinerServiceTest.java @@ -571,7 +571,7 @@ public void resolveTcfContextShouldIncrementInvalidConsentStringMetricWhenDisclo assertThat(context.getWarnings()).containsExactly("Invalid TCF string: `disclosedVendors` list is empty."); }); - verify(metrics).updatePrivacyTcfInvalidMetric(); + verify(metrics).updatePrivacyTcfNoDisclosedVendorsMetric(); } @Test From bd4b1bd946e65ff8a892db5a56a863dbfdad289c Mon Sep 17 00:00:00 2001 From: Danylo Date: Fri, 1 May 2026 15:10:41 +0200 Subject: [PATCH 12/12] Add units --- .../gdpr/DisclosedVendorsStrictnessTest.java | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/test/java/org/prebid/server/privacy/gdpr/DisclosedVendorsStrictnessTest.java 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()); + } +}