We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
login requires Referer
This just started recently.  When I try to log in I get a blank page.  Inspecting the response in my console shows I'm getting a 500 from the server.

My browser (ABrowser): Mozilla/5.0 (X11; Linux i686; rv:33.0) Gecko/20100101 Firefox/33.0

login requires Referer
This just started recently.  When I try to log in I get a blank page.  Inspecting the response in my console shows I'm getting a 500 from the server.

My browser (ABrowser): Mozilla/5.0 (X11; Linux i686; rv:33.0) Gecko/20100101 Firefox/33.0


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gratipay/security/csrf.py`
Content:
```
1 """Cross Site Request Forgery middleware, borrowed from Django.
2 
3 See also:
4 
5     https://github.com/django/django/blob/master/django/middleware/csrf.py
6     https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
7     https://github.com/gratipay/gratipay.com/issues/88
8 
9 """
10 
11 from datetime import timedelta
12 import re
13 import urlparse
14 from aspen import log_dammit
15 
16 
17 #from django.utils.cache import patch_vary_headers
18 cc_delim_re = re.compile(r'\s*,\s*')
19 def patch_vary_headers(response, newheaders):
20     """
21     Adds (or updates) the "Vary" header in the given HttpResponse object.
22     newheaders is a list of header names that should be in "Vary". Existing
23     headers in "Vary" aren't removed.
24     """
25     # Note that we need to keep the original order intact, because cache
26     # implementations may rely on the order of the Vary contents in, say,
27     # computing an MD5 hash.
28     if 'Vary' in response.headers:
29         vary_headers = cc_delim_re.split(response.headers['Vary'])
30     else:
31         vary_headers = []
32     # Use .lower() here so we treat headers as case-insensitive.
33     existing_headers = set([header.lower() for header in vary_headers])
34     additional_headers = [newheader for newheader in newheaders
35                           if newheader.lower() not in existing_headers]
36     response.headers['Vary'] = ', '.join(vary_headers + additional_headers)
37 
38 
39 #from django.utils.http import same_origin
40 def same_origin(url1, url2):
41     """
42     Checks if two URLs are 'same-origin'
43     """
44     p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2)
45     return (p1.scheme, p1.hostname, p1.port) == (p2.scheme, p2.hostname, p2.port)
46 
47 
48 from aspen import Response
49 from crypto import constant_time_compare, get_random_string
50 
51 REASON_NO_REFERER = "Referer checking failed - no Referer."
52 REASON_BAD_REFERER = "Referer checking failed - %s does not match %s."
53 REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
54 REASON_BAD_TOKEN = "CSRF token missing or incorrect."
55 
56 TOKEN_LENGTH = 32
57 CSRF_TIMEOUT = timedelta(days=7)
58 
59 
60 def _get_new_csrf_key():
61     return get_random_string(TOKEN_LENGTH)
62 
63 
64 def _sanitize_token(token):
65     # Allow only alphanum, and ensure we return a 'str' for the sake
66     # of the post processing middleware.
67     if len(token) > TOKEN_LENGTH:
68         return _get_new_csrf_key()
69     token = re.sub('[^a-zA-Z0-9]+', '', str(token.decode('ascii', 'ignore')))
70     if token == "":
71         # In case the cookie has been truncated to nothing at some point.
72         return _get_new_csrf_key()
73     return token
74 
75 def _is_secure(request):
76     import gratipay
77     return gratipay.canonical_scheme == 'https'
78 
79 def _get_host(request):
80     """Returns the HTTP host using the request headers.
81     """
82     return request.headers.get('X-Forwarded-Host', request.headers['Host'])
83 
84 
85 
86 def get_csrf_token_from_request(request):
87     """Given a Request object, reject it if it's a forgery.
88     """
89     if request.line.uri.startswith('/assets/'): return
90     if request.line.uri.startswith('/callbacks/'): return
91 
92     try:
93         csrf_token = _sanitize_token(request.headers.cookie['csrf_token'].value)
94     except KeyError:
95         csrf_token = None
96 
97     request.context['csrf_token'] = csrf_token or _get_new_csrf_key()
98 
99     # Assume that anything not defined as 'safe' by RC2616 needs protection
100     if request.line.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
101 
102         if _is_secure(request):
103             # Suppose user visits http://example.com/
104             # An active network attacker (man-in-the-middle, MITM) sends a
105             # POST form that targets https://example.com/detonate-bomb/ and
106             # submits it via JavaScript.
107             #
108             # The attacker will need to provide a CSRF cookie and token, but
109             # that's no problem for a MITM and the session-independent
110             # nonce we're using. So the MITM can circumvent the CSRF
111             # protection. This is true for any HTTP connection, but anyone
112             # using HTTPS expects better! For this reason, for
113             # https://example.com/ we need additional protection that treats
114             # http://example.com/ as completely untrusted. Under HTTPS,
115             # Barth et al. found that the Referer header is missing for
116             # same-domain requests in only about 0.2% of cases or less, so
117             # we can use strict Referer checking.
118             referer = request.headers.get('Referer')
119             if referer is None:
120                 raise Response(403, REASON_NO_REFERER)
121 
122             good_referer = 'https://%s/' % _get_host(request)
123             if not same_origin(referer, good_referer):
124                 reason = REASON_BAD_REFERER % (referer, good_referer)
125                 log_dammit(reason)
126                 raise Response(403, reason)
127 
128         if csrf_token is None:
129             raise Response(403, REASON_NO_CSRF_COOKIE)
130 
131         # Check non-cookie token for match.
132         request_csrf_token = ""
133         if request.line.method == "POST":
134             if isinstance(request.body, dict):
135                 request_csrf_token = request.body.get('csrf_token', '')
136 
137         if request_csrf_token == "":
138             # Fall back to X-CSRF-TOKEN, to make things easier for AJAX,
139             # and possible for PUT/DELETE.
140             request_csrf_token = request.headers.get('X-CSRF-TOKEN', '')
141 
142         if not constant_time_compare(request_csrf_token, csrf_token):
143             raise Response(403, REASON_BAD_TOKEN)
144 
145 
146 def add_csrf_token_to_response(response, request=None):
147     """Store the latest CSRF token as a cookie.
148     """
149     if request is None:
150         return  # early parsing must've failed
151     csrf_token = request.context.get('csrf_token')
152     if csrf_token:
153         # Don't set httponly so that we can POST using XHR.
154         # https://github.com/gratipay/gratipay.com/issues/3030
155         response.set_cookie('csrf_token', csrf_token, expires=CSRF_TIMEOUT, httponly=False)
156 
157         # Content varies with the CSRF cookie, so set the Vary header.
158         patch_vary_headers(response, ('Cookie',))
159
```

Path: `gratipay/security/authentication.py`
Content:
```
1 """Defines website authentication helpers.
2 """
3 import binascii
4 from datetime import date, datetime
5 
6 from aspen import Response
7 from aspen.utils import to_rfc822
8 from gratipay.models.participant import Participant
9 from gratipay.security import csrf
10 from gratipay.security.crypto import constant_time_compare
11 from gratipay.security.user import User, SESSION
12 
13 
14 ANON = User()
15 BEGINNING_OF_EPOCH = to_rfc822(datetime(1970, 1, 1))
16 
17 def _get_user_via_api_key(api_key):
18     """Given an api_key, return a User. This auth method is deprecated.
19     """
20     user = User()
21     user.participant = Participant._from_thing('api_key', api_key)
22     if user.participant:
23         p = user.participant
24         today = date.today()
25         if p.old_auth_usage != today:
26             Participant.db.run("""
27                 UPDATE participants
28                    SET old_auth_usage = %s
29                  WHERE id = %s
30             """, (today, p.id))
31     return user
32 
33 def _get_user_via_basic_auth(auth_header):
34     """Given a basic auth header, return a User object.
35     """
36     try:
37         creds = binascii.a2b_base64(auth_header[len('Basic '):]).split(':', 1)
38     except binascii.Error:
39         raise Response(400, 'Malformed "Authorization" header')
40     if len(creds) != 2:
41         raise Response(401)
42     userid, api_key = creds
43     if len(userid) == 36 and '-' in userid:
44         user = _get_user_via_api_key(userid)  # For backward-compatibility
45     else:
46         try:
47             userid = int(userid)
48         except ValueError:
49             raise Response(401)
50         user = User.from_id(userid)
51         if user.ANON or not constant_time_compare(user.participant.api_key, api_key):
52             raise Response(401)
53     return user
54 
55 def _turn_off_csrf(request):
56     """Given a request, short-circuit CSRF.
57     """
58     csrf_token = csrf._get_new_csrf_key()
59     request.headers.cookie['csrf_token'] = csrf_token
60     request.headers['X-CSRF-TOKEN'] = csrf_token
61     if 'Referer' not in request.headers:
62         request.headers['Referer'] = 'https://%s/' % csrf._get_host(request)
63 
64 def set_request_context_user(request):
65     """Set request.context['user']. This signs the user in.
66     """
67 
68     request.context['user'] = user = ANON  # Make sure we always have a user object, even if
69                                            # there's an exception in the rest of this function.
70 
71     if request.line.uri.startswith('/assets/'):
72         pass
73     elif 'Authorization' in request.headers:
74         header = request.headers['authorization']
75         if header.startswith('Basic '):
76             user = _get_user_via_basic_auth(header)
77             if not user.ANON:
78                 _turn_off_csrf(request)
79     elif SESSION in request.headers.cookie:
80         token = request.headers.cookie[SESSION].value
81         user = User.from_session_token(token)
82 
83     request.context['user'] = user
84 
85 def add_auth_to_response(response, request=None):
86     if request is None:
87         return  # early parsing must've failed
88     if request.line.uri.startswith('/assets/'):
89         return  # assets never get auth headers and have their own caching done elsewhere
90 
91     response.headers['Expires'] = BEGINNING_OF_EPOCH # don't cache
92 
93     if SESSION in request.headers.cookie:
94         user = request.context.get('user') or ANON
95         if not user.ANON:
96             user.keep_signed_in(response.headers.cookie)
97
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gratipay/security/authentication.py b/gratipay/security/authentication.py
--- a/gratipay/security/authentication.py
+++ b/gratipay/security/authentication.py
@@ -58,8 +58,6 @@
     csrf_token = csrf._get_new_csrf_key()
     request.headers.cookie['csrf_token'] = csrf_token
     request.headers['X-CSRF-TOKEN'] = csrf_token
-    if 'Referer' not in request.headers:
-        request.headers['Referer'] = 'https://%s/' % csrf._get_host(request)
 
 def set_request_context_user(request):
     """Set request.context['user']. This signs the user in.
diff --git a/gratipay/security/csrf.py b/gratipay/security/csrf.py
--- a/gratipay/security/csrf.py
+++ b/gratipay/security/csrf.py
@@ -10,8 +10,6 @@
 
 from datetime import timedelta
 import re
-import urlparse
-from aspen import log_dammit
 
 
 #from django.utils.cache import patch_vary_headers
@@ -36,20 +34,9 @@
     response.headers['Vary'] = ', '.join(vary_headers + additional_headers)
 
 
-#from django.utils.http import same_origin
-def same_origin(url1, url2):
-    """
-    Checks if two URLs are 'same-origin'
-    """
-    p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2)
-    return (p1.scheme, p1.hostname, p1.port) == (p2.scheme, p2.hostname, p2.port)
-
-
 from aspen import Response
 from crypto import constant_time_compare, get_random_string
 
-REASON_NO_REFERER = "Referer checking failed - no Referer."
-REASON_BAD_REFERER = "Referer checking failed - %s does not match %s."
 REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
 REASON_BAD_TOKEN = "CSRF token missing or incorrect."
 
@@ -72,15 +59,6 @@
         return _get_new_csrf_key()
     return token
 
-def _is_secure(request):
-    import gratipay
-    return gratipay.canonical_scheme == 'https'
-
-def _get_host(request):
-    """Returns the HTTP host using the request headers.
-    """
-    return request.headers.get('X-Forwarded-Host', request.headers['Host'])
-
 
 
 def get_csrf_token_from_request(request):
@@ -99,32 +77,6 @@
     # Assume that anything not defined as 'safe' by RC2616 needs protection
     if request.line.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
 
-        if _is_secure(request):
-            # Suppose user visits http://example.com/
-            # An active network attacker (man-in-the-middle, MITM) sends a
-            # POST form that targets https://example.com/detonate-bomb/ and
-            # submits it via JavaScript.
-            #
-            # The attacker will need to provide a CSRF cookie and token, but
-            # that's no problem for a MITM and the session-independent
-            # nonce we're using. So the MITM can circumvent the CSRF
-            # protection. This is true for any HTTP connection, but anyone
-            # using HTTPS expects better! For this reason, for
-            # https://example.com/ we need additional protection that treats
-            # http://example.com/ as completely untrusted. Under HTTPS,
-            # Barth et al. found that the Referer header is missing for
-            # same-domain requests in only about 0.2% of cases or less, so
-            # we can use strict Referer checking.
-            referer = request.headers.get('Referer')
-            if referer is None:
-                raise Response(403, REASON_NO_REFERER)
-
-            good_referer = 'https://%s/' % _get_host(request)
-            if not same_origin(referer, good_referer):
-                reason = REASON_BAD_REFERER % (referer, good_referer)
-                log_dammit(reason)
-                raise Response(403, reason)
-
         if csrf_token is None:
             raise Response(403, REASON_NO_CSRF_COOKIE)

{"golden_diff": "diff --git a/gratipay/security/authentication.py b/gratipay/security/authentication.py\n--- a/gratipay/security/authentication.py\n+++ b/gratipay/security/authentication.py\n@@ -58,8 +58,6 @@\n     csrf_token = csrf._get_new_csrf_key()\n     request.headers.cookie['csrf_token'] = csrf_token\n     request.headers['X-CSRF-TOKEN'] = csrf_token\n-    if 'Referer' not in request.headers:\n-        request.headers['Referer'] = 'https://%s/' % csrf._get_host(request)\n \n def set_request_context_user(request):\n     \"\"\"Set request.context['user']. This signs the user in.\ndiff --git a/gratipay/security/csrf.py b/gratipay/security/csrf.py\n--- a/gratipay/security/csrf.py\n+++ b/gratipay/security/csrf.py\n@@ -10,8 +10,6 @@\n \n from datetime import timedelta\n import re\n-import urlparse\n-from aspen import log_dammit\n \n \n #from django.utils.cache import patch_vary_headers\n@@ -36,20 +34,9 @@\n     response.headers['Vary'] = ', '.join(vary_headers + additional_headers)\n \n \n-#from django.utils.http import same_origin\n-def same_origin(url1, url2):\n-    \"\"\"\n-    Checks if two URLs are 'same-origin'\n-    \"\"\"\n-    p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2)\n-    return (p1.scheme, p1.hostname, p1.port) == (p2.scheme, p2.hostname, p2.port)\n-\n-\n from aspen import Response\n from crypto import constant_time_compare, get_random_string\n \n-REASON_NO_REFERER = \"Referer checking failed - no Referer.\"\n-REASON_BAD_REFERER = \"Referer checking failed - %s does not match %s.\"\n REASON_NO_CSRF_COOKIE = \"CSRF cookie not set.\"\n REASON_BAD_TOKEN = \"CSRF token missing or incorrect.\"\n \n@@ -72,15 +59,6 @@\n         return _get_new_csrf_key()\n     return token\n \n-def _is_secure(request):\n-    import gratipay\n-    return gratipay.canonical_scheme == 'https'\n-\n-def _get_host(request):\n-    \"\"\"Returns the HTTP host using the request headers.\n-    \"\"\"\n-    return request.headers.get('X-Forwarded-Host', request.headers['Host'])\n-\n \n \n def get_csrf_token_from_request(request):\n@@ -99,32 +77,6 @@\n     # Assume that anything not defined as 'safe' by RC2616 needs protection\n     if request.line.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):\n \n-        if _is_secure(request):\n-            # Suppose user visits http://example.com/\n-            # An active network attacker (man-in-the-middle, MITM) sends a\n-            # POST form that targets https://example.com/detonate-bomb/ and\n-            # submits it via JavaScript.\n-            #\n-            # The attacker will need to provide a CSRF cookie and token, but\n-            # that's no problem for a MITM and the session-independent\n-            # nonce we're using. So the MITM can circumvent the CSRF\n-            # protection. This is true for any HTTP connection, but anyone\n-            # using HTTPS expects better! For this reason, for\n-            # https://example.com/ we need additional protection that treats\n-            # http://example.com/ as completely untrusted. Under HTTPS,\n-            # Barth et al. found that the Referer header is missing for\n-            # same-domain requests in only about 0.2% of cases or less, so\n-            # we can use strict Referer checking.\n-            referer = request.headers.get('Referer')\n-            if referer is None:\n-                raise Response(403, REASON_NO_REFERER)\n-\n-            good_referer = 'https://%s/' % _get_host(request)\n-            if not same_origin(referer, good_referer):\n-                reason = REASON_BAD_REFERER % (referer, good_referer)\n-                log_dammit(reason)\n-                raise Response(403, reason)\n-\n         if csrf_token is None:\n             raise Response(403, REASON_NO_CSRF_COOKIE)\n", "issue": "login requires Referer\nThis just started recently.  When I try to log in I get a blank page.  Inspecting the response in my console shows I'm getting a 500 from the server.\n\nMy browser (ABrowser): Mozilla/5.0 (X11; Linux i686; rv:33.0) Gecko/20100101 Firefox/33.0\n\nlogin requires Referer\nThis just started recently.  When I try to log in I get a blank page.  Inspecting the response in my console shows I'm getting a 500 from the server.\n\nMy browser (ABrowser): Mozilla/5.0 (X11; Linux i686; rv:33.0) Gecko/20100101 Firefox/33.0\n\n", "before_files": [{"content": "\"\"\"Cross Site Request Forgery middleware, borrowed from Django.\n\nSee also:\n\n    https://github.com/django/django/blob/master/django/middleware/csrf.py\n    https://docs.djangoproject.com/en/dev/ref/contrib/csrf/\n    https://github.com/gratipay/gratipay.com/issues/88\n\n\"\"\"\n\nfrom datetime import timedelta\nimport re\nimport urlparse\nfrom aspen import log_dammit\n\n\n#from django.utils.cache import patch_vary_headers\ncc_delim_re = re.compile(r'\\s*,\\s*')\ndef patch_vary_headers(response, newheaders):\n    \"\"\"\n    Adds (or updates) the \"Vary\" header in the given HttpResponse object.\n    newheaders is a list of header names that should be in \"Vary\". Existing\n    headers in \"Vary\" aren't removed.\n    \"\"\"\n    # Note that we need to keep the original order intact, because cache\n    # implementations may rely on the order of the Vary contents in, say,\n    # computing an MD5 hash.\n    if 'Vary' in response.headers:\n        vary_headers = cc_delim_re.split(response.headers['Vary'])\n    else:\n        vary_headers = []\n    # Use .lower() here so we treat headers as case-insensitive.\n    existing_headers = set([header.lower() for header in vary_headers])\n    additional_headers = [newheader for newheader in newheaders\n                          if newheader.lower() not in existing_headers]\n    response.headers['Vary'] = ', '.join(vary_headers + additional_headers)\n\n\n#from django.utils.http import same_origin\ndef same_origin(url1, url2):\n    \"\"\"\n    Checks if two URLs are 'same-origin'\n    \"\"\"\n    p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2)\n    return (p1.scheme, p1.hostname, p1.port) == (p2.scheme, p2.hostname, p2.port)\n\n\nfrom aspen import Response\nfrom crypto import constant_time_compare, get_random_string\n\nREASON_NO_REFERER = \"Referer checking failed - no Referer.\"\nREASON_BAD_REFERER = \"Referer checking failed - %s does not match %s.\"\nREASON_NO_CSRF_COOKIE = \"CSRF cookie not set.\"\nREASON_BAD_TOKEN = \"CSRF token missing or incorrect.\"\n\nTOKEN_LENGTH = 32\nCSRF_TIMEOUT = timedelta(days=7)\n\n\ndef _get_new_csrf_key():\n    return get_random_string(TOKEN_LENGTH)\n\n\ndef _sanitize_token(token):\n    # Allow only alphanum, and ensure we return a 'str' for the sake\n    # of the post processing middleware.\n    if len(token) > TOKEN_LENGTH:\n        return _get_new_csrf_key()\n    token = re.sub('[^a-zA-Z0-9]+', '', str(token.decode('ascii', 'ignore')))\n    if token == \"\":\n        # In case the cookie has been truncated to nothing at some point.\n        return _get_new_csrf_key()\n    return token\n\ndef _is_secure(request):\n    import gratipay\n    return gratipay.canonical_scheme == 'https'\n\ndef _get_host(request):\n    \"\"\"Returns the HTTP host using the request headers.\n    \"\"\"\n    return request.headers.get('X-Forwarded-Host', request.headers['Host'])\n\n\n\ndef get_csrf_token_from_request(request):\n    \"\"\"Given a Request object, reject it if it's a forgery.\n    \"\"\"\n    if request.line.uri.startswith('/assets/'): return\n    if request.line.uri.startswith('/callbacks/'): return\n\n    try:\n        csrf_token = _sanitize_token(request.headers.cookie['csrf_token'].value)\n    except KeyError:\n        csrf_token = None\n\n    request.context['csrf_token'] = csrf_token or _get_new_csrf_key()\n\n    # Assume that anything not defined as 'safe' by RC2616 needs protection\n    if request.line.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):\n\n        if _is_secure(request):\n            # Suppose user visits http://example.com/\n            # An active network attacker (man-in-the-middle, MITM) sends a\n            # POST form that targets https://example.com/detonate-bomb/ and\n            # submits it via JavaScript.\n            #\n            # The attacker will need to provide a CSRF cookie and token, but\n            # that's no problem for a MITM and the session-independent\n            # nonce we're using. So the MITM can circumvent the CSRF\n            # protection. This is true for any HTTP connection, but anyone\n            # using HTTPS expects better! For this reason, for\n            # https://example.com/ we need additional protection that treats\n            # http://example.com/ as completely untrusted. Under HTTPS,\n            # Barth et al. found that the Referer header is missing for\n            # same-domain requests in only about 0.2% of cases or less, so\n            # we can use strict Referer checking.\n            referer = request.headers.get('Referer')\n            if referer is None:\n                raise Response(403, REASON_NO_REFERER)\n\n            good_referer = 'https://%s/' % _get_host(request)\n            if not same_origin(referer, good_referer):\n                reason = REASON_BAD_REFERER % (referer, good_referer)\n                log_dammit(reason)\n                raise Response(403, reason)\n\n        if csrf_token is None:\n            raise Response(403, REASON_NO_CSRF_COOKIE)\n\n        # Check non-cookie token for match.\n        request_csrf_token = \"\"\n        if request.line.method == \"POST\":\n            if isinstance(request.body, dict):\n                request_csrf_token = request.body.get('csrf_token', '')\n\n        if request_csrf_token == \"\":\n            # Fall back to X-CSRF-TOKEN, to make things easier for AJAX,\n            # and possible for PUT/DELETE.\n            request_csrf_token = request.headers.get('X-CSRF-TOKEN', '')\n\n        if not constant_time_compare(request_csrf_token, csrf_token):\n            raise Response(403, REASON_BAD_TOKEN)\n\n\ndef add_csrf_token_to_response(response, request=None):\n    \"\"\"Store the latest CSRF token as a cookie.\n    \"\"\"\n    if request is None:\n        return  # early parsing must've failed\n    csrf_token = request.context.get('csrf_token')\n    if csrf_token:\n        # Don't set httponly so that we can POST using XHR.\n        # https://github.com/gratipay/gratipay.com/issues/3030\n        response.set_cookie('csrf_token', csrf_token, expires=CSRF_TIMEOUT, httponly=False)\n\n        # Content varies with the CSRF cookie, so set the Vary header.\n        patch_vary_headers(response, ('Cookie',))\n", "path": "gratipay/security/csrf.py"}, {"content": "\"\"\"Defines website authentication helpers.\n\"\"\"\nimport binascii\nfrom datetime import date, datetime\n\nfrom aspen import Response\nfrom aspen.utils import to_rfc822\nfrom gratipay.models.participant import Participant\nfrom gratipay.security import csrf\nfrom gratipay.security.crypto import constant_time_compare\nfrom gratipay.security.user import User, SESSION\n\n\nANON = User()\nBEGINNING_OF_EPOCH = to_rfc822(datetime(1970, 1, 1))\n\ndef _get_user_via_api_key(api_key):\n    \"\"\"Given an api_key, return a User. This auth method is deprecated.\n    \"\"\"\n    user = User()\n    user.participant = Participant._from_thing('api_key', api_key)\n    if user.participant:\n        p = user.participant\n        today = date.today()\n        if p.old_auth_usage != today:\n            Participant.db.run(\"\"\"\n                UPDATE participants\n                   SET old_auth_usage = %s\n                 WHERE id = %s\n            \"\"\", (today, p.id))\n    return user\n\ndef _get_user_via_basic_auth(auth_header):\n    \"\"\"Given a basic auth header, return a User object.\n    \"\"\"\n    try:\n        creds = binascii.a2b_base64(auth_header[len('Basic '):]).split(':', 1)\n    except binascii.Error:\n        raise Response(400, 'Malformed \"Authorization\" header')\n    if len(creds) != 2:\n        raise Response(401)\n    userid, api_key = creds\n    if len(userid) == 36 and '-' in userid:\n        user = _get_user_via_api_key(userid)  # For backward-compatibility\n    else:\n        try:\n            userid = int(userid)\n        except ValueError:\n            raise Response(401)\n        user = User.from_id(userid)\n        if user.ANON or not constant_time_compare(user.participant.api_key, api_key):\n            raise Response(401)\n    return user\n\ndef _turn_off_csrf(request):\n    \"\"\"Given a request, short-circuit CSRF.\n    \"\"\"\n    csrf_token = csrf._get_new_csrf_key()\n    request.headers.cookie['csrf_token'] = csrf_token\n    request.headers['X-CSRF-TOKEN'] = csrf_token\n    if 'Referer' not in request.headers:\n        request.headers['Referer'] = 'https://%s/' % csrf._get_host(request)\n\ndef set_request_context_user(request):\n    \"\"\"Set request.context['user']. This signs the user in.\n    \"\"\"\n\n    request.context['user'] = user = ANON  # Make sure we always have a user object, even if\n                                           # there's an exception in the rest of this function.\n\n    if request.line.uri.startswith('/assets/'):\n        pass\n    elif 'Authorization' in request.headers:\n        header = request.headers['authorization']\n        if header.startswith('Basic '):\n            user = _get_user_via_basic_auth(header)\n            if not user.ANON:\n                _turn_off_csrf(request)\n    elif SESSION in request.headers.cookie:\n        token = request.headers.cookie[SESSION].value\n        user = User.from_session_token(token)\n\n    request.context['user'] = user\n\ndef add_auth_to_response(response, request=None):\n    if request is None:\n        return  # early parsing must've failed\n    if request.line.uri.startswith('/assets/'):\n        return  # assets never get auth headers and have their own caching done elsewhere\n\n    response.headers['Expires'] = BEGINNING_OF_EPOCH # don't cache\n\n    if SESSION in request.headers.cookie:\n        user = request.context.get('user') or ANON\n        if not user.ANON:\n            user.keep_signed_in(response.headers.cookie)\n", "path": "gratipay/security/authentication.py"}], "after_files": [{"content": "\"\"\"Cross Site Request Forgery middleware, borrowed from Django.\n\nSee also:\n\n    https://github.com/django/django/blob/master/django/middleware/csrf.py\n    https://docs.djangoproject.com/en/dev/ref/contrib/csrf/\n    https://github.com/gratipay/gratipay.com/issues/88\n\n\"\"\"\n\nfrom datetime import timedelta\nimport re\n\n\n#from django.utils.cache import patch_vary_headers\ncc_delim_re = re.compile(r'\\s*,\\s*')\ndef patch_vary_headers(response, newheaders):\n    \"\"\"\n    Adds (or updates) the \"Vary\" header in the given HttpResponse object.\n    newheaders is a list of header names that should be in \"Vary\". Existing\n    headers in \"Vary\" aren't removed.\n    \"\"\"\n    # Note that we need to keep the original order intact, because cache\n    # implementations may rely on the order of the Vary contents in, say,\n    # computing an MD5 hash.\n    if 'Vary' in response.headers:\n        vary_headers = cc_delim_re.split(response.headers['Vary'])\n    else:\n        vary_headers = []\n    # Use .lower() here so we treat headers as case-insensitive.\n    existing_headers = set([header.lower() for header in vary_headers])\n    additional_headers = [newheader for newheader in newheaders\n                          if newheader.lower() not in existing_headers]\n    response.headers['Vary'] = ', '.join(vary_headers + additional_headers)\n\n\nfrom aspen import Response\nfrom crypto import constant_time_compare, get_random_string\n\nREASON_NO_CSRF_COOKIE = \"CSRF cookie not set.\"\nREASON_BAD_TOKEN = \"CSRF token missing or incorrect.\"\n\nTOKEN_LENGTH = 32\nCSRF_TIMEOUT = timedelta(days=7)\n\n\ndef _get_new_csrf_key():\n    return get_random_string(TOKEN_LENGTH)\n\n\ndef _sanitize_token(token):\n    # Allow only alphanum, and ensure we return a 'str' for the sake\n    # of the post processing middleware.\n    if len(token) > TOKEN_LENGTH:\n        return _get_new_csrf_key()\n    token = re.sub('[^a-zA-Z0-9]+', '', str(token.decode('ascii', 'ignore')))\n    if token == \"\":\n        # In case the cookie has been truncated to nothing at some point.\n        return _get_new_csrf_key()\n    return token\n\n\n\ndef get_csrf_token_from_request(request):\n    \"\"\"Given a Request object, reject it if it's a forgery.\n    \"\"\"\n    if request.line.uri.startswith('/assets/'): return\n    if request.line.uri.startswith('/callbacks/'): return\n\n    try:\n        csrf_token = _sanitize_token(request.headers.cookie['csrf_token'].value)\n    except KeyError:\n        csrf_token = None\n\n    request.context['csrf_token'] = csrf_token or _get_new_csrf_key()\n\n    # Assume that anything not defined as 'safe' by RC2616 needs protection\n    if request.line.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):\n\n        if csrf_token is None:\n            raise Response(403, REASON_NO_CSRF_COOKIE)\n\n        # Check non-cookie token for match.\n        request_csrf_token = \"\"\n        if request.line.method == \"POST\":\n            if isinstance(request.body, dict):\n                request_csrf_token = request.body.get('csrf_token', '')\n\n        if request_csrf_token == \"\":\n            # Fall back to X-CSRF-TOKEN, to make things easier for AJAX,\n            # and possible for PUT/DELETE.\n            request_csrf_token = request.headers.get('X-CSRF-TOKEN', '')\n\n        if not constant_time_compare(request_csrf_token, csrf_token):\n            raise Response(403, REASON_BAD_TOKEN)\n\n\ndef add_csrf_token_to_response(response, request=None):\n    \"\"\"Store the latest CSRF token as a cookie.\n    \"\"\"\n    if request is None:\n        return  # early parsing must've failed\n    csrf_token = request.context.get('csrf_token')\n    if csrf_token:\n        # Don't set httponly so that we can POST using XHR.\n        # https://github.com/gratipay/gratipay.com/issues/3030\n        response.set_cookie('csrf_token', csrf_token, expires=CSRF_TIMEOUT, httponly=False)\n\n        # Content varies with the CSRF cookie, so set the Vary header.\n        patch_vary_headers(response, ('Cookie',))\n", "path": "gratipay/security/csrf.py"}, {"content": "\"\"\"Defines website authentication helpers.\n\"\"\"\nimport binascii\nfrom datetime import date, datetime\n\nfrom aspen import Response\nfrom aspen.utils import to_rfc822\nfrom gratipay.models.participant import Participant\nfrom gratipay.security import csrf\nfrom gratipay.security.crypto import constant_time_compare\nfrom gratipay.security.user import User, SESSION\n\n\nANON = User()\nBEGINNING_OF_EPOCH = to_rfc822(datetime(1970, 1, 1))\n\ndef _get_user_via_api_key(api_key):\n    \"\"\"Given an api_key, return a User. This auth method is deprecated.\n    \"\"\"\n    user = User()\n    user.participant = Participant._from_thing('api_key', api_key)\n    if user.participant:\n        p = user.participant\n        today = date.today()\n        if p.old_auth_usage != today:\n            Participant.db.run(\"\"\"\n                UPDATE participants\n                   SET old_auth_usage = %s\n                 WHERE id = %s\n            \"\"\", (today, p.id))\n    return user\n\ndef _get_user_via_basic_auth(auth_header):\n    \"\"\"Given a basic auth header, return a User object.\n    \"\"\"\n    try:\n        creds = binascii.a2b_base64(auth_header[len('Basic '):]).split(':', 1)\n    except binascii.Error:\n        raise Response(400, 'Malformed \"Authorization\" header')\n    if len(creds) != 2:\n        raise Response(401)\n    userid, api_key = creds\n    if len(userid) == 36 and '-' in userid:\n        user = _get_user_via_api_key(userid)  # For backward-compatibility\n    else:\n        try:\n            userid = int(userid)\n        except ValueError:\n            raise Response(401)\n        user = User.from_id(userid)\n        if user.ANON or not constant_time_compare(user.participant.api_key, api_key):\n            raise Response(401)\n    return user\n\ndef _turn_off_csrf(request):\n    \"\"\"Given a request, short-circuit CSRF.\n    \"\"\"\n    csrf_token = csrf._get_new_csrf_key()\n    request.headers.cookie['csrf_token'] = csrf_token\n    request.headers['X-CSRF-TOKEN'] = csrf_token\n\ndef set_request_context_user(request):\n    \"\"\"Set request.context['user']. This signs the user in.\n    \"\"\"\n\n    request.context['user'] = user = ANON  # Make sure we always have a user object, even if\n                                           # there's an exception in the rest of this function.\n\n    if request.line.uri.startswith('/assets/'):\n        pass\n    elif 'Authorization' in request.headers:\n        header = request.headers['authorization']\n        if header.startswith('Basic '):\n            user = _get_user_via_basic_auth(header)\n            if not user.ANON:\n                _turn_off_csrf(request)\n    elif SESSION in request.headers.cookie:\n        token = request.headers.cookie[SESSION].value\n        user = User.from_session_token(token)\n\n    request.context['user'] = user\n\ndef add_auth_to_response(response, request=None):\n    if request is None:\n        return  # early parsing must've failed\n    if request.line.uri.startswith('/assets/'):\n        return  # assets never get auth headers and have their own caching done elsewhere\n\n    response.headers['Expires'] = BEGINNING_OF_EPOCH # don't cache\n\n    if SESSION in request.headers.cookie:\n        user = request.context.get('user') or ANON\n        if not user.ANON:\n            user.keep_signed_in(response.headers.cookie)\n", "path": "gratipay/security/authentication.py"}]}