Skip to content

Commit

Permalink
Add Flag for Host to Enabled/Disable Bidder from Usersync (prebid#3285)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexBVolcy authored Dec 5, 2023
1 parent 3f42b89 commit 8128b24
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 1 deletion.
3 changes: 3 additions & 0 deletions config/bidderinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ type Syncer struct {
// SupportCORS identifies if CORS is supported for the user syncing endpoints.
SupportCORS *bool `yaml:"supportCors" mapstructure:"support_cors"`

// Enabled signifies whether a bidder is enabled/disabled for user sync
Enabled *bool `yaml:"enabled" mapstructure:"enabled"`

// SkipWhen allows bidders to specify when they don't want to sync
SkipWhen *SkipWhen `yaml:"skipwhen" mapstructure:"skipwhen"`
}
Expand Down
2 changes: 2 additions & 0 deletions endpoints/cookie_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,8 @@ func getDebugMessage(status usersync.Status) string {
return "No sync config"
case usersync.StatusTypeNotSupported:
return "Type not supported"
case usersync.StatusBlockedByDisabledUsersync:
return "Sync disabled by config"
}
return ""
}
Expand Down
3 changes: 2 additions & 1 deletion endpoints/cookie_sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,7 @@ func TestCookieSyncHandleResponse(t *testing.T) {
{Bidder: "Bidder4", Status: usersync.StatusBlockedByPrivacy},
{Bidder: "Bidder5", Status: usersync.StatusTypeNotSupported},
{Bidder: "Bidder6", Status: usersync.StatusBlockedByUserOptOut},
{Bidder: "Bidder7", Status: usersync.StatusBlockedByDisabledUsersync},
{Bidder: "BidderA", Status: usersync.StatusDuplicate, SyncerKey: "syncerB"},
}

Expand Down Expand Up @@ -1730,7 +1731,7 @@ func TestCookieSyncHandleResponse(t *testing.T) {
givenCookieHasSyncs: true,
givenDebug: true,
givenSyncersChosen: []usersync.SyncerChoice{},
expectedJSON: `{"status":"ok","bidder_status":[],"debug":[{"bidder":"Bidder1","error":"Already in sync"},{"bidder":"Bidder2","error":"Unsupported bidder"},{"bidder":"Bidder3","error":"No sync config"},{"bidder":"Bidder4","error":"Rejected by privacy"},{"bidder":"Bidder5","error":"Type not supported"},{"bidder":"Bidder6","error":"Status blocked by user opt out"},{"bidder":"BidderA","error":"Duplicate bidder synced as syncerB"}]}` + "\n",
expectedJSON: `{"status":"ok","bidder_status":[],"debug":[{"bidder":"Bidder1","error":"Already in sync"},{"bidder":"Bidder2","error":"Unsupported bidder"},{"bidder":"Bidder3","error":"No sync config"},{"bidder":"Bidder4","error":"Rejected by privacy"},{"bidder":"Bidder5","error":"Type not supported"},{"bidder":"Bidder6","error":"Status blocked by user opt out"},{"bidder":"Bidder7","error":"Sync disabled by config"},{"bidder":"BidderA","error":"Duplicate bidder synced as syncerB"}]}` + "\n",
expectedAnalytics: analytics.CookieSyncObject{Status: 200, BidderStatus: []*analytics.CookieSyncBidder{}},
},
}
Expand Down
7 changes: 7 additions & 0 deletions usersync/chooser.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ const (

// StatusUnconfiguredBidder refers to a bidder who hasn't been configured to have a syncer key, but is known by Prebid Server
StatusUnconfiguredBidder

// StatusBlockedByDisabledUsersync refers to a bidder who won't be synced because it's been disabled in its config by the host
StatusBlockedByDisabledUsersync
)

// Privacy determines which privacy policies will be enforced for a user sync request.
Expand Down Expand Up @@ -194,6 +197,10 @@ func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{}
return nil, BidderEvaluation{Status: StatusBlockedByPrivacy, Bidder: bidder, SyncerKey: syncer.Key()}
}

if c.bidderInfo[bidder].Syncer != nil && c.bidderInfo[bidder].Syncer.Enabled != nil && !*c.bidderInfo[bidder].Syncer.Enabled {
return nil, BidderEvaluation{Status: StatusBlockedByDisabledUsersync, Bidder: bidder, SyncerKey: syncer.Key()}
}

if privacy.GDPRInScope() && c.bidderInfo[bidder].Syncer != nil && c.bidderInfo[bidder].Syncer.SkipWhen != nil && c.bidderInfo[bidder].Syncer.SkipWhen.GDPR {
return nil, BidderEvaluation{Status: StatusBlockedByRegulationScope, Bidder: bidder, SyncerKey: syncer.Key()}
}
Expand Down
46 changes: 46 additions & 0 deletions usersync/chooser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/prebid/prebid-server/v2/config"
"github.com/prebid/prebid-server/v2/openrtb_ext"
"github.com/prebid/prebid-server/v2/util/ptrutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

Expand Down Expand Up @@ -69,6 +70,8 @@ func TestChooserChoose(t *testing.T) {

cooperativeConfig := Cooperative{Enabled: true}

usersyncDisabled := ptrutil.ToPtr(false)

testCases := []struct {
description string
givenRequest Request
Expand Down Expand Up @@ -343,6 +346,28 @@ func TestChooserChoose(t *testing.T) {
SyncersChosen: []SyncerChoice{{Bidder: "AppNexus", Syncer: fakeSyncerA}},
},
},
{
description: "Disabled Usersync",
givenRequest: Request{
Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true},
Limit: 0,
},
givenChosenBidders: []string{"a"},
givenCookie: Cookie{},
givenBidderInfo: map[string]config.BidderInfo{
"a": {
Syncer: &config.Syncer{
Enabled: usersyncDisabled,
},
},
},
bidderNamesLookup: normalizedBidderNamesLookup,
expected: Result{
Status: StatusOK,
BiddersEvaluated: []BidderEvaluation{{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByDisabledUsersync}},
SyncersChosen: []SyncerChoice{},
},
},
{
description: "Regulation Scope GDPR",
givenRequest: Request{
Expand Down Expand Up @@ -442,6 +467,8 @@ func TestChooserEvaluate(t *testing.T) {
cookieAlreadyHasSyncForA := Cookie{uids: map[string]UIDEntry{"keyA": {Expires: time.Now().Add(time.Duration(24) * time.Hour)}}}
cookieAlreadyHasSyncForB := Cookie{uids: map[string]UIDEntry{"keyB": {Expires: time.Now().Add(time.Duration(24) * time.Hour)}}}

usersyncDisabled := ptrutil.ToPtr(false)

testCases := []struct {
description string
givenBidder string
Expand Down Expand Up @@ -600,6 +627,25 @@ func TestChooserEvaluate(t *testing.T) {
expectedSyncer: nil,
expectedEvaluation: BidderEvaluation{Bidder: "unconfigured", Status: StatusUnconfiguredBidder},
},
{
description: "Disabled Usersync",
givenBidder: "a",
normalisedBidderName: "a",
givenSyncersSeen: map[string]struct{}{},
givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true},
givenCookie: cookieNeedsSync,
givenBidderInfo: map[string]config.BidderInfo{
"a": {
Syncer: &config.Syncer{
Enabled: usersyncDisabled,
},
},
},
givenSyncTypeFilter: syncTypeFilter,
normalizedBidderNamesLookup: normalizedBidderNamesLookup,
expectedSyncer: nil,
expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByDisabledUsersync},
},
{
description: "Blocked By Regulation Scope - GDPR",
givenBidder: "a",
Expand Down

0 comments on commit 8128b24

Please sign in to comment.