-
Notifications
You must be signed in to change notification settings - Fork 735
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
CCPA Phase 2: Enforcement #1138
Changes from all 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"` | ||
} | ||
|
@@ -202,6 +207,7 @@ const ( | |
dummyPublisherID string = "12" | ||
dummyGDPR string = "0" | ||
dummyGDPRConsent string = "someGDPRConsentString" | ||
dummyCCPA string = "1NYN" | ||
) | ||
|
||
type Adapter struct { | ||
|
@@ -216,8 +222,9 @@ type Adapter struct { | |
// | ||
// This value will be interpreted as a Golang Template. At runtime, the following Template variables will be replaced. | ||
// | ||
// {{.GDPR}} -- This will be replaced with the "gdpr" property sent to /cookie_sync | ||
// {{.Consent}} -- This will be replaced with the "consent" property sent to /cookie_sync | ||
// {{.GDPR}} -- This will be replaced with the "gdpr" property sent to /cookie_sync | ||
// {{.Consent}} -- This will be replaced with the "consent" property sent to /cookie_sync | ||
// {{.USPrivacy}} -- This will be replaced with the "us_privacy" property sent to /cookie_sync | ||
// | ||
// For more info on templates, see: https://golang.org/pkg/text/template/ | ||
UserSyncURL string `mapstructure:"usersync_url"` | ||
|
@@ -275,7 +282,12 @@ func validateAdapterUserSyncURL(userSyncURL string, adapterName string, errs con | |
return append(errs, fmt.Errorf("Invalid user sync URL template: %s for adapter: %s. %v", userSyncURL, adapterName, err)) | ||
} | ||
// Resolve macros (if any) in the user_sync URL | ||
resolvedUserSyncURL, err := macros.ResolveMacros(*userSyncTemplate, macros.UserSyncTemplateParams{GDPR: dummyGDPR, GDPRConsent: dummyGDPRConsent}) | ||
dummyMacroValues := macros.UserSyncTemplateParams{ | ||
GDPR: dummyGDPR, | ||
GDPRConsent: dummyGDPRConsent, | ||
USPrivacy: dummyCCPA, | ||
} | ||
resolvedUserSyncURL, err := macros.ResolveMacros(*userSyncTemplate, dummyMacroValues) | ||
if err != nil { | ||
return append(errs, fmt.Errorf("Unable to resolve user sync URL: %s for adapter: %s. %v", userSyncURL, adapterName, err)) | ||
} | ||
|
@@ -706,6 +718,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 |
---|---|---|
|
@@ -384,13 +384,12 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope | |
|
||
privacyPolicies := privacy.Policies{ | ||
GDPR: gdpr.Policy{ | ||
Consent: getQueryParam(httpRequest, "gdpr_consent"), | ||
Consent: httpRequest.URL.Query().Get("gdpr_consent"), | ||
}, | ||
CCPA: ccpa.Policy{ | ||
Signal: getQueryParam(httpRequest, "us_privacy"), | ||
Value: httpRequest.URL.Query().Get("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 | ||
} | ||
|
@@ -402,17 +401,6 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope | |
return nil | ||
} | ||
|
||
func getQueryParam(httpRequest *http.Request, name string) string { | ||
values, ok := httpRequest.URL.Query()[name] | ||
|
||
if !ok || len(values) == 0 { | ||
return "" | ||
} | ||
|
||
// return first value of the query param, matching the behavior of httpRequest.FormValue | ||
return values[0] | ||
} | ||
|
||
func makeFormatReplacement(overrideWidth uint64, overrideHeight uint64, width uint64, height uint64, multisize string) []openrtb.Format { | ||
if overrideWidth != 0 && overrideHeight != 0 { | ||
return []openrtb.Format{{ | ||
|
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,16 @@ 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, &errortypes.Warning{Message: fmt.Sprintf("CCPA value is invalid and will be ignored. (%s)", err.Error())}) | ||
} | ||
|
||
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] | ||
|
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.