Skip to content

Commit

Permalink
Remove GDPR TCF1 (prebid#1854)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsardo authored and sachin-pubmatic committed Aug 2, 2021
1 parent b0230ea commit 73482d6
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 377 deletions.
81 changes: 75 additions & 6 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ func TestDefaults(t *testing.T) {
cmpStrings(t, "stored_requests.filesystem.directorypath", "./stored_requests/data/by_id", cfg.StoredRequests.Files.Path)
cmpBools(t, "auto_gen_source_tid", cfg.AutoGenSourceTID, true)
cmpBools(t, "generate_bid_id", cfg.GenerateBidID, false)
cmpBools(t, "gdpr.tcf2.purpose_one_treatment.enabled", true, cfg.GDPR.TCF2.PurposeOneTreatment.Enabled)
cmpBools(t, "gdpr.tcf2.purpose_one_treatment.access_allowed", true, cfg.GDPR.TCF2.PurposeOneTreatment.AccessAllowed)
}

var fullConfig = []byte(`
Expand Down Expand Up @@ -510,6 +512,79 @@ func TestMigrateConfigFromEnv(t *testing.T) {
cmpBools(t, "stored_requests.filesystem.enabled", true, cfg.StoredRequests.Files.Enabled)
}

func TestMigrateConfigPurposeOneTreatment(t *testing.T) {
oldPurposeOneTreatmentConfig := []byte(`
gdpr:
tcf2:
purpose_one_treatement:
enabled: true
access_allowed: true
`)
newPurposeOneTreatmentConfig := []byte(`
gdpr:
tcf2:
purpose_one_treatment:
enabled: true
access_allowed: true
`)
oldAndNewPurposeOneTreatmentConfig := []byte(`
gdpr:
tcf2:
purpose_one_treatement:
enabled: false
access_allowed: true
purpose_one_treatment:
enabled: true
access_allowed: false
`)

tests := []struct {
description string
config []byte
wantPurpose1TreatmentEnabled bool
wantPurpose1TreatmentAccessAllowed bool
}{
{
description: "New config and old config not set",
config: []byte{},
},
{
description: "New config not set, old config set",
config: oldPurposeOneTreatmentConfig,
wantPurpose1TreatmentEnabled: true,
wantPurpose1TreatmentAccessAllowed: true,
},
{
description: "New config set, old config not set",
config: newPurposeOneTreatmentConfig,
wantPurpose1TreatmentEnabled: true,
wantPurpose1TreatmentAccessAllowed: true,
},
{
description: "New config and old config set",
config: oldAndNewPurposeOneTreatmentConfig,
wantPurpose1TreatmentEnabled: true,
wantPurpose1TreatmentAccessAllowed: false,
},
}

for _, tt := range tests {
v := viper.New()
v.SetConfigType("yaml")
v.ReadConfig(bytes.NewBuffer(tt.config))

migrateConfigPurposeOneTreatment(v)

if len(tt.config) > 0 {
assert.Equal(t, tt.wantPurpose1TreatmentEnabled, v.Get("gdpr.tcf2.purpose_one_treatment.enabled").(bool), tt.description)
assert.Equal(t, tt.wantPurpose1TreatmentAccessAllowed, v.Get("gdpr.tcf2.purpose_one_treatment.access_allowed").(bool), tt.description)
} else {
assert.Nil(t, v.Get("gdpr.tcf2.purpose_one_treatment.enabled"), tt.description)
assert.Nil(t, v.Get("gdpr.tcf2.purpose_one_treatment.access_allowed"), tt.description)
}
}
}

func TestInvalidAdapterEndpointConfig(t *testing.T) {
v := viper.New()
SetupViper(v, "")
Expand Down Expand Up @@ -569,12 +644,6 @@ func TestInvalidHostVendorID(t *testing.T) {
}
}

func TestInvalidFetchGVL(t *testing.T) {
cfg := newDefaultConfig(t)
cfg.GDPR.TCF1.FetchGVL = true
assertOneError(t, cfg.validate(), "gdpr.tcf1.fetch_gvl has been discontinued and must be removed from your config. TCF1 will always use the fallback GVL going forward")
}

func TestInvalidAMPException(t *testing.T) {
cfg := newDefaultConfig(t)
cfg.GDPR.AMPException = true
Expand Down
57 changes: 22 additions & 35 deletions exchange/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1447,8 +1447,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) {
}
}

func TestCleanOpenRTBRequestsGDPRScrub(t *testing.T) {
tcf1Consent := "BONV8oqONXwgmADACHENAO7pqzAAppY"
func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
tcf2Consent := "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA"
trueValue, falseValue := true, false

Expand Down Expand Up @@ -1477,19 +1476,7 @@ func TestCleanOpenRTBRequestsGDPRScrub(t *testing.T) {
},
},
{
description: "Enforce - TCF 1",
gdprAccountEnabled: &trueValue,
gdprHostEnabled: true,
gdpr: "1",
gdprConsent: tcf1Consent,
gdprScrub: true,
expectPrivacyLabels: metrics.PrivacyLabels{
GDPREnforced: true,
GDPRTCFVersion: metrics.TCFVersionV1,
},
},
{
description: "Enforce - TCF 2",
description: "Enforce",
gdprAccountEnabled: &trueValue,
gdprHostEnabled: true,
gdpr: "1",
Expand All @@ -1501,72 +1488,72 @@ func TestCleanOpenRTBRequestsGDPRScrub(t *testing.T) {
},
},
{
description: "Not Enforce - TCF 1",
description: "Not Enforce",
gdprAccountEnabled: &trueValue,
gdprHostEnabled: true,
gdpr: "0",
gdprConsent: tcf1Consent,
gdprConsent: tcf2Consent,
gdprScrub: false,
expectPrivacyLabels: metrics.PrivacyLabels{
GDPREnforced: false,
GDPRTCFVersion: "",
},
},
{
description: "Enforce - TCF 1; GDPR signal extraction error",
description: "Enforce; GDPR signal extraction error",
gdprAccountEnabled: &trueValue,
gdprHostEnabled: true,
gdpr: "0{",
gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY",
gdprConsent: tcf2Consent,
gdprScrub: true,
expectPrivacyLabels: metrics.PrivacyLabels{
GDPREnforced: true,
GDPRTCFVersion: metrics.TCFVersionV1,
GDPRTCFVersion: metrics.TCFVersionV2,
},
expectError: true,
},
{
description: "Enforce - TCF 1; account GDPR enabled, host GDPR setting disregarded",
description: "Enforce; account GDPR enabled, host GDPR setting disregarded",
gdprAccountEnabled: &trueValue,
gdprHostEnabled: false,
gdpr: "1",
gdprConsent: tcf1Consent,
gdprConsent: tcf2Consent,
gdprScrub: true,
expectPrivacyLabels: metrics.PrivacyLabels{
GDPREnforced: true,
GDPRTCFVersion: metrics.TCFVersionV1,
GDPRTCFVersion: metrics.TCFVersionV2,
},
},
{
description: "Not Enforce - TCF 1; account GDPR disabled, host GDPR setting disregarded",
description: "Not Enforce; account GDPR disabled, host GDPR setting disregarded",
gdprAccountEnabled: &falseValue,
gdprHostEnabled: true,
gdpr: "1",
gdprConsent: tcf1Consent,
gdprConsent: tcf2Consent,
gdprScrub: false,
expectPrivacyLabels: metrics.PrivacyLabels{
GDPREnforced: false,
GDPRTCFVersion: "",
},
},
{
description: "Enforce - TCF 1; account GDPR not specified, host GDPR enabled",
description: "Enforce; account GDPR not specified, host GDPR enabled",
gdprAccountEnabled: nil,
gdprHostEnabled: true,
gdpr: "1",
gdprConsent: tcf1Consent,
gdprConsent: tcf2Consent,
gdprScrub: true,
expectPrivacyLabels: metrics.PrivacyLabels{
GDPREnforced: true,
GDPRTCFVersion: metrics.TCFVersionV1,
GDPRTCFVersion: metrics.TCFVersionV2,
},
},
{
description: "Not Enforce - TCF 1; account GDPR not specified, host GDPR disabled",
description: "Not Enforce; account GDPR not specified, host GDPR disabled",
gdprAccountEnabled: nil,
gdprHostEnabled: false,
gdpr: "1",
gdprConsent: tcf1Consent,
gdprConsent: tcf2Consent,
gdprScrub: false,
expectPrivacyLabels: metrics.PrivacyLabels{
GDPREnforced: false,
Expand All @@ -1578,20 +1565,20 @@ func TestCleanOpenRTBRequestsGDPRScrub(t *testing.T) {
gdprAccountEnabled: nil,
gdprHostEnabled: true,
gdpr: "null",
gdprConsent: tcf1Consent,
gdprConsent: tcf2Consent,
gdprScrub: true,
userSyncIfAmbiguous: false,
expectPrivacyLabels: metrics.PrivacyLabels{
GDPREnforced: true,
GDPRTCFVersion: metrics.TCFVersionV1,
GDPRTCFVersion: metrics.TCFVersionV2,
},
},
{
description: "Not Enforce - Ambiguous signal, sync user if ambiguous",
gdprAccountEnabled: nil,
gdprHostEnabled: true,
gdpr: "null",
gdprConsent: tcf1Consent,
gdprConsent: tcf2Consent,
gdprScrub: false,
userSyncIfAmbiguous: true,
expectPrivacyLabels: metrics.PrivacyLabels{
Expand All @@ -1604,12 +1591,12 @@ func TestCleanOpenRTBRequestsGDPRScrub(t *testing.T) {
gdprAccountEnabled: nil,
gdprHostEnabled: true,
gdpr: "1",
gdprConsent: tcf1Consent,
gdprConsent: tcf2Consent,
gdprScrub: true,
permissionsError: errors.New("Some error"),
expectPrivacyLabels: metrics.PrivacyLabels{
GDPREnforced: true,
GDPRTCFVersion: metrics.TCFVersionV1,
GDPRTCFVersion: metrics.TCFVersionV2,
},
},
}
Expand Down
4 changes: 1 addition & 3 deletions gdpr/gdpr.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ type Permissions interface {

// Versions of the GDPR TCF technical specification.
const (
tcf1SpecVersion uint8 = 1
tcf2SpecVersion uint8 = 2
)

Expand All @@ -44,8 +43,7 @@ func NewPermissions(ctx context.Context, cfg config.GDPR, vendorIDs map[openrtb_
cfg: cfg,
vendorIDs: vendorIDs,
fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
tcf1SpecVersion: newVendorListFetcherTCF1(cfg),
tcf2SpecVersion: newVendorListFetcherTCF2(ctx, cfg, client, vendorListURLMaker)},
tcf2SpecVersion: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker)},
}

if cfg.HostVendorID == 0 {
Expand Down
57 changes: 19 additions & 38 deletions gdpr/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"fmt"

"github.com/prebid/go-gdpr/api"
tcf1constants "github.com/prebid/go-gdpr/consentconstants"
consentconstants "github.com/prebid/go-gdpr/consentconstants/tcf2"
"github.com/prebid/go-gdpr/consentconstants"
tcf2ConsentConstants "github.com/prebid/go-gdpr/consentconstants/tcf2"
"github.com/prebid/go-gdpr/vendorconsent"
tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2"
"github.com/prebid/go-gdpr/vendorlist"
Expand Down Expand Up @@ -120,23 +120,15 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consen
return false, nil
}

// InfoStorageAccess is the same across TCF 1 and TCF 2
if parsedConsent.Version() == 2 {
if !p.cfg.TCF2.Purpose1.Enabled {
// We are not enforcing purpose 1
return true, nil
}
consent, ok := parsedConsent.(tcf2.ConsentMetadata)
if !ok {
err := fmt.Errorf("Unable to access TCF2 parsed consent")
return false, err
}
return p.checkPurpose(consent, vendor, vendorID, consentconstants.InfoStorageAccess, false), nil
}
if vendor.Purpose(consentconstants.InfoStorageAccess) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && parsedConsent.VendorConsent(vendorID) {
if !p.cfg.TCF2.Purpose1.Enabled {
return true, nil
}
return false, nil
consentMeta, ok := parsedConsent.(tcf2.ConsentMetadata)
if !ok {
err := fmt.Errorf("Unable to access TCF2 parsed consent")
return false, err
}
return p.checkPurpose(consentMeta, vendor, vendorID, tcf2ConsentConstants.InfoStorageAccess, false), nil
}

func (p *permissionsImpl) allowActivities(ctx context.Context, vendorID uint16, consent string, weakVendorEnforcement bool) (allowBidRequest bool, passGeo bool, passID bool, err error) {
Expand All @@ -145,44 +137,33 @@ func (p *permissionsImpl) allowActivities(ctx context.Context, vendorID uint16,
return false, false, false, err
}

// vendor will be nil if not a valid TCF2 consent string
if vendor == nil {
return false, false, false, nil
}

if parsedConsent.Version() == 2 {
if p.cfg.TCF2.Enabled {
return p.allowActivitiesTCF2(parsedConsent, vendor, vendorID, weakVendorEnforcement)
}
if (vendor.Purpose(consentconstants.InfoStorageAccess) || vendor.LegitimateInterest(consentconstants.InfoStorageAccess) || weakVendorEnforcement) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && (vendor.Purpose(consentconstants.PersonalizationProfile) || vendor.LegitimateInterest(consentconstants.PersonalizationProfile) || weakVendorEnforcement) && parsedConsent.PurposeAllowed(consentconstants.PersonalizationProfile) && (parsedConsent.VendorConsent(vendorID) || weakVendorEnforcement) {
return true, true, true, nil
}
} else {
if (vendor.Purpose(tcf1constants.InfoStorageAccess) || vendor.LegitimateInterest(tcf1constants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(tcf1constants.InfoStorageAccess) && (vendor.Purpose(tcf1constants.AdSelectionDeliveryReporting) || vendor.LegitimateInterest(tcf1constants.AdSelectionDeliveryReporting)) && parsedConsent.PurposeAllowed(tcf1constants.AdSelectionDeliveryReporting) && parsedConsent.VendorConsent(vendorID) {
return true, true, true, nil
}
if !p.cfg.TCF2.Enabled {
return true, false, false, nil
}
return true, false, false, nil
}

func (p *permissionsImpl) allowActivitiesTCF2(parsedConsent api.VendorConsents, vendor api.Vendor, vendorID uint16, weakVendorEnforcement bool) (allowBidRequest bool, passGeo bool, passID bool, err error) {
consent, ok := parsedConsent.(tcf2.ConsentMetadata)
consentMeta, ok := parsedConsent.(tcf2.ConsentMetadata)
if !ok {
err = fmt.Errorf("Unable to access TCF2 parsed consent")
return
}

if p.cfg.TCF2.SpecialPurpose1.Enabled {
passGeo = consent.SpecialFeatureOptIn(1) && (vendor.SpecialPurpose(1) || weakVendorEnforcement)
passGeo = consentMeta.SpecialFeatureOptIn(1) && (vendor.SpecialPurpose(1) || weakVendorEnforcement)
} else {
passGeo = true
}
if p.cfg.TCF2.Purpose2.Enabled {
allowBidRequest = p.checkPurpose(consent, vendor, vendorID, tcf1constants.Purpose(2), weakVendorEnforcement)
allowBidRequest = p.checkPurpose(consentMeta, vendor, vendorID, consentconstants.Purpose(2), weakVendorEnforcement)
} else {
allowBidRequest = true
}
for i := 2; i <= 10; i++ {
if p.checkPurpose(consent, vendor, vendorID, tcf1constants.Purpose(i), weakVendorEnforcement) {
if p.checkPurpose(consentMeta, vendor, vendorID, consentconstants.Purpose(i), weakVendorEnforcement) {
passID = true
break
}
Expand All @@ -195,8 +176,8 @@ const pubRestrictNotAllowed = 0
const pubRestrictRequireConsent = 1
const pubRestrictRequireLegitInterest = 2

func (p *permissionsImpl) checkPurpose(consent tcf2.ConsentMetadata, vendor api.Vendor, vendorID uint16, purpose tcf1constants.Purpose, weakVendorEnforcement bool) bool {
if purpose == consentconstants.InfoStorageAccess && p.cfg.TCF2.PurposeOneTreatment.Enabled && consent.PurposeOneTreatment() {
func (p *permissionsImpl) checkPurpose(consent tcf2.ConsentMetadata, vendor api.Vendor, vendorID uint16, purpose consentconstants.Purpose, weakVendorEnforcement bool) bool {
if purpose == tcf2ConsentConstants.InfoStorageAccess && p.cfg.TCF2.PurposeOneTreatment.Enabled && consent.PurposeOneTreatment() {
return p.cfg.TCF2.PurposeOneTreatment.AccessAllowed
}
if consent.CheckPubRestriction(uint8(purpose), pubRestrictNotAllowed, vendorID) {
Expand Down Expand Up @@ -228,7 +209,7 @@ func (p *permissionsImpl) parseVendor(ctx context.Context, vendorID uint16, cons
}

version := parsedConsent.Version()
if version < 1 || version > 2 {
if version != 2 {
return
}
vendorList, err := p.fetchVendorList[version](ctx, parsedConsent.VendorListVersion())
Expand Down
Loading

0 comments on commit 73482d6

Please sign in to comment.