github.com/prebid/prebid-server@v0.275.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/openrtb_ext" 11 "github.com/prebid/prebid-server/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 ) 24 25 // Account represents a publisher account configuration 26 type Account struct { 27 ID string `mapstructure:"id" json:"id"` 28 Disabled bool `mapstructure:"disabled" json:"disabled"` 29 CacheTTL DefaultTTLs `mapstructure:"cache_ttl" json:"cache_ttl"` 30 EventsEnabled *bool `mapstructure:"events_enabled" json:"events_enabled"` // Deprecated: Use events.enabled instead. 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 IntegrationEnabled AccountChannel `mapstructure:"integration_enabled" json:"integration_enabled"` 58 ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"` 59 } 60 61 type AccountPriceFloors struct { 62 Enabled bool `mapstructure:"enabled" json:"enabled"` 63 EnforceFloorsRate int `mapstructure:"enforce_floors_rate" json:"enforce_floors_rate"` 64 AdjustForBidAdjustment bool `mapstructure:"adjust_for_bid_adjustment" json:"adjust_for_bid_adjustment"` 65 EnforceDealFloors bool `mapstructure:"enforce_deal_floors" json:"enforce_deal_floors"` 66 UseDynamicData bool `mapstructure:"use_dynamic_data" json:"use_dynamic_data"` 67 MaxRule int `mapstructure:"max_rules" json:"max_rules"` 68 MaxSchemaDims int `mapstructure:"max_schema_dims" json:"max_schema_dims"` 69 } 70 71 func (pf *AccountPriceFloors) validate(errs []error) []error { 72 73 if pf.EnforceFloorsRate < 0 || pf.EnforceFloorsRate > 100 { 74 errs = append(errs, fmt.Errorf(`account_defaults.price_floors.enforce_floors_rate should be between 0 and 100`)) 75 } 76 77 if pf.MaxRule < 0 || pf.MaxRule > math.MaxInt32 { 78 errs = append(errs, fmt.Errorf(`account_defaults.price_floors.max_rules should be between 0 and %v`, math.MaxInt32)) 79 } 80 81 if pf.MaxSchemaDims < 0 || pf.MaxSchemaDims > 20 { 82 errs = append(errs, fmt.Errorf(`account_defaults.price_floors.max_schema_dims should be between 0 and 20`)) 83 } 84 85 return errs 86 } 87 88 func (pf *AccountPriceFloors) IsAdjustForBidAdjustmentEnabled() bool { 89 return pf.AdjustForBidAdjustment 90 } 91 92 // EnabledForChannelType indicates whether CCPA is turned on at the account level for the specified channel type 93 // by using the channel type setting if defined or the general CCPA setting if defined; otherwise it returns nil 94 func (a *AccountCCPA) EnabledForChannelType(channelType ChannelType) *bool { 95 if channelEnabled := a.ChannelEnabled.GetByChannelType(channelType); channelEnabled != nil { 96 return channelEnabled 97 } else if integrationEnabled := a.IntegrationEnabled.GetByChannelType(channelType); integrationEnabled != nil { 98 return integrationEnabled 99 } 100 return a.Enabled 101 } 102 103 // AccountGDPR represents account-specific GDPR configuration 104 type AccountGDPR struct { 105 Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"` 106 IntegrationEnabled AccountChannel `mapstructure:"integration_enabled" json:"integration_enabled"` 107 ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"` 108 // Array of basic enforcement vendors that is used to create the hash table so vendor names can be instantly accessed 109 BasicEnforcementVendors []string `mapstructure:"basic_enforcement_vendors" json:"basic_enforcement_vendors"` 110 BasicEnforcementVendorsMap map[string]struct{} 111 Purpose1 AccountGDPRPurpose `mapstructure:"purpose1" json:"purpose1"` 112 Purpose2 AccountGDPRPurpose `mapstructure:"purpose2" json:"purpose2"` 113 Purpose3 AccountGDPRPurpose `mapstructure:"purpose3" json:"purpose3"` 114 Purpose4 AccountGDPRPurpose `mapstructure:"purpose4" json:"purpose4"` 115 Purpose5 AccountGDPRPurpose `mapstructure:"purpose5" json:"purpose5"` 116 Purpose6 AccountGDPRPurpose `mapstructure:"purpose6" json:"purpose6"` 117 Purpose7 AccountGDPRPurpose `mapstructure:"purpose7" json:"purpose7"` 118 Purpose8 AccountGDPRPurpose `mapstructure:"purpose8" json:"purpose8"` 119 Purpose9 AccountGDPRPurpose `mapstructure:"purpose9" json:"purpose9"` 120 Purpose10 AccountGDPRPurpose `mapstructure:"purpose10" json:"purpose10"` 121 // Hash table of purpose configs for convenient purpose config lookup 122 PurposeConfigs map[consentconstants.Purpose]*AccountGDPRPurpose 123 PurposeOneTreatment AccountGDPRPurposeOneTreatment `mapstructure:"purpose_one_treatment" json:"purpose_one_treatment"` 124 SpecialFeature1 AccountGDPRSpecialFeature `mapstructure:"special_feature1" json:"special_feature1"` 125 } 126 127 // EnabledForChannelType indicates whether GDPR is turned on at the account level for the specified channel type 128 // by using the channel type setting if defined or the general GDPR setting if defined; otherwise it returns nil. 129 func (a *AccountGDPR) EnabledForChannelType(channelType ChannelType) *bool { 130 if channelEnabled := a.ChannelEnabled.GetByChannelType(channelType); channelEnabled != nil { 131 return channelEnabled 132 } else if integrationEnabled := a.IntegrationEnabled.GetByChannelType(channelType); integrationEnabled != nil { 133 return integrationEnabled 134 } 135 return a.Enabled 136 } 137 138 // FeatureOneEnforced gets the account level feature one enforced setting returning the value and whether or not it 139 // was set. If not set, a default value of true is returned matching host default behavior. 140 func (a *AccountGDPR) FeatureOneEnforced() (value, exists bool) { 141 if a.SpecialFeature1.Enforce == nil { 142 return true, false 143 } 144 return *a.SpecialFeature1.Enforce, true 145 } 146 147 // FeatureOneVendorException checks if the given bidder is a vendor exception. 148 func (a *AccountGDPR) FeatureOneVendorException(bidder openrtb_ext.BidderName) (value, exists bool) { 149 if a.SpecialFeature1.VendorExceptionMap == nil { 150 return false, false 151 } 152 _, found := a.SpecialFeature1.VendorExceptionMap[bidder] 153 154 return found, true 155 } 156 157 // PurposeEnforced checks if full enforcement is turned on for a given purpose at the account level. It returns the 158 // enforcement strategy type and whether or not it is set on the account. If not set, a default value of true is 159 // returned matching host default behavior. 160 func (a *AccountGDPR) PurposeEnforced(purpose consentconstants.Purpose) (value, exists bool) { 161 if a.PurposeConfigs[purpose] == nil { 162 return true, false 163 } 164 if a.PurposeConfigs[purpose].EnforcePurpose == nil { 165 return true, false 166 } 167 return *a.PurposeConfigs[purpose].EnforcePurpose, true 168 } 169 170 // PurposeEnforcementAlgo checks the purpose enforcement algo for a given purpose by first 171 // looking at the account settings, and if not set there, defaulting to the host configuration. 172 func (a *AccountGDPR) PurposeEnforcementAlgo(purpose consentconstants.Purpose) (value TCF2EnforcementAlgo, exists bool) { 173 var c *AccountGDPRPurpose 174 c, exists = a.PurposeConfigs[purpose] 175 176 if exists && (c.EnforceAlgoID == TCF2BasicEnforcement || c.EnforceAlgoID == TCF2FullEnforcement) { 177 return c.EnforceAlgoID, true 178 } 179 return TCF2UndefinedEnforcement, false 180 } 181 182 // PurposeEnforcingVendors gets the account level enforce vendors setting for a given purpose returning the value and 183 // whether or not it is set. If not set, a default value of true is returned matching host default behavior. 184 func (a *AccountGDPR) PurposeEnforcingVendors(purpose consentconstants.Purpose) (value, exists bool) { 185 if a.PurposeConfigs[purpose] == nil { 186 return true, false 187 } 188 if a.PurposeConfigs[purpose].EnforceVendors == nil { 189 return true, false 190 } 191 return *a.PurposeConfigs[purpose].EnforceVendors, true 192 } 193 194 // PurposeVendorExceptions returns the vendor exception map for a given purpose. 195 func (a *AccountGDPR) PurposeVendorExceptions(purpose consentconstants.Purpose) (value map[openrtb_ext.BidderName]struct{}, exists bool) { 196 c, exists := a.PurposeConfigs[purpose] 197 198 if exists && c.VendorExceptionMap != nil { 199 return c.VendorExceptionMap, true 200 } 201 return nil, false 202 } 203 204 // PurposeOneTreatmentEnabled gets the account level purpose one treatment enabled setting returning the value and 205 // whether or not it is set. If not set, a default value of true is returned matching host default behavior. 206 func (a *AccountGDPR) PurposeOneTreatmentEnabled() (value, exists bool) { 207 if a.PurposeOneTreatment.Enabled == nil { 208 return true, false 209 } 210 return *a.PurposeOneTreatment.Enabled, true 211 } 212 213 // PurposeOneTreatmentAccessAllowed gets the account level purpose one treatment access allowed setting returning the 214 // value and whether or not it is set. If not set, a default value of true is returned matching host default behavior. 215 func (a *AccountGDPR) PurposeOneTreatmentAccessAllowed() (value, exists bool) { 216 if a.PurposeOneTreatment.AccessAllowed == nil { 217 return true, false 218 } 219 return *a.PurposeOneTreatment.AccessAllowed, true 220 } 221 222 // AccountGDPRPurpose represents account-specific GDPR purpose configuration 223 type AccountGDPRPurpose struct { 224 EnforceAlgo string `mapstructure:"enforce_algo" json:"enforce_algo,omitempty"` 225 // Integer representation of enforcement algo for performance improvement on compares 226 EnforceAlgoID TCF2EnforcementAlgo 227 EnforcePurpose *bool `mapstructure:"enforce_purpose" json:"enforce_purpose,omitempty"` 228 EnforceVendors *bool `mapstructure:"enforce_vendors" json:"enforce_vendors,omitempty"` 229 // Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed 230 VendorExceptions []openrtb_ext.BidderName `mapstructure:"vendor_exceptions" json:"vendor_exceptions"` 231 VendorExceptionMap map[openrtb_ext.BidderName]struct{} 232 } 233 234 // AccountGDPRSpecialFeature represents account-specific GDPR special feature configuration 235 type AccountGDPRSpecialFeature struct { 236 Enforce *bool `mapstructure:"enforce" json:"enforce"` 237 // Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed 238 VendorExceptions []openrtb_ext.BidderName `mapstructure:"vendor_exceptions" json:"vendor_exceptions"` 239 VendorExceptionMap map[openrtb_ext.BidderName]struct{} 240 } 241 242 // AccountGDPRPurposeOneTreatment represents account-specific GDPR purpose one treatment configuration 243 type AccountGDPRPurposeOneTreatment struct { 244 Enabled *bool `mapstructure:"enabled"` 245 AccessAllowed *bool `mapstructure:"access_allowed"` 246 } 247 248 // AccountChannel indicates whether a particular privacy policy (GDPR, CCPA) is enabled for each channel type 249 type AccountChannel struct { 250 AMP *bool `mapstructure:"amp" json:"amp,omitempty"` 251 App *bool `mapstructure:"app" json:"app,omitempty"` 252 Video *bool `mapstructure:"video" json:"video,omitempty"` 253 Web *bool `mapstructure:"web" json:"web,omitempty"` 254 } 255 256 // GetByChannelType looks up the account integration enabled setting for the specified channel type 257 func (a *AccountChannel) GetByChannelType(channelType ChannelType) *bool { 258 var channelEnabled *bool 259 260 switch channelType { 261 case ChannelAMP: 262 channelEnabled = a.AMP 263 case ChannelApp: 264 channelEnabled = a.App 265 case ChannelVideo: 266 channelEnabled = a.Video 267 case ChannelWeb: 268 channelEnabled = a.Web 269 } 270 271 return channelEnabled 272 } 273 274 // AccountHooks represents account-specific hooks configuration 275 type AccountHooks struct { 276 Modules AccountModules `mapstructure:"modules" json:"modules"` 277 ExecutionPlan HookExecutionPlan `mapstructure:"execution_plan" json:"execution_plan"` 278 } 279 280 // AccountModules mapping provides account-level module configuration 281 // format: map[vendor_name]map[module_name]json.RawMessage 282 type AccountModules map[string]map[string]json.RawMessage 283 284 // ModuleConfig returns the account-level module config. 285 // The id argument must be passed in the form "vendor.module_name", 286 // otherwise an error is returned. 287 func (m AccountModules) ModuleConfig(id string) (json.RawMessage, error) { 288 ns := strings.SplitN(id, ".", 2) 289 if len(ns) < 2 { 290 return nil, fmt.Errorf("ID must consist of vendor and module names separated by dot, got: %s", id) 291 } 292 293 vendor := ns[0] 294 module := ns[1] 295 return m[vendor][module], nil 296 } 297 298 func (a *AccountChannel) IsSet() bool { 299 return a.AMP != nil || a.App != nil || a.Video != nil || a.Web != nil 300 } 301 302 type AccountPrivacy struct { 303 AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"` 304 IPv6Config IPv6 `mapstructure:"ipv6" json:"ipv6"` 305 IPv4Config IPv4 `mapstructure:"ipv4" json:"ipv4"` 306 } 307 308 type IPv6 struct { 309 AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"` 310 } 311 312 type IPv4 struct { 313 AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"` 314 } 315 316 func (ip *IPv6) Validate(errs []error) []error { 317 if ip.AnonKeepBits > iputil.IPv6BitSize || ip.AnonKeepBits < 0 { 318 err := fmt.Errorf("bits cannot exceed %d in ipv6 address, or be less than 0", iputil.IPv6BitSize) 319 errs = append(errs, err) 320 } 321 return errs 322 } 323 324 func (ip *IPv4) Validate(errs []error) []error { 325 if ip.AnonKeepBits > iputil.IPv4BitSize || ip.AnonKeepBits < 0 { 326 err := fmt.Errorf("bits cannot exceed %d in ipv4 address, or be less than 0", iputil.IPv4BitSize) 327 errs = append(errs, err) 328 } 329 return errs 330 }