diff --git a/exchange/exchange.go b/exchange/exchange.go index e688b3e0a9b..8a1e753fc2a 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -313,6 +313,19 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog // Make our best guess if GDPR applies gdprDefaultValue := e.parseGDPRDefaultValue(r.BidRequestWrapper) + gdprSignal, err := getGDPR(r.BidRequestWrapper) + if err != nil { + return nil, err + } + channelEnabled := r.TCF2Config.ChannelEnabled(channelTypeMap[r.LegacyLabels.RType]) + gdprEnforced := enforceGDPR(gdprSignal, gdprDefaultValue, channelEnabled) + dsaWriter := dsa.Writer{ + Config: r.Account.Privacy.DSA, + GDPRInScope: gdprEnforced, + } + if err := dsaWriter.Write(r.BidRequestWrapper); err != nil { + return nil, err + } // rebuild/resync the request in the request wrapper. if err := r.BidRequestWrapper.RebuildRequest(); err != nil { @@ -324,7 +337,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog Prebid: *requestExtPrebid, SChain: requestExt.GetSChain(), } - bidderRequests, privacyLabels, errs := e.requestSplitter.cleanOpenRTBRequests(ctx, *r, requestExtLegacy, gdprDefaultValue, bidAdjustmentFactors) + bidderRequests, privacyLabels, errs := e.requestSplitter.cleanOpenRTBRequests(ctx, *r, requestExtLegacy, gdprSignal, gdprEnforced, bidAdjustmentFactors) errs = append(errs, floorErrs...) mergedBidAdj, err := bidadjustment.Merge(r.BidRequestWrapper, r.Account.BidAdjustments) diff --git a/exchange/utils.go b/exchange/utils.go index 7d2e47c2f21..d5e94316a62 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -18,7 +18,6 @@ import ( "github.com/prebid/openrtb/v20/openrtb2" "github.com/prebid/prebid-server/v2/config" - "github.com/prebid/prebid-server/v2/dsa" "github.com/prebid/prebid-server/v2/errortypes" "github.com/prebid/prebid-server/v2/firstpartydata" "github.com/prebid/prebid-server/v2/gdpr" @@ -60,7 +59,9 @@ type requestSplitter struct { func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, auctionReq AuctionRequest, requestExt *openrtb_ext.ExtRequest, - gdprDefaultValue gdpr.Signal, bidAdjustmentFactors map[string]float64, + gdprSignal gdpr.Signal, + gdprEnforced bool, + bidAdjustmentFactors map[string]float64, ) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) { req := auctionReq.BidRequestWrapper aliases, errs := parseAliases(req.BidRequest) @@ -83,24 +84,6 @@ 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.Writer{ - Config: auctionReq.Account.Privacy.DSA, - GDPRInScope: gdprEnforced, - } - if err := dsaWriter.Write(req); err != nil { - errs = append(errs, err) - } - if err := req.RebuildRequest(); err != nil { - errs = append(errs, err) - return - } - var allBidderRequests []BidderRequest var allBidderRequestErrs []error allBidderRequests, allBidderRequestErrs = getAuctionBidderRequests(auctionReq, requestExt, rs.bidderToSyncerKey, impsByBidder, aliases, rs.hostSChainNode) diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 4ca13c524ed..eeef2e3a2d2 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -475,7 +475,7 @@ func TestCleanOpenRTBRequests(t *testing.T) { hostSChainNode: nil, bidderInfo: config.BidderInfos{}, } - bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{}) + bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, false, map[string]float64{}) if test.hasError { assert.NotNil(t, err, "Error shouldn't be nil") } else { @@ -541,7 +541,7 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) { bidderInfo: config.BidderInfos{}, } - bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{}) + bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, false, map[string]float64{}) assert.Empty(t, err, "No errors should be returned") for _, bidderRequest := range bidderRequests { bidderName := bidderRequest.BidderName @@ -856,7 +856,7 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) { bidderInfo: config.BidderInfos{}, } - actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{}) + actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{}) assert.Empty(t, err, "No errors should be returned") assert.Len(t, actualBidderRequests, len(test.expectedBidderRequests), "result len doesn't match for testCase %s", test.description) for _, actualBidderRequest := range actualBidderRequests { @@ -1028,7 +1028,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { bidderInfo: config.BidderInfos{}, } - bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{}) + bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{}) result := bidderRequests[0] assert.Nil(t, errs) @@ -1107,7 +1107,7 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) { bidderInfo: config.BidderInfos{}, } - _, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo, map[string]float64{}) + _, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo, false, map[string]float64{}) assert.ElementsMatch(t, []error{test.expectError}, errs, test.description) } @@ -1166,7 +1166,7 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) { bidderInfo: config.BidderInfos{}, } - bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{}) + bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{}) result := bidderRequests[0] assert.Nil(t, errs) @@ -1259,7 +1259,7 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) { bidderInfo: config.BidderInfos{}, } - bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{}) + bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, false, map[string]float64{}) if test.hasError == true { assert.NotNil(t, errs) assert.Len(t, bidderRequests, 0) @@ -1330,7 +1330,7 @@ func TestCleanOpenRTBRequestsBidderParams(t *testing.T) { bidderInfo: config.BidderInfos{}, } - bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{}) + bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, false, map[string]float64{}) if test.hasError == true { assert.NotNil(t, errs) assert.Len(t, bidderRequests, 0) @@ -1922,7 +1922,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { bidderInfo: config.BidderInfos{}, } - results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{}) + results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{}) result := results[0] assert.Nil(t, errs) @@ -1939,160 +1939,57 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { func TestCleanOpenRTBRequestsGDPR(t *testing.T) { tcf2Consent := "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA" - trueValue, falseValue := true, false testCases := []struct { description string - gdprAccountEnabled *bool - gdprHostEnabled bool - gdpr string gdprConsent string gdprScrub bool + gdprSignal gdpr.Signal + gdprEnforced bool permissionsError error - gdprDefaultValue string expectPrivacyLabels metrics.PrivacyLabels expectError bool }{ { - description: "Enforce - TCF Invalid", - gdprAccountEnabled: &trueValue, - gdprHostEnabled: true, - gdpr: "1", - gdprConsent: "malformed", - gdprScrub: false, - gdprDefaultValue: "1", + description: "enforce no scrub - TCF invalid", + gdprConsent: "malformed", + gdprScrub: false, + gdprSignal: gdpr.SignalYes, + gdprEnforced: true, expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: "", }, }, { - description: "Enforce", - gdprAccountEnabled: &trueValue, - gdprHostEnabled: true, - gdpr: "1", - gdprConsent: tcf2Consent, - gdprScrub: true, - gdprDefaultValue: "1", - expectPrivacyLabels: metrics.PrivacyLabels{ - GDPREnforced: true, - GDPRTCFVersion: metrics.TCFVersionV2, - }, - }, - { - description: "Not Enforce", - gdprAccountEnabled: &trueValue, - gdprHostEnabled: true, - gdpr: "0", - gdprConsent: tcf2Consent, - gdprScrub: false, - gdprDefaultValue: "1", - expectPrivacyLabels: metrics.PrivacyLabels{ - GDPREnforced: false, - GDPRTCFVersion: "", - }, - }, - { - description: "Enforce; GDPR signal extraction error", - gdprAccountEnabled: &trueValue, - gdprHostEnabled: true, - gdpr: "0{", - gdprConsent: tcf2Consent, - gdprScrub: true, - gdprDefaultValue: "1", + description: "enforce and scrub", + gdprConsent: tcf2Consent, + gdprScrub: true, + gdprSignal: gdpr.SignalYes, + gdprEnforced: true, expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: metrics.TCFVersionV2, }, - expectError: true, }, { - description: "Enforce; account GDPR enabled, host GDPR setting disregarded", - gdprAccountEnabled: &trueValue, - gdprHostEnabled: false, - gdpr: "1", - gdprConsent: tcf2Consent, - gdprScrub: true, - gdprDefaultValue: "1", - expectPrivacyLabels: metrics.PrivacyLabels{ - GDPREnforced: true, - GDPRTCFVersion: metrics.TCFVersionV2, - }, - }, - { - description: "Not Enforce; account GDPR disabled, host GDPR setting disregarded", - gdprAccountEnabled: &falseValue, - gdprHostEnabled: true, - gdpr: "1", - gdprConsent: tcf2Consent, - gdprScrub: false, - gdprDefaultValue: "1", + description: "not enforce", + gdprConsent: tcf2Consent, + gdprScrub: false, + gdprSignal: gdpr.SignalYes, + gdprEnforced: false, expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: false, GDPRTCFVersion: "", }, }, { - description: "Enforce; account GDPR not specified, host GDPR enabled", - gdprAccountEnabled: nil, - gdprHostEnabled: true, - gdpr: "1", - gdprConsent: tcf2Consent, - gdprScrub: true, - gdprDefaultValue: "1", - expectPrivacyLabels: metrics.PrivacyLabels{ - GDPREnforced: true, - GDPRTCFVersion: metrics.TCFVersionV2, - }, - }, - { - description: "Not Enforce; account GDPR not specified, host GDPR disabled", - gdprAccountEnabled: nil, - gdprHostEnabled: false, - gdpr: "1", - gdprConsent: tcf2Consent, - gdprScrub: false, - gdprDefaultValue: "1", - expectPrivacyLabels: metrics.PrivacyLabels{ - GDPREnforced: false, - GDPRTCFVersion: "", - }, - }, - { - description: "Enforce - Ambiguous signal, don't sync user if ambiguous", - gdprAccountEnabled: nil, - gdprHostEnabled: true, - gdpr: "null", - gdprConsent: tcf2Consent, - gdprScrub: true, - gdprDefaultValue: "1", - expectPrivacyLabels: metrics.PrivacyLabels{ - GDPREnforced: true, - GDPRTCFVersion: metrics.TCFVersionV2, - }, - }, - { - description: "Not Enforce - Ambiguous signal, sync user if ambiguous", - gdprAccountEnabled: nil, - gdprHostEnabled: true, - gdpr: "null", - gdprConsent: tcf2Consent, - gdprScrub: false, - gdprDefaultValue: "0", - expectPrivacyLabels: metrics.PrivacyLabels{ - GDPREnforced: false, - GDPRTCFVersion: "", - }, - }, - { - description: "Enforce - error while checking if personal info is allowed", - gdprAccountEnabled: nil, - gdprHostEnabled: true, - gdpr: "1", - gdprConsent: tcf2Consent, - gdprScrub: true, - permissionsError: errors.New("Some error"), - gdprDefaultValue: "1", + description: "enforce - error while checking if personal info is allowed", + gdprConsent: tcf2Consent, + gdprScrub: true, + permissionsError: errors.New("Some error"), + gdprSignal: gdpr.SignalYes, + gdprEnforced: true, expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: metrics.TCFVersionV2, @@ -2103,24 +2000,9 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { for _, test := range testCases { req := newBidRequest(t) req.User.Ext = json.RawMessage(`{"consent":"` + test.gdprConsent + `"}`) - req.Regs = &openrtb2.Regs{ - Ext: json.RawMessage(`{"gdpr":` + test.gdpr + `}`), - } - - privacyConfig := config.Privacy{ - GDPR: config.GDPR{ - DefaultValue: test.gdprDefaultValue, - TCF2: config.TCF2{ - Enabled: test.gdprHostEnabled, - }, - }, - } - accountConfig := config.Account{ - GDPR: config.AccountGDPR{ - Enabled: test.gdprAccountEnabled, - }, - } + privacyConfig := config.Privacy{} + accountConfig := config.Account{} auctionReq := AuctionRequest{ BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req}, @@ -2141,11 +2023,6 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { }, }.Builder - gdprDefaultValue := gdpr.SignalYes - if test.gdprDefaultValue == "0" { - gdprDefaultValue = gdpr.SignalNo - } - metricsMock := metrics.MetricsEngineMock{} metricsMock.Mock.On("RecordAdapterBuyerUIDScrubbed", mock.Anything).Return() @@ -2158,7 +2035,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { bidderInfo: config.BidderInfos{}, } - results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdprDefaultValue, map[string]float64{}) + results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, test.gdprSignal, test.gdprEnforced, map[string]float64{}) result := results[0] if test.expectError { @@ -2218,15 +2095,7 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) { } req.Imp[0].Ext = json.RawMessage(`{"prebid":{"bidder":{"appnexus": {"placementId": 1}, "rubicon": {}}}}`) - privacyConfig := config.Privacy{ - GDPR: config.GDPR{ - DefaultValue: "0", - TCF2: config.TCF2{ - Enabled: test.gdprEnforced, - }, - }, - } - + privacyConfig := config.Privacy{} accountConfig := config.Account{ GDPR: config.AccountGDPR{ Enabled: nil, @@ -2261,7 +2130,7 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) { bidderInfo: config.BidderInfos{}, } - results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{}) + results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalYes, test.gdprEnforced, map[string]float64{}) // extract bidder name from each request in the results bidders := []openrtb_ext.BidderName{} @@ -2349,7 +2218,7 @@ func TestCleanOpenRTBRequestsWithOpenRTBDowngrade(t *testing.T) { hostSChainNode: nil, bidderInfo: test.bidderInfos, } - bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{}) + bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, false, map[string]float64{}) assert.Nil(t, err, "Err should be nil") bidRequest := bidderRequests[0] assert.Equal(t, test.expectRegs, bidRequest.BidRequest.Regs) @@ -3125,7 +2994,7 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) { hostSChainNode: nil, bidderInfo: config.BidderInfos{}, } - bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{}) + bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, false, map[string]float64{}) assert.Nil(t, errs) assert.Len(t, bidderRequests, 2, "Bid request count is not 2") @@ -3246,7 +3115,7 @@ func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) { hostSChainNode: nil, bidderInfo: config.BidderInfos{}, } - results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, test.bidAdjustmentFactor) + results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, test.bidAdjustmentFactor) result := results[0] assert.Nil(t, errs) assert.Equal(t, test.expectedImp, result.BidRequest.Imp, test.description) @@ -3672,7 +3541,7 @@ func TestCleanOpenRTBRequestsFilterBidderRequestExt(t *testing.T) { bidderInfo: config.BidderInfos{}, } - bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{}) + bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, false, map[string]float64{}) assert.Equal(t, test.wantError, len(errs) != 0, test.desc) sort.Slice(bidderRequests, func(i, j int) bool { return bidderRequests[i].BidderCoreName < bidderRequests[j].BidderCoreName @@ -4693,7 +4562,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { bidderInfo: config.BidderInfos{}, } - bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{}) + bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{}) assert.Empty(t, errs) assert.Len(t, bidderRequests, test.expectedReqNumber)