Skip to content

Commit

Permalink
Support configurable host cookie (prebid#146)
Browse files Browse the repository at this point in the history
* Making host cookie space inspection configurableRemoving some AppNexus specific items

* Removing stupidity

* Removing binaries

* gofmt
  • Loading branch information
pdezwart authored and dbemiller committed Oct 5, 2017
1 parent 39a2a4b commit bf7d1f7
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 67 deletions.
4 changes: 3 additions & 1 deletion adapters/appnexus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,9 @@ func TestAppNexusBasicResponse(t *testing.T) {
req.Header.Add("Cookie", fakewriter.Header().Get("Set-Cookie"))

cacheClient, _ := dummycache.New()
pbReq, err := pbs.ParsePBSRequest(req, cacheClient)
hcs := pbs.HostCookieSettings{}

pbReq, err := pbs.ParsePBSRequest(req, cacheClient, &hcs)
if err != nil {
t.Fatalf("ParsePBSRequest failed: %v", err)
}
Expand Down
4 changes: 3 additions & 1 deletion adapters/facebook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,9 @@ func TestFacebookBasicResponse(t *testing.T) {
req.Header.Add("Cookie", fakewriter.Header().Get("Set-Cookie"))

cacheClient, _ := dummycache.New()
pbReq, err := pbs.ParsePBSRequest(req, cacheClient)
hcs := pbs.HostCookieSettings{}

pbReq, err := pbs.ParsePBSRequest(req, cacheClient, &hcs)
if err != nil {
t.Fatalf("ParsePBSRequest failed: %v", err)
}
Expand Down
3 changes: 2 additions & 1 deletion adapters/lifestreet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ func TestLifestreetBasicResponse(t *testing.T) {
req.Header.Add("Cookie", fakewriter.Header().Get("Set-Cookie"))

cacheClient, _ := dummycache.New()
pbReq, err := pbs.ParsePBSRequest(req, cacheClient)
hcs := pbs.HostCookieSettings{}
pbReq, err := pbs.ParsePBSRequest(req, cacheClient, &hcs)
if err != nil {
t.Fatalf("ParsePBSRequest failed: %v", err)
}
Expand Down
4 changes: 3 additions & 1 deletion adapters/pulsepoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,9 @@ func SampleRequest(numberOfImpressions int, t *testing.T) *pbs.PBSRequest {
httpReq.Header.Add("Cookie", fakewriter.Header().Get("Set-Cookie"))
// parse the http request
cacheClient, _ := dummycache.New()
parsedReq, err := pbs.ParsePBSRequest(httpReq, cacheClient)
hcs := pbs.HostCookieSettings{}

parsedReq, err := pbs.ParsePBSRequest(httpReq, cacheClient, &hcs)
if err != nil {
t.Fatalf("Error when parsing request: %v", err)
}
Expand Down
4 changes: 3 additions & 1 deletion adapters/rubicon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,9 @@ func TestRubiconBasicResponse(t *testing.T) {
req.Header.Add("Cookie", fakewriter.Header().Get("Set-Cookie"))

cacheClient, _ := dummycache.New()
pbReq, err := pbs.ParsePBSRequest(req, cacheClient)
hcs := pbs.HostCookieSettings{}

pbReq, err := pbs.ParsePBSRequest(req, cacheClient, &hcs)
if err != nil {
t.Fatalf("ParsePBSRequest failed: %v", err)
}
Expand Down
11 changes: 9 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,27 @@ import (

// Configuration
type Configuration struct {
CookieDomain string `mapstructure:"cookie_domain"`
ExternalURL string `mapstructure:"external_url"`
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
AdminPort int `mapstructure:"admin_port"`
DefaultTimeout uint64 `mapstructure:"default_timeout_ms"`
CacheURL string `mapstructure:"prebid_cache_url"`
RequireUUID2 bool `mapstructure:"require_uuid2"`
RecaptchaSecret string `mapstructure:"recaptcha_secret"`
HostCookie HostCookie `mapstructure:"host_cookie"`
Metrics Metrics `mapstructure:"metrics"`
DataCache DataCache `mapstructure:"datacache"`
Adapters map[string]Adapter `mapstructure:"adapters"`
}

type HostCookie struct {
Domain string `mapstructure:"domain"`
Family string `mapstructure:"family"`
CookieName string `mapstructure:"cookie_name"`
OptOutURL string `mapstructure:"opt_out_url"`
OptInURL string `mapstructure:"opt_in_url"`
}

type Adapter struct {
Endpoint string `mapstructure:"endpoint"` // Required
UserSyncURL string `mapstructure:"usersync_url"`
Expand Down
26 changes: 16 additions & 10 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,19 @@ func TestDefaults(t *testing.T) {

}

var fullConfig = []byte(`cookie_domain: ".adnxs.com"
external_url: http://prebid.adnxs.com/
host: prebid.adnxs.com
var fullConfig = []byte(`
host_cookie:
cookie_name: userid
family: prebid
domain: cookies.prebid.org
opt_out_url: http://prebid.org/optout
opt_in_url: http://prebid.org/optin
external_url: http://prebid-server.prebid.org/
host: prebid-server.prebid.org
port: 1234
admin_port: 5678
default_timeout_ms: 123
prebid_cache_url: http://prebidcache.net/test/a1?qs=something
require_uuid2: true
recaptcha_secret: asdfasdfasdfasdf
metrics:
host: upstream:8232
Expand Down Expand Up @@ -111,18 +116,19 @@ func TestFullConfig(t *testing.T) {
if err != nil {
t.Fatal(err.Error())
}
cmpStrings(t, "cookie domain", cfg.CookieDomain, ".adnxs.com")
cmpStrings(t, "external url", cfg.ExternalURL, "http://prebid.adnxs.com/")
cmpStrings(t, "host", cfg.Host, "prebid.adnxs.com")
cmpStrings(t, "cookie domain", cfg.HostCookie.Domain, "cookies.prebid.org")
cmpStrings(t, "cookie name", cfg.HostCookie.CookieName, "userid")
cmpStrings(t, "cookie family", cfg.HostCookie.Family, "prebid")
cmpStrings(t, "opt out", cfg.HostCookie.OptOutURL, "http://prebid.org/optout")
cmpStrings(t, "opt in", cfg.HostCookie.OptInURL, "http://prebid.org/optin")
cmpStrings(t, "external url", cfg.ExternalURL, "http://prebid-server.prebid.org/")
cmpStrings(t, "host", cfg.Host, "prebid-server.prebid.org")
cmpInts(t, "port", cfg.Port, 1234)
cmpInts(t, "admin_port", cfg.AdminPort, 5678)
if cfg.DefaultTimeout != 123 {
t.Errorf("DefaultTimeout was %d not 123", cfg.DefaultTimeout)
}
cmpStrings(t, "prebid_cache_url", cfg.CacheURL, "http://prebidcache.net/test/a1?qs=something")
if cfg.RequireUUID2 != true {
t.Errorf("RequireUUID2 was false")
}
cmpStrings(t, "recaptcha_secret", cfg.RecaptchaSecret, "asdfasdfasdfasdf")
cmpStrings(t, "metrics.host", cfg.Metrics.Host, "upstream:8232")
cmpStrings(t, "metrics.database", cfg.Metrics.Database, "metricsdb")
Expand Down
10 changes: 6 additions & 4 deletions pbs/pbsrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func ParseMediaTypes(types []string) []MediaType {
return mtypes
}

func ParsePBSRequest(r *http.Request, cache cache.Cache) (*PBSRequest, error) {
func ParsePBSRequest(r *http.Request, cache cache.Cache, hostCookieSettings *HostCookieSettings) (*PBSRequest, error) {
defer r.Body.Close()

pbsReq := &PBSRequest{}
Expand Down Expand Up @@ -237,9 +237,11 @@ func ParsePBSRequest(r *http.Request, cache cache.Cache) (*PBSRequest, error) {
if pbsReq.App == nil {
pbsReq.Cookie = ParsePBSCookieFromRequest(r)

// this would be for the shared adnxs.com domain
if anid, err := r.Cookie("uuid2"); err == nil {
pbsReq.Cookie.TrySync("adnxs", anid.Value)
// Host has right to leverage private cookie store for user ID
if uid, _, _ := pbsReq.Cookie.GetUID(hostCookieSettings.Family); uid == "" && hostCookieSettings.CookieName != "" {
if hostCookie, err := r.Cookie(hostCookieSettings.CookieName); err == nil {
pbsReq.Cookie.TrySync(hostCookieSettings.Family, hostCookie.Value)
}
}

pbsReq.Device.UA = r.Header.Get("User-Agent")
Expand Down
63 changes: 56 additions & 7 deletions pbs/pbsrequest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pbs

import (
"bytes"
"net/http"
"net/http/httptest"
"testing"

Expand Down Expand Up @@ -63,8 +64,9 @@ func TestParseSimpleRequest(t *testing.T) {
r := httptest.NewRequest("POST", "/auction", bytes.NewBuffer(body))
r.Header.Add("Referer", "http://nytimes.com/cool.html")
d, _ := dummycache.New()
hcs := HostCookieSettings{}

pbs_req, err := ParsePBSRequest(r, d)
pbs_req, err := ParsePBSRequest(r, d, &hcs)
if err != nil {
t.Fatalf("Parse simple request failed: %v", err)
}
Expand Down Expand Up @@ -136,10 +138,11 @@ func TestHeaderParsing(t *testing.T) {
r.Header.Add("Referer", "http://nytimes.com/cool.html")
r.Header.Add("User-Agent", "Mozilla/")
d, _ := dummycache.New()
hcs := HostCookieSettings{}

d.Config().Set("dummy", dummyConfig)

pbs_req, err := ParsePBSRequest(r, d)
pbs_req, err := ParsePBSRequest(r, d, &hcs)
if err != nil {
t.Fatalf("Parse simple request failed")
}
Expand Down Expand Up @@ -217,10 +220,11 @@ func TestParseConfig(t *testing.T) {
r := httptest.NewRequest("POST", "/auction", bytes.NewBuffer(body))
r.Header.Add("Referer", "http://nytimes.com/cool.html")
d, _ := dummycache.New()
hcs := HostCookieSettings{}

d.Config().Set("dummy", dummyConfig)

pbs_req, err := ParsePBSRequest(r, d)
pbs_req, err := ParsePBSRequest(r, d, &hcs)
if err != nil {
t.Fatalf("Parse simple request failed: %v", err)
}
Expand Down Expand Up @@ -299,8 +303,9 @@ func TestParseMobileRequestFirstVersion(t *testing.T) {
`)
r := httptest.NewRequest("POST", "/auction", bytes.NewBuffer(body))
d, _ := dummycache.New()
hcs := HostCookieSettings{}

pbs_req, err := ParsePBSRequest(r, d)
pbs_req, err := ParsePBSRequest(r, d, &hcs)
if err != nil {
t.Fatalf("Parse simple request failed: %v", err)
}
Expand Down Expand Up @@ -394,8 +399,9 @@ func TestParseMobileRequest(t *testing.T) {
`)
r := httptest.NewRequest("POST", "/auction", bytes.NewBuffer(body))
d, _ := dummycache.New()
hcs := HostCookieSettings{}

pbs_req, err := ParsePBSRequest(r, d)
pbs_req, err := ParsePBSRequest(r, d, &hcs)
if err != nil {
t.Fatalf("Parse simple request failed: %v", err)
}
Expand Down Expand Up @@ -493,8 +499,9 @@ func TestParseMalformedMobileRequest(t *testing.T) {
`)
r := httptest.NewRequest("POST", "/auction", bytes.NewBuffer(body))
d, _ := dummycache.New()
hcs := HostCookieSettings{}

pbs_req, err := ParsePBSRequest(r, d)
pbs_req, err := ParsePBSRequest(r, d, &hcs)
if err != nil {
t.Fatalf("Parse simple request failed: %v", err)
}
Expand Down Expand Up @@ -596,8 +603,9 @@ func TestParseRequestWithInstl(t *testing.T) {
`)
r := httptest.NewRequest("POST", "/auction", bytes.NewBuffer(body))
d, _ := dummycache.New()
hcs := HostCookieSettings{}

pbs_req, err := ParsePBSRequest(r, d)
pbs_req, err := ParsePBSRequest(r, d, &hcs)
if err != nil {
t.Fatalf("Parse simple request failed: %v", err)
}
Expand All @@ -612,3 +620,44 @@ func TestParseRequestWithInstl(t *testing.T) {
}

}

func TestParsePBSRequestUsesHostCookie(t *testing.T) {
body := []byte(`{
"tid": "abcd",
"ad_units": [
{
"code": "first",
"sizes": [{"w": 300, "h": 250}],
"bidders": [
{
"bidder": "bidder1",
"params": {
"id": "417",
"siteID": "test-site"
}
}
]
}
]
}
`)
r, err := http.NewRequest("POST", "/auction", bytes.NewBuffer(body))
r.Header.Add("Referer", "http://nytimes.com/cool.html")
if err != nil {
t.Fatalf("new request failed")
}
r.AddCookie(&http.Cookie{Name: "key", Value: "testcookie"})
d, _ := dummycache.New()
hcs := HostCookieSettings{
CookieName: "key",
Family: "family",
}

pbs_req, err2 := ParsePBSRequest(r, d, &hcs)
if err2 != nil {
t.Fatalf("Parse simple request failed %v", err2)
}
if uid, _, _ := pbs_req.Cookie.GetUID("family"); uid != "testcookie" {
t.Errorf("Failed to leverage host cookie space for user identifier")
}
}
34 changes: 22 additions & 12 deletions pbs/usersync.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ type PBSCookie struct {
birthday *time.Time
}

type HostCookieSettings struct {
Domain string
Family string
CookieName string
OptOutURL string
OptInURL string
}

// uidWithExpiry bundles the UID with an Expiration date.
// After the expiration, the UID is no longer valid.
type uidWithExpiry struct {
Expand All @@ -54,10 +62,12 @@ type uidWithExpiry struct {
}

type UserSyncDeps struct {
Cookie_domain string
External_url string
Recaptcha_secret string
Metrics metrics.Registry
ExternalUrl string
RecaptchaSecret string
OptOutUrl string
OptInUrl string
HostCookieSettings *HostCookieSettings
Metrics metrics.Registry
}

// ParsePBSCookieFromRequest parses the UserSyncMap from an HTTP Request.
Expand Down Expand Up @@ -264,7 +274,7 @@ func (cookie *PBSCookie) UnmarshalJSON(b []byte) error {

func (deps *UserSyncDeps) GetUIDs(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
pc := ParsePBSCookieFromRequest(r)
pc.SetCookieOnResponse(w, deps.Cookie_domain)
pc.SetCookieOnResponse(w, deps.HostCookieSettings.Domain)
json.NewEncoder(w).Encode(pc)
return
}
Expand Down Expand Up @@ -311,7 +321,7 @@ func (deps *UserSyncDeps) SetUID(w http.ResponseWriter, r *http.Request, _ httpr
metrics.GetOrRegisterMeter(fmt.Sprintf(USERSYNC_SUCCESS, bidder), deps.Metrics).Mark(1)
}

pc.SetCookieOnResponse(w, deps.Cookie_domain)
pc.SetCookieOnResponse(w, deps.HostCookieSettings.Domain)
}

// Struct for parsing json in google's response
Expand All @@ -329,7 +339,7 @@ func (deps *UserSyncDeps) VerifyRecaptcha(response string) error {
Transport: ts,
}
resp, err := client.PostForm(RECAPTCHA_URL,
url.Values{"secret": {deps.Recaptcha_secret}, "response": {response}})
url.Values{"secret": {deps.RecaptchaSecret}, "response": {response}})
if err != nil {
return err
}
Expand All @@ -349,14 +359,14 @@ func (deps *UserSyncDeps) OptOut(w http.ResponseWriter, r *http.Request, _ httpr
rr := r.FormValue("g-recaptcha-response")

if rr == "" {
http.Redirect(w, r, fmt.Sprintf("%s/static/optout.html", deps.External_url), 301)
http.Redirect(w, r, fmt.Sprintf("%s/static/optout.html", deps.ExternalUrl), 301)
return
}

err := deps.VerifyRecaptcha(rr)
if err != nil {
if glog.V(2) {
glog.Infof("Optout failed recaptcha: %v", err)
glog.Infof("Opt Out failed recaptcha: %v", err)
}
w.WriteHeader(http.StatusUnauthorized)
return
Expand All @@ -365,11 +375,11 @@ func (deps *UserSyncDeps) OptOut(w http.ResponseWriter, r *http.Request, _ httpr
pc := ParsePBSCookieFromRequest(r)
pc.SetPreference(optout == "")

pc.SetCookieOnResponse(w, deps.Cookie_domain)
pc.SetCookieOnResponse(w, deps.HostCookieSettings.Domain)
if optout == "" {
http.Redirect(w, r, "https://ib.adnxs.com/optin", 301)
http.Redirect(w, r, deps.OptInUrl, 301)
} else {
http.Redirect(w, r, "https://ib.adnxs.com/optout", 301)
http.Redirect(w, r, deps.OptOutUrl, 301)
}
}

Expand Down
Loading

0 comments on commit bf7d1f7

Please sign in to comment.