Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Activities framework #2844

Merged
merged 14 commits into from
Jul 6, 2023
Merged
2 changes: 1 addition & 1 deletion config/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Account struct {
Validations Validations `mapstructure:"validations" json:"validations"`
DefaultBidLimit int `mapstructure:"default_bid_limit" json:"default_bid_limit"`
BidAdjustments *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"`
Privacy AccountPrivacy `mapstructure:"privacy" json:"privacy"`
Privacy *AccountPrivacy `mapstructure:"privacy" json:"privacy"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we say this needed to be a pointer? I can't recall.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was changed to "short-circuit NewActivityControl call in auction for performance reasons".
Also we need this nil check, at least for the meantime to make sure we don't reject all requests if activities are not configured.

}

// CookieSync represents the account-level defaults for the cookie sync endpoint.
Expand Down
3 changes: 2 additions & 1 deletion config/activity.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ type AllowActivities struct {
TransmitUserFPD Activity `mapstructure:"transmitUfpd" json:"transmitUfpd"`
TransmitPreciseGeo Activity `mapstructure:"transmitPreciseGeo" json:"transmitPreciseGeo"`
TransmitUniqueRequestIds Activity `mapstructure:"transmitUniqueRequestIds" json:"transmitUniqueRequestIds"`
TransmitTids Activity `mapstructure:"transmitTid" json:"transmitTid"`
}

type Activity struct {
Default *bool `mapstructure:"default" json:"default"`
Rules []ActivityRule `mapstructure:"rules" json:"rules"`
Allow bool `mapstructure:"allow" json:"allow"`
}

type ActivityRule struct {
Condition ActivityCondition `mapstructure:"condition" json:"condition"`
Allow bool `mapstructure:"allow" json:"allow"`
}

type ActivityCondition struct {
Expand Down
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ func (cfg *Configuration) validate(v *viper.Viper) []error {
cfg.TmaxAdjustments.Enabled = false
}

if cfg.AccountDefaults.Privacy != nil {
glog.Warning("account_defaults.Privacy has no effect as the feature is under development.")
}

errs = cfg.Experiment.validate(errs)
errs = cfg.BidderInfos.validate(errs)
return errs
Expand Down
9 changes: 9 additions & 0 deletions endpoints/openrtb2/amp_auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/prebid/prebid-server/privacy"
"net/http"
"net/url"
"strings"
Expand Down Expand Up @@ -222,6 +223,13 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h

tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)

activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
if activitiesErr != nil {
errL = append(errL, activitiesErr)
writeError(errL, w, &labels)
return
}

secGPC := r.Header.Get("Sec-GPC")

auctionRequest := &exchange.AuctionRequest{
Expand All @@ -239,6 +247,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
HookExecutor: hookExecutor,
QueryParams: r.URL.Query(),
TCF2Config: tcf2Config,
Activities: activities,
}

auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
Expand Down
11 changes: 11 additions & 0 deletions endpoints/openrtb2/auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/prebid/prebid-server/privacy"
"io"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -191,6 +192,15 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http

tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)

activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
if activitiesErr != nil {
errL = append(errL, activitiesErr)
if errortypes.ContainsFatalError(errL) {
writeError(errL, w, &labels)
return
}
}

ctx := context.Background()

timeout := deps.cfg.AuctionTimeouts.LimitAuctionTimeout(time.Duration(req.TMax) * time.Millisecond)
Expand Down Expand Up @@ -236,6 +246,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
PubID: labels.PubID,
HookExecutor: hookExecutor,
TCF2Config: tcf2Config,
Activities: activities,
}
auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
ao.RequestWrapper = req
Expand Down
2 changes: 2 additions & 0 deletions exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/prebid/prebid-server/privacy"
"math/rand"
"net/url"
"runtime/debug"
Expand Down Expand Up @@ -195,6 +196,7 @@ type AuctionRequest struct {
GlobalPrivacyControlHeader string
ImpExtInfoMap map[string]ImpExtInfo
TCF2Config gdpr.TCF2ConfigReader
Activities privacy.ActivityControl

// LegacyLabels is included here for temporary compatibility with cleanOpenRTBRequests
// in HoldAuction until we get to factoring it away. Do not use for anything new.
Expand Down
9 changes: 9 additions & 0 deletions exchange/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
for _, bidderRequest := range allBidderRequests {
bidRequestAllowed := true

// fetchBids activity
fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids,
privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidderRequest.BidderName.String()})
if fetchBidsActivityAllowed == privacy.ActivityDeny {
// skip the call to a bidder if fetchBids activity is not allowed
// do not add this bidder to allowedBidderRequests
continue
}
Copy link
Collaborator

@bsardo bsardo Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notes from offline discussion on how to use activities framework along with existing privacy packages as part of GPP phase 3:

if fetchBidsActivityAllowed == privacy.ActivityDeny {
    // skip the call to a bidder if fetchBids activity is not allowed
	// do not add this bidder to allowedBidderRequests
	continue
} else if fetchBidsActivityAllowed == privacy.ActivityAbstain and gdprEnforced == true {
    allowed, err := gdprPerms.FetchBidAllowed(ctx, bidderRequest.BidderCoreName, bidderRequest.BidderName)
        if !allowed {
            continue
        }
}
// proceed with processing ID and geo to see if needs to be scrubbed


// CCPA
privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String())

Expand Down
70 changes: 70 additions & 0 deletions exchange/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/prebid/prebid-server/gdpr"
"github.com/prebid/prebid-server/metrics"
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/prebid/prebid-server/privacy"
"github.com/prebid/prebid-server/util/ptrutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
Expand Down Expand Up @@ -4273,3 +4274,72 @@ func TestGetMediaTypeForBid(t *testing.T) {
})
}
}

func TemporarilyDisabledTestCleanOpenRTBRequestsActivitiesFetchBids(t *testing.T) {
testCases := []struct {
name string
req *openrtb2.BidRequest
componentName string
allow bool
expectedReqNumber int
}{
{
name: "request_with_one_bidder_allowed",
req: newBidRequest(t),
componentName: "appnexus",
allow: true,
expectedReqNumber: 1,
},
{
name: "request_with_one_bidder_not_allowed",
req: newBidRequest(t),
componentName: "appnexus",
allow: false,
expectedReqNumber: 0,
},
}

for _, test := range testCases {
privacyConfig := getDefaultActivityConfig(test.componentName, test.allow)
activities, err := privacy.NewActivityControl(privacyConfig)
assert.NoError(t, err, "")
auctionReq := AuctionRequest{
BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req},
UserSyncs: &emptyUsersync{},
Activities: activities,
}

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)
assert.Empty(t, errs)
assert.Len(t, bidderRequests, test.expectedReqNumber)
})
}
}

func getDefaultActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
return &config.AccountPrivacy{
AllowActivities: config.AllowActivities{
FetchBids: config.Activity{
Default: ptrutil.ToPtr(true),
Rules: []config.ActivityRule{
{
Allow: allow,
Condition: config.ActivityCondition{
ComponentName: []string{componentName},
ComponentType: []string{"bidder"},
},
},
},
},
},
}
}
3 changes: 3 additions & 0 deletions privacy/activity.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const (
ActivityTransmitUserFPD
ActivityTransmitPreciseGeo
ActivityTransmitUniqueRequestIds
ActivityTransmitTids
)

func (a Activity) String() string {
Expand All @@ -29,6 +30,8 @@ func (a Activity) String() string {
return "transmitPreciseGeo"
case ActivityTransmitUniqueRequestIds:
return "transmitUniqueRequestIds"
case ActivityTransmitTids:
return "transmitTid"
}

return ""
Expand Down
Loading