Repository: prebid/prebid-server
Pull request #1856 (2021-05-20 15:54:02)

Update go-gdpr package to v0.9.0


Patch:
diff --git a/config/config.go b/config/config.go
index 280bdbf0..afb4ac8d 100644
--- a/config/config.go
+++ b/config/config.go
@@ -353,6 +353,9 @@ type DisabledMetrics struct {
 	// server establishes with bidder servers such as the number of connections
 	// that were created or reused.
 	AdapterConnectionMetrics bool `mapstructure:"adapter_connections_metrics"`
+
+	// True if we don't want to collect the per adapter GDPR request blocked metric
+	AdapterGDPRRequestBlocked bool `mapstructure:"adapter_gdpr_request_blocked"`
 }
 
 func (cfg *Metrics) validate(errs []error) []error {
@@ -718,6 +721,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	// no metrics configured by default (metrics{host|database|username|password})
 	v.SetDefault("metrics.disabled_metrics.account_adapter_details", false)
 	v.SetDefault("metrics.disabled_metrics.adapter_connections_metrics", true)
+	v.SetDefault("metrics.disabled_metrics.adapter_gdpr_request_blocked", false)
 	v.SetDefault("metrics.influxdb.host", "")
 	v.SetDefault("metrics.influxdb.database", "")
 	v.SetDefault("metrics.influxdb.username", "")
diff --git a/exchange/exchange.go b/exchange/exchange.go
index c8646801..0d3d93cf 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -179,7 +179,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
 	usersyncIfAmbiguous := e.parseUsersyncIfAmbiguous(r.BidRequest)
 
 	// Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder
-	bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(ctx, r, requestExt, e.gDPR, usersyncIfAmbiguous, e.privacyConfig, &r.Account)
+	bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(ctx, r, requestExt, e.gDPR, e.me, usersyncIfAmbiguous, e.privacyConfig, &r.Account)
 
 	e.me.RecordRequestPrivacy(privacyLabels)
 
diff --git a/exchange/utils.go b/exchange/utils.go
index e367facb..3d5e2374 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -56,9 +56,10 @@ func cleanOpenRTBRequests(ctx context.Context,
 	req AuctionRequest,
 	requestExt *openrtb_ext.ExtRequest,
 	gDPR gdpr.Permissions,
+	metricsEngine metrics.MetricsEngine,
 	usersyncIfAmbiguous bool,
 	privacyConfig config.Privacy,
-	account *config.Account) (bidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
+	account *config.Account) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
 
 	impsByBidder, err := splitImps(req.BidRequest.Imp)
 	if err != nil {
@@ -71,9 +72,10 @@ func cleanOpenRTBRequests(ctx context.Context,
 		return
 	}
 
-	bidderRequests, errs = getAuctionBidderRequests(req, requestExt, impsByBidder, aliases)
+	var allBidderRequests []BidderRequest
+	allBidderRequests, errs = getAuctionBidderRequests(req, requestExt, impsByBidder, aliases)
 
-	if len(bidderRequests) == 0 {
+	if len(allBidderRequests) == 0 {
 		return
 	}
 
@@ -117,7 +119,10 @@ func cleanOpenRTBRequests(ctx context.Context,
 	}
 
 	// bidder level privacy policies
-	for _, bidderRequest := range bidderRequests {
+	allowedBidderRequests = make([]BidderRequest, 0, len(allBidderRequests))
+	for _, bidderRequest := range allBidderRequests {
+		bidRequestAllowed := true
+
 		// CCPA
 		privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())
 
@@ -133,7 +138,9 @@ func cleanOpenRTBRequests(ctx context.Context,
 				}
 			}
 			var publisherID = req.LegacyLabels.PubID
-			geo, id, err := gDPR.PersonalInfoAllowed(ctx, bidderRequest.BidderCoreName, publisherID, gdprSignal, consent, weakVendorEnforcement)
+			bidReq, geo, id, err := gDPR.AuctionActivitiesAllowed(ctx, bidderRequest.BidderCoreName, publisherID, gdprSignal, consent, weakVendorEnforcement)
+			bidRequestAllowed = bidReq
+
 			if err == nil {
 				privacyEnforcement.GDPRGeo = !geo
 				privacyEnforcement.GDPRID = !id
@@ -141,9 +148,16 @@ func cleanOpenRTBRequests(ctx context.Context,
 				privacyEnforcement.GDPRGeo = true
 				privacyEnforcement.GDPRID = true
 			}
+
+			if !bidRequestAllowed {
+				metricsEngine.RecordAdapterGDPRRequestBlocked(bidderRequest.BidderCoreName)
+			}
 		}
 
-		privacyEnforcement.Apply(bidderRequest.BidRequest)
+		if bidRequestAllowed {
+			privacyEnforcement.Apply(bidderRequest.BidRequest)
+			allowedBidderRequests = append(allowedBidderRequests, bidderRequest)
+		}
 	}
 
 	return
diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go
index 2b450160..ffd5ced4 100644
--- a/gdpr/gdpr.go
+++ b/gdpr/gdpr.go
@@ -25,7 +25,7 @@ type Permissions interface {
 	// Determines whether or not to send PI information to a bidder, or mask it out.
 	//
 	// If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent.
-	PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error)
+	AuctionActivitiesAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (allowBidReq bool, passGeo bool, passID bool, err error)
 }
 
 // Versions of the GDPR TCF technical specification.
diff --git a/gdpr/impl.go b/gdpr/impl.go
index 22b4dbe3..a91a9308 100644
--- a/gdpr/impl.go
+++ b/gdpr/impl.go
@@ -58,35 +58,35 @@ func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return false, nil
 }
 
-func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context,
+func (p *permissionsImpl) AuctionActivitiesAllowed(ctx context.Context,
 	bidder openrtb_ext.BidderName,
 	PublisherID string,
 	gdprSignal Signal,
 	consent string,
-	weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
+	weakVendorEnforcement bool) (allowBidRequest bool, passGeo bool, passID bool, err error) {
 	if _, ok := p.cfg.NonStandardPublisherMap[PublisherID]; ok {
-		return true, true, nil
+		return true, true, true, nil
 	}
 
 	gdprSignal = p.normalizeGDPR(gdprSignal)
 
 	if gdprSignal == SignalNo {
-		return true, true, nil
+		return true, true, true, nil
 	}
 
 	if consent == "" && gdprSignal == SignalYes {
-		return false, false, nil
+		return false, false, false, nil
 	}
 
 	if id, ok := p.vendorIDs[bidder]; ok {
-		return p.allowPI(ctx, id, consent, weakVendorEnforcement)
+		return p.allowActivities(ctx, id, consent, weakVendorEnforcement)
 	}
 
 	return p.defaultVendorPermissions()
 }
 
-func (p *permissionsImpl) defaultVendorPermissions() (allowGeo bool, allowID bool, err error) {
-	return false, false, nil
+func (p *permissionsImpl) defaultVendorPermissions() (allowBidRequest bool, passGeo bool, passID bool, err error) {
+	return false, false, false, nil
 }
 
 func (p *permissionsImpl) normalizeGDPR(gdprSignal Signal) Signal {
@@ -135,48 +135,51 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consen
 	return false, nil
 }
 
-func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
+func (p *permissionsImpl) allowActivities(ctx context.Context, vendorID uint16, consent string, weakVendorEnforcement bool) (allowBidRequest bool, passGeo bool, passID bool, err error) {
 	parsedConsent, vendor, err := p.parseVendor(ctx, vendorID, consent)
 	if err != nil {
-		return false, false, err
+		return false, false, false, err
 	}
 
 	if vendor == nil {
-		return false, false, nil
+		return false, false, false, nil
 	}
 
 	if parsedConsent.Version() == 2 {
 		if p.cfg.TCF2.Enabled {
-			return p.allowPITCF2(parsedConsent, vendor, vendorID, weakVendorEnforcement)
+			return p.allowActivitiesTCF2(parsedConsent, vendor, vendorID, weakVendorEnforcement)
 		}
 		if (vendor.Purpose(consentconstants.InfoStorageAccess) || vendor.LegitimateInterest(consentconstants.InfoStorageAccess) || weakVendorEnforcement) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && (vendor.Purpose(consentconstants.PersonalizationProfile) || vendor.LegitimateInterest(consentconstants.PersonalizationProfile) || weakVendorEnforcement) && parsedConsent.PurposeAllowed(consentconstants.PersonalizationProfile) && (parsedConsent.VendorConsent(vendorID) || weakVendorEnforcement) {
-			return true, true, nil
+			return true, true, true, nil
 		}
 	} else {
 		if (vendor.Purpose(tcf1constants.InfoStorageAccess) || vendor.LegitimateInterest(tcf1constants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(tcf1constants.InfoStorageAccess) && (vendor.Purpose(tcf1constants.AdSelectionDeliveryReporting) || vendor.LegitimateInterest(tcf1constants.AdSelectionDeliveryReporting)) && parsedConsent.PurposeAllowed(tcf1constants.AdSelectionDeliveryReporting) && parsedConsent.VendorConsent(vendorID) {
-			return true, true, nil
+			return true, true, true, nil
 		}
 	}
-	return false, false, nil
+	return true, false, false, nil
 }
 
-func (p *permissionsImpl) allowPITCF2(parsedConsent api.VendorConsents, vendor api.Vendor, vendorID uint16, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
+func (p *permissionsImpl) allowActivitiesTCF2(parsedConsent api.VendorConsents, vendor api.Vendor, vendorID uint16, weakVendorEnforcement bool) (allowBidRequest bool, passGeo bool, passID bool, err error) {
 	consent, ok := parsedConsent.(tcf2.ConsentMetadata)
-	err = nil
-	allowGeo = false
-	allowID = false
 	if !ok {
 		err = fmt.Errorf("Unable to access TCF2 parsed consent")
 		return
 	}
+
 	if p.cfg.TCF2.SpecialPurpose1.Enabled {
-		allowGeo = consent.SpecialFeatureOptIn(1) && (vendor.SpecialPurpose(1) || weakVendorEnforcement)
+		passGeo = consent.SpecialFeatureOptIn(1) && (vendor.SpecialPurpose(1) || weakVendorEnforcement)
+	} else {
+		passGeo = true
+	}
+	if p.cfg.TCF2.Purpose2.Enabled {
+		allowBidRequest = p.checkPurpose(consent, vendor, vendorID, tcf1constants.Purpose(2), weakVendorEnforcement)
 	} else {
-		allowGeo = true
+		allowBidRequest = true
 	}
 	for i := 2; i <= 10; i++ {
 		if p.checkPurpose(consent, vendor, vendorID, tcf1constants.Purpose(i), weakVendorEnforcement) {
-			allowID = true
+			passID = true
 			break
 		}
 	}
@@ -254,21 +257,6 @@ func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.B
 	return true, nil
 }
 
-func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
-	return true, true, nil
-}
-
-// Exporting to allow for easy test setups
-type AlwaysFail struct{}
-
-func (a AlwaysFail) HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) {
-	return false, nil
-}
-
-func (a AlwaysFail) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal Signal, consent string) (bool, error) {
-	return false, nil
-}
-
-func (a AlwaysFail) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
-	return false, false, nil
+func (a AlwaysAllow) AuctionActivitiesAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal Signal, consent string, weakVendorEnforcement bool) (allowBidRequest bool, passGeo bool, passID bool, err error) {
+	return true, true, true, nil
 }
diff --git a/go.mod b/go.mod
index b7fab89f..e3b9ff55 100644
--- a/go.mod
+++ b/go.mod
@@ -31,7 +31,7 @@ require (
 	github.com/mitchellh/mapstructure v1.0.0 // indirect
 	github.com/mxmCherry/openrtb/v15 v15.0.0
 	github.com/pelletier/go-toml v1.2.0 // indirect
-	github.com/prebid/go-gdpr v0.8.3
+	github.com/prebid/go-gdpr v0.9.0
 	github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed
 	github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
 	github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect
@@ -46,7 +46,7 @@ require (
 	github.com/spf13/pflag v1.0.2 // indirect
 	github.com/spf13/viper v1.1.0
 	github.com/stretchr/objx v0.1.1 // indirect
-	github.com/stretchr/testify v1.5.1
+	github.com/stretchr/testify v1.7.0
 	github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303
 	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
diff --git a/go.sum b/go.sum
index 3ddeaa2f..215f5e68 100644
--- a/go.sum
+++ b/go.sum
@@ -30,7 +30,6 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DP
 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
 github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd h1:biTJQdqouE5by89AAffXG8++TY+9Fsdrg5rinbt3tHk=
 github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -39,7 +38,6 @@ github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4
 github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -55,7 +53,6 @@ github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/influxdata/influxdb v1.6.1 h1:OseoBlzI5ftNI/bczyxSWq6PKRCNEeiXvyWP/wS5fB0=
 github.com/influxdata/influxdb v1.6.1/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
@@ -96,8 +93,8 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prebid/go-gdpr v0.8.3 h1:rjCZNV0AdKygiGHpVhNB42usjEpTN3qidXUPB1yarb0=
-github.com/prebid/go-gdpr v0.8.3/go.mod h1:TGzgqQDGKOVUkbqmY25K4uvcwMywSddXEaY4zUFiVBQ=
+github.com/prebid/go-gdpr v0.9.0 h1:FL1ZXuccMYOPIt69mIHF2AyRhv8ezvtjnUoAE3Ph8O0=
+github.com/prebid/go-gdpr v0.9.0/go.mod h1:OfBxLfd+JfP3OAJ1MhI4JYAV3dSMQYT1QAb80DHpZFo=
 github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg=
 github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
@@ -128,8 +125,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303 h1:Va10CytCCYRm4xBTses5ZDeDjeIQjhaiC9nRCe/yflI=
 github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303/go.mod h1:Xdcad1nGVhQfhoV0go+/4WaI/RZkWlvfjkVCdpMTxPY=
 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
@@ -198,3 +196,5 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go
index c1f726e9..2c65a475 100644
--- a/metrics/config/metrics.go
+++ b/metrics/config/metrics.go
@@ -244,6 +244,13 @@ func (me *MultiMetricsEngine) RecordRequestPrivacy(privacy metrics.PrivacyLabels
 	}
 }
 
+// RecordAdapterGDPRRequestBlocked across all engines
+func (me *MultiMetricsEngine) RecordAdapterGDPRRequestBlocked(adapter openrtb_ext.BidderName) {
+	for _, thisME := range *me {
+		thisME.RecordAdapterGDPRRequestBlocked(adapter)
+	}
+}
+
 // DummyMetricsEngine is a Noop metrics engine in case no metrics are configured. (may also be useful for tests)
 type DummyMetricsEngine struct{}
 
@@ -350,3 +357,7 @@ func (me *DummyMetricsEngine) RecordTimeoutNotice(success bool) {
 // RecordRequestPrivacy as a noop
 func (me *DummyMetricsEngine) RecordRequestPrivacy(privacy metrics.PrivacyLabels) {
 }
+
+// RecordAdapterGDPRRequestBlocked as a noop
+func (me *DummyMetricsEngine) RecordAdapterGDPRRequestBlocked(adapter openrtb_ext.BidderName) {
+}
diff --git a/metrics/go_metrics.go b/metrics/go_metrics.go
index ac7ed069..93659db3 100644
--- a/metrics/go_metrics.go
+++ b/metrics/go_metrics.go
@@ -75,18 +75,19 @@ type Metrics struct {
 
 // AdapterMetrics houses the metrics for a particular adapter
 type AdapterMetrics struct {
-	NoCookieMeter     metrics.Meter
-	ErrorMeters       map[AdapterError]metrics.Meter
-	NoBidMeter        metrics.Meter
-	GotBidsMeter      metrics.Meter
-	RequestTimer      metrics.Timer
-	PriceHistogram    metrics.Histogram
-	BidsReceivedMeter metrics.Meter
-	PanicMeter        metrics.Meter
-	MarkupMetrics     map[openrtb_ext.BidType]*MarkupDeliveryMetrics
-	ConnCreated       metrics.Counter
-	ConnReused        metrics.Counter
-	ConnWaitTime      metrics.Timer
+	NoCookieMeter      metrics.Meter
+	ErrorMeters        map[AdapterError]metrics.Meter
+	NoBidMeter         metrics.Meter
+	GotBidsMeter       metrics.Meter
+	RequestTimer       metrics.Timer
+	PriceHistogram     metrics.Histogram
+	BidsReceivedMeter  metrics.Meter
+	PanicMeter         metrics.Meter
+	MarkupMetrics      map[openrtb_ext.BidType]*MarkupDeliveryMetrics
+	ConnCreated        metrics.Counter
+	ConnReused         metrics.Counter
+	ConnWaitTime       metrics.Timer
+	GDPRRequestBlocked metrics.Meter
 }
 
 type MarkupDeliveryMetrics struct {
@@ -306,6 +307,9 @@ func makeBlankAdapterMetrics(disabledMetrics config.DisabledMetrics) *AdapterMet
 		newAdapter.ConnReused = metrics.NilCounter{}
 		newAdapter.ConnWaitTime = &metrics.NilTimer{}
 	}
+	if !disabledMetrics.AdapterGDPRRequestBlocked {
+		newAdapter.GDPRRequestBlocked = blankMeter
+	}
 	for _, err := range AdapterErrors() {
 		newAdapter.ErrorMeters[err] = blankMeter
 	}
@@ -350,6 +354,7 @@ func registerAdapterMetrics(registry metrics.Registry, adapterOrAccount string,
 		am.BidsReceivedMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.bids_received", adapterOrAccount, exchange), registry)
 	}
 	am.PanicMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.requests.panic", adapterOrAccount, exchange), registry)
+	am.GDPRRequestBlocked = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.gdpr_request_blocked", adapterOrAccount, exchange), registry)
 }
 
 func makeDeliveryMetrics(registry metrics.Registry, prefix string, bidType openrtb_ext.BidType) *MarkupDeliveryMetrics {
@@ -704,6 +709,20 @@ func (me *Metrics) RecordRequestPrivacy(privacy PrivacyLabels) {
 	return
 }
 
+func (me *Metrics) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName) {
+	if me.MetricsDisabled.AdapterGDPRRequestBlocked {
+		return
+	}
+
+	am, ok := me.AdapterMetrics[adapterName]
+	if !ok {
+		glog.Errorf("Trying to log adapter GDPR request blocked metric for %s: adapter not found", string(adapterName))
+		return
+	}
+
+	am.GDPRRequestBlocked.Mark(1)
+}
+
 func doMark(bidder openrtb_ext.BidderName, meters map[openrtb_ext.BidderName]metrics.Meter) {
 	met, ok := meters[bidder]
 	if ok {
diff --git a/metrics/metrics.go b/metrics/metrics.go
index 62de3afa..7cb5f0f1 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -358,4 +358,5 @@ type MetricsEngine interface {
 	RecordRequestQueueTime(success bool, requestType RequestType, length time.Duration)
 	RecordTimeoutNotice(sucess bool)
 	RecordRequestPrivacy(privacy PrivacyLabels)
+	RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName)
 }
diff --git a/metrics/metrics_mock.go b/metrics/metrics_mock.go
index c852eb47..261002b3 100644
--- a/metrics/metrics_mock.go
+++ b/metrics/metrics_mock.go
@@ -140,3 +140,8 @@ func (me *MetricsEngineMock) RecordTimeoutNotice(success bool) {
 func (me *MetricsEngineMock) RecordRequestPrivacy(privacy PrivacyLabels) {
 	me.Called(privacy)
 }
+
+// RecordAdapterGDPRRequestBlocked mock
+func (me *MetricsEngineMock) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName) {
+	me.Called(adapterName)
+}
diff --git a/metrics/prometheus/preload.go b/metrics/prometheus/preload.go
index f4dfe434..621ad808 100644
--- a/metrics/prometheus/preload.go
+++ b/metrics/prometheus/preload.go
@@ -178,6 +178,12 @@ func preloadLabelValues(m *Metrics) {
 		sourceLabel:  sourceValues,
 		versionLabel: tcfVersionsAsString(),
 	})
+
+	if !m.metricsDisabled.AdapterGDPRRequestBlocked {
+		preloadLabelValuesForCounter(m.adapterGDPRBlockedRequests, map[string][]string{
+			adapterLabel: adapterValues,
+		})
+	}
 }
 
 func preloadLabelValuesForCounter(counter *prometheus.CounterVec, labelsWithValues map[string][]string) {
diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go
index 1e384c0e..2c7361d0 100644
--- a/metrics/prometheus/prometheus.go
+++ b/metrics/prometheus/prometheus.go
@@ -48,17 +48,18 @@ type Metrics struct {
 	privacyTCF                   *prometheus.CounterVec
 
 	// Adapter Metrics
-	adapterBids               *prometheus.CounterVec
-	adapterCookieSync         *prometheus.CounterVec
-	adapterErrors             *prometheus.CounterVec
-	adapterPanics             *prometheus.CounterVec
-	adapterPrices             *prometheus.HistogramVec
-	adapterRequests           *prometheus.CounterVec
-	adapterRequestsTimer      *prometheus.HistogramVec
-	adapterUserSync           *prometheus.CounterVec
-	adapterReusedConnections  *prometheus.CounterVec
-	adapterCreatedConnections *prometheus.CounterVec
-	adapterConnectionWaitTime *prometheus.HistogramVec
+	adapterBids                *prometheus.CounterVec
+	adapterCookieSync          *prometheus.CounterVec
+	adapterErrors              *prometheus.CounterVec
+	adapterPanics              *prometheus.CounterVec
+	adapterPrices              *prometheus.HistogramVec
+	adapterRequests            *prometheus.CounterVec
+	adapterRequestsTimer       *prometheus.HistogramVec
+	adapterUserSync            *prometheus.CounterVec
+	adapterReusedConnections   *prometheus.CounterVec
+	adapterCreatedConnections  *prometheus.CounterVec
+	adapterConnectionWaitTime  *prometheus.HistogramVec
+	adapterGDPRBlockedRequests *prometheus.CounterVec
 
 	// Account Metrics
 	accountRequests *prometheus.CounterVec
@@ -283,6 +284,13 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
 		"Count of total requests to Prebid Server where the LMT flag was set by source",
 		[]string{sourceLabel})
 
+	if !metrics.metricsDisabled.AdapterGDPRRequestBlocked {
+		metrics.adapterGDPRBlockedRequests = newCounter(cfg, metrics.Registry,
+			"adapter_gdpr_requests_blocked",
+			"Count of total bidder requests blocked due to unsatisfied GDPR purpose 2 legal basis",
+			[]string{adapterLabel})
+	}
+
 	metrics.adapterBids = newCounter(cfg, metrics.Registry,
 		"adapter_bids",
 		"Count of bids labeled by adapter and markup delivery type (adm or nurl).",
@@ -691,3 +699,13 @@ func (m *Metrics) RecordRequestPrivacy(privacy metrics.PrivacyLabels) {
 		}).Inc()
 	}
 }
+
+func (m *Metrics) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName) {
+	if m.metricsDisabled.AdapterGDPRRequestBlocked {
+		return
+	}
+
+	m.adapterGDPRBlockedRequests.With(prometheus.Labels{
+		adapterLabel: string(adapterName),
+	}).Inc()
+}


Test patch:
diff --git a/config/config_test.go b/config/config_test.go
index 43ee8fa2..1d4c00a5 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -135,6 +135,7 @@ func TestDefaults(t *testing.T) {
 	cmpInts(t, "metrics.influxdb.collection_rate_seconds", cfg.Metrics.Influxdb.MetricSendInterval, 20)
 	cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, false)
 	cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true)
+	cmpBools(t, "adapter_gdpr_request_blocked", cfg.Metrics.Disabled.AdapterGDPRRequestBlocked, false)
 	cmpStrings(t, "certificates_file", cfg.PemCertsFile, "")
 	cmpBools(t, "stored_requests.filesystem.enabled", false, cfg.StoredRequests.Files.Enabled)
 	cmpStrings(t, "stored_requests.filesystem.directorypath", "./stored_requests/data/by_id", cfg.StoredRequests.Files.Path)
@@ -197,6 +198,7 @@ metrics:
   disabled_metrics:
     account_adapter_details: true
     adapter_connections_metrics: true
+    adapter_gdpr_request_blocked: true
 datacache:
   type: postgres
   filename: /usr/db/db.db
@@ -413,6 +415,7 @@ func TestFullConfig(t *testing.T) {
 	cmpBools(t, "auto_gen_source_tid", cfg.AutoGenSourceTID, false)
 	cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, true)
 	cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true)
+	cmpBools(t, "adapter_gdpr_request_blocked", cfg.Metrics.Disabled.AdapterGDPRRequestBlocked, true)
 	cmpStrings(t, "certificates_file", cfg.PemCertsFile, "/etc/ssl/cert.pem")
 	cmpStrings(t, "request_validation.ipv4_private_networks", cfg.RequestValidation.IPv4PrivateNetworks[0], "1.1.1.0/24")
 	cmpStrings(t, "request_validation.ipv6_private_networks", cfg.RequestValidation.IPv6PrivateNetworks[0], "1111::/16")
diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go
index f8b4f9d9..2062589e 100644
--- a/endpoints/auction_test.go
+++ b/endpoints/auction_test.go
@@ -441,8 +441,9 @@ func TestShouldUsersync(t *testing.T) {
 type auctionMockPermissions struct {
 	allowBidderSync  bool
 	allowHostCookies bool
-	allowGeo         bool
-	allowID          bool
+	allowBidRequest  bool
+	passGeo          bool
+	passID           bool
 }
 
 func (m *auctionMockPermissions) HostCookiesAllowed(ctx context.Context, gdprSignal gdpr.Signal, consent string) (bool, error) {
@@ -453,8 +454,8 @@ func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder o
 	return m.allowBidderSync, nil
 }
 
-func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
-	return m.allowGeo, m.allowID, nil
+func (m *auctionMockPermissions) AuctionActivitiesAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (allowBidRequest bool, passGeo bool, passID bool, err error) {
+	return m.allowBidRequest, m.passGeo, m.passID, nil
 }
 
 func TestBidSizeValidate(t *testing.T) {
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index bfd6c507..561b4ed7 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -254,6 +254,6 @@ func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.Bi
 	return ok, nil
 }
 
-func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
-	return true, true, nil
+func (g *gdprPerms) AuctionActivitiesAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (allowBidRequest, passGeo bool, passID bool, err error) {
+	return true, true, true, nil
 }
diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go
index 45ea2490..caeb09a8 100644
--- a/endpoints/setuid_test.go
+++ b/endpoints/setuid_test.go
@@ -439,8 +439,8 @@ func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return false, nil
 }
 
-func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
-	return g.personalInfoAllowed, g.personalInfoAllowed, nil
+func (g *mockPermsSetUID) AuctionActivitiesAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdprSignal gdpr.Signal, consent string, weakVendorEnforcement bool) (allowBidRequest bool, passGeo bool, passID bool, err error) {
+	return g.personalInfoAllowed, g.personalInfoAllowed, g.personalInfoAllowed, nil
 }
 
 func newFakeSyncer(familyName string) usersync.Usersyncer {
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index c18f4533..c64b3935 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -1770,7 +1770,7 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string]
 		me:                  metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.CoreBidderNames()),
 		cache:               &wellBehavedCache{},
 		cacheTime:           0,
-		gDPR:                gdpr.AlwaysFail{},
+		gDPR:                &permissionsMock{allowAllBidders: true},
 		currencyConverter:   currency.NewRateConverter(&http.Client{}, "", time.Duration(0)),
 		UsersyncIfAmbiguous: privacyConfig.GDPR.UsersyncIfAmbiguous,
 		privacyConfig:       privacyConfig,
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 9f6041e9..50636d35 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -14,14 +14,16 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 )
 
 // permissionsMock mocks the Permissions interface for tests
-//
-// It only allows appnexus for GDPR consent
 type permissionsMock struct {
-	personalInfoAllowed      bool
-	personalInfoAllowedError error
+	allowAllBidders bool
+	allowedBidders  []openrtb_ext.BidderName
+	passGeo         bool
+	passID          bool
+	activitiesError error
 }
 
 func (p *permissionsMock) HostCookiesAllowed(ctx context.Context, gdpr gdpr.Signal, consent string) (bool, error) {
@@ -32,8 +34,18 @@ func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_
 	return true, nil
 }
 
-func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdpr gdpr.Signal, consent string, weakVendorEnforcement bool) (allowGeo bool, allowID bool, err error) {
-	return p.personalInfoAllowed, p.personalInfoAllowed, p.personalInfoAllowedError
+func (p *permissionsMock) AuctionActivitiesAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, gdpr gdpr.Signal, consent string, weakVendorEnforcement bool) (allowBidRequest bool, passGeo bool, passID bool, err error) {
+	if p.allowAllBidders {
+		return true, p.passGeo, p.passID, p.activitiesError
+	}
+
+	for _, allowedBidder := range p.allowedBidders {
+		if bidder == allowedBidder {
+			allowBidRequest = true
+		}
+	}
+
+	return allowBidRequest, p.passGeo, p.passID, p.activitiesError
 }
 
 func assertReq(t *testing.T, bidderRequests []BidderRequest,
@@ -465,7 +477,9 @@ func TestCleanOpenRTBRequests(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig, nil)
+		metricsMock := metrics.MetricsEngineMock{}
+		permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true}
+		bidderRequests, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, &permissions, &metricsMock, true, privacyConfig, nil)
 		if test.hasError {
 			assert.NotNil(t, err, "Error shouldn't be nil")
 		} else {
@@ -620,7 +634,8 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) {
 			context.Background(),
 			auctionReq,
 			nil,
-			&permissionsMock{personalInfoAllowed: true},
+			&permissionsMock{allowAllBidders: true, passGeo: true, passID: true},
+			&metrics.MetricsEngineMock{},
 			true,
 			privacyConfig,
 			nil)
@@ -681,7 +696,9 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) {
 				Enforce: true,
 			},
 		}
-		_, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig, nil)
+		permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true}
+		metrics := metrics.MetricsEngineMock{}
+		_, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, &permissions, &metrics, true, privacyConfig, nil)
 
 		assert.ElementsMatch(t, []error{test.expectError}, errs, test.description)
 	}
@@ -721,7 +738,9 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) {
 			UserSyncs:  &emptyUsersync{},
 		}
 
-		bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, &permissionsMock{personalInfoAllowed: true}, true, config.Privacy{}, nil)
+		permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true}
+		metrics := metrics.MetricsEngineMock{}
+		bidderRequests, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, &permissions, &metrics, true, config.Privacy{}, nil)
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -828,7 +847,9 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) {
 			UserSyncs:  &emptyUsersync{},
 		}
 
-		bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, &permissionsMock{}, true, config.Privacy{}, nil)
+		permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true}
+		metrics := metrics.MetricsEngineMock{}
+		bidderRequests, _, errs := cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, &permissions, &metrics, true, config.Privacy{}, nil)
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1409,7 +1430,9 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) {
 			},
 		}
 
-		results, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig, nil)
+		permissions := permissionsMock{allowAllBidders: true, passGeo: true, passID: true}
+		metrics := metrics.MetricsEngineMock{}
+		results, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), auctionReq, nil, &permissions, &metrics, true, privacyConfig, nil)
 		result := results[0]
 
 		assert.Nil(t, errs)
@@ -1424,7 +1447,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) {
 	}
 }
 
-func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
+func TestCleanOpenRTBRequestsGDPRScrub(t *testing.T) {
 	tcf1Consent := "BONV8oqONXwgmADACHENAO7pqzAAppY"
 	tcf2Consent := "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA"
 	trueValue, falseValue := true, false
@@ -1624,7 +1647,8 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			context.Background(),
 			auctionReq,
 			nil,
-			&permissionsMock{personalInfoAllowed: !test.gdprScrub, personalInfoAllowedError: test.permissionsError},
+			&permissionsMock{allowAllBidders: true, passGeo: !test.gdprScrub, passID: !test.gdprScrub, activitiesError: test.permissionsError},
+			&metrics.MetricsEngineMock{},
 			test.userSyncIfAmbiguous,
 			privacyConfig,
 			nil)
@@ -1647,6 +1671,97 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 	}
 }
 
+func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) {
+	testCases := []struct {
+		description            string
+		gdprEnforced           bool
+		gdprAllowedBidders     []openrtb_ext.BidderName
+		expectedBidders        []openrtb_ext.BidderName
+		expectedBlockedBidders []openrtb_ext.BidderName
+	}{
+		{
+			description:            "gdpr enforced, one request allowed and one request blocked",
+			gdprEnforced:           true,
+			gdprAllowedBidders:     []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus},
+			expectedBidders:        []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus},
+			expectedBlockedBidders: []openrtb_ext.BidderName{openrtb_ext.BidderRubicon},
+		},
+		{
+			description:            "gdpr enforced, two requests allowed and no requests blocked",
+			gdprEnforced:           true,
+			gdprAllowedBidders:     []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon},
+			expectedBidders:        []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon},
+			expectedBlockedBidders: []openrtb_ext.BidderName{},
+		},
+		{
+			description:            "gdpr not enforced, two requests allowed and no requests blocked",
+			gdprEnforced:           false,
+			gdprAllowedBidders:     []openrtb_ext.BidderName{},
+			expectedBidders:        []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon},
+			expectedBlockedBidders: []openrtb_ext.BidderName{},
+		},
+	}
+
+	for _, test := range testCases {
+		req := newBidRequest(t)
+		req.Regs = &openrtb2.Regs{
+			Ext: json.RawMessage(`{"gdpr":1}`),
+		}
+		req.Imp[0].Ext = json.RawMessage(`{"appnexus": {"placementId": 1}, "rubicon": {}}`)
+
+		privacyConfig := config.Privacy{
+			GDPR: config.GDPR{
+				Enabled:             test.gdprEnforced,
+				UsersyncIfAmbiguous: true,
+				TCF2: config.TCF2{
+					Enabled: true,
+				},
+			},
+		}
+
+		accountConfig := config.Account{
+			GDPR: config.AccountGDPR{
+				Enabled: nil,
+			},
+		}
+
+		auctionReq := AuctionRequest{
+			BidRequest: req,
+			UserSyncs:  &emptyUsersync{},
+			Account:    accountConfig,
+		}
+
+		metricsMock := metrics.MetricsEngineMock{}
+		metricsMock.Mock.On("RecordAdapterGDPRRequestBlocked", mock.Anything).Return()
+
+		results, _, errs := cleanOpenRTBRequests(
+			context.Background(),
+			auctionReq,
+			nil,
+			&permissionsMock{allowedBidders: test.gdprAllowedBidders, passGeo: true, passID: true, activitiesError: nil},
+			&metricsMock,
+			true,
+			privacyConfig,
+			nil)
+
+		// extract bidder name from each request in the results
+		bidders := []openrtb_ext.BidderName{}
+		for _, req := range results {
+			bidders = append(bidders, req.BidderName)
+		}
+
+		assert.Empty(t, errs, test.description)
+		assert.ElementsMatch(t, bidders, test.expectedBidders, test.description)
+
+		for _, blockedBidder := range test.expectedBlockedBidders {
+			metricsMock.AssertCalled(t, "RecordAdapterGDPRRequestBlocked", blockedBidder)
+		}
+		for _, allowedBidder := range test.expectedBidders {
+			metricsMock.AssertNotCalled(t, "RecordAdapterGDPRRequestBlocked", allowedBidder)
+		}
+	}
+}
+
 // newAdapterAliasBidRequest builds a BidRequest with aliases
 func newAdapterAliasBidRequest(t *testing.T) *openrtb2.BidRequest {
 	dnt := int8(1)
diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go
index cc08aedd..3b974ffa 100644
--- a/gdpr/impl_test.go
+++ b/gdpr/impl_test.go
@@ -169,7 +169,7 @@ func TestMalformedConsent(t *testing.T) {
 	assertBoolsEqual(t, false, sync)
 }
 
-func TestAllowPersonalInfo(t *testing.T) {
+func TestAllowActivities(t *testing.T) {
 	bidderAllowedByConsent := openrtb_ext.BidderAppnexus
 	bidderBlockedByConsent := openrtb_ext.BidderRubicon
 	consent := "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA"
@@ -181,7 +181,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 		userSyncIfAmbiguous   bool
 		gdpr                  Signal
 		consent               string
-		allowID               bool
+		passID                bool
 		weakVendorEnforcement bool
 	}{
 		{
@@ -191,7 +191,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalYes,
 			consent:             consent,
-			allowID:             true,
+			passID:              true,
 		},
 		{
 			description:         "Allow PI - known vendor with No GDPR",
@@ -199,7 +199,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalNo,
 			consent:             consent,
-			allowID:             true,
+			passID:              true,
 		},
 		{
 			description:         "Allow PI - known vendor with Yes GDPR",
@@ -207,7 +207,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalYes,
 			consent:             consent,
-			allowID:             true,
+			passID:              true,
 		},
 		{
 			description:         "PI allowed according to host setting UserSyncIfAmbiguous true - known vendor with ambiguous GDPR and empty consent",
@@ -215,7 +215,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: true,
 			gdpr:                SignalAmbiguous,
 			consent:             "",
-			allowID:             true,
+			passID:              true,
 		},
 		{
 			description:         "PI allowed according to host setting UserSyncIfAmbiguous true - known vendor with ambiguous GDPR and non-empty consent",
@@ -223,7 +223,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: true,
 			gdpr:                SignalAmbiguous,
 			consent:             consent,
-			allowID:             true,
+			passID:              true,
 		},
 		{
 			description:         "PI allowed according to host setting UserSyncIfAmbiguous false - known vendor with ambiguous GDPR and empty consent",
@@ -231,7 +231,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalAmbiguous,
 			consent:             "",
-			allowID:             false,
+			passID:              false,
 		},
 		{
 			description:         "PI allowed according to host setting UserSyncIfAmbiguous false - known vendor with ambiguous GDPR and non-empty consent",
@@ -239,7 +239,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalAmbiguous,
 			consent:             consent,
-			allowID:             true,
+			passID:              true,
 		},
 		{
 			description:         "Don't allow PI - known vendor with Yes GDPR and empty consent",
@@ -247,7 +247,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalYes,
 			consent:             "",
-			allowID:             false,
+			passID:              false,
 		},
 		{
 			description:         "Don't allow PI - default vendor with Yes GDPR and non-empty consent",
@@ -255,7 +255,7 @@ func TestAllowPersonalInfo(t *testing.T) {
 			userSyncIfAmbiguous: false,
 			gdpr:                SignalYes,
 			consent:             consent,
-			allowID:             false,
+			passID:              false,
 		},
 	}
 
@@ -286,10 +286,10 @@ func TestAllowPersonalInfo(t *testing.T) {
 	for _, tt := range tests {
 		perms.cfg.UsersyncIfAmbiguous = tt.userSyncIfAmbiguous
 
-		_, allowID, err := perms.PersonalInfoAllowed(context.Background(), tt.bidderName, tt.publisherID, tt.gdpr, tt.consent, tt.weakVendorEnforcement)
+		_, _, passID, err := perms.AuctionActivitiesAllowed(context.Background(), tt.bidderName, tt.publisherID, tt.gdpr, tt.consent, tt.weakVendorEnforcement)
 
 		assert.Nil(t, err, tt.description)
-		assert.Equal(t, tt.allowID, allowID, tt.description)
+		assert.Equal(t, tt.passID, passID, tt.description)
 	}
 }
 
@@ -347,12 +347,13 @@ type tcf2TestDef struct {
 	description           string
 	bidder                openrtb_ext.BidderName
 	consent               string
-	allowGeo              bool
-	allowID               bool
+	allowBid              bool
+	passGeo               bool
+	passID                bool
 	weakVendorEnforcement bool
 }
 
-func TestAllowPersonalInfoTCF2(t *testing.T) {
+func TestAllowActivitiesTCF2(t *testing.T) {
 	vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34())
 	perms := permissionsImpl{
 		cfg: tcf2Config,
@@ -372,36 +373,39 @@ func TestAllowPersonalInfoTCF2(t *testing.T) {
 	}
 
 	// COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consents to purposes and vendors 2, 6, 8
-	// PI needs all purposes to succeed
 	testDefs := []tcf2TestDef{
 		{
 			description: "Appnexus vendor test, insufficient purposes claimed",
 			bidder:      openrtb_ext.BidderAppnexus,
 			consent:     "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
-			allowGeo:    false,
-			allowID:     false,
+			allowBid:    false,
+			passGeo:     false,
+			passID:      false,
 		},
 		{
 			description:           "Appnexus vendor test, insufficient purposes claimed, basic enforcement",
 			bidder:                openrtb_ext.BidderAppnexus,
 			consent:               "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
-			allowGeo:              true,
-			allowID:               true,
+			allowBid:              true,
+			passGeo:               true,
+			passID:                true,
 			weakVendorEnforcement: true,
 		},
 		{
 			description: "Pubmatic vendor test, flex purposes claimed",
 			bidder:      openrtb_ext.BidderPubmatic,
 			consent:     "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
-			allowGeo:    true,
-			allowID:     true,
+			allowBid:    true,
+			passGeo:     true,
+			passID:      true,
 		},
 		{
 			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
 			bidder:      openrtb_ext.BidderRubicon,
 			consent:     "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA",
-			allowGeo:    false,
-			allowID:     true,
+			allowBid:    true,
+			passGeo:     false,
+			passID:      true,
 		},
 		{
 			// This requires publisher restrictions on any claimed purposes, 2-10. Vendor must declare all claimed purposes
@@ -410,20 +414,22 @@ func TestAllowPersonalInfoTCF2(t *testing.T) {
 			description: "OpenX vendor test, Specific purposes/LIs claimed, no geo claimed, Publisher restrictions apply",
 			bidder:      openrtb_ext.BidderOpenx,
 			consent:     "CPAavcCPAavcCAGABCFRBKCsAP_AAH_AAAqIHFNf_X_fb3_j-_59_9t0eY1f9_7_v-0zjgeds-8Nyd_X_L8X5mM7vB36pq4KuR4Eu3LBAQdlHOHcTUmw6IkVqTPsbk2Mr7NKJ7PEinMbe2dYGH9_n9XT_ZKY79_____7__-_____7_f__-__3_vp9V---wOJAIMBAUAgAEMAAQIFCIQAAQhiQAAAABBCIBQJIAEqgAWVwEdoIEACAxAQgQAgBBQgwCAAQAAJKAgBACwQCAAiAQAAgAEAIAAEIAILACQEAAAEAJCAAiACECAgiAAg5DAgIgCCAFABAAAuJDACAMooASBAPGQGAAKAAqACGAEwALgAjgBlgDUAHZAPsA_ACMAFLAK2AbwBMQCbAFogLYAYEAw8BkQDOQGeAM-EQHwAVABWAC4AIYAZAAywBqADZAHYAPwAgABGAClgFPANYAdUA-QCGwEOgIvASIAmwBOwCkQFyAMCAYSAw8Bk4DOQGfCQAYADgBzgN_CQTgAEAALgAoACoAGQAOAAeABAACIAFQAMIAaABqADyAIYAigBMgCqAKwAWAAuABvADmAHoAQ0AiACJgEsAS4AmgBSgC3AGGAMgAZcA1ADVAGyAO8AewA-IB9gH6AQAAjABQQClgFPAL8AYoA1gBtADcAG8AOIAegA-QCGwEOgIqAReAkQBMQCZQE2AJ2AUOApEBYoC2AFyALvAYEAwYBhIDDQGHgMiAZIAycBlwDOQGfANIAadA1gDWQoAEAYQaBIACoAKwAXABDADIAGWANQAbIA7AB-AEAAIKARgApYBT4C0ALSAawA3gB1QD5AIbAQ6Ai8BIgCbAE7AKRAXIAwIBhIDDwGMAMnAZyAzwBnwcAEAA4Bv4qA2ABQAFQAQwAmABcAEcAMsAagA7AB-AEYAKXAWgBaQDeAJBATEAmwBTYC2AFyAMCAYeAyIBnIDPAGfANyHQWQAFwAUABUADIAHAAQAAiABdADAAMYAaABqADwAH0AQwBFACZAFUAVgAsABcADEAGYAN4AcwA9ACGAERAJYAmABNACjAFKALEAW4AwwBkADKAGiANQAbIA3wB3gD2gH2AfoBGACVAFBAKeAWKAtAC0gFzALyAX4AxQBuADiQHTAdQA9ACGwEOgIiAReAkEBIgCbAE7AKHAU0AqwBYsC2ALZAXAAuQBdoC7wGEgMNAYeAxIBjADHgGSAMnAZUAywBlwDOQGfANEgaQBpIDSwGnANYAbGPABAIqAb-QgZgALAAoABkAEQALgAYgBDACYAFUALgAYgAzABvAD0AI4AWIAygBqADfAHfAPsA_ACMAFBAKGAU-AtAC0gF-AMUAdQA9ACQQEiAJsAU0AsUBaMC2ALaAXAAuQBdoDDwGJAMiAZOAzkBngDPgGiANJAaWA4AlAyAAQAAsACgAGQAOAAigBgAGIAPAAiABMACqAFwAMQAZgA2gCGgEQARIAowBSgC3AGEAMoAaoA2QB3gD8AIwAU-AtAC0gGKANwAcQA6gCHQEXgJEATYAsUBbAC7QGHgMiAZOAywBnIDPAGfANIAawA4AmACARUA38pBBAAXABQAFQAMgAcABAACKAGAAYwA0ADUAHkAQwBFACYAFIAKoAWAAuABiADMAHMAQwAiABRgClAFiALcAZQA0QBqgDZAHfAPsA_ACMAFBAKGAVsAuYBeQDaAG4APQAh0BF4CRAE2AJ2AUOApoBWwCxQFsALgAXIAu0BhoDDwGMAMiAZIAycBlwDOQGeAM-gaQBpMDWANZAbGVABAA-Ab-A.YAAAAAAAAAAA",
-			allowGeo:    false,
-			allowID:     true,
+			allowBid:    true,
+			passGeo:     false,
+			passID:      true,
 		},
 	}
 
 	for _, td := range testDefs {
-		allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
-		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
-		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
-		assert.EqualValuesf(t, td.allowID, allowID, "AllowGeo failure on %s", td.description)
+		allowBid, passGeo, passID, err := perms.AuctionActivitiesAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
+		assert.NoErrorf(t, err, "Error processing AuctionActivitiesAllowed for %s", td.description)
+		assert.EqualValuesf(t, td.allowBid, allowBid, "AllowBid failure on %s", td.description)
+		assert.EqualValuesf(t, td.passGeo, passGeo, "PassGeo failure on %s", td.description)
+		assert.EqualValuesf(t, td.passID, passID, "PassID failure on %s", td.description)
 	}
 }
 
-func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) {
+func TestAllowActivitiesWhitelistTCF2(t *testing.T) {
 	vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34())
 	perms := permissionsImpl{
 		cfg: tcf2Config,
@@ -441,13 +447,13 @@ func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) {
 	}
 	// Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array
 	perms.cfg.NonStandardPublisherMap = map[string]struct{}{"appNexusAppID": {}}
-	allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", false)
-	assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed")
-	assert.EqualValuesf(t, true, allowGeo, "AllowGeo failure")
-	assert.EqualValuesf(t, true, allowID, "AllowID failure")
+	_, passGeo, passID, err := perms.AuctionActivitiesAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", false)
+	assert.NoErrorf(t, err, "Error processing AuctionActivitiesAllowed")
+	assert.EqualValuesf(t, true, passGeo, "PassGeo failure")
+	assert.EqualValuesf(t, true, passID, "PassID failure")
 }
 
-func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) {
+func TestAllowActivitiesTCF2PubRestrict(t *testing.T) {
 	vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34())
 	perms := permissionsImpl{
 		cfg: tcf2Config,
@@ -471,135 +477,30 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) {
 			description: "Appnexus vendor test, insufficient purposes claimed",
 			bidder:      openrtb_ext.BidderAppnexus,
 			consent:     "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA",
-			allowGeo:    false,
-			allowID:     false,
+			passGeo:     false,
+			passID:      false,
 		},
 		{
 			description: "Pubmatic vendor test, flex purposes claimed",
 			bidder:      openrtb_ext.BidderPubmatic,
 			consent:     "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA",
-			allowGeo:    false,
-			allowID:     false,
+			passGeo:     false,
+			passID:      false,
 		},
 		{
 			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
 			bidder:      openrtb_ext.BidderRubicon,
 			consent:     "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA",
-			allowGeo:    false,
-			allowID:     true,
+			passGeo:     false,
+			passID:      true,
 		},
 	}
 
 	for _, td := range testDefs {
-		allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
-		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
-		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
-		assert.EqualValuesf(t, td.allowID, allowID, "AllowID failure on %s", td.description)
-	}
-}
-
-func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) {
-	vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34())
-	perms := permissionsImpl{
-		cfg: tcf2Config,
-		vendorIDs: map[openrtb_ext.BidderName]uint16{
-			openrtb_ext.BidderAppnexus: 2,
-			openrtb_ext.BidderPubmatic: 10,
-			openrtb_ext.BidderRubicon:  8,
-		},
-		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
-			tcf1SpecVersion: nil,
-			tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{
-				34: parseVendorListDataV2(t, vendorListData),
-			}),
-		},
-	}
-	perms.cfg.TCF2.PurposeOneTreatment.Enabled = true
-	perms.cfg.TCF2.PurposeOneTreatment.AccessAllowed = true
-
-	// COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA Purpose one flag set
-	testDefs := []tcf2TestDef{
-		{
-			description: "Appnexus vendor test, insufficient purposes claimed",
-			bidder:      openrtb_ext.BidderAppnexus,
-			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowGeo:    false,
-			allowID:     false,
-		},
-		{
-			description: "Pubmatic vendor test, flex purposes claimed",
-			bidder:      openrtb_ext.BidderPubmatic,
-			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowGeo:    true,
-			allowID:     true,
-		},
-		{
-			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
-			bidder:      openrtb_ext.BidderRubicon,
-			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowGeo:    false,
-			allowID:     true,
-		},
-	}
-
-	for _, td := range testDefs {
-		allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
-		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
-		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
-		assert.EqualValuesf(t, td.allowID, allowID, "AllowID failure on %s", td.description)
-	}
-}
-
-func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) {
-	vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34())
-	perms := permissionsImpl{
-		cfg: tcf2Config,
-		vendorIDs: map[openrtb_ext.BidderName]uint16{
-			openrtb_ext.BidderAppnexus: 2,
-			openrtb_ext.BidderPubmatic: 10,
-			openrtb_ext.BidderRubicon:  8,
-		},
-		fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
-			tcf1SpecVersion: nil,
-			tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{
-				34: parseVendorListDataV2(t, vendorListData),
-			}),
-		},
-	}
-	perms.cfg.TCF2.PurposeOneTreatment.Enabled = true
-	perms.cfg.TCF2.PurposeOneTreatment.AccessAllowed = false
-
-	// COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA Purpose one flag set
-	// Purpose one treatment will fail PI, but allow passing the IDs.
-	testDefs := []tcf2TestDef{
-		{
-			description: "Appnexus vendor test, insufficient purposes claimed",
-			bidder:      openrtb_ext.BidderAppnexus,
-			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowGeo:    false,
-			allowID:     false,
-		},
-		{
-			description: "Pubmatic vendor test, flex purposes claimed",
-			bidder:      openrtb_ext.BidderPubmatic,
-			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowGeo:    true,
-			allowID:     true,
-		},
-		{
-			description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed",
-			bidder:      openrtb_ext.BidderRubicon,
-			consent:     "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA",
-			allowGeo:    false,
-			allowID:     true,
-		},
-	}
-
-	for _, td := range testDefs {
-		allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
-		assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description)
-		assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description)
-		assert.EqualValuesf(t, td.allowID, allowID, "AllowID failure on %s", td.description)
+		_, passGeo, passID, err := perms.AuctionActivitiesAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
+		assert.NoErrorf(t, err, "Error processing AuctionActivitiesAllowed for %s", td.description)
+		assert.EqualValuesf(t, td.passGeo, passGeo, "PassGeo failure on %s", td.description)
+		assert.EqualValuesf(t, td.passID, passID, "PassID failure on %s", td.description)
 	}
 }
 
@@ -811,3 +712,78 @@ func TestNormalizeGDPR(t *testing.T) {
 		assert.Equal(t, tt.wantSignal, normalizedSignal, tt.description)
 	}
 }
+
+func TestAllowActivitiesTCF2BidRequests(t *testing.T) {
+	purpose2AndVendorConsent := "CPF_61ePF_61eFxAAAENAiCAAEAAAAAAAAAAADAQAAAAAA"
+	purpose2ConsentWithoutVendorConsent := "CPF_61ePF_61eFxAAAENAiCAAEAAAAAAAAAAABIAAAAA"
+
+	testDefs := []struct {
+		description           string
+		purpose2Enabled       bool
+		bidder                openrtb_ext.BidderName
+		consent               string
+		allowBid              bool
+		passGeo               bool
+		passID                bool
+		weakVendorEnforcement bool
+	}{
+		{
+			description:     "Bid blocked - p2 enabled, user consents to p2 but not vendor, vendor consents to p2",
+			purpose2Enabled: true,
+			bidder:          openrtb_ext.BidderPubmatic,
+			consent:         purpose2ConsentWithoutVendorConsent,
+			allowBid:        false,
+			passGeo:         false,
+			passID:          false,
+		},
+		{
+			description:     "Bid allowed - p2 disabled, user consents to p2 but not vendor, vendor consents to p2",
+			purpose2Enabled: false,
+			bidder:          openrtb_ext.BidderPubmatic,
+			consent:         purpose2ConsentWithoutVendorConsent,
+			allowBid:        true,
+			passGeo:         false,
+			passID:          false,
+		},
+		{
+			description:     "Bid allowed - p2 enabled, user consents to p2 and vendor, vendor consents to p2",
+			purpose2Enabled: true,
+			bidder:          openrtb_ext.BidderPubmatic,
+			consent:         purpose2AndVendorConsent,
+			allowBid:        true,
+			passGeo:         false,
+			passID:          true,
+		},
+	}
+
+	for _, td := range testDefs {
+		vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34())
+		perms := permissionsImpl{
+			cfg: config.GDPR{
+				HostVendorID: 2,
+				TCF2: config.TCF2{
+					Enabled:         true,
+					Purpose1:        config.PurposeDetail{Enabled: true},
+					Purpose2:        config.PurposeDetail{Enabled: td.purpose2Enabled},
+					Purpose7:        config.PurposeDetail{Enabled: true},
+					SpecialPurpose1: config.PurposeDetail{Enabled: true},
+				},
+			},
+			vendorIDs: map[openrtb_ext.BidderName]uint16{
+				openrtb_ext.BidderPubmatic: 6,
+			},
+			fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){
+				tcf1SpecVersion: nil,
+				tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{
+					34: parseVendorListDataV2(t, vendorListData),
+				}),
+			},
+		}
+
+		allowBid, passGeo, passID, err := perms.AuctionActivitiesAllowed(context.Background(), td.bidder, "", SignalYes, td.consent, td.weakVendorEnforcement)
+		assert.NoErrorf(t, err, "Error processing AuctionActivitiesAllowed for %s", td.description)
+		assert.EqualValuesf(t, td.allowBid, allowBid, "AllowBid failure on %s", td.description)
+		assert.EqualValuesf(t, td.passGeo, passGeo, "PassGeo failure on %s", td.description)
+		assert.EqualValuesf(t, td.passID, passID, "PassID failure on %s", td.description)
+	}
+}
diff --git a/metrics/config/metrics_test.go b/metrics/config/metrics_test.go
index 5b70b53b..7f964333 100644
--- a/metrics/config/metrics_test.go
+++ b/metrics/config/metrics_test.go
@@ -118,6 +118,8 @@ func TestMultiMetricsEngine(t *testing.T) {
 	metricsEngine.RecordStoredImpCacheResult(metrics.CacheHit, 5)
 	metricsEngine.RecordAccountCacheResult(metrics.CacheHit, 6)
 
+	metricsEngine.RecordAdapterGDPRRequestBlocked(openrtb_ext.BidderAppnexus)
+
 	metricsEngine.RecordRequestQueueTime(false, metrics.ReqTypeVideo, time.Duration(1))
 
 	//Make the metrics engine, instantiated here with goEngine, fill its RequestStatuses[RequestType][metrics.RequestStatusXX] with the new boolean values added to metrics.Labels
@@ -161,6 +163,8 @@ func TestMultiMetricsEngine(t *testing.T) {
 	VerifyMetrics(t, "StoredReqCache.Hit", goEngine.StoredReqCacheMeter[metrics.CacheHit].Count(), 4)
 	VerifyMetrics(t, "StoredImpCache.Hit", goEngine.StoredImpCacheMeter[metrics.CacheHit].Count(), 5)
 	VerifyMetrics(t, "AccountCache.Hit", goEngine.AccountCacheMeter[metrics.CacheHit].Count(), 6)
+
+	VerifyMetrics(t, "AdapterMetrics.AppNexus.GDPRRequestBlocked", goEngine.AdapterMetrics[openrtb_ext.BidderAppnexus].GDPRRequestBlocked.Count(), 1)
 }
 
 func VerifyMetrics(t *testing.T, name string, actual int64, expected int64) {
diff --git a/metrics/go_metrics_test.go b/metrics/go_metrics_test.go
index 2d0b9097..7c0a3f37 100644
--- a/metrics/go_metrics_test.go
+++ b/metrics/go_metrics_test.go
@@ -570,6 +570,45 @@ func TestRecordRequestPrivacy(t *testing.T) {
 	assert.Equal(t, m.PrivacyTCFRequestVersion[TCFVersionV2].Count(), int64(1), "TCF V2")
 }
 
+func TestRecordAdapterGDPRRequestBlocked(t *testing.T) {
+	var fakeBidder openrtb_ext.BidderName = "fooAdvertising"
+
+	tests := []struct {
+		description     string
+		metricsDisabled bool
+		adapterName     openrtb_ext.BidderName
+		expectedCount   int64
+	}{
+		{
+			description:     "",
+			metricsDisabled: false,
+			adapterName:     openrtb_ext.BidderAppnexus,
+			expectedCount:   1,
+		},
+		{
+			description:     "",
+			metricsDisabled: false,
+			adapterName:     fakeBidder,
+			expectedCount:   0,
+		},
+		{
+			description:     "",
+			metricsDisabled: true,
+			adapterName:     openrtb_ext.BidderAppnexus,
+			expectedCount:   0,
+		},
+	}
+
+	for _, tt := range tests {
+		registry := metrics.NewRegistry()
+		m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AdapterGDPRRequestBlocked: tt.metricsDisabled})
+
+		m.RecordAdapterGDPRRequestBlocked(tt.adapterName)
+
+		assert.Equal(t, tt.expectedCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].GDPRRequestBlocked.Count(), tt.description)
+	}
+}
+
 func ensureContainsBidTypeMetrics(t *testing.T, registry metrics.Registry, prefix string, mdm map[openrtb_ext.BidType]*MarkupDeliveryMetrics) {
 	ensureContains(t, registry, prefix+".banner.adm_bids_received", mdm[openrtb_ext.BidTypeBanner].AdmMeter)
 	ensureContains(t, registry, prefix+".banner.nurl_bids_received", mdm[openrtb_ext.BidTypeBanner].NurlMeter)
diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go
index b2ffff34..72ddd105 100644
--- a/metrics/prometheus/prometheus_test.go
+++ b/metrics/prometheus/prometheus_test.go
@@ -1341,17 +1341,21 @@ func TestRecordAdapterConnections(t *testing.T) {
 	}
 }
 
-func TestDisableAdapterConnections(t *testing.T) {
+func TestDisabledMetrics(t *testing.T) {
 	prometheusMetrics := NewMetrics(config.PrometheusMetrics{
 		Port:      8080,
 		Namespace: "prebid",
 		Subsystem: "server",
-	}, config.DisabledMetrics{AdapterConnectionMetrics: true})
+	}, config.DisabledMetrics{
+		AdapterConnectionMetrics:  true,
+		AdapterGDPRRequestBlocked: true,
+	})
 
 	// Assert counter vector was not initialized
 	assert.Nil(t, prometheusMetrics.adapterReusedConnections, "Counter Vector adapterReusedConnections should be nil")
 	assert.Nil(t, prometheusMetrics.adapterCreatedConnections, "Counter Vector adapterCreatedConnections should be nil")
 	assert.Nil(t, prometheusMetrics.adapterConnectionWaitTime, "Counter Vector adapterConnectionWaitTime should be nil")
+	assert.Nil(t, prometheusMetrics.adapterGDPRBlockedRequests, "Counter Vector adapterGDPRBlockedRequests should be nil")
 }
 
 func TestRecordRequestPrivacy(t *testing.T) {
@@ -1510,3 +1514,18 @@ func assertHistogram(t *testing.T, name string, histogram dto.Histogram, expecte
 	assert.Equal(t, expectedCount, histogram.GetSampleCount(), name+":count")
 	assert.Equal(t, expectedSum, histogram.GetSampleSum(), name+":sum")
 }
+
+func TestRecordAdapterGDPRRequestBlocked(t *testing.T) {
+	m := createMetricsForTesting()
+
+	m.RecordAdapterGDPRRequestBlocked(openrtb_ext.BidderAppnexus)
+
+	assertCounterVecValue(t,
+		"Increment adapter GDPR request blocked counter",
+		"adapter_gdpr_requests_blocked",
+		m.adapterGDPRBlockedRequests,
+		1,
+		prometheus.Labels{
+			adapterLabel: string(openrtb_ext.BidderAppnexus),
+		})
+}