Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/main/java/org/prebid/server/privacy/gdpr/Tcf2Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,19 @@ private Future<Collection<VendorPermission>> permissionsForInternal(Collection<V
TCString tcfConsent,
AccountGdprConfig accountGdprConfig) {

final Collection<VendorPermission> 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<VendorPermission> vendorPermissionsByType =
toVendorPermissionsByType(vendorPermissions, accountGdprConfig);
toVendorPermissionsByType(disclosedVendors, accountGdprConfig);

return versionedVendorListService.forConsent(tcfConsent)
.compose(vendorGvlPermissions -> processSupportedPurposeStrategies(
Expand All @@ -120,8 +127,9 @@ private Future<Collection<VendorPermission>> permissionsForInternal(Collection<V
.map(ignored -> enforcePurpose4IfRequired(mergedPurposes, vendorPermissionsByType))
.map(ignored -> processSupportedSpecialFeatureStrategies(
tcfConsent,
vendorPermissions,
mergeAccountSpecialFeatures(accountGdprConfig)));
disclosedVendors,
mergeAccountSpecialFeatures(accountGdprConfig)))
.map(vendorPermissions);
}

private static VendorPermissionsByType<VendorPermission> toVendorPermissionsByType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}

Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,25 @@

import com.iabtcf.decoder.TCString;
import com.iabtcf.utils.IntIterable;
import org.apache.commons.collections4.SetUtils;
import org.prebid.server.privacy.gdpr.model.VendorPermission;
import org.prebid.server.privacy.gdpr.model.VendorPermissionWithGvl;
import org.prebid.server.privacy.gdpr.vendorlist.proto.PurposeCode;

import java.util.Collection;
import java.util.Set;
import java.util.stream.Stream;

public abstract class EnforcePurposeStrategy {

protected static final Set<PurposeCode> LI_SUPPORTED_PURPOSES = SetUtils.difference(
Set.of(PurposeCode.values()),
Set.of(
PurposeCode.THREE,
PurposeCode.FOUR,
PurposeCode.FIVE,
PurposeCode.SIX));

public abstract Stream<VendorPermission> allowedByTypeStrategy(
PurposeCode purpose,
TCString vendorConsent,
Expand Down Expand Up @@ -43,6 +53,10 @@ protected boolean isAllowedByLegitimateInterest(PurposeCode purpose,
boolean isEnforceVendor,
TCString tcString) {

if (!LI_SUPPORTED_PURPOSES.contains(purpose)) {
return false;
}

final IntIterable purposesConsent = tcString.getPurposesLITransparency();
final IntIterable vendorConsent = tcString.getVendorLegitimateInterest();

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -18,7 +19,9 @@ public Stream<VendorPermission> 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<VendorPermission> allowedVendorPermissions = toVendorPermissions(vendorsForPurpose)
.filter(vendorPermission -> vendorPermission.getVendorId() != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public class GdprConfig {

@JsonProperty("host-vendor-id")
String hostVendorId;
Integer hostVendorId;

Boolean enabled;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand All @@ -371,7 +371,7 @@ Ortb2ImplicitParametersResolver ortb2ImplicitParametersResolver(
adServerCurrency,
splitToList(blocklistedAppsString),
externalUrl,
hostVendorId,
gdprConfig.getHostVendorId(),
datacenterRegion,
bidderCatalog,
implicitParametersExtractor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
}
Loading