From ef67df24d0f19dcc75045ef7541b197e5b7f21c1 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Wed, 3 Jan 2024 11:58:50 +0530 Subject: [PATCH 01/30] Privacy Sandbox: support testing label header #3297 --- config/account.go | 10 ++++++++++ endpoints/cookie_sync.go | 34 +++++++++++++++++++++++++++++----- usersync/chooser.go | 16 +++++++++------- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/config/account.go b/config/account.go index ee131873e35..34b025b0f42 100644 --- a/config/account.go +++ b/config/account.go @@ -42,6 +42,7 @@ type Account struct { DefaultBidLimit int `mapstructure:"default_bid_limit" json:"default_bid_limit"` BidAdjustments *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"` Privacy AccountPrivacy `mapstructure:"privacy" json:"privacy"` + Auction AuctionPrivacy `mapstructure:"auctionprivacy" json:"auctionprivacy"` } // CookieSync represents the account-level defaults for the cookie sync endpoint. @@ -362,3 +363,12 @@ func (ip *IPv4) Validate(errs []error) []error { } return errs } + +type AuctionPrivacy struct { + PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox" json:"privacysandbox"` +} + +type PrivacySandbox struct { + CookieDeprecation bool `mapstructure:"cookiedeprecation" json:"cookiedeprecation"` + CookieDeprecationExpirationSec int `mapstructure:"cookiedeprecationexpirationsec" json:"cookiedeprecationexpirationsec"` +} diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index ef32e4048d7..8c8d01f77b9 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -9,6 +9,7 @@ import ( "net/http" "strconv" "strings" + "time" "github.com/golang/glog" "github.com/julienschmidt/httprouter" @@ -97,6 +98,13 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht cookie := usersync.ReadCookie(r, decoder, &c.config.HostCookie) usersync.SyncHostCookie(r, cookie, &c.config.HostCookie) + setCookieDeprecationHeader := request.SetCookieDeprecationHeader + if setCookieDeprecationHeader { + if rcd, err := r.Cookie("receive-cookie-deprecation"); err == nil && rcd != nil { + setCookieDeprecationHeader = false + } + } + result := c.chooser.Choose(request, cookie) switch result.Status { @@ -105,11 +113,11 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht c.handleError(w, errCookieSyncOptOut, http.StatusUnauthorized) case usersync.StatusBlockedByPrivacy: c.metrics.RecordCookieSync(metrics.CookieSyncGDPRHostCookieBlocked) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil, result.BiddersEvaluated, request.Debug) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil, result.BiddersEvaluated, request.Debug, setCookieDeprecationHeader, request.CookieDeprecationExpirationSec) case usersync.StatusOK: c.metrics.RecordCookieSync(metrics.CookieSyncOK) c.writeSyncerMetrics(result.BiddersEvaluated) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen, result.BiddersEvaluated, request.Debug) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen, result.BiddersEvaluated, request.Debug, setCookieDeprecationHeader, request.CookieDeprecationExpirationSec) } } @@ -182,8 +190,10 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma activityRequest: privacy.NewRequestFromPolicies(privacyPolicies), gdprSignal: gdprSignal, }, - SyncTypeFilter: syncTypeFilter, - GPPSID: request.GPPSID, + SyncTypeFilter: syncTypeFilter, + GPPSID: request.GPPSID, + SetCookieDeprecationHeader: account.Auction.PrivacySandbox.CookieDeprecation, + CookieDeprecationExpirationSec: account.Auction.PrivacySandbox.CookieDeprecationExpirationSec, } return rx, privacyMacros, nil } @@ -401,7 +411,7 @@ func (c *cookieSyncEndpoint) writeSyncerMetrics(biddersEvaluated []usersync.Bidd } } -func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice, biddersEvaluated []usersync.BidderEvaluation, debug bool) { +func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice, biddersEvaluated []usersync.BidderEvaluation, debug, setCookieDeprecationHeader bool, cookieDeprecationExpirationSec int) { status := "no_cookie" if co.HasAnyLiveSyncs() { status = "ok" @@ -455,6 +465,20 @@ func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.S }) w.Header().Set("Content-Type", "application/json; charset=utf-8") + + if setCookieDeprecationHeader { + http.SetCookie(w, &http.Cookie{ + Name: "receive-cookie-deprecation", + Value: "1", + Secure: true, + HttpOnly: true, + Path: "/", + SameSite: http.SameSiteNoneMode, + // Partition: "", + Expires: time.Now().Add(time.Second * time.Duration(cookieDeprecationExpirationSec)), + }) + } + enc := json.NewEncoder(w) enc.SetEscapeHTML(false) enc.Encode(response) diff --git a/usersync/chooser.go b/usersync/chooser.go index d8bf731f693..3a7bf240c48 100644 --- a/usersync/chooser.go +++ b/usersync/chooser.go @@ -34,13 +34,15 @@ func NewChooser(bidderSyncerLookup map[string]Syncer, biddersKnown map[string]st // Request specifies a user sync request. type Request struct { - Bidders []string - Cooperative Cooperative - Limit int - Privacy Privacy - SyncTypeFilter SyncTypeFilter - GPPSID string - Debug bool + Bidders []string + Cooperative Cooperative + Limit int + Privacy Privacy + SyncTypeFilter SyncTypeFilter + GPPSID string + Debug bool + SetCookieDeprecationHeader bool + CookieDeprecationExpirationSec int } // Cooperative specifies the settings for cooperative syncing for a given request, where bidders From 63056067099b04f73af71bcb73350a2ad08486a7 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Wed, 3 Jan 2024 14:24:40 +0530 Subject: [PATCH 02/30] udpate device.ext.cdep --- endpoints/cookie_sync_test.go | 2 +- endpoints/openrtb2/auction.go | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 401c6796237..c8777c8b1a1 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -1756,7 +1756,7 @@ func TestCookieSyncHandleResponse(t *testing.T) { } else { bidderEval = []usersync.BidderEvaluation{} } - endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug) + endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug, false, 0) if assert.Equal(t, writer.Code, http.StatusOK, test.description+":http_status") { assert.Equal(t, writer.Header().Get("Content-Type"), "application/json; charset=utf-8", test.description+":http_header") diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index eb3f5c02ccb..7ebbfae76c3 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -2020,7 +2020,7 @@ func setDeviceImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, i setIPImplicitly(httpReq, r, ipValidtor) setUAImplicitly(httpReq, r) setDoNotTrackImplicitly(httpReq, r) - + secCookieDeprecation(httpReq, r) } // setAuctionTypeImplicitly sets the auction type to 1 if it wasn't on the request, @@ -2352,6 +2352,33 @@ func setDoNotTrackImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrappe } } +func secCookieDeprecation(httpReq *http.Request, r *openrtb_ext.RequestWrapper) { + secCookieDeprecation := httpReq.Header.Get("Sec-Cookie-Deprecation") + if secCookieDeprecation == "" { + return + } + + if r.Device == nil { + r.Device = &openrtb2.Device{} + } + + updated := false + deviceExt := map[string]interface{}{} + if r.Device.Ext == nil { + updated = true + deviceExt["cdep"] = secCookieDeprecation + } else { + if err := json.Unmarshal(r.Device.Ext, &deviceExt); err == nil && deviceExt["cdep"] == nil { + updated = true + deviceExt["cdep"] = secCookieDeprecation + } + } + + if updated { + r.Device.Ext, _ = json.Marshal(deviceExt) + } +} + // Write(return) errors to the client, if any. Returns true if errors were found. func writeError(errs []error, w http.ResponseWriter, labels *metrics.Labels) bool { var rc bool = false From 187d6a31a745cf6d83dd2a34f9fa2a779610ba76 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Thu, 18 Jan 2024 16:49:28 +0530 Subject: [PATCH 03/30] move config auction to root lvl --- config/account.go | 11 +---------- config/config.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/config/account.go b/config/account.go index 34b025b0f42..c2953d2e0d9 100644 --- a/config/account.go +++ b/config/account.go @@ -42,7 +42,7 @@ type Account struct { DefaultBidLimit int `mapstructure:"default_bid_limit" json:"default_bid_limit"` BidAdjustments *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"` Privacy AccountPrivacy `mapstructure:"privacy" json:"privacy"` - Auction AuctionPrivacy `mapstructure:"auctionprivacy" json:"auctionprivacy"` + Auction Auction `mapstructure:"auctionprivacy" json:"auctionprivacy"` } // CookieSync represents the account-level defaults for the cookie sync endpoint. @@ -363,12 +363,3 @@ func (ip *IPv4) Validate(errs []error) []error { } return errs } - -type AuctionPrivacy struct { - PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox" json:"privacysandbox"` -} - -type PrivacySandbox struct { - CookieDeprecation bool `mapstructure:"cookiedeprecation" json:"cookiedeprecation"` - CookieDeprecationExpirationSec int `mapstructure:"cookiedeprecationexpirationsec" json:"cookiedeprecationexpirationsec"` -} diff --git a/config/config.go b/config/config.go index 60a541e2ed0..bdcc806ac58 100644 --- a/config/config.go +++ b/config/config.go @@ -100,6 +100,8 @@ type Configuration struct { Hooks Hooks `mapstructure:"hooks"` Validations Validations `mapstructure:"validations"` PriceFloors PriceFloors `mapstructure:"price_floors"` + + Auction Auction `mapstructure:"auction"` } type PriceFloors struct { @@ -1285,3 +1287,13 @@ type TmaxAdjustments struct { // PBS won't send a request to the bidder if the bidder tmax calculated is less than the BidderResponseDurationMin value BidderResponseDurationMin uint `mapstructure:"bidder_response_duration_min_ms"` } + +type Auction struct { + PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox" json:"privacysandbox"` +} + +type PrivacySandbox struct { + TopicsDomain string `mapstructure:"topicsdomain"` + CookieDeprecation bool `mapstructure:"cookiedeprecation"` + CookieDeprecationExpirationSec int `mapstructure:"cookiedeprecationexpirationsec"` +} From dd3244e50cd30259c894e85a2ffd56b845a95111 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Fri, 19 Jan 2024 13:28:58 +0530 Subject: [PATCH 04/30] refactor + new ut --- config/config.go | 12 ++- endpoints/cookie_sync.go | 20 ++--- endpoints/cookie_sync_test.go | 145 ++++++++++++++++++++++++++++++---- usersync/chooser.go | 23 +++--- 4 files changed, 162 insertions(+), 38 deletions(-) diff --git a/config/config.go b/config/config.go index bdcc806ac58..0ca986298fa 100644 --- a/config/config.go +++ b/config/config.go @@ -1289,11 +1289,15 @@ type TmaxAdjustments struct { } type Auction struct { - PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox" json:"privacysandbox"` + PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox"` } type PrivacySandbox struct { - TopicsDomain string `mapstructure:"topicsdomain"` - CookieDeprecation bool `mapstructure:"cookiedeprecation"` - CookieDeprecationExpirationSec int `mapstructure:"cookiedeprecationexpirationsec"` + TopicsDomain string `mapstructure:"topicsdomain"` + CookieDeprecation CookieDeprecation `mapstructure:"cookiedeprecation"` +} + +type CookieDeprecation struct { + Enabled bool `mapstructure:"enabled"` + TTLSec int `mapstructure:"ttlsec"` } diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index 8c8d01f77b9..e4b59b482e1 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -98,7 +98,7 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht cookie := usersync.ReadCookie(r, decoder, &c.config.HostCookie) usersync.SyncHostCookie(r, cookie, &c.config.HostCookie) - setCookieDeprecationHeader := request.SetCookieDeprecationHeader + setCookieDeprecationHeader := request.CookieDeprecation.SetHeader if setCookieDeprecationHeader { if rcd, err := r.Cookie("receive-cookie-deprecation"); err == nil && rcd != nil { setCookieDeprecationHeader = false @@ -113,11 +113,11 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht c.handleError(w, errCookieSyncOptOut, http.StatusUnauthorized) case usersync.StatusBlockedByPrivacy: c.metrics.RecordCookieSync(metrics.CookieSyncGDPRHostCookieBlocked) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil, result.BiddersEvaluated, request.Debug, setCookieDeprecationHeader, request.CookieDeprecationExpirationSec) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil, result.BiddersEvaluated, request.Debug, setCookieDeprecationHeader, request.CookieDeprecation.TTLSec) case usersync.StatusOK: c.metrics.RecordCookieSync(metrics.CookieSyncOK) c.writeSyncerMetrics(result.BiddersEvaluated) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen, result.BiddersEvaluated, request.Debug, setCookieDeprecationHeader, request.CookieDeprecationExpirationSec) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen, result.BiddersEvaluated, request.Debug, setCookieDeprecationHeader, request.CookieDeprecation.TTLSec) } } @@ -190,10 +190,12 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma activityRequest: privacy.NewRequestFromPolicies(privacyPolicies), gdprSignal: gdprSignal, }, - SyncTypeFilter: syncTypeFilter, - GPPSID: request.GPPSID, - SetCookieDeprecationHeader: account.Auction.PrivacySandbox.CookieDeprecation, - CookieDeprecationExpirationSec: account.Auction.PrivacySandbox.CookieDeprecationExpirationSec, + SyncTypeFilter: syncTypeFilter, + GPPSID: request.GPPSID, + CookieDeprecation: usersync.CookieDeprecation{ + SetHeader: account.Auction.PrivacySandbox.CookieDeprecation.Enabled, + TTLSec: account.Auction.PrivacySandbox.CookieDeprecation.TTLSec, + }, } return rx, privacyMacros, nil } @@ -411,7 +413,7 @@ func (c *cookieSyncEndpoint) writeSyncerMetrics(biddersEvaluated []usersync.Bidd } } -func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice, biddersEvaluated []usersync.BidderEvaluation, debug, setCookieDeprecationHeader bool, cookieDeprecationExpirationSec int) { +func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice, biddersEvaluated []usersync.BidderEvaluation, debug, setCookieDeprecationHeader bool, cookieDeprecationTTLSec int) { status := "no_cookie" if co.HasAnyLiveSyncs() { status = "ok" @@ -475,7 +477,7 @@ func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.S Path: "/", SameSite: http.SameSiteNoneMode, // Partition: "", - Expires: time.Now().Add(time.Second * time.Duration(cookieDeprecationExpirationSec)), + Expires: time.Now().Add(time.Second * time.Duration(cookieDeprecationTTLSec)), }) } diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index c8777c8b1a1..aa4708b25ec 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -111,14 +111,17 @@ func TestCookieSyncHandle(t *testing.T) { cookieWithSyncs.Sync("foo", "anyID") testCases := []struct { - description string - givenCookie *usersync.Cookie - givenBody io.Reader - givenChooserResult usersync.Result - expectedStatusCode int - expectedBody string - setMetricsExpectations func(*metrics.MetricsEngineMock) - setAnalyticsExpectations func(*MockAnalyticsRunner) + description string + givenCookie *usersync.Cookie + givenChromeDeprecationCookie bool + givenBody io.Reader + givenChooserResult usersync.Result + givenAccountData map[string]json.RawMessage + expectedStatusCode int + expectedBody string + expectedCookieDeprecationHeader bool + setMetricsExpectations func(*metrics.MetricsEngineMock) + setAnalyticsExpectations func(*MockAnalyticsRunner) }{ { description: "Request With Cookie", @@ -285,6 +288,79 @@ func TestCookieSyncHandle(t *testing.T) { a.On("LogCookieSyncObject", &expected).Once() }, }, + { + description: "CookieDeprecation enabled for account, receive-cookie-deprecation cookie not present in request, expect cookie deprecation header set in response", + givenCookie: cookieWithSyncs, + givenBody: strings.NewReader(`{"account": "testAccount"}`), + givenChooserResult: usersync.Result{ + Status: usersync.StatusOK, + BiddersEvaluated: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusAlreadySynced}}, + SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, + }, + givenAccountData: map[string]json.RawMessage{ + "testAccount": json.RawMessage(`{"id":"1","auctionprivacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + }, + expectedStatusCode: 200, + expectedBody: `{"status":"ok","bidder_status":[` + + `{"bidder":"a","no_cookie":true,"usersync":{"url":"aURL","type":"redirect","supportCORS":true}}` + + `]}` + "\n", + expectedCookieDeprecationHeader: true, + setMetricsExpectations: func(m *metrics.MetricsEngineMock) { + m.On("RecordCookieSync", metrics.CookieSyncOK).Once() + m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncAlreadySynced).Once() + }, + setAnalyticsExpectations: func(a *MockAnalyticsRunner) { + expected := analytics.CookieSyncObject{ + Status: 200, + Errors: nil, + BidderStatus: []*analytics.CookieSyncBidder{ + { + BidderCode: "a", + NoCookie: true, + UsersyncInfo: &analytics.UsersyncInfo{URL: "aURL", Type: "redirect", SupportCORS: true}, + }, + }, + } + a.On("LogCookieSyncObject", &expected).Once() + }, + }, + { + description: "CookieDeprecation enabled for account, receive-cookie-deprecation cookie present in request, cookie deprecation header not set in response", + givenCookie: cookieWithSyncs, + givenChromeDeprecationCookie: true, + givenBody: strings.NewReader(`{"account": "testAccount"}`), + givenChooserResult: usersync.Result{ + Status: usersync.StatusOK, + BiddersEvaluated: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusAlreadySynced}}, + SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, + }, + givenAccountData: map[string]json.RawMessage{ + "testAccount": json.RawMessage(`{"id":"1","auctionprivacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + }, + expectedStatusCode: 200, + expectedBody: `{"status":"ok","bidder_status":[` + + `{"bidder":"a","no_cookie":true,"usersync":{"url":"aURL","type":"redirect","supportCORS":true}}` + + `]}` + "\n", + expectedCookieDeprecationHeader: false, + setMetricsExpectations: func(m *metrics.MetricsEngineMock) { + m.On("RecordCookieSync", metrics.CookieSyncOK).Once() + m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncAlreadySynced).Once() + }, + setAnalyticsExpectations: func(a *MockAnalyticsRunner) { + expected := analytics.CookieSyncObject{ + Status: 200, + Errors: nil, + BidderStatus: []*analytics.CookieSyncBidder{ + { + BidderCode: "a", + NoCookie: true, + UsersyncInfo: &analytics.UsersyncInfo{URL: "aURL", Type: "redirect", SupportCORS: true}, + }, + }, + } + a.On("LogCookieSyncObject", &expected).Once() + }, + }, } for _, test := range testCases { @@ -294,7 +370,9 @@ func TestCookieSyncHandle(t *testing.T) { mockAnalytics := MockAnalyticsRunner{} test.setAnalyticsExpectations(&mockAnalytics) - fakeAccountFetcher := FakeAccountsFetcher{} + fakeAccountFetcher := FakeAccountsFetcher{ + AccountData: test.givenAccountData, + } gdprPermsBuilder := fakePermissionsBuilder{ permissions: &fakePermissions{}, @@ -310,6 +388,10 @@ func TestCookieSyncHandle(t *testing.T) { request.AddCookie(httpCookie) } + if test.givenChromeDeprecationCookie { + request.AddCookie(&http.Cookie{Name: "receive-cookie-deprecation", Value: "1"}) + } + writer := httptest.NewRecorder() endpoint := cookieSyncEndpoint{ @@ -336,6 +418,15 @@ func TestCookieSyncHandle(t *testing.T) { assert.Equal(t, test.expectedStatusCode, writer.Code, test.description+":status_code") assert.Equal(t, test.expectedBody, writer.Body.String(), test.description+":body") + + // -receive-cookie-deprecation=1; Path=/; Expires=Sat, 20 Jan 2024 06:06:49 GMT; HttpOnly; Secure; SameSite=None + // +receive-cookie-deprecation=1; Path=/; Expires=Sat, 20 Jan 2024 06:36:49 GMT; HttpOnly; Secure; SameSite=None + setCookie := writer.Header().Get("Set-Cookie") + assert.Equal(t, test.expectedCookieDeprecationHeader, + strings.Contains(setCookie, "receive-cookie-deprecation=1; Path=/; Expires="), test.description) + assert.Equal(t, test.expectedCookieDeprecationHeader, + strings.Contains(setCookie, "; HttpOnly; Secure; SameSite=None"), test.description) + mockMetrics.AssertExpectations(t) mockAnalytics.AssertExpectations(t) } @@ -1645,12 +1736,14 @@ func TestCookieSyncHandleResponse(t *testing.T) { } testCases := []struct { - description string - givenCookieHasSyncs bool - givenSyncersChosen []usersync.SyncerChoice - givenDebug bool - expectedJSON string - expectedAnalytics analytics.CookieSyncObject + description string + givenCookieHasSyncs bool + givenSyncersChosen []usersync.SyncerChoice + givenDebug bool + givenSetCookieDeprecationHeader bool + givenCookieDeprecationTTLSec int + expectedJSON string + expectedAnalytics analytics.CookieSyncObject }{ { description: "None", @@ -1734,6 +1827,16 @@ func TestCookieSyncHandleResponse(t *testing.T) { 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{}}, }, + { + description: "SetCookieDeprecationHeader is true, should see receive-cookie-deprecation header in response", + givenCookieHasSyncs: true, + givenDebug: true, + givenSyncersChosen: []usersync.SyncerChoice{}, + givenSetCookieDeprecationHeader: true, + givenCookieDeprecationTTLSec: 86400, + 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{}}, + }, } for _, test := range testCases { @@ -1756,12 +1859,22 @@ func TestCookieSyncHandleResponse(t *testing.T) { } else { bidderEval = []usersync.BidderEvaluation{} } - endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug, false, 0) + endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug, test.givenSetCookieDeprecationHeader, test.givenCookieDeprecationTTLSec) if assert.Equal(t, writer.Code, http.StatusOK, test.description+":http_status") { assert.Equal(t, writer.Header().Get("Content-Type"), "application/json; charset=utf-8", test.description+":http_header") assert.Equal(t, test.expectedJSON, writer.Body.String(), test.description+":http_response") } + + // assert.Equal(t, writer.Header().Get("Set-Cookie"), "receive-cookie-deprecation=1; Path=/; Expires="+time.Now().Add(time.Second*time.Duration(test.givenCookieDeprecationTTLSec)).Format("Mon, 02 Jan 2006 15:04:05 GMT")+"; HttpOnly; Secure; SameSite=None", test.description+":set_cookie_deprecation_header") + // -receive-cookie-deprecation=1; Path=/; Expires=Sat, 20 Jan 2024 06:06:49 GMT; HttpOnly; Secure; SameSite=None + // +receive-cookie-deprecation=1; Path=/; Expires=Sat, 20 Jan 2024 11:36:49 GMT; HttpOnly; Secure; SameSite=None + setCookie := writer.Header().Get("Set-Cookie") + assert.Equal(t, test.givenSetCookieDeprecationHeader, + strings.Contains(setCookie, "receive-cookie-deprecation=1; Path=/; Expires="), test.description) + assert.Equal(t, test.givenSetCookieDeprecationHeader, + strings.Contains(setCookie, "; HttpOnly; Secure; SameSite=None"), test.description) + mockAnalytics.AssertExpectations(t) } } diff --git a/usersync/chooser.go b/usersync/chooser.go index 3a7bf240c48..065a4b915e9 100644 --- a/usersync/chooser.go +++ b/usersync/chooser.go @@ -34,15 +34,14 @@ func NewChooser(bidderSyncerLookup map[string]Syncer, biddersKnown map[string]st // Request specifies a user sync request. type Request struct { - Bidders []string - Cooperative Cooperative - Limit int - Privacy Privacy - SyncTypeFilter SyncTypeFilter - GPPSID string - Debug bool - SetCookieDeprecationHeader bool - CookieDeprecationExpirationSec int + Bidders []string + Cooperative Cooperative + Limit int + Privacy Privacy + SyncTypeFilter SyncTypeFilter + GPPSID string + Debug bool + CookieDeprecation CookieDeprecation } // Cooperative specifies the settings for cooperative syncing for a given request, where bidders @@ -72,6 +71,12 @@ type SyncerChoice struct { Syncer Syncer } +// CookieDeprecation defines reponse header +type CookieDeprecation struct { + SetHeader bool + TTLSec int +} + // Status specifies the result of a sync evaluation. type Status int From 067330a6b2ea5024fad8d7a74bd06bd83ecae28a Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Fri, 19 Jan 2024 13:52:17 +0530 Subject: [PATCH 05/30] add ut for secCookieDeprecation --- endpoints/openrtb2/auction_test.go | 69 ++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index c521d653cac..011896e3503 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6108,3 +6108,72 @@ func TestValidateAliases(t *testing.T) { func fakeNormalizeBidderName(name string) (openrtb_ext.BidderName, bool) { return openrtb_ext.BidderName(strings.ToLower(name)), true } + +func Test_secCookieDeprecation(t *testing.T) { + type args struct { + httpReq *http.Request + r *openrtb_ext.RequestWrapper + } + tests := []struct { + name string + args args + wantDeviceExt json.RawMessage + }{ + { + name: "Sec-Cookie-Deprecation not present in request", + args: args{ + httpReq: &http.Request{}, + }, + }, + { + name: "Sec-Cookie-Deprecation present in request where request.device is nil", + args: args{ + httpReq: &http.Request{ + Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + }, + r: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + }, + wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1"}`), + }, + { + name: "Sec-Cookie-Deprecation present in request where request.device.ext is nil", + args: args{ + httpReq: &http.Request{ + Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + }, + r: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{}, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1"}`), + }, + { + name: "Sec-Cookie-Deprecation present in request where request.device.ext is not nil", + args: args{ + httpReq: &http.Request{ + Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + }, + r: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"foo":"bar"}`), + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{"foo":"bar","cdep":"example_label_1"}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + secCookieDeprecation(tt.args.httpReq, tt.args.r) + if tt.wantDeviceExt != nil { + assert.JSONEq(t, string(tt.wantDeviceExt), string(tt.args.r.BidRequest.Device.Ext), tt.name) + } + }) + } +} From 2894864b75a49c2c8cefa76ee1e37b80ed4a770d Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Mon, 22 Jan 2024 10:52:34 +0530 Subject: [PATCH 06/30] refactor as per review --- config/config.go | 3 - endpoints/openrtb2/amp_auction.go | 23 +++++--- endpoints/openrtb2/auction.go | 38 ++++++------- endpoints/openrtb2/auction_test.go | 88 +++++++++++++++++++++++++++-- endpoints/openrtb2/video_auction.go | 18 +++--- 5 files changed, 127 insertions(+), 43 deletions(-) diff --git a/config/config.go b/config/config.go index 0ca986298fa..cdfe6907e29 100644 --- a/config/config.go +++ b/config/config.go @@ -100,8 +100,6 @@ type Configuration struct { Hooks Hooks `mapstructure:"hooks"` Validations Validations `mapstructure:"validations"` PriceFloors PriceFloors `mapstructure:"price_floors"` - - Auction Auction `mapstructure:"auction"` } type PriceFloors struct { @@ -1293,7 +1291,6 @@ type Auction struct { } type PrivacySandbox struct { - TopicsDomain string `mapstructure:"topicsdomain"` CookieDeprecation CookieDeprecation `mapstructure:"cookiedeprecation"` } diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index db427f380ed..fe43080a74e 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -230,6 +230,22 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h return } + // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). + deps.setFieldsImplicitly(r, reqWrapper, account) + + hasStoredResponses := len(storedAuctionResponses) > 0 + errs := deps.validateRequest(reqWrapper, true, hasStoredResponses, storedBidResponses, false) + errL = append(errL, errs...) + ao.Errors = append(ao.Errors, errs...) + if errortypes.ContainsFatalError(errs) { + w.WriteHeader(http.StatusBadRequest) + for _, err := range errortypes.FatalOnly(errs) { + w.Write([]byte(fmt.Sprintf("Invalid request: %s\n", err.Error()))) + } + labels.RequestStatus = metrics.RequestStatusBadInput + return + } + tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR) activityControl = privacy.NewActivityControl(&account.Privacy) @@ -482,9 +498,6 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr // move to using the request wrapper req = &openrtb_ext.RequestWrapper{BidRequest: reqNormal} - // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). - deps.setFieldsImplicitly(httpRequest, req) - // Need to ensure cache and targeting are turned on e = initAmpTargetingAndCache(req) if errs = append(errs, e...); errortypes.ContainsFatalError(errs) { @@ -496,10 +509,6 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr return } - hasStoredResponses := len(storedAuctionResponses) > 0 - e = deps.validateRequest(req, true, hasStoredResponses, storedBidResponses, false) - errs = append(errs, e...) - return } diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 7ebbfae76c3..4eba807ab42 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -530,7 +530,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric } // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). - deps.setFieldsImplicitly(httpRequest, req) + deps.setFieldsImplicitly(httpRequest, req, account) if err := ortb.SetDefaults(req); err != nil { errs = []error{err} @@ -1917,6 +1917,10 @@ func validateDevice(device *openrtb2.Device) error { return errors.New("request.device.geo.accuracy must be a positive number") } + if cdep, err := jsonparser.GetString(device.Ext, "cdep"); err == nil && len(cdep) > 100 { + return errors.New("request.device.ext.cdep must be less than 100 characters") + } + return nil } @@ -2001,10 +2005,10 @@ func sanitizeRequest(r *openrtb_ext.RequestWrapper, ipValidator iputil.IPValidat // OpenRTB properties from the headers and other implicit info. // // This function _should not_ override any fields which were defined explicitly by the caller in the request. -func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) { +func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, account *config.Account) { sanitizeRequest(r, deps.privateNetworkIPValidator) - setDeviceImplicitly(httpReq, r, deps.privateNetworkIPValidator) + setDeviceImplicitly(httpReq, r, deps.privateNetworkIPValidator, account) // Per the OpenRTB spec: A bid request must not contain more than one of Site|App|DOOH // Assume it's a site request if it's not declared as one of the other values @@ -2016,11 +2020,11 @@ func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, r *openrtb_ } // setDeviceImplicitly uses implicit info from httpReq to populate bidReq.Device -func setDeviceImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, ipValidtor iputil.IPValidator) { +func setDeviceImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, ipValidtor iputil.IPValidator, account *config.Account) { setIPImplicitly(httpReq, r, ipValidtor) setUAImplicitly(httpReq, r) setDoNotTrackImplicitly(httpReq, r) - secCookieDeprecation(httpReq, r) + setCookieDeprecation(httpReq, r, account) } // setAuctionTypeImplicitly sets the auction type to 1 if it wasn't on the request, @@ -2352,7 +2356,11 @@ func setDoNotTrackImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrappe } } -func secCookieDeprecation(httpReq *http.Request, r *openrtb_ext.RequestWrapper) { +func setCookieDeprecation(httpReq *http.Request, r *openrtb_ext.RequestWrapper, account *config.Account) { + if account == nil || !account.Auction.PrivacySandbox.CookieDeprecation.Enabled { + return + } + secCookieDeprecation := httpReq.Header.Get("Sec-Cookie-Deprecation") if secCookieDeprecation == "" { return @@ -2362,21 +2370,13 @@ func secCookieDeprecation(httpReq *http.Request, r *openrtb_ext.RequestWrapper) r.Device = &openrtb2.Device{} } - updated := false - deviceExt := map[string]interface{}{} - if r.Device.Ext == nil { - updated = true - deviceExt["cdep"] = secCookieDeprecation - } else { - if err := json.Unmarshal(r.Device.Ext, &deviceExt); err == nil && deviceExt["cdep"] == nil { - updated = true - deviceExt["cdep"] = secCookieDeprecation + if deviceExt, err := r.GetDeviceExt(); err == nil { + ext := deviceExt.GetExt() + if ext["cdep"] == nil { + ext["cdep"] = json.RawMessage(fmt.Sprintf(`"%s"`, secCookieDeprecation)) + deviceExt.SetExt(ext) } } - - if updated { - r.Device.Ext, _ = json.Marshal(deviceExt) - } } // Write(return) errors to the client, if any. Returns true if errors were found. diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 011896e3503..f5a5335c9d3 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6109,24 +6109,71 @@ func fakeNormalizeBidderName(name string) (openrtb_ext.BidderName, bool) { return openrtb_ext.BidderName(strings.ToLower(name)), true } -func Test_secCookieDeprecation(t *testing.T) { +func TestSetCookieDeprecation(t *testing.T) { type args struct { httpReq *http.Request r *openrtb_ext.RequestWrapper + account config.Account } tests := []struct { name string args args wantDeviceExt json.RawMessage }{ + { + name: "account nil", + }, + { + name: "cookie deprecation not enabled", + args: args{ + httpReq: &http.Request{ + Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + }, + r: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + }, + wantDeviceExt: nil, + }, + { + name: "cookie deprecation disabled explicitly", + args: args{ + httpReq: &http.Request{ + Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + }, + r: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + account: config.Account{ + Auction: config.Auction{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, + }, + }, + }, + }, + }, + wantDeviceExt: nil, + }, { name: "Sec-Cookie-Deprecation not present in request", args: args{ httpReq: &http.Request{}, + account: config.Account{ + Auction: config.Auction{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, }, + wantDeviceExt: nil, }, { - name: "Sec-Cookie-Deprecation present in request where request.device is nil", + name: "Sec-Cookie-Deprecation present in request where request.device.ext is nil", args: args{ httpReq: &http.Request{ Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, @@ -6134,6 +6181,15 @@ func Test_secCookieDeprecation(t *testing.T) { r: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{}, }, + account: config.Account{ + Auction: config.Auction{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, }, wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1"}`), }, @@ -6145,7 +6201,18 @@ func Test_secCookieDeprecation(t *testing.T) { }, r: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{ - Device: &openrtb2.Device{}, + Device: &openrtb2.Device{ + Ext: nil, + }, + }, + }, + account: config.Account{ + Auction: config.Auction{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, }, }, }, @@ -6164,15 +6231,26 @@ func Test_secCookieDeprecation(t *testing.T) { }, }, }, + account: config.Account{ + Auction: config.Auction{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, }, wantDeviceExt: json.RawMessage(`{"foo":"bar","cdep":"example_label_1"}`), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - secCookieDeprecation(tt.args.httpReq, tt.args.r) + setCookieDeprecation(tt.args.httpReq, tt.args.r, &tt.args.account) if tt.wantDeviceExt != nil { - assert.JSONEq(t, string(tt.wantDeviceExt), string(tt.args.r.BidRequest.Device.Ext), tt.name) + err := tt.args.r.RebuildRequest() + assert.NoError(t, err) + assert.JSONEq(t, string(tt.wantDeviceExt), string(tt.args.r.Device.Ext), tt.name) } }) } diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 05802bbd506..20b31762347 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -259,20 +259,11 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re // all code after this line should use the bidReqWrapper instead of bidReq directly bidReqWrapper := &openrtb_ext.RequestWrapper{BidRequest: bidReq} - // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). - deps.setFieldsImplicitly(r, bidReqWrapper) - if err := ortb.SetDefaults(bidReqWrapper); err != nil { handleError(&labels, w, errL, &vo, &debugLog) return } - errL = deps.validateRequest(bidReqWrapper, false, false, nil, false) - if errortypes.ContainsFatalError(errL) { - handleError(&labels, w, errL, &vo, &debugLog) - return - } - ctx := context.Background() timeout := deps.cfg.AuctionTimeouts.LimitAuctionTimeout(time.Duration(bidReqWrapper.TMax) * time.Millisecond) if timeout > 0 { @@ -306,6 +297,15 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re return } + // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). + deps.setFieldsImplicitly(r, bidReqWrapper, account) + + errL = deps.validateRequest(bidReqWrapper, false, false, nil, false) + if errortypes.ContainsFatalError(errL) { + handleError(&labels, w, errL, &vo, &debugLog) + return + } + activityControl = privacy.NewActivityControl(&account.Privacy) secGPC := r.Header.Get("Sec-GPC") From 0ecfdece8d87849ef5b91811c0705b66a0611b14 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Mon, 22 Jan 2024 13:16:16 +0530 Subject: [PATCH 07/30] cookie sync refactor as per review comments --- endpoints/cookie_sync.go | 74 +++++++++++++++---------- endpoints/cookie_sync_test.go | 88 +++++++++++++++++------------- endpoints/openrtb2/auction.go | 3 +- endpoints/openrtb2/auction_test.go | 10 ++-- usersync/chooser.go | 21 +++---- 5 files changed, 110 insertions(+), 86 deletions(-) diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index e4b59b482e1..431fc1b38c8 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -30,8 +30,17 @@ import ( "github.com/prebid/prebid-server/v2/usersync" "github.com/prebid/prebid-server/v2/util/jsonutil" stringutil "github.com/prebid/prebid-server/v2/util/stringutil" + "github.com/prebid/prebid-server/v2/util/timeutil" ) +const receiveCookieDeprecation = "receive-cookie-deprecation" + +// cookieDeprecation defines reponse header +type cookieDeprecation struct { + SetHeader bool + TTLSec int +} + var ( errCookieSyncOptOut = errors.New("User has opted out") errCookieSyncBody = errors.New("Failed to read request body") @@ -74,6 +83,7 @@ func NewCookieSyncEndpoint( metrics: metrics, pbsAnalytics: analyticsRunner, accountsFetcher: accountsFetcher, + time: &timeutil.RealTime{}, } } @@ -84,13 +94,14 @@ type cookieSyncEndpoint struct { metrics metrics.MetricsEngine pbsAnalytics analytics.Runner accountsFetcher stored_requests.AccountFetcher + time timeutil.Time } func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - request, privacyMacros, err := c.parseRequest(r) + request, privacyMacros, cookieDeprecation, err := c.parseRequest(r) if err != nil { c.writeParseRequestErrorMetrics(err) - c.handleError(w, err, http.StatusBadRequest) + c.handleError(w, err, http.StatusBadRequest, cookieDeprecation) return } decoder := usersync.Base64Decoder{} @@ -98,10 +109,9 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht cookie := usersync.ReadCookie(r, decoder, &c.config.HostCookie) usersync.SyncHostCookie(r, cookie, &c.config.HostCookie) - setCookieDeprecationHeader := request.CookieDeprecation.SetHeader - if setCookieDeprecationHeader { - if rcd, err := r.Cookie("receive-cookie-deprecation"); err == nil && rcd != nil { - setCookieDeprecationHeader = false + if cookieDeprecation.SetHeader { + if rcd, err := r.Cookie(receiveCookieDeprecation); err == nil && rcd != nil { + cookieDeprecation.SetHeader = false } } @@ -110,27 +120,27 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht switch result.Status { case usersync.StatusBlockedByUserOptOut: c.metrics.RecordCookieSync(metrics.CookieSyncOptOut) - c.handleError(w, errCookieSyncOptOut, http.StatusUnauthorized) + c.handleError(w, errCookieSyncOptOut, http.StatusUnauthorized, cookieDeprecation) case usersync.StatusBlockedByPrivacy: c.metrics.RecordCookieSync(metrics.CookieSyncGDPRHostCookieBlocked) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil, result.BiddersEvaluated, request.Debug, setCookieDeprecationHeader, request.CookieDeprecation.TTLSec) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil, result.BiddersEvaluated, request.Debug, cookieDeprecation) case usersync.StatusOK: c.metrics.RecordCookieSync(metrics.CookieSyncOK) c.writeSyncerMetrics(result.BiddersEvaluated) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen, result.BiddersEvaluated, request.Debug, setCookieDeprecationHeader, request.CookieDeprecation.TTLSec) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen, result.BiddersEvaluated, request.Debug, cookieDeprecation) } } -func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, error) { +func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, cookieDeprecation, error) { defer r.Body.Close() body, err := io.ReadAll(r.Body) if err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, errCookieSyncBody + return usersync.Request{}, macros.UserSyncPrivacy{}, cookieDeprecation{}, errCookieSyncBody } request := cookieSyncRequest{} if err := jsonutil.UnmarshalValid(body, &request); err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, fmt.Errorf("JSON parsing failed: %s", err.Error()) + return usersync.Request{}, macros.UserSyncPrivacy{}, cookieDeprecation{}, fmt.Errorf("JSON parsing failed: %s", err.Error()) } if request.Account == "" { @@ -138,7 +148,12 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma } account, fetchErrs := accountService.GetAccount(context.Background(), c.config, c.accountsFetcher, request.Account, c.metrics) if len(fetchErrs) > 0 { - return usersync.Request{}, macros.UserSyncPrivacy{}, combineErrors(fetchErrs) + return usersync.Request{}, macros.UserSyncPrivacy{}, cookieDeprecation{}, combineErrors(fetchErrs) + } + + cd := cookieDeprecation{ + SetHeader: account.Auction.PrivacySandbox.CookieDeprecation.Enabled, + TTLSec: account.Auction.PrivacySandbox.CookieDeprecation.TTLSec, } request = c.setLimit(request, account.CookieSync) @@ -146,7 +161,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma privacyMacros, gdprSignal, privacyPolicies, err := extractPrivacyPolicies(request, c.privacyConfig.gdprConfig.DefaultValue) if err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, err + return usersync.Request{}, macros.UserSyncPrivacy{}, cd, err } ccpaParsedPolicy := ccpa.ParsedPolicy{} @@ -164,7 +179,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma syncTypeFilter, err := parseTypeFilter(request.FilterSettings) if err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, err + return usersync.Request{}, macros.UserSyncPrivacy{}, cd, err } gdprRequestInfo := gdpr.RequestInfo{ @@ -192,12 +207,8 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma }, SyncTypeFilter: syncTypeFilter, GPPSID: request.GPPSID, - CookieDeprecation: usersync.CookieDeprecation{ - SetHeader: account.Auction.PrivacySandbox.CookieDeprecation.Enabled, - TTLSec: account.Auction.PrivacySandbox.CookieDeprecation.TTLSec, - }, } - return rx, privacyMacros, nil + return rx, privacyMacros, cd, nil } func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue string) (macros.UserSyncPrivacy, gdpr.Signal, privacy.Policies, error) { @@ -370,7 +381,8 @@ func parseBidderFilter(filter *cookieSyncRequestFilter) (usersync.BidderFilter, } } -func (c *cookieSyncEndpoint) handleError(w http.ResponseWriter, err error, httpStatus int) { +func (c *cookieSyncEndpoint) handleError(w http.ResponseWriter, err error, httpStatus int, cookieDeprecation cookieDeprecation) { + c.setCookieDeprecationHeader(w, cookieDeprecation) http.Error(w, err.Error(), httpStatus) c.pbsAnalytics.LogCookieSyncObject(&analytics.CookieSyncObject{ Status: httpStatus, @@ -413,7 +425,7 @@ func (c *cookieSyncEndpoint) writeSyncerMetrics(biddersEvaluated []usersync.Bidd } } -func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice, biddersEvaluated []usersync.BidderEvaluation, debug, setCookieDeprecationHeader bool, cookieDeprecationTTLSec int) { +func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice, biddersEvaluated []usersync.BidderEvaluation, debug bool, cookieDeprecation cookieDeprecation) { status := "no_cookie" if co.HasAnyLiveSyncs() { status = "ok" @@ -468,22 +480,26 @@ func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.S w.Header().Set("Content-Type", "application/json; charset=utf-8") - if setCookieDeprecationHeader { + c.setCookieDeprecationHeader(w, cookieDeprecation) + + enc := json.NewEncoder(w) + enc.SetEscapeHTML(false) + enc.Encode(response) +} + +func (c *cookieSyncEndpoint) setCookieDeprecationHeader(w http.ResponseWriter, cookieDeprecation cookieDeprecation) { + if cookieDeprecation.SetHeader { http.SetCookie(w, &http.Cookie{ - Name: "receive-cookie-deprecation", + Name: receiveCookieDeprecation, Value: "1", Secure: true, HttpOnly: true, Path: "/", SameSite: http.SameSiteNoneMode, // Partition: "", - Expires: time.Now().Add(time.Second * time.Duration(cookieDeprecationTTLSec)), + Expires: c.time.Now().Add(time.Second * time.Duration(cookieDeprecation.TTLSec)), }) } - - enc := json.NewEncoder(w) - enc.SetEscapeHTML(false) - enc.Encode(response) } func mapBidderStatusToAnalytics(from []cookieSyncResponseBidder) []*analytics.CookieSyncBidder { diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index aa4708b25ec..9a43b1b75de 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "io" "net/http" "net/http/httptest" @@ -11,6 +12,7 @@ import ( "strings" "testing" "testing/iotest" + "time" "github.com/prebid/prebid-server/v2/analytics" "github.com/prebid/prebid-server/v2/config" @@ -28,6 +30,15 @@ import ( "github.com/stretchr/testify/mock" ) +// fakeTime implements the Time interface +type fakeTime struct { + time time.Time +} + +func (ft *fakeTime) Now() time.Time { + return ft.time +} + func TestNewCookieSyncEndpoint(t *testing.T) { var ( syncersByBidder = map[string]usersync.Syncer{"a": &MockSyncer{}} @@ -389,7 +400,7 @@ func TestCookieSyncHandle(t *testing.T) { } if test.givenChromeDeprecationCookie { - request.AddCookie(&http.Cookie{Name: "receive-cookie-deprecation", Value: "1"}) + request.AddCookie(&http.Cookie{Name: receiveCookieDeprecation, Value: "1"}) } writer := httptest.NewRecorder() @@ -411,6 +422,7 @@ func TestCookieSyncHandle(t *testing.T) { metrics: &mockMetrics, pbsAnalytics: &mockAnalytics, accountsFetcher: &fakeAccountFetcher, + time: &fakeTime{time: time.Now()}, } assert.NoError(t, endpoint.config.MarshalAccountDefaults()) @@ -419,13 +431,14 @@ func TestCookieSyncHandle(t *testing.T) { assert.Equal(t, test.expectedStatusCode, writer.Code, test.description+":status_code") assert.Equal(t, test.expectedBody, writer.Body.String(), test.description+":body") - // -receive-cookie-deprecation=1; Path=/; Expires=Sat, 20 Jan 2024 06:06:49 GMT; HttpOnly; Secure; SameSite=None - // +receive-cookie-deprecation=1; Path=/; Expires=Sat, 20 Jan 2024 06:36:49 GMT; HttpOnly; Secure; SameSite=None - setCookie := writer.Header().Get("Set-Cookie") - assert.Equal(t, test.expectedCookieDeprecationHeader, - strings.Contains(setCookie, "receive-cookie-deprecation=1; Path=/; Expires="), test.description) - assert.Equal(t, test.expectedCookieDeprecationHeader, - strings.Contains(setCookie, "; HttpOnly; Secure; SameSite=None"), test.description) + gotCookie := writer.Header().Get("Set-Cookie") + if test.expectedCookieDeprecationHeader { + wantCookieTTL := endpoint.time.Now().Add(time.Second * time.Duration(86400)).UTC().Format(http.TimeFormat) + wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None", wantCookieTTL) + assert.Equal(t, wantCookie, gotCookie, ":set_cookie_deprecation_header") + } else { + assert.Equal(t, gotCookie, "") + } mockMetrics.AssertExpectations(t) mockAnalytics.AssertExpectations(t) @@ -1151,7 +1164,7 @@ func TestCookieSyncParseRequest(t *testing.T) { }}, } assert.NoError(t, endpoint.config.MarshalAccountDefaults()) - request, privacyPolicies, err := endpoint.parseRequest(httpRequest) + request, privacyPolicies, _, err := endpoint.parseRequest(httpRequest) if test.expectedError == "" { assert.NoError(t, err, test.description+":err") @@ -1397,7 +1410,7 @@ func TestWriteParseRequestErrorMetrics(t *testing.T) { writer := httptest.NewRecorder() endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics} - endpoint.handleError(writer, err, 418) + endpoint.handleError(writer, err, 418, cookieDeprecation{}) assert.Equal(t, writer.Code, 418) assert.Equal(t, writer.Body.String(), "anyError\n") @@ -1620,7 +1633,7 @@ func TestCookieSyncHandleError(t *testing.T) { writer := httptest.NewRecorder() endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics} - endpoint.handleError(writer, err, 418) + endpoint.handleError(writer, err, 418, cookieDeprecation{}) assert.Equal(t, writer.Code, 418) assert.Equal(t, writer.Body.String(), "anyError\n") @@ -1736,14 +1749,13 @@ func TestCookieSyncHandleResponse(t *testing.T) { } testCases := []struct { - description string - givenCookieHasSyncs bool - givenSyncersChosen []usersync.SyncerChoice - givenDebug bool - givenSetCookieDeprecationHeader bool - givenCookieDeprecationTTLSec int - expectedJSON string - expectedAnalytics analytics.CookieSyncObject + description string + givenCookieHasSyncs bool + givenSyncersChosen []usersync.SyncerChoice + givenDebug bool + givenCookieDeprecation cookieDeprecation + expectedJSON string + expectedAnalytics analytics.CookieSyncObject }{ { description: "None", @@ -1828,14 +1840,16 @@ func TestCookieSyncHandleResponse(t *testing.T) { expectedAnalytics: analytics.CookieSyncObject{Status: 200, BidderStatus: []*analytics.CookieSyncBidder{}}, }, { - description: "SetCookieDeprecationHeader is true, should see receive-cookie-deprecation header in response", - givenCookieHasSyncs: true, - givenDebug: true, - givenSyncersChosen: []usersync.SyncerChoice{}, - givenSetCookieDeprecationHeader: true, - givenCookieDeprecationTTLSec: 86400, - 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{}}, + description: "SetCookieDeprecationHeader is true, should see receive-cookie-deprecation header in response", + givenCookieHasSyncs: true, + givenDebug: true, + givenSyncersChosen: []usersync.SyncerChoice{}, + givenCookieDeprecation: cookieDeprecation{ + SetHeader: true, + TTLSec: 86400, + }, + 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{}}, }, } @@ -1851,7 +1865,7 @@ func TestCookieSyncHandleResponse(t *testing.T) { } writer := httptest.NewRecorder() - endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics} + endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics, time: &fakeTime{time: time.Now()}} var bidderEval []usersync.BidderEvaluation if test.givenDebug { @@ -1859,21 +1873,21 @@ func TestCookieSyncHandleResponse(t *testing.T) { } else { bidderEval = []usersync.BidderEvaluation{} } - endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug, test.givenSetCookieDeprecationHeader, test.givenCookieDeprecationTTLSec) + endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug, test.givenCookieDeprecation) if assert.Equal(t, writer.Code, http.StatusOK, test.description+":http_status") { assert.Equal(t, writer.Header().Get("Content-Type"), "application/json; charset=utf-8", test.description+":http_header") assert.Equal(t, test.expectedJSON, writer.Body.String(), test.description+":http_response") } - // assert.Equal(t, writer.Header().Get("Set-Cookie"), "receive-cookie-deprecation=1; Path=/; Expires="+time.Now().Add(time.Second*time.Duration(test.givenCookieDeprecationTTLSec)).Format("Mon, 02 Jan 2006 15:04:05 GMT")+"; HttpOnly; Secure; SameSite=None", test.description+":set_cookie_deprecation_header") - // -receive-cookie-deprecation=1; Path=/; Expires=Sat, 20 Jan 2024 06:06:49 GMT; HttpOnly; Secure; SameSite=None - // +receive-cookie-deprecation=1; Path=/; Expires=Sat, 20 Jan 2024 11:36:49 GMT; HttpOnly; Secure; SameSite=None - setCookie := writer.Header().Get("Set-Cookie") - assert.Equal(t, test.givenSetCookieDeprecationHeader, - strings.Contains(setCookie, "receive-cookie-deprecation=1; Path=/; Expires="), test.description) - assert.Equal(t, test.givenSetCookieDeprecationHeader, - strings.Contains(setCookie, "; HttpOnly; Secure; SameSite=None"), test.description) + gotCookie := writer.Header().Get("Set-Cookie") + if test.givenCookieDeprecation.SetHeader { + wantCookieTTL := endpoint.time.Now().Add(time.Second * time.Duration(test.givenCookieDeprecation.TTLSec)).UTC().Format(http.TimeFormat) + wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None", wantCookieTTL) + assert.Equal(t, wantCookie, gotCookie, ":set_cookie_deprecation_header") + } else { + assert.Equal(t, gotCookie, "") + } mockAnalytics.AssertExpectations(t) } diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 4eba807ab42..34ad99dc47c 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -60,6 +60,7 @@ import ( const storedRequestTimeoutMillis = 50 const ampChannel = "amp" const appChannel = "app" +const secCookieDeprecation = "Sec-Cookie-Deprecation" var ( dntKey string = http.CanonicalHeaderKey("DNT") @@ -2361,7 +2362,7 @@ func setCookieDeprecation(httpReq *http.Request, r *openrtb_ext.RequestWrapper, return } - secCookieDeprecation := httpReq.Header.Get("Sec-Cookie-Deprecation") + secCookieDeprecation := httpReq.Header.Get(secCookieDeprecation) if secCookieDeprecation == "" { return } diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index f5a5335c9d3..b7b452bcfc7 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6127,7 +6127,7 @@ func TestSetCookieDeprecation(t *testing.T) { name: "cookie deprecation not enabled", args: args{ httpReq: &http.Request{ - Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, r: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{}, @@ -6139,7 +6139,7 @@ func TestSetCookieDeprecation(t *testing.T) { name: "cookie deprecation disabled explicitly", args: args{ httpReq: &http.Request{ - Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, r: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{}, @@ -6176,7 +6176,7 @@ func TestSetCookieDeprecation(t *testing.T) { name: "Sec-Cookie-Deprecation present in request where request.device.ext is nil", args: args{ httpReq: &http.Request{ - Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, r: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{}, @@ -6197,7 +6197,7 @@ func TestSetCookieDeprecation(t *testing.T) { name: "Sec-Cookie-Deprecation present in request where request.device.ext is nil", args: args{ httpReq: &http.Request{ - Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, r: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{ @@ -6222,7 +6222,7 @@ func TestSetCookieDeprecation(t *testing.T) { name: "Sec-Cookie-Deprecation present in request where request.device.ext is not nil", args: args{ httpReq: &http.Request{ - Header: http.Header{"Sec-Cookie-Deprecation": []string{"example_label_1"}}, + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, r: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{ diff --git a/usersync/chooser.go b/usersync/chooser.go index 065a4b915e9..d8bf731f693 100644 --- a/usersync/chooser.go +++ b/usersync/chooser.go @@ -34,14 +34,13 @@ func NewChooser(bidderSyncerLookup map[string]Syncer, biddersKnown map[string]st // Request specifies a user sync request. type Request struct { - Bidders []string - Cooperative Cooperative - Limit int - Privacy Privacy - SyncTypeFilter SyncTypeFilter - GPPSID string - Debug bool - CookieDeprecation CookieDeprecation + Bidders []string + Cooperative Cooperative + Limit int + Privacy Privacy + SyncTypeFilter SyncTypeFilter + GPPSID string + Debug bool } // Cooperative specifies the settings for cooperative syncing for a given request, where bidders @@ -71,12 +70,6 @@ type SyncerChoice struct { Syncer Syncer } -// CookieDeprecation defines reponse header -type CookieDeprecation struct { - SetHeader bool - TTLSec int -} - // Status specifies the result of a sync evaluation. type Status int From 96141e713b82e5078c17273b66161a8e85586544 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Mon, 22 Jan 2024 14:00:06 +0530 Subject: [PATCH 08/30] prc --- config/account.go | 2 +- endpoints/cookie_sync_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/account.go b/config/account.go index c2953d2e0d9..dc78b4858bb 100644 --- a/config/account.go +++ b/config/account.go @@ -42,7 +42,7 @@ type Account struct { DefaultBidLimit int `mapstructure:"default_bid_limit" json:"default_bid_limit"` BidAdjustments *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"` Privacy AccountPrivacy `mapstructure:"privacy" json:"privacy"` - Auction Auction `mapstructure:"auctionprivacy" json:"auctionprivacy"` + Auction Auction `mapstructure:"auction" json:"auction"` } // CookieSync represents the account-level defaults for the cookie sync endpoint. diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 9a43b1b75de..2271fa68d1e 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -309,7 +309,7 @@ func TestCookieSyncHandle(t *testing.T) { SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, }, givenAccountData: map[string]json.RawMessage{ - "testAccount": json.RawMessage(`{"id":"1","auctionprivacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + "testAccount": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), }, expectedStatusCode: 200, expectedBody: `{"status":"ok","bidder_status":[` + @@ -346,7 +346,7 @@ func TestCookieSyncHandle(t *testing.T) { SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, }, givenAccountData: map[string]json.RawMessage{ - "testAccount": json.RawMessage(`{"id":"1","auctionprivacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + "testAccount": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), }, expectedStatusCode: 200, expectedBody: `{"status":"ok","bidder_status":[` + From 94e020c4becfb25f6da4bde7d8b357c25d7ca9b4 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Thu, 25 Jan 2024 14:11:32 +0530 Subject: [PATCH 09/30] add cdep len validation tests --- endpoints/openrtb2/amp_auction_test.go | 2 +- endpoints/openrtb2/auction.go | 26 +++-- endpoints/openrtb2/auction_test.go | 103 ++++++++++++++++++ .../valid-whole/supplementary/site.json | 5 +- 4 files changed, 125 insertions(+), 11 deletions(-) diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index bd56457b3d7..990e16421a6 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -1204,7 +1204,7 @@ func TestInitAmpTargetingAndCache(t *testing.T) { func TestQueryParamOverrides(t *testing.T) { requests := map[string]json.RawMessage{ - "1": json.RawMessage(validRequest(t, "site.json")), + "1": json.RawMessage(validRequest(t, "site-amp.json")), } endpoint, _ := NewAmpEndpoint( diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 34ad99dc47c..2ca02505ef3 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -870,7 +870,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp } } - if err := validateDevice(req.Device); err != nil { + if err := validateDevice(req); err != nil { return append(errL, err) } @@ -1898,28 +1898,36 @@ func validateRegs(req *openrtb_ext.RequestWrapper, gpp gpplib.GppContainer) []er return errL } -func validateDevice(device *openrtb2.Device) error { - if device == nil { +func validateDevice(req *openrtb_ext.RequestWrapper) error { + if req.Device == nil { return nil } // The following fields were previously uints in the OpenRTB library we use, but have // since been changed to ints. We decided to maintain the non-negative check. - if device.W < 0 { + if req.Device.W < 0 { return errors.New("request.device.w must be a positive number") } - if device.H < 0 { + if req.Device.H < 0 { return errors.New("request.device.h must be a positive number") } - if device.PPI < 0 { + if req.Device.PPI < 0 { return errors.New("request.device.ppi must be a positive number") } - if device.Geo != nil && device.Geo.Accuracy < 0 { + if req.Device.Geo != nil && req.Device.Geo.Accuracy < 0 { return errors.New("request.device.geo.accuracy must be a positive number") } - if cdep, err := jsonparser.GetString(device.Ext, "cdep"); err == nil && len(cdep) > 100 { - return errors.New("request.device.ext.cdep must be less than 100 characters") + return validateDeviceExt(req) +} + +func validateDeviceExt(req *openrtb_ext.RequestWrapper) error { + if deviceExt, err := req.GetDeviceExt(); err == nil { + if ext := deviceExt.GetExt(); ext != nil { + if value, ok := ext["cdep"]; ok && len(string(value)) > 100 { + return errors.New("request.device.ext.cdep must be less than 100 characters") + } + } } return nil diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index b7b452bcfc7..73ea572d358 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6255,3 +6255,106 @@ func TestSetCookieDeprecation(t *testing.T) { }) } } + +func TestParseRequestCookieDeprecation(t *testing.T) { + testCases := + []struct { + name string + httpReq *http.Request + givenAccountData map[string]json.RawMessage + expectedErr string + }{ + { + name: "Request without Sec-Cookie-Deprecation header (cookiedeprecation not enabled for account)", + httpReq: httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))), + expectedErr: "", + }, + { + name: "Request with Sec-Cookie-Deprecation header having length less than 100 (cookiedeprecation not enabled for account)", + httpReq: func() *http.Request { + req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) + req.Header.Set(secCookieDeprecation, "sample-value") + return req + }(), + expectedErr: "", + }, + { + name: "Request with Sec-Cookie-Deprecation header having length more than 100 (cookiedeprecation not enabled for account)", + httpReq: func() *http.Request { + req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) + req.Header.Set(secCookieDeprecation, "zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa") + return req + }(), + expectedErr: "", + }, + { + name: "Request without Sec-Cookie-Deprecation header (cookiedeprecation enabled for account)", + httpReq: httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))), + givenAccountData: map[string]json.RawMessage{ + "some-test-account-id": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + }, + expectedErr: "", + }, + { + name: "Request with Sec-Cookie-Deprecation header having length less than 100 (cookiedeprecation not enabled for account)", + httpReq: func() *http.Request { + req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) + req.Header.Set(secCookieDeprecation, "sample-value") + return req + }(), + givenAccountData: map[string]json.RawMessage{ + "some-test-account-id": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + }, + expectedErr: "", + }, + { + name: "Request with Sec-Cookie-Deprecation header having length more than 100 (cookiedeprecation not enabled for account)", + httpReq: func() *http.Request { + req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) + req.Header.Set(secCookieDeprecation, "zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa") + return req + }(), + givenAccountData: map[string]json.RawMessage{ + "some-test-account-id": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + }, + expectedErr: "request.device.ext.cdep must be less than 100 characters", + }, + } + + deps := &endpointDeps{ + fakeUUIDGenerator{}, + &warningsCheckExchange{}, + mockBidderParamValidator{}, + &mockStoredReqFetcher{}, + empty_fetcher.EmptyFetcher{}, + &mockAccountFetcher{}, + &config.Configuration{MaxRequestSize: int64(1000)}, + &metricsConfig.NilMetricsEngine{}, + analyticsBuild.New(&config.Analytics{}), + map[string]string{}, + false, + []byte{}, + openrtb_ext.BuildBidderMap(), + nil, + nil, + hardcodedResponseIPValidator{response: true}, + empty_fetcher.EmptyFetcher{}, + hooks.EmptyPlanBuilder{}, + nil, + openrtb_ext.NormalizeBidderName, + } + + hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine) + for _, test := range testCases { + deps.accounts = &mockAccountFetcher{data: test.givenAccountData} + resReq, _, _, _, _, _, errL := deps.parseRequest(test.httpReq, &metrics.Labels{}, hookExecutor) + + assert.NotNil(t, resReq, "Result request should not be nil", test.name) + if test.expectedErr == "" { + assert.Nil(t, errL, "Error list should be nil", test.name) + } else { + assert.Len(t, errL, 1, "One error should be returned", test.name) + assert.Contains(t, errL[0].Error(), test.expectedErr, "Incorrect error message", test.name) + } + } +} diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site.json index 60b8d7ecd4f..7a6729f78b6 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site.json @@ -3,7 +3,10 @@ "mockBidRequest": { "id": "some-request-id", "site": { - "page": "test.somepage.com" + "page": "test.somepage.com", + "publisher": { + "id": "some-test-account-id" + } }, "imp": [ { From fcf8b001dc88532fa6d07dbd371194bb5a4bb1d7 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Thu, 25 Jan 2024 14:29:36 +0530 Subject: [PATCH 10/30] fix: use warning for cdep len check --- endpoints/openrtb2/auction.go | 5 ++++- errortypes/code.go | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 2ca02505ef3..6f690942773 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -1925,7 +1925,10 @@ func validateDeviceExt(req *openrtb_ext.RequestWrapper) error { if deviceExt, err := req.GetDeviceExt(); err == nil { if ext := deviceExt.GetExt(); ext != nil { if value, ok := ext["cdep"]; ok && len(string(value)) > 100 { - return errors.New("request.device.ext.cdep must be less than 100 characters") + return &errortypes.Warning{ + Message: "request.device.ext.cdep must be less than 100 characters", + WarningCode: errortypes.SecCookieDeprecationLenWarningCode, + } } } } diff --git a/errortypes/code.go b/errortypes/code.go index 1de5e648cef..803a877be02 100644 --- a/errortypes/code.go +++ b/errortypes/code.go @@ -31,6 +31,7 @@ const ( AdServerTargetingWarningCode BidAdjustmentWarningCode FloorBidRejectionWarningCode + SecCookieDeprecationLenWarningCode ) // Coder provides an error or warning code with severity. From 644f48449e868aa678a454ec9801568fccf1423b Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Tue, 30 Jan 2024 12:02:29 +0530 Subject: [PATCH 11/30] refactor as per review --- config/account.go | 2 +- config/config.go | 5 -- endpoints/cookie_sync.go | 71 +++++++++++++---------------- endpoints/cookie_sync_test.go | 50 +++++--------------- endpoints/openrtb2/auction.go | 24 ++++------ endpoints/openrtb2/auction_test.go | 20 ++++---- openrtb_ext/request_wrapper.go | 34 +++++++++++++- openrtb_ext/request_wrapper_test.go | 42 ++++++++++------- 8 files changed, 121 insertions(+), 127 deletions(-) diff --git a/config/account.go b/config/account.go index dc78b4858bb..4833a679291 100644 --- a/config/account.go +++ b/config/account.go @@ -42,7 +42,6 @@ type Account struct { DefaultBidLimit int `mapstructure:"default_bid_limit" json:"default_bid_limit"` BidAdjustments *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"` Privacy AccountPrivacy `mapstructure:"privacy" json:"privacy"` - Auction Auction `mapstructure:"auction" json:"auction"` } // CookieSync represents the account-level defaults for the cookie sync endpoint. @@ -338,6 +337,7 @@ type AccountPrivacy struct { AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"` IPv6Config IPv6 `mapstructure:"ipv6" json:"ipv6"` IPv4Config IPv4 `mapstructure:"ipv4" json:"ipv4"` + PrivacySandbox PrivacySandbox `mapstructure:"auction" json:"privacysandbox"` } type IPv6 struct { diff --git a/config/config.go b/config/config.go index cdfe6907e29..a693e4d5b0e 100644 --- a/config/config.go +++ b/config/config.go @@ -1285,11 +1285,6 @@ type TmaxAdjustments struct { // PBS won't send a request to the bidder if the bidder tmax calculated is less than the BidderResponseDurationMin value BidderResponseDurationMin uint `mapstructure:"bidder_response_duration_min_ms"` } - -type Auction struct { - PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox"` -} - type PrivacySandbox struct { CookieDeprecation CookieDeprecation `mapstructure:"cookiedeprecation"` } diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index 431fc1b38c8..9eaa56c7277 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -98,10 +98,11 @@ type cookieSyncEndpoint struct { } func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - request, privacyMacros, cookieDeprecation, err := c.parseRequest(r) + request, privacyMacros, account, err := c.parseRequest(r) + c.setCookieDeprecationHeader(w, r, account) if err != nil { c.writeParseRequestErrorMetrics(err) - c.handleError(w, err, http.StatusBadRequest, cookieDeprecation) + c.handleError(w, err, http.StatusBadRequest) return } decoder := usersync.Base64Decoder{} @@ -109,38 +110,32 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht cookie := usersync.ReadCookie(r, decoder, &c.config.HostCookie) usersync.SyncHostCookie(r, cookie, &c.config.HostCookie) - if cookieDeprecation.SetHeader { - if rcd, err := r.Cookie(receiveCookieDeprecation); err == nil && rcd != nil { - cookieDeprecation.SetHeader = false - } - } - result := c.chooser.Choose(request, cookie) switch result.Status { case usersync.StatusBlockedByUserOptOut: c.metrics.RecordCookieSync(metrics.CookieSyncOptOut) - c.handleError(w, errCookieSyncOptOut, http.StatusUnauthorized, cookieDeprecation) + c.handleError(w, errCookieSyncOptOut, http.StatusUnauthorized) case usersync.StatusBlockedByPrivacy: c.metrics.RecordCookieSync(metrics.CookieSyncGDPRHostCookieBlocked) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil, result.BiddersEvaluated, request.Debug, cookieDeprecation) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil, result.BiddersEvaluated, request.Debug) case usersync.StatusOK: c.metrics.RecordCookieSync(metrics.CookieSyncOK) c.writeSyncerMetrics(result.BiddersEvaluated) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen, result.BiddersEvaluated, request.Debug, cookieDeprecation) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen, result.BiddersEvaluated, request.Debug) } } -func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, cookieDeprecation, error) { +func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, *config.Account, error) { defer r.Body.Close() body, err := io.ReadAll(r.Body) if err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, cookieDeprecation{}, errCookieSyncBody + return usersync.Request{}, macros.UserSyncPrivacy{}, nil, errCookieSyncBody } request := cookieSyncRequest{} if err := jsonutil.UnmarshalValid(body, &request); err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, cookieDeprecation{}, fmt.Errorf("JSON parsing failed: %s", err.Error()) + return usersync.Request{}, macros.UserSyncPrivacy{}, nil, fmt.Errorf("JSON parsing failed: %s", err.Error()) } if request.Account == "" { @@ -148,12 +143,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma } account, fetchErrs := accountService.GetAccount(context.Background(), c.config, c.accountsFetcher, request.Account, c.metrics) if len(fetchErrs) > 0 { - return usersync.Request{}, macros.UserSyncPrivacy{}, cookieDeprecation{}, combineErrors(fetchErrs) - } - - cd := cookieDeprecation{ - SetHeader: account.Auction.PrivacySandbox.CookieDeprecation.Enabled, - TTLSec: account.Auction.PrivacySandbox.CookieDeprecation.TTLSec, + return usersync.Request{}, macros.UserSyncPrivacy{}, account, combineErrors(fetchErrs) } request = c.setLimit(request, account.CookieSync) @@ -161,7 +151,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma privacyMacros, gdprSignal, privacyPolicies, err := extractPrivacyPolicies(request, c.privacyConfig.gdprConfig.DefaultValue) if err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, cd, err + return usersync.Request{}, macros.UserSyncPrivacy{}, account, err } ccpaParsedPolicy := ccpa.ParsedPolicy{} @@ -179,7 +169,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma syncTypeFilter, err := parseTypeFilter(request.FilterSettings) if err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, cd, err + return usersync.Request{}, macros.UserSyncPrivacy{}, account, err } gdprRequestInfo := gdpr.RequestInfo{ @@ -208,7 +198,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma SyncTypeFilter: syncTypeFilter, GPPSID: request.GPPSID, } - return rx, privacyMacros, cd, nil + return rx, privacyMacros, account, nil } func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue string) (macros.UserSyncPrivacy, gdpr.Signal, privacy.Policies, error) { @@ -381,8 +371,7 @@ func parseBidderFilter(filter *cookieSyncRequestFilter) (usersync.BidderFilter, } } -func (c *cookieSyncEndpoint) handleError(w http.ResponseWriter, err error, httpStatus int, cookieDeprecation cookieDeprecation) { - c.setCookieDeprecationHeader(w, cookieDeprecation) +func (c *cookieSyncEndpoint) handleError(w http.ResponseWriter, err error, httpStatus int) { http.Error(w, err.Error(), httpStatus) c.pbsAnalytics.LogCookieSyncObject(&analytics.CookieSyncObject{ Status: httpStatus, @@ -425,7 +414,7 @@ func (c *cookieSyncEndpoint) writeSyncerMetrics(biddersEvaluated []usersync.Bidd } } -func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice, biddersEvaluated []usersync.BidderEvaluation, debug bool, cookieDeprecation cookieDeprecation) { +func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice, biddersEvaluated []usersync.BidderEvaluation, debug bool) { status := "no_cookie" if co.HasAnyLiveSyncs() { status = "ok" @@ -480,26 +469,28 @@ func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.S w.Header().Set("Content-Type", "application/json; charset=utf-8") - c.setCookieDeprecationHeader(w, cookieDeprecation) - enc := json.NewEncoder(w) enc.SetEscapeHTML(false) enc.Encode(response) } -func (c *cookieSyncEndpoint) setCookieDeprecationHeader(w http.ResponseWriter, cookieDeprecation cookieDeprecation) { - if cookieDeprecation.SetHeader { - http.SetCookie(w, &http.Cookie{ - Name: receiveCookieDeprecation, - Value: "1", - Secure: true, - HttpOnly: true, - Path: "/", - SameSite: http.SameSiteNoneMode, - // Partition: "", - Expires: c.time.Now().Add(time.Second * time.Duration(cookieDeprecation.TTLSec)), - }) +func (c *cookieSyncEndpoint) setCookieDeprecationHeader(w http.ResponseWriter, r *http.Request, account *config.Account) { + if rcd, err := r.Cookie(receiveCookieDeprecation); err == nil && rcd != nil { + return } + if account == nil || !account.Privacy.PrivacySandbox.CookieDeprecation.Enabled { + return + } + http.SetCookie(w, &http.Cookie{ + Name: receiveCookieDeprecation, + Value: "1", + Secure: true, + HttpOnly: true, + Path: "/", + SameSite: http.SameSiteNoneMode, + // Partition: "", + Expires: c.time.Now().Add(time.Second * time.Duration(account.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)), + }) } func mapBidderStatusToAnalytics(from []cookieSyncResponseBidder) []*analytics.CookieSyncBidder { diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 2271fa68d1e..2e006fc3a61 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -309,7 +309,7 @@ func TestCookieSyncHandle(t *testing.T) { SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, }, givenAccountData: map[string]json.RawMessage{ - "testAccount": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + "testAccount": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), }, expectedStatusCode: 200, expectedBody: `{"status":"ok","bidder_status":[` + @@ -346,7 +346,7 @@ func TestCookieSyncHandle(t *testing.T) { SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, }, givenAccountData: map[string]json.RawMessage{ - "testAccount": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + "testAccount": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), }, expectedStatusCode: 200, expectedBody: `{"status":"ok","bidder_status":[` + @@ -1410,7 +1410,7 @@ func TestWriteParseRequestErrorMetrics(t *testing.T) { writer := httptest.NewRecorder() endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics} - endpoint.handleError(writer, err, 418, cookieDeprecation{}) + endpoint.handleError(writer, err, 418) assert.Equal(t, writer.Code, 418) assert.Equal(t, writer.Body.String(), "anyError\n") @@ -1633,7 +1633,7 @@ func TestCookieSyncHandleError(t *testing.T) { writer := httptest.NewRecorder() endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics} - endpoint.handleError(writer, err, 418, cookieDeprecation{}) + endpoint.handleError(writer, err, 418) assert.Equal(t, writer.Code, 418) assert.Equal(t, writer.Body.String(), "anyError\n") @@ -1749,13 +1749,12 @@ func TestCookieSyncHandleResponse(t *testing.T) { } testCases := []struct { - description string - givenCookieHasSyncs bool - givenSyncersChosen []usersync.SyncerChoice - givenDebug bool - givenCookieDeprecation cookieDeprecation - expectedJSON string - expectedAnalytics analytics.CookieSyncObject + description string + givenCookieHasSyncs bool + givenSyncersChosen []usersync.SyncerChoice + givenDebug bool + expectedJSON string + expectedAnalytics analytics.CookieSyncObject }{ { description: "None", @@ -1839,18 +1838,6 @@ func TestCookieSyncHandleResponse(t *testing.T) { 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{}}, }, - { - description: "SetCookieDeprecationHeader is true, should see receive-cookie-deprecation header in response", - givenCookieHasSyncs: true, - givenDebug: true, - givenSyncersChosen: []usersync.SyncerChoice{}, - givenCookieDeprecation: cookieDeprecation{ - SetHeader: true, - TTLSec: 86400, - }, - 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{}}, - }, } for _, test := range testCases { @@ -1873,22 +1860,7 @@ func TestCookieSyncHandleResponse(t *testing.T) { } else { bidderEval = []usersync.BidderEvaluation{} } - endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug, test.givenCookieDeprecation) - - if assert.Equal(t, writer.Code, http.StatusOK, test.description+":http_status") { - assert.Equal(t, writer.Header().Get("Content-Type"), "application/json; charset=utf-8", test.description+":http_header") - assert.Equal(t, test.expectedJSON, writer.Body.String(), test.description+":http_response") - } - - gotCookie := writer.Header().Get("Set-Cookie") - if test.givenCookieDeprecation.SetHeader { - wantCookieTTL := endpoint.time.Now().Add(time.Second * time.Duration(test.givenCookieDeprecation.TTLSec)).UTC().Format(http.TimeFormat) - wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None", wantCookieTTL) - assert.Equal(t, wantCookie, gotCookie, ":set_cookie_deprecation_header") - } else { - assert.Equal(t, gotCookie, "") - } - + endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug) mockAnalytics.AssertExpectations(t) } } diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 6f690942773..282f8751ce0 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -61,6 +61,8 @@ const storedRequestTimeoutMillis = 50 const ampChannel = "amp" const appChannel = "app" const secCookieDeprecation = "Sec-Cookie-Deprecation" +const cdep = "cdep" +const cdepLen = 100 var ( dntKey string = http.CanonicalHeaderKey("DNT") @@ -1923,16 +1925,13 @@ func validateDevice(req *openrtb_ext.RequestWrapper) error { func validateDeviceExt(req *openrtb_ext.RequestWrapper) error { if deviceExt, err := req.GetDeviceExt(); err == nil { - if ext := deviceExt.GetExt(); ext != nil { - if value, ok := ext["cdep"]; ok && len(string(value)) > 100 { - return &errortypes.Warning{ - Message: "request.device.ext.cdep must be less than 100 characters", - WarningCode: errortypes.SecCookieDeprecationLenWarningCode, - } + if cdep := deviceExt.GetCDep(); len(cdep) > cdepLen { + return &errortypes.Warning{ + Message: "request.device.ext.cdep must be less than 100 characters", + WarningCode: errortypes.SecCookieDeprecationLenWarningCode, } } } - return nil } @@ -2369,24 +2368,19 @@ func setDoNotTrackImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrappe } func setCookieDeprecation(httpReq *http.Request, r *openrtb_ext.RequestWrapper, account *config.Account) { - if account == nil || !account.Auction.PrivacySandbox.CookieDeprecation.Enabled { + if account == nil || !account.Privacy.PrivacySandbox.CookieDeprecation.Enabled { return } - secCookieDeprecation := httpReq.Header.Get(secCookieDeprecation) if secCookieDeprecation == "" { return } - if r.Device == nil { r.Device = &openrtb2.Device{} } - if deviceExt, err := r.GetDeviceExt(); err == nil { - ext := deviceExt.GetExt() - if ext["cdep"] == nil { - ext["cdep"] = json.RawMessage(fmt.Sprintf(`"%s"`, secCookieDeprecation)) - deviceExt.SetExt(ext) + if cdep := deviceExt.GetCDep(); cdep == "" { + deviceExt.SetCDep(secCookieDeprecation) } } } diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 73ea572d358..2970fecd6d8 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6145,7 +6145,7 @@ func TestSetCookieDeprecation(t *testing.T) { BidRequest: &openrtb2.BidRequest{}, }, account: config.Account{ - Auction: config.Auction{ + Privacy: config.AccountPrivacy{ PrivacySandbox: config.PrivacySandbox{ CookieDeprecation: config.CookieDeprecation{ Enabled: false, @@ -6161,7 +6161,7 @@ func TestSetCookieDeprecation(t *testing.T) { args: args{ httpReq: &http.Request{}, account: config.Account{ - Auction: config.Auction{ + Privacy: config.AccountPrivacy{ PrivacySandbox: config.PrivacySandbox{ CookieDeprecation: config.CookieDeprecation{ Enabled: true, @@ -6182,7 +6182,7 @@ func TestSetCookieDeprecation(t *testing.T) { BidRequest: &openrtb2.BidRequest{}, }, account: config.Account{ - Auction: config.Auction{ + Privacy: config.AccountPrivacy{ PrivacySandbox: config.PrivacySandbox{ CookieDeprecation: config.CookieDeprecation{ Enabled: true, @@ -6207,7 +6207,7 @@ func TestSetCookieDeprecation(t *testing.T) { }, }, account: config.Account{ - Auction: config.Auction{ + Privacy: config.AccountPrivacy{ PrivacySandbox: config.PrivacySandbox{ CookieDeprecation: config.CookieDeprecation{ Enabled: true, @@ -6232,7 +6232,7 @@ func TestSetCookieDeprecation(t *testing.T) { }, }, account: config.Account{ - Auction: config.Auction{ + Privacy: config.AccountPrivacy{ PrivacySandbox: config.PrivacySandbox{ CookieDeprecation: config.CookieDeprecation{ Enabled: true, @@ -6291,31 +6291,31 @@ func TestParseRequestCookieDeprecation(t *testing.T) { name: "Request without Sec-Cookie-Deprecation header (cookiedeprecation enabled for account)", httpReq: httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))), givenAccountData: map[string]json.RawMessage{ - "some-test-account-id": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + "some-test-account-id": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), }, expectedErr: "", }, { - name: "Request with Sec-Cookie-Deprecation header having length less than 100 (cookiedeprecation not enabled for account)", + name: "Request with Sec-Cookie-Deprecation header having length less than 100 (cookiedeprecation enabled for account)", httpReq: func() *http.Request { req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) req.Header.Set(secCookieDeprecation, "sample-value") return req }(), givenAccountData: map[string]json.RawMessage{ - "some-test-account-id": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + "some-test-account-id": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), }, expectedErr: "", }, { - name: "Request with Sec-Cookie-Deprecation header having length more than 100 (cookiedeprecation not enabled for account)", + name: "Request with Sec-Cookie-Deprecation header having length more than 100 (cookiedeprecation enabled for account)", httpReq: func() *http.Request { req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) req.Header.Set(secCookieDeprecation, "zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa") return req }(), givenAccountData: map[string]json.RawMessage{ - "some-test-account-id": json.RawMessage(`{"id":"1","auction":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + "some-test-account-id": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), }, expectedErr: "request.device.ext.cdep must be less than 100 characters", }, diff --git a/openrtb_ext/request_wrapper.go b/openrtb_ext/request_wrapper.go index 5adc192d36f..86702b00f6d 100644 --- a/openrtb_ext/request_wrapper.go +++ b/openrtb_ext/request_wrapper.go @@ -59,6 +59,7 @@ const ( dataKey = "data" schainKey = "schain" us_privacyKey = "us_privacy" + cdepKey = "cdep" ) // LenImp returns the number of impressions without causing the creation of ImpWrapper objects. @@ -882,6 +883,8 @@ type DeviceExt struct { extDirty bool prebid *ExtDevicePrebid prebidDirty bool + cdep string + cdepDirty bool } func (de *DeviceExt) unmarshal(extJson json.RawMessage) error { @@ -909,6 +912,13 @@ func (de *DeviceExt) unmarshal(extJson json.RawMessage) error { } } + cdep, hasCDep := de.ext[cdepKey] + if hasCDep && cdep != nil { + if err := jsonutil.Unmarshal(cdep, &de.cdep); err != nil { + return err + } + } + return nil } @@ -930,6 +940,19 @@ func (de *DeviceExt) marshal() (json.RawMessage, error) { de.prebidDirty = false } + if de.cdepDirty { + if len(de.cdep) > 0 { + rawjson, err := jsonutil.Marshal(de.cdep) + if err != nil { + return nil, err + } + de.ext[cdepKey] = rawjson + } else { + delete(de.ext, cdepKey) + } + de.cdepDirty = false + } + de.extDirty = false if len(de.ext) == 0 { return nil, nil @@ -938,7 +961,7 @@ func (de *DeviceExt) marshal() (json.RawMessage, error) { } func (de *DeviceExt) Dirty() bool { - return de.extDirty || de.prebidDirty + return de.extDirty || de.prebidDirty || de.cdepDirty } func (de *DeviceExt) GetExt() map[string]json.RawMessage { @@ -967,6 +990,15 @@ func (de *DeviceExt) SetPrebid(prebid *ExtDevicePrebid) { de.prebidDirty = true } +func (de *DeviceExt) GetCDep() string { + return de.cdep +} + +func (de *DeviceExt) SetCDep(cdep string) { + de.cdep = cdep + de.cdepDirty = true +} + func (de *DeviceExt) Clone() *DeviceExt { if de == nil { return nil diff --git a/openrtb_ext/request_wrapper_test.go b/openrtb_ext/request_wrapper_test.go index bd55c86beb2..e61a1126fed 100644 --- a/openrtb_ext/request_wrapper_test.go +++ b/openrtb_ext/request_wrapper_test.go @@ -767,13 +767,13 @@ func TestRebuildDeviceExt(t *testing.T) { { description: "Nil - Dirty", request: openrtb2.BidRequest{}, - requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true, cdep: "1", cdepDirty: true}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, }, { description: "Nil - Dirty - No Change", request: openrtb2.BidRequest{}, - requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true}, + requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true, cdep: "", cdepDirty: true}, expectedRequest: openrtb2.BidRequest{}, }, { @@ -785,37 +785,37 @@ func TestRebuildDeviceExt(t *testing.T) { { description: "Empty - Dirty", request: openrtb2.BidRequest{Device: &openrtb2.Device{}}, - requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true, cdep: "1", cdepDirty: true}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, }, { description: "Empty - Dirty - No Change", request: openrtb2.BidRequest{Device: &openrtb2.Device{}}, - requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true}, + requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true, cdep: "", cdepDirty: true}, expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{}}, }, { description: "Populated - Not Dirty", - request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, requestDeviceExtWrapper: DeviceExt{}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, }, { description: "Populated - Dirty", - request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, - requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent2, prebidDirty: true}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":2,"minheightperc":0}}}`)}}, + request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent2, prebidDirty: true, cdep: "2", cdepDirty: true}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"2","prebid":{"interstitial":{"minwidthperc":2,"minheightperc":0}}}`)}}, }, { description: "Populated - Dirty - No Change", - request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, - requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true, cdep: "1", cdepDirty: true}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, }, { description: "Populated - Dirty - Cleared", - request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, - requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true}, + request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true, cdep: "", cdepDirty: true}, expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{}}, }, } @@ -1014,6 +1014,8 @@ func TestCloneDeviceExt(t *testing.T) { prebid: &ExtDevicePrebid{ Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0}, }, + cdep: "1", + cdepDirty: true, }, devExtCopy: &DeviceExt{ ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, @@ -1021,6 +1023,8 @@ func TestCloneDeviceExt(t *testing.T) { prebid: &ExtDevicePrebid{ Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0}, }, + cdep: "1", + cdepDirty: true, }, mutator: func(t *testing.T, devExt *DeviceExt) {}, }, @@ -1032,6 +1036,8 @@ func TestCloneDeviceExt(t *testing.T) { prebid: &ExtDevicePrebid{ Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0}, }, + cdep: "1", + cdepDirty: true, }, devExtCopy: &DeviceExt{ ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, @@ -1039,6 +1045,8 @@ func TestCloneDeviceExt(t *testing.T) { prebid: &ExtDevicePrebid{ Interstitial: &ExtDeviceInt{MinWidthPerc: 65, MinHeightPerc: 75}, }, + cdep: "1", + cdepDirty: true, }, mutator: func(t *testing.T, devExt *DeviceExt) { devExt.ext["A"] = json.RawMessage(`"string"`) @@ -1047,6 +1055,8 @@ func TestCloneDeviceExt(t *testing.T) { devExt.prebid.Interstitial.MinHeightPerc = 55 devExt.prebid.Interstitial = &ExtDeviceInt{MinWidthPerc: 80} devExt.prebid = nil + devExt.cdep = "" + devExt.cdepDirty = true }, }, } From 445bb75ea8f9a27663b3dda56aaaac81dbf54364 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Tue, 30 Jan 2024 12:10:54 +0530 Subject: [PATCH 12/30] typo fix --- config/account.go | 2 +- config/config.go | 1 + endpoints/cookie_sync.go | 8 +------- endpoints/cookie_sync_test.go | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/config/account.go b/config/account.go index 4833a679291..50db2cbe712 100644 --- a/config/account.go +++ b/config/account.go @@ -337,7 +337,7 @@ type AccountPrivacy struct { AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"` IPv6Config IPv6 `mapstructure:"ipv6" json:"ipv6"` IPv4Config IPv4 `mapstructure:"ipv4" json:"ipv4"` - PrivacySandbox PrivacySandbox `mapstructure:"auction" json:"privacysandbox"` + PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox" json:"privacysandbox"` } type IPv6 struct { diff --git a/config/config.go b/config/config.go index a693e4d5b0e..7b0019d4c69 100644 --- a/config/config.go +++ b/config/config.go @@ -1285,6 +1285,7 @@ type TmaxAdjustments struct { // PBS won't send a request to the bidder if the bidder tmax calculated is less than the BidderResponseDurationMin value BidderResponseDurationMin uint `mapstructure:"bidder_response_duration_min_ms"` } + type PrivacySandbox struct { CookieDeprecation CookieDeprecation `mapstructure:"cookiedeprecation"` } diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index 9eaa56c7277..b269c406407 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -35,12 +35,6 @@ import ( const receiveCookieDeprecation = "receive-cookie-deprecation" -// cookieDeprecation defines reponse header -type cookieDeprecation struct { - SetHeader bool - TTLSec int -} - var ( errCookieSyncOptOut = errors.New("User has opted out") errCookieSyncBody = errors.New("Failed to read request body") @@ -143,7 +137,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma } account, fetchErrs := accountService.GetAccount(context.Background(), c.config, c.accountsFetcher, request.Account, c.metrics) if len(fetchErrs) > 0 { - return usersync.Request{}, macros.UserSyncPrivacy{}, account, combineErrors(fetchErrs) + return usersync.Request{}, macros.UserSyncPrivacy{}, nil, combineErrors(fetchErrs) } request = c.setLimit(request, account.CookieSync) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 2e006fc3a61..27d70e0b16a 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -1852,7 +1852,7 @@ func TestCookieSyncHandleResponse(t *testing.T) { } writer := httptest.NewRecorder() - endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics, time: &fakeTime{time: time.Now()}} + endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics} var bidderEval []usersync.BidderEvaluation if test.givenDebug { From cf2b4669e4903885d662f94ae09b3fa1a159eb01 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Tue, 30 Jan 2024 12:12:15 +0530 Subject: [PATCH 13/30] revert typo --- endpoints/cookie_sync_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 27d70e0b16a..42106aca5fe 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -1861,6 +1861,11 @@ func TestCookieSyncHandleResponse(t *testing.T) { bidderEval = []usersync.BidderEvaluation{} } endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug) + + if assert.Equal(t, writer.Code, http.StatusOK, test.description+":http_status") { + assert.Equal(t, writer.Header().Get("Content-Type"), "application/json; charset=utf-8", test.description+":http_header") + assert.Equal(t, test.expectedJSON, writer.Body.String(), test.description+":http_response") + } mockAnalytics.AssertExpectations(t) } } From 2a844f617e52cf95ecf4476451785d6795a36ddc Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Mon, 5 Feb 2024 12:03:11 +0530 Subject: [PATCH 14/30] address nitpick pr reviews --- config/account.go | 9 +++++++++ config/config.go | 9 --------- endpoints/openrtb2/auction.go | 7 +------ openrtb_ext/request_wrapper.go | 6 +++--- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/config/account.go b/config/account.go index 50db2cbe712..a31e3bea616 100644 --- a/config/account.go +++ b/config/account.go @@ -340,6 +340,15 @@ type AccountPrivacy struct { PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox" json:"privacysandbox"` } +type PrivacySandbox struct { + CookieDeprecation CookieDeprecation `mapstructure:"cookiedeprecation"` +} + +type CookieDeprecation struct { + Enabled bool `mapstructure:"enabled"` + TTLSec int `mapstructure:"ttl_sec"` +} + type IPv6 struct { AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"` } diff --git a/config/config.go b/config/config.go index 7b0019d4c69..60a541e2ed0 100644 --- a/config/config.go +++ b/config/config.go @@ -1285,12 +1285,3 @@ type TmaxAdjustments struct { // PBS won't send a request to the bidder if the bidder tmax calculated is less than the BidderResponseDurationMin value BidderResponseDurationMin uint `mapstructure:"bidder_response_duration_min_ms"` } - -type PrivacySandbox struct { - CookieDeprecation CookieDeprecation `mapstructure:"cookiedeprecation"` -} - -type CookieDeprecation struct { - Enabled bool `mapstructure:"enabled"` - TTLSec int `mapstructure:"ttlsec"` -} diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 282f8751ce0..c8cfc568c09 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -61,8 +61,6 @@ const storedRequestTimeoutMillis = 50 const ampChannel = "amp" const appChannel = "app" const secCookieDeprecation = "Sec-Cookie-Deprecation" -const cdep = "cdep" -const cdepLen = 100 var ( dntKey string = http.CanonicalHeaderKey("DNT") @@ -1925,7 +1923,7 @@ func validateDevice(req *openrtb_ext.RequestWrapper) error { func validateDeviceExt(req *openrtb_ext.RequestWrapper) error { if deviceExt, err := req.GetDeviceExt(); err == nil { - if cdep := deviceExt.GetCDep(); len(cdep) > cdepLen { + if cdep := deviceExt.GetCDep(); len(cdep) > 100 { return &errortypes.Warning{ Message: "request.device.ext.cdep must be less than 100 characters", WarningCode: errortypes.SecCookieDeprecationLenWarningCode, @@ -2375,9 +2373,6 @@ func setCookieDeprecation(httpReq *http.Request, r *openrtb_ext.RequestWrapper, if secCookieDeprecation == "" { return } - if r.Device == nil { - r.Device = &openrtb2.Device{} - } if deviceExt, err := r.GetDeviceExt(); err == nil { if cdep := deviceExt.GetCDep(); cdep == "" { deviceExt.SetCDep(secCookieDeprecation) diff --git a/openrtb_ext/request_wrapper.go b/openrtb_ext/request_wrapper.go index 86702b00f6d..3cc516729af 100644 --- a/openrtb_ext/request_wrapper.go +++ b/openrtb_ext/request_wrapper.go @@ -912,9 +912,9 @@ func (de *DeviceExt) unmarshal(extJson json.RawMessage) error { } } - cdep, hasCDep := de.ext[cdepKey] - if hasCDep && cdep != nil { - if err := jsonutil.Unmarshal(cdep, &de.cdep); err != nil { + cdepJson, hasCDep := de.ext[cdepKey] + if hasCDep && cdepJson != nil { + if err := jsonutil.Unmarshal(cdepJson, &de.cdep); err != nil { return err } } From 1b1ca069da428ca675efd6408b28a50d59ba6ace Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Thu, 8 Feb 2024 10:48:35 +0530 Subject: [PATCH 15/30] add Partitioned cookie type --- endpoints/cookie_sync.go | 14 +++++++++++--- endpoints/cookie_sync_test.go | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index b269c406407..d0dd96b660a 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -475,16 +475,24 @@ func (c *cookieSyncEndpoint) setCookieDeprecationHeader(w http.ResponseWriter, r if account == nil || !account.Privacy.PrivacySandbox.CookieDeprecation.Enabled { return } - http.SetCookie(w, &http.Cookie{ + cookie := &http.Cookie{ Name: receiveCookieDeprecation, Value: "1", Secure: true, HttpOnly: true, Path: "/", SameSite: http.SameSiteNoneMode, - // Partition: "", + // Partition: true, Expires: c.time.Now().Add(time.Second * time.Duration(account.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)), - }) + } + setCookie(w, cookie) +} + +// setCookie temporary substitute for http.SetCookie(w, cookie) until it supports Partitioned cookie type. Refer https://github.com/golang/go/issues/62490 +func setCookie(w http.ResponseWriter, cookie *http.Cookie) { + if v := cookie.String(); v != "" { + w.Header().Add("Set-Cookie", v+"; Partitioned;") + } } func mapBidderStatusToAnalytics(from []cookieSyncResponseBidder) []*analytics.CookieSyncBidder { diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 42106aca5fe..796ca839d47 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -434,7 +434,7 @@ func TestCookieSyncHandle(t *testing.T) { gotCookie := writer.Header().Get("Set-Cookie") if test.expectedCookieDeprecationHeader { wantCookieTTL := endpoint.time.Now().Add(time.Second * time.Duration(86400)).UTC().Format(http.TimeFormat) - wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None", wantCookieTTL) + wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None; Partitioned;", wantCookieTTL) assert.Equal(t, wantCookie, gotCookie, ":set_cookie_deprecation_header") } else { assert.Equal(t, gotCookie, "") From e443ea5b9dbd523b7d876c4f28557b9e6f2fd382 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Tue, 13 Feb 2024 14:42:15 +0530 Subject: [PATCH 16/30] refactor: validateOrFillCDep --- endpoints/openrtb2/amp_auction.go | 4 +- endpoints/openrtb2/amp_auction_test.go | 2 +- endpoints/openrtb2/auction.go | 72 ++-- endpoints/openrtb2/auction_test.go | 360 +++++++++++++++--- .../valid-whole/supplementary/site.json | 5 +- endpoints/openrtb2/video_auction.go | 4 +- 6 files changed, 347 insertions(+), 100 deletions(-) diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index fe43080a74e..73fa6b9015c 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -231,10 +231,10 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h } // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). - deps.setFieldsImplicitly(r, reqWrapper, account) + deps.setFieldsImplicitly(r, reqWrapper) hasStoredResponses := len(storedAuctionResponses) > 0 - errs := deps.validateRequest(reqWrapper, true, hasStoredResponses, storedBidResponses, false) + errs := deps.validateRequest(account, r, reqWrapper, true, hasStoredResponses, storedBidResponses, false) errL = append(errL, errs...) ao.Errors = append(ao.Errors, errs...) if errortypes.ContainsFatalError(errs) { diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index 990e16421a6..bd56457b3d7 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -1204,7 +1204,7 @@ func TestInitAmpTargetingAndCache(t *testing.T) { func TestQueryParamOverrides(t *testing.T) { requests := map[string]json.RawMessage{ - "1": json.RawMessage(validRequest(t, "site-amp.json")), + "1": json.RawMessage(validRequest(t, "site.json")), } endpoint, _ := NewAmpEndpoint( diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index c8cfc568c09..0949bddc5c7 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -531,7 +531,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric } // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). - deps.setFieldsImplicitly(httpRequest, req, account) + deps.setFieldsImplicitly(httpRequest, req) if err := ortb.SetDefaults(req); err != nil { errs = []error{err} @@ -552,7 +552,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric } hasStoredResponses := len(storedAuctionResponses) > 0 - errL := deps.validateRequest(req, false, hasStoredResponses, storedBidResponses, hasStoredBidRequest) + errL := deps.validateRequest(account, httpRequest, req, false, hasStoredResponses, storedBidResponses, hasStoredBidRequest) if len(errL) > 0 { errs = append(errs, errL...) } @@ -746,7 +746,7 @@ func mergeBidderParamsImpExtPrebid(impExt *openrtb_ext.ImpExt, reqExtParams map[ return nil } -func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp bool, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp, hasStoredBidRequest bool) []error { +func (deps *endpointDeps) validateRequest(account *config.Account, httpReq *http.Request, req *openrtb_ext.RequestWrapper, isAmp bool, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp, hasStoredBidRequest bool) []error { errL := []error{} if req.ID == "" { return []error{errors.New("request missing required field: \"id\"")} @@ -870,7 +870,11 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp } } - if err := validateDevice(req); err != nil { + if err := validateDevice(req.Device); err != nil { + return append(errL, err) + } + + if err := validateOrFillCDep(httpReq, req, account); err != nil { return append(errL, err) } @@ -1898,38 +1902,48 @@ func validateRegs(req *openrtb_ext.RequestWrapper, gpp gpplib.GppContainer) []er return errL } -func validateDevice(req *openrtb_ext.RequestWrapper) error { - if req.Device == nil { +func validateDevice(device *openrtb2.Device) error { + if device == nil { return nil } // The following fields were previously uints in the OpenRTB library we use, but have // since been changed to ints. We decided to maintain the non-negative check. - if req.Device.W < 0 { + if device.W < 0 { return errors.New("request.device.w must be a positive number") } - if req.Device.H < 0 { + if device.H < 0 { return errors.New("request.device.h must be a positive number") } - if req.Device.PPI < 0 { + if device.PPI < 0 { return errors.New("request.device.ppi must be a positive number") } - if req.Device.Geo != nil && req.Device.Geo.Accuracy < 0 { + if device.Geo != nil && device.Geo.Accuracy < 0 { return errors.New("request.device.geo.accuracy must be a positive number") } - return validateDeviceExt(req) + return nil } -func validateDeviceExt(req *openrtb_ext.RequestWrapper) error { - if deviceExt, err := req.GetDeviceExt(); err == nil { - if cdep := deviceExt.GetCDep(); len(cdep) > 100 { - return &errortypes.Warning{ - Message: "request.device.ext.cdep must be less than 100 characters", - WarningCode: errortypes.SecCookieDeprecationLenWarningCode, - } +func validateOrFillCDep(httpReq *http.Request, req *openrtb_ext.RequestWrapper, account *config.Account) error { + if account == nil || !account.Privacy.PrivacySandbox.CookieDeprecation.Enabled { + return nil + } + + secCookieDeprecation := httpReq.Header.Get(secCookieDeprecation) + if secCookieDeprecation == "" { + return nil + } + if len(secCookieDeprecation) > 100 { + return &errortypes.Warning{ + Message: "request.device.ext.cdep must be less than 100 characters", + WarningCode: errortypes.SecCookieDeprecationLenWarningCode, } } + + if deviceExt, err := req.GetDeviceExt(); err == nil { + deviceExt.SetCDep(secCookieDeprecation) + } return nil } @@ -2014,10 +2028,10 @@ func sanitizeRequest(r *openrtb_ext.RequestWrapper, ipValidator iputil.IPValidat // OpenRTB properties from the headers and other implicit info. // // This function _should not_ override any fields which were defined explicitly by the caller in the request. -func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, account *config.Account) { +func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) { sanitizeRequest(r, deps.privateNetworkIPValidator) - setDeviceImplicitly(httpReq, r, deps.privateNetworkIPValidator, account) + setDeviceImplicitly(httpReq, r, deps.privateNetworkIPValidator) // Per the OpenRTB spec: A bid request must not contain more than one of Site|App|DOOH // Assume it's a site request if it's not declared as one of the other values @@ -2029,11 +2043,10 @@ func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, r *openrtb_ } // setDeviceImplicitly uses implicit info from httpReq to populate bidReq.Device -func setDeviceImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, ipValidtor iputil.IPValidator, account *config.Account) { +func setDeviceImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, ipValidtor iputil.IPValidator) { setIPImplicitly(httpReq, r, ipValidtor) setUAImplicitly(httpReq, r) setDoNotTrackImplicitly(httpReq, r) - setCookieDeprecation(httpReq, r, account) } // setAuctionTypeImplicitly sets the auction type to 1 if it wasn't on the request, @@ -2365,21 +2378,6 @@ func setDoNotTrackImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrappe } } -func setCookieDeprecation(httpReq *http.Request, r *openrtb_ext.RequestWrapper, account *config.Account) { - if account == nil || !account.Privacy.PrivacySandbox.CookieDeprecation.Enabled { - return - } - secCookieDeprecation := httpReq.Header.Get(secCookieDeprecation) - if secCookieDeprecation == "" { - return - } - if deviceExt, err := r.GetDeviceExt(); err == nil { - if cdep := deviceExt.GetCDep(); cdep == "" { - deviceExt.SetCDep(secCookieDeprecation) - } - } -} - // Write(return) errors to the client, if any. Returns true if errors were found. func writeError(errs []error, w http.ResponseWriter, labels *metrics.Labels) bool { var rc bool = false diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 2970fecd6d8..000d002a3f1 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -1644,6 +1644,8 @@ func TestValidateRequest(t *testing.T) { description string givenIsAmp bool givenRequestWrapper *openrtb_ext.RequestWrapper + givenHttpRequest *http.Request + givenAccount *config.Account expectedErrorList []error expectedChannelObject *openrtb_ext.ExtRequestPrebidChannel }{ @@ -1864,7 +1866,7 @@ func TestValidateRequest(t *testing.T) { } for _, test := range testCases { - errorList := deps.validateRequest(test.givenRequestWrapper, test.givenIsAmp, false, nil, false) + errorList := deps.validateRequest(test.givenAccount, test.givenHttpRequest, test.givenRequestWrapper, test.givenIsAmp, false, nil, false) assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description) if len(errorList) == 0 { @@ -3047,7 +3049,7 @@ func TestCurrencyTrunc(t *testing.T) { Cur: []string{"USD", "EUR"}, } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedError := errortypes.Warning{Message: "A prebid request can only process one currency. Taking the first currency in the list, USD, as the active currency"} assert.ElementsMatch(t, errL, []error{&expectedError}) @@ -3098,7 +3100,7 @@ func TestCCPAInvalid(t *testing.T) { }, } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedWarning := errortypes.Warning{ Message: "CCPA consent is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)", @@ -3152,7 +3154,7 @@ func TestNoSaleInvalid(t *testing.T) { Ext: json.RawMessage(`{"prebid": {"nosale": ["*", "appnexus"]} }`), } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedError := errors.New("request.ext.prebid.nosale is invalid: can only specify all bidders if no other bidders are provided") assert.ElementsMatch(t, errL, []error{expectedError}) @@ -3204,7 +3206,7 @@ func TestValidateSourceTID(t *testing.T) { }, } - deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) assert.NotEmpty(t, req.Source.TID, "Expected req.Source.TID to be filled with a randomly generated UID") } @@ -3251,7 +3253,7 @@ func TestSChainInvalid(t *testing.T) { Ext: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`), } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedError := errors.New("request.ext.prebid.schains contains multiple schains for bidder appnexus; it must contain no more than one per bidder.") assert.ElementsMatch(t, errL, []error{expectedError}) @@ -3820,7 +3822,7 @@ func TestEidPermissionsInvalid(t *testing.T) { Ext: json.RawMessage(`{"prebid": {"data": {"eidpermissions": [{"source":"a", "bidders":[]}]} } }`), } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedError := errors.New(`request.ext.prebid.data.eidpermissions[0] missing or empty required field: "bidders"`) assert.ElementsMatch(t, errL, []error{expectedError}) @@ -5128,6 +5130,8 @@ func TestValidateStoredResp(t *testing.T) { testCases := []struct { description string givenRequestWrapper *openrtb_ext.RequestWrapper + givenHttpRequest *http.Request + givenAccount *config.Account expectedErrorList []error hasStoredAuctionResponses bool storedBidResponses stored_responses.ImpBidderStoredResp @@ -5669,7 +5673,7 @@ func TestValidateStoredResp(t *testing.T) { for _, test := range testCases { t.Run(test.description, func(t *testing.T) { - errorList := deps.validateRequest(test.givenRequestWrapper, false, test.hasStoredAuctionResponses, test.storedBidResponses, false) + errorList := deps.validateRequest(test.givenAccount, test.givenHttpRequest, test.givenRequestWrapper, false, test.hasStoredAuctionResponses, test.storedBidResponses, false) assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description) }) } @@ -6109,19 +6113,22 @@ func fakeNormalizeBidderName(name string) (openrtb_ext.BidderName, bool) { return openrtb_ext.BidderName(strings.ToLower(name)), true } -func TestSetCookieDeprecation(t *testing.T) { +func TestValidateOrFillCDep(t *testing.T) { type args struct { httpReq *http.Request - r *openrtb_ext.RequestWrapper + req *openrtb_ext.RequestWrapper account config.Account } tests := []struct { name string args args wantDeviceExt json.RawMessage + wantErr error }{ { - name: "account nil", + name: "account nil", + wantDeviceExt: nil, + wantErr: nil, }, { name: "cookie deprecation not enabled", @@ -6129,11 +6136,12 @@ func TestSetCookieDeprecation(t *testing.T) { httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, - r: &openrtb_ext.RequestWrapper{ + req: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{}, }, }, wantDeviceExt: nil, + wantErr: nil, }, { name: "cookie deprecation disabled explicitly", @@ -6141,7 +6149,7 @@ func TestSetCookieDeprecation(t *testing.T) { httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, - r: &openrtb_ext.RequestWrapper{ + req: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{}, }, account: config.Account{ @@ -6155,6 +6163,7 @@ func TestSetCookieDeprecation(t *testing.T) { }, }, wantDeviceExt: nil, + wantErr: nil, }, { name: "Sec-Cookie-Deprecation not present in request", @@ -6171,6 +6180,7 @@ func TestSetCookieDeprecation(t *testing.T) { }, }, wantDeviceExt: nil, + wantErr: nil, }, { name: "Sec-Cookie-Deprecation present in request where request.device.ext is nil", @@ -6178,7 +6188,7 @@ func TestSetCookieDeprecation(t *testing.T) { httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, - r: &openrtb_ext.RequestWrapper{ + req: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{}, }, account: config.Account{ @@ -6192,6 +6202,7 @@ func TestSetCookieDeprecation(t *testing.T) { }, }, wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1"}`), + wantErr: nil, }, { name: "Sec-Cookie-Deprecation present in request where request.device.ext is nil", @@ -6199,7 +6210,7 @@ func TestSetCookieDeprecation(t *testing.T) { httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, - r: &openrtb_ext.RequestWrapper{ + req: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{ Device: &openrtb2.Device{ Ext: nil, @@ -6217,6 +6228,7 @@ func TestSetCookieDeprecation(t *testing.T) { }, }, wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1"}`), + wantErr: nil, }, { name: "Sec-Cookie-Deprecation present in request where request.device.ext is not nil", @@ -6224,7 +6236,7 @@ func TestSetCookieDeprecation(t *testing.T) { httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, }, - r: &openrtb_ext.RequestWrapper{ + req: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{ Device: &openrtb2.Device{ Ext: json.RawMessage(`{"foo":"bar"}`), @@ -6242,15 +6254,46 @@ func TestSetCookieDeprecation(t *testing.T) { }, }, wantDeviceExt: json.RawMessage(`{"foo":"bar","cdep":"example_label_1"}`), + wantErr: nil, + }, + { + name: "Sec-Cookie-Deprecation present in request with length more than 100", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"foo":"bar"}`), + }, + }, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{"foo":"bar"}`), + wantErr: &errortypes.Warning{ + Message: "request.device.ext.cdep must be less than 100 characters", + WarningCode: errortypes.SecCookieDeprecationLenWarningCode, + }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - setCookieDeprecation(tt.args.httpReq, tt.args.r, &tt.args.account) + err := validateOrFillCDep(tt.args.httpReq, tt.args.req, &tt.args.account) + assert.Equal(t, tt.wantErr, err) if tt.wantDeviceExt != nil { - err := tt.args.r.RebuildRequest() + err := tt.args.req.RebuildRequest() assert.NoError(t, err) - assert.JSONEq(t, string(tt.wantDeviceExt), string(tt.args.r.Device.Ext), tt.name) + assert.JSONEq(t, string(tt.wantDeviceExt), string(tt.args.req.Device.Ext), tt.name) } }) } @@ -6259,65 +6302,279 @@ func TestSetCookieDeprecation(t *testing.T) { func TestParseRequestCookieDeprecation(t *testing.T) { testCases := []struct { - name string - httpReq *http.Request - givenAccountData map[string]json.RawMessage - expectedErr string + name string + givenAccount *config.Account + httpReq *http.Request + reqWrapper *openrtb_ext.RequestWrapper + wantErrs []error + wantCDep string }{ { - name: "Request without Sec-Cookie-Deprecation header (cookiedeprecation not enabled for account)", - httpReq: httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))), - expectedErr: "", + name: "Request without Sec-Cookie-Deprecation header (cookiedeprecation not enabled for account)", + httpReq: httptest.NewRequest("POST", "/openrtb2/auction", nil), + givenAccount: &config.Account{ + ID: "1", + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, + TTLSec: 86400, + }, + }, + }, + }, + reqWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + App: &openrtb2.App{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), + }, + }, + }, + }, + wantErrs: []error{}, + wantCDep: "", }, { name: "Request with Sec-Cookie-Deprecation header having length less than 100 (cookiedeprecation not enabled for account)", httpReq: func() *http.Request { - req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) + req := httptest.NewRequest("POST", "/openrtb2/auction", nil) req.Header.Set(secCookieDeprecation, "sample-value") return req }(), - expectedErr: "", + givenAccount: &config.Account{ + ID: "1", + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, + TTLSec: 86400, + }, + }, + }, + }, + reqWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + App: &openrtb2.App{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), + }, + }, + }, + }, + wantErrs: []error{}, + wantCDep: "", }, { name: "Request with Sec-Cookie-Deprecation header having length more than 100 (cookiedeprecation not enabled for account)", httpReq: func() *http.Request { - req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) + req := httptest.NewRequest("POST", "/openrtb2/auction", nil) req.Header.Set(secCookieDeprecation, "zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa") return req }(), - expectedErr: "", + givenAccount: &config.Account{ + ID: "1", + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, + TTLSec: 86400, + }, + }, + }, + }, + reqWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + App: &openrtb2.App{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), + }, + }, + }, + }, + wantErrs: []error{}, + wantCDep: "", }, { name: "Request without Sec-Cookie-Deprecation header (cookiedeprecation enabled for account)", - httpReq: httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))), - givenAccountData: map[string]json.RawMessage{ - "some-test-account-id": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + httpReq: httptest.NewRequest("POST", "/openrtb2/auction", nil), + givenAccount: &config.Account{ + ID: "1", + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, }, - expectedErr: "", + reqWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + App: &openrtb2.App{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), + }, + }, + }, + }, + wantErrs: []error{}, + wantCDep: "", }, { name: "Request with Sec-Cookie-Deprecation header having length less than 100 (cookiedeprecation enabled for account)", httpReq: func() *http.Request { - req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) + req := httptest.NewRequest("POST", "/openrtb2/auction", nil) req.Header.Set(secCookieDeprecation, "sample-value") return req }(), - givenAccountData: map[string]json.RawMessage{ - "some-test-account-id": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + givenAccount: &config.Account{ + ID: "1", + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, }, - expectedErr: "", + reqWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + App: &openrtb2.App{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), + }, + }, + }, + }, + wantErrs: []error{}, + wantCDep: "sample-value", }, { name: "Request with Sec-Cookie-Deprecation header having length more than 100 (cookiedeprecation enabled for account)", httpReq: func() *http.Request { - req := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader([]byte(validRequest(t, "site.json")))) + req := httptest.NewRequest("POST", "/openrtb2/auction", nil) req.Header.Set(secCookieDeprecation, "zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa") return req }(), - givenAccountData: map[string]json.RawMessage{ - "some-test-account-id": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + givenAccount: &config.Account{ + ID: "1", + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, }, - expectedErr: "request.device.ext.cdep must be less than 100 characters", + reqWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + App: &openrtb2.App{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), + }, + }, + }, + }, + wantErrs: []error{ + &errortypes.Warning{ + Message: "request.device.ext.cdep must be less than 100 characters", + WarningCode: errortypes.SecCookieDeprecationLenWarningCode, + }, + }, + wantCDep: "", }, } @@ -6328,7 +6585,7 @@ func TestParseRequestCookieDeprecation(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, &mockAccountFetcher{}, - &config.Configuration{MaxRequestSize: int64(1000)}, + &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, analyticsBuild.New(&config.Analytics{}), map[string]string{}, @@ -6344,17 +6601,12 @@ func TestParseRequestCookieDeprecation(t *testing.T) { openrtb_ext.NormalizeBidderName, } - hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine) for _, test := range testCases { - deps.accounts = &mockAccountFetcher{data: test.givenAccountData} - resReq, _, _, _, _, _, errL := deps.parseRequest(test.httpReq, &metrics.Labels{}, hookExecutor) - - assert.NotNil(t, resReq, "Result request should not be nil", test.name) - if test.expectedErr == "" { - assert.Nil(t, errL, "Error list should be nil", test.name) - } else { - assert.Len(t, errL, 1, "One error should be returned", test.name) - assert.Contains(t, errL[0].Error(), test.expectedErr, "Incorrect error message", test.name) - } + errs := deps.validateRequest(test.givenAccount, test.httpReq, test.reqWrapper, false, false, stored_responses.ImpBidderStoredResp{}, false) + assert.Equal(t, test.wantErrs, errs, test.name) + test.reqWrapper.RebuildRequest() + deviceExt, err := test.reqWrapper.GetDeviceExt() + assert.NoError(t, err) + assert.Equal(t, test.wantCDep, deviceExt.GetCDep()) } } diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site.json index 7a6729f78b6..60b8d7ecd4f 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site.json @@ -3,10 +3,7 @@ "mockBidRequest": { "id": "some-request-id", "site": { - "page": "test.somepage.com", - "publisher": { - "id": "some-test-account-id" - } + "page": "test.somepage.com" }, "imp": [ { diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 20b31762347..e2fd5434405 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -298,9 +298,9 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). - deps.setFieldsImplicitly(r, bidReqWrapper, account) + deps.setFieldsImplicitly(r, bidReqWrapper) - errL = deps.validateRequest(bidReqWrapper, false, false, nil, false) + errL = deps.validateRequest(account, r, bidReqWrapper, false, false, nil, false) if errortypes.ContainsFatalError(errL) { handleError(&labels, w, errL, &vo, &debugLog) return From 3625a9b63925c23ab9984d3129d9704bcde173b9 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Fri, 16 Feb 2024 09:15:18 +0530 Subject: [PATCH 17/30] pr comments --- endpoints/openrtb2/amp_auction.go | 6 +- endpoints/openrtb2/auction.go | 2 +- endpoints/openrtb2/auction_test.go | 186 ++-------------------------- endpoints/openrtb2/video_auction.go | 6 +- 4 files changed, 17 insertions(+), 183 deletions(-) diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index 73fa6b9015c..f1c4fb0e4e2 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -230,9 +230,6 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h return } - // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). - deps.setFieldsImplicitly(r, reqWrapper) - hasStoredResponses := len(storedAuctionResponses) > 0 errs := deps.validateRequest(account, r, reqWrapper, true, hasStoredResponses, storedBidResponses, false) errL = append(errL, errs...) @@ -498,6 +495,9 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr // move to using the request wrapper req = &openrtb_ext.RequestWrapper{BidRequest: reqNormal} + // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). + deps.setFieldsImplicitly(httpRequest, req) + // Need to ensure cache and targeting are turned on e = initAmpTargetingAndCache(req) if errs = append(errs, e...); errortypes.ContainsFatalError(errs) { diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 0949bddc5c7..7451a99a150 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -875,7 +875,7 @@ func (deps *endpointDeps) validateRequest(account *config.Account, httpReq *http } if err := validateOrFillCDep(httpReq, req, account); err != nil { - return append(errL, err) + errL = append(errL, err) } if ccpaPolicy, err := ccpa.ReadFromRequestWrapper(req, gpp); err != nil { diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 000d002a3f1..c739d04144a 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6289,17 +6289,23 @@ func TestValidateOrFillCDep(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := validateOrFillCDep(tt.args.httpReq, tt.args.req, &tt.args.account) - assert.Equal(t, tt.wantErr, err) - if tt.wantDeviceExt != nil { + assert.Equal(t, tt.wantErr, err, tt.name) + if tt.args.req != nil { err := tt.args.req.RebuildRequest() - assert.NoError(t, err) + assert.NoError(t, err, tt.name) + } + if tt.wantDeviceExt == nil { + if tt.args.req != nil && tt.args.req.Device != nil { + assert.Nil(t, tt.args.req.Device.Ext, tt.name) + } + } else { assert.JSONEq(t, string(tt.wantDeviceExt), string(tt.args.req.Device.Ext), tt.name) } }) } } -func TestParseRequestCookieDeprecation(t *testing.T) { +func TestValidateRequestCookieDeprecation(t *testing.T) { testCases := []struct { name string @@ -6309,178 +6315,6 @@ func TestParseRequestCookieDeprecation(t *testing.T) { wantErrs []error wantCDep string }{ - { - name: "Request without Sec-Cookie-Deprecation header (cookiedeprecation not enabled for account)", - httpReq: httptest.NewRequest("POST", "/openrtb2/auction", nil), - givenAccount: &config.Account{ - ID: "1", - Privacy: config.AccountPrivacy{ - PrivacySandbox: config.PrivacySandbox{ - CookieDeprecation: config.CookieDeprecation{ - Enabled: false, - TTLSec: 86400, - }, - }, - }, - }, - reqWrapper: &openrtb_ext.RequestWrapper{ - BidRequest: &openrtb2.BidRequest{ - ID: "Some-ID", - App: &openrtb2.App{}, - Imp: []openrtb2.Imp{ - { - ID: "Some-Imp-ID", - Banner: &openrtb2.Banner{ - Format: []openrtb2.Format{ - { - W: 600, - H: 500, - }, - { - W: 300, - H: 600, - }, - }, - }, - Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), - }, - }, - }, - }, - wantErrs: []error{}, - wantCDep: "", - }, - { - name: "Request with Sec-Cookie-Deprecation header having length less than 100 (cookiedeprecation not enabled for account)", - httpReq: func() *http.Request { - req := httptest.NewRequest("POST", "/openrtb2/auction", nil) - req.Header.Set(secCookieDeprecation, "sample-value") - return req - }(), - givenAccount: &config.Account{ - ID: "1", - Privacy: config.AccountPrivacy{ - PrivacySandbox: config.PrivacySandbox{ - CookieDeprecation: config.CookieDeprecation{ - Enabled: false, - TTLSec: 86400, - }, - }, - }, - }, - reqWrapper: &openrtb_ext.RequestWrapper{ - BidRequest: &openrtb2.BidRequest{ - ID: "Some-ID", - App: &openrtb2.App{}, - Imp: []openrtb2.Imp{ - { - ID: "Some-Imp-ID", - Banner: &openrtb2.Banner{ - Format: []openrtb2.Format{ - { - W: 600, - H: 500, - }, - { - W: 300, - H: 600, - }, - }, - }, - Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), - }, - }, - }, - }, - wantErrs: []error{}, - wantCDep: "", - }, - { - name: "Request with Sec-Cookie-Deprecation header having length more than 100 (cookiedeprecation not enabled for account)", - httpReq: func() *http.Request { - req := httptest.NewRequest("POST", "/openrtb2/auction", nil) - req.Header.Set(secCookieDeprecation, "zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa") - return req - }(), - givenAccount: &config.Account{ - ID: "1", - Privacy: config.AccountPrivacy{ - PrivacySandbox: config.PrivacySandbox{ - CookieDeprecation: config.CookieDeprecation{ - Enabled: false, - TTLSec: 86400, - }, - }, - }, - }, - reqWrapper: &openrtb_ext.RequestWrapper{ - BidRequest: &openrtb2.BidRequest{ - ID: "Some-ID", - App: &openrtb2.App{}, - Imp: []openrtb2.Imp{ - { - ID: "Some-Imp-ID", - Banner: &openrtb2.Banner{ - Format: []openrtb2.Format{ - { - W: 600, - H: 500, - }, - { - W: 300, - H: 600, - }, - }, - }, - Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), - }, - }, - }, - }, - wantErrs: []error{}, - wantCDep: "", - }, - { - name: "Request without Sec-Cookie-Deprecation header (cookiedeprecation enabled for account)", - httpReq: httptest.NewRequest("POST", "/openrtb2/auction", nil), - givenAccount: &config.Account{ - ID: "1", - Privacy: config.AccountPrivacy{ - PrivacySandbox: config.PrivacySandbox{ - CookieDeprecation: config.CookieDeprecation{ - Enabled: true, - TTLSec: 86400, - }, - }, - }, - }, - reqWrapper: &openrtb_ext.RequestWrapper{ - BidRequest: &openrtb2.BidRequest{ - ID: "Some-ID", - App: &openrtb2.App{}, - Imp: []openrtb2.Imp{ - { - ID: "Some-Imp-ID", - Banner: &openrtb2.Banner{ - Format: []openrtb2.Format{ - { - W: 600, - H: 500, - }, - { - W: 300, - H: 600, - }, - }, - }, - Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), - }, - }, - }, - }, - wantErrs: []error{}, - wantCDep: "", - }, { name: "Request with Sec-Cookie-Deprecation header having length less than 100 (cookiedeprecation enabled for account)", httpReq: func() *http.Request { diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index e2fd5434405..da45fdfd8d3 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -259,6 +259,9 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re // all code after this line should use the bidReqWrapper instead of bidReq directly bidReqWrapper := &openrtb_ext.RequestWrapper{BidRequest: bidReq} + // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). + deps.setFieldsImplicitly(r, bidReqWrapper) + if err := ortb.SetDefaults(bidReqWrapper); err != nil { handleError(&labels, w, errL, &vo, &debugLog) return @@ -297,9 +300,6 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re return } - // Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers). - deps.setFieldsImplicitly(r, bidReqWrapper) - errL = deps.validateRequest(account, r, bidReqWrapper, false, false, nil, false) if errortypes.ContainsFatalError(errL) { handleError(&labels, w, errL, &vo, &debugLog) From 05cdf32af0443223aa2b1006bdf0f508013d0623 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Fri, 16 Feb 2024 09:19:28 +0530 Subject: [PATCH 18/30] pr comment --- endpoints/openrtb2/auction.go | 1 + endpoints/openrtb2/auction_test.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 7451a99a150..1a655fba7ac 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -2047,6 +2047,7 @@ func setDeviceImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, i setIPImplicitly(httpReq, r, ipValidtor) setUAImplicitly(httpReq, r) setDoNotTrackImplicitly(httpReq, r) + } // setAuctionTypeImplicitly sets the auction type to 1 if it wasn't on the request, diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index c739d04144a..b672acb911f 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6183,7 +6183,7 @@ func TestValidateOrFillCDep(t *testing.T) { wantErr: nil, }, { - name: "Sec-Cookie-Deprecation present in request where request.device.ext is nil", + name: "Sec-Cookie-Deprecation present in request where request.device is nil", args: args{ httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, From 344de67fddf7dc2d0ff542303e5d08e30492788c Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Fri, 16 Feb 2024 10:49:18 +0530 Subject: [PATCH 19/30] seperate setCookieDeprecationHeader tests --- endpoints/cookie_sync_test.go | 179 +++++++++++++++++++++++++++------- 1 file changed, 142 insertions(+), 37 deletions(-) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 796ca839d47..58d190cb213 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -25,7 +25,6 @@ import ( "github.com/prebid/prebid-server/v2/privacy/ccpa" "github.com/prebid/prebid-server/v2/usersync" "github.com/prebid/prebid-server/v2/util/ptrutil" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -299,42 +298,6 @@ func TestCookieSyncHandle(t *testing.T) { a.On("LogCookieSyncObject", &expected).Once() }, }, - { - description: "CookieDeprecation enabled for account, receive-cookie-deprecation cookie not present in request, expect cookie deprecation header set in response", - givenCookie: cookieWithSyncs, - givenBody: strings.NewReader(`{"account": "testAccount"}`), - givenChooserResult: usersync.Result{ - Status: usersync.StatusOK, - BiddersEvaluated: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusAlreadySynced}}, - SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, - }, - givenAccountData: map[string]json.RawMessage{ - "testAccount": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), - }, - expectedStatusCode: 200, - expectedBody: `{"status":"ok","bidder_status":[` + - `{"bidder":"a","no_cookie":true,"usersync":{"url":"aURL","type":"redirect","supportCORS":true}}` + - `]}` + "\n", - expectedCookieDeprecationHeader: true, - setMetricsExpectations: func(m *metrics.MetricsEngineMock) { - m.On("RecordCookieSync", metrics.CookieSyncOK).Once() - m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncAlreadySynced).Once() - }, - setAnalyticsExpectations: func(a *MockAnalyticsRunner) { - expected := analytics.CookieSyncObject{ - Status: 200, - Errors: nil, - BidderStatus: []*analytics.CookieSyncBidder{ - { - BidderCode: "a", - NoCookie: true, - UsersyncInfo: &analytics.UsersyncInfo{URL: "aURL", Type: "redirect", SupportCORS: true}, - }, - }, - } - a.On("LogCookieSyncObject", &expected).Once() - }, - }, { description: "CookieDeprecation enabled for account, receive-cookie-deprecation cookie present in request, cookie deprecation header not set in response", givenCookie: cookieWithSyncs, @@ -2311,3 +2274,145 @@ func getDefaultActivityConfig(componentName string, allow bool) *config.AccountP }, } } + +func TestSetCookieDeprecationHeader(t *testing.T) { + getTestRequest := func(addCookie bool) *http.Request { + r := httptest.NewRequest("POST", "/cookie_sync", nil) + if addCookie { + r.AddCookie(&http.Cookie{Name: receiveCookieDeprecation, Value: "1"}) + } + return r + } + + type args struct { + w http.ResponseWriter + r *http.Request + account *config.Account + } + tests := []struct { + name string + args args + expectedCookieDeprecationHeader bool + }{ + { + name: "receive-cookie-deprecation not present in request but account is nil", + args: args{ + r: getTestRequest(false), + w: httptest.NewRecorder(), + }, + expectedCookieDeprecationHeader: false, + }, + { + name: "receive-cookie-deprecation not present in request but CookieDeprecation is disabled", + args: args{ + r: getTestRequest(false), + w: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, + }, + }, + }, + }, + }, + expectedCookieDeprecationHeader: false, + }, + { + name: "receive-cookie-deprecation present in request and CookieDeprecation is disabled", + args: args{ + r: getTestRequest(true), + w: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, + }, + }, + }, + }, + }, + expectedCookieDeprecationHeader: false, + }, + { + name: "receive-cookie-deprecation present in request and CookieDeprecation is enabled", + args: args{ + r: getTestRequest(true), + w: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, + }, + }, + expectedCookieDeprecationHeader: false, + }, + { + name: "receive-cookie-deprecation present in request and account is nil", + args: args{ + r: getTestRequest(true), + w: httptest.NewRecorder(), + }, + expectedCookieDeprecationHeader: false, + }, + { + name: "receive-cookie-deprecation not present in request and CookieDeprecation is enabled for account", + args: args{ + r: getTestRequest(false), + w: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, + }, + }, + expectedCookieDeprecationHeader: true, + }, + { + name: "failed to read receive-cookie-deprecation from request but CookieDeprecation is enabled", + args: args{ + r: &http.Request{}, // nil cookie. error: http: named cookie not present + w: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, + }, + }, + expectedCookieDeprecationHeader: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &cookieSyncEndpoint{ + time: &fakeTime{time: time.Now()}, + } + c.setCookieDeprecationHeader(tt.args.w, tt.args.r, tt.args.account) + gotCookie := tt.args.w.Header().Get("Set-Cookie") + if tt.expectedCookieDeprecationHeader { + wantCookieTTL := c.time.Now().Add(time.Second * time.Duration(86400)).UTC().Format(http.TimeFormat) + wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None; Partitioned;", wantCookieTTL) + assert.Equal(t, wantCookie, gotCookie, ":set_cookie_deprecation_header") + } else { + assert.Empty(t, gotCookie, ":set_cookie_deprecation_header") + } + }) + } +} From f9969ab226194d88d1785e4c1ba2edb645741d11 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Thu, 22 Feb 2024 10:12:45 +0530 Subject: [PATCH 20/30] prc --- endpoints/cookie_sync.go | 3 +- endpoints/cookie_sync_test.go | 134 +++++++++++++---------------- endpoints/openrtb2/auction.go | 7 +- endpoints/openrtb2/auction_test.go | 32 ++++++- 4 files changed, 95 insertions(+), 81 deletions(-) diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index f22f2e60f69..6e82ce8bcbc 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -482,8 +482,7 @@ func (c *cookieSyncEndpoint) setCookieDeprecationHeader(w http.ResponseWriter, r HttpOnly: true, Path: "/", SameSite: http.SameSiteNoneMode, - // Partition: true, - Expires: c.time.Now().Add(time.Second * time.Duration(account.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)), + Expires: c.time.Now().Add(time.Second * time.Duration(account.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)), } setCookie(w, cookie) } diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 99f6048ca97..a3500365d97 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -385,7 +385,7 @@ func TestCookieSyncHandle(t *testing.T) { metrics: &mockMetrics, pbsAnalytics: &mockAnalytics, accountsFetcher: &fakeAccountFetcher, - time: &fakeTime{time: time.Now()}, + time: &fakeTime{time: time.Date(2024, 2, 22, 9, 42, 4, 13, time.UTC)}, } assert.NoError(t, endpoint.config.MarshalAccountDefaults()) @@ -398,9 +398,9 @@ func TestCookieSyncHandle(t *testing.T) { if test.expectedCookieDeprecationHeader { wantCookieTTL := endpoint.time.Now().Add(time.Second * time.Duration(86400)).UTC().Format(http.TimeFormat) wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None; Partitioned;", wantCookieTTL) - assert.Equal(t, wantCookie, gotCookie, ":set_cookie_deprecation_header") + assert.Equal(t, wantCookie, gotCookie, test.description) } else { - assert.Equal(t, gotCookie, "") + assert.Empty(t, gotCookie, test.description) } mockMetrics.AssertExpectations(t) @@ -2284,35 +2284,28 @@ func TestSetCookieDeprecationHeader(t *testing.T) { return r } - type args struct { - w http.ResponseWriter - r *http.Request - account *config.Account - } tests := []struct { name string - args args + responseWriter http.ResponseWriter + request *http.Request + account *config.Account expectedCookieDeprecationHeader bool }{ { - name: "receive-cookie-deprecation not present in request but account is nil", - args: args{ - r: getTestRequest(false), - w: httptest.NewRecorder(), - }, + name: "receive-cookie-deprecation not present in request but account is nil", + request: getTestRequest(false), + responseWriter: httptest.NewRecorder(), expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation not present in request but CookieDeprecation is disabled", - args: args{ - r: getTestRequest(false), - w: httptest.NewRecorder(), - account: &config.Account{ - Privacy: config.AccountPrivacy{ - PrivacySandbox: config.PrivacySandbox{ - CookieDeprecation: config.CookieDeprecation{ - Enabled: false, - }, + name: "receive-cookie-deprecation not present in request but CookieDeprecation is disabled", + request: getTestRequest(false), + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, }, }, }, @@ -2320,16 +2313,14 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation present in request and CookieDeprecation is disabled", - args: args{ - r: getTestRequest(true), - w: httptest.NewRecorder(), - account: &config.Account{ - Privacy: config.AccountPrivacy{ - PrivacySandbox: config.PrivacySandbox{ - CookieDeprecation: config.CookieDeprecation{ - Enabled: false, - }, + name: "receive-cookie-deprecation present in request and CookieDeprecation is disabled", + request: getTestRequest(true), + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, }, }, }, @@ -2337,43 +2328,38 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation present in request and CookieDeprecation is enabled", - args: args{ - r: getTestRequest(true), - w: httptest.NewRecorder(), - account: &config.Account{ - Privacy: config.AccountPrivacy{ - PrivacySandbox: config.PrivacySandbox{ - CookieDeprecation: config.CookieDeprecation{ - Enabled: true, - TTLSec: 86400, - }, + name: "receive-cookie-deprecation present in request and CookieDeprecation is enabled", + request: getTestRequest(true), + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, }, }, }, }, + expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation present in request and account is nil", - args: args{ - r: getTestRequest(true), - w: httptest.NewRecorder(), - }, + name: "receive-cookie-deprecation present in request and account is nil", + request: getTestRequest(true), + responseWriter: httptest.NewRecorder(), expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation not present in request and CookieDeprecation is enabled for account", - args: args{ - r: getTestRequest(false), - w: httptest.NewRecorder(), - account: &config.Account{ - Privacy: config.AccountPrivacy{ - PrivacySandbox: config.PrivacySandbox{ - CookieDeprecation: config.CookieDeprecation{ - Enabled: true, - TTLSec: 86400, - }, + name: "receive-cookie-deprecation not present in request and CookieDeprecation is enabled for account", + request: getTestRequest(false), + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, }, }, }, @@ -2381,17 +2367,15 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: true, }, { - name: "failed to read receive-cookie-deprecation from request but CookieDeprecation is enabled", - args: args{ - r: &http.Request{}, // nil cookie. error: http: named cookie not present - w: httptest.NewRecorder(), - account: &config.Account{ - Privacy: config.AccountPrivacy{ - PrivacySandbox: config.PrivacySandbox{ - CookieDeprecation: config.CookieDeprecation{ - Enabled: true, - TTLSec: 86400, - }, + name: "failed to read receive-cookie-deprecation from request but CookieDeprecation is enabled", + request: &http.Request{}, // nil cookie. error: http: named cookie not present + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, }, }, }, @@ -2402,10 +2386,10 @@ func TestSetCookieDeprecationHeader(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &cookieSyncEndpoint{ - time: &fakeTime{time: time.Now()}, + time: &fakeTime{time: time.Date(2024, 2, 22, 9, 42, 4, 13, time.UTC)}, } - c.setCookieDeprecationHeader(tt.args.w, tt.args.r, tt.args.account) - gotCookie := tt.args.w.Header().Get("Set-Cookie") + c.setCookieDeprecationHeader(tt.responseWriter, tt.request, tt.account) + gotCookie := tt.responseWriter.Header().Get("Set-Cookie") if tt.expectedCookieDeprecationHeader { wantCookieTTL := c.time.Now().Add(time.Second * time.Duration(86400)).UTC().Format(http.TimeFormat) wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None; Partitioned;", wantCookieTTL) diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 26301f19021..18a4553a095 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -1943,9 +1943,12 @@ func validateOrFillCDep(httpReq *http.Request, req *openrtb_ext.RequestWrapper, } } - if deviceExt, err := req.GetDeviceExt(); err == nil { - deviceExt.SetCDep(secCookieDeprecation) + deviceExt, err := req.GetDeviceExt() + if err != nil { + return err } + + deviceExt.SetCDep(secCookieDeprecation) return nil } diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 824edde8715..a828158b33f 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6253,7 +6253,7 @@ func TestValidateOrFillCDep(t *testing.T) { }, }, }, - wantDeviceExt: json.RawMessage(`{"foo":"bar","cdep":"example_label_1"}`), + wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1","foo":"bar"}`), wantErr: nil, }, { @@ -6285,6 +6285,34 @@ func TestValidateOrFillCDep(t *testing.T) { WarningCode: errortypes.SecCookieDeprecationLenWarningCode, }, }, + { + name: "Sec-Cookie-Deprecation valid but invalid request.device.ext", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{`), + }, + }, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{`), + wantErr: &errortypes.FailedToUnmarshal{ + Message: "expects \" or n, but found \x00", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -6299,7 +6327,7 @@ func TestValidateOrFillCDep(t *testing.T) { assert.Nil(t, tt.args.req.Device.Ext, tt.name) } } else { - assert.JSONEq(t, string(tt.wantDeviceExt), string(tt.args.req.Device.Ext), tt.name) + assert.Equal(t, string(tt.wantDeviceExt), string(tt.args.req.Device.Ext), tt.name) } }) } From dabc8bb943cd4e5ff9ce497f42ebee44320cd750 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Thu, 22 Feb 2024 11:25:36 +0530 Subject: [PATCH 21/30] prc --- endpoints/cookie_sync_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index a3500365d97..497ea3e982c 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -2292,13 +2292,13 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader bool }{ { - name: "receive-cookie-deprecation not present in request but account is nil", + name: "not-present-account-nil", // receive-cookie-deprecation not present in request but account is nil request: getTestRequest(false), responseWriter: httptest.NewRecorder(), expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation not present in request but CookieDeprecation is disabled", + name: "not-present-cookiedeprecation-disabled", // receive-cookie-deprecation not present in request but CookieDeprecation is disabled request: getTestRequest(false), responseWriter: httptest.NewRecorder(), account: &config.Account{ @@ -2313,7 +2313,7 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation present in request and CookieDeprecation is disabled", + name: "present-cookiedeprecation-disabled", // receive-cookie-deprecation present in request and CookieDeprecation is disabled request: getTestRequest(true), responseWriter: httptest.NewRecorder(), account: &config.Account{ @@ -2328,7 +2328,7 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation present in request and CookieDeprecation is enabled", + name: "present-cookiedeprecation-enabled", // receive-cookie-deprecation present in request and CookieDeprecation is enabled request: getTestRequest(true), responseWriter: httptest.NewRecorder(), account: &config.Account{ @@ -2345,13 +2345,13 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation present in request and account is nil", + name: "present-account-nil", // receive-cookie-deprecation present in request and account is nil request: getTestRequest(true), responseWriter: httptest.NewRecorder(), expectedCookieDeprecationHeader: false, }, { - name: "receive-cookie-deprecation not present in request and CookieDeprecation is enabled for account", + name: "not-present-cookiedeprecation-enabled", // receive-cookie-deprecation not present in request and CookieDeprecation is enabled for account request: getTestRequest(false), responseWriter: httptest.NewRecorder(), account: &config.Account{ @@ -2367,8 +2367,8 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: true, }, { - name: "failed to read receive-cookie-deprecation from request but CookieDeprecation is enabled", - request: &http.Request{}, // nil cookie. error: http: named cookie not present + name: "failed-to-read-cookiedeprecation-enabled", // failed to read receive-cookie-deprecation from request but CookieDeprecation is enabled + request: &http.Request{}, // nil cookie. error: http: named cookie not present responseWriter: httptest.NewRecorder(), account: &config.Account{ Privacy: config.AccountPrivacy{ From fdc2829ecdf019af73419ce3506e7a6844e7984c Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Thu, 22 Feb 2024 11:27:26 +0530 Subject: [PATCH 22/30] prc --- endpoints/cookie_sync_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 497ea3e982c..857e5b29369 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -299,7 +299,7 @@ func TestCookieSyncHandle(t *testing.T) { }, }, { - description: "CookieDeprecation enabled for account, receive-cookie-deprecation cookie present in request, cookie deprecation header not set in response", + description: "CookieDeprecation not set in repsonse" // CookieDeprecation enabled for account, receive-cookie-deprecation cookie present in request, cookie deprecation header not set in response givenCookie: cookieWithSyncs, givenChromeDeprecationCookie: true, givenBody: strings.NewReader(`{"account": "testAccount"}`), From 829c7976e2eed873a1e972639443b9a958b83c9a Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Thu, 22 Feb 2024 11:27:47 +0530 Subject: [PATCH 23/30] typo --- endpoints/cookie_sync_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 857e5b29369..fed350a2339 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -299,7 +299,7 @@ func TestCookieSyncHandle(t *testing.T) { }, }, { - description: "CookieDeprecation not set in repsonse" // CookieDeprecation enabled for account, receive-cookie-deprecation cookie present in request, cookie deprecation header not set in response + description: "CookieDeprecation not set in repsonse", // CookieDeprecation enabled for account, receive-cookie-deprecation cookie present in request, cookie deprecation header not set in response givenCookie: cookieWithSyncs, givenChromeDeprecationCookie: true, givenBody: strings.NewReader(`{"account": "testAccount"}`), From 990faea1aa6b785315dc12288ec5f736a560f280 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Mon, 26 Feb 2024 14:10:58 +0530 Subject: [PATCH 24/30] fix TestAmpBadRequests --- endpoints/openrtb2/amp_auction_test.go | 60 +++++++++++-------- .../amp/invalid-whole/tmax-negative.json | 50 ++++++++++++++++ 2 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 endpoints/openrtb2/sample-requests/amp/invalid-whole/tmax-negative.json diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index 7d87b65301d..b30603c0f8c 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -1028,40 +1028,50 @@ func TestAMPSiteExt(t *testing.T) { // TestBadRequests makes sure we return 400's on bad requests. func TestAmpBadRequests(t *testing.T) { - dir := "sample-requests/invalid-whole" + dir := "sample-requests/amp/invalid-whole" files, err := os.ReadDir(dir) assert.NoError(t, err, "Failed to read folder: %s", dir) - badRequests := make(map[string]json.RawMessage, len(files)) for index, file := range files { - badRequests[strconv.Itoa(100+index)] = readFile(t, "sample-requests/invalid-whole/"+file.Name()) - } + filename := file.Name() + fileData := readFile(t, "sample-requests/amp/invalid-whole/"+filename) - endpoint, _ := NewAmpEndpoint( - fakeUUIDGenerator{}, - &mockAmpExchange{}, - newParamsValidator(t), - &mockAmpStoredReqFetcher{badRequests}, - empty_fetcher.EmptyFetcher{}, - &config.Configuration{MaxRequestSize: maxSize}, - &metricsConfig.NilMetricsEngine{}, - analyticsBuild.New(&config.Analytics{}), - map[string]string{}, - []byte{}, - openrtb_ext.BuildBidderMap(), - empty_fetcher.EmptyFetcher{}, - hooks.EmptyPlanBuilder{}, - nil, - ) - for requestID := range badRequests { - request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=%s", requestID), nil) + test, err := parseTestData(fileData, filename) + if !assert.NoError(t, err) { + return + } + + requestID := strconv.Itoa(100 + index) + + endpoint, _ := NewAmpEndpoint( + fakeUUIDGenerator{}, + &mockAmpExchange{}, + newParamsValidator(t), + &mockAmpStoredReqFetcher{data: map[string]json.RawMessage{ + requestID: test.BidRequest, + }}, + &mockAccountFetcher{ + data: map[string]json.RawMessage{"test_pub": json.RawMessage("{}")}, + }, + &config.Configuration{MaxRequestSize: maxSize}, + &metricsConfig.NilMetricsEngine{}, + analyticsBuild.New(&config.Analytics{}), + map[string]string{}, + []byte{}, + openrtb_ext.BuildBidderMap(), + empty_fetcher.EmptyFetcher{}, + hooks.EmptyPlanBuilder{}, + nil, + ) + + request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?account=test_pub&tag_id=%s", requestID), nil) recorder := httptest.NewRecorder() endpoint(recorder, request, nil) - if recorder.Code != http.StatusBadRequest { - t.Errorf("Expected status %d. Got %d. Input was: %s", http.StatusBadRequest, recorder.Code, fmt.Sprintf("/openrtb2/auction/amp?config=%s", requestID)) - } + response := recorder.Body.String() + assert.Equal(t, test.ExpectedReturnCode, recorder.Code, filename) + assert.Contains(t, response, test.ExpectedErrorMessage, "Actual: %s \nExpected: %s. Filename: %s \n", response, test.ExpectedErrorMessage, filename) } } diff --git a/endpoints/openrtb2/sample-requests/amp/invalid-whole/tmax-negative.json b/endpoints/openrtb2/sample-requests/amp/invalid-whole/tmax-negative.json new file mode 100644 index 00000000000..8fe54bc073a --- /dev/null +++ b/endpoints/openrtb2/sample-requests/amp/invalid-whole/tmax-negative.json @@ -0,0 +1,50 @@ +{ + "description": "Bid request with negative tmax value. Expect error", + "mockBidRequest": { + "tmax": -2, + "id": "some-request-id", + "site": { + "page": "test.somepage.com", + "ext": { + "amp": 1 + } + }, + "imp": [ + { + "id": "my-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "pmp": { + "deals": [ + { + "id": "some-deal-id" + } + ] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + } + } + } + ], + "ext": { + "prebid": { + "targeting": { + "pricegranularity": "low" + }, + "cache": { + "bids": {} + } + } + } + }, + "expectedReturnCode": 400, + "expectedErrorMessage": "Invalid request: request.tmax must be nonnegative. Got -2\n" + } \ No newline at end of file From 5d5cf55e627dee83106c2bcbe5547c733e855f89 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Tue, 27 Feb 2024 00:01:50 +0530 Subject: [PATCH 25/30] Revert "fix TestAmpBadRequests" This reverts commit 990faea1aa6b785315dc12288ec5f736a560f280. --- endpoints/openrtb2/amp_auction_test.go | 60 ++++++++----------- .../amp/invalid-whole/tmax-negative.json | 50 ---------------- 2 files changed, 25 insertions(+), 85 deletions(-) delete mode 100644 endpoints/openrtb2/sample-requests/amp/invalid-whole/tmax-negative.json diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index b30603c0f8c..7d87b65301d 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -1028,50 +1028,40 @@ func TestAMPSiteExt(t *testing.T) { // TestBadRequests makes sure we return 400's on bad requests. func TestAmpBadRequests(t *testing.T) { - dir := "sample-requests/amp/invalid-whole" + dir := "sample-requests/invalid-whole" files, err := os.ReadDir(dir) assert.NoError(t, err, "Failed to read folder: %s", dir) + badRequests := make(map[string]json.RawMessage, len(files)) for index, file := range files { - filename := file.Name() - fileData := readFile(t, "sample-requests/amp/invalid-whole/"+filename) - - test, err := parseTestData(fileData, filename) - if !assert.NoError(t, err) { - return - } - - requestID := strconv.Itoa(100 + index) - - endpoint, _ := NewAmpEndpoint( - fakeUUIDGenerator{}, - &mockAmpExchange{}, - newParamsValidator(t), - &mockAmpStoredReqFetcher{data: map[string]json.RawMessage{ - requestID: test.BidRequest, - }}, - &mockAccountFetcher{ - data: map[string]json.RawMessage{"test_pub": json.RawMessage("{}")}, - }, - &config.Configuration{MaxRequestSize: maxSize}, - &metricsConfig.NilMetricsEngine{}, - analyticsBuild.New(&config.Analytics{}), - map[string]string{}, - []byte{}, - openrtb_ext.BuildBidderMap(), - empty_fetcher.EmptyFetcher{}, - hooks.EmptyPlanBuilder{}, - nil, - ) + badRequests[strconv.Itoa(100+index)] = readFile(t, "sample-requests/invalid-whole/"+file.Name()) + } - request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?account=test_pub&tag_id=%s", requestID), nil) + endpoint, _ := NewAmpEndpoint( + fakeUUIDGenerator{}, + &mockAmpExchange{}, + newParamsValidator(t), + &mockAmpStoredReqFetcher{badRequests}, + empty_fetcher.EmptyFetcher{}, + &config.Configuration{MaxRequestSize: maxSize}, + &metricsConfig.NilMetricsEngine{}, + analyticsBuild.New(&config.Analytics{}), + map[string]string{}, + []byte{}, + openrtb_ext.BuildBidderMap(), + empty_fetcher.EmptyFetcher{}, + hooks.EmptyPlanBuilder{}, + nil, + ) + for requestID := range badRequests { + request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=%s", requestID), nil) recorder := httptest.NewRecorder() endpoint(recorder, request, nil) - response := recorder.Body.String() - assert.Equal(t, test.ExpectedReturnCode, recorder.Code, filename) - assert.Contains(t, response, test.ExpectedErrorMessage, "Actual: %s \nExpected: %s. Filename: %s \n", response, test.ExpectedErrorMessage, filename) + if recorder.Code != http.StatusBadRequest { + t.Errorf("Expected status %d. Got %d. Input was: %s", http.StatusBadRequest, recorder.Code, fmt.Sprintf("/openrtb2/auction/amp?config=%s", requestID)) + } } } diff --git a/endpoints/openrtb2/sample-requests/amp/invalid-whole/tmax-negative.json b/endpoints/openrtb2/sample-requests/amp/invalid-whole/tmax-negative.json deleted file mode 100644 index 8fe54bc073a..00000000000 --- a/endpoints/openrtb2/sample-requests/amp/invalid-whole/tmax-negative.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "description": "Bid request with negative tmax value. Expect error", - "mockBidRequest": { - "tmax": -2, - "id": "some-request-id", - "site": { - "page": "test.somepage.com", - "ext": { - "amp": 1 - } - }, - "imp": [ - { - "id": "my-imp-id", - "banner": { - "format": [ - { - "w": 300, - "h": 600 - } - ] - }, - "pmp": { - "deals": [ - { - "id": "some-deal-id" - } - ] - }, - "ext": { - "appnexus": { - "placementId": 12883451 - } - } - } - ], - "ext": { - "prebid": { - "targeting": { - "pricegranularity": "low" - }, - "cache": { - "bids": {} - } - } - } - }, - "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: request.tmax must be nonnegative. Got -2\n" - } \ No newline at end of file From 0d8360727f960267939a81eb2097af77eb97b748 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Tue, 27 Feb 2024 00:02:26 +0530 Subject: [PATCH 26/30] prc test case names --- endpoints/cookie_sync_test.go | 34 ++++++++++++------------------ endpoints/openrtb2/auction_test.go | 14 ++++++------ 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index fed350a2339..89b6dafc38e 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -123,15 +123,14 @@ func TestCookieSyncHandle(t *testing.T) { testCases := []struct { description string givenCookie *usersync.Cookie - givenChromeDeprecationCookie bool givenBody io.Reader givenChooserResult usersync.Result givenAccountData map[string]json.RawMessage expectedStatusCode int expectedBody string - expectedCookieDeprecationHeader bool setMetricsExpectations func(*metrics.MetricsEngineMock) setAnalyticsExpectations func(*MockAnalyticsRunner) + expectedCookieDeprecationHeader bool }{ { description: "Request With Cookie", @@ -299,10 +298,9 @@ func TestCookieSyncHandle(t *testing.T) { }, }, { - description: "CookieDeprecation not set in repsonse", // CookieDeprecation enabled for account, receive-cookie-deprecation cookie present in request, cookie deprecation header not set in response - givenCookie: cookieWithSyncs, - givenChromeDeprecationCookie: true, - givenBody: strings.NewReader(`{"account": "testAccount"}`), + description: "CookieDeprecation-Set", + givenCookie: cookieWithSyncs, + givenBody: strings.NewReader(`{"account": "testAccount"}`), givenChooserResult: usersync.Result{ Status: usersync.StatusOK, BiddersEvaluated: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusAlreadySynced}}, @@ -311,11 +309,11 @@ func TestCookieSyncHandle(t *testing.T) { givenAccountData: map[string]json.RawMessage{ "testAccount": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), }, - expectedStatusCode: 200, + expectedStatusCode: 200, + expectedCookieDeprecationHeader: true, expectedBody: `{"status":"ok","bidder_status":[` + `{"bidder":"a","no_cookie":true,"usersync":{"url":"aURL","type":"redirect","supportCORS":true}}` + `]}` + "\n", - expectedCookieDeprecationHeader: false, setMetricsExpectations: func(m *metrics.MetricsEngineMock) { m.On("RecordCookieSync", metrics.CookieSyncOK).Once() m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncAlreadySynced).Once() @@ -362,10 +360,6 @@ func TestCookieSyncHandle(t *testing.T) { request.AddCookie(httpCookie) } - if test.givenChromeDeprecationCookie { - request.AddCookie(&http.Cookie{Name: receiveCookieDeprecation, Value: "1"}) - } - writer := httptest.NewRecorder() endpoint := cookieSyncEndpoint{ @@ -2292,13 +2286,13 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader bool }{ { - name: "not-present-account-nil", // receive-cookie-deprecation not present in request but account is nil + name: "not-present-account-nil", request: getTestRequest(false), responseWriter: httptest.NewRecorder(), expectedCookieDeprecationHeader: false, }, { - name: "not-present-cookiedeprecation-disabled", // receive-cookie-deprecation not present in request but CookieDeprecation is disabled + name: "not-present-cookiedeprecation-disabled", request: getTestRequest(false), responseWriter: httptest.NewRecorder(), account: &config.Account{ @@ -2313,7 +2307,7 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: false, }, { - name: "present-cookiedeprecation-disabled", // receive-cookie-deprecation present in request and CookieDeprecation is disabled + name: "present-cookiedeprecation-disabled", request: getTestRequest(true), responseWriter: httptest.NewRecorder(), account: &config.Account{ @@ -2328,7 +2322,7 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: false, }, { - name: "present-cookiedeprecation-enabled", // receive-cookie-deprecation present in request and CookieDeprecation is enabled + name: "present-cookiedeprecation-enabled", request: getTestRequest(true), responseWriter: httptest.NewRecorder(), account: &config.Account{ @@ -2345,13 +2339,13 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: false, }, { - name: "present-account-nil", // receive-cookie-deprecation present in request and account is nil + name: "present-account-nil", request: getTestRequest(true), responseWriter: httptest.NewRecorder(), expectedCookieDeprecationHeader: false, }, { - name: "not-present-cookiedeprecation-enabled", // receive-cookie-deprecation not present in request and CookieDeprecation is enabled for account + name: "not-present-cookiedeprecation-enabled", request: getTestRequest(false), responseWriter: httptest.NewRecorder(), account: &config.Account{ @@ -2367,8 +2361,8 @@ func TestSetCookieDeprecationHeader(t *testing.T) { expectedCookieDeprecationHeader: true, }, { - name: "failed-to-read-cookiedeprecation-enabled", // failed to read receive-cookie-deprecation from request but CookieDeprecation is enabled - request: &http.Request{}, // nil cookie. error: http: named cookie not present + name: "failed-to-read-cookiedeprecation-enabled", + request: &http.Request{}, // nil cookie. error: http: named cookie not present responseWriter: httptest.NewRecorder(), account: &config.Account{ Privacy: config.AccountPrivacy{ diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index a828158b33f..5322720c494 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6317,17 +6317,17 @@ func TestValidateOrFillCDep(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := validateOrFillCDep(tt.args.httpReq, tt.args.req, &tt.args.account) - assert.Equal(t, tt.wantErr, err, tt.name) + assert.Equal(t, tt.wantErr, err) if tt.args.req != nil { err := tt.args.req.RebuildRequest() - assert.NoError(t, err, tt.name) + assert.NoError(t, err) } if tt.wantDeviceExt == nil { if tt.args.req != nil && tt.args.req.Device != nil { - assert.Nil(t, tt.args.req.Device.Ext, tt.name) + assert.Nil(t, tt.args.req.Device.Ext) } } else { - assert.Equal(t, string(tt.wantDeviceExt), string(tt.args.req.Device.Ext), tt.name) + assert.Equal(t, string(tt.wantDeviceExt), string(tt.args.req.Device.Ext)) } }) } @@ -6344,7 +6344,7 @@ func TestValidateRequestCookieDeprecation(t *testing.T) { wantCDep string }{ { - name: "Request with Sec-Cookie-Deprecation header having length less than 100 (cookiedeprecation enabled for account)", + name: "header-with-length-less-than-100", httpReq: func() *http.Request { req := httptest.NewRequest("POST", "/openrtb2/auction", nil) req.Header.Set(secCookieDeprecation, "sample-value") @@ -6389,7 +6389,7 @@ func TestValidateRequestCookieDeprecation(t *testing.T) { wantCDep: "sample-value", }, { - name: "Request with Sec-Cookie-Deprecation header having length more than 100 (cookiedeprecation enabled for account)", + name: "header-with-length-more-than-100", httpReq: func() *http.Request { req := httptest.NewRequest("POST", "/openrtb2/auction", nil) req.Header.Set(secCookieDeprecation, "zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa") @@ -6465,7 +6465,7 @@ func TestValidateRequestCookieDeprecation(t *testing.T) { for _, test := range testCases { errs := deps.validateRequest(test.givenAccount, test.httpReq, test.reqWrapper, false, false, stored_responses.ImpBidderStoredResp{}, false) - assert.Equal(t, test.wantErrs, errs, test.name) + assert.Equal(t, test.wantErrs, errs) test.reqWrapper.RebuildRequest() deviceExt, err := test.reqWrapper.GetDeviceExt() assert.NoError(t, err) From b6068d471976ab0988da2c8d345dd283245d5df8 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Tue, 27 Feb 2024 00:12:37 +0530 Subject: [PATCH 27/30] update testname validateOrFillCDep --- endpoints/openrtb2/auction_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 5322720c494..f78c4122153 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6126,12 +6126,12 @@ func TestValidateOrFillCDep(t *testing.T) { wantErr error }{ { - name: "account nil", + name: "account-nil", wantDeviceExt: nil, wantErr: nil, }, { - name: "cookie deprecation not enabled", + name: "cookie-deprecation-not-enabled", args: args{ httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, @@ -6144,7 +6144,7 @@ func TestValidateOrFillCDep(t *testing.T) { wantErr: nil, }, { - name: "cookie deprecation disabled explicitly", + name: "cookie-deprecation-disabled-explicitly", args: args{ httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, @@ -6166,7 +6166,7 @@ func TestValidateOrFillCDep(t *testing.T) { wantErr: nil, }, { - name: "Sec-Cookie-Deprecation not present in request", + name: "header-not-present-in-request", args: args{ httpReq: &http.Request{}, account: config.Account{ @@ -6183,7 +6183,7 @@ func TestValidateOrFillCDep(t *testing.T) { wantErr: nil, }, { - name: "Sec-Cookie-Deprecation present in request where request.device is nil", + name: "header-present-request-device-nil", args: args{ httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, @@ -6205,7 +6205,7 @@ func TestValidateOrFillCDep(t *testing.T) { wantErr: nil, }, { - name: "Sec-Cookie-Deprecation present in request where request.device.ext is nil", + name: "header-present-request-device-ext-nil", args: args{ httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, @@ -6231,7 +6231,7 @@ func TestValidateOrFillCDep(t *testing.T) { wantErr: nil, }, { - name: "Sec-Cookie-Deprecation present in request where request.device.ext is not nil", + name: "header-present-request-device-ext-not-nil", args: args{ httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, @@ -6257,7 +6257,7 @@ func TestValidateOrFillCDep(t *testing.T) { wantErr: nil, }, { - name: "Sec-Cookie-Deprecation present in request with length more than 100", + name: "header-present-with-length-more-than-100", args: args{ httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa"}}, @@ -6286,7 +6286,7 @@ func TestValidateOrFillCDep(t *testing.T) { }, }, { - name: "Sec-Cookie-Deprecation valid but invalid request.device.ext", + name: "header-present-request-device-ext-invalid", args: args{ httpReq: &http.Request{ Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, From c7bee8e142495c65e7a1d1f29ccdc5676440378a Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Tue, 27 Feb 2024 00:20:18 +0530 Subject: [PATCH 28/30] tn --- endpoints/openrtb2/auction_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index f78c4122153..6a7cd95bb01 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6166,7 +6166,7 @@ func TestValidateOrFillCDep(t *testing.T) { wantErr: nil, }, { - name: "header-not-present-in-request", + name: "cookie-deprecation-enabled-header-not-present-in-request", args: args{ httpReq: &http.Request{}, account: config.Account{ From a09ead09fcc120dfd0116dbc5fd7ef638fe9d928 Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Wed, 28 Feb 2024 10:06:01 +0530 Subject: [PATCH 29/30] prc --- config/config.go | 2 ++ config/config_test.go | 2 ++ endpoints/cookie_sync.go | 6 +++--- endpoints/cookie_sync_test.go | 2 ++ endpoints/openrtb2/auction.go | 14 +++++++++----- endpoints/openrtb2/auction_test.go | 29 +++++++++++++++++++++++++++++ 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/config/config.go b/config/config.go index f0e9f5c7fa7..ac6fc74e1a5 100644 --- a/config/config.go +++ b/config/config.go @@ -1107,6 +1107,8 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("account_defaults.price_floors.fetch.max_age_sec", 86400) v.SetDefault("account_defaults.price_floors.fetch.period_sec", 3600) v.SetDefault("account_defaults.price_floors.fetch.max_schema_dims", 0) + v.SetDefault("account_defaults.privacy.privacysandbox.cookiedeprecation.enabled", false) + v.SetDefault("account_defaults.privacy.privacysandbox.cookiedeprecation.ttl_sec", 604800) v.SetDefault("account_defaults.events_enabled", false) v.SetDefault("account_defaults.privacy.ipv6.anon_keep_bits", 56) diff --git a/config/config_test.go b/config/config_test.go index a551c1be66e..8fe132f51f8 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -203,6 +203,8 @@ func TestDefaults(t *testing.T) { cmpInts(t, "account_defaults.price_floors.fetch.period_sec", 3600, cfg.AccountDefaults.PriceFloors.Fetcher.Period) cmpInts(t, "account_defaults.price_floors.fetch.max_age_sec", 86400, cfg.AccountDefaults.PriceFloors.Fetcher.MaxAge) cmpInts(t, "account_defaults.price_floors.fetch.max_schema_dims", 0, cfg.AccountDefaults.PriceFloors.Fetcher.MaxSchemaDims) + cmpBools(t, "account_defaults.privacy.privacysandbox.cookiedeprecation.enabled", false, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.Enabled) + cmpInts(t, "account_defaults.privacy.privacysandbox.cookiedeprecation.ttl_sec", 604800, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.TTLSec) cmpBools(t, "account_defaults.events.enabled", false, cfg.AccountDefaults.Events.Enabled) diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index 6e82ce8bcbc..d6e0b31a096 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -484,11 +484,11 @@ func (c *cookieSyncEndpoint) setCookieDeprecationHeader(w http.ResponseWriter, r SameSite: http.SameSiteNoneMode, Expires: c.time.Now().Add(time.Second * time.Duration(account.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)), } - setCookie(w, cookie) + setCookiePartitioned(w, cookie) } -// setCookie temporary substitute for http.SetCookie(w, cookie) until it supports Partitioned cookie type. Refer https://github.com/golang/go/issues/62490 -func setCookie(w http.ResponseWriter, cookie *http.Cookie) { +// setCookiePartitioned temporary substitute for http.SetCookie(w, cookie) until it supports Partitioned cookie type. Refer https://github.com/golang/go/issues/62490 +func setCookiePartitioned(w http.ResponseWriter, cookie *http.Cookie) { if v := cookie.String(); v != "" { w.Header().Add("Set-Cookie", v+"; Partitioned;") } diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 89b6dafc38e..35d7dd4baf5 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -2289,6 +2289,7 @@ func TestSetCookieDeprecationHeader(t *testing.T) { name: "not-present-account-nil", request: getTestRequest(false), responseWriter: httptest.NewRecorder(), + account: nil, expectedCookieDeprecationHeader: false, }, { @@ -2342,6 +2343,7 @@ func TestSetCookieDeprecationHeader(t *testing.T) { name: "present-account-nil", request: getTestRequest(true), responseWriter: httptest.NewRecorder(), + account: nil, expectedCookieDeprecationHeader: false, }, { diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 18a4553a095..27423c6322a 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -1932,6 +1932,15 @@ func validateOrFillCDep(httpReq *http.Request, req *openrtb_ext.RequestWrapper, return nil } + deviceExt, err := req.GetDeviceExt() + if err != nil { + return err + } + + if deviceExt.GetCDep() != "" { + return nil + } + secCookieDeprecation := httpReq.Header.Get(secCookieDeprecation) if secCookieDeprecation == "" { return nil @@ -1943,11 +1952,6 @@ func validateOrFillCDep(httpReq *http.Request, req *openrtb_ext.RequestWrapper, } } - deviceExt, err := req.GetDeviceExt() - if err != nil { - return err - } - deviceExt.SetCDep(secCookieDeprecation) return nil } diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 6a7cd95bb01..135409bc5d1 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6169,6 +6169,9 @@ func TestValidateOrFillCDep(t *testing.T) { name: "cookie-deprecation-enabled-header-not-present-in-request", args: args{ httpReq: &http.Request{}, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, account: config.Account{ Privacy: config.AccountPrivacy{ PrivacySandbox: config.PrivacySandbox{ @@ -6285,6 +6288,32 @@ func TestValidateOrFillCDep(t *testing.T) { WarningCode: errortypes.SecCookieDeprecationLenWarningCode, }, }, + { + name: "header-present-request-device-ext-cdep-present", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"foo":"bar","cdep":"example_label_2"}`), + }, + }, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{"foo":"bar","cdep":"example_label_2"}`), + wantErr: nil, + }, { name: "header-present-request-device-ext-invalid", args: args{ From b4e1d3ce7a6536e1a0322fc51da168c3b7a2d7ab Mon Sep 17 00:00:00 2001 From: Nilesh Chate Date: Thu, 29 Feb 2024 08:56:19 +0530 Subject: [PATCH 30/30] prc --- config/config_test.go | 7 +++++++ endpoints/openrtb2/auction.go | 2 +- endpoints/openrtb2/auction_test.go | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/config/config_test.go b/config/config_test.go index 8fe132f51f8..14ef1aeef81 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -505,6 +505,10 @@ account_defaults: anon_keep_bits: 50 ipv4: anon_keep_bits: 20 + privacysandbox: + cookiedeprecation: + enabled: true + ttl_sec: 86400 tmax_adjustments: enabled: true bidder_response_duration_min_ms: 700 @@ -624,6 +628,9 @@ func TestFullConfig(t *testing.T) { cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 50, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits) cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 20, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits) + cmpBools(t, "account_defaults.privacy.cookiedeprecation.enabled", true, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.Enabled) + cmpInts(t, "account_defaults.privacy.cookiedeprecation.ttl_sec", 86400, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.TTLSec) + // Assert compression related defaults cmpBools(t, "compression.request.enable_gzip", true, cfg.Compression.Request.GZIP) cmpBools(t, "compression.response.enable_gzip", false, cfg.Compression.Response.GZIP) diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 27423c6322a..2aafe6808ef 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -1947,7 +1947,7 @@ func validateOrFillCDep(httpReq *http.Request, req *openrtb_ext.RequestWrapper, } if len(secCookieDeprecation) > 100 { return &errortypes.Warning{ - Message: "request.device.ext.cdep must be less than 100 characters", + Message: "request.device.ext.cdep must not exceed 100 characters", WarningCode: errortypes.SecCookieDeprecationLenWarningCode, } } diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 135409bc5d1..8880384ee0d 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -6284,7 +6284,7 @@ func TestValidateOrFillCDep(t *testing.T) { }, wantDeviceExt: json.RawMessage(`{"foo":"bar"}`), wantErr: &errortypes.Warning{ - Message: "request.device.ext.cdep must be less than 100 characters", + Message: "request.device.ext.cdep must not exceed 100 characters", WarningCode: errortypes.SecCookieDeprecationLenWarningCode, }, }, @@ -6461,7 +6461,7 @@ func TestValidateRequestCookieDeprecation(t *testing.T) { }, wantErrs: []error{ &errortypes.Warning{ - Message: "request.device.ext.cdep must be less than 100 characters", + Message: "request.device.ext.cdep must not exceed 100 characters", WarningCode: errortypes.SecCookieDeprecationLenWarningCode, }, },