github.com/prebid/prebid-server@v0.275.0/exchange/utils.go (about) 1 package exchange 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "math/rand" 10 11 "github.com/buger/jsonparser" 12 "github.com/prebid/go-gdpr/vendorconsent" 13 gpplib "github.com/prebid/go-gpp" 14 gppConstants "github.com/prebid/go-gpp/constants" 15 "github.com/prebid/openrtb/v19/openrtb2" 16 17 "github.com/prebid/prebid-server/config" 18 "github.com/prebid/prebid-server/errortypes" 19 "github.com/prebid/prebid-server/firstpartydata" 20 "github.com/prebid/prebid-server/gdpr" 21 "github.com/prebid/prebid-server/metrics" 22 "github.com/prebid/prebid-server/openrtb_ext" 23 "github.com/prebid/prebid-server/privacy" 24 "github.com/prebid/prebid-server/privacy/ccpa" 25 "github.com/prebid/prebid-server/privacy/lmt" 26 "github.com/prebid/prebid-server/schain" 27 "github.com/prebid/prebid-server/stored_responses" 28 "github.com/prebid/prebid-server/util/ptrutil" 29 ) 30 31 var channelTypeMap = map[metrics.RequestType]config.ChannelType{ 32 metrics.ReqTypeAMP: config.ChannelAMP, 33 metrics.ReqTypeORTB2App: config.ChannelApp, 34 metrics.ReqTypeVideo: config.ChannelVideo, 35 metrics.ReqTypeORTB2Web: config.ChannelWeb, 36 } 37 38 const unknownBidder string = "" 39 40 type requestSplitter struct { 41 bidderToSyncerKey map[string]string 42 me metrics.MetricsEngine 43 privacyConfig config.Privacy 44 gdprPermsBuilder gdpr.PermissionsBuilder 45 hostSChainNode *openrtb2.SupplyChainNode 46 bidderInfo config.BidderInfos 47 } 48 49 // cleanOpenRTBRequests splits the input request into requests which are sanitized for each bidder. Intended behavior is: 50 // 51 // 1. BidRequest.Imp[].Ext will only contain the "prebid" field and a "bidder" field which has the params for the intended Bidder. 52 // 2. Every BidRequest.Imp[] requested Bids from the Bidder who keys it. 53 // 3. BidRequest.User.BuyerUID will be set to that Bidder's ID. 54 func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, 55 auctionReq AuctionRequest, 56 requestExt *openrtb_ext.ExtRequest, 57 gdprDefaultValue gdpr.Signal, bidAdjustmentFactors map[string]float64, 58 ) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) { 59 req := auctionReq.BidRequestWrapper 60 aliases, errs := parseAliases(req.BidRequest) 61 if len(errs) > 0 { 62 return 63 } 64 65 allowedBidderRequests = make([]BidderRequest, 0) 66 67 bidderImpWithBidResp := stored_responses.InitStoredBidResponses(req.BidRequest, auctionReq.StoredBidResponses) 68 69 impsByBidder, err := splitImps(req.BidRequest.Imp) 70 if err != nil { 71 errs = []error{err} 72 return 73 } 74 75 aliasesGVLIDs, errs := parseAliasesGVLIDs(req.BidRequest) 76 if len(errs) > 0 { 77 return 78 } 79 80 var allBidderRequests []BidderRequest 81 allBidderRequests, errs = getAuctionBidderRequests(auctionReq, requestExt, rs.bidderToSyncerKey, impsByBidder, aliases, rs.hostSChainNode) 82 83 bidderNameToBidderReq := buildBidResponseRequest(req.BidRequest, bidderImpWithBidResp, aliases, auctionReq.BidderImpReplaceImpID) 84 //this function should be executed after getAuctionBidderRequests 85 allBidderRequests = mergeBidderRequests(allBidderRequests, bidderNameToBidderReq) 86 87 var gpp gpplib.GppContainer 88 if req.BidRequest.Regs != nil && len(req.BidRequest.Regs.GPP) > 0 { 89 gpp, err = gpplib.Parse(req.BidRequest.Regs.GPP) 90 if err != nil { 91 errs = append(errs, err) 92 } 93 } 94 95 if auctionReq.Account.PriceFloors.IsAdjustForBidAdjustmentEnabled() { 96 //Apply BidAdjustmentFactor to imp.BidFloor 97 applyBidAdjustmentToFloor(allBidderRequests, bidAdjustmentFactors) 98 } 99 100 gdprSignal, err := getGDPR(req) 101 if err != nil { 102 errs = append(errs, err) 103 } 104 105 consent, err := getConsent(req, gpp) 106 if err != nil { 107 errs = append(errs, err) 108 } 109 gdprApplies := gdprSignal == gdpr.SignalYes || (gdprSignal == gdpr.SignalAmbiguous && gdprDefaultValue == gdpr.SignalYes) 110 111 ccpaEnforcer, err := extractCCPA(req.BidRequest, rs.privacyConfig, &auctionReq.Account, aliases, channelTypeMap[auctionReq.LegacyLabels.RType], gpp) 112 if err != nil { 113 errs = append(errs, err) 114 } 115 116 lmtEnforcer := extractLMT(req.BidRequest, rs.privacyConfig) 117 118 // request level privacy policies 119 coppa := req.BidRequest.Regs != nil && req.BidRequest.Regs.COPPA == 1 120 lmt := lmtEnforcer.ShouldEnforce(unknownBidder) 121 122 privacyLabels.CCPAProvided = ccpaEnforcer.CanEnforce() 123 privacyLabels.CCPAEnforced = ccpaEnforcer.ShouldEnforce(unknownBidder) 124 privacyLabels.COPPAEnforced = coppa 125 privacyLabels.LMTEnforced = lmt 126 127 var gdprEnforced bool 128 var gdprPerms gdpr.Permissions = &gdpr.AlwaysAllow{} 129 130 if gdprApplies { 131 gdprEnforced = auctionReq.TCF2Config.ChannelEnabled(channelTypeMap[auctionReq.LegacyLabels.RType]) 132 } 133 134 if gdprEnforced { 135 privacyLabels.GDPREnforced = true 136 parsedConsent, err := vendorconsent.ParseString(consent) 137 if err == nil { 138 version := int(parsedConsent.Version()) 139 privacyLabels.GDPRTCFVersion = metrics.TCFVersionToValue(version) 140 } 141 142 gdprRequestInfo := gdpr.RequestInfo{ 143 AliasGVLIDs: aliasesGVLIDs, 144 Consent: consent, 145 GDPRSignal: gdprSignal, 146 PublisherID: auctionReq.LegacyLabels.PubID, 147 } 148 gdprPerms = rs.gdprPermsBuilder(auctionReq.TCF2Config, gdprRequestInfo) 149 } 150 151 // bidder level privacy policies 152 for _, bidderRequest := range allBidderRequests { 153 privacyEnforcement := privacy.Enforcement{ 154 COPPA: coppa, 155 LMT: lmt, 156 } 157 158 // fetchBids activity 159 scopedName := privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderRequest.BidderName.String()} 160 fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName, privacy.NewRequestFromBidRequest(*req)) 161 if !fetchBidsActivityAllowed { 162 // skip the call to a bidder if fetchBids activity is not allowed 163 // do not add this bidder to allowedBidderRequests 164 continue 165 } 166 167 var auctionPermissions gdpr.AuctionPermissions 168 var gdprErr error 169 170 if gdprEnforced { 171 auctionPermissions, gdprErr = gdprPerms.AuctionActivitiesAllowed(ctx, bidderRequest.BidderCoreName, bidderRequest.BidderName) 172 if !auctionPermissions.AllowBidRequest { 173 // auction request is not permitted by GDPR 174 // do not add this bidder to allowedBidderRequests 175 rs.me.RecordAdapterGDPRRequestBlocked(bidderRequest.BidderCoreName) 176 continue 177 } 178 } 179 180 passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName, privacy.NewRequestFromBidRequest(*req)) 181 if !passIDActivityAllowed { 182 privacyEnforcement.UFPD = true 183 } else { 184 // run existing policies (GDPR, CCPA, COPPA, LMT) 185 // potentially block passing IDs based on GDPR 186 if gdprEnforced { 187 if gdprErr == nil { 188 privacyEnforcement.GDPRID = !auctionPermissions.PassID 189 } else { 190 privacyEnforcement.GDPRID = true 191 } 192 } 193 // potentially block passing IDs based on CCPA 194 privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) 195 } 196 197 passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName, privacy.NewRequestFromBidRequest(*req)) 198 if !passGeoActivityAllowed { 199 privacyEnforcement.PreciseGeo = true 200 } else { 201 // run existing policies (GDPR, CCPA, COPPA, LMT) 202 // potentially block passing geo based on GDPR 203 if gdprEnforced { 204 if gdprErr == nil { 205 privacyEnforcement.GDPRGeo = !auctionPermissions.PassGeo 206 } else { 207 privacyEnforcement.GDPRGeo = true 208 } 209 } 210 // potentially block passing geo based on CCPA 211 privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) 212 213 } 214 215 if auctionReq.FirstPartyData != nil && auctionReq.FirstPartyData[bidderRequest.BidderName] != nil { 216 applyFPD(auctionReq.FirstPartyData[bidderRequest.BidderName], bidderRequest.BidRequest) 217 } 218 219 privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTIDs, scopedName, privacy.NewRequestFromBidRequest(*req)) 220 221 privacyEnforcement.Apply(bidderRequest.BidRequest, auctionReq.Account.Privacy) 222 allowedBidderRequests = append(allowedBidderRequests, bidderRequest) 223 224 // GPP downgrade: always downgrade unless we can confirm GPP is supported 225 if shouldSetLegacyPrivacy(rs.bidderInfo, string(bidderRequest.BidderCoreName)) { 226 setLegacyGDPRFromGPP(bidderRequest.BidRequest, gpp) 227 setLegacyUSPFromGPP(bidderRequest.BidRequest, gpp) 228 } 229 } 230 231 return 232 } 233 234 func shouldSetLegacyPrivacy(bidderInfo config.BidderInfos, bidder string) bool { 235 binfo, defined := bidderInfo[bidder] 236 237 if !defined || binfo.OpenRTB == nil { 238 return true 239 } 240 241 return !binfo.OpenRTB.GPPSupported 242 } 243 244 func ccpaEnabled(account *config.Account, privacyConfig config.Privacy, requestType config.ChannelType) bool { 245 if accountEnabled := account.CCPA.EnabledForChannelType(requestType); accountEnabled != nil { 246 return *accountEnabled 247 } 248 return privacyConfig.CCPA.Enforce 249 } 250 251 func extractCCPA(orig *openrtb2.BidRequest, privacyConfig config.Privacy, account *config.Account, aliases map[string]string, requestType config.ChannelType, gpp gpplib.GppContainer) (privacy.PolicyEnforcer, error) { 252 // Quick extra wrapper until RequestWrapper makes its way into CleanRequests 253 ccpaPolicy, err := ccpa.ReadFromRequestWrapper(&openrtb_ext.RequestWrapper{BidRequest: orig}, gpp) 254 if err != nil { 255 return privacy.NilPolicyEnforcer{}, err 256 } 257 258 validBidders := GetValidBidders(aliases) 259 ccpaParsedPolicy, err := ccpaPolicy.Parse(validBidders) 260 if err != nil { 261 return privacy.NilPolicyEnforcer{}, err 262 } 263 264 ccpaEnforcer := privacy.EnabledPolicyEnforcer{ 265 Enabled: ccpaEnabled(account, privacyConfig, requestType), 266 PolicyEnforcer: ccpaParsedPolicy, 267 } 268 return ccpaEnforcer, nil 269 } 270 271 func extractLMT(orig *openrtb2.BidRequest, privacyConfig config.Privacy) privacy.PolicyEnforcer { 272 return privacy.EnabledPolicyEnforcer{ 273 Enabled: privacyConfig.LMT.Enforce, 274 PolicyEnforcer: lmt.ReadFromRequest(orig), 275 } 276 } 277 278 func ExtractReqExtBidderParamsMap(bidRequest *openrtb2.BidRequest) (map[string]json.RawMessage, error) { 279 if bidRequest == nil { 280 return nil, errors.New("error bidRequest should not be nil") 281 } 282 283 reqExt := &openrtb_ext.ExtRequest{} 284 if len(bidRequest.Ext) > 0 { 285 err := json.Unmarshal(bidRequest.Ext, &reqExt) 286 if err != nil { 287 return nil, fmt.Errorf("error decoding Request.ext : %s", err.Error()) 288 } 289 } 290 291 if reqExt.Prebid.BidderParams == nil { 292 return nil, nil 293 } 294 295 var bidderParams map[string]json.RawMessage 296 err := json.Unmarshal(reqExt.Prebid.BidderParams, &bidderParams) 297 if err != nil { 298 return nil, err 299 } 300 301 return bidderParams, nil 302 } 303 304 func getAuctionBidderRequests(auctionRequest AuctionRequest, 305 requestExt *openrtb_ext.ExtRequest, 306 bidderToSyncerKey map[string]string, 307 impsByBidder map[string][]openrtb2.Imp, 308 aliases map[string]string, 309 hostSChainNode *openrtb2.SupplyChainNode) ([]BidderRequest, []error) { 310 311 bidderRequests := make([]BidderRequest, 0, len(impsByBidder)) 312 req := auctionRequest.BidRequestWrapper 313 explicitBuyerUIDs, err := extractBuyerUIDs(req.BidRequest.User) 314 if err != nil { 315 return nil, []error{err} 316 } 317 318 bidderParamsInReqExt, err := ExtractReqExtBidderParamsMap(req.BidRequest) 319 if err != nil { 320 return nil, []error{err} 321 } 322 323 sChainWriter, err := schain.NewSChainWriter(requestExt, hostSChainNode) 324 if err != nil { 325 return nil, []error{err} 326 } 327 328 var errs []error 329 for bidder, imps := range impsByBidder { 330 coreBidder := resolveBidder(bidder, aliases) 331 332 reqCopy := *req.BidRequest 333 reqCopy.Imp = imps 334 335 sChainWriter.Write(&reqCopy, bidder) 336 337 reqCopy.Ext, err = buildRequestExtForBidder(bidder, req.BidRequest.Ext, requestExt, bidderParamsInReqExt, auctionRequest.Account.AlternateBidderCodes) 338 if err != nil { 339 return nil, []error{err} 340 } 341 342 if err := removeUnpermissionedEids(&reqCopy, bidder, requestExt); err != nil { 343 errs = append(errs, fmt.Errorf("unable to enforce request.ext.prebid.data.eidpermissions because %v", err)) 344 continue 345 } 346 347 bidderRequest := BidderRequest{ 348 BidderName: openrtb_ext.BidderName(bidder), 349 BidderCoreName: coreBidder, 350 BidRequest: &reqCopy, 351 BidderLabels: metrics.AdapterLabels{ 352 Source: auctionRequest.LegacyLabels.Source, 353 RType: auctionRequest.LegacyLabels.RType, 354 Adapter: coreBidder, 355 PubID: auctionRequest.LegacyLabels.PubID, 356 CookieFlag: auctionRequest.LegacyLabels.CookieFlag, 357 AdapterBids: metrics.AdapterBidPresent, 358 }, 359 } 360 361 syncerKey := bidderToSyncerKey[string(coreBidder)] 362 if hadSync := prepareUser(&reqCopy, bidder, syncerKey, explicitBuyerUIDs, auctionRequest.UserSyncs); !hadSync && req.BidRequest.App == nil { 363 bidderRequest.BidderLabels.CookieFlag = metrics.CookieFlagNo 364 } else { 365 bidderRequest.BidderLabels.CookieFlag = metrics.CookieFlagYes 366 } 367 368 bidderRequests = append(bidderRequests, bidderRequest) 369 } 370 return bidderRequests, errs 371 } 372 373 func buildRequestExtForBidder(bidder string, requestExt json.RawMessage, requestExtParsed *openrtb_ext.ExtRequest, bidderParamsInReqExt map[string]json.RawMessage, cfgABC *openrtb_ext.ExtAlternateBidderCodes) (json.RawMessage, error) { 374 // Resolve alternatebiddercode for current bidder 375 var reqABC *openrtb_ext.ExtAlternateBidderCodes 376 if len(requestExt) != 0 && requestExtParsed != nil && requestExtParsed.Prebid.AlternateBidderCodes != nil { 377 reqABC = requestExtParsed.Prebid.AlternateBidderCodes 378 } 379 alternateBidderCodes := buildRequestExtAlternateBidderCodes(bidder, cfgABC, reqABC) 380 381 if (len(requestExt) == 0 || requestExtParsed == nil) && alternateBidderCodes == nil { 382 return nil, nil 383 } 384 385 // Resolve Bidder Params 386 var bidderParams json.RawMessage 387 if bidderParamsInReqExt != nil { 388 bidderParams = bidderParamsInReqExt[bidder] 389 } 390 391 // Copy Allowed Fields 392 // Per: https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html#prebid-server-ortb2-extension-summary 393 prebid := openrtb_ext.ExtRequestPrebid{ 394 BidderParams: bidderParams, 395 AlternateBidderCodes: alternateBidderCodes, 396 } 397 398 if requestExtParsed != nil { 399 prebid.Channel = requestExtParsed.Prebid.Channel 400 prebid.CurrencyConversions = requestExtParsed.Prebid.CurrencyConversions 401 prebid.Debug = requestExtParsed.Prebid.Debug 402 prebid.Integration = requestExtParsed.Prebid.Integration 403 prebid.MultiBid = buildRequestExtMultiBid(bidder, requestExtParsed.Prebid.MultiBid, alternateBidderCodes) 404 prebid.Sdk = requestExtParsed.Prebid.Sdk 405 prebid.Server = requestExtParsed.Prebid.Server 406 } 407 408 // Marshal New Prebid Object 409 prebidJson, err := json.Marshal(prebid) 410 if err != nil { 411 return nil, err 412 } 413 414 // Parse Existing Ext 415 extMap := make(map[string]json.RawMessage) 416 if len(requestExt) != 0 { 417 if err := json.Unmarshal(requestExt, &extMap); err != nil { 418 return nil, err 419 } 420 } 421 422 // Update Ext With Prebid Json 423 if bytes.Equal(prebidJson, []byte(`{}`)) { 424 delete(extMap, "prebid") 425 } else { 426 extMap["prebid"] = prebidJson 427 } 428 429 if len(extMap) > 0 { 430 return json.Marshal(extMap) 431 } else { 432 return nil, nil 433 } 434 } 435 436 func buildRequestExtAlternateBidderCodes(bidder string, accABC *openrtb_ext.ExtAlternateBidderCodes, reqABC *openrtb_ext.ExtAlternateBidderCodes) *openrtb_ext.ExtAlternateBidderCodes { 437 if reqABC != nil { 438 alternateBidderCodes := &openrtb_ext.ExtAlternateBidderCodes{ 439 Enabled: reqABC.Enabled, 440 } 441 if bidderCodes, ok := reqABC.Bidders[bidder]; ok { 442 alternateBidderCodes.Bidders = map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ 443 bidder: bidderCodes, 444 } 445 } 446 return alternateBidderCodes 447 } 448 449 if accABC != nil { 450 alternateBidderCodes := &openrtb_ext.ExtAlternateBidderCodes{ 451 Enabled: accABC.Enabled, 452 } 453 if bidderCodes, ok := accABC.Bidders[bidder]; ok { 454 alternateBidderCodes.Bidders = map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ 455 bidder: bidderCodes, 456 } 457 } 458 return alternateBidderCodes 459 } 460 461 return nil 462 } 463 464 func buildRequestExtMultiBid(adapter string, reqMultiBid []*openrtb_ext.ExtMultiBid, adapterABC *openrtb_ext.ExtAlternateBidderCodes) []*openrtb_ext.ExtMultiBid { 465 adapterMultiBid := make([]*openrtb_ext.ExtMultiBid, 0) 466 for _, multiBid := range reqMultiBid { 467 if multiBid.Bidder != "" { 468 if multiBid.Bidder == adapter || isBidderInExtAlternateBidderCodes(adapter, multiBid.Bidder, adapterABC) { 469 adapterMultiBid = append(adapterMultiBid, multiBid) 470 } 471 } else { 472 for _, bidder := range multiBid.Bidders { 473 if bidder == adapter || isBidderInExtAlternateBidderCodes(adapter, bidder, adapterABC) { 474 adapterMultiBid = append(adapterMultiBid, &openrtb_ext.ExtMultiBid{ 475 Bidders: []string{bidder}, 476 MaxBids: multiBid.MaxBids, 477 }) 478 } 479 } 480 } 481 } 482 483 if len(adapterMultiBid) > 0 { 484 return adapterMultiBid 485 } 486 487 return nil 488 } 489 490 func isBidderInExtAlternateBidderCodes(adapter, currentMultiBidBidder string, adapterABC *openrtb_ext.ExtAlternateBidderCodes) bool { 491 if adapterABC != nil { 492 if abc, ok := adapterABC.Bidders[adapter]; ok { 493 for _, bidder := range abc.AllowedBidderCodes { 494 if bidder == "*" || bidder == currentMultiBidBidder { 495 return true 496 } 497 } 498 } 499 } 500 return false 501 } 502 503 // extractBuyerUIDs parses the values from user.ext.prebid.buyeruids, and then deletes those values from the ext. 504 // This prevents a Bidder from using these values to figure out who else is involved in the Auction. 505 func extractBuyerUIDs(user *openrtb2.User) (map[string]string, error) { 506 if user == nil { 507 return nil, nil 508 } 509 if len(user.Ext) == 0 { 510 return nil, nil 511 } 512 513 var userExt openrtb_ext.ExtUser 514 if err := json.Unmarshal(user.Ext, &userExt); err != nil { 515 return nil, err 516 } 517 if userExt.Prebid == nil { 518 return nil, nil 519 } 520 521 // The API guarantees that user.ext.prebid.buyeruids exists and has at least one ID defined, 522 // as long as user.ext.prebid exists. 523 buyerUIDs := userExt.Prebid.BuyerUIDs 524 userExt.Prebid = nil 525 526 // Remarshal (instead of removing) if the ext has other known fields 527 if userExt.Consent != "" || len(userExt.Eids) > 0 { 528 if newUserExtBytes, err := json.Marshal(userExt); err != nil { 529 return nil, err 530 } else { 531 user.Ext = newUserExtBytes 532 } 533 } else { 534 user.Ext = nil 535 } 536 return buyerUIDs, nil 537 } 538 539 // splitImps takes a list of Imps and returns a map of imps which have been sanitized for each bidder. 540 // 541 // For example, suppose imps has two elements. One goes to rubicon, while the other goes to appnexus and index. 542 // The returned map will have three keys: rubicon, appnexus, and index--each with one Imp. 543 // The "imp.ext" value of the appnexus Imp will only contain the "prebid" values, and "appnexus" value at the "bidder" key. 544 // The "imp.ext" value of the rubicon Imp will only contain the "prebid" values, and "rubicon" value at the "bidder" key. 545 // 546 // The goal here is so that Bidders only get Imps and Imp.Ext values which are intended for them. 547 func splitImps(imps []openrtb2.Imp) (map[string][]openrtb2.Imp, error) { 548 bidderImps := make(map[string][]openrtb2.Imp) 549 550 for i, imp := range imps { 551 var impExt map[string]json.RawMessage 552 if err := json.Unmarshal(imp.Ext, &impExt); err != nil { 553 return nil, fmt.Errorf("invalid json for imp[%d]: %v", i, err) 554 } 555 556 var impExtPrebid map[string]json.RawMessage 557 if impExtPrebidJSON, exists := impExt[openrtb_ext.PrebidExtKey]; exists { 558 // validation already performed by impExt unmarshal. no error is possible here, proven by tests. 559 json.Unmarshal(impExtPrebidJSON, &impExtPrebid) 560 } 561 562 var impExtPrebidBidder map[string]json.RawMessage 563 if impExtPrebidBidderJSON, exists := impExtPrebid[openrtb_ext.PrebidExtBidderKey]; exists { 564 // validation already performed by impExt unmarshal. no error is possible here, proven by tests. 565 json.Unmarshal(impExtPrebidBidderJSON, &impExtPrebidBidder) 566 } 567 568 sanitizedImpExt, err := createSanitizedImpExt(impExt, impExtPrebid) 569 if err != nil { 570 return nil, fmt.Errorf("unable to remove other bidder fields for imp[%d]: %v", i, err) 571 } 572 573 for bidder, bidderExt := range impExtPrebidBidder { 574 impCopy := imp 575 576 sanitizedImpExt[openrtb_ext.PrebidExtBidderKey] = bidderExt 577 578 impExtJSON, err := json.Marshal(sanitizedImpExt) 579 if err != nil { 580 return nil, fmt.Errorf("unable to remove other bidder fields for imp[%d]: cannot marshal ext: %v", i, err) 581 } 582 impCopy.Ext = impExtJSON 583 584 bidderImps[bidder] = append(bidderImps[bidder], impCopy) 585 } 586 } 587 588 return bidderImps, nil 589 } 590 591 var allowedImpExtFields = map[string]interface{}{ 592 openrtb_ext.AuctionEnvironmentKey: struct{}{}, 593 openrtb_ext.FirstPartyDataExtKey: struct{}{}, 594 openrtb_ext.FirstPartyDataContextExtKey: struct{}{}, 595 openrtb_ext.GPIDKey: struct{}{}, 596 openrtb_ext.SKAdNExtKey: struct{}{}, 597 openrtb_ext.TIDKey: struct{}{}, 598 } 599 600 var allowedImpExtPrebidFields = map[string]interface{}{ 601 openrtb_ext.IsRewardedInventoryKey: struct{}{}, 602 openrtb_ext.OptionsKey: struct{}{}, 603 } 604 605 func createSanitizedImpExt(impExt, impExtPrebid map[string]json.RawMessage) (map[string]json.RawMessage, error) { 606 sanitizedImpExt := make(map[string]json.RawMessage, 6) 607 sanitizedImpPrebidExt := make(map[string]json.RawMessage, 2) 608 609 // copy allowed imp[].ext.prebid fields 610 for k := range allowedImpExtPrebidFields { 611 if v, exists := impExtPrebid[k]; exists { 612 sanitizedImpPrebidExt[k] = v 613 } 614 } 615 616 // marshal sanitized imp[].ext.prebid 617 if len(sanitizedImpPrebidExt) > 0 { 618 if impExtPrebidJSON, err := json.Marshal(sanitizedImpPrebidExt); err == nil { 619 sanitizedImpExt[openrtb_ext.PrebidExtKey] = impExtPrebidJSON 620 } else { 621 return nil, fmt.Errorf("cannot marshal ext.prebid: %v", err) 622 } 623 } 624 625 // copy reserved imp[].ext fields known to not be bidder names 626 for k := range allowedImpExtFields { 627 if v, exists := impExt[k]; exists { 628 sanitizedImpExt[k] = v 629 } 630 } 631 632 return sanitizedImpExt, nil 633 } 634 635 // prepareUser changes req.User so that it's ready for the given bidder. 636 // This *will* mutate the request, but will *not* mutate any objects nested inside it. 637 // 638 // In this function, "givenBidder" may or may not be an alias. "coreBidder" must *not* be an alias. 639 // It returns true if a Cookie User Sync existed, and false otherwise. 640 func prepareUser(req *openrtb2.BidRequest, givenBidder, syncerKey string, explicitBuyerUIDs map[string]string, usersyncs IdFetcher) bool { 641 cookieId, hadCookie, _ := usersyncs.GetUID(syncerKey) 642 643 if id, ok := explicitBuyerUIDs[givenBidder]; ok { 644 req.User = copyWithBuyerUID(req.User, id) 645 } else if hadCookie { 646 req.User = copyWithBuyerUID(req.User, cookieId) 647 } 648 649 return hadCookie 650 } 651 652 // copyWithBuyerUID either overwrites the BuyerUID property on user with the argument, or returns 653 // a new (empty) User with the BuyerUID already set. 654 func copyWithBuyerUID(user *openrtb2.User, buyerUID string) *openrtb2.User { 655 if user == nil { 656 return &openrtb2.User{ 657 BuyerUID: buyerUID, 658 } 659 } 660 if user.BuyerUID == "" { 661 clone := *user 662 clone.BuyerUID = buyerUID 663 return &clone 664 } 665 return user 666 } 667 668 // removeUnpermissionedEids modifies the request to remove any request.user.ext.eids not permissions for the specific bidder 669 func removeUnpermissionedEids(request *openrtb2.BidRequest, bidder string, requestExt *openrtb_ext.ExtRequest) error { 670 // ensure request might have eids (as much as we can check before unmarshalling) 671 if request.User == nil || len(request.User.Ext) == 0 { 672 return nil 673 } 674 675 // ensure request has eid permissions to enforce 676 if requestExt == nil || requestExt.Prebid.Data == nil || len(requestExt.Prebid.Data.EidPermissions) == 0 { 677 return nil 678 } 679 680 // low level unmarshal to preserve other request.user.ext values. prebid server is non-destructive. 681 var userExt map[string]json.RawMessage 682 if err := json.Unmarshal(request.User.Ext, &userExt); err != nil { 683 return err 684 } 685 686 eidsJSON, eidsSpecified := userExt["eids"] 687 if !eidsSpecified { 688 return nil 689 } 690 691 var eids []openrtb2.EID 692 if err := json.Unmarshal(eidsJSON, &eids); err != nil { 693 return err 694 } 695 696 // exit early if there are no eids (empty array) 697 if len(eids) == 0 { 698 return nil 699 } 700 701 // translate eid permissions to a map for quick lookup 702 eidRules := make(map[string][]string) 703 for _, p := range requestExt.Prebid.Data.EidPermissions { 704 eidRules[p.Source] = p.Bidders 705 } 706 707 eidsAllowed := make([]openrtb2.EID, 0, len(eids)) 708 for _, eid := range eids { 709 allowed := false 710 if rule, hasRule := eidRules[eid.Source]; hasRule { 711 for _, ruleBidder := range rule { 712 if ruleBidder == "*" || ruleBidder == bidder { 713 allowed = true 714 break 715 } 716 } 717 } else { 718 allowed = true 719 } 720 721 if allowed { 722 eidsAllowed = append(eidsAllowed, eid) 723 } 724 } 725 726 // exit early if all eids are allowed and nothing needs to be removed 727 if len(eids) == len(eidsAllowed) { 728 return nil 729 } 730 731 // marshal eidsAllowed back to userExt 732 if len(eidsAllowed) == 0 { 733 delete(userExt, "eids") 734 } else { 735 eidsRaw, err := json.Marshal(eidsAllowed) 736 if err != nil { 737 return err 738 } 739 userExt["eids"] = eidsRaw 740 } 741 742 // exit early if userExt is empty 743 if len(userExt) == 0 { 744 setUserExtWithCopy(request, nil) 745 return nil 746 } 747 748 userExtJSON, err := json.Marshal(userExt) 749 if err != nil { 750 return err 751 } 752 setUserExtWithCopy(request, userExtJSON) 753 return nil 754 } 755 756 func setUserExtWithCopy(request *openrtb2.BidRequest, userExtJSON json.RawMessage) { 757 userCopy := *request.User 758 userCopy.Ext = userExtJSON 759 request.User = &userCopy 760 } 761 762 // resolveBidder returns the known BidderName associated with bidder, if bidder is an alias. If it's not an alias, the bidder is returned. 763 func resolveBidder(bidder string, aliases map[string]string) openrtb_ext.BidderName { 764 if coreBidder, ok := aliases[bidder]; ok { 765 return openrtb_ext.BidderName(coreBidder) 766 } 767 return openrtb_ext.BidderName(bidder) 768 } 769 770 // parseAliases parses the aliases from the BidRequest 771 func parseAliases(orig *openrtb2.BidRequest) (map[string]string, []error) { 772 var aliases map[string]string 773 if value, dataType, _, err := jsonparser.Get(orig.Ext, openrtb_ext.PrebidExtKey, "aliases"); dataType == jsonparser.Object && err == nil { 774 if err := json.Unmarshal(value, &aliases); err != nil { 775 return nil, []error{err} 776 } 777 } else if dataType != jsonparser.NotExist && err != jsonparser.KeyPathNotFoundError { 778 return nil, []error{err} 779 } 780 return aliases, nil 781 } 782 783 // parseAliasesGVLIDs parses the Bidder Alias GVLIDs from the BidRequest 784 func parseAliasesGVLIDs(orig *openrtb2.BidRequest) (map[string]uint16, []error) { 785 var aliasesGVLIDs map[string]uint16 786 if value, dataType, _, err := jsonparser.Get(orig.Ext, openrtb_ext.PrebidExtKey, "aliasgvlids"); dataType == jsonparser.Object && err == nil { 787 if err := json.Unmarshal(value, &aliasesGVLIDs); err != nil { 788 return nil, []error{err} 789 } 790 } else if dataType != jsonparser.NotExist && err != jsonparser.KeyPathNotFoundError { 791 return nil, []error{err} 792 } 793 return aliasesGVLIDs, nil 794 } 795 796 func GetValidBidders(aliases map[string]string) map[string]struct{} { 797 validBidders := openrtb_ext.BuildBidderNameHashSet() 798 799 for k := range aliases { 800 validBidders[k] = struct{}{} 801 } 802 803 return validBidders 804 } 805 806 // Quick little randomizer for a list of strings. Stuffing it in utils to keep other files clean 807 func randomizeList(list []openrtb_ext.BidderName) { 808 l := len(list) 809 perm := rand.Perm(l) 810 var j int 811 for i := 0; i < l; i++ { 812 j = perm[i] 813 list[i], list[j] = list[j], list[i] 814 } 815 } 816 817 func getExtCacheInstructions(requestExtPrebid *openrtb_ext.ExtRequestPrebid) extCacheInstructions { 818 //returnCreative defaults to true 819 cacheInstructions := extCacheInstructions{returnCreative: true} 820 foundBidsRC := false 821 foundVastRC := false 822 823 if requestExtPrebid != nil && requestExtPrebid.Cache != nil { 824 if requestExtPrebid.Cache.Bids != nil { 825 cacheInstructions.cacheBids = true 826 if requestExtPrebid.Cache.Bids.ReturnCreative != nil { 827 cacheInstructions.returnCreative = *requestExtPrebid.Cache.Bids.ReturnCreative 828 foundBidsRC = true 829 } 830 } 831 832 if requestExtPrebid.Cache.VastXML != nil { 833 cacheInstructions.cacheVAST = true 834 if requestExtPrebid.Cache.VastXML.ReturnCreative != nil { 835 cacheInstructions.returnCreative = *requestExtPrebid.Cache.VastXML.ReturnCreative 836 foundVastRC = true 837 } 838 } 839 } 840 841 if foundBidsRC && foundVastRC { 842 cacheInstructions.returnCreative = *requestExtPrebid.Cache.Bids.ReturnCreative || *requestExtPrebid.Cache.VastXML.ReturnCreative 843 } 844 845 return cacheInstructions 846 } 847 848 func getExtTargetData(requestExtPrebid *openrtb_ext.ExtRequestPrebid, cacheInstructions extCacheInstructions) *targetData { 849 if requestExtPrebid != nil && requestExtPrebid.Targeting != nil { 850 return &targetData{ 851 includeWinners: *requestExtPrebid.Targeting.IncludeWinners, 852 includeBidderKeys: *requestExtPrebid.Targeting.IncludeBidderKeys, 853 includeCacheBids: cacheInstructions.cacheBids, 854 includeCacheVast: cacheInstructions.cacheVAST, 855 includeFormat: requestExtPrebid.Targeting.IncludeFormat, 856 priceGranularity: *requestExtPrebid.Targeting.PriceGranularity, 857 mediaTypePriceGranularity: requestExtPrebid.Targeting.MediaTypePriceGranularity, 858 preferDeals: requestExtPrebid.Targeting.PreferDeals, 859 } 860 } 861 862 return nil 863 } 864 865 // getDebugInfo returns the boolean flags that allow for debug information in bidResponse.Ext, the SeatBid.httpcalls slice, and 866 // also sets the debugLog information 867 func getDebugInfo(test int8, requestExtPrebid *openrtb_ext.ExtRequestPrebid, accountDebugFlag bool, debugLog *DebugLog) (bool, bool, *DebugLog) { 868 requestDebugAllow := parseRequestDebugValues(test, requestExtPrebid) 869 debugLog = setDebugLogValues(accountDebugFlag, debugLog) 870 871 responseDebugAllow := (requestDebugAllow && accountDebugFlag) || debugLog.DebugEnabledOrOverridden 872 accountDebugAllow := (requestDebugAllow && accountDebugFlag) || (debugLog.DebugEnabledOrOverridden && accountDebugFlag) 873 874 return responseDebugAllow, accountDebugAllow, debugLog 875 } 876 877 // setDebugLogValues initializes the DebugLog if nil. It also sets the value of the debugInfo flag 878 // used in HoldAuction 879 func setDebugLogValues(accountDebugFlag bool, debugLog *DebugLog) *DebugLog { 880 if debugLog == nil { 881 debugLog = &DebugLog{} 882 } 883 884 debugLog.Enabled = debugLog.DebugEnabledOrOverridden || accountDebugFlag 885 return debugLog 886 } 887 888 func parseRequestDebugValues(test int8, requestExtPrebid *openrtb_ext.ExtRequestPrebid) bool { 889 return test == 1 || (requestExtPrebid != nil && requestExtPrebid.Debug) 890 } 891 892 func getExtBidAdjustmentFactors(requestExtPrebid *openrtb_ext.ExtRequestPrebid) map[string]float64 { 893 if requestExtPrebid != nil { 894 return requestExtPrebid.BidAdjustmentFactors 895 } 896 return nil 897 } 898 899 func applyFPD(fpd *firstpartydata.ResolvedFirstPartyData, bidReq *openrtb2.BidRequest) { 900 if fpd.Site != nil { 901 bidReq.Site = fpd.Site 902 } 903 if fpd.App != nil { 904 bidReq.App = fpd.App 905 } 906 if fpd.User != nil { 907 //BuyerUID is a value obtained between fpd extraction and fpd application. 908 //BuyerUID needs to be set back to fpd before applying this fpd to final bidder request 909 if bidReq.User != nil && len(bidReq.User.BuyerUID) > 0 { 910 fpd.User.BuyerUID = bidReq.User.BuyerUID 911 } 912 bidReq.User = fpd.User 913 } 914 } 915 916 func buildBidResponseRequest(req *openrtb2.BidRequest, 917 bidderImpResponses stored_responses.BidderImpsWithBidResponses, 918 aliases map[string]string, 919 bidderImpReplaceImpID stored_responses.BidderImpReplaceImpID) map[openrtb_ext.BidderName]BidderRequest { 920 921 bidderToBidderResponse := make(map[openrtb_ext.BidderName]BidderRequest) 922 923 for bidderName, impResps := range bidderImpResponses { 924 resolvedBidder := resolveBidder(string(bidderName), aliases) 925 bidderToBidderResponse[bidderName] = BidderRequest{ 926 BidRequest: req, 927 BidderCoreName: resolvedBidder, 928 BidderName: bidderName, 929 BidderStoredResponses: impResps, 930 ImpReplaceImpId: bidderImpReplaceImpID[string(resolvedBidder)], 931 BidderLabels: metrics.AdapterLabels{Adapter: resolvedBidder}, 932 } 933 } 934 return bidderToBidderResponse 935 } 936 937 func mergeBidderRequests(allBidderRequests []BidderRequest, bidderNameToBidderReq map[openrtb_ext.BidderName]BidderRequest) []BidderRequest { 938 if len(allBidderRequests) == 0 && len(bidderNameToBidderReq) == 0 { 939 return allBidderRequests 940 } 941 if len(allBidderRequests) == 0 && len(bidderNameToBidderReq) > 0 { 942 for _, v := range bidderNameToBidderReq { 943 allBidderRequests = append(allBidderRequests, v) 944 } 945 return allBidderRequests 946 } else if len(allBidderRequests) > 0 && len(bidderNameToBidderReq) > 0 { 947 //merge bidder requests with real imps and imps with stored resp 948 for bn, br := range bidderNameToBidderReq { 949 found := false 950 for i, ar := range allBidderRequests { 951 if ar.BidderName == bn { 952 //bidder req with real imps and imps with stored resp 953 allBidderRequests[i].BidderStoredResponses = br.BidderStoredResponses 954 found = true 955 break 956 } 957 } 958 if !found { 959 //bidder req with stored bid responses only 960 br.BidRequest.Imp = nil // to indicate this bidder request has bidder responses only 961 allBidderRequests = append(allBidderRequests, br) 962 } 963 } 964 } 965 return allBidderRequests 966 } 967 968 func setLegacyGDPRFromGPP(r *openrtb2.BidRequest, gpp gpplib.GppContainer) { 969 if r.Regs != nil && r.Regs.GDPR == nil { 970 if r.Regs.GPPSID != nil { 971 // Set to 0 unless SID exists 972 regs := *r.Regs 973 regs.GDPR = ptrutil.ToPtr[int8](0) 974 for _, id := range r.Regs.GPPSID { 975 if id == int8(gppConstants.SectionTCFEU2) { 976 regs.GDPR = ptrutil.ToPtr[int8](1) 977 } 978 } 979 r.Regs = ®s 980 } 981 } 982 983 if r.User == nil || len(r.User.Consent) == 0 { 984 for _, sec := range gpp.Sections { 985 if sec.GetID() == gppConstants.SectionTCFEU2 { 986 var user openrtb2.User 987 if r.User == nil { 988 user = openrtb2.User{} 989 } else { 990 user = *r.User 991 } 992 user.Consent = sec.GetValue() 993 r.User = &user 994 } 995 } 996 } 997 998 } 999 func setLegacyUSPFromGPP(r *openrtb2.BidRequest, gpp gpplib.GppContainer) { 1000 if r.Regs == nil { 1001 return 1002 } 1003 1004 if len(r.Regs.USPrivacy) > 0 || r.Regs.GPPSID == nil { 1005 return 1006 } 1007 for _, sid := range r.Regs.GPPSID { 1008 if sid == int8(gppConstants.SectionUSPV1) { 1009 for _, sec := range gpp.Sections { 1010 if sec.GetID() == gppConstants.SectionUSPV1 { 1011 regs := *r.Regs 1012 regs.USPrivacy = sec.GetValue() 1013 r.Regs = ®s 1014 } 1015 } 1016 } 1017 } 1018 1019 } 1020 1021 func WrapJSONInData(data []byte) []byte { 1022 res := make([]byte, 0, len(data)) 1023 res = append(res, []byte(`{"data":`)...) 1024 res = append(res, data...) 1025 res = append(res, []byte(`}`)...) 1026 return res 1027 } 1028 1029 func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { 1030 mType := bid.MType 1031 var bidType openrtb_ext.BidType 1032 if mType > 0 { 1033 switch mType { 1034 case openrtb2.MarkupBanner: 1035 bidType = openrtb_ext.BidTypeBanner 1036 case openrtb2.MarkupVideo: 1037 bidType = openrtb_ext.BidTypeVideo 1038 case openrtb2.MarkupAudio: 1039 bidType = openrtb_ext.BidTypeAudio 1040 case openrtb2.MarkupNative: 1041 bidType = openrtb_ext.BidTypeNative 1042 default: 1043 return bidType, fmt.Errorf("Failed to parse bid mType for impression \"%s\"", bid.ImpID) 1044 } 1045 } else { 1046 var err error 1047 bidType, err = getPrebidMediaTypeForBid(bid) 1048 if err != nil { 1049 return bidType, err 1050 } 1051 } 1052 return bidType, nil 1053 } 1054 1055 func getPrebidMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { 1056 var err error 1057 var bidType openrtb_ext.BidType 1058 1059 if bid.Ext != nil { 1060 var bidExt openrtb_ext.ExtBid 1061 err = json.Unmarshal(bid.Ext, &bidExt) 1062 if err == nil && bidExt.Prebid != nil { 1063 if bidType, err = openrtb_ext.ParseBidType(string(bidExt.Prebid.Type)); err == nil { 1064 return bidType, nil 1065 } 1066 } 1067 } 1068 1069 errMsg := fmt.Sprintf("Failed to parse bid mediatype for impression \"%s\"", bid.ImpID) 1070 if err != nil { 1071 errMsg = fmt.Sprintf("%s, %s", errMsg, err.Error()) 1072 } 1073 1074 return bidType, &errortypes.BadServerResponse{ 1075 Message: errMsg, 1076 } 1077 } 1078 1079 func applyBidAdjustmentToFloor(allBidderRequests []BidderRequest, bidAdjustmentFactors map[string]float64) { 1080 1081 if len(bidAdjustmentFactors) == 0 { 1082 return 1083 } 1084 1085 for _, bidderRequest := range allBidderRequests { 1086 bidAdjustment := 1.0 1087 1088 if bidAdjustemntValue, ok := bidAdjustmentFactors[string(bidderRequest.BidderName)]; ok { 1089 bidAdjustment = bidAdjustemntValue 1090 } 1091 1092 if bidAdjustment != 1.0 { 1093 for index, imp := range bidderRequest.BidRequest.Imp { 1094 imp.BidFloor = imp.BidFloor / bidAdjustment 1095 bidderRequest.BidRequest.Imp[index] = imp 1096 } 1097 } 1098 } 1099 }