github.com/prebid/prebid-server/v2@v2.18.0/privacy/ccpa/policy.go (about) 1 package ccpa 2 3 import ( 4 "errors" 5 "fmt" 6 7 gpplib "github.com/prebid/go-gpp" 8 gppConstants "github.com/prebid/go-gpp/constants" 9 "github.com/prebid/openrtb/v20/openrtb2" 10 "github.com/prebid/prebid-server/v2/errortypes" 11 "github.com/prebid/prebid-server/v2/openrtb_ext" 12 gppPolicy "github.com/prebid/prebid-server/v2/privacy/gpp" 13 ) 14 15 // Policy represents the CCPA regulatory information from an OpenRTB bid request. 16 type Policy struct { 17 Consent string 18 NoSaleBidders []string 19 } 20 21 // ReadFromRequestWrapper extracts the CCPA regulatory information from an OpenRTB bid request. 22 func ReadFromRequestWrapper(req *openrtb_ext.RequestWrapper, gpp gpplib.GppContainer) (Policy, error) { 23 var noSaleBidders []string 24 var gppSIDs []int8 25 var requestUSPrivacy string 26 var warn error 27 28 if req == nil || req.BidRequest == nil { 29 return Policy{}, nil 30 } 31 32 if req.BidRequest.Regs != nil { 33 requestUSPrivacy = req.BidRequest.Regs.USPrivacy 34 gppSIDs = req.BidRequest.Regs.GPPSID 35 } 36 37 consent, err := SelectCCPAConsent(requestUSPrivacy, gpp, gppSIDs) 38 if err != nil { 39 warn = &errortypes.Warning{ 40 Message: "regs.us_privacy consent does not match uspv1 in GPP, using regs.gpp", 41 WarningCode: errortypes.InvalidPrivacyConsentWarningCode} 42 } 43 44 if consent == "" { 45 // Read consent from request.regs.ext 46 regsExt, err := req.GetRegExt() 47 if err != nil { 48 return Policy{}, fmt.Errorf("error reading request.regs.ext: %s", err) 49 } 50 if regsExt != nil { 51 consent = regsExt.GetUSPrivacy() 52 } 53 } 54 // Read no sale bidders from request.ext.prebid 55 reqExt, err := req.GetRequestExt() 56 if err != nil { 57 return Policy{}, fmt.Errorf("error reading request.ext: %s", err) 58 } 59 reqPrebid := reqExt.GetPrebid() 60 if reqPrebid != nil { 61 noSaleBidders = reqPrebid.NoSale 62 } 63 64 return Policy{consent, noSaleBidders}, warn 65 } 66 67 func ReadFromRequest(req *openrtb2.BidRequest) (Policy, error) { 68 var gpp gpplib.GppContainer 69 if req != nil && req.Regs != nil && len(req.Regs.GPP) > 0 { 70 gpp, _ = gpplib.Parse(req.Regs.GPP) 71 } 72 73 return ReadFromRequestWrapper(&openrtb_ext.RequestWrapper{BidRequest: req}, gpp) 74 } 75 76 // Write mutates an OpenRTB bid request with the CCPA regulatory information. 77 func (p Policy) Write(req *openrtb_ext.RequestWrapper) error { 78 if req == nil { 79 return nil 80 } 81 82 regsExt, err := req.GetRegExt() 83 if err != nil { 84 return err 85 } 86 87 reqExt, err := req.GetRequestExt() 88 if err != nil { 89 return err 90 } 91 92 regsExt.SetUSPrivacy(p.Consent) 93 setPrebidNoSale(p.NoSaleBidders, reqExt) 94 return nil 95 } 96 97 func SelectCCPAConsent(requestUSPrivacy string, gpp gpplib.GppContainer, gppSIDs []int8) (string, error) { 98 var consent string 99 var err error 100 101 if len(gpp.SectionTypes) > 0 { 102 if gppPolicy.IsSIDInList(gppSIDs, gppConstants.SectionUSPV1) { 103 if i := gppPolicy.IndexOfSID(gpp, gppConstants.SectionUSPV1); i >= 0 { 104 consent = gpp.Sections[i].GetValue() 105 } 106 } 107 } 108 109 if requestUSPrivacy != "" { 110 if consent == "" { 111 consent = requestUSPrivacy 112 } else if consent != requestUSPrivacy { 113 err = errors.New("request.us_privacy consent does not match uspv1") 114 } 115 } 116 117 return consent, err 118 } 119 120 func setPrebidNoSale(noSaleBidders []string, ext *openrtb_ext.RequestExt) { 121 if len(noSaleBidders) == 0 { 122 setPrebidNoSaleClear(ext) 123 } else { 124 setPrebidNoSaleWrite(noSaleBidders, ext) 125 } 126 } 127 128 func setPrebidNoSaleClear(ext *openrtb_ext.RequestExt) { 129 prebid := ext.GetPrebid() 130 if prebid == nil { 131 return 132 } 133 134 // Remove no sale member 135 prebid.NoSale = []string{} 136 ext.SetPrebid(prebid) 137 } 138 139 func setPrebidNoSaleWrite(noSaleBidders []string, ext *openrtb_ext.RequestExt) { 140 if ext == nil { 141 // This should hopefully not be possible. The only caller insures that this has been initialized 142 return 143 } 144 145 prebid := ext.GetPrebid() 146 if prebid == nil { 147 prebid = &openrtb_ext.ExtRequestPrebid{} 148 } 149 prebid.NoSale = noSaleBidders 150 ext.SetPrebid(prebid) 151 }