github.com/prebid/prebid-server/v2@v2.18.0/gdpr/impl.go (about)

     1  package gdpr
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/prebid/go-gdpr/api"
     7  	"github.com/prebid/go-gdpr/consentconstants"
     8  	tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2"
     9  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    10  )
    11  
    12  const noBidder openrtb_ext.BidderName = ""
    13  
    14  // permissionsImpl contains global and request-specific GDPR config data and is used to determine
    15  // whether various cookie sync and auction activities are permitted for a request
    16  // permissionsImpl implements the Permissions interface
    17  type permissionsImpl struct {
    18  	// global
    19  	fetchVendorList        VendorListFetcher
    20  	gdprDefaultValue       string
    21  	hostVendorID           int
    22  	nonStandardPublishers  map[string]struct{}
    23  	purposeEnforcerBuilder PurposeEnforcerBuilder
    24  	vendorIDs              map[openrtb_ext.BidderName]uint16
    25  	// request-specific
    26  	aliasGVLIDs map[string]uint16
    27  	cfg         TCF2ConfigReader
    28  	consent     string
    29  	gdprSignal  Signal
    30  	publisherID string
    31  }
    32  
    33  // HostCookiesAllowed determines whether the host is allowed to set cookies on the user's device
    34  func (p *permissionsImpl) HostCookiesAllowed(ctx context.Context) (bool, error) {
    35  	if p.gdprSignal != SignalYes {
    36  		return true, nil
    37  	}
    38  
    39  	return p.allowSync(ctx, uint16(p.hostVendorID), noBidder, false)
    40  }
    41  
    42  // BidderSyncAllowed determines whether a given bidder is allowed to perform a cookie sync
    43  func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName) (bool, error) {
    44  	if p.gdprSignal != SignalYes {
    45  		return true, nil
    46  	}
    47  
    48  	id, ok := p.vendorIDs[bidder]
    49  	if ok {
    50  		vendorExceptions := p.cfg.PurposeVendorExceptions(consentconstants.Purpose(1))
    51  		_, vendorException := vendorExceptions[string(bidder)]
    52  		return p.allowSync(ctx, id, bidder, vendorException)
    53  	}
    54  
    55  	return false, nil
    56  }
    57  
    58  // AuctionActivitiesAllowed determines whether auction activities are permitted for a given bidder
    59  func (p *permissionsImpl) AuctionActivitiesAllowed(ctx context.Context, bidderCoreName openrtb_ext.BidderName, bidder openrtb_ext.BidderName) (permissions AuctionPermissions, err error) {
    60  	if _, ok := p.nonStandardPublishers[p.publisherID]; ok {
    61  		return AllowAll, nil
    62  	}
    63  	if p.gdprSignal != SignalYes {
    64  		return AllowAll, nil
    65  	}
    66  	if p.consent == "" {
    67  		return p.defaultPermissions(), nil
    68  	}
    69  	pc, err := parseConsent(p.consent)
    70  	if err != nil {
    71  		return p.defaultPermissions(), err
    72  	}
    73  	vendorID, _ := p.resolveVendorID(bidderCoreName, bidder)
    74  	vendor, err := p.getVendor(ctx, vendorID, *pc)
    75  	if err != nil {
    76  		return p.defaultPermissions(), err
    77  	}
    78  	vendorInfo := VendorInfo{vendorID: vendorID, vendor: vendor}
    79  
    80  	permissions = AuctionPermissions{}
    81  	permissions.AllowBidRequest = p.allowBidRequest(bidderCoreName, pc.consentMeta, vendorInfo)
    82  	permissions.PassGeo = p.allowGeo(bidderCoreName, pc.consentMeta, vendor)
    83  	permissions.PassID = p.allowID(bidderCoreName, pc.consentMeta, vendorInfo)
    84  
    85  	return permissions, nil
    86  }
    87  
    88  // defaultPermissions returns a permissions object that denies passing user IDs while
    89  // allowing passing geo information and sending bid requests based on whether purpose 2
    90  // and feature one are enforced respectively
    91  // if the consent string is empty or malformed we should use the default permissions
    92  func (p *permissionsImpl) defaultPermissions() AuctionPermissions {
    93  	perms := AuctionPermissions{}
    94  
    95  	if !p.cfg.PurposeEnforced(consentconstants.Purpose(2)) {
    96  		perms.AllowBidRequest = true
    97  	}
    98  	if !p.cfg.FeatureOneEnforced() {
    99  		perms.PassGeo = true
   100  	}
   101  	return perms
   102  }
   103  
   104  // resolveVendorID gets the vendor ID for the specified bidder from either the alias GVL IDs
   105  // provided in the request or from the bidder configs loaded at startup
   106  func (p *permissionsImpl) resolveVendorID(bidderCoreName openrtb_ext.BidderName, bidder openrtb_ext.BidderName) (id uint16, ok bool) {
   107  	if id, ok = p.aliasGVLIDs[string(bidder)]; ok {
   108  		return id, ok
   109  	}
   110  
   111  	id, ok = p.vendorIDs[bidderCoreName]
   112  
   113  	return id, ok
   114  }
   115  
   116  // allowSync computes cookie sync activity legal basis for a given bidder using the enforcement
   117  // algorithms selected by the purpose enforcer builder
   118  func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, bidder openrtb_ext.BidderName, vendorException bool) (bool, error) {
   119  	if p.consent == "" {
   120  		return false, nil
   121  	}
   122  	pc, err := parseConsent(p.consent)
   123  	if err != nil {
   124  		return false, err
   125  	}
   126  	vendor, err := p.getVendor(ctx, vendorID, *pc)
   127  	if err != nil {
   128  		return false, nil
   129  	}
   130  	vendorInfo := VendorInfo{vendorID: vendorID, vendor: vendor}
   131  
   132  	if !p.cfg.PurposeEnforced(consentconstants.Purpose(1)) {
   133  		return true, nil
   134  	}
   135  
   136  	if p.cfg.PurposeOneTreatmentEnabled() && pc.consentMeta.PurposeOneTreatment() {
   137  		return p.cfg.PurposeOneTreatmentAccessAllowed(), nil
   138  	}
   139  
   140  	purpose := consentconstants.Purpose(1)
   141  	enforcer := p.purposeEnforcerBuilder(purpose, string(bidder))
   142  
   143  	if enforcer.LegalBasis(vendorInfo, string(bidder), pc.consentMeta, Overrides{blockVendorExceptions: !vendorException}) {
   144  		return true, nil
   145  	}
   146  	return false, nil
   147  }
   148  
   149  // allowBidRequest computes legal basis for a given bidder using the enforcement algorithms selected
   150  // by the purpose enforcer builder
   151  func (p *permissionsImpl) allowBidRequest(bidder openrtb_ext.BidderName, consentMeta tcf2.ConsentMetadata, vendorInfo VendorInfo) bool {
   152  	enforcer := p.purposeEnforcerBuilder(consentconstants.Purpose(2), string(bidder))
   153  
   154  	overrides := Overrides{}
   155  	if _, ok := enforcer.(*BasicEnforcement); ok {
   156  		overrides.allowLITransparency = true
   157  	}
   158  	return enforcer.LegalBasis(vendorInfo, string(bidder), consentMeta, overrides)
   159  }
   160  
   161  // allowGeo computes legal basis for a given bidder using the configs, consent and GVL pertaining to
   162  // feature one
   163  func (p *permissionsImpl) allowGeo(bidder openrtb_ext.BidderName, consentMeta tcf2.ConsentMetadata, vendor api.Vendor) bool {
   164  	if !p.cfg.FeatureOneEnforced() {
   165  		return true
   166  	}
   167  	if p.cfg.FeatureOneVendorException(bidder) {
   168  		return true
   169  	}
   170  
   171  	basicEnforcementVendors := p.cfg.BasicEnforcementVendors()
   172  	_, weakVendorEnforcement := basicEnforcementVendors[string(bidder)]
   173  	return consentMeta.SpecialFeatureOptIn(1) && ((vendor != nil && vendor.SpecialFeature(1)) || weakVendorEnforcement)
   174  }
   175  
   176  // allowID computes the pass user ID activity legal basis for a given bidder using the enforcement algorithms
   177  // selected by the purpose enforcer builder. For the user ID activity, the selected enforcement algorithm must
   178  // always assume we are enforcing the purpose.
   179  // If the purpose for which we are computing legal basis is purpose 2, the algorithm should allow LI transparency.
   180  func (p *permissionsImpl) allowID(bidder openrtb_ext.BidderName, consentMeta tcf2.ConsentMetadata, vendorInfo VendorInfo) bool {
   181  	for i := 2; i <= 10; i++ {
   182  		purpose := consentconstants.Purpose(i)
   183  		enforcer := p.purposeEnforcerBuilder(purpose, string(bidder))
   184  
   185  		overrides := Overrides{enforcePurpose: true, enforceVendors: true}
   186  		if _, ok := enforcer.(*BasicEnforcement); ok && purpose == consentconstants.Purpose(2) {
   187  			overrides.allowLITransparency = true
   188  		}
   189  		if enforcer.LegalBasis(vendorInfo, string(bidder), consentMeta, overrides) {
   190  			return true
   191  		}
   192  	}
   193  
   194  	return false
   195  }
   196  
   197  // getVendor retrieves the GVL vendor information for a particular bidder
   198  func (p *permissionsImpl) getVendor(ctx context.Context, vendorID uint16, pc parsedConsent) (api.Vendor, error) {
   199  	vendorList, err := p.fetchVendorList(ctx, pc.specVersion, pc.listVersion)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	return vendorList.Vendor(vendorID), nil
   204  }
   205  
   206  // AllowHostCookies represents a GDPR permissions policy with host cookie syncing always allowed
   207  type AllowHostCookies struct {
   208  	*permissionsImpl
   209  }
   210  
   211  // HostCookiesAllowed always returns true
   212  func (p *AllowHostCookies) HostCookiesAllowed(ctx context.Context) (bool, error) {
   213  	return true, nil
   214  }
   215  
   216  // Exporting to allow for easy test setups
   217  type AlwaysAllow struct{}
   218  
   219  func (a AlwaysAllow) HostCookiesAllowed(ctx context.Context) (bool, error) {
   220  	return true, nil
   221  }
   222  func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName) (bool, error) {
   223  	return true, nil
   224  }
   225  func (a AlwaysAllow) AuctionActivitiesAllowed(ctx context.Context, bidderCoreName openrtb_ext.BidderName, bidder openrtb_ext.BidderName) (permissions AuctionPermissions, err error) {
   226  	return AllowAll, nil
   227  }