diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index bcce4947832..0ee873c42a5 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -978,6 +978,7 @@ func TestFloorsSignalling(t *testing.T) { Account: config.Account{DebugAllow: true, PriceFloors: config.AccountPriceFloors{Enabled: test.floorsEnable, MaxRule: 100, MaxSchemaDims: 5}}, UserSyncs: &emptyUsersync{}, HookExecutor: &hookexecution.EmptyHookExecutor{}, + TCF2Config: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}), } outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, &DebugLog{}) @@ -2170,7 +2171,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { impExtInfoMap[impID] = ImpExtInfo{} } - activityControl := privacy.NewActivityControl(spec.AccountPrivacy) + activityControl := privacy.NewActivityControl(&spec.AccountPrivacy) auctionRequest := &AuctionRequest{ BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &spec.IncomingRequest.OrtbRequest}, @@ -2181,7 +2182,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { }, DebugAllow: true, PriceFloors: config.AccountPriceFloors{Enabled: spec.AccountFloorsEnabled}, - Privacy: *spec.AccountPrivacy, + Privacy: spec.AccountPrivacy, Validations: spec.AccountConfigBidValidation, }, UserSyncs: mockIdFetcher(spec.IncomingRequest.Usersyncs), @@ -5485,7 +5486,7 @@ type exchangeSpec struct { FledgeEnabled bool `json:"fledge_enabled,omitempty"` MultiBid *multiBidSpec `json:"multiBid,omitempty"` Server exchangeServer `json:"server,omitempty"` - AccountPrivacy *config.AccountPrivacy `json:"accountPrivacy,omitempty"` + AccountPrivacy config.AccountPrivacy `json:"accountPrivacy,omitempty"` } type multiBidSpec struct { diff --git a/exchange/gdpr.go b/exchange/gdpr.go index 52fb860f5df..2f94eefdaef 100644 --- a/exchange/gdpr.go +++ b/exchange/gdpr.go @@ -35,3 +35,9 @@ func getConsent(req *openrtb_ext.RequestWrapper, gpp gpplib.GppContainer) (conse } return *ue.GetConsent(), nil } + +// enforceGDPR determines if GDPR should be enforced based on the request signal and whether the channel is enabled +func enforceGDPR(signal gdpr.Signal, defaultValue gdpr.Signal, channelEnabled bool) bool { + gdprApplies := signal == gdpr.SignalYes || (signal == gdpr.SignalAmbiguous && defaultValue == gdpr.SignalYes) + return gdprApplies && channelEnabled +} diff --git a/exchange/utils.go b/exchange/utils.go index 6fb48f463c7..4a2cc6510a7 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -83,8 +83,27 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, return } + gdprSignal, err := getGDPR(req) + if err != nil { + errs = append(errs, err) + } + channelEnabled := auctionReq.TCF2Config.ChannelEnabled(channelTypeMap[auctionReq.LegacyLabels.RType]) + gdprEnforced := enforceGDPR(gdprSignal, gdprDefaultValue, channelEnabled) + DSAWriter := dsa.DSAWriter{ + Config: auctionReq.Account.Privacy.DSA, + GDPRInScope: gdprEnforced, + } + if err := DSAWriter.Write(req); err != nil { + errs = append(errs, err) + } + req.RebuildRequest() + var allBidderRequests []BidderRequest - allBidderRequests, errs = getAuctionBidderRequests(auctionReq, requestExt, rs.bidderToSyncerKey, impsByBidder, aliases, rs.hostSChainNode) + var allBidderRequestErrs []error + allBidderRequests, allBidderRequestErrs = getAuctionBidderRequests(auctionReq, requestExt, rs.bidderToSyncerKey, impsByBidder, aliases, rs.hostSChainNode) + if allBidderRequestErrs != nil { + errs = append(errs, allBidderRequestErrs...) + } bidderNameToBidderReq := buildBidResponseRequest(req.BidRequest, bidderImpWithBidResp, aliases, auctionReq.BidderImpReplaceImpID) //this function should be executed after getAuctionBidderRequests @@ -104,16 +123,10 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, applyBidAdjustmentToFloor(allBidderRequests, bidAdjustmentFactors) } - gdprSignal, err := getGDPR(req) - if err != nil { - errs = append(errs, err) - } - consent, err := getConsent(req, gpp) if err != nil { errs = append(errs, err) } - gdprApplies := gdprSignal == gdpr.SignalYes || (gdprSignal == gdpr.SignalAmbiguous && gdprDefaultValue == gdpr.SignalYes) ccpaEnforcer, err := extractCCPA(req.BidRequest, rs.privacyConfig, &auctionReq.Account, aliases, channelTypeMap[auctionReq.LegacyLabels.RType], gpp) if err != nil { @@ -131,13 +144,8 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, privacyLabels.COPPAEnforced = coppa privacyLabels.LMTEnforced = lmt - var gdprEnforced bool var gdprPerms gdpr.Permissions = &gdpr.AlwaysAllow{} - if gdprApplies { - gdprEnforced = auctionReq.TCF2Config.ChannelEnabled(channelTypeMap[auctionReq.LegacyLabels.RType]) - } - if gdprEnforced { privacyLabels.GDPREnforced = true parsedConsent, err := vendorconsent.ParseString(consent) @@ -155,11 +163,6 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, gdprPerms = rs.gdprPermsBuilder(auctionReq.TCF2Config, gdprRequestInfo) } - DSAWriter := dsa.DSAWriter{ - Config: auctionReq.Account.Privacy.DSA, - GDPRInScope: gdprEnforced, - } - // bidder level privacy policies for _, bidderRequest := range allBidderRequests { // fetchBids activity @@ -233,10 +236,6 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, privacy.ScrubTID(reqWrapper) } - if err := DSAWriter.Write(reqWrapper); err != nil { - errs = append(errs, err) - } - reqWrapper.RebuildRequest() bidderRequest.BidRequest = reqWrapper.BidRequest diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 0c8153b6b11..71ab0fad989 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -3219,6 +3219,7 @@ func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) { BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req}, UserSyncs: &emptyUsersync{}, Account: accountConfig, + TCF2Config: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}), } gdprPermissionsBuilder := fakePermissionsBuilder{ permissions: &permissionsMock{ @@ -4666,6 +4667,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { AnonKeepBits: 16, }, }}, + TCF2Config: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}), } bidderToSyncerKey := map[string]string{}