Skip to content

Commit cdc1d04

Browse files
bsardoshunj-nb
authored andcommitted
GDPR: host-level per-purpose enforce vendor signals config (prebid#1921)
* Add GDPR host-level per-purpose enforce vendor signals config * Update config defaults test with TCF2 object compare
1 parent b3f6dff commit cdc1d04

File tree

4 files changed

+353
-142
lines changed

4 files changed

+353
-142
lines changed

config/config.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,8 @@ type TCF2 struct {
257257

258258
// Making a purpose struct so purpose specific details can be added later.
259259
type TCF2Purpose struct {
260-
Enabled bool `mapstructure:"enabled"`
260+
Enabled bool `mapstructure:"enabled"`
261+
EnforceVendors bool `mapstructure:"enforce_vendors"`
261262
// Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed
262263
VendorExceptions []openrtb_ext.BidderName `mapstructure:"vendor_exceptions"`
263264
VendorExceptionMap map[openrtb_ext.BidderName]struct{}
@@ -999,6 +1000,16 @@ func SetupViper(v *viper.Viper, filename string) {
9991000
v.SetDefault("gdpr.tcf2.purpose8.enabled", true)
10001001
v.SetDefault("gdpr.tcf2.purpose9.enabled", true)
10011002
v.SetDefault("gdpr.tcf2.purpose10.enabled", true)
1003+
v.SetDefault("gdpr.tcf2.purpose1.enforce_vendors", true)
1004+
v.SetDefault("gdpr.tcf2.purpose2.enforce_vendors", true)
1005+
v.SetDefault("gdpr.tcf2.purpose3.enforce_vendors", true)
1006+
v.SetDefault("gdpr.tcf2.purpose4.enforce_vendors", true)
1007+
v.SetDefault("gdpr.tcf2.purpose5.enforce_vendors", true)
1008+
v.SetDefault("gdpr.tcf2.purpose6.enforce_vendors", true)
1009+
v.SetDefault("gdpr.tcf2.purpose7.enforce_vendors", true)
1010+
v.SetDefault("gdpr.tcf2.purpose8.enforce_vendors", true)
1011+
v.SetDefault("gdpr.tcf2.purpose9.enforce_vendors", true)
1012+
v.SetDefault("gdpr.tcf2.purpose10.enforce_vendors", true)
10021013
v.SetDefault("gdpr.tcf2.purpose1.vendor_exceptions", []openrtb_ext.BidderName{})
10031014
v.SetDefault("gdpr.tcf2.purpose2.vendor_exceptions", []openrtb_ext.BidderName{})
10041015
v.SetDefault("gdpr.tcf2.purpose3.vendor_exceptions", []openrtb_ext.BidderName{})

config/config_test.go

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,81 @@ func TestDefaults(t *testing.T) {
138138
cmpStrings(t, "stored_requests.filesystem.directorypath", "./stored_requests/data/by_id", cfg.StoredRequests.Files.Path)
139139
cmpBools(t, "auto_gen_source_tid", cfg.AutoGenSourceTID, true)
140140
cmpBools(t, "generate_bid_id", cfg.GenerateBidID, false)
141-
cmpBools(t, "gdpr.tcf2.purpose_one_treatment.enabled", true, cfg.GDPR.TCF2.PurposeOneTreatment.Enabled)
142-
cmpBools(t, "gdpr.tcf2.purpose_one_treatment.access_allowed", true, cfg.GDPR.TCF2.PurposeOneTreatment.AccessAllowed)
141+
142+
//Assert purpose VendorExceptionMap hash tables were built correctly
143+
expectedTCF2 := TCF2{
144+
Enabled: true,
145+
Purpose1: TCF2Purpose{
146+
Enabled: true,
147+
EnforceVendors: true,
148+
VendorExceptions: []openrtb_ext.BidderName{},
149+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
150+
},
151+
Purpose2: TCF2Purpose{
152+
Enabled: true,
153+
EnforceVendors: true,
154+
VendorExceptions: []openrtb_ext.BidderName{},
155+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
156+
},
157+
Purpose3: TCF2Purpose{
158+
Enabled: true,
159+
EnforceVendors: true,
160+
VendorExceptions: []openrtb_ext.BidderName{},
161+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
162+
},
163+
Purpose4: TCF2Purpose{
164+
Enabled: true,
165+
EnforceVendors: true,
166+
VendorExceptions: []openrtb_ext.BidderName{},
167+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
168+
},
169+
Purpose5: TCF2Purpose{
170+
Enabled: true,
171+
EnforceVendors: true,
172+
VendorExceptions: []openrtb_ext.BidderName{},
173+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
174+
},
175+
Purpose6: TCF2Purpose{
176+
Enabled: true,
177+
EnforceVendors: true,
178+
VendorExceptions: []openrtb_ext.BidderName{},
179+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
180+
},
181+
Purpose7: TCF2Purpose{
182+
Enabled: true,
183+
EnforceVendors: true,
184+
VendorExceptions: []openrtb_ext.BidderName{},
185+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
186+
},
187+
Purpose8: TCF2Purpose{
188+
Enabled: true,
189+
EnforceVendors: true,
190+
VendorExceptions: []openrtb_ext.BidderName{},
191+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
192+
},
193+
Purpose9: TCF2Purpose{
194+
Enabled: true,
195+
EnforceVendors: true,
196+
VendorExceptions: []openrtb_ext.BidderName{},
197+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
198+
},
199+
Purpose10: TCF2Purpose{
200+
Enabled: true,
201+
EnforceVendors: true,
202+
VendorExceptions: []openrtb_ext.BidderName{},
203+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
204+
},
205+
SpecialPurpose1: TCF2Purpose{
206+
Enabled: true,
207+
VendorExceptions: []openrtb_ext.BidderName{},
208+
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{},
209+
},
210+
PurposeOneTreatment: TCF2PurposeOneTreatment{
211+
Enabled: true,
212+
AccessAllowed: true,
213+
},
214+
}
215+
assert.Equal(t, expectedTCF2, cfg.GDPR.TCF2, "gdpr.tcf2")
143216
}
144217

145218
var fullConfig = []byte(`
@@ -149,25 +222,35 @@ gdpr:
149222
non_standard_publishers: ["siteID","fake-site-id","appID","agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA"]
150223
tcf2:
151224
purpose1:
225+
enforce_vendors: false
152226
vendor_exceptions: ["foo1a", "foo1b"]
153227
purpose2:
154228
enabled: false
229+
enforce_vendors: false
155230
vendor_exceptions: ["foo2"]
156231
purpose3:
232+
enforce_vendors: false
157233
vendor_exceptions: ["foo3"]
158234
purpose4:
235+
enforce_vendors: false
159236
vendor_exceptions: ["foo4"]
160237
purpose5:
238+
enforce_vendors: false
161239
vendor_exceptions: ["foo5"]
162240
purpose6:
241+
enforce_vendors: false
163242
vendor_exceptions: ["foo6"]
164243
purpose7:
244+
enforce_vendors: false
165245
vendor_exceptions: ["foo7"]
166246
purpose8:
247+
enforce_vendors: false
167248
vendor_exceptions: ["foo8"]
168249
purpose9:
250+
enforce_vendors: false
169251
vendor_exceptions: ["foo9"]
170252
purpose10:
253+
enforce_vendors: false
171254
vendor_exceptions: ["foo10"]
172255
special_purpose1:
173256
vendor_exceptions: ["fooSP1"]
@@ -407,51 +490,61 @@ func TestFullConfig(t *testing.T) {
407490
Enabled: true,
408491
Purpose1: TCF2Purpose{
409492
Enabled: true, // true by default
493+
EnforceVendors: false,
410494
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo1a"), openrtb_ext.BidderName("foo1b")},
411495
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo1a"): {}, openrtb_ext.BidderName("foo1b"): {}},
412496
},
413497
Purpose2: TCF2Purpose{
414498
Enabled: false,
499+
EnforceVendors: false,
415500
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo2")},
416501
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo2"): {}},
417502
},
418503
Purpose3: TCF2Purpose{
419504
Enabled: true, // true by default
505+
EnforceVendors: false,
420506
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo3")},
421507
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo3"): {}},
422508
},
423509
Purpose4: TCF2Purpose{
424510
Enabled: true, // true by default
511+
EnforceVendors: false,
425512
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo4")},
426513
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo4"): {}},
427514
},
428515
Purpose5: TCF2Purpose{
429516
Enabled: true, // true by default
517+
EnforceVendors: false,
430518
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo5")},
431519
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo5"): {}},
432520
},
433521
Purpose6: TCF2Purpose{
434522
Enabled: true, // true by default
523+
EnforceVendors: false,
435524
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo6")},
436525
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo6"): {}},
437526
},
438527
Purpose7: TCF2Purpose{
439528
Enabled: true, // true by default
529+
EnforceVendors: false,
440530
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo7")},
441531
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo7"): {}},
442532
},
443533
Purpose8: TCF2Purpose{
444534
Enabled: true, // true by default
535+
EnforceVendors: false,
445536
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo8")},
446537
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo8"): {}},
447538
},
448539
Purpose9: TCF2Purpose{
449540
Enabled: true, // true by default
541+
EnforceVendors: false,
450542
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo9")},
451543
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo9"): {}},
452544
},
453545
Purpose10: TCF2Purpose{
454546
Enabled: true, // true by default
547+
EnforceVendors: false,
455548
VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo10")},
456549
VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo10"): {}},
457550
},

gdpr/impl.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ func (p *permissionsImpl) checkPurpose(consent tcf2.ConsentMetadata, vendor api.
210210
return true
211211
}
212212

213-
purposeAllowed := consent.PurposeAllowed(purpose) && (weakVendorEnforcement || (vendor.Purpose(purpose) && consent.VendorConsent(vendorID)))
214-
legitInterest := consent.PurposeLITransparency(purpose) && (weakVendorEnforcement || (vendor.LegitimateInterest(purpose) && consent.VendorLegitInterest(vendorID)))
213+
purposeAllowed := p.consentEstablished(consent, vendor, vendorID, purpose, weakVendorEnforcement)
214+
legitInterest := p.legitInterestEstablished(consent, vendor, vendorID, purpose, weakVendorEnforcement)
215215

216216
if consent.CheckPubRestriction(uint8(purpose), pubRestrictRequireConsent, vendorID) {
217217
return purposeAllowed
@@ -224,6 +224,38 @@ func (p *permissionsImpl) checkPurpose(consent tcf2.ConsentMetadata, vendor api.
224224
return purposeAllowed || legitInterest
225225
}
226226

227+
func (p *permissionsImpl) consentEstablished(consent tcf2.ConsentMetadata, vendor api.Vendor, vendorID uint16, purpose consentconstants.Purpose, weakVendorEnforcement bool) bool {
228+
if !consent.PurposeAllowed(purpose) {
229+
return false
230+
}
231+
if weakVendorEnforcement {
232+
return true
233+
}
234+
if !p.purposeConfigs[purpose].EnforceVendors {
235+
return true
236+
}
237+
if vendor.Purpose(purpose) && consent.VendorConsent(vendorID) {
238+
return true
239+
}
240+
return false
241+
}
242+
243+
func (p *permissionsImpl) legitInterestEstablished(consent tcf2.ConsentMetadata, vendor api.Vendor, vendorID uint16, purpose consentconstants.Purpose, weakVendorEnforcement bool) bool {
244+
if !consent.PurposeLITransparency(purpose) {
245+
return false
246+
}
247+
if weakVendorEnforcement {
248+
return true
249+
}
250+
if !p.purposeConfigs[purpose].EnforceVendors {
251+
return true
252+
}
253+
if vendor.LegitimateInterest(purpose) && consent.VendorLegitInterest(vendorID) {
254+
return true
255+
}
256+
return false
257+
}
258+
227259
func (p *permissionsImpl) parseVendor(ctx context.Context, vendorID uint16, consent string) (parsedConsent api.VendorConsents, vendor api.Vendor, err error) {
228260
parsedConsent, err = vendorconsent.ParseString(consent)
229261
if err != nil {

0 commit comments

Comments
 (0)