-
Notifications
You must be signed in to change notification settings - Fork 812
CCPA Phase 2: Enforcement #1138
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
Changes from 2 commits
ace7f43
534c39a
a219ded
a237cf7
675efaf
7e25811
75179b3
c810703
6b43612
f074e60
db833a1
44da58e
dc94e39
40bcddc
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 |
---|---|---|
|
@@ -47,6 +47,7 @@ type Configuration struct { | |
Analytics Analytics `mapstructure:"analytics"` | ||
AMPTimeoutAdjustment int64 `mapstructure:"amp_timeout_adjustment_ms"` | ||
GDPR GDPR `mapstructure:"gdpr"` | ||
CCPA CCPA `mapstructure:"ccpa"` | ||
CurrencyConverter CurrencyConverter `mapstructure:"currency_converter"` | ||
DefReqConfig DefReqConfig `mapstructure:"default_request"` | ||
|
||
|
@@ -160,6 +161,10 @@ func (t *GDPRTimeouts) ActiveTimeout() time.Duration { | |
return time.Duration(t.ActiveVendorlistFetch) * time.Millisecond | ||
} | ||
|
||
type CCPA 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. FYI: This looks to be a long term option, with additional config coming in future PRs. You may want to re-review in that context. I don't want to merge GDPR and CCPA in the config object because I don't want to break existing GDPR config. |
||
Enforce bool `mapstructure:"enforce"` | ||
} | ||
|
||
type Analytics struct { | ||
File FileLogs `mapstructure:"file"` | ||
} | ||
|
@@ -706,6 +711,7 @@ func SetupViper(v *viper.Viper, filename string) { | |
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{""}) | ||
v.SetDefault("ccpa.enforce", false) | ||
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. Default to disabled for now. Will change in the future when the Prebid.org CCPA enforcement spec is finalized. |
||
v.SetDefault("currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json") | ||
v.SetDefault("currency_converter.fetch_interval_seconds", 1800) // fetch currency rates every 30 minutes | ||
v.SetDefault("default_request.type", "") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -382,17 +382,14 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope | |
req.Imp[0].TagID = slot | ||
} | ||
|
||
gdprConsent := getQueryParam(httpRequest, "gdpr_consent") | ||
ccpaValue := getQueryParam(httpRequest, "us_privacy") | ||
privacyPolicies := privacy.Policies{ | ||
GDPR: gdpr.Policy{ | ||
Consent: gdprConsent, | ||
Consent: getQueryParam(httpRequest, "gdpr_consent"), | ||
}, | ||
CCPA: ccpa.Policy{ | ||
Value: ccpaValue, | ||
Value: getQueryParam(httpRequest, "us_privacy"), | ||
}, | ||
} | ||
|
||
if err := privacyPolicies.Write(req); err != nil { | ||
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. The following was tested in the Go playground:
Output is:
Therefore, is fair to assume
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. Thank you very much. Changed. |
||
return err | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ import ( | |
"github.com/prebid/prebid-server/openrtb_ext" | ||
"github.com/prebid/prebid-server/pbsmetrics" | ||
"github.com/prebid/prebid-server/prebid" | ||
"github.com/prebid/prebid-server/privacy/ccpa" | ||
"github.com/prebid/prebid-server/stored_requests" | ||
"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" | ||
"github.com/prebid/prebid-server/usersync" | ||
|
@@ -301,6 +302,17 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error { | |
return errL | ||
} | ||
|
||
ccpaPolicy, ccpaPolicyErr := ccpa.ReadPolicy(req) | ||
if ccpaPolicyErr != nil { | ||
errL = append(errL, ccpaPolicyErr) | ||
return errL | ||
} | ||
|
||
if err := ccpaPolicy.Validate(); err != nil { | ||
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. Per the guidance I have so far, an invalid CCPA value can be ignored but I think a warning would be useful. Right now, warnings are not included in the bid response, but I view that as a separate issue. |
||
errL = append(errL, err) | ||
return errL | ||
} | ||
|
||
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.
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. You're right. I consider this part of 'transition pain'. I'm trying to keep all privacy logic in each respective privacy policy and will move the GDPR and COPPA logic there too, but that's a bit too much for this PR. Do you think this will cause a major performance issue? I could restructure a few things for now to avoid the extra unmarshal if you feel it's better. 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. No, I'll think it'd be perfectly fine to refactor in another PR latter 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. 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. Nope. That code isn't reachable. It will only get triggered if the |
||
impIDs := make(map[string]int, len(req.Imp)) | ||
for index := range req.Imp { | ||
imp := &req.Imp[index] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
{ | ||
"message": "Invalid request: request.regs.ext.us_privacy must contain 4 characters\n", | ||
"requestPayload": { | ||
"id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5", | ||
"site": { | ||
"page": "prebid.org", | ||
"publisher": { | ||
"id": "a3de7af2-a86a-4043-a77b-c7e86744155e" | ||
} | ||
}, | ||
"source": { | ||
"tid": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5" | ||
}, | ||
"tmax": 1000, | ||
"imp": [ | ||
{ | ||
"id": "/19968336/header-bid-tag-0", | ||
"ext": { | ||
"appnexus": { | ||
"placementId": 10433394 | ||
} | ||
}, | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 300, | ||
"h": 250 | ||
}, | ||
{ | ||
"w": 300, | ||
"h": 300 | ||
} | ||
] | ||
} | ||
} | ||
], | ||
"regs": { | ||
"ext": { | ||
"us_privacy": "invalid by length" | ||
} | ||
}, | ||
"user": { | ||
"ext": {} | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
{ | ||
"message": "Invalid request: request.regs.ext is invalid: json: cannot unmarshal string into Go value of type openrtb_ext.ExtRegs\n", | ||
"requestPayload": { | ||
"id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5", | ||
"site": { | ||
"page": "prebid.org", | ||
"publisher": { | ||
"id": "a3de7af2-a86a-4043-a77b-c7e86744155e" | ||
} | ||
}, | ||
"source": { | ||
"tid": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5" | ||
}, | ||
"tmax": 1000, | ||
"imp": [ | ||
{ | ||
"id": "/19968336/header-bid-tag-0", | ||
"ext": { | ||
"appnexus": { | ||
"placementId": 10433394 | ||
} | ||
}, | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 300, | ||
"h": 250 | ||
}, | ||
{ | ||
"w": 300, | ||
"h": 300 | ||
} | ||
] | ||
} | ||
} | ||
], | ||
"regs": { | ||
"ext": "malformed" | ||
}, | ||
"user": { | ||
"ext": {} | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,8 @@ | |
}, | ||
"regs": { | ||
"ext": { | ||
"gdpr": 1 | ||
"gdpr": 1, | ||
"us_privacy": "1NYN" | ||
} | ||
}, | ||
"imp": [ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"id": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5", | ||
"site": { | ||
"page": "prebid.org", | ||
"publisher": { | ||
"id": "a3de7af2-a86a-4043-a77b-c7e86744155e" | ||
} | ||
}, | ||
"source": { | ||
"tid": "b9c97a4b-cbc4-483d-b2c4-58a19ed5cfc5" | ||
}, | ||
"tmax": 1000, | ||
"imp": [ | ||
{ | ||
"id": "/19968336/header-bid-tag-0", | ||
"ext": { | ||
"appnexus": { | ||
"placementId": 10433394 | ||
} | ||
}, | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 300, | ||
"h": 250 | ||
}, | ||
{ | ||
"w": 300, | ||
"h": 300 | ||
} | ||
] | ||
} | ||
} | ||
], | ||
"regs": { | ||
"ext": { | ||
"us_privacy": "1NYN" | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,7 @@ type exchange struct { | |
currencyConverter *currencies.RateConverter | ||
UsersyncIfAmbiguous bool | ||
defaultTTLs config.DefaultTTLs | ||
enforceCCPA bool | ||
} | ||
|
||
// Container to pass out response ext data from the GetAllBids goroutines back into the main thread | ||
|
@@ -71,6 +72,7 @@ func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *con | |
e.currencyConverter = currencyConverter | ||
e.UsersyncIfAmbiguous = cfg.GDPR.UsersyncIfAmbiguous | ||
e.defaultTTLs = cfg.CacheURL.DefaultTTLs | ||
e.enforceCCPA = cfg.CCPA.Enforce | ||
return e | ||
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. I realize this change would imply a thorough refactoring effort that is out of the scope of this PR so we don't have to do it right now, but I believe we could be better off if we consolidate all the privacy enforcement in a single class with its own fields and methods
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 exactly the goal I'm working towards. Baby steps. |
||
} | ||
|
||
|
@@ -93,7 +95,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque | |
|
||
// 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, errs := cleanOpenRTBRequests(ctx, bidRequest, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous) | ||
cleanRequests, aliases, errs := cleanOpenRTBRequests(ctx, bidRequest, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.enforceCCPA) | ||
|
||
// List of bidders we have requests for. | ||
liveAdapters := listBiddersWithRequests(cleanRequests) | ||
|
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.
CCPA is a California State Bill, GDPR is a European law, what if other Privacy laws start getting enforced elsewhere like in Asia, Latin America or even another State of the Union? Do you think it makes more sense to put all of these privacy data structure under a single struct? We could call it
PrivacyEnforcement
,PrivacyConfig
, or something else.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.
I agree with you on establishing a more common approach. This PR expands on the new
privacy
package to do just that. This here is just a temporary app config until Prebid.org devises an official enforcement stance.