-
Notifications
You must be signed in to change notification settings - Fork 735
/
account.go
374 lines (321 loc) · 17.3 KB
/
account.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
package config
import (
"encoding/json"
"fmt"
"math"
"strings"
"github.com/prebid/go-gdpr/consentconstants"
"github.com/prebid/prebid-server/v2/openrtb_ext"
"github.com/prebid/prebid-server/v2/util/iputil"
)
// ChannelType enumerates the values of integrations Prebid Server can configure for an account
type ChannelType string
// Possible values of channel types Prebid Server can configure for an account
const (
ChannelAMP ChannelType = "amp"
ChannelApp ChannelType = "app"
ChannelVideo ChannelType = "video"
ChannelWeb ChannelType = "web"
ChannelDOOH ChannelType = "dooh"
)
// Account represents a publisher account configuration
type Account struct {
ID string `mapstructure:"id" json:"id"`
Disabled bool `mapstructure:"disabled" json:"disabled"`
CacheTTL DefaultTTLs `mapstructure:"cache_ttl" json:"cache_ttl"`
CCPA AccountCCPA `mapstructure:"ccpa" json:"ccpa"`
GDPR AccountGDPR `mapstructure:"gdpr" json:"gdpr"`
DebugAllow bool `mapstructure:"debug_allow" json:"debug_allow"`
DefaultIntegration string `mapstructure:"default_integration" json:"default_integration"`
CookieSync CookieSync `mapstructure:"cookie_sync" json:"cookie_sync"`
Events Events `mapstructure:"events" json:"events"` // Don't enable this feature. It is still under developmment - https://github.com/prebid/prebid-server/issues/1725
TruncateTargetAttribute *int `mapstructure:"truncate_target_attr" json:"truncate_target_attr"`
AlternateBidderCodes *openrtb_ext.ExtAlternateBidderCodes `mapstructure:"alternatebiddercodes" json:"alternatebiddercodes"`
Hooks AccountHooks `mapstructure:"hooks" json:"hooks"`
PriceFloors AccountPriceFloors `mapstructure:"price_floors" json:"price_floors"`
Validations Validations `mapstructure:"validations" json:"validations"`
DefaultBidLimit int `mapstructure:"default_bid_limit" json:"default_bid_limit"`
BidAdjustments *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"`
Privacy AccountPrivacy `mapstructure:"privacy" json:"privacy"`
}
// CookieSync represents the account-level defaults for the cookie sync endpoint.
type CookieSync struct {
DefaultLimit *int `mapstructure:"default_limit" json:"default_limit"`
MaxLimit *int `mapstructure:"max_limit" json:"max_limit"`
DefaultCoopSync *bool `mapstructure:"default_coop_sync" json:"default_coop_sync"`
}
// AccountCCPA represents account-specific CCPA configuration
type AccountCCPA struct {
Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"`
ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"`
}
type AccountPriceFloors struct {
Enabled bool `mapstructure:"enabled" json:"enabled"`
EnforceFloorsRate int `mapstructure:"enforce_floors_rate" json:"enforce_floors_rate"`
AdjustForBidAdjustment bool `mapstructure:"adjust_for_bid_adjustment" json:"adjust_for_bid_adjustment"`
EnforceDealFloors bool `mapstructure:"enforce_deal_floors" json:"enforce_deal_floors"`
UseDynamicData bool `mapstructure:"use_dynamic_data" json:"use_dynamic_data"`
MaxRule int `mapstructure:"max_rules" json:"max_rules"`
MaxSchemaDims int `mapstructure:"max_schema_dims" json:"max_schema_dims"`
Fetcher AccountFloorFetch `mapstructure:"fetch" json:"fetch"`
}
// AccountFloorFetch defines the configuration for dynamic floors fetching.
type AccountFloorFetch struct {
Enabled bool `mapstructure:"enabled" json:"enabled"`
URL string `mapstructure:"url" json:"url"`
Timeout int `mapstructure:"timeout_ms" json:"timeout_ms"`
MaxFileSizeKB int `mapstructure:"max_file_size_kb" json:"max_file_size_kb"`
MaxRules int `mapstructure:"max_rules" json:"max_rules"`
MaxAge int `mapstructure:"max_age_sec" json:"max_age_sec"`
Period int `mapstructure:"period_sec" json:"period_sec"`
MaxSchemaDims int `mapstructure:"max_schema_dims" json:"max_schema_dims"`
AccountID string `mapstructure:"accountID" json:"accountID"`
}
func (pf *AccountPriceFloors) validate(errs []error) []error {
if pf.EnforceFloorsRate < 0 || pf.EnforceFloorsRate > 100 {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.enforce_floors_rate should be between 0 and 100`))
}
if pf.MaxRule < 0 || pf.MaxRule > math.MaxInt32 {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.max_rules should be between 0 and %v`, math.MaxInt32))
}
if pf.MaxSchemaDims < 0 || pf.MaxSchemaDims > 20 {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.max_schema_dims should be between 0 and 20`))
}
if pf.Fetcher.Period > pf.Fetcher.MaxAge {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.period_sec should be less than account_defaults.price_floors.fetch.max_age_sec`))
}
if pf.Fetcher.Period < 300 {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.period_sec should not be less than 300 seconds`))
}
if pf.Fetcher.MaxAge < 600 {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_age_sec should not be less than 600 seconds and greater than maximum integer value`))
}
if !(pf.Fetcher.Timeout > 10 && pf.Fetcher.Timeout < 10000) {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.timeout_ms should be between 10 to 10,000 miliseconds`))
}
if pf.Fetcher.MaxRules < 0 {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_rules should be greater than or equal to 0`))
}
if pf.Fetcher.MaxFileSizeKB < 0 {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_file_size_kb should be greater than or equal to 0`))
}
if !(pf.Fetcher.MaxSchemaDims >= 0 && pf.Fetcher.MaxSchemaDims < 20) {
errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_schema_dims should not be less than 0 and greater than 20`))
}
return errs
}
func (pf *AccountPriceFloors) IsAdjustForBidAdjustmentEnabled() bool {
return pf.AdjustForBidAdjustment
}
// EnabledForChannelType indicates whether CCPA is turned on at the account level for the specified channel type
// by using the channel type setting if defined or the general CCPA setting if defined; otherwise it returns nil
func (a *AccountCCPA) EnabledForChannelType(channelType ChannelType) *bool {
if channelEnabled := a.ChannelEnabled.GetByChannelType(channelType); channelEnabled != nil {
return channelEnabled
}
return a.Enabled
}
// AccountGDPR represents account-specific GDPR configuration
type AccountGDPR struct {
Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"`
ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"`
// Array of basic enforcement vendors that is used to create the hash table so vendor names can be instantly accessed
BasicEnforcementVendors []string `mapstructure:"basic_enforcement_vendors" json:"basic_enforcement_vendors"`
BasicEnforcementVendorsMap map[string]struct{}
Purpose1 AccountGDPRPurpose `mapstructure:"purpose1" json:"purpose1"`
Purpose2 AccountGDPRPurpose `mapstructure:"purpose2" json:"purpose2"`
Purpose3 AccountGDPRPurpose `mapstructure:"purpose3" json:"purpose3"`
Purpose4 AccountGDPRPurpose `mapstructure:"purpose4" json:"purpose4"`
Purpose5 AccountGDPRPurpose `mapstructure:"purpose5" json:"purpose5"`
Purpose6 AccountGDPRPurpose `mapstructure:"purpose6" json:"purpose6"`
Purpose7 AccountGDPRPurpose `mapstructure:"purpose7" json:"purpose7"`
Purpose8 AccountGDPRPurpose `mapstructure:"purpose8" json:"purpose8"`
Purpose9 AccountGDPRPurpose `mapstructure:"purpose9" json:"purpose9"`
Purpose10 AccountGDPRPurpose `mapstructure:"purpose10" json:"purpose10"`
// Hash table of purpose configs for convenient purpose config lookup
PurposeConfigs map[consentconstants.Purpose]*AccountGDPRPurpose
PurposeOneTreatment AccountGDPRPurposeOneTreatment `mapstructure:"purpose_one_treatment" json:"purpose_one_treatment"`
SpecialFeature1 AccountGDPRSpecialFeature `mapstructure:"special_feature1" json:"special_feature1"`
}
// EnabledForChannelType indicates whether GDPR is turned on at the account level for the specified channel type
// by using the channel type setting if defined or the general GDPR setting if defined; otherwise it returns nil.
func (a *AccountGDPR) EnabledForChannelType(channelType ChannelType) *bool {
if channelEnabled := a.ChannelEnabled.GetByChannelType(channelType); channelEnabled != nil {
return channelEnabled
}
return a.Enabled
}
// FeatureOneEnforced gets the account level feature one enforced setting returning the value and whether or not it
// was set. If not set, a default value of true is returned matching host default behavior.
func (a *AccountGDPR) FeatureOneEnforced() (value, exists bool) {
if a.SpecialFeature1.Enforce == nil {
return true, false
}
return *a.SpecialFeature1.Enforce, true
}
// FeatureOneVendorException checks if the given bidder is a vendor exception.
func (a *AccountGDPR) FeatureOneVendorException(bidder openrtb_ext.BidderName) (value, exists bool) {
if a.SpecialFeature1.VendorExceptionMap == nil {
return false, false
}
_, found := a.SpecialFeature1.VendorExceptionMap[bidder]
return found, true
}
// PurposeEnforced checks if full enforcement is turned on for a given purpose at the account level. It returns the
// enforcement strategy type and whether or not it is set on the account. If not set, a default value of true is
// returned matching host default behavior.
func (a *AccountGDPR) PurposeEnforced(purpose consentconstants.Purpose) (value, exists bool) {
if a.PurposeConfigs[purpose] == nil {
return true, false
}
if a.PurposeConfigs[purpose].EnforcePurpose == nil {
return true, false
}
return *a.PurposeConfigs[purpose].EnforcePurpose, true
}
// PurposeEnforcementAlgo checks the purpose enforcement algo for a given purpose by first
// looking at the account settings, and if not set there, defaulting to the host configuration.
func (a *AccountGDPR) PurposeEnforcementAlgo(purpose consentconstants.Purpose) (value TCF2EnforcementAlgo, exists bool) {
var c *AccountGDPRPurpose
c, exists = a.PurposeConfigs[purpose]
if exists && (c.EnforceAlgoID == TCF2BasicEnforcement || c.EnforceAlgoID == TCF2FullEnforcement) {
return c.EnforceAlgoID, true
}
return TCF2UndefinedEnforcement, false
}
// PurposeEnforcingVendors gets the account level enforce vendors setting for a given purpose returning the value and
// whether or not it is set. If not set, a default value of true is returned matching host default behavior.
func (a *AccountGDPR) PurposeEnforcingVendors(purpose consentconstants.Purpose) (value, exists bool) {
if a.PurposeConfigs[purpose] == nil {
return true, false
}
if a.PurposeConfigs[purpose].EnforceVendors == nil {
return true, false
}
return *a.PurposeConfigs[purpose].EnforceVendors, true
}
// PurposeVendorExceptions returns the vendor exception map for a given purpose.
func (a *AccountGDPR) PurposeVendorExceptions(purpose consentconstants.Purpose) (value map[string]struct{}, exists bool) {
c, exists := a.PurposeConfigs[purpose]
if exists && c.VendorExceptionMap != nil {
return c.VendorExceptionMap, true
}
return nil, false
}
// PurposeOneTreatmentEnabled gets the account level purpose one treatment enabled setting returning the value and
// whether or not it is set. If not set, a default value of true is returned matching host default behavior.
func (a *AccountGDPR) PurposeOneTreatmentEnabled() (value, exists bool) {
if a.PurposeOneTreatment.Enabled == nil {
return true, false
}
return *a.PurposeOneTreatment.Enabled, true
}
// PurposeOneTreatmentAccessAllowed gets the account level purpose one treatment access allowed setting returning the
// value and whether or not it is set. If not set, a default value of true is returned matching host default behavior.
func (a *AccountGDPR) PurposeOneTreatmentAccessAllowed() (value, exists bool) {
if a.PurposeOneTreatment.AccessAllowed == nil {
return true, false
}
return *a.PurposeOneTreatment.AccessAllowed, true
}
// AccountGDPRPurpose represents account-specific GDPR purpose configuration
type AccountGDPRPurpose struct {
EnforceAlgo string `mapstructure:"enforce_algo" json:"enforce_algo,omitempty"`
// Integer representation of enforcement algo for performance improvement on compares
EnforceAlgoID TCF2EnforcementAlgo
EnforcePurpose *bool `mapstructure:"enforce_purpose" json:"enforce_purpose,omitempty"`
EnforceVendors *bool `mapstructure:"enforce_vendors" json:"enforce_vendors,omitempty"`
// Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed
VendorExceptions []string `mapstructure:"vendor_exceptions" json:"vendor_exceptions"`
VendorExceptionMap map[string]struct{}
}
// AccountGDPRSpecialFeature represents account-specific GDPR special feature configuration
type AccountGDPRSpecialFeature struct {
Enforce *bool `mapstructure:"enforce" json:"enforce"`
// Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed
VendorExceptions []openrtb_ext.BidderName `mapstructure:"vendor_exceptions" json:"vendor_exceptions"`
VendorExceptionMap map[openrtb_ext.BidderName]struct{}
}
// AccountGDPRPurposeOneTreatment represents account-specific GDPR purpose one treatment configuration
type AccountGDPRPurposeOneTreatment struct {
Enabled *bool `mapstructure:"enabled"`
AccessAllowed *bool `mapstructure:"access_allowed"`
}
// AccountChannel indicates whether a particular privacy policy (GDPR, CCPA) is enabled for each channel type
type AccountChannel 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"`
DOOH *bool `mapstructure:"dooh" json:"dooh,omitempty"`
}
// GetByChannelType looks up the account integration enabled setting for the specified channel type
func (a *AccountChannel) GetByChannelType(channelType ChannelType) *bool {
var channelEnabled *bool
switch channelType {
case ChannelAMP:
channelEnabled = a.AMP
case ChannelApp:
channelEnabled = a.App
case ChannelVideo:
channelEnabled = a.Video
case ChannelWeb:
channelEnabled = a.Web
case ChannelDOOH:
channelEnabled = a.DOOH
}
return channelEnabled
}
// AccountHooks represents account-specific hooks configuration
type AccountHooks struct {
Modules AccountModules `mapstructure:"modules" json:"modules"`
ExecutionPlan HookExecutionPlan `mapstructure:"execution_plan" json:"execution_plan"`
}
// AccountModules mapping provides account-level module configuration
// format: map[vendor_name]map[module_name]json.RawMessage
type AccountModules map[string]map[string]json.RawMessage
// ModuleConfig returns the account-level module config.
// The id argument must be passed in the form "vendor.module_name",
// otherwise an error is returned.
func (m AccountModules) ModuleConfig(id string) (json.RawMessage, error) {
ns := strings.SplitN(id, ".", 2)
if len(ns) < 2 {
return nil, fmt.Errorf("ID must consist of vendor and module names separated by dot, got: %s", id)
}
vendor := ns[0]
module := ns[1]
return m[vendor][module], nil
}
type AccountPrivacy struct {
AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"`
IPv6Config IPv6 `mapstructure:"ipv6" json:"ipv6"`
IPv4Config IPv4 `mapstructure:"ipv4" json:"ipv4"`
PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox" json:"privacysandbox"`
}
type PrivacySandbox struct {
CookieDeprecation CookieDeprecation `mapstructure:"cookiedeprecation"`
}
type CookieDeprecation struct {
Enabled bool `mapstructure:"enabled"`
TTLSec int `mapstructure:"ttl_sec"`
}
type IPv6 struct {
AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"`
}
type IPv4 struct {
AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"`
}
func (ip *IPv6) Validate(errs []error) []error {
if ip.AnonKeepBits > iputil.IPv6BitSize || ip.AnonKeepBits < 0 {
err := fmt.Errorf("bits cannot exceed %d in ipv6 address, or be less than 0", iputil.IPv6BitSize)
errs = append(errs, err)
}
return errs
}
func (ip *IPv4) Validate(errs []error) []error {
if ip.AnonKeepBits > iputil.IPv4BitSize || ip.AnonKeepBits < 0 {
err := fmt.Errorf("bits cannot exceed %d in ipv4 address, or be less than 0", iputil.IPv4BitSize)
errs = append(errs, err)
}
return errs
}