-
Notifications
You must be signed in to change notification settings - Fork 749
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable geo activation of GDPR flag #1427
Changes from all commits
645aca7
626a8f4
f91ce65
80ee19a
e79168a
30563a2
ba719b6
bade84a
130bbaf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -159,6 +159,11 @@ type GDPR struct { | |
TCF1 TCF1 `mapstructure:"tcf1"` | ||
TCF2 TCF2 `mapstructure:"tcf2"` | ||
AMPException bool `mapstructure:"amp_exception"` | ||
// 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 | ||
EEACountries []string `mapstructure:"eea_countries"` | ||
} | ||
|
||
func (cfg *GDPR) validate(errs configErrors) configErrors { | ||
|
@@ -903,6 +908,10 @@ func SetupViper(v *viper.Viper, filename string) { | |
v.SetDefault("gdpr.tcf2.purpose_one_treatement.enabled", true) | ||
v.SetDefault("gdpr.tcf2.purpose_one_treatement.access_allowed", true) | ||
v.SetDefault("gdpr.amp_exception", false) | ||
v.SetDefault("gdpr.eea_countries", []string{"ALA", "AUT", "BEL", "BGR", "HRV", "CYP", "CZE", "DNK", "EST", | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"FIN", "FRA", "GUF", "DEU", "GIB", "GRC", "GLP", "GGY", "HUN", "ISL", "IRL", "IMN", "ITA", "JEY", "LVA", | ||
"LIE", "LTU", "LUX", "MLT", "MTQ", "MYT", "NLD", "NOR", "POL", "PRT", "REU", "ROU", "BLM", "MAF", "SPM", | ||
"SVK", "SVN", "ESP", "SWE", "GBR"}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a future PR we might want to have a test in |
||
v.SetDefault("ccpa.enforce", false) | ||
v.SetDefault("lmt.enforce", true) | ||
v.SetDefault("currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,7 @@ type exchange struct { | |
UsersyncIfAmbiguous bool | ||
defaultTTLs config.DefaultTTLs | ||
privacyConfig config.Privacy | ||
eeaCountries map[string]struct{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a future PR, maybe we can refactor to have all GDPR related fields inside some privacy wrapper in order to simplify the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's why I added privacyConfig. I didn't think to comment about adding this in there though. |
||
} | ||
|
||
// Container to pass out response ext data from the GetAllBids goroutines back into the main thread | ||
|
@@ -75,6 +76,10 @@ type bidResponseWrapper struct { | |
func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine pbsmetrics.MetricsEngine, infos adapters.BidderInfos, gDPR gdpr.Permissions, currencyConverter *currencies.RateConverter) Exchange { | ||
e := new(exchange) | ||
|
||
var s struct{} | ||
for _, c := range cfg.GDPR.EEACountries { | ||
e.eeaCountries[c] = s | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. inside
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's only used in the exchange endpoint though. I'm personally happy with its current location. |
||
e.adapterMap = newAdapterMap(client, cfg, infos, metricsEngine) | ||
e.cache = cache | ||
e.cacheTime = time.Duration(cfg.CacheURL.ExpectedTimeMillis) * time.Millisecond | ||
|
@@ -121,9 +126,27 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque | |
e.me.RecordImps(impLabels) | ||
} | ||
|
||
// Make our best guess if GDPR applies | ||
usersyncIfAmbiguous := e.UsersyncIfAmbiguous | ||
var geo *openrtb.Geo = nil | ||
if bidRequest.User != nil && bidRequest.User.Geo != nil { | ||
geo = bidRequest.User.Geo | ||
} else if bidRequest.Device != nil && bidRequest.Device.Geo != nil { | ||
geo = bidRequest.Device.Geo | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
if geo != nil { | ||
// 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.eeaCountries[strings.ToUpper(geo.Country)]; found { | ||
usersyncIfAmbiguous = false | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else if len(geo.Country) == 3 { | ||
// The country field is formatted properly as a three character country code | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
usersyncIfAmbiguous = true | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to make it extra readable maybe we could refactor this entire block to live its own function
|
||
// Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder | ||
blabels := make(map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels) | ||
cleanRequests, aliases, privacyLabels, errs := cleanOpenRTBRequests(ctx, bidRequest, requestExt, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.privacyConfig) | ||
cleanRequests, aliases, privacyLabels, errs := cleanOpenRTBRequests(ctx, bidRequest, requestExt, usersyncs, blabels, labels, e.gDPR, usersyncIfAmbiguous, e.privacyConfig) | ||
|
||
e.me.RecordRequestPrivacy(privacyLabels) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -909,6 +909,9 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { | |
LMT: config.LMT{ | ||
Enforce: spec.EnforceLMT, | ||
}, | ||
GDPR: config.GDPR{ | ||
UsersyncIfAmbiguous: !spec.AssumeGDPRApplies, | ||
}, | ||
} | ||
|
||
ex := newExchangeForTests(t, filename, spec.OutgoingRequests, aliases, privacyConfig) | ||
|
@@ -1026,15 +1029,22 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string] | |
} | ||
} | ||
|
||
var s struct{} | ||
eeac := make(map[string]struct{}) | ||
for _, c := range []string{"FIN", "FRA", "GUF"} { | ||
eeac[c] = s | ||
} | ||
|
||
return &exchange{ | ||
adapterMap: adapters, | ||
me: metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.BidderList()), | ||
cache: &wellBehavedCache{}, | ||
cacheTime: 0, | ||
gDPR: gdpr.AlwaysAllow{}, | ||
gDPR: gdpr.AlwaysFail{}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hans I'm getting this output. Is it just me?
|
||
currencyConverter: currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)), | ||
UsersyncIfAmbiguous: false, | ||
UsersyncIfAmbiguous: privacyConfig.GDPR.UsersyncIfAmbiguous, | ||
privacyConfig: privacyConfig, | ||
eeaCountries: eeac, | ||
} | ||
} | ||
|
||
|
@@ -1882,12 +1892,13 @@ func TestUpdateHbPbCatDur(t *testing.T) { | |
} | ||
|
||
type exchangeSpec struct { | ||
IncomingRequest exchangeRequest `json:"incomingRequest"` | ||
OutgoingRequests map[string]*bidderSpec `json:"outgoingRequests"` | ||
Response exchangeResponse `json:"response,omitempty"` | ||
EnforceCCPA bool `json:"enforceCcpa"` | ||
EnforceLMT bool `json:"enforceLmt"` | ||
DebugLog *DebugLog `json:"debuglog,omitempty"` | ||
IncomingRequest exchangeRequest `json:"incomingRequest"` | ||
OutgoingRequests map[string]*bidderSpec `json:"outgoingRequests"` | ||
Response exchangeResponse `json:"response,omitempty"` | ||
EnforceCCPA bool `json:"enforceCcpa"` | ||
EnforceLMT bool `json:"enforceLmt"` | ||
AssumeGDPRApplies bool `json:"assume_gdpr_applies"` | ||
DebugLog *DebugLog `json:"debuglog,omitempty"` | ||
} | ||
|
||
type exchangeRequest struct { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
{ | ||
"assume_gdpr_applies": false, | ||
"incomingRequest": { | ||
"ortbRequest": { | ||
"id": "some-request-id", | ||
"site": { | ||
"page": "test.somepage.com" | ||
}, | ||
"imp": [{ | ||
"id": "my-imp-id", | ||
"video": { | ||
"mimes": ["video/mp4"] | ||
}, | ||
"ext": { | ||
"appnexus": { | ||
"placementId": 1 | ||
} | ||
} | ||
}], | ||
"user": { | ||
"buyeruid": "some-buyer-id" | ||
}, | ||
"device": { | ||
"geo": { | ||
"country": "FRA" | ||
} | ||
} | ||
} | ||
}, | ||
"outgoingRequests": { | ||
"appnexus": { | ||
"expectRequest": { | ||
"ortbRequest": { | ||
"id": "some-request-id", | ||
"site": { | ||
"page": "test.somepage.com" | ||
}, | ||
"imp": [{ | ||
"id": "my-imp-id", | ||
"video": { | ||
"mimes": ["video/mp4"] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"placementId": 1 | ||
} | ||
} | ||
}], | ||
"user": { | ||
}, | ||
"device": { | ||
"geo": { | ||
"country": "FRA" | ||
} | ||
} | ||
}, | ||
"bidAdjustment": 1.0 | ||
}, | ||
"mockResponse": { | ||
"errors": ["appnexus-error"] | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
{ | ||
"assume_gdpr_applies": false, | ||
"incomingRequest": { | ||
"ortbRequest": { | ||
"id": "some-request-id", | ||
"site": { | ||
"page": "test.somepage.com" | ||
}, | ||
"imp": [{ | ||
"id": "my-imp-id", | ||
"video": { | ||
"mimes": ["video/mp4"] | ||
}, | ||
"ext": { | ||
"appnexus": { | ||
"placementId": 1 | ||
} | ||
} | ||
}], | ||
"user": { | ||
"buyeruid": "some-buyer-id", | ||
"geo": { | ||
"country": "FRA" | ||
} | ||
} | ||
} | ||
}, | ||
"outgoingRequests": { | ||
"appnexus": { | ||
"expectRequest": { | ||
"ortbRequest": { | ||
"id": "some-request-id", | ||
"site": { | ||
"page": "test.somepage.com" | ||
}, | ||
"imp": [{ | ||
"id": "my-imp-id", | ||
"video": { | ||
"mimes": ["video/mp4"] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"placementId": 1 | ||
} | ||
} | ||
}], | ||
"user": { | ||
"geo": { | ||
"country": "FRA" | ||
} | ||
} | ||
}, | ||
"bidAdjustment": 1.0 | ||
}, | ||
"mockResponse": { | ||
"errors": ["appnexus-error"] | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
{ | ||
"assume_gdpr_applies": true, | ||
"incomingRequest": { | ||
"ortbRequest": { | ||
"id": "some-request-id", | ||
"site": { | ||
"page": "test.somepage.com" | ||
}, | ||
"imp": [{ | ||
"id": "my-imp-id", | ||
"video": { | ||
"mimes": ["video/mp4"] | ||
}, | ||
"ext": { | ||
"appnexus": { | ||
"placementId": 1 | ||
} | ||
} | ||
}], | ||
"user": { | ||
"buyeruid": "some-buyer-id", | ||
"geo": { | ||
"country": "FRA" | ||
} | ||
} | ||
} | ||
}, | ||
"outgoingRequests": { | ||
"appnexus": { | ||
"expectRequest": { | ||
"ortbRequest": { | ||
"id": "some-request-id", | ||
"site": { | ||
"page": "test.somepage.com" | ||
}, | ||
"imp": [{ | ||
"id": "my-imp-id", | ||
"video": { | ||
"mimes": ["video/mp4"] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"placementId": 1 | ||
} | ||
} | ||
}], | ||
"user": { | ||
"geo": { | ||
"country": "FRA" | ||
} | ||
} | ||
}, | ||
"bidAdjustment": 1.0 | ||
}, | ||
"mockResponse": { | ||
"errors": ["appnexus-error"] | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
{ | ||
"assume_gdpr_applies": false, | ||
"incomingRequest": { | ||
"ortbRequest": { | ||
"id": "some-request-id", | ||
"site": { | ||
"page": "test.somepage.com" | ||
}, | ||
"imp": [{ | ||
"id": "my-imp-id", | ||
"video": { | ||
"mimes": ["video/mp4"] | ||
}, | ||
"ext": { | ||
"appnexus": { | ||
"placementId": 1 | ||
} | ||
} | ||
}], | ||
"user": { | ||
"buyeruid": "some-buyer-id", | ||
"geo": { | ||
"country": "USA" | ||
} | ||
} | ||
} | ||
}, | ||
"outgoingRequests": { | ||
"appnexus": { | ||
"expectRequest": { | ||
"ortbRequest": { | ||
"id": "some-request-id", | ||
"site": { | ||
"page": "test.somepage.com" | ||
}, | ||
"imp": [{ | ||
"id": "my-imp-id", | ||
"video": { | ||
"mimes": ["video/mp4"] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"placementId": 1 | ||
} | ||
} | ||
}], | ||
"user": { | ||
"buyeruid": "some-buyer-id", | ||
"geo": { | ||
"country": "USA" | ||
} | ||
} | ||
}, | ||
"bidAdjustment": 1.0 | ||
}, | ||
"mockResponse": { | ||
"errors": ["appnexus-error"] | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect! Thank you.