From a9ee429449797f9a7fdf48a47c295fd5377cbf85 Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Tue, 15 Jun 2021 11:26:55 -0400 Subject: [PATCH] Rename GDPR UserSyncIfAmbiguous to DefaultValue (#1858) --- config/config.go | 9 +- config/config_test.go | 13 ++- endpoints/cookie_sync.go | 6 +- endpoints/cookie_sync_test.go | 4 +- exchange/exchange.go | 58 +++++------ exchange/exchange_test.go | 37 ++++--- exchange/targeting_test.go | 18 ++-- exchange/utils.go | 4 +- exchange/utils_test.go | 63 ++++++------ gdpr/impl.go | 2 +- gdpr/impl_test.go | 176 +++++++++++++++++----------------- 11 files changed, 209 insertions(+), 181 deletions(-) diff --git a/config/config.go b/config/config.go index 7e24a00d370..1260d28a779 100644 --- a/config/config.go +++ b/config/config.go @@ -193,7 +193,7 @@ type Privacy struct { type GDPR struct { Enabled bool `mapstructure:"enabled"` HostVendorID int `mapstructure:"host_vendor_id"` - UsersyncIfAmbiguous bool `mapstructure:"usersync_if_ambiguous"` + DefaultValue string `mapstructure:"default_value"` Timeouts GDPRTimeouts `mapstructure:"timeouts_ms"` NonStandardPublishers []string `mapstructure:"non_standard_publishers,flow"` NonStandardPublisherMap map[string]struct{} @@ -202,12 +202,15 @@ type GDPR struct { // EEACountries (EEA = European Economic Area) are a list of countries where we should assume GDPR applies. // If the gdpr flag is unset in a request, but geo.country is set, we will assume GDPR applies if and only // if the country matches one on this list. If both the GDPR flag and country are not set, we default - // to UsersyncIfAmbiguous + // to DefaultValue EEACountries []string `mapstructure:"eea_countries"` EEACountriesMap map[string]struct{} } func (cfg *GDPR) validate(errs []error) []error { + if cfg.DefaultValue != "0" && cfg.DefaultValue != "1" { + errs = append(errs, fmt.Errorf("gdpr.default_value must be 0 or 1")) + } if cfg.HostVendorID < 0 || cfg.HostVendorID > 0xffff { errs = append(errs, fmt.Errorf("gdpr.host_vendor_id must be in the range [0, %d]. Got %d", 0xffff, cfg.HostVendorID)) } @@ -937,7 +940,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("amp_timeout_adjustment_ms", 0) v.SetDefault("gdpr.enabled", true) v.SetDefault("gdpr.host_vendor_id", 0) - v.SetDefault("gdpr.usersync_if_ambiguous", false) + v.SetDefault("gdpr.default_value", "1") v.SetDefault("gdpr.timeouts_ms.init_vendorlist_fetches", 0) v.SetDefault("gdpr.timeouts_ms.active_vendorlist_fetch", 0) v.SetDefault("gdpr.non_standard_publishers", []string{""}) diff --git a/config/config_test.go b/config/config_test.go index f34bd5fa189..a2b28d026d7 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -148,7 +148,7 @@ func TestDefaults(t *testing.T) { var fullConfig = []byte(` gdpr: host_vendor_id: 15 - usersync_if_ambiguous: true + default_value: "0" non_standard_publishers: ["siteID","fake-site-id","appID","agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA"] ccpa: enforce: true @@ -352,7 +352,7 @@ func TestFullConfig(t *testing.T) { cmpInts(t, "http_client_cache.max_idle_connections_per_host", cfg.CacheClient.MaxIdleConnsPerHost, 2) cmpInts(t, "http_client_cache.idle_connection_timeout_seconds", cfg.CacheClient.IdleConnTimeout, 3) cmpInts(t, "gdpr.host_vendor_id", cfg.GDPR.HostVendorID, 15) - cmpBools(t, "gdpr.usersync_if_ambiguous", cfg.GDPR.UsersyncIfAmbiguous, true) + cmpStrings(t, "gdpr.default_value", cfg.GDPR.DefaultValue, "0") //Assert the NonStandardPublishers was correctly unmarshalled cmpStrings(t, "gdpr.non_standard_publishers", cfg.GDPR.NonStandardPublishers[0], "siteID") @@ -460,6 +460,9 @@ func TestUnmarshalAdapterExtraInfo(t *testing.T) { func TestValidConfig(t *testing.T) { cfg := Configuration{ + GDPR: GDPR{ + DefaultValue: "1", + }, StoredRequests: StoredRequests{ Files: FileFetcherConfig{Enabled: true}, InMemoryCache: InMemoryCache{ @@ -650,6 +653,12 @@ func TestInvalidAMPException(t *testing.T) { assertOneError(t, cfg.validate(), "gdpr.amp_exception has been discontinued and must be removed from your config. If you need to disable GDPR for AMP, you may do so per-account (gdpr.integration_enabled.amp) or at the host level for the default account (account_defaults.gdpr.integration_enabled.amp)") } +func TestInvalidGDPRDefaultValue(t *testing.T) { + cfg := newDefaultConfig(t) + cfg.GDPR.DefaultValue = "2" + assertOneError(t, cfg.validate(), "gdpr.default_value must be 0 or 1") +} + func TestNegativeCurrencyConverterFetchInterval(t *testing.T) { cfg := Configuration{ CurrencyConverter: CurrencyConverter{ diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index bf3935f0535..3c1354c86bd 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -97,7 +97,7 @@ func (deps *cookieSyncDeps) Endpoint(w http.ResponseWriter, r *http.Request, _ h } parsedReq := &cookieSyncRequest{} - if err := parseRequest(parsedReq, bodyBytes, deps.gDPR.UsersyncIfAmbiguous); err != nil { + if err := parseRequest(parsedReq, bodyBytes, deps.gDPR.DefaultValue); err != nil { co.Status = http.StatusBadRequest co.Errors = append(co.Errors, err) http.Error(w, co.Errors[len(co.Errors)-1].Error(), co.Status) @@ -181,7 +181,7 @@ func (deps *cookieSyncDeps) Endpoint(w http.ResponseWriter, r *http.Request, _ h enc.Encode(csResp) } -func parseRequest(parsedReq *cookieSyncRequest, bodyBytes []byte, usersyncIfAmbiguous bool) error { +func parseRequest(parsedReq *cookieSyncRequest, bodyBytes []byte, gdprDefaultValue string) error { if err := json.Unmarshal(bodyBytes, parsedReq); err != nil { return fmt.Errorf("JSON parsing failed: %s", err.Error()) } @@ -193,7 +193,7 @@ func parseRequest(parsedReq *cookieSyncRequest, bodyBytes []byte, usersyncIfAmbi if parsedReq.GDPR == nil { var gdpr = new(int) *gdpr = 1 - if usersyncIfAmbiguous { + if gdprDefaultValue == "0" { *gdpr = 0 } parsedReq.GDPR = gdpr diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 7632894baf6..2f56c262979 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -110,7 +110,7 @@ func TestCCPA(t *testing.T) { } for _, test := range testCases { - gdpr := config.GDPR{UsersyncIfAmbiguous: true} + gdpr := config.GDPR{DefaultValue: "0"} ccpa := config.CCPA{Enforce: test.enforceCCPA} rr := doConfigurablePost(test.requestBody, nil, true, syncersForTest(), gdpr, ccpa) assert.Equal(t, http.StatusOK, rr.Code, test.description+":httpResponseCode") @@ -149,7 +149,7 @@ func TestCookieSyncNoBidders(t *testing.T) { } func TestCookieSyncNoCookiesBrokenGDPR(t *testing.T) { - rr := doConfigurablePost(`{"bidders":["appnexus", "audienceNetwork", "random"],"gdpr_consent":"GLKHGKGKKGK"}`, nil, true, map[openrtb_ext.BidderName]usersync.Usersyncer{}, config.GDPR{UsersyncIfAmbiguous: true}, config.CCPA{}) + rr := doConfigurablePost(`{"bidders":["appnexus", "audienceNetwork", "random"],"gdpr_consent":"GLKHGKGKKGK"}`, nil, true, map[openrtb_ext.BidderName]usersync.Usersyncer{}, config.GDPR{DefaultValue: "0"}, config.CCPA{}) assert.Equal(t, rr.Header().Get("Content-Type"), "application/json; charset=utf-8") assert.Equal(t, http.StatusOK, rr.Code) assert.ElementsMatch(t, []string{"appnexus", "audienceNetwork"}, parseSyncs(t, rr.Body.Bytes())) diff --git a/exchange/exchange.go b/exchange/exchange.go index 6f0c610a958..7b156b5dee2 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -51,18 +51,18 @@ type IdFetcher interface { } type exchange struct { - adapterMap map[openrtb_ext.BidderName]adaptedBidder - bidderInfo config.BidderInfos - me metrics.MetricsEngine - cache prebid_cache_client.Client - cacheTime time.Duration - gDPR gdpr.Permissions - currencyConverter *currency.RateConverter - externalURL string - UsersyncIfAmbiguous bool - privacyConfig config.Privacy - categoriesFetcher stored_requests.CategoryFetcher - bidIDGenerator BidIDGenerator + adapterMap map[openrtb_ext.BidderName]adaptedBidder + bidderInfo config.BidderInfos + me metrics.MetricsEngine + cache prebid_cache_client.Client + cacheTime time.Duration + gDPR gdpr.Permissions + currencyConverter *currency.RateConverter + externalURL string + gdprDefaultValue string + privacyConfig config.Privacy + categoriesFetcher stored_requests.CategoryFetcher + bidIDGenerator BidIDGenerator } // Container to pass out response ext data from the GetAllBids goroutines back into the main thread @@ -111,16 +111,16 @@ func (randomDeduplicateBidBooleanGenerator) Generate() bool { func NewExchange(adapters map[openrtb_ext.BidderName]adaptedBidder, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine metrics.MetricsEngine, infos config.BidderInfos, gDPR gdpr.Permissions, currencyConverter *currency.RateConverter, categoriesFetcher stored_requests.CategoryFetcher) Exchange { return &exchange{ - adapterMap: adapters, - bidderInfo: infos, - cache: cache, - cacheTime: time.Duration(cfg.CacheURL.ExpectedTimeMillis) * time.Millisecond, - categoriesFetcher: categoriesFetcher, - currencyConverter: currencyConverter, - externalURL: cfg.ExternalURL, - gDPR: gDPR, - me: metricsEngine, - UsersyncIfAmbiguous: cfg.GDPR.UsersyncIfAmbiguous, + adapterMap: adapters, + bidderInfo: infos, + cache: cache, + cacheTime: time.Duration(cfg.CacheURL.ExpectedTimeMillis) * time.Millisecond, + categoriesFetcher: categoriesFetcher, + currencyConverter: currencyConverter, + externalURL: cfg.ExternalURL, + gDPR: gDPR, + me: metricsEngine, + gdprDefaultValue: cfg.GDPR.DefaultValue, privacyConfig: config.Privacy{ CCPA: cfg.CCPA, GDPR: cfg.GDPR, @@ -186,10 +186,10 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog * recordImpMetrics(r.BidRequest, e.me) // Make our best guess if GDPR applies - usersyncIfAmbiguous := e.parseUsersyncIfAmbiguous(r.BidRequest) + gdprDefaultValue := e.parseGDPRDefaultValue(r.BidRequest) // Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder - bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(ctx, r, requestExt, e.gDPR, e.me, usersyncIfAmbiguous, e.privacyConfig, &r.Account) + bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(ctx, r, requestExt, e.gDPR, e.me, gdprDefaultValue, e.privacyConfig, &r.Account) e.me.RecordRequestPrivacy(privacyLabels) @@ -301,8 +301,8 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog * return e.buildBidResponse(ctx, liveAdapters, adapterBids, r.BidRequest, adapterExtra, auc, bidResponseExt, cacheInstructions.returnCreative, errs) } -func (e *exchange) parseUsersyncIfAmbiguous(bidRequest *openrtb2.BidRequest) bool { - usersyncIfAmbiguous := e.UsersyncIfAmbiguous +func (e *exchange) parseGDPRDefaultValue(bidRequest *openrtb2.BidRequest) string { + gdprDefaultValue := e.gdprDefaultValue var geo *openrtb2.Geo = nil if bidRequest.User != nil && bidRequest.User.Geo != nil { @@ -314,14 +314,14 @@ func (e *exchange) parseUsersyncIfAmbiguous(bidRequest *openrtb2.BidRequest) boo // If we have a country set, and it is on the list, we assume GDPR applies if not set on the request. // Otherwise we assume it does not apply as long as it appears "valid" (is 3 characters long). if _, found := e.privacyConfig.GDPR.EEACountriesMap[strings.ToUpper(geo.Country)]; found { - usersyncIfAmbiguous = false + gdprDefaultValue = "1" } else if len(geo.Country) == 3 { // The country field is formatted properly as a three character country code - usersyncIfAmbiguous = true + gdprDefaultValue = "0" } } - return usersyncIfAmbiguous + return gdprDefaultValue } func recordImpMetrics(bidRequest *openrtb2.BidRequest, metricsEngine metrics.MetricsEngine) { diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index cd36ad94b22..5a54b5e14d9 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -2004,6 +2004,13 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { eeac[c] = s } + var gdprDefaultValue string + if spec.AssumeGDPRApplies { + gdprDefaultValue = "1" + } else { + gdprDefaultValue = "0" + } + privacyConfig := config.Privacy{ CCPA: config.CCPA{ Enforce: spec.EnforceCCPA, @@ -2012,9 +2019,9 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { Enforce: spec.EnforceLMT, }, GDPR: config.GDPR{ - Enabled: spec.GDPREnabled, - UsersyncIfAmbiguous: !spec.AssumeGDPRApplies, - EEACountriesMap: eeac, + Enabled: spec.GDPREnabled, + DefaultValue: gdprDefaultValue, + EEACountriesMap: eeac, }, } bidIdGenerator := &mockBidIDGenerator{} @@ -2156,18 +2163,18 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string] } return &exchange{ - adapterMap: bidderAdapters, - me: metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames()), - cache: &wellBehavedCache{}, - cacheTime: 0, - gDPR: &permissionsMock{allowAllBidders: true}, - currencyConverter: currency.NewRateConverter(&http.Client{}, "", time.Duration(0)), - UsersyncIfAmbiguous: privacyConfig.GDPR.UsersyncIfAmbiguous, - privacyConfig: privacyConfig, - categoriesFetcher: categoriesFetcher, - bidderInfo: bidderInfos, - externalURL: "http://localhost", - bidIDGenerator: bidIDGenerator, + adapterMap: bidderAdapters, + me: metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames()), + cache: &wellBehavedCache{}, + cacheTime: 0, + gDPR: &permissionsMock{allowAllBidders: true}, + currencyConverter: currency.NewRateConverter(&http.Client{}, "", time.Duration(0)), + gdprDefaultValue: privacyConfig.GDPR.DefaultValue, + privacyConfig: privacyConfig, + categoriesFetcher: categoriesFetcher, + bidderInfo: bidderInfos, + externalURL: "http://localhost", + bidIDGenerator: bidIDGenerator, } } diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index aa07ed0c77b..86646957091 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -87,15 +87,15 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op } ex := &exchange{ - adapterMap: buildAdapterMap(mockBids, server.URL, server.Client()), - me: &metricsConf.DummyMetricsEngine{}, - cache: &wellBehavedCache{}, - cacheTime: time.Duration(0), - gDPR: gdpr.AlwaysAllow{}, - currencyConverter: currency.NewRateConverter(&http.Client{}, "", time.Duration(0)), - UsersyncIfAmbiguous: false, - categoriesFetcher: categoriesFetcher, - bidIDGenerator: &mockBidIDGenerator{false, false}, + adapterMap: buildAdapterMap(mockBids, server.URL, server.Client()), + me: &metricsConf.DummyMetricsEngine{}, + cache: &wellBehavedCache{}, + cacheTime: time.Duration(0), + gDPR: gdpr.AlwaysAllow{}, + currencyConverter: currency.NewRateConverter(&http.Client{}, "", time.Duration(0)), + gdprDefaultValue: "1", + categoriesFetcher: categoriesFetcher, + bidIDGenerator: &mockBidIDGenerator{false, false}, } imps := buildImps(t, mockBids) diff --git a/exchange/utils.go b/exchange/utils.go index 3d5e2374008..c5cf673250d 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -57,7 +57,7 @@ func cleanOpenRTBRequests(ctx context.Context, requestExt *openrtb_ext.ExtRequest, gDPR gdpr.Permissions, metricsEngine metrics.MetricsEngine, - usersyncIfAmbiguous bool, + gdprDefaultValue string, privacyConfig config.Privacy, account *config.Account) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) { @@ -87,7 +87,7 @@ func cleanOpenRTBRequests(ctx context.Context, if err != nil { errs = append(errs, err) } - gdprEnforced := gdprSignal == gdpr.SignalYes || (gdprSignal == gdpr.SignalAmbiguous && !usersyncIfAmbiguous) + gdprEnforced := gdprSignal == gdpr.SignalYes || (gdprSignal == gdpr.SignalAmbiguous && gdprDefaultValue == "1") ccpaEnforcer, err := extractCCPA(req.BidRequest, privacyConfig, &req.Account, aliases, integrationTypeMap[req.LegacyLabels.RType]) if err != nil { diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 1788d508c31..dad0d69db15 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -479,7 +479,7 @@ func TestCleanOpenRTBRequests(t *testing.T) { for _, test := range testCases { metricsMock := metrics.MetricsEngineMock{} permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true} - bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, &permissions, &metricsMock, true, privacyConfig, nil) + bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, &permissions, &metricsMock, "0", privacyConfig, nil) if test.hasError { assert.NotNil(t, err, "Error shouldn't be nil") } else { @@ -636,7 +636,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { nil, &permissionsMock{allowAllBidders: true, passGeo: true, passID: true}, &metrics.MetricsEngineMock{}, - true, + "0", privacyConfig, nil) result := bidderRequests[0] @@ -698,7 +698,7 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) { } permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true} metrics := metrics.MetricsEngineMock{} - _, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, &permissions, &metrics, true, privacyConfig, nil) + _, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, &permissions, &metrics, "0", privacyConfig, nil) assert.ElementsMatch(t, []error{test.expectError}, errs, test.description) } @@ -740,7 +740,7 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) { permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true} metrics := metrics.MetricsEngineMock{} - bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, &permissions, &metrics, true, config.Privacy{}, nil) + bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, &permissions, &metrics, "0", config.Privacy{}, nil) result := bidderRequests[0] assert.Nil(t, errs) @@ -849,7 +849,7 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) { permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true} metrics := metrics.MetricsEngineMock{} - bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, &permissions, &metrics, true, config.Privacy{}, nil) + bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, &permissions, &metrics, "0", config.Privacy{}, nil) if test.hasError == true { assert.NotNil(t, errs) assert.Len(t, bidderRequests, 0) @@ -1432,7 +1432,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true} metrics := metrics.MetricsEngineMock{} - results, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, &permissions, &metrics, true, privacyConfig, nil) + results, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, &permissions, &metrics, "0", privacyConfig, nil) result := results[0] assert.Nil(t, errs) @@ -1459,7 +1459,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdprConsent string gdprScrub bool permissionsError error - userSyncIfAmbiguous bool + gdprDefaultValue string expectPrivacyLabels metrics.PrivacyLabels expectError bool }{ @@ -1470,6 +1470,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdpr: "1", gdprConsent: "malformed", gdprScrub: false, + gdprDefaultValue: "1", expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: "", @@ -1482,6 +1483,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdpr: "1", gdprConsent: tcf2Consent, gdprScrub: true, + gdprDefaultValue: "1", expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: metrics.TCFVersionV2, @@ -1494,6 +1496,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdpr: "0", gdprConsent: tcf2Consent, gdprScrub: false, + gdprDefaultValue: "1", expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: false, GDPRTCFVersion: "", @@ -1506,6 +1509,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdpr: "0{", gdprConsent: tcf2Consent, gdprScrub: true, + gdprDefaultValue: "1", expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: metrics.TCFVersionV2, @@ -1519,6 +1523,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdpr: "1", gdprConsent: tcf2Consent, gdprScrub: true, + gdprDefaultValue: "1", expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: metrics.TCFVersionV2, @@ -1531,6 +1536,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdpr: "1", gdprConsent: tcf2Consent, gdprScrub: false, + gdprDefaultValue: "1", expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: false, GDPRTCFVersion: "", @@ -1543,6 +1549,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdpr: "1", gdprConsent: tcf2Consent, gdprScrub: true, + gdprDefaultValue: "1", expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: metrics.TCFVersionV2, @@ -1555,32 +1562,33 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { 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, - userSyncIfAmbiguous: false, + 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, - userSyncIfAmbiguous: true, + 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: "", @@ -1594,6 +1602,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdprConsent: tcf2Consent, gdprScrub: true, permissionsError: errors.New("Some error"), + gdprDefaultValue: "1", expectPrivacyLabels: metrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: metrics.TCFVersionV2, @@ -1610,8 +1619,8 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { privacyConfig := config.Privacy{ GDPR: config.GDPR{ - Enabled: test.gdprHostEnabled, - UsersyncIfAmbiguous: test.userSyncIfAmbiguous, + Enabled: test.gdprHostEnabled, + DefaultValue: test.gdprDefaultValue, TCF2: config.TCF2{ Enabled: true, }, @@ -1636,7 +1645,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { nil, &permissionsMock{allowAllBidders: true, passGeo: !test.gdprScrub, passID: !test.gdprScrub, activitiesError: test.permissionsError}, &metrics.MetricsEngineMock{}, - test.userSyncIfAmbiguous, + test.gdprDefaultValue, privacyConfig, nil) result := results[0] @@ -1698,8 +1707,8 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) { privacyConfig := config.Privacy{ GDPR: config.GDPR{ - Enabled: test.gdprEnforced, - UsersyncIfAmbiguous: true, + Enabled: test.gdprEnforced, + DefaultValue: "0", TCF2: config.TCF2{ Enabled: true, }, @@ -1727,7 +1736,7 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) { nil, &permissionsMock{allowedBidders: test.gdprAllowedBidders, passGeo: true, passID: true, activitiesError: nil}, &metricsMock, - true, + "0", privacyConfig, nil) diff --git a/gdpr/impl.go b/gdpr/impl.go index aca07fd068d..9c09e90b58e 100644 --- a/gdpr/impl.go +++ b/gdpr/impl.go @@ -94,7 +94,7 @@ func (p *permissionsImpl) normalizeGDPR(gdprSignal Signal) Signal { return gdprSignal } - if p.cfg.UsersyncIfAmbiguous { + if p.cfg.DefaultValue == "0" { return SignalNo } diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go index d26c0c57231..345dd52621d 100644 --- a/gdpr/impl_test.go +++ b/gdpr/impl_test.go @@ -18,8 +18,8 @@ import ( func TestDisallowOnEmptyConsent(t *testing.T) { perms := permissionsImpl{ cfg: config.GDPR{ - HostVendorID: 3, - UsersyncIfAmbiguous: true, + HostVendorID: 3, + DefaultValue: "0", }, vendorIDs: nil, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ @@ -190,84 +190,84 @@ func TestAllowActivities(t *testing.T) { description string bidderName openrtb_ext.BidderName publisherID string - userSyncIfAmbiguous bool + gdprDefaultValue string gdpr Signal consent string passID bool weakVendorEnforcement bool }{ { - description: "Allow PI - Non standard publisher", - bidderName: bidderBlockedByConsent, - publisherID: "appNexusAppID", - userSyncIfAmbiguous: false, - gdpr: SignalYes, - consent: vendor2AndPurpose2Consent, - passID: true, + description: "Allow PI - Non standard publisher", + bidderName: bidderBlockedByConsent, + publisherID: "appNexusAppID", + gdprDefaultValue: "1", + gdpr: SignalYes, + consent: vendor2AndPurpose2Consent, + passID: true, }, { - description: "Allow PI - known vendor with No GDPR", - bidderName: bidderBlockedByConsent, - userSyncIfAmbiguous: false, - gdpr: SignalNo, - consent: vendor2AndPurpose2Consent, - passID: true, + description: "Allow PI - known vendor with No GDPR", + bidderName: bidderBlockedByConsent, + gdprDefaultValue: "1", + gdpr: SignalNo, + consent: vendor2AndPurpose2Consent, + passID: true, }, { - description: "Allow PI - known vendor with Yes GDPR", - bidderName: bidderAllowedByConsent, - userSyncIfAmbiguous: false, - gdpr: SignalYes, - consent: vendor2AndPurpose2Consent, - passID: true, + description: "Allow PI - known vendor with Yes GDPR", + bidderName: bidderAllowedByConsent, + gdprDefaultValue: "1", + gdpr: SignalYes, + consent: vendor2AndPurpose2Consent, + passID: true, }, { - description: "PI allowed according to host setting UserSyncIfAmbiguous true - known vendor with ambiguous GDPR and empty consent", - bidderName: bidderAllowedByConsent, - userSyncIfAmbiguous: true, - gdpr: SignalAmbiguous, - consent: "", - passID: true, + description: "PI allowed according to host setting gdprDefaultValue 0 - known vendor with ambiguous GDPR and empty consent", + bidderName: bidderAllowedByConsent, + gdprDefaultValue: "0", + gdpr: SignalAmbiguous, + consent: "", + passID: true, }, { - description: "PI allowed according to host setting UserSyncIfAmbiguous true - known vendor with ambiguous GDPR and non-empty consent", - bidderName: bidderAllowedByConsent, - userSyncIfAmbiguous: true, - gdpr: SignalAmbiguous, - consent: vendor2AndPurpose2Consent, - passID: true, + description: "PI allowed according to host setting gdprDefaultValue 0 - known vendor with ambiguous GDPR and non-empty consent", + bidderName: bidderAllowedByConsent, + gdprDefaultValue: "0", + gdpr: SignalAmbiguous, + consent: vendor2AndPurpose2Consent, + passID: true, }, { - description: "PI allowed according to host setting UserSyncIfAmbiguous false - known vendor with ambiguous GDPR and empty consent", - bidderName: bidderAllowedByConsent, - userSyncIfAmbiguous: false, - gdpr: SignalAmbiguous, - consent: "", - passID: false, + description: "PI allowed according to host setting gdprDefaultValue 1 - known vendor with ambiguous GDPR and empty consent", + bidderName: bidderAllowedByConsent, + gdprDefaultValue: "1", + gdpr: SignalAmbiguous, + consent: "", + passID: false, }, { - description: "PI allowed according to host setting UserSyncIfAmbiguous false - known vendor with ambiguous GDPR and non-empty consent", - bidderName: bidderAllowedByConsent, - userSyncIfAmbiguous: false, - gdpr: SignalAmbiguous, - consent: vendor2AndPurpose2Consent, - passID: true, + description: "PI allowed according to host setting gdprDefaultValue 1 - known vendor with ambiguous GDPR and non-empty consent", + bidderName: bidderAllowedByConsent, + gdprDefaultValue: "1", + gdpr: SignalAmbiguous, + consent: vendor2AndPurpose2Consent, + passID: true, }, { - description: "Don't allow PI - known vendor with Yes GDPR and empty consent", - bidderName: bidderAllowedByConsent, - userSyncIfAmbiguous: false, - gdpr: SignalYes, - consent: "", - passID: false, + description: "Don't allow PI - known vendor with Yes GDPR and empty consent", + bidderName: bidderAllowedByConsent, + gdprDefaultValue: "1", + gdpr: SignalYes, + consent: "", + passID: false, }, { - description: "Don't allow PI - default vendor with Yes GDPR and non-empty consent", - bidderName: bidderBlockedByConsent, - userSyncIfAmbiguous: false, - gdpr: SignalYes, - consent: vendor2AndPurpose2Consent, - passID: false, + description: "Don't allow PI - default vendor with Yes GDPR and non-empty consent", + bidderName: bidderBlockedByConsent, + gdprDefaultValue: "1", + gdpr: SignalYes, + consent: vendor2AndPurpose2Consent, + passID: false, }, } vendorListData := MarshalVendorList(vendorList{ @@ -302,7 +302,7 @@ func TestAllowActivities(t *testing.T) { } for _, tt := range tests { - perms.cfg.UsersyncIfAmbiguous = tt.userSyncIfAmbiguous + perms.cfg.DefaultValue = tt.gdprDefaultValue _, _, passID, err := perms.AuctionActivitiesAllowed(context.Background(), tt.bidderName, tt.publisherID, tt.gdpr, tt.consent, tt.weakVendorEnforcement) @@ -669,53 +669,53 @@ func assertStringsEqual(t *testing.T, expected string, actual string) { func TestNormalizeGDPR(t *testing.T) { tests := []struct { - description string - userSyncIfAmbiguous bool - giveSignal Signal - wantSignal Signal + description string + gdprDefaultValue string + giveSignal Signal + wantSignal Signal }{ { - description: "Don't normalize - Signal No and userSyncIfAmbiguous false", - userSyncIfAmbiguous: false, - giveSignal: SignalNo, - wantSignal: SignalNo, + description: "Don't normalize - Signal No and gdprDefaultValue 1", + gdprDefaultValue: "1", + giveSignal: SignalNo, + wantSignal: SignalNo, }, { - description: "Don't normalize - Signal No and userSyncIfAmbiguous true", - userSyncIfAmbiguous: true, - giveSignal: SignalNo, - wantSignal: SignalNo, + description: "Don't normalize - Signal No and gdprDefaultValue 0", + gdprDefaultValue: "0", + giveSignal: SignalNo, + wantSignal: SignalNo, }, { - description: "Don't normalize - Signal Yes and userSyncIfAmbiguous false", - userSyncIfAmbiguous: false, - giveSignal: SignalYes, - wantSignal: SignalYes, + description: "Don't normalize - Signal Yes and gdprDefaultValue 1", + gdprDefaultValue: "1", + giveSignal: SignalYes, + wantSignal: SignalYes, }, { - description: "Don't normalize - Signal Yes and userSyncIfAmbiguous true", - userSyncIfAmbiguous: true, - giveSignal: SignalYes, - wantSignal: SignalYes, + description: "Don't normalize - Signal Yes and gdprDefaultValue 0", + gdprDefaultValue: "0", + giveSignal: SignalYes, + wantSignal: SignalYes, }, { - description: "Normalize - Signal Ambiguous and userSyncIfAmbiguous false", - userSyncIfAmbiguous: false, - giveSignal: SignalAmbiguous, - wantSignal: SignalYes, + description: "Normalize - Signal Ambiguous and gdprDefaultValue 1", + gdprDefaultValue: "1", + giveSignal: SignalAmbiguous, + wantSignal: SignalYes, }, { - description: "Normalize - Signal Ambiguous and userSyncIfAmbiguous true", - userSyncIfAmbiguous: true, - giveSignal: SignalAmbiguous, - wantSignal: SignalNo, + description: "Normalize - Signal Ambiguous and gdprDefaultValue 0", + gdprDefaultValue: "0", + giveSignal: SignalAmbiguous, + wantSignal: SignalNo, }, } for _, tt := range tests { perms := permissionsImpl{ cfg: config.GDPR{ - UsersyncIfAmbiguous: tt.userSyncIfAmbiguous, + DefaultValue: tt.gdprDefaultValue, }, }