Skip to content

Commit

Permalink
DSA: Bid response adrender, behalf & paid validations (#3523)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsardo committed Feb 27, 2024
1 parent 030da80 commit d13dfc5
Show file tree
Hide file tree
Showing 7 changed files with 363 additions and 83 deletions.
102 changes: 77 additions & 25 deletions dsa/validate.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,103 @@
package dsa

import (
"errors"

"github.com/prebid/prebid-server/v2/exchange/entities"
"github.com/prebid/prebid-server/v2/openrtb_ext"
"github.com/prebid/prebid-server/v2/util/jsonutil"
)

"github.com/buger/jsonparser"
// Required values representing whether a DSA object is required
const (
Required int8 = 2 // bid responses without DSA object will not be accepted
RequiredOnlinePlatform int8 = 3 // bid responses without DSA object will not be accepted, Publisher is Online Platform
)

// PubRender values representing publisher rendering intentions
const (
// Required - bid responses without DSA object will not be accepted
Required = 2
// RequiredOnlinePlatform - bid responses without DSA object will not be accepted, Publisher is an Online Platform
RequiredOnlinePlatform = 3
PubRenderCannotRender int8 = 0 // publisher can't render
PubRenderWillRender int8 = 2 // publisher will render
)

// AdRender values representing buyer/advertiser rendering intentions
const (
AdRenderWillRender int8 = 1 // buyer/advertiser will render
)

var (
ErrDsaMissing = errors.New("DSA object missing when required")
ErrBehalfTooLong = errors.New("DSA behalf exceeds limit of 100 chars")
ErrPaidTooLong = errors.New("DSA paid exceeds limit of 100 chars")
ErrNeitherWillRender = errors.New("DSA publisher and buyer both signal will not render")
ErrBothWillRender = errors.New("DSA publisher and buyer both signal will render")
)

const (
behalfMaxLength = 100
paidMaxLength = 100
)

// Validate determines whether a given bid is valid from a DSA perspective.
// A bid is considered valid unless the bid request indicates that a DSA object is required
// in bid responses and the object happens to be missing from the specified bid.
func Validate(req *openrtb_ext.RequestWrapper, bid *entities.PbsOrtbBid) (valid bool) {
if !dsaRequired(req) {
return true
// in bid responses and the object happens to be missing from the specified bid, or if the bid
// DSA object contents are invalid
func Validate(req *openrtb_ext.RequestWrapper, bid *entities.PbsOrtbBid) error {
reqDSA := getReqDSA(req)
bidDSA := getBidDSA(bid)

if dsaRequired(reqDSA) && bidDSA == nil {
return ErrDsaMissing
}
if bid == nil || bid.Bid == nil {
return false
if bidDSA == nil {
return nil
}
if len(bidDSA.Behalf) > behalfMaxLength {
return ErrBehalfTooLong
}
_, dataType, _, err := jsonparser.Get(bid.Bid.Ext, "dsa")
if dataType == jsonparser.Object && err == nil {
return true
} else if err != nil && err != jsonparser.KeyPathNotFoundError {
return true
if len(bidDSA.Paid) > paidMaxLength {
return ErrPaidTooLong
}
return false
if reqDSA != nil && reqDSA.PubRender != nil && bidDSA.AdRender != nil {
if *reqDSA.PubRender == PubRenderCannotRender && *bidDSA.AdRender != AdRenderWillRender {
return ErrNeitherWillRender
}
if *reqDSA.PubRender == PubRenderWillRender && *bidDSA.AdRender == AdRenderWillRender {
return ErrBothWillRender
}
}
return nil
}

// dsaRequired examines the bid request to determine if the dsarequired field indicates
// that bid responses include a dsa object
func dsaRequired(req *openrtb_ext.RequestWrapper) bool {
func dsaRequired(dsa *openrtb_ext.ExtRegsDSA) bool {
if dsa == nil || dsa.Required == nil {
return false
}
return *dsa.Required == Required || *dsa.Required == RequiredOnlinePlatform
}

// getReqDSA retrieves the DSA object from the request
func getReqDSA(req *openrtb_ext.RequestWrapper) *openrtb_ext.ExtRegsDSA {
if req == nil {
return nil
}
regExt, err := req.GetRegExt()
if regExt == nil || err != nil {
return false
return nil
}
regsDSA := regExt.GetDSA()
if regsDSA == nil {
return false
return regExt.GetDSA()
}

// getBidDSA retrieves the DSA object from the bid
func getBidDSA(bid *entities.PbsOrtbBid) *openrtb_ext.ExtBidDSA {
if bid == nil || bid.Bid == nil {
return nil
}
if regsDSA.Required == Required || regsDSA.Required == RequiredOnlinePlatform {
return true
var bidExt openrtb_ext.ExtBid
if err := jsonutil.Unmarshal(bid.Bid.Ext, &bidExt); err != nil {
return nil
}
return false
return bidExt.DSA
}
Loading

0 comments on commit d13dfc5

Please sign in to comment.