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  }