From ea46c83d9795a1c78ec79ac4a2f0dcfe75312e6b Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Tue, 4 Jul 2023 14:52:25 -0700 Subject: [PATCH 01/11] UserSync activity --- endpoints/cookie_sync.go | 10 +++++++- endpoints/setuid.go | 33 +++++++++++++++++++------- endpoints/setuid_test.go | 32 +++++++++++++++++++++++++ usersync/chooser.go | 29 +++++++++++++++++------ usersync/chooser_test.go | 50 +++++++++++++++++++++++++++++++--------- 5 files changed, 127 insertions(+), 27 deletions(-) diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index fa154bbcbff..7fdcb858790 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -147,6 +147,13 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr } } + activityControl, activitiesErr := privacy.NewActivityControl(account.Privacy) + if activitiesErr != nil { + if errortypes.ContainsFatalError([]error{activitiesErr}) { + return usersync.Request{}, privacy.Policies{}, err + } + } + syncTypeFilter, err := parseTypeFilter(request.FilterSettings) if err != nil { return usersync.Request{}, privacy.Policies{}, err @@ -171,7 +178,8 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr gdprPermissions: gdprPerms, ccpaParsedPolicy: ccpaParsedPolicy, }, - SyncTypeFilter: syncTypeFilter, + SyncTypeFilter: syncTypeFilter, + ActivityControl: activityControl, } return rx, privacyPolicies, nil } diff --git a/endpoints/setuid.go b/endpoints/setuid.go index a4d04749eae..4739b98788f 100644 --- a/endpoints/setuid.go +++ b/endpoints/setuid.go @@ -3,6 +3,8 @@ package endpoints import ( "context" "errors" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/privacy" "net/http" "net/url" "strconv" @@ -56,7 +58,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use query := r.URL.Query() - syncer, err := getSyncer(query, syncersByKey) + syncer, bidderName, err := getSyncer(query, syncersByKey) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) @@ -101,6 +103,21 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use return } + activities, activitiesErr := privacy.NewActivityControl(account.Privacy) + if activitiesErr != nil { + if errortypes.ContainsFatalError([]error{activitiesErr}) { + w.WriteHeader(http.StatusBadRequest) + return + } + } + + userSyncActivityAllowed := activities.Allow(privacy.ActivitySyncUser, + privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidderName}) + if userSyncActivityAllowed == privacy.ActivityDeny { + w.WriteHeader(http.StatusUnavailableForLegalReasons) + return + } + tcf2Cfg := tcf2CfgBuilder(cfg.GDPR.TCF2, account.GDPR) if shouldReturn, status, body := preventSyncsGDPR(query.Get("gdpr"), query.Get("gdpr_consent"), gdprPermsBuilder, tcf2Cfg); shouldReturn { @@ -148,19 +165,19 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use }) } -func getSyncer(query url.Values, syncersByKey map[string]usersync.Syncer) (usersync.Syncer, error) { - key := query.Get("bidder") +func getSyncer(query url.Values, syncersByKey map[string]usersync.Syncer) (usersync.Syncer, string, error) { + bidderName := query.Get("bidder") - if key == "" { - return nil, errors.New(`"bidder" query param is required`) + if bidderName == "" { + return nil, "", errors.New(`"bidder" query param is required`) } - syncer, syncerExists := syncersByKey[key] + syncer, syncerExists := syncersByKey[bidderName] if !syncerExists { - return nil, errors.New("The bidder name provided is not supported by Prebid Server") + return nil, "", errors.New("The bidder name provided is not supported by Prebid Server") } - return syncer, nil + return syncer, bidderName, nil } // getResponseFormat reads the format query parameter or falls back to the syncer's default. diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go index 609d85395fd..101b9c312f6 100644 --- a/endpoints/setuid_test.go +++ b/endpoints/setuid_test.go @@ -254,6 +254,34 @@ func TestSetUIDEndpoint(t *testing.T) { expectedBody: "account is disabled, please reach out to the prebid server host", description: "Set uid for valid bidder with valid disabled account provided", }, + { + uri: "/setuid?bidder=pubmatic&uid=123&account=valid_acct_with_valid_activities_usersync_enabled", + syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"}, + existingSyncs: nil, + gdprAllowsHostCookies: true, + expectedSyncs: map[string]string{"pubmatic": "123"}, + expectedStatusCode: http.StatusOK, + expectedHeaders: map[string]string{"Content-Type": "text/html", "Content-Length": "0"}, + description: "Set uid for valid bidder with valid account provided with user sync allowed activity", + }, + { + uri: "/setuid?bidder=pubmatic&uid=123&account=valid_acct_with_valid_activities_usersync_disabled", + syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"}, + existingSyncs: nil, + gdprAllowsHostCookies: true, + expectedSyncs: nil, + expectedStatusCode: http.StatusUnavailableForLegalReasons, + description: "Set uid for valid bidder with valid account provided with user sync disallowed activity", + }, + { + uri: "/setuid?bidder=pubmatic&uid=123&account=valid_acct_with_invalid_activities", + syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"}, + existingSyncs: nil, + gdprAllowsHostCookies: true, + expectedSyncs: nil, + expectedStatusCode: http.StatusBadRequest, + description: "Set uid for valid bidder with valid account provided with invalid user sync activity", + }, } analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{}) @@ -740,6 +768,10 @@ func doRequest(req *http.Request, analytics analytics.PBSAnalyticsModule, metric "disabled_acct": json.RawMessage(`{"disabled":true}`), "malformed_acct": json.RawMessage(`{"disabled":"malformed"}`), "invalid_json_acct": json.RawMessage(`{"}`), + + "valid_acct_with_valid_activities_usersync_enabled": json.RawMessage(`{"privacy":{"allowactivities":{"syncUser":{"default": true}}}}`), + "valid_acct_with_valid_activities_usersync_disabled": json.RawMessage(`{"privacy":{"allowactivities":{"syncUser":{"default": false}}}}`), + "valid_acct_with_invalid_activities": json.RawMessage(`{"privacy":{"allowactivities":{"syncUser":{"rules":[{"condition":{"componentName": ["bidderA.bidderB.bidderC"]}}]}}}}`), }} endpoint := NewSetUIDEndpoint(&cfg, syncersByBidder, gdprPermsBuilder, tcf2ConfigBuilder, analytics, fakeAccountsFetcher, metrics) diff --git a/usersync/chooser.go b/usersync/chooser.go index 97fa1471b7e..1d3e310d6a1 100644 --- a/usersync/chooser.go +++ b/usersync/chooser.go @@ -1,5 +1,9 @@ package usersync +import ( + privacyActivity "github.com/prebid/prebid-server/privacy" +) + // Chooser determines which syncers are eligible for a given request. type Chooser interface { // Choose considers bidders to sync, filters the bidders, and returns the result of the @@ -23,11 +27,12 @@ func NewChooser(bidderSyncerLookup map[string]Syncer) Chooser { // Request specifies a user sync request. type Request struct { - Bidders []string - Cooperative Cooperative - Limit int - Privacy Privacy - SyncTypeFilter SyncTypeFilter + Bidders []string + Cooperative Cooperative + Limit int + Privacy Privacy + SyncTypeFilter SyncTypeFilter + ActivityControl privacyActivity.ActivityControl } // Cooperative specifies the settings for cooperative syncing for a given request, where bidders @@ -85,6 +90,9 @@ const ( // StatusDuplicate specifies the bidder is a duplicate or shared a syncer key with another bidder choice. StatusDuplicate + + // StatusBlockedByPrivacy specifies a bidder sync url is not allowed by privacy activities + StatusBlockedByPrivacy ) // Privacy determines which privacy policies will be enforced for a user sync request. @@ -120,7 +128,7 @@ func (c standardChooser) Choose(request Request, cookie *Cookie) Result { bidders := c.bidderChooser.choose(request.Bidders, c.biddersAvailable, request.Cooperative) for i := 0; i < len(bidders) && (limitDisabled || len(syncersChosen) < request.Limit); i++ { - syncer, evaluation := c.evaluate(bidders[i], syncersSeen, request.SyncTypeFilter, request.Privacy, cookie) + syncer, evaluation := c.evaluate(bidders[i], syncersSeen, request.SyncTypeFilter, request.Privacy, cookie, request.ActivityControl) biddersEvaluated = append(biddersEvaluated, evaluation) if evaluation.Status == StatusOK { @@ -131,7 +139,7 @@ func (c standardChooser) Choose(request Request, cookie *Cookie) Result { return Result{Status: StatusOK, BiddersEvaluated: biddersEvaluated, SyncersChosen: syncersChosen} } -func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{}, syncTypeFilter SyncTypeFilter, privacy Privacy, cookie *Cookie) (Syncer, BidderEvaluation) { +func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{}, syncTypeFilter SyncTypeFilter, privacy Privacy, cookie *Cookie, activityControl privacyActivity.ActivityControl) (Syncer, BidderEvaluation) { syncer, exists := c.bidderSyncerLookup[bidder] if !exists { return nil, BidderEvaluation{Status: StatusUnknownBidder, Bidder: bidder} @@ -151,6 +159,13 @@ func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{} return nil, BidderEvaluation{Status: StatusAlreadySynced, Bidder: bidder, SyncerKey: syncer.Key()} } + userSyncActivityAllowed := activityControl.Allow(privacyActivity.ActivitySyncUser, + privacyActivity.ScopedName{Scope: privacyActivity.ScopeTypeBidder, Name: bidder}) + if userSyncActivityAllowed == privacyActivity.ActivityDeny { + return nil, BidderEvaluation{Status: StatusBlockedByPrivacy, Bidder: bidder, SyncerKey: syncer.Key()} + // from requirements: Debug message can be general "Bidder sync blocked for privacy reasons" - not done + } + if !privacy.GDPRAllowsBidderSync(bidder) { return nil, BidderEvaluation{Status: StatusBlockedByGDPR, Bidder: bidder, SyncerKey: syncer.Key()} } diff --git a/usersync/chooser_test.go b/usersync/chooser_test.go index 3b820b99f24..521041fb366 100644 --- a/usersync/chooser_test.go +++ b/usersync/chooser_test.go @@ -1,12 +1,13 @@ package usersync import ( - "testing" - "time" - + "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/util/ptrutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "testing" + "time" ) func TestNewChooser(t *testing.T) { @@ -241,14 +242,31 @@ func TestChooserEvaluate(t *testing.T) { cookieAlreadyHasSyncForA := Cookie{uids: map[string]uidWithExpiry{"keyA": {Expires: time.Now().Add(time.Duration(24) * time.Hour)}}} cookieAlreadyHasSyncForB := Cookie{uids: map[string]uidWithExpiry{"keyB": {Expires: time.Now().Add(time.Duration(24) * time.Hour)}}} + activityControl, activitiesErr := privacy.NewActivityControl(&config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + SyncUser: config.Activity{ + Default: ptrutil.ToPtr(true), + Rules: []config.ActivityRule{ + { + Allow: false, + Condition: config.ActivityCondition{ + ComponentName: []string{"bidder.a"}, + }, + }, + }, + }}, + }) + assert.NoError(t, activitiesErr) + testCases := []struct { - description string - givenBidder string - givenSyncersSeen map[string]struct{} - givenPrivacy Privacy - givenCookie Cookie - expectedSyncer Syncer - expectedEvaluation BidderEvaluation + description string + givenBidder string + givenSyncersSeen map[string]struct{} + givenPrivacy Privacy + givenCookie Cookie + givenActivityControl privacy.ActivityControl + expectedSyncer Syncer + expectedEvaluation BidderEvaluation }{ { description: "Valid", @@ -322,11 +340,21 @@ func TestChooserEvaluate(t *testing.T) { expectedSyncer: nil, expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByCCPA}, }, + { + description: "Blocked By activity control", + givenBidder: "a", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + givenCookie: cookieNeedsSync, + givenActivityControl: activityControl, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByPrivacy}, + }, } for _, test := range testCases { chooser, _ := NewChooser(bidderSyncerLookup).(standardChooser) - sync, evaluation := chooser.evaluate(test.givenBidder, test.givenSyncersSeen, syncTypeFilter, test.givenPrivacy, &test.givenCookie) + sync, evaluation := chooser.evaluate(test.givenBidder, test.givenSyncersSeen, syncTypeFilter, test.givenPrivacy, &test.givenCookie, test.givenActivityControl) assert.Equal(t, test.expectedSyncer, sync, test.description+":syncer") assert.Equal(t, test.expectedEvaluation, evaluation, test.description+":evaluation") From 5c9cff1468d68bb5aa8d6ebed5d2263dfa971874 Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Thu, 6 Jul 2023 15:36:24 -0700 Subject: [PATCH 02/11] Removed comment and removed statement to disable actions. --- privacy/enforcer.go | 4 ---- usersync/chooser.go | 1 - 2 files changed, 5 deletions(-) diff --git a/privacy/enforcer.go b/privacy/enforcer.go index d63cd8de31f..a8685e29276 100644 --- a/privacy/enforcer.go +++ b/privacy/enforcer.go @@ -3,7 +3,6 @@ package privacy import ( "fmt" "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" "strings" ) @@ -33,9 +32,6 @@ func NewActivityControl(privacyConf *config.AccountPrivacy) (ActivityControl, er if privacyConf == nil { return ac, err - } else { - //temporarily disable Activities if they are specified at the account level - return ac, &errortypes.Warning{Message: "account.Privacy has no effect as the feature is under development."} } plans := make(map[Activity]ActivityPlan) diff --git a/usersync/chooser.go b/usersync/chooser.go index 1d3e310d6a1..8b60eaf50a7 100644 --- a/usersync/chooser.go +++ b/usersync/chooser.go @@ -163,7 +163,6 @@ func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{} privacyActivity.ScopedName{Scope: privacyActivity.ScopeTypeBidder, Name: bidder}) if userSyncActivityAllowed == privacyActivity.ActivityDeny { return nil, BidderEvaluation{Status: StatusBlockedByPrivacy, Bidder: bidder, SyncerKey: syncer.Key()} - // from requirements: Debug message can be general "Bidder sync blocked for privacy reasons" - not done } if !privacy.GDPRAllowsBidderSync(bidder) { From 75b7e8b23b3cf612628700e376127475d3fa4021 Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Thu, 6 Jul 2023 15:39:50 -0700 Subject: [PATCH 03/11] Uncomment disabled test --- privacy/enforcer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/privacy/enforcer_test.go b/privacy/enforcer_test.go index e87a9eb2bff..2b9715f87f8 100644 --- a/privacy/enforcer_test.go +++ b/privacy/enforcer_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -func TemporarilyDisabledTestNewActivityControl(t *testing.T) { +func TestNewActivityControl(t *testing.T) { testCases := []struct { name string From b7d1ab524543d3573bd31801cdbe372e1b66063e Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Thu, 6 Jul 2023 19:21:43 -0700 Subject: [PATCH 04/11] Moved ActivityControl inside the privacy interface and object --- endpoints/cookie_sync.go | 11 ++++++-- usersync/chooser.go | 19 ++++++------- usersync/chooser_test.go | 61 +++++++++++++++------------------------- 3 files changed, 41 insertions(+), 50 deletions(-) diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index 7fdcb858790..9850e22d4e9 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -177,9 +177,9 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr Privacy: usersyncPrivacy{ gdprPermissions: gdprPerms, ccpaParsedPolicy: ccpaParsedPolicy, + activityControl: activityControl, }, - SyncTypeFilter: syncTypeFilter, - ActivityControl: activityControl, + SyncTypeFilter: syncTypeFilter, } return rx, privacyPolicies, nil } @@ -507,6 +507,7 @@ type usersyncPrivacyConfig struct { type usersyncPrivacy struct { gdprPermissions gdpr.Permissions ccpaParsedPolicy ccpa.ParsedPolicy + activityControl privacy.ActivityControl } func (p usersyncPrivacy) GDPRAllowsHostCookie() bool { @@ -523,3 +524,9 @@ func (p usersyncPrivacy) CCPAAllowsBidderSync(bidder string) bool { enforce := p.ccpaParsedPolicy.CanEnforce() && p.ccpaParsedPolicy.ShouldEnforce(bidder) return !enforce } + +func (p usersyncPrivacy) ActivityAllowsUserSync(bidder string) privacy.ActivityResult { + activityResult := p.activityControl.Allow(privacy.ActivitySyncUser, + privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidder}) + return activityResult +} diff --git a/usersync/chooser.go b/usersync/chooser.go index 8b60eaf50a7..adbf0eaa24c 100644 --- a/usersync/chooser.go +++ b/usersync/chooser.go @@ -27,12 +27,11 @@ func NewChooser(bidderSyncerLookup map[string]Syncer) Chooser { // Request specifies a user sync request. type Request struct { - Bidders []string - Cooperative Cooperative - Limit int - Privacy Privacy - SyncTypeFilter SyncTypeFilter - ActivityControl privacyActivity.ActivityControl + Bidders []string + Cooperative Cooperative + Limit int + Privacy Privacy + SyncTypeFilter SyncTypeFilter } // Cooperative specifies the settings for cooperative syncing for a given request, where bidders @@ -100,6 +99,7 @@ type Privacy interface { GDPRAllowsHostCookie() bool GDPRAllowsBidderSync(bidder string) bool CCPAAllowsBidderSync(bidder string) bool + ActivityAllowsUserSync(bidder string) privacyActivity.ActivityResult } // standardChooser implements the user syncer algorithm per official Prebid specification. @@ -128,7 +128,7 @@ func (c standardChooser) Choose(request Request, cookie *Cookie) Result { bidders := c.bidderChooser.choose(request.Bidders, c.biddersAvailable, request.Cooperative) for i := 0; i < len(bidders) && (limitDisabled || len(syncersChosen) < request.Limit); i++ { - syncer, evaluation := c.evaluate(bidders[i], syncersSeen, request.SyncTypeFilter, request.Privacy, cookie, request.ActivityControl) + syncer, evaluation := c.evaluate(bidders[i], syncersSeen, request.SyncTypeFilter, request.Privacy, cookie) biddersEvaluated = append(biddersEvaluated, evaluation) if evaluation.Status == StatusOK { @@ -139,7 +139,7 @@ func (c standardChooser) Choose(request Request, cookie *Cookie) Result { return Result{Status: StatusOK, BiddersEvaluated: biddersEvaluated, SyncersChosen: syncersChosen} } -func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{}, syncTypeFilter SyncTypeFilter, privacy Privacy, cookie *Cookie, activityControl privacyActivity.ActivityControl) (Syncer, BidderEvaluation) { +func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{}, syncTypeFilter SyncTypeFilter, privacy Privacy, cookie *Cookie) (Syncer, BidderEvaluation) { syncer, exists := c.bidderSyncerLookup[bidder] if !exists { return nil, BidderEvaluation{Status: StatusUnknownBidder, Bidder: bidder} @@ -159,8 +159,7 @@ func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{} return nil, BidderEvaluation{Status: StatusAlreadySynced, Bidder: bidder, SyncerKey: syncer.Key()} } - userSyncActivityAllowed := activityControl.Allow(privacyActivity.ActivitySyncUser, - privacyActivity.ScopedName{Scope: privacyActivity.ScopeTypeBidder, Name: bidder}) + userSyncActivityAllowed := privacy.ActivityAllowsUserSync(bidder) if userSyncActivityAllowed == privacyActivity.ActivityDeny { return nil, BidderEvaluation{Status: StatusBlockedByPrivacy, Bidder: bidder, SyncerKey: syncer.Key()} } diff --git a/usersync/chooser_test.go b/usersync/chooser_test.go index 521041fb366..250b567fb74 100644 --- a/usersync/chooser_test.go +++ b/usersync/chooser_test.go @@ -1,9 +1,7 @@ package usersync import ( - "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/util/ptrutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "testing" @@ -242,31 +240,14 @@ func TestChooserEvaluate(t *testing.T) { cookieAlreadyHasSyncForA := Cookie{uids: map[string]uidWithExpiry{"keyA": {Expires: time.Now().Add(time.Duration(24) * time.Hour)}}} cookieAlreadyHasSyncForB := Cookie{uids: map[string]uidWithExpiry{"keyB": {Expires: time.Now().Add(time.Duration(24) * time.Hour)}}} - activityControl, activitiesErr := privacy.NewActivityControl(&config.AccountPrivacy{ - AllowActivities: config.AllowActivities{ - SyncUser: config.Activity{ - Default: ptrutil.ToPtr(true), - Rules: []config.ActivityRule{ - { - Allow: false, - Condition: config.ActivityCondition{ - ComponentName: []string{"bidder.a"}, - }, - }, - }, - }}, - }) - assert.NoError(t, activitiesErr) - testCases := []struct { - description string - givenBidder string - givenSyncersSeen map[string]struct{} - givenPrivacy Privacy - givenCookie Cookie - givenActivityControl privacy.ActivityControl - expectedSyncer Syncer - expectedEvaluation BidderEvaluation + description string + givenBidder string + givenSyncersSeen map[string]struct{} + givenPrivacy Privacy + givenCookie Cookie + expectedSyncer Syncer + expectedEvaluation BidderEvaluation }{ { description: "Valid", @@ -341,20 +322,19 @@ func TestChooserEvaluate(t *testing.T) { expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByCCPA}, }, { - description: "Blocked By activity control", - givenBidder: "a", - givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, - givenCookie: cookieNeedsSync, - givenActivityControl: activityControl, - expectedSyncer: nil, - expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByPrivacy}, + description: "Blocked By activity control", + givenBidder: "a", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowIUserSync: privacy.ActivityDeny}, + givenCookie: cookieNeedsSync, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByPrivacy}, }, } for _, test := range testCases { chooser, _ := NewChooser(bidderSyncerLookup).(standardChooser) - sync, evaluation := chooser.evaluate(test.givenBidder, test.givenSyncersSeen, syncTypeFilter, test.givenPrivacy, &test.givenCookie, test.givenActivityControl) + sync, evaluation := chooser.evaluate(test.givenBidder, test.givenSyncersSeen, syncTypeFilter, test.givenPrivacy, &test.givenCookie) assert.Equal(t, test.expectedSyncer, sync, test.description+":syncer") assert.Equal(t, test.expectedEvaluation, evaluation, test.description+":evaluation") @@ -401,9 +381,10 @@ func (fakeSyncer) GetSync(syncTypes []SyncType, privacyPolicies privacy.Policies } type fakePrivacy struct { - gdprAllowsHostCookie bool - gdprAllowsBidderSync bool - ccpaAllowsBidderSync bool + gdprAllowsHostCookie bool + gdprAllowsBidderSync bool + ccpaAllowsBidderSync bool + activityAllowIUserSync privacy.ActivityResult } func (p fakePrivacy) GDPRAllowsHostCookie() bool { @@ -417,3 +398,7 @@ func (p fakePrivacy) GDPRAllowsBidderSync(bidder string) bool { func (p fakePrivacy) CCPAAllowsBidderSync(bidder string) bool { return p.ccpaAllowsBidderSync } + +func (p fakePrivacy) ActivityAllowsUserSync(bidder string) privacy.ActivityResult { + return p.activityAllowIUserSync +} From 5a388eb904184a0be7a99fe66dafd6665c2df816 Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Mon, 10 Jul 2023 22:43:18 -0700 Subject: [PATCH 05/11] Added unit tests --- endpoints/cookie_sync_test.go | 91 ++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 37acf0c2add..6223e538226 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -498,6 +498,7 @@ func TestCookieSyncParseRequest(t *testing.T) { expectedPrivacy privacy.Policies expectedRequest usersync.Request }{ + { description: "Complete Request - includes GPP string with EU TCF V2", givenBody: strings.NewReader(`{` + @@ -972,6 +973,36 @@ func TestCookieSyncParseRequest(t *testing.T) { expectedError: errCookieSyncAccountBlocked.Error(), givenAccountRequired: true, }, + + { + description: "Account Defaults - Invalid activities Activities", + givenBody: strings.NewReader(`{` + + `"bidders":["a", "b"],` + + `"account":"ValidAccountInvalidActivities"` + + `}`), + givenGDPRConfig: config.GDPR{Enabled: true, DefaultValue: "0"}, + givenCCPAEnabled: true, + givenConfig: config.UserSync{ + Cooperative: config.UserSyncCooperative{ + EnabledByDefault: false, + PriorityGroups: [][]string{{"a", "b", "c"}}, + }, + }, + expectedPrivacy: privacy.Policies{}, + expectedRequest: usersync.Request{ + Bidders: []string(nil), + Cooperative: usersync.Cooperative{ + Enabled: false, + PriorityGroups: [][]string(nil), + }, + Limit: 0, + Privacy: nil, + SyncTypeFilter: usersync.SyncTypeFilter{ + IFrame: nil, + Redirect: nil, + }, + }, + }, } for _, test := range testCases { @@ -996,8 +1027,9 @@ func TestCookieSyncParseRequest(t *testing.T) { ccpaEnforce: test.givenCCPAEnabled, }, accountsFetcher: FakeAccountsFetcher{AccountData: map[string]json.RawMessage{ - "TestAccount": json.RawMessage(`{"cookie_sync": {"default_limit": 20, "max_limit": 30, "default_coop_sync": true}}`), - "DisabledAccount": json.RawMessage(`{"disabled":true}`), + "TestAccount": json.RawMessage(`{"cookie_sync": {"default_limit": 20, "max_limit": 30, "default_coop_sync": true}}`), + "DisabledAccount": json.RawMessage(`{"disabled":true}`), + "ValidAccountInvalidActivities": json.RawMessage(`{"privacy":{"allowactivities":{"syncUser":{"rules":[{"condition":{"componentName": ["bidderA.bidderB.bidderC"]}}]}}}}`), }}, } assert.NoError(t, endpoint.config.MarshalAccountDefaults()) @@ -1870,6 +1902,42 @@ func TestUsersyncPrivacyCCPAAllowsBidderSync(t *testing.T) { } } +func TestActivityDefaultToDefaultResult(t *testing.T) { + + testCases := []struct { + name string + bidderName string + allow bool + expectedResult privacy.ActivityResult + }{ + { + name: "activity_is_allowed", + bidderName: "bidderA", + allow: true, + expectedResult: privacy.ActivityAllow, + }, + { + name: "activity_is_denied", + bidderName: "bidderA", + allow: false, + expectedResult: privacy.ActivityDeny, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + privacyConfig := getDefaultActivityConfig(test.bidderName, test.allow) + activities, err := privacy.NewActivityControl(privacyConfig) + assert.NoError(t, err) + up := usersyncPrivacy{ + activityControl: activities, + } + actualResult := up.ActivityAllowsUserSync(test.bidderName) + assert.Equal(t, test.expectedResult, actualResult) + }) + } +} + func TestCombineErrors(t *testing.T) { testCases := []struct { description string @@ -2030,3 +2098,22 @@ func (p *fakePermissions) AuctionActivitiesAllowed(ctx context.Context, bidderCo AllowBidRequest: true, }, nil } + +func getDefaultActivityConfig(componentName string, allow bool) *config.AccountPrivacy { + return &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + SyncUser: config.Activity{ + Default: ptrutil.ToPtr(true), + Rules: []config.ActivityRule{ + { + Allow: allow, + Condition: config.ActivityCondition{ + ComponentName: []string{componentName}, + ComponentType: []string{"bidder"}, + }, + }, + }, + }, + }, + } +} From 2bdc95e099f74e6f1fa546165b70f5be4a09f4a4 Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Tue, 11 Jul 2023 22:43:55 -0700 Subject: [PATCH 06/11] Minor cleanup --- endpoints/cookie_sync_test.go | 1 - usersync/chooser_test.go | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 6223e538226..97386ba58b5 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -1903,7 +1903,6 @@ func TestUsersyncPrivacyCCPAAllowsBidderSync(t *testing.T) { } func TestActivityDefaultToDefaultResult(t *testing.T) { - testCases := []struct { name string bidderName string diff --git a/usersync/chooser_test.go b/usersync/chooser_test.go index 250b567fb74..26e2f98d113 100644 --- a/usersync/chooser_test.go +++ b/usersync/chooser_test.go @@ -325,7 +325,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Blocked By activity control", givenBidder: "a", givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowIUserSync: privacy.ActivityDeny}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: privacy.ActivityDeny}, givenCookie: cookieNeedsSync, expectedSyncer: nil, expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByPrivacy}, @@ -381,10 +381,10 @@ func (fakeSyncer) GetSync(syncTypes []SyncType, privacyPolicies privacy.Policies } type fakePrivacy struct { - gdprAllowsHostCookie bool - gdprAllowsBidderSync bool - ccpaAllowsBidderSync bool - activityAllowIUserSync privacy.ActivityResult + gdprAllowsHostCookie bool + gdprAllowsBidderSync bool + ccpaAllowsBidderSync bool + activityAllowUserSync privacy.ActivityResult } func (p fakePrivacy) GDPRAllowsHostCookie() bool { @@ -400,5 +400,5 @@ func (p fakePrivacy) CCPAAllowsBidderSync(bidder string) bool { } func (p fakePrivacy) ActivityAllowsUserSync(bidder string) privacy.ActivityResult { - return p.activityAllowIUserSync + return p.activityAllowUserSync } From 2cdc04842418291afe4f91f990acca3fcdb80eba Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Thu, 13 Jul 2023 14:21:56 -0700 Subject: [PATCH 07/11] CR refactoring --- endpoints/cookie_sync.go | 6 ++--- endpoints/cookie_sync_test.go | 24 +++++++++++--------- endpoints/setuid.go | 3 +-- endpoints/setuid_test.go | 5 +++-- privacy/enforcer.go | 2 +- usersync/chooser.go | 8 ++----- usersync/chooser_test.go | 42 +++++++++++++++++------------------ 7 files changed, 44 insertions(+), 46 deletions(-) diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index 9850e22d4e9..4f3c7fea0dc 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -150,7 +150,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr activityControl, activitiesErr := privacy.NewActivityControl(account.Privacy) if activitiesErr != nil { if errortypes.ContainsFatalError([]error{activitiesErr}) { - return usersync.Request{}, privacy.Policies{}, err + activityControl = privacy.ActivityControl{} } } @@ -525,8 +525,8 @@ func (p usersyncPrivacy) CCPAAllowsBidderSync(bidder string) bool { return !enforce } -func (p usersyncPrivacy) ActivityAllowsUserSync(bidder string) privacy.ActivityResult { +func (p usersyncPrivacy) ActivityAllowsUserSync(bidder string) bool { activityResult := p.activityControl.Allow(privacy.ActivitySyncUser, privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidder}) - return activityResult + return activityResult == privacy.ActivityAllow } diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 97386ba58b5..00ecb615ddf 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -975,7 +975,7 @@ func TestCookieSyncParseRequest(t *testing.T) { }, { - description: "Account Defaults - Invalid activities Activities", + description: "Account Defaults - Invalid Activities", givenBody: strings.NewReader(`{` + `"bidders":["a", "b"],` + `"account":"ValidAccountInvalidActivities"` + @@ -990,16 +990,18 @@ func TestCookieSyncParseRequest(t *testing.T) { }, expectedPrivacy: privacy.Policies{}, expectedRequest: usersync.Request{ - Bidders: []string(nil), + Bidders: []string{"a", "b"}, Cooperative: usersync.Cooperative{ Enabled: false, - PriorityGroups: [][]string(nil), + PriorityGroups: [][]string{{"a", "b", "c"}}, + }, + Limit: 0, + Privacy: usersyncPrivacy{ + gdprPermissions: &fakePermissions{}, }, - Limit: 0, - Privacy: nil, SyncTypeFilter: usersync.SyncTypeFilter{ - IFrame: nil, - Redirect: nil, + IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), + Redirect: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), }, }, }, @@ -1902,24 +1904,24 @@ func TestUsersyncPrivacyCCPAAllowsBidderSync(t *testing.T) { } } -func TestActivityDefaultToDefaultResult(t *testing.T) { +func TestCookieSyncActivityControlIntegration(t *testing.T) { testCases := []struct { name string bidderName string allow bool - expectedResult privacy.ActivityResult + expectedResult bool }{ { name: "activity_is_allowed", bidderName: "bidderA", allow: true, - expectedResult: privacy.ActivityAllow, + expectedResult: true, }, { name: "activity_is_denied", bidderName: "bidderA", allow: false, - expectedResult: privacy.ActivityDeny, + expectedResult: false, }, } diff --git a/endpoints/setuid.go b/endpoints/setuid.go index 4739b98788f..b12dbcf31ff 100644 --- a/endpoints/setuid.go +++ b/endpoints/setuid.go @@ -106,8 +106,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use activities, activitiesErr := privacy.NewActivityControl(account.Privacy) if activitiesErr != nil { if errortypes.ContainsFatalError([]error{activitiesErr}) { - w.WriteHeader(http.StatusBadRequest) - return + activities = privacy.ActivityControl{} } } diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go index 101b9c312f6..5edf38bd2e8 100644 --- a/endpoints/setuid_test.go +++ b/endpoints/setuid_test.go @@ -278,8 +278,9 @@ func TestSetUIDEndpoint(t *testing.T) { syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"}, existingSyncs: nil, gdprAllowsHostCookies: true, - expectedSyncs: nil, - expectedStatusCode: http.StatusBadRequest, + expectedSyncs: map[string]string{"pubmatic": "123"}, + expectedStatusCode: http.StatusOK, + expectedHeaders: map[string]string{"Content-Type": "text/html", "Content-Length": "0"}, description: "Set uid for valid bidder with valid account provided with invalid user sync activity", }, } diff --git a/privacy/enforcer.go b/privacy/enforcer.go index a8685e29276..bcd9e948377 100644 --- a/privacy/enforcer.go +++ b/privacy/enforcer.go @@ -31,7 +31,7 @@ func NewActivityControl(privacyConf *config.AccountPrivacy) (ActivityControl, er var err error if privacyConf == nil { - return ac, err + return ac, nil } plans := make(map[Activity]ActivityPlan) diff --git a/usersync/chooser.go b/usersync/chooser.go index adbf0eaa24c..3f478049066 100644 --- a/usersync/chooser.go +++ b/usersync/chooser.go @@ -1,9 +1,5 @@ package usersync -import ( - privacyActivity "github.com/prebid/prebid-server/privacy" -) - // Chooser determines which syncers are eligible for a given request. type Chooser interface { // Choose considers bidders to sync, filters the bidders, and returns the result of the @@ -99,7 +95,7 @@ type Privacy interface { GDPRAllowsHostCookie() bool GDPRAllowsBidderSync(bidder string) bool CCPAAllowsBidderSync(bidder string) bool - ActivityAllowsUserSync(bidder string) privacyActivity.ActivityResult + ActivityAllowsUserSync(bidder string) bool } // standardChooser implements the user syncer algorithm per official Prebid specification. @@ -160,7 +156,7 @@ func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{} } userSyncActivityAllowed := privacy.ActivityAllowsUserSync(bidder) - if userSyncActivityAllowed == privacyActivity.ActivityDeny { + if !userSyncActivityAllowed { return nil, BidderEvaluation{Status: StatusBlockedByPrivacy, Bidder: bidder, SyncerKey: syncer.Key()} } diff --git a/usersync/chooser_test.go b/usersync/chooser_test.go index 26e2f98d113..6883840d041 100644 --- a/usersync/chooser_test.go +++ b/usersync/chooser_test.go @@ -65,7 +65,7 @@ func TestChooserChoose(t *testing.T) { { description: "Cookie Opt Out", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a"}, @@ -79,7 +79,7 @@ func TestChooserChoose(t *testing.T) { { description: "GDPR Host Cookie Not Allowed", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: false, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: false, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a"}, @@ -93,7 +93,7 @@ func TestChooserChoose(t *testing.T) { { description: "No Bidders", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{}, @@ -107,7 +107,7 @@ func TestChooserChoose(t *testing.T) { { description: "One Bidder - Sync", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a"}, @@ -121,7 +121,7 @@ func TestChooserChoose(t *testing.T) { { description: "One Bidder - No Sync", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"c"}, @@ -135,7 +135,7 @@ func TestChooserChoose(t *testing.T) { { description: "Many Bidders - All Sync - Limit Disabled With 0", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a", "b"}, @@ -149,7 +149,7 @@ func TestChooserChoose(t *testing.T) { { description: "Many Bidders - All Sync - Limit Disabled With Negative Value", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: -1, }, givenChosenBidders: []string{"a", "b"}, @@ -163,7 +163,7 @@ func TestChooserChoose(t *testing.T) { { description: "Many Bidders - Limited Sync", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 1, }, givenChosenBidders: []string{"a", "b"}, @@ -177,7 +177,7 @@ func TestChooserChoose(t *testing.T) { { description: "Many Bidders - Limited Sync - Disqualified Syncers Don't Count Towards Limit", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 1, }, givenChosenBidders: []string{"c", "a", "b"}, @@ -191,7 +191,7 @@ func TestChooserChoose(t *testing.T) { { description: "Many Bidders - Some Sync, Some Don't", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a", "c"}, @@ -253,7 +253,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Valid", givenBidder: "a", givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, givenCookie: cookieNeedsSync, expectedSyncer: fakeSyncerA, expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}, @@ -262,7 +262,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Unknown Bidder", givenBidder: "unknown", givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, givenCookie: cookieNeedsSync, expectedSyncer: nil, expectedEvaluation: BidderEvaluation{Bidder: "unknown", Status: StatusUnknownBidder}, @@ -271,7 +271,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Duplicate Syncer", givenBidder: "a", givenSyncersSeen: map[string]struct{}{"keyA": {}}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, givenCookie: cookieNeedsSync, expectedSyncer: nil, expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusDuplicate}, @@ -280,7 +280,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Incompatible Kind", givenBidder: "b", givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, givenCookie: cookieNeedsSync, expectedSyncer: nil, expectedEvaluation: BidderEvaluation{Bidder: "b", SyncerKey: "keyB", Status: StatusTypeNotSupported}, @@ -289,7 +289,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Already Synced", givenBidder: "a", givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, givenCookie: cookieAlreadyHasSyncForA, expectedSyncer: nil, expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusAlreadySynced}, @@ -298,7 +298,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Different Bidder Already Synced", givenBidder: "a", givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, givenCookie: cookieAlreadyHasSyncForB, expectedSyncer: fakeSyncerA, expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}, @@ -307,7 +307,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Blocked By GDPR", givenBidder: "a", givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: false, ccpaAllowsBidderSync: true}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: false, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, givenCookie: cookieNeedsSync, expectedSyncer: nil, expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByGDPR}, @@ -316,7 +316,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Blocked By CCPA", givenBidder: "a", givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: false}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: false, activityAllowUserSync: true}, givenCookie: cookieNeedsSync, expectedSyncer: nil, expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByCCPA}, @@ -325,7 +325,7 @@ func TestChooserEvaluate(t *testing.T) { description: "Blocked By activity control", givenBidder: "a", givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: privacy.ActivityDeny}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: false}, givenCookie: cookieNeedsSync, expectedSyncer: nil, expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByPrivacy}, @@ -384,7 +384,7 @@ type fakePrivacy struct { gdprAllowsHostCookie bool gdprAllowsBidderSync bool ccpaAllowsBidderSync bool - activityAllowUserSync privacy.ActivityResult + activityAllowUserSync bool } func (p fakePrivacy) GDPRAllowsHostCookie() bool { @@ -399,6 +399,6 @@ func (p fakePrivacy) CCPAAllowsBidderSync(bidder string) bool { return p.ccpaAllowsBidderSync } -func (p fakePrivacy) ActivityAllowsUserSync(bidder string) privacy.ActivityResult { +func (p fakePrivacy) ActivityAllowsUserSync(bidder string) bool { return p.activityAllowUserSync } From f33bd5a9b288d7a34ccfa627e3f9af1d186bb704 Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Tue, 18 Jul 2023 14:27:02 -0700 Subject: [PATCH 08/11] Merge conflicts --- endpoints/setuid.go | 1 + 1 file changed, 1 insertion(+) diff --git a/endpoints/setuid.go b/endpoints/setuid.go index 925fad3163e..fac35f6fe63 100644 --- a/endpoints/setuid.go +++ b/endpoints/setuid.go @@ -18,6 +18,7 @@ import ( "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/gdpr" "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/privacy" gppPrivacy "github.com/prebid/prebid-server/privacy/gpp" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/usersync" From 384c00b91b378f306de5273910ab92f69e588c6f Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Wed, 26 Jul 2023 11:55:33 -0700 Subject: [PATCH 09/11] Merge conflicts --- endpoints/setuid.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/endpoints/setuid.go b/endpoints/setuid.go index 92919b53e90..9f761a16813 100644 --- a/endpoints/setuid.go +++ b/endpoints/setuid.go @@ -59,7 +59,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use query := r.URL.Query() - syncer, err := getSyncer(query, syncersByBidder) + syncer, bidderName, err := getSyncer(query, syncersByBidder) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) @@ -321,19 +321,19 @@ func parseConsentFromGppStr(gppQueryValue string) (string, error) { return gdprConsent, nil } -func getSyncer(query url.Values, syncersByBidder map[string]usersync.Syncer) (usersync.Syncer, error) { +func getSyncer(query url.Values, syncersByBidder map[string]usersync.Syncer) (usersync.Syncer, string, error) { bidder := query.Get("bidder") if bidder == "" { - return nil, errors.New(`"bidder" query param is required`) + return nil, "", errors.New(`"bidder" query param is required`) } syncer, syncerExists := syncersByBidder[bidder] if !syncerExists { - return nil, errors.New("The bidder name provided is not supported by Prebid Server") + return nil, "", errors.New("The bidder name provided is not supported by Prebid Server") } - return syncer, nil + return syncer, bidder, nil } // getResponseFormat reads the format query parameter or falls back to the syncer's default. From 695ab46cbf97eabe58a3432504819bb39ab7baab Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Wed, 26 Jul 2023 22:13:05 -0700 Subject: [PATCH 10/11] Removed empty line --- endpoints/setuid.go | 1 - 1 file changed, 1 deletion(-) diff --git a/endpoints/setuid.go b/endpoints/setuid.go index 9f761a16813..8f691389758 100644 --- a/endpoints/setuid.go +++ b/endpoints/setuid.go @@ -193,7 +193,6 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use // first and the 'gdpr' and 'gdpr_consent' query params second. If found in both, throws a // warning. Can also throw a parsing or validation error func extractGDPRInfo(query url.Values) (reqInfo gdpr.RequestInfo, err error) { - reqInfo, err = parseGDPRFromGPP(query) if err != nil { return gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous}, err From cc2854a06385c40bfc3f9f112758ad0158ed312d Mon Sep 17 00:00:00 2001 From: VeronikaSolovei9 Date: Thu, 27 Jul 2023 14:41:33 -0700 Subject: [PATCH 11/11] Error to nil fix. --- privacy/enforcer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/privacy/enforcer.go b/privacy/enforcer.go index a8685e29276..bcd9e948377 100644 --- a/privacy/enforcer.go +++ b/privacy/enforcer.go @@ -31,7 +31,7 @@ func NewActivityControl(privacyConf *config.AccountPrivacy) (ActivityControl, er var err error if privacyConf == nil { - return ac, err + return ac, nil } plans := make(map[Activity]ActivityPlan)