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