Skip to content

Commit 3b42fe0

Browse files
committed
Enable better tracking of users in GA
1 parent 9c5b246 commit 3b42fe0

File tree

3 files changed

+40
-3
lines changed

3 files changed

+40
-3
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"babel-preset-es2015-native-modules": "6.6.0",
1111
"babel-register": "6.7.2",
1212
"clipboard": "1.5.10",
13+
"cookie": "0.3.1",
1314
"del": "2.2.0",
1415
"exports-loader": "0.6.3",
1516
"font-awesome": "4.5.0",

warehouse/accounts/views.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import datetime
1414

15+
from pyblake2 import blake2b
1516
from pyramid.httpexceptions import HTTPMovedPermanently, HTTPSeeOther
1617
from pyramid.security import remember, forget
1718
from pyramid.view import view_config
@@ -25,6 +26,9 @@
2526
from warehouse.utils.http import is_safe_url
2627

2728

29+
USER_ID_INSECURE_COOKIE = "user_id__insecure"
30+
31+
2832
@view_config(
2933
route_name="accounts.profile",
3034
renderer="accounts/profile.html",
@@ -91,7 +95,23 @@ def login(request, redirect_field_name=REDIRECT_FIELD_NAME,
9195

9296
# Now that we're logged in we'll want to redirect the user to either
9397
# where they were trying to go originally, or to the default view.
94-
return HTTPSeeOther(redirect_to, headers=dict(headers))
98+
resp = HTTPSeeOther(redirect_to, headers=dict(headers))
99+
100+
# We'll use this cookie so that client side javascript can Determine
101+
# the actual user ID (not username, user ID). This is *not* a security
102+
# sensitive context and it *MUST* not be used where security matters.
103+
#
104+
# We'll also hash this value just to avoid leaking the actual User IDs
105+
# here, even though it really shouldn't matter.
106+
resp.set_cookie(
107+
USER_ID_INSECURE_COOKIE,
108+
blake2b(
109+
str(userid).encode("ascii"),
110+
person=b"warehouse.userid",
111+
).hexdigest().lower(),
112+
)
113+
114+
return resp
95115

96116
return {
97117
"form": form,
@@ -141,7 +161,13 @@ def logout(request, redirect_field_name=REDIRECT_FIELD_NAME):
141161

142162
# Now that we're logged out we'll want to redirect the user to either
143163
# where they were originally, or to the default view.
144-
return HTTPSeeOther(redirect_to, headers=dict(headers))
164+
resp = HTTPSeeOther(redirect_to, headers=dict(headers))
165+
166+
# Ensure that we delete our user_id__insecure cookie, since the user is
167+
# no longer logged in.
168+
resp.delete_cookie(USER_ID_INSECURE_COOKIE)
169+
170+
return resp
145171

146172
return {"redirect": {"field": REDIRECT_FIELD_NAME, "data": redirect_to}}
147173

@@ -213,7 +239,7 @@ def _login_user(request, userid):
213239
request.session.update(data)
214240

215241
# Remember the userid using the authentication policy.
216-
headers = remember(request, userid)
242+
headers = remember(request, str(userid))
217243

218244
# Cycle the CSRF token since we've crossed an authentication boundary
219245
# and we don't want to continue using the old one.

warehouse/static/js/warehouse/utils/analytics.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
/* global ga */
1616

17+
import * as cookie from "cookie";
18+
1719

1820
export default () => {
1921
// Here we want to ensure that our ga function exists in the global scope,
@@ -34,6 +36,14 @@ export default () => {
3436
// user's IP addresses.
3537
ga("create", element.dataset.GaId, "auto", { anonymizeIp: true });
3638

39+
// Determine if we have a user ID associated with this person, if so we'll
40+
// go ahead and tell Google it to enable better tracking of individual
41+
// users.
42+
let cookies = cookie.parse(document.cookie);
43+
if (cookies.user_id__insecure) {
44+
ga("set", "userId", cookies.user_id__insecure);
45+
}
46+
3747
// Finally, we'll send an event to mark our page view.
3848
ga("send", "pageview");
3949
}

0 commit comments

Comments
 (0)