Skip to content
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

Add account CCPA enabled and per-request-type enabled flags #1566

Merged
merged 7 commits into from
Nov 18, 2020
62 changes: 40 additions & 22 deletions config/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,63 @@ type Account struct {
Disabled bool `mapstructure:"disabled" json:"disabled"`
CacheTTL DefaultTTLs `mapstructure:"cache_ttl" json:"cache_ttl"`
EventsEnabled bool `mapstructure:"events_enabled" json:"events_enabled"`
CCPA AccountCCPA `mapstructure:"ccpa" json:"ccpa"`
GDPR AccountGDPR `mapstructure:"gdpr" json:"gdpr"`
}

// AccountCCPA represents account-specific CCPA configuration
type AccountCCPA struct {
Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"`
IntegrationEnabled AccountIntegration `mapstructure:"integration_enabled" json:"integration_enabled"`
}

// EnabledForIntegrationType indicates whether CCPA is turned on at the account level for the specified integration type
// by using the integration type setting if defined or the general CCPA setting if defined; otherwise it returns nil
func (a *AccountCCPA) EnabledForIntegrationType(integrationType IntegrationType) *bool {
if integrationEnabled := a.IntegrationEnabled.GetByIntegrationType(integrationType); integrationEnabled != nil {
return integrationEnabled
}
return a.Enabled
}

// AccountGDPR represents account-specific GDPR configuration
type AccountGDPR struct {
Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"`
IntegrationEnabled AccountGDPRIntegration `mapstructure:"integration_enabled" json:"integration_enabled"`
Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"`
IntegrationEnabled AccountIntegration `mapstructure:"integration_enabled" json:"integration_enabled"`
}

// EnabledForIntegrationType indicates whether GDPR is turned on at the account level for the specified integration type
// by using the integration type setting if defined or the general GDPR setting if defined; otherwise it returns nil
func (a *AccountGDPR) EnabledForIntegrationType(integrationType IntegrationType) *bool {
var integrationEnabled *bool

switch integrationType {
case IntegrationTypeAMP:
integrationEnabled = a.IntegrationEnabled.AMP
case IntegrationTypeApp:
integrationEnabled = a.IntegrationEnabled.App
case IntegrationTypeVideo:
integrationEnabled = a.IntegrationEnabled.Video
case IntegrationTypeWeb:
integrationEnabled = a.IntegrationEnabled.Web
}

if integrationEnabled != nil {
if integrationEnabled := a.IntegrationEnabled.GetByIntegrationType(integrationType); integrationEnabled != nil {
return integrationEnabled
}
if a.Enabled != nil {
return a.Enabled
}

return nil
return a.Enabled
}

// AccountGDPRIntegration indicates whether GDPR is enabled for each integration type
type AccountGDPRIntegration struct {
// AccountIntegration indicates whether a particular privacy policy (GDPR, CCPA) is enabled for each integration type
type AccountIntegration struct {
AMP *bool `mapstructure:"amp" json:"amp,omitempty"`
App *bool `mapstructure:"app" json:"app,omitempty"`
Video *bool `mapstructure:"video" json:"video,omitempty"`
Web *bool `mapstructure:"web" json:"web,omitempty"`
}

// GetByIntegrationType looks up the account integration enabled setting for the specified integration type
func (a *AccountIntegration) GetByIntegrationType(integrationType IntegrationType) *bool {
var integrationEnabled *bool

switch integrationType {
case IntegrationTypeAMP:
integrationEnabled = a.AMP
case IntegrationTypeApp:
integrationEnabled = a.App
case IntegrationTypeVideo:
integrationEnabled = a.Video
case IntegrationTypeWeb:
integrationEnabled = a.Web
}

return integrationEnabled
}
223 changes: 175 additions & 48 deletions config/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,64 +10,26 @@ func TestAccountGDPREnabledForIntegrationType(t *testing.T) {
trueValue, falseValue := true, false

tests := []struct {
description string
giveIntegrationType IntegrationType
giveGDPREnabled *bool
giveAMPGDPREnabled *bool
giveAppGDPREnabled *bool
giveVideoGDPREnabled *bool
giveWebGDPREnabled *bool
wantEnabled *bool
description string
giveIntegrationType IntegrationType
giveGDPREnabled *bool
giveWebGDPREnabled *bool
wantEnabled *bool
}{
{
description: "GDPR AMP integration enabled, general GDPR disabled",
giveIntegrationType: IntegrationTypeAMP,
giveGDPREnabled: &falseValue,
giveAMPGDPREnabled: &trueValue,
wantEnabled: &trueValue,
},
{
description: "GDPR App integration enabled, general GDPR disabled",
giveIntegrationType: IntegrationTypeApp,
giveGDPREnabled: &falseValue,
giveAppGDPREnabled: &trueValue,
wantEnabled: &trueValue,
},
{
description: "GDPR Video integration enabled, general GDPR disabled",
giveIntegrationType: IntegrationTypeVideo,
giveGDPREnabled: &falseValue,
giveVideoGDPREnabled: &trueValue,
wantEnabled: &trueValue,
},
{
description: "GDPR Web integration enabled, general GDPR disabled",
giveIntegrationType: IntegrationTypeWeb,
giveGDPREnabled: &falseValue,
giveWebGDPREnabled: &trueValue,
wantEnabled: &trueValue,
},
{
description: "Web integration enabled, general GDPR unspecified",
giveIntegrationType: IntegrationTypeWeb,
giveGDPREnabled: nil,
giveWebGDPREnabled: &trueValue,
wantEnabled: &trueValue,
},
{
description: "GDPR Web integration disabled, general GDPR enabled",
giveIntegrationType: IntegrationTypeWeb,
giveGDPREnabled: &trueValue,
giveWebGDPREnabled: &falseValue,
wantEnabled: &falseValue,
},
{
description: "GDPR Web integration disabled, general GDPR unspecified",
giveIntegrationType: IntegrationTypeWeb,
giveGDPREnabled: nil,
giveWebGDPREnabled: &falseValue,
wantEnabled: &falseValue,
},
{
description: "GDPR Web integration unspecified, general GDPR disabled",
giveIntegrationType: IntegrationTypeWeb,
Expand Down Expand Up @@ -95,11 +57,8 @@ func TestAccountGDPREnabledForIntegrationType(t *testing.T) {
account := Account{
GDPR: AccountGDPR{
Enabled: tt.giveGDPREnabled,
IntegrationEnabled: AccountGDPRIntegration{
AMP: tt.giveAMPGDPREnabled,
App: tt.giveAppGDPREnabled,
Video: tt.giveVideoGDPREnabled,
Web: tt.giveWebGDPREnabled,
IntegrationEnabled: AccountIntegration{
Web: tt.giveWebGDPREnabled,
},
},
}
Expand All @@ -114,3 +73,171 @@ func TestAccountGDPREnabledForIntegrationType(t *testing.T) {
}
}
}

func TestAccountCCPAEnabledForIntegrationType(t *testing.T) {
trueValue, falseValue := true, false

tests := []struct {
description string
giveIntegrationType IntegrationType
giveCCPAEnabled *bool
giveWebCCPAEnabled *bool
wantEnabled *bool
}{
{
description: "CCPA Web integration enabled, general CCPA disabled",
giveIntegrationType: IntegrationTypeWeb,
giveCCPAEnabled: &falseValue,
giveWebCCPAEnabled: &trueValue,
wantEnabled: &trueValue,
},
{
description: "CCPA Web integration disabled, general CCPA enabled",
giveIntegrationType: IntegrationTypeWeb,
giveCCPAEnabled: &trueValue,
giveWebCCPAEnabled: &falseValue,
wantEnabled: &falseValue,
},
{
description: "CCPA Web integration unspecified, general CCPA disabled",
giveIntegrationType: IntegrationTypeWeb,
giveCCPAEnabled: &falseValue,
giveWebCCPAEnabled: nil,
wantEnabled: &falseValue,
},
{
description: "CCPA Web integration unspecified, general CCPA enabled",
giveIntegrationType: IntegrationTypeWeb,
giveCCPAEnabled: &trueValue,
giveWebCCPAEnabled: nil,
wantEnabled: &trueValue,
},
{
description: "CCPA Web integration unspecified, general CCPA unspecified",
giveIntegrationType: IntegrationTypeWeb,
giveCCPAEnabled: nil,
giveWebCCPAEnabled: nil,
wantEnabled: nil,
},
}

for _, tt := range tests {
account := Account{
CCPA: AccountCCPA{
Enabled: tt.giveCCPAEnabled,
IntegrationEnabled: AccountIntegration{
Web: tt.giveWebCCPAEnabled,
},
},
}

enabled := account.CCPA.EnabledForIntegrationType(tt.giveIntegrationType)

if tt.wantEnabled == nil {
assert.Nil(t, enabled, tt.description)
} else {
assert.NotNil(t, enabled, tt.description)
assert.Equal(t, *tt.wantEnabled, *enabled, tt.description)
}
}
}

func TestAccountIntegrationGetByIntegrationType(t *testing.T) {
trueValue, falseValue := true, false

tests := []struct {
description string
giveAMPEnabled *bool
giveAppEnabled *bool
giveVideoEnabled *bool
giveWebEnabled *bool
giveIntegrationType IntegrationType
wantEnabled *bool
}{
{
description: "AMP integration setting unspecified, returns nil",
giveIntegrationType: IntegrationTypeAMP,
wantEnabled: nil,
},
{
description: "AMP integration disabled, returns false",
giveAMPEnabled: &falseValue,
giveIntegrationType: IntegrationTypeAMP,
wantEnabled: &falseValue,
},
{
description: "AMP integration enabled, returns true",
giveAMPEnabled: &trueValue,
giveIntegrationType: IntegrationTypeAMP,
wantEnabled: &trueValue,
},
{
description: "App integration setting unspecified, returns nil",
giveIntegrationType: IntegrationTypeApp,
wantEnabled: nil,
},
{
description: "App integration disabled, returns false",
giveAppEnabled: &falseValue,
giveIntegrationType: IntegrationTypeApp,
wantEnabled: &falseValue,
},
{
description: "App integration enabled, returns true",
giveAppEnabled: &trueValue,
giveIntegrationType: IntegrationTypeApp,
wantEnabled: &trueValue,
},
{
description: "Video integration setting unspecified, returns nil",
giveIntegrationType: IntegrationTypeVideo,
wantEnabled: nil,
},
{
description: "Video integration disabled, returns false",
giveVideoEnabled: &falseValue,
giveIntegrationType: IntegrationTypeVideo,
wantEnabled: &falseValue,
},
{
description: "Video integration enabled, returns true",
giveVideoEnabled: &trueValue,
giveIntegrationType: IntegrationTypeVideo,
wantEnabled: &trueValue,
},
{
description: "Web integration setting unspecified, returns nil",
giveIntegrationType: IntegrationTypeWeb,
wantEnabled: nil,
},
{
description: "Web integration disabled, returns false",
giveWebEnabled: &falseValue,
giveIntegrationType: IntegrationTypeWeb,
wantEnabled: &falseValue,
},
{
description: "Web integration enabled, returns true",
giveWebEnabled: &trueValue,
giveIntegrationType: IntegrationTypeWeb,
wantEnabled: &trueValue,
},
}

for _, tt := range tests {
accountIntegration := AccountIntegration{
AMP: tt.giveAMPEnabled,
App: tt.giveAppEnabled,
Video: tt.giveVideoEnabled,
Web: tt.giveWebEnabled,
}

result := accountIntegration.GetByIntegrationType(tt.giveIntegrationType)
if tt.wantEnabled == nil {
assert.Nil(t, result, tt.description)
} else {
assert.NotNil(t, result, tt.description)
assert.Equal(t, *tt.wantEnabled, *result, tt.description)
}
}
}
13 changes: 10 additions & 3 deletions exchange/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func cleanOpenRTBRequests(ctx context.Context,
consent := extractConsent(orig)
ampGDPRException := (labels.RType == pbsmetrics.ReqTypeAMP) && gDPR.AMPException()

ccpaEnforcer, err := extractCCPA(orig, privacyConfig, aliases)
ccpaEnforcer, err := extractCCPA(orig, privacyConfig, account, aliases, integrationTypeMap[labels.RType])
if err != nil {
errs = append(errs, err)
return
Expand Down Expand Up @@ -144,7 +144,14 @@ func gdprEnabled(account *config.Account, privacyConfig config.Privacy, integrat
return privacyConfig.GDPR.Enabled
}

func extractCCPA(orig *openrtb.BidRequest, privacyConfig config.Privacy, aliases map[string]string) (privacy.PolicyEnforcer, error) {
func ccpaEnabled(account *config.Account, privacyConfig config.Privacy, requestType config.IntegrationType) bool {
if accountEnabled := account.CCPA.EnabledForIntegrationType(requestType); accountEnabled != nil {
return *accountEnabled
}
return privacyConfig.CCPA.Enforce
}

func extractCCPA(orig *openrtb.BidRequest, privacyConfig config.Privacy, account *config.Account, aliases map[string]string, requestType config.IntegrationType) (privacy.PolicyEnforcer, error) {
ccpaPolicy, err := ccpa.ReadFromRequest(orig)
if err != nil {
return privacy.NilPolicyEnforcer{}, err
Expand All @@ -157,7 +164,7 @@ func extractCCPA(orig *openrtb.BidRequest, privacyConfig config.Privacy, aliases
}

ccpaEnforcer := privacy.EnabledPolicyEnforcer{
Enabled: privacyConfig.CCPA.Enforce,
Enabled: ccpaEnabled(account, privacyConfig, requestType),
PolicyEnforcer: ccpaParsedPolicy,
}
return ccpaEnforcer, nil
Expand Down
Loading