diff --git a/exchange/utils.go b/exchange/utils.go index 150fac3ca53..dfe1fe448ba 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -21,6 +21,7 @@ import ( "github.com/prebid/prebid-server/v2/gdpr" "github.com/prebid/prebid-server/v2/metrics" "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/ortb" "github.com/prebid/prebid-server/v2/privacy" "github.com/prebid/prebid-server/v2/privacy/ccpa" "github.com/prebid/prebid-server/v2/privacy/lmt" @@ -153,11 +154,6 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, // bidder level privacy policies for _, bidderRequest := range allBidderRequests { - privacyEnforcement := privacy.Enforcement{ - COPPA: coppa, - LMT: lmt, - } - // fetchBids activity scopedName := privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderRequest.BidderName.String()} fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName, privacy.NewRequestFromBidRequest(*req)) @@ -180,46 +176,56 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, } } + ipConf := privacy.IPConf{IPV6: auctionReq.Account.Privacy.IPv6Config, IPV4: auctionReq.Account.Privacy.IPv4Config} + + // FPD should be applied before policies, otherwise it overrides policies and activities restricted data + applyFPD(auctionReq.FirstPartyData, bidderRequest) + + reqWrapper := cloneBidderReq(bidderRequest.BidRequest) + passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName, privacy.NewRequestFromBidRequest(*req)) if !passIDActivityAllowed { - privacyEnforcement.UFPD = true + //UFPD + privacy.ScrubUserFPD(reqWrapper) } else { // run existing policies (GDPR, CCPA, COPPA, LMT) // potentially block passing IDs based on GDPR - if gdprEnforced { - if gdprErr == nil { - privacyEnforcement.GDPRID = !auctionPermissions.PassID - } else { - privacyEnforcement.GDPRID = true - } + if gdprEnforced && (gdprErr != nil || !auctionPermissions.PassID) { + privacy.ScrubGdprID(reqWrapper) } // potentially block passing IDs based on CCPA - privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) + if ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) { + privacy.ScrubDeviceIDsIPsUserDemoExt(reqWrapper, ipConf, "eids", false) + } } passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName, privacy.NewRequestFromBidRequest(*req)) if !passGeoActivityAllowed { - privacyEnforcement.PreciseGeo = true + privacy.ScrubGeoAndDeviceIP(reqWrapper, ipConf) } else { // run existing policies (GDPR, CCPA, COPPA, LMT) // potentially block passing geo based on GDPR - if gdprEnforced { - if gdprErr == nil { - privacyEnforcement.GDPRGeo = !auctionPermissions.PassGeo - } else { - privacyEnforcement.GDPRGeo = true - } + if gdprEnforced && (gdprErr != nil || !auctionPermissions.PassGeo) { + privacy.ScrubGeoAndDeviceIP(reqWrapper, ipConf) } // potentially block passing geo based on CCPA - privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) + if ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) { + privacy.ScrubDeviceIDsIPsUserDemoExt(reqWrapper, ipConf, "eids", false) + } + } + if lmt || coppa { + privacy.ScrubDeviceIDsIPsUserDemoExt(reqWrapper, ipConf, "eids", coppa) } - applyFPD(auctionReq.FirstPartyData, bidderRequest) + passTIDAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitTIDs, scopedName, privacy.NewRequestFromBidRequest(*req)) + if !passTIDAllowed { + privacy.ScrubTID(reqWrapper) + } - privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTIDs, scopedName, privacy.NewRequestFromBidRequest(*req)) + reqWrapper.RebuildRequest() + bidderRequest.BidRequest = reqWrapper.BidRequest - privacyEnforcement.Apply(bidderRequest.BidRequest, auctionReq.Account.Privacy) allowedBidderRequests = append(allowedBidderRequests, bidderRequest) // GPP downgrade: always downgrade unless we can confirm GPP is supported @@ -232,6 +238,34 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, return } +// cloneBidderReq - clones bidder request and replaces req.User and req.Device with new copies +func cloneBidderReq(req *openrtb2.BidRequest) *openrtb_ext.RequestWrapper { + + // bidder request may be modified differently per bidder based on privacy configs + // new request should be created for each bidder request + // pointer fields like User and Device should be cloned and set back to the request copy + var newReq *openrtb2.BidRequest + newReq = ptrutil.Clone(req) + + if req.User != nil { + userCopy := ortb.CloneUser(req.User) + newReq.User = userCopy + } + + if req.Device != nil { + deviceCopy := ortb.CloneDevice(req.Device) + newReq.Device = deviceCopy + } + + if req.Source != nil { + sourceCopy := ortb.CloneSource(req.Source) + newReq.Source = sourceCopy + } + + reqWrapper := &openrtb_ext.RequestWrapper{BidRequest: newReq} + return reqWrapper +} + func shouldSetLegacyPrivacy(bidderInfo config.BidderInfos, bidder string) bool { binfo, defined := bidderInfo[bidder] diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 03184b7402d..1e5213ab0b1 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/mock" ) +const deviceUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36" + // permissionsMock mocks the Permissions interface for tests type permissionsMock struct { allowAllBidders bool @@ -2279,6 +2281,7 @@ func TestCleanOpenRTBRequestsWithOpenRTBDowngrade(t *testing.T) { bidReq.User.ID = "" bidReq.User.BuyerUID = "" bidReq.User.Yob = 0 + bidReq.User.Gender = "" bidReq.User.Geo = &openrtb2.Geo{Lat: 123.46} downgradedRegs := *bidReq.Regs @@ -2536,7 +2539,7 @@ func newAdapterAliasBidRequest(t *testing.T) *openrtb2.BidRequest { }, Device: &openrtb2.Device{ DIDMD5: "some device ID hash", - UA: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", + UA: deviceUA, IFA: "ifa", IP: "132.173.230.74", DNT: &dnt, @@ -2580,11 +2583,17 @@ func newBidRequest(t *testing.T) *openrtb2.BidRequest { }, }, Device: &openrtb2.Device{ - DIDMD5: "some device ID hash", - UA: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", - IFA: "ifa", + UA: deviceUA, IP: "132.173.230.74", Language: "EN", + DIDMD5: "DIDMD5", + IFA: "IFA", + DIDSHA1: "DIDSHA1", + DPIDMD5: "DPIDMD5", + DPIDSHA1: "DPIDSHA1", + MACMD5: "MACMD5", + MACSHA1: "MACSHA1", + Geo: &openrtb2.Geo{Lat: 123.456, Lon: 11.278}, }, Source: &openrtb2.Source{ TID: "testTID", @@ -2593,8 +2602,13 @@ func newBidRequest(t *testing.T) *openrtb2.BidRequest { ID: "our-id", BuyerUID: "their-id", Yob: 1982, - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{Lat: 123.456}, + Gender: "test", + Ext: json.RawMessage(`{"data": 1, "test": 2}`), + Geo: &openrtb2.Geo{Lat: 123.456, Lon: 11.278}, + EIDs: []openrtb2.EID{ + {Source: "eids-source"}, + }, + Data: []openrtb2.Data{{ID: "data-id"}}, }, Imp: []openrtb2.Imp{{ BidFloor: 100, @@ -2624,7 +2638,7 @@ func newBidRequestWithBidderParams(t *testing.T) *openrtb2.BidRequest { }, Device: &openrtb2.Device{ DIDMD5: "some device ID hash", - UA: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", + UA: deviceUA, IFA: "ifa", IP: "132.173.230.74", Language: "EN", @@ -4466,126 +4480,210 @@ func TestGetMediaTypeForBid(t *testing.T) { } func TestCleanOpenRTBRequestsActivities(t *testing.T) { + expectedUserDefault := openrtb2.User{ + ID: "our-id", + BuyerUID: "their-id", + Yob: 1982, + Gender: "test", + Ext: json.RawMessage(`{"data": 1, "test": 2}`), + Geo: &openrtb2.Geo{Lat: 123.456, Lon: 11.278}, + EIDs: []openrtb2.EID{ + {Source: "eids-source"}, + }, + Data: []openrtb2.Data{{ID: "data-id"}}, + } + expectedDeviceDefault := openrtb2.Device{ + UA: deviceUA, + IP: "132.173.230.74", + Language: "EN", + DIDMD5: "DIDMD5", + IFA: "IFA", + DIDSHA1: "DIDSHA1", + DPIDMD5: "DPIDMD5", + DPIDSHA1: "DPIDSHA1", + MACMD5: "MACMD5", + MACSHA1: "MACSHA1", + Geo: &openrtb2.Geo{Lat: 123.456, Lon: 11.278}, + } + + expectedSourceDefault := openrtb2.Source{ + TID: "testTID", + } + testCases := []struct { - name string - req *openrtb2.BidRequest - privacyConfig config.AccountPrivacy - componentName string - allow bool - expectedReqNumber int - expectedUserYOB int64 - expectedUserLat float64 - expectedDeviceDIDMD5 string - expectedSourceTID string + name string + req *openrtb2.BidRequest + privacyConfig config.AccountPrivacy + componentName string + allow bool + expectedReqNumber int + expectedUser openrtb2.User + expectedDevice openrtb2.Device + expectedSource openrtb2.Source + expectedImpExt json.RawMessage }{ { - name: "fetch_bids_request_with_one_bidder_allowed", - req: newBidRequest(t), - privacyConfig: getFetchBidsActivityConfig("appnexus", true), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "fetch_bids_request_with_one_bidder_not_allowed", - req: newBidRequest(t), - privacyConfig: getFetchBidsActivityConfig("appnexus", false), - expectedReqNumber: 0, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_ufpd_allowed", - req: newBidRequest(t), - privacyConfig: getTransmitUFPDActivityConfig("appnexus", true), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_ufpd_deny", - req: newBidRequest(t), - privacyConfig: getTransmitUFPDActivityConfig("appnexus", false), - expectedReqNumber: 1, - expectedUserYOB: 0, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "", - expectedSourceTID: "testTID", - }, - { - name: "transmit_precise_geo_allowed", - req: newBidRequest(t), - privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", true), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_precise_geo_deny", - req: newBidRequest(t), - privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", false), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.46, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_tid_allowed", - req: newBidRequest(t), - privacyConfig: getTransmitTIDActivityConfig("appnexus", true), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_tid_deny", - req: newBidRequest(t), - privacyConfig: getTransmitTIDActivityConfig("appnexus", false), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "", + name: "fetch_bids_request_with_one_bidder_allowed", + req: newBidRequest(t), + privacyConfig: getFetchBidsActivityConfig("appnexus", true), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + name: "fetch_bids_request_with_one_bidder_not_allowed", + req: newBidRequest(t), + privacyConfig: getFetchBidsActivityConfig("appnexus", false), + expectedReqNumber: 0, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + name: "transmit_ufpd_allowed", + req: newBidRequest(t), + privacyConfig: getTransmitUFPDActivityConfig("appnexus", true), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + //remove user.eids, user.ext.data.*, user.data.*, user.{id, buyeruid, yob, gender} + //and device-specific IDs + name: "transmit_ufpd_deny", + req: newBidRequest(t), + privacyConfig: getTransmitUFPDActivityConfig("appnexus", false), + expectedReqNumber: 1, + expectedUser: openrtb2.User{ + ID: "", + BuyerUID: "", + Yob: 0, + Geo: &openrtb2.Geo{Lat: 123.456, Lon: 11.278}, + EIDs: nil, + Ext: json.RawMessage(`{"test":2}`), + Data: nil, + }, + expectedDevice: openrtb2.Device{ + UA: deviceUA, + Language: "EN", + IP: "132.173.230.74", + DIDMD5: "", + IFA: "", + DIDSHA1: "", + DPIDMD5: "", + DPIDSHA1: "", + MACMD5: "", + MACSHA1: "", + Geo: &openrtb2.Geo{Lat: 123.456, Lon: 11.278}, + }, + expectedSource: expectedSourceDefault, + }, + { + name: "transmit_precise_geo_allowed", + req: newBidRequest(t), + privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", true), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + //round user's geographic location by rounding off IP address and lat/lng data. + //this applies to both device.geo and user.geo + name: "transmit_precise_geo_deny", + req: newBidRequest(t), + privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", false), + expectedReqNumber: 1, + expectedUser: openrtb2.User{ + ID: "our-id", + BuyerUID: "their-id", + Yob: 1982, + Geo: &openrtb2.Geo{Lat: 123.46, Lon: 11.28}, + Gender: "test", + Ext: json.RawMessage(`{"data": 1, "test": 2}`), + EIDs: []openrtb2.EID{ + {Source: "eids-source"}, + }, + Data: []openrtb2.Data{{ID: "data-id"}}, + }, + expectedDevice: openrtb2.Device{ + UA: deviceUA, + IP: "132.173.0.0", + Language: "EN", + DIDMD5: "DIDMD5", + IFA: "IFA", + DIDSHA1: "DIDSHA1", + DPIDMD5: "DPIDMD5", + DPIDSHA1: "DPIDSHA1", + MACMD5: "MACMD5", + MACSHA1: "MACSHA1", + Geo: &openrtb2.Geo{Lat: 123.46, Lon: 11.28}, + }, + expectedSource: expectedSourceDefault, + }, + { + name: "transmit_tid_allowed", + req: newBidRequest(t), + privacyConfig: getTransmitTIDActivityConfig("appnexus", true), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + //remove source.tid and imp.ext.tid + name: "transmit_tid_deny", + req: newBidRequest(t), + privacyConfig: getTransmitTIDActivityConfig("appnexus", false), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: openrtb2.Source{ + TID: "", + }, + expectedImpExt: json.RawMessage(`{"bidder": {"placementId": 1}}`), }, } for _, test := range testCases { - activities := privacy.NewActivityControl(&test.privacyConfig) - auctionReq := AuctionRequest{ - BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req}, - UserSyncs: &emptyUsersync{}, - Activities: activities, - } + t.Run(test.name, func(t *testing.T) { + activities := privacy.NewActivityControl(&test.privacyConfig) + auctionReq := AuctionRequest{ + BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req}, + UserSyncs: &emptyUsersync{}, + Activities: activities, + Account: config.Account{Privacy: config.AccountPrivacy{ + IPv6Config: config.IPv6{ + AnonKeepBits: 32, + }, + IPv4Config: config.IPv4{ + AnonKeepBits: 16, + }, + }}, + } - bidderToSyncerKey := map[string]string{} - reqSplitter := &requestSplitter{ - bidderToSyncerKey: bidderToSyncerKey, - me: &metrics.MetricsEngineMock{}, - hostSChainNode: nil, - bidderInfo: config.BidderInfos{}, - } + bidderToSyncerKey := map[string]string{} + reqSplitter := &requestSplitter{ + bidderToSyncerKey: bidderToSyncerKey, + me: &metrics.MetricsEngineMock{}, + hostSChainNode: nil, + bidderInfo: config.BidderInfos{}, + } - t.Run(test.name, func(t *testing.T) { bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{}) assert.Empty(t, errs) assert.Len(t, bidderRequests, test.expectedReqNumber) if test.expectedReqNumber == 1 { - assert.Equal(t, test.expectedUserYOB, bidderRequests[0].BidRequest.User.Yob) - assert.Equal(t, test.expectedUserLat, bidderRequests[0].BidRequest.User.Geo.Lat) - assert.Equal(t, test.expectedDeviceDIDMD5, bidderRequests[0].BidRequest.Device.DIDMD5) - assert.Equal(t, test.expectedSourceTID, bidderRequests[0].BidRequest.Source.TID) + assert.Equal(t, &test.expectedUser, bidderRequests[0].BidRequest.User) + assert.Equal(t, &test.expectedDevice, bidderRequests[0].BidRequest.Device) + assert.Equal(t, &test.expectedSource, bidderRequests[0].BidRequest.Source) + + if len(test.expectedImpExt) > 0 { + assert.JSONEq(t, string(test.expectedImpExt), string(bidderRequests[0].BidRequest.Imp[0].Ext)) + } } }) } diff --git a/ortb/clone.go b/ortb/clone.go index 0dd210a986e..c831aae21b5 100644 --- a/ortb/clone.go +++ b/ortb/clone.go @@ -189,6 +189,140 @@ func CloneUser(s *openrtb2.User) *openrtb2.User { return &c } +func CloneDevice(s *openrtb2.Device) *openrtb2.Device { + if s == nil { + return nil + } + + // Shallow Copy (Value Fields) + c := *s + + // Deep Copy (Pointers) + c.Geo = CloneGeo(s.Geo) + + c.DNT = CloneInt8Pointer(s.DNT) + c.Lmt = CloneInt8Pointer(s.Lmt) + + c.SUA = CloneUserAgent(s.SUA) + if s.ConnectionType != nil { + connectionTypeCopy := s.ConnectionType.Val() + c.ConnectionType = &connectionTypeCopy + } + + c.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneInt8Pointer(s *int8) *int8 { + if s == nil { + return nil + } + var dntCopy int8 + dntCopy = *s + return &dntCopy +} + +func CloneUserAgent(s *openrtb2.UserAgent) *openrtb2.UserAgent { + if s == nil { + return nil + } + + // Shallow Copy (Value Fields) + c := *s + + // Deep Copy (Pointers) + c.Browsers = CloneBrandVersionSlice(s.Browsers) + c.Platform = CloneBrandVersion(s.Platform) + + if s.Mobile != nil { + mobileCopy := *s.Mobile + c.Mobile = &mobileCopy + } + s.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneBrandVersionSlice(s []openrtb2.BrandVersion) []openrtb2.BrandVersion { + if s == nil { + return nil + } + + c := make([]openrtb2.BrandVersion, len(s)) + for i, d := range s { + bv := CloneBrandVersion(&d) + c[i] = *bv + } + + return c +} + +func CloneBrandVersion(s *openrtb2.BrandVersion) *openrtb2.BrandVersion { + if s == nil { + return nil + } + c := *s + + // Deep Copy (Pointers) + c.Version = sliceutil.Clone(s.Version) + c.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneSource(s *openrtb2.Source) *openrtb2.Source { + if s == nil { + return nil + } + + // Shallow Copy (Value Fields) + c := *s + + // Deep Copy (Pointers) + c.SChain = CloneSChain(s.SChain) + c.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneSChain(s *openrtb2.SupplyChain) *openrtb2.SupplyChain { + if s == nil { + return nil + } + // Shallow Copy (Value Fields) + c := *s + + // Deep Copy (Pointers) + c.Nodes = CloneSupplyChainNodes(s.Nodes) + c.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneSupplyChainNodes(s []openrtb2.SupplyChainNode) []openrtb2.SupplyChainNode { + if s == nil { + return nil + } + + c := make([]openrtb2.SupplyChainNode, len(s)) + for i, d := range s { + c[i] = CloneSupplyChainNode(d) + } + + return c +} + +func CloneSupplyChainNode(s openrtb2.SupplyChainNode) openrtb2.SupplyChainNode { + // Shallow Copy (Value Fields) Occurred By Passing Argument By Value + + // Deep Copy (Pointers) + s.HP = CloneInt8Pointer(s.HP) + s.Ext = sliceutil.Clone(s.Ext) + + return s +} + func CloneGeo(s *openrtb2.Geo) *openrtb2.Geo { if s == nil { return nil diff --git a/ortb/clone_test.go b/ortb/clone_test.go index 50dd94057ee..820c24397f4 100644 --- a/ortb/clone_test.go +++ b/ortb/clone_test.go @@ -536,6 +536,384 @@ func TestCloneUser(t *testing.T) { }) } +func TestCloneDevice(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneDevice(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.Device{} + result := CloneDevice(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + var n int8 = 1 + np := &n + ct := adcom1.ConnectionWIFI + + given := &openrtb2.Device{ + Geo: &openrtb2.Geo{Lat: 1.2, Lon: 2.3, Ext: json.RawMessage(`{"geo":1}`)}, + DNT: np, + Lmt: np, + UA: "UserAgent", + SUA: &openrtb2.UserAgent{Mobile: np, Model: "iPad"}, + IP: "127.0.0.1", + IPv6: "2001::", + DeviceType: adcom1.DeviceTablet, + Make: "Apple", + Model: "iPad", + OS: "macOS", + OSV: "1.2.3", + HWV: "mini", + H: 20, + W: 30, + PPI: 100, + PxRatio: 200, + JS: 2, + GeoFetch: 4, + FlashVer: "1.22.33", + Language: "En", + LangB: "ENG", + Carrier: "AT&T", + MCCMNC: "111-222", + ConnectionType: &ct, + IFA: "IFA", + DIDSHA1: "DIDSHA1", + DIDMD5: "DIDMD5", + DPIDSHA1: "DPIDSHA1", + DPIDMD5: "DPIDMD5", + MACSHA1: "MACSHA1", + MACMD5: "MACMD5", + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneDevice(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Geo, result.Geo, "geo") + assert.NotSame(t, given.Geo.Ext, result.Geo.Ext, "geo-ext") + assert.NotSame(t, given.DNT, result.DNT, "dnt") + assert.NotSame(t, given.Lmt, result.Lmt, "lmt") + assert.NotSame(t, given.SUA, result.SUA, "sua") + assert.NotSame(t, given.ConnectionType, result.ConnectionType, "connectionType") + assert.NotSame(t, given.Ext, result.Ext, "ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Device{})), + []string{ + "Geo", + "DNT", + "Lmt", + "SUA", + "ConnectionType", + "Ext", + }) + }) +} + +func TestCloneInt8Pointer(t *testing.T) { + + t.Run("nil", func(t *testing.T) { + result := CloneInt8Pointer(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + var given *int8 + result := CloneInt8Pointer(given) + assert.Nil(t, result) + }) + + t.Run("populated", func(t *testing.T) { + var n int8 = 1 + given := &n + result := CloneInt8Pointer(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + }) +} + +func TestCloneUserAgent(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneUserAgent(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.UserAgent{} + result := CloneUserAgent(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + var n int8 = 1 + np := &n + + given := &openrtb2.UserAgent{ + Browsers: []openrtb2.BrandVersion{{Brand: "Apple"}}, + Platform: &openrtb2.BrandVersion{Brand: "Apple"}, + Mobile: np, + Architecture: "X86", + Bitness: "64", + Model: "iPad", + Source: adcom1.UASourceLowEntropy, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneUserAgent(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Browsers, result.Browsers, "browsers") + assert.NotSame(t, given.Platform, result.Platform, "platform") + assert.NotSame(t, given.Mobile, result.Mobile, "mobile") + assert.NotSame(t, given.Architecture, result.Architecture, "architecture") + assert.NotSame(t, given.Ext, result.Ext, "ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.UserAgent{})), + []string{ + "Browsers", + "Platform", + "Mobile", + "Ext", + }) + }) +} + +func TestCloneBrandVersionSlice(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneBrandVersionSlice(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := []openrtb2.BrandVersion{} + result := CloneBrandVersionSlice(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("one", func(t *testing.T) { + given := []openrtb2.BrandVersion{ + {Brand: "1", Version: []string{"s1", "s2"}, Ext: json.RawMessage(`{"anyField":1}`)}, + } + result := CloneBrandVersionSlice(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given[0], result[0], "item-pointer") + assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext") + }) + + t.Run("many", func(t *testing.T) { + given := []openrtb2.BrandVersion{ + {Brand: "1", Version: []string{"s1", "s2"}, Ext: json.RawMessage(`{"anyField":1}`)}, + {Brand: "2", Version: []string{"s3", "s4"}, Ext: json.RawMessage(`{"anyField":1}`)}, + {Brand: "3", Version: []string{"s5", "s6"}, Ext: json.RawMessage(`{"anyField":1}`)}, + } + result := CloneBrandVersionSlice(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given[0], result[0], "item0-pointer") + assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext") + assert.NotSame(t, given[1], result[1], "item1-pointer") + assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext") + assert.NotSame(t, given[2], result[2], "item1-pointer") + assert.NotSame(t, given[2].Ext, result[2].Ext, "item1-pointer-ext") + }) +} + +func TestCloneBrandVersion(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneBrandVersion(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.BrandVersion{} + result := CloneBrandVersion(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + given := &openrtb2.BrandVersion{ + Brand: "Apple", + Version: []string{"s1"}, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneBrandVersion(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Ext, result.Ext, "ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.BrandVersion{})), + []string{ + "Version", + "Ext", + }) + }) +} + +func TestCloneSource(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneSource(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.Source{} + result := CloneSource(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + + given := &openrtb2.Source{ + FD: 1, + TID: "Tid", + PChain: "PChain", + SChain: &openrtb2.SupplyChain{ + Complete: 1, + Nodes: []openrtb2.SupplyChainNode{ + {ASI: "asi", Ext: json.RawMessage(`{"anyField":1}`)}, + }, + Ext: json.RawMessage(`{"anyField":2}`), + }, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneSource(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.SChain, result.SChain, "schain") + assert.NotSame(t, given.SChain.Ext, result.SChain.Ext, "schain.ext") + assert.NotSame(t, given.Ext, result.Ext, "ext") + assert.NotSame(t, given.SChain.Nodes[0].Ext, result.SChain.Nodes[0].Ext, "schain.nodes.ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Source{})), + []string{ + "SChain", + "Ext", + }) + }) +} + +func TestCloneSChain(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneSource(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.SupplyChain{} + result := CloneSChain(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + given := &openrtb2.SupplyChain{ + Complete: 1, + Nodes: []openrtb2.SupplyChainNode{ + {ASI: "asi", Ext: json.RawMessage(`{"anyField":1}`)}, + }, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneSChain(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Nodes, result.Nodes, "nodes") + assert.NotSame(t, given.Nodes[0].Ext, result.Nodes[0].Ext, "nodes.ext") + assert.NotSame(t, given.Ext, result.Ext, "ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.SupplyChain{})), + []string{ + "Nodes", + "Ext", + }) + }) +} + +func TestCloneSupplyChainNodes(t *testing.T) { + var n int8 = 1 + np := &n + t.Run("nil", func(t *testing.T) { + result := CloneSupplyChainNodes(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := []openrtb2.SupplyChainNode{} + result := CloneSupplyChainNodes(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("one", func(t *testing.T) { + given := []openrtb2.SupplyChainNode{ + {ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)}, + } + result := CloneSupplyChainNodes(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given[0], result[0], "item-pointer") + assert.NotSame(t, given[0].HP, result[0].HP, "item-pointer-hp") + assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext") + }) + + t.Run("many", func(t *testing.T) { + given := []openrtb2.SupplyChainNode{ + {ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)}, + {ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)}, + {ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)}, + } + result := CloneSupplyChainNodes(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given[0], result[0], "item0-pointer") + assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext") + assert.NotSame(t, given[0].HP, result[0].HP, "item0-pointer-hp") + assert.NotSame(t, given[1], result[1], "item1-pointer") + assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext") + assert.NotSame(t, given[1].HP, result[1].HP, "item1-pointer-hp") + assert.NotSame(t, given[2], result[2], "item2-pointer") + assert.NotSame(t, given[2].Ext, result[2].Ext, "item2-pointer-ext") + assert.NotSame(t, given[2].HP, result[2].HP, "item2-pointer-hp") + }) +} + +func TestCloneSupplyChainNode(t *testing.T) { + t.Run("populated", func(t *testing.T) { + var n int8 = 1 + np := &n + + given := openrtb2.SupplyChainNode{ + ASI: "asi", + HP: np, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneSupplyChainNode(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Ext, result.Ext, "ext") + assert.NotSame(t, given.HP, result.HP, "hp") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.SupplyChainNode{})), + []string{ + "HP", + "Ext", + }) + }) +} + func TestCloneGeo(t *testing.T) { t.Run("nil", func(t *testing.T) { result := CloneGeo(nil) diff --git a/privacy/enforcement.go b/privacy/enforcement.go deleted file mode 100644 index 901c062ea4e..00000000000 --- a/privacy/enforcement.go +++ /dev/null @@ -1,92 +0,0 @@ -package privacy - -import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/v2/config" -) - -// Enforcement represents the privacy policies to enforce for an OpenRTB bid request. -type Enforcement struct { - CCPA bool - COPPA bool - GDPRGeo bool - GDPRID bool - LMT bool - - // activities - UFPD bool - Eids bool - PreciseGeo bool - TID bool -} - -// Any returns true if at least one privacy policy requires enforcement. -func (e Enforcement) AnyLegacy() bool { - return e.CCPA || e.COPPA || e.GDPRGeo || e.GDPRID || e.LMT -} - -func (e Enforcement) AnyActivities() bool { - return e.UFPD || e.PreciseGeo || e.Eids || e.TID -} - -// Apply cleans personally identifiable information from an OpenRTB bid request. -func (e Enforcement) Apply(bidRequest *openrtb2.BidRequest, privacy config.AccountPrivacy) { - e.apply(bidRequest, NewScrubber(privacy.IPv6Config, privacy.IPv4Config)) -} - -func (e Enforcement) apply(bidRequest *openrtb2.BidRequest, scrubber Scrubber) { - if bidRequest != nil { - if e.AnyActivities() { - bidRequest = scrubber.ScrubRequest(bidRequest, e) - } - if e.AnyLegacy() && !(e.UFPD && e.PreciseGeo && e.Eids) { - bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getUserScrubStrategy(), e.getGeoScrubStrategy()) - } - if e.AnyLegacy() && !(e.UFPD && e.PreciseGeo) { - bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getDeviceIDScrubStrategy(), e.getIPv4ScrubStrategy(), e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy()) - } - } -} - -func (e Enforcement) getDeviceIDScrubStrategy() ScrubStrategyDeviceID { - if e.COPPA || e.GDPRID || e.CCPA || e.LMT { - return ScrubStrategyDeviceIDAll - } - - return ScrubStrategyDeviceIDNone -} - -func (e Enforcement) getIPv4ScrubStrategy() ScrubStrategyIPV4 { - if e.COPPA || e.GDPRGeo || e.CCPA || e.LMT { - return ScrubStrategyIPV4Subnet - } - - return ScrubStrategyIPV4None -} - -func (e Enforcement) getIPv6ScrubStrategy() ScrubStrategyIPV6 { - if e.GDPRGeo || e.CCPA || e.LMT || e.COPPA { - return ScrubStrategyIPV6Subnet - } - return ScrubStrategyIPV6None -} - -func (e Enforcement) getGeoScrubStrategy() ScrubStrategyGeo { - if e.COPPA { - return ScrubStrategyGeoFull - } - - if e.GDPRGeo || e.CCPA || e.LMT { - return ScrubStrategyGeoReducedPrecision - } - - return ScrubStrategyGeoNone -} - -func (e Enforcement) getUserScrubStrategy() ScrubStrategyUser { - if e.COPPA || e.CCPA || e.LMT || e.GDPRID { - return ScrubStrategyUserIDAndDemographic - } - - return ScrubStrategyUserNone -} diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go deleted file mode 100644 index a97779eb903..00000000000 --- a/privacy/enforcement_test.go +++ /dev/null @@ -1,393 +0,0 @@ -package privacy - -import ( - "testing" - - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestAnyLegacy(t *testing.T) { - testCases := []struct { - enforcement Enforcement - expected bool - description string - }{ - { - description: "All False", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: false, - GDPRID: false, - LMT: false, - }, - expected: false, - }, - { - description: "All True", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - }, - expected: true, - }, - { - description: "Mixed", - enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPRGeo: false, - GDPRID: false, - LMT: true, - }, - expected: true, - }, - } - - for _, test := range testCases { - result := test.enforcement.AnyLegacy() - assert.Equal(t, test.expected, result, test.description) - } -} - -func TestApplyGDPR(t *testing.T) { - testCases := []struct { - description string - enforcement Enforcement - expectedDeviceID ScrubStrategyDeviceID - expectedDeviceIPv4 ScrubStrategyIPV4 - expectedDeviceIPv6 ScrubStrategyIPV6 - expectedDeviceGeo ScrubStrategyGeo - expectedUser ScrubStrategyUser - expectedUserGeo ScrubStrategyGeo - }{ - { - description: "All Enforced", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoFull, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoFull, - }, - { - description: "CCPA Only", - enforcement: Enforcement{ - CCPA: true, - COPPA: false, - GDPRGeo: false, - GDPRID: false, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "COPPA Only", - enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPRGeo: false, - GDPRID: false, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoFull, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoFull, - }, - { - description: "GDPR Only - Full", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: true, - GDPRID: true, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "GDPR Only - ID Only", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: false, - GDPRID: true, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4None, - expectedDeviceIPv6: ScrubStrategyIPV6None, - expectedDeviceGeo: ScrubStrategyGeoNone, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoNone, - }, - { - description: "GDPR Only - Geo Only", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: true, - GDPRID: false, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDNone, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserNone, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "LMT Only", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: false, - GDPRID: false, - LMT: true, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "Interactions: COPPA + GDPR Full", - enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoFull, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoFull, - }, - } - - for _, test := range testCases { - req := &openrtb2.BidRequest{ - Device: &openrtb2.Device{}, - User: &openrtb2.User{}, - } - replacedDevice := &openrtb2.Device{} - replacedUser := &openrtb2.User{} - - m := &mockScrubber{} - m.On("ScrubDevice", req.Device, test.expectedDeviceID, test.expectedDeviceIPv4, test.expectedDeviceIPv6, test.expectedDeviceGeo).Return(replacedDevice).Once() - m.On("ScrubUser", req.User, test.expectedUser, test.expectedUserGeo).Return(replacedUser).Once() - - test.enforcement.apply(req, m) - - m.AssertExpectations(t) - assert.Same(t, replacedDevice, req.Device, "Device") - assert.Same(t, replacedUser, req.User, "User") - } -} - -func TestApplyToggle(t *testing.T) { - testCases := []struct { - description string - enforcement Enforcement - expectedScrubRequestExecuted bool - expectedScrubUserExecuted bool - expectedScrubDeviceExecuted bool - }{ - { - description: "All enforced - only ScrubRequest execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: true, - Eids: true, - PreciseGeo: true, - TID: true, - }, - expectedScrubRequestExecuted: true, - expectedScrubUserExecuted: false, - expectedScrubDeviceExecuted: false, - }, - { - description: "All Legacy and no activities - ScrubUser and ScrubDevice execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: false, - Eids: false, - PreciseGeo: false, - TID: false, - }, - expectedScrubRequestExecuted: false, - expectedScrubUserExecuted: true, - expectedScrubDeviceExecuted: true, - }, - { - description: "Some Legacy and some activities - ScrubRequest, ScrubUser and ScrubDevice execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: true, - Eids: false, - PreciseGeo: false, - TID: false, - }, - expectedScrubRequestExecuted: true, - expectedScrubUserExecuted: true, - expectedScrubDeviceExecuted: true, - }, - { - description: "Some Legacy and some activities - ScrubRequest execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: true, - Eids: true, - PreciseGeo: true, - TID: false, - }, - expectedScrubRequestExecuted: true, - expectedScrubUserExecuted: false, - expectedScrubDeviceExecuted: false, - }, - { - description: "Some Legacy and some activities overlap - ScrubRequest and ScrubUser execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: true, - Eids: false, - PreciseGeo: true, - TID: false, - }, - expectedScrubRequestExecuted: true, - expectedScrubUserExecuted: true, - expectedScrubDeviceExecuted: false, - }, - } - - for _, test := range testCases { - t.Run(test.description, func(t *testing.T) { - req := &openrtb2.BidRequest{ - Device: &openrtb2.Device{}, - User: &openrtb2.User{}, - } - replacedDevice := &openrtb2.Device{} - replacedUser := &openrtb2.User{} - - m := &mockScrubber{} - - if test.expectedScrubRequestExecuted { - m.On("ScrubRequest", req, test.enforcement).Return(req).Once() - } - if test.expectedScrubUserExecuted { - m.On("ScrubUser", req.User, ScrubStrategyUserIDAndDemographic, ScrubStrategyGeoFull).Return(replacedUser).Once() - } - if test.expectedScrubDeviceExecuted { - m.On("ScrubDevice", req.Device, ScrubStrategyDeviceIDAll, ScrubStrategyIPV4Subnet, ScrubStrategyIPV6Subnet, ScrubStrategyGeoFull).Return(replacedDevice).Once() - } - - test.enforcement.apply(req, m) - - m.AssertExpectations(t) - - }) - } -} - -func TestApplyNoneApplicable(t *testing.T) { - req := &openrtb2.BidRequest{} - - m := &mockScrubber{} - - enforcement := Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: false, - GDPRID: false, - LMT: false, - - UFPD: false, - PreciseGeo: false, - TID: false, - Eids: false, - } - enforcement.apply(req, m) - - m.AssertNotCalled(t, "ScrubDevice") - m.AssertNotCalled(t, "ScrubUser") -} - -func TestApplyNil(t *testing.T) { - m := &mockScrubber{} - - enforcement := Enforcement{} - enforcement.apply(nil, m) - - m.AssertNotCalled(t, "ScrubDevice") - m.AssertNotCalled(t, "ScrubUser") -} - -type mockScrubber struct { - mock.Mock -} - -func (m *mockScrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest { - args := m.Called(bidRequest, enforcement) - return args.Get(0).(*openrtb2.BidRequest) -} - -func (m *mockScrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device { - args := m.Called(device, id, ipv4, ipv6, geo) - return args.Get(0).(*openrtb2.Device) -} - -func (m *mockScrubber) ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User { - args := m.Called(user, strategy, geo) - return args.Get(0).(*openrtb2.User) -} diff --git a/privacy/scrubber.go b/privacy/scrubber.go index 54941669ab9..ba1f8a9478b 100644 --- a/privacy/scrubber.go +++ b/privacy/scrubber.go @@ -2,252 +2,147 @@ package privacy import ( "encoding/json" + "github.com/prebid/prebid-server/v2/util/jsonutil" "net" + "github.com/prebid/openrtb/v19/openrtb2" "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/prebid/prebid-server/v2/util/iputil" - "github.com/prebid/prebid-server/v2/util/jsonutil" - "github.com/prebid/prebid-server/v2/util/ptrutil" - - "github.com/prebid/openrtb/v19/openrtb2" -) - -// ScrubStrategyIPV4 defines the approach to scrub PII from an IPV4 address. -type ScrubStrategyIPV4 int - -const ( - // ScrubStrategyIPV4None does not remove any part of an IPV4 address. - ScrubStrategyIPV4None ScrubStrategyIPV4 = iota - - // ScrubStrategyIPV4Subnet zeroes out the last 8 bits of an IPV4 address. - ScrubStrategyIPV4Subnet -) - -// ScrubStrategyIPV6 defines the approach to scrub PII from an IPV6 address. -type ScrubStrategyIPV6 int - -const ( - // ScrubStrategyIPV6None does not remove any part of an IPV6 address. - ScrubStrategyIPV6None ScrubStrategyIPV6 = iota - - // ScrubStrategyIPV6Subnet zeroes out the last 16 bits of an IPV6 sub net address. - ScrubStrategyIPV6Subnet -) - -// ScrubStrategyGeo defines the approach to scrub PII from geographical data. -type ScrubStrategyGeo int - -const ( - // ScrubStrategyGeoNone does not remove any geographical data. - ScrubStrategyGeoNone ScrubStrategyGeo = iota - - // ScrubStrategyGeoFull removes all geographical data. - ScrubStrategyGeoFull - - // ScrubStrategyGeoReducedPrecision anonymizes geographical data with rounding. - ScrubStrategyGeoReducedPrecision -) - -// ScrubStrategyUser defines the approach to scrub PII from user data. -type ScrubStrategyUser int - -const ( - // ScrubStrategyUserNone does not remove non-location data. - ScrubStrategyUserNone ScrubStrategyUser = iota - - // ScrubStrategyUserIDAndDemographic removes the user's buyer id, exchange id year of birth, and gender. - ScrubStrategyUserIDAndDemographic -) - -// ScrubStrategyDeviceID defines the approach to remove hardware id and device id data. -type ScrubStrategyDeviceID int - -const ( - // ScrubStrategyDeviceIDNone does not remove hardware id and device id data. - ScrubStrategyDeviceIDNone ScrubStrategyDeviceID = iota - - // ScrubStrategyDeviceIDAll removes all hardware and device id data (ifa, mac hashes device id hashes) - ScrubStrategyDeviceIDAll ) -// Scrubber removes PII from parts of an OpenRTB request. -type Scrubber interface { - ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest - ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device - ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User +type IPConf struct { + IPV6 config.IPv6 + IPV4 config.IPv4 } -type scrubber struct { - ipV6 config.IPv6 - ipV4 config.IPv4 +func scrubDeviceIDs(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.Device != nil { + reqWrapper.Device.DIDMD5 = "" + reqWrapper.Device.DIDSHA1 = "" + reqWrapper.Device.DPIDMD5 = "" + reqWrapper.Device.DPIDSHA1 = "" + reqWrapper.Device.IFA = "" + reqWrapper.Device.MACMD5 = "" + reqWrapper.Device.MACSHA1 = "" + } } -// NewScrubber returns an OpenRTB scrubber. -func NewScrubber(ipV6 config.IPv6, ipV4 config.IPv4) Scrubber { - return scrubber{ - ipV6: ipV6, - ipV4: ipV4, +func scrubUserIDs(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.User != nil { + reqWrapper.User.Data = nil + reqWrapper.User.ID = "" + reqWrapper.User.BuyerUID = "" + reqWrapper.User.Yob = 0 + reqWrapper.User.Gender = "" + reqWrapper.User.Keywords = "" + reqWrapper.User.KwArray = nil } } -func (s scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest { - var userExtParsed map[string]json.RawMessage - userExtModified := false - - // expressed in two lines because IntelliJ cannot infer the generic type - var userCopy *openrtb2.User - userCopy = ptrutil.Clone(bidRequest.User) - - // expressed in two lines because IntelliJ cannot infer the generic type - var deviceCopy *openrtb2.Device - deviceCopy = ptrutil.Clone(bidRequest.Device) - - if userCopy != nil && (enforcement.UFPD || enforcement.Eids) { - if len(userCopy.Ext) != 0 { - jsonutil.Unmarshal(userCopy.Ext, &userExtParsed) - } +func scrubUserDemographics(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.User != nil { + reqWrapper.User.BuyerUID = "" + reqWrapper.User.ID = "" + reqWrapper.User.Yob = 0 + reqWrapper.User.Gender = "" } +} - if enforcement.UFPD { - // transmitUfpd covers user.ext.data, user.data, user.id, user.buyeruid, user.yob, user.gender, user.keywords, user.kwarray - // and device.{ifa, macsha1, macmd5, dpidsha1, dpidmd5, didsha1, didmd5} - if deviceCopy != nil { - deviceCopy.DIDMD5 = "" - deviceCopy.DIDSHA1 = "" - deviceCopy.DPIDMD5 = "" - deviceCopy.DPIDSHA1 = "" - deviceCopy.IFA = "" - deviceCopy.MACMD5 = "" - deviceCopy.MACSHA1 = "" - } - if userCopy != nil { - userCopy.Data = nil - userCopy.ID = "" - userCopy.BuyerUID = "" - userCopy.Yob = 0 - userCopy.Gender = "" - userCopy.Keywords = "" - userCopy.KwArray = nil - - _, hasField := userExtParsed["data"] - if hasField { - delete(userExtParsed, "data") - userExtModified = true - } +func scrubUserExt(reqWrapper *openrtb_ext.RequestWrapper, fieldName string) error { + if reqWrapper.User != nil { + userExt, err := reqWrapper.GetUserExt() + if err != nil { + return err } - } - if enforcement.Eids { - //transmitEids covers user.eids and user.ext.eids - if userCopy != nil { - userCopy.EIDs = nil - _, hasField := userExtParsed["eids"] - if hasField { - delete(userExtParsed, "eids") - userExtModified = true - } + ext := userExt.GetExt() + _, hasField := ext[fieldName] + if hasField { + delete(ext, fieldName) + userExt.SetExt(ext) } } + return nil +} - if userExtModified { - userExt, _ := jsonutil.Marshal(userExtParsed) - userCopy.Ext = userExt +func ScrubEIDs(reqWrapper *openrtb_ext.RequestWrapper) error { + //transmitEids removes user.eids and user.ext.eids + if reqWrapper.User != nil { + reqWrapper.User.EIDs = nil } + return scrubUserExt(reqWrapper, "eids") +} - if enforcement.TID { - //remove source.tid and imp.ext.tid - if bidRequest.Source != nil { - sourceCopy := ptrutil.Clone(bidRequest.Source) - sourceCopy.TID = "" - bidRequest.Source = sourceCopy - } - for ind, imp := range bidRequest.Imp { - impExt := scrubExtIDs(imp.Ext, "tid") - bidRequest.Imp[ind].Ext = impExt - } +func ScrubTID(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.Source != nil { + reqWrapper.Source.TID = "" } - - if enforcement.PreciseGeo { - //round user's geographic location by rounding off IP address and lat/lng data. - //this applies to both device.geo and user.geo - if userCopy != nil && userCopy.Geo != nil { - userCopy.Geo = scrubGeoPrecision(userCopy.Geo) - } - - if deviceCopy != nil { - if deviceCopy.Geo != nil { - deviceCopy.Geo = scrubGeoPrecision(deviceCopy.Geo) - } - deviceCopy.IP = scrubIP(deviceCopy.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize) - deviceCopy.IPv6 = scrubIP(deviceCopy.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize) - } + impWrapper := reqWrapper.GetImp() + for ind, imp := range impWrapper { + impExt := scrubExtIDs(imp.Ext, "tid") + impWrapper[ind].Ext = impExt } - - bidRequest.Device = deviceCopy - bidRequest.User = userCopy - return bidRequest + reqWrapper.SetImp(impWrapper) } -func (s scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device { - if device == nil { - return nil - } - - deviceCopy := *device - - switch id { - case ScrubStrategyDeviceIDAll: - deviceCopy.DIDMD5 = "" - deviceCopy.DIDSHA1 = "" - deviceCopy.DPIDMD5 = "" - deviceCopy.DPIDSHA1 = "" - deviceCopy.IFA = "" - deviceCopy.MACMD5 = "" - deviceCopy.MACSHA1 = "" +func scrubGEO(reqWrapper *openrtb_ext.RequestWrapper) { + //round user's geographic location by rounding off IP address and lat/lng data. + //this applies to both device.geo and user.geo + if reqWrapper.User != nil && reqWrapper.User.Geo != nil { + reqWrapper.User.Geo = scrubGeoPrecision(reqWrapper.User.Geo) } - switch ipv4 { - case ScrubStrategyIPV4Subnet: - deviceCopy.IP = scrubIP(device.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize) + if reqWrapper.Device != nil && reqWrapper.Device.Geo != nil { + reqWrapper.Device.Geo = scrubGeoPrecision(reqWrapper.Device.Geo) } +} - switch ipv6 { - case ScrubStrategyIPV6Subnet: - deviceCopy.IPv6 = scrubIP(device.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize) +func scrubGeoFull(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.User != nil && reqWrapper.User.Geo != nil { + reqWrapper.User.Geo = &openrtb2.Geo{} } - - switch geo { - case ScrubStrategyGeoFull: - deviceCopy.Geo = scrubGeoFull(device.Geo) - case ScrubStrategyGeoReducedPrecision: - deviceCopy.Geo = scrubGeoPrecision(device.Geo) + if reqWrapper.Device != nil && reqWrapper.Device.Geo != nil { + reqWrapper.Device.Geo = &openrtb2.Geo{} } - return &deviceCopy } -func (scrubber) ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User { - if user == nil { - return nil +func scrubDeviceIP(reqWrapper *openrtb_ext.RequestWrapper, ipConf IPConf) { + if reqWrapper.Device != nil { + reqWrapper.Device.IP = scrubIP(reqWrapper.Device.IP, ipConf.IPV4.AnonKeepBits, iputil.IPv4BitSize) + reqWrapper.Device.IPv6 = scrubIP(reqWrapper.Device.IPv6, ipConf.IPV6.AnonKeepBits, iputil.IPv6BitSize) } +} - userCopy := *user +func ScrubDeviceIDsIPsUserDemoExt(reqWrapper *openrtb_ext.RequestWrapper, ipConf IPConf, fieldName string, scrubFullGeo bool) { + scrubDeviceIDs(reqWrapper) + scrubDeviceIP(reqWrapper, ipConf) + scrubUserDemographics(reqWrapper) + scrubUserExt(reqWrapper, fieldName) - if strategy == ScrubStrategyUserIDAndDemographic { - userCopy.BuyerUID = "" - userCopy.ID = "" - userCopy.Ext = scrubExtIDs(userCopy.Ext, "eids") - userCopy.Yob = 0 - userCopy.Gender = "" + if scrubFullGeo { + scrubGeoFull(reqWrapper) + } else { + scrubGEO(reqWrapper) } +} - switch geo { - case ScrubStrategyGeoFull: - userCopy.Geo = scrubGeoFull(user.Geo) - case ScrubStrategyGeoReducedPrecision: - userCopy.Geo = scrubGeoPrecision(user.Geo) - } +func ScrubUserFPD(reqWrapper *openrtb_ext.RequestWrapper) { + scrubDeviceIDs(reqWrapper) + scrubUserIDs(reqWrapper) + scrubUserExt(reqWrapper, "data") + reqWrapper.User.EIDs = nil +} - return &userCopy +func ScrubGdprID(reqWrapper *openrtb_ext.RequestWrapper) { + scrubDeviceIDs(reqWrapper) + scrubUserDemographics(reqWrapper) + scrubUserExt(reqWrapper, "eids") +} + +func ScrubGeoAndDeviceIP(reqWrapper *openrtb_ext.RequestWrapper, ipConf IPConf) { + scrubDeviceIP(reqWrapper, ipConf) + scrubGEO(reqWrapper) } func scrubIP(ip string, ones, bits int) string { @@ -259,14 +154,6 @@ func scrubIP(ip string, ones, bits int) string { return ipMasked.String() } -func scrubGeoFull(geo *openrtb2.Geo) *openrtb2.Geo { - if geo == nil { - return nil - } - - return &openrtb2.Geo{} -} - func scrubGeoPrecision(geo *openrtb2.Geo) *openrtb2.Geo { if geo == nil { return nil diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go index 299acd9eba3..1fb88874d43 100644 --- a/privacy/scrubber_test.go +++ b/privacy/scrubber_test.go @@ -4,453 +4,347 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/v2/config" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) -func TestScrubDevice(t *testing.T) { - device := getTestDevice() - +func TestScrubDeviceIDs(t *testing.T) { testCases := []struct { - description string - expected *openrtb2.Device - id ScrubStrategyDeviceID - ipv4 ScrubStrategyIPV4 - ipv6 ScrubStrategyIPV6 - geo ScrubStrategyGeo + name string + deviceIn *openrtb2.Device + expectedDevice *openrtb2.Device }{ { - description: "All Strategies - None", - expected: device, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoNone, - }, - { - description: "All Strategies - Strictest", - expected: &openrtb2.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.0", - IPv6: "2001:1db8:2233:4400::", - Geo: &openrtb2.Geo{}, - }, - id: ScrubStrategyDeviceIDAll, - ipv4: ScrubStrategyIPV4Subnet, - ipv6: ScrubStrategyIPV6Subnet, - geo: ScrubStrategyGeoFull, - }, - { - description: "Isolated - ID - All", - expected: &openrtb2.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: device.Geo, - }, - id: ScrubStrategyDeviceIDAll, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoNone, - }, - { - description: "Isolated - IPv4 - Lowest 8", - expected: &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.0", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: device.Geo, - }, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4Subnet, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoNone, - }, - { - description: "Isolated - IPv6", - expected: &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4400::", - Geo: device.Geo, - }, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6Subnet, - geo: ScrubStrategyGeoNone, - }, - { - description: "Isolated - Geo - Reduced Precision", - expected: &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: &openrtb2.Geo{ - Lat: 123.46, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "Isolated - Geo - Full", - expected: &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: &openrtb2.Geo{}, - }, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoFull, + name: "all", + deviceIn: &openrtb2.Device{DIDMD5: "MD5", DIDSHA1: "SHA1", DPIDMD5: "MD5", DPIDSHA1: "SHA1", IFA: "IFA", MACMD5: "MD5", MACSHA1: "SHA1"}, + expectedDevice: &openrtb2.Device{DIDMD5: "", DIDSHA1: "", DPIDMD5: "", DPIDSHA1: "", IFA: "", MACMD5: "", MACSHA1: ""}, + }, + { + name: "nil", + deviceIn: nil, + expectedDevice: nil, }, } - testIPMasking := getTestIPMasking() for _, test := range testCases { - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(device, test.id, test.ipv4, test.ipv6, test.geo) - assert.Equal(t, test.expected, result, test.description) + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Device: test.deviceIn}} + scrubDeviceIDs(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedDevice, brw.Device) + }) } } -func TestScrubDeviceNil(t *testing.T) { - testIPMasking := getTestIPMasking() - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(nil, ScrubStrategyDeviceIDNone, ScrubStrategyIPV4None, ScrubStrategyIPV6None, ScrubStrategyGeoNone) - assert.Nil(t, result) +func TestScrubUserIDs(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + expectedUser *openrtb2.User + }{ + { + name: "all", + userIn: &openrtb2.User{Data: []openrtb2.Data{}, ID: "ID", BuyerUID: "bID", Yob: 2000, Gender: "M", Keywords: "keywords", KwArray: nil}, + expectedUser: &openrtb2.User{Data: nil, ID: "", BuyerUID: "", Yob: 0, Gender: "", Keywords: "", KwArray: nil}, + }, + { + name: "nil", + userIn: nil, + expectedUser: nil, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} + scrubUserIDs(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + }) + } } -func TestScrubUser(t *testing.T) { - user := getTestUser() - +func TestScrubUserDemographics(t *testing.T) { testCases := []struct { - description string - expected *openrtb2.User - scrubUser ScrubStrategyUser - scrubGeo ScrubStrategyGeo + name string + userIn *openrtb2.User + expectedUser *openrtb2.User }{ { - description: "User ID And Demographic & Geo Full", - expected: &openrtb2.User{ - ID: "", - BuyerUID: "", - Yob: 0, - Gender: "", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{}, - }, - scrubUser: ScrubStrategyUserIDAndDemographic, - scrubGeo: ScrubStrategyGeoFull, - }, - { - description: "User ID And Demographic & Geo Reduced", - expected: &openrtb2.User{ - ID: "", - BuyerUID: "", - Yob: 0, - Gender: "", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.46, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - scrubUser: ScrubStrategyUserIDAndDemographic, - scrubGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "User ID And Demographic & Geo None", - expected: &openrtb2.User{ - ID: "", - BuyerUID: "", - Yob: 0, - Gender: "", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - scrubUser: ScrubStrategyUserIDAndDemographic, - scrubGeo: ScrubStrategyGeoNone, - }, - { - description: "User None & Geo Full", - expected: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{}, - }, - scrubUser: ScrubStrategyUserNone, - scrubGeo: ScrubStrategyGeoFull, - }, - { - description: "User None & Geo Reduced", - expected: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.46, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - scrubUser: ScrubStrategyUserNone, - scrubGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "User None & Geo None", - expected: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - scrubUser: ScrubStrategyUserNone, - scrubGeo: ScrubStrategyGeoNone, + name: "all", + userIn: &openrtb2.User{ID: "ID", BuyerUID: "bID", Yob: 2000, Gender: "M"}, + expectedUser: &openrtb2.User{ID: "", BuyerUID: "", Yob: 0, Gender: ""}, + }, + { + name: "nil", + userIn: nil, + expectedUser: nil, }, } - - testIPMasking := getTestIPMasking() for _, test := range testCases { - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(user, test.scrubUser, test.scrubGeo) - assert.Equal(t, test.expected, result, test.description) + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} + scrubUserDemographics(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + }) } } -func TestScrubUserNil(t *testing.T) { - testIPMasking := getTestIPMasking() - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(nil, ScrubStrategyUserNone, ScrubStrategyGeoNone) - assert.Nil(t, result) +func TestScrubUserExt(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + fieldName string + expectedUser *openrtb2.User + }{ + { + name: "nil_user", + userIn: nil, + expectedUser: nil, + }, + { + name: "nil_ext", + userIn: &openrtb2.User{ID: "ID", Ext: nil}, + expectedUser: &openrtb2.User{ID: "ID", Ext: nil}, + }, + { + name: "empty_ext", + userIn: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{}`)}, + expectedUser: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{}`)}, + }, + { + name: "ext_with_field", + userIn: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"data":"123","test":1}`)}, + fieldName: "data", + expectedUser: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"test":1}`)}, + }, + { + name: "ext_without_field", + userIn: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"data":"123","test":1}`)}, + fieldName: "noData", + expectedUser: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"data":"123","test":1}`)}, + }, + { + name: "nil", + userIn: nil, + expectedUser: nil, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} + scrubUserExt(brw, test.fieldName) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + }) + } } -func TestScrubRequest(t *testing.T) { - - imps := []openrtb2.Imp{ - {ID: "testId", Ext: json.RawMessage(`{"test": 1, "tid": 2}`)}, +func TestScrubEids(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + expectedUser *openrtb2.User + }{ + { + name: "eids", + userIn: &openrtb2.User{ID: "ID", EIDs: []openrtb2.EID{}}, + expectedUser: &openrtb2.User{ID: "ID", EIDs: nil}, + }, + { + name: "nil_eids", + userIn: &openrtb2.User{ID: "ID", EIDs: nil}, + expectedUser: &openrtb2.User{ID: "ID", EIDs: nil}, + }, + { + name: "nil", + userIn: nil, + expectedUser: nil, + }, } - source := &openrtb2.Source{ - TID: "testTid", + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} + ScrubEIDs(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + }) } - device := getTestDevice() - user := getTestUser() - user.Ext = json.RawMessage(`{"data": 1, "eids": 2}`) - user.EIDs = []openrtb2.EID{{Source: "test"}} +} +func TestScrubTID(t *testing.T) { testCases := []struct { - description string - enforcement Enforcement - userExtPresent bool - expected *openrtb2.BidRequest + name string + sourceIn *openrtb2.Source + impIn []openrtb2.Imp + expectedSource *openrtb2.Source + expectedImp []openrtb2.Imp }{ { - description: "enforce transmitUFPD with user.ext", - enforcement: Enforcement{UFPD: true}, - userExtPresent: true, - expected: &openrtb2.BidRequest{ - Imp: imps, - Source: source, - User: &openrtb2.User{ - EIDs: []openrtb2.EID{{Source: "test"}}, - Geo: user.Geo, - Ext: json.RawMessage(`{"eids":2}`), - }, - Device: &openrtb2.Device{ - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: device.Geo, - }, - }, - }, - { - description: "enforce transmitUFPD without user.ext", - enforcement: Enforcement{UFPD: true}, - userExtPresent: false, - expected: &openrtb2.BidRequest{ - Imp: imps, - Source: source, - User: &openrtb2.User{ - EIDs: []openrtb2.EID{{Source: "test"}}, - Geo: user.Geo, - }, - Device: &openrtb2.Device{ - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: device.Geo, - }, - }, - }, - { - description: "enforce transmitEids", - enforcement: Enforcement{Eids: true}, - userExtPresent: true, - expected: &openrtb2.BidRequest{ - Imp: imps, - Source: source, - Device: device, - User: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Geo: user.Geo, - EIDs: nil, - Ext: json.RawMessage(`{"data":1}`), - }, - }, - }, - { - description: "enforce transmitTid", - enforcement: Enforcement{TID: true}, - userExtPresent: true, - expected: &openrtb2.BidRequest{ - Imp: []openrtb2.Imp{ - {ID: "testId", Ext: json.RawMessage(`{"test":1}`)}, - }, - Source: &openrtb2.Source{ - TID: "", - }, - Device: device, - User: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Geo: user.Geo, - EIDs: []openrtb2.EID{{Source: "test"}}, - Ext: json.RawMessage(`{"data": 1, "eids": 2}`), - }, - }, - }, - { - description: "enforce precise Geo", - enforcement: Enforcement{PreciseGeo: true}, - userExtPresent: true, - expected: &openrtb2.BidRequest{ - Imp: imps, - Source: source, - User: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Geo: &openrtb2.Geo{ - Lat: 123.46, Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - EIDs: []openrtb2.EID{{Source: "test"}}, - Ext: json.RawMessage(`{"data": 1, "eids": 2}`), - }, - Device: &openrtb2.Device{ - IFA: "anyIFA", - DIDSHA1: "anyDIDSHA1", - DIDMD5: "anyDIDMD5", - DPIDSHA1: "anyDPIDSHA1", - DPIDMD5: "anyDPIDMD5", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IP: "1.2.3.0", - IPv6: "2001:1db8:2233:4400::", - Geo: &openrtb2.Geo{ - Lat: 123.46, Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - }, + name: "nil", + sourceIn: nil, + expectedSource: nil, + }, + { + name: "nil_imp_ext", + sourceIn: &openrtb2.Source{TID: "tid"}, + impIn: []openrtb2.Imp{{ID: "impID", Ext: nil}}, + expectedSource: &openrtb2.Source{TID: ""}, + expectedImp: []openrtb2.Imp{{ID: "impID", Ext: nil}}, + }, + { + name: "empty_imp_ext", + sourceIn: &openrtb2.Source{TID: "tid"}, + impIn: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{}`)}}, + expectedSource: &openrtb2.Source{TID: ""}, + expectedImp: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{}`)}}, + }, + { + name: "ext_with_tid", + sourceIn: &openrtb2.Source{TID: "tid"}, + impIn: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"tid":"123","test":1}`)}}, + expectedSource: &openrtb2.Source{TID: ""}, + expectedImp: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"test":1}`)}}, + }, + { + name: "ext_without_tid", + sourceIn: &openrtb2.Source{TID: "tid"}, + impIn: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"data":"123","test":1}`)}}, + expectedSource: &openrtb2.Source{TID: ""}, + expectedImp: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"data":"123","test":1}`)}}, }, } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Source: test.sourceIn, Imp: test.impIn}} + ScrubTID(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedSource, brw.Source) + assert.Equal(t, test.expectedImp, brw.Imp) + }) + } +} - testIPMasking := getTestIPMasking() +func TestScrubGEO(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + expectedUser *openrtb2.User + deviceIn *openrtb2.Device + expectedDevice *openrtb2.Device + }{ + { + name: "nil", + userIn: nil, + expectedUser: nil, + deviceIn: nil, + expectedDevice: nil, + }, + { + name: "nil_user_geo", + userIn: &openrtb2.User{ID: "ID", Geo: nil}, + expectedUser: &openrtb2.User{ID: "ID", Geo: nil}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: 123.123}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: 123.12}}, + }, + { + name: "with_user_geo", + userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: 123.123}}, + expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: 123.12}}, + deviceIn: &openrtb2.Device{}, + expectedDevice: &openrtb2.Device{}, + }, + { + name: "nil_device_geo", + userIn: &openrtb2.User{}, + expectedUser: &openrtb2.User{}, + deviceIn: &openrtb2.Device{Geo: nil}, + expectedDevice: &openrtb2.Device{Geo: nil}, + }, + { + name: "with_device_geo", + userIn: &openrtb2.User{}, + expectedUser: &openrtb2.User{}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: 123.123}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: 123.12}}, + }, + { + name: "with_user_and_device_geo", + userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: 123.123}}, + expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: 123.12}}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: 123.123}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: 123.12}}, + }, + } for _, test := range testCases { - t.Run(test.description, func(t *testing.T) { - bidRequest := &openrtb2.BidRequest{ - Imp: []openrtb2.Imp{ - {ID: "testId", Ext: json.RawMessage(`{"test": 1, "tid": 2}`)}, - }, - Source: &openrtb2.Source{ - TID: "testTid", - }, - User: getTestUser(), - Device: getTestDevice(), - } - if test.userExtPresent { - bidRequest.User.Ext = json.RawMessage(`{"data": 1, "eids": 2}`) - } else { - bidRequest.User.Ext = nil - } - bidRequest.User.EIDs = []openrtb2.EID{{Source: "test"}} + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn, Device: test.deviceIn}} + scrubGEO(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + assert.Equal(t, test.expectedDevice, brw.Device) + }) + } +} - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubRequest(bidRequest, test.enforcement) - assert.Equal(t, test.expected, result, test.description) +func TestScrubGeoFull(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + expectedUser *openrtb2.User + deviceIn *openrtb2.Device + expectedDevice *openrtb2.Device + }{ + { + name: "nil", + userIn: nil, + expectedUser: nil, + deviceIn: nil, + expectedDevice: nil, + }, + { + name: "nil_user_geo", + userIn: &openrtb2.User{ID: "ID", Geo: nil}, + expectedUser: &openrtb2.User{ID: "ID", Geo: nil}, + deviceIn: &openrtb2.Device{}, + expectedDevice: &openrtb2.Device{}, + }, + { + name: "with_user_geo", + userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: 123.123}}, + expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{}}, + deviceIn: &openrtb2.Device{}, + expectedDevice: &openrtb2.Device{}, + }, + { + name: "nil_device_geo", + userIn: &openrtb2.User{}, + expectedUser: &openrtb2.User{}, + deviceIn: &openrtb2.Device{Geo: nil}, + expectedDevice: &openrtb2.Device{Geo: nil}, + }, + { + name: "with_device_geo", + userIn: &openrtb2.User{}, + expectedUser: &openrtb2.User{}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: 123.123}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{}}, + }, + { + name: "with_user_and_device_geo", + userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: 123.123}}, + expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{}}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: 123.123}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{}}, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn, Device: test.deviceIn}} + scrubGeoFull(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + assert.Equal(t, test.expectedDevice, brw.Device) }) } } @@ -510,42 +404,6 @@ func TestScrubIP(t *testing.T) { bits: 128, maskBits: 96, }, - { - IP: "2001:1db8:0000:0000:0000:ff00:0042:8329", - cleanedIP: "2001:1db8::ff00:42:0", - bits: 128, - maskBits: 112, - }, - { - IP: "2001:1db8:0000:0000:0000:ff00:0042:0", - cleanedIP: "2001:1db8::ff00:42:0", - bits: 128, - maskBits: 112, - }, - { - IP: "127.0.0.1", - cleanedIP: "127.0.0.0", - bits: 32, - maskBits: 24, - }, - { - IP: "0.0.0.0", - cleanedIP: "0.0.0.0", - bits: 32, - maskBits: 24, - }, - { - IP: "192.127.111.134", - cleanedIP: "192.127.111.0", - bits: 32, - maskBits: 24, - }, - { - IP: "192.127.111.0", - cleanedIP: "192.127.111.0", - bits: 32, - maskBits: 24, - }, } for _, test := range testCases { t.Run(test.IP, func(t *testing.T) { @@ -556,32 +414,6 @@ func TestScrubIP(t *testing.T) { } } -func TestScrubGeoFull(t *testing.T) { - geo := &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - } - geoExpected := &openrtb2.Geo{ - Lat: 0, - Lon: 0, - Metro: "", - City: "", - ZIP: "", - } - - result := scrubGeoFull(geo) - - assert.Equal(t, geoExpected, result) -} - -func TestScrubGeoFullWhenNil(t *testing.T) { - result := scrubGeoFull(nil) - assert.Nil(t, result) -} - func TestScrubGeoPrecision(t *testing.T) { geo := &openrtb2.Geo{ Lat: 123.456, @@ -654,26 +486,11 @@ func TestScrubUserExtIDs(t *testing.T) { userExt: json.RawMessage(`{"anyExisting":{"existing":42},"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), expected: json.RawMessage(`{"anyExisting":{"existing":42}}`), }, - { - description: "Remove eids Only", - userExt: json.RawMessage(`{"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), - expected: json.RawMessage(`{}`), - }, { description: "Remove eids Only - Empty Array", userExt: json.RawMessage(`{"eids":[]}`), expected: json.RawMessage(`{}`), }, - { - description: "Remove eids Only - With Other Data", - userExt: json.RawMessage(`{"anyExisting":42,"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), - expected: json.RawMessage(`{"anyExisting":42}`), - }, - { - description: "Remove eids Only - With Other Nested Data", - userExt: json.RawMessage(`{"anyExisting":{"existing":42},"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), - expected: json.RawMessage(`{"anyExisting":{"existing":42}}`), - }, } for _, test := range testCases { @@ -681,52 +498,3 @@ func TestScrubUserExtIDs(t *testing.T) { assert.Equal(t, test.expected, result, test.description) } } - -func getTestUser() *openrtb2.User { - return &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - } -} - -func getTestDevice() *openrtb2.Device { - return &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - } -} - -func getTestIPMasking() config.AccountPrivacy { - return config.AccountPrivacy{ - IPv6Config: config.IPv6{ - 54, - }, - IPv4Config: config.IPv4{ - 24, - }, - } -}