Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
Refactor get_auth_from_request
Browse files Browse the repository at this point in the history
This breaks the function into four smaller function, and renames it to
better reflect its side-effect.
  • Loading branch information
chadwhitacre committed Feb 5, 2015
1 parent 57a5973 commit bc2818d
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 42 deletions.
2 changes: 1 addition & 1 deletion gratipay/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def filter_profile_subnav(user, participant, pages):

, canonize
, i18n.set_up_i18n
, authentication.get_auth_from_request
, authentication.set_request_context_user
, csrf.get_csrf_token_from_request
, add_stuff_to_context

Expand Down
98 changes: 57 additions & 41 deletions gratipay/security/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,56 +14,72 @@
ANON = User()
BEGINNING_OF_EPOCH = to_rfc822(datetime(1970, 1, 1))

def get_auth_from_request(request):
"""Authenticate from a cookie or an API key in basic auth.
def _get_user_via_api_key(api_key):
"""Given an api_key, return a User. This auth method is deprecated.
"""
user = User()
user.participant = Participant._from_thing('api_key', api_key)
if user.participant:
p = user.participant
today = date.today()
if p.old_auth_usage != today:
Participant.db.run("""
UPDATE participants
SET old_auth_usage = %s
WHERE id = %s
""", (today, p.id))
return user

request.context['user'] = ANON # Make sure we always have a user object
def _get_user_via_basic_auth(auth_header):
"""Given a basic auth header, return a User object.
"""
try:
creds = binascii.a2b_base64(auth_header[len('Basic '):]).split(':', 1)
except binascii.Error:
raise Response(400, 'Malformed "Authorization" header')
if len(creds) != 2:
raise Response(401)
userid, api_key = creds
if len(userid) == 36 and '-' in userid:
user = _get_user_via_api_key(userid) # For backward-compatibility
else:
try:
userid = int(userid)
except ValueError:
raise Response(401)
user = User.from_id(userid)
if user.ANON or not constant_time_compare(user.participant.api_key, api_key):
raise Response(401)
return user

def _turn_off_csrf(request):
"""Given a request, short-circuit CSRF.
"""
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.
"""

user = ANON # Make sure we always have a user object

if request.line.uri.startswith('/assets/'):
return
pass
elif 'Authorization' in request.headers:
header = request.headers['authorization']
if header.startswith('Basic '):
try:
creds = binascii.a2b_base64(header[len('Basic '):]).split(':', 1)
except binascii.Error:
raise Response(400, 'Malformed "Authorization" header')
if len(creds) != 2:
raise Response(401)
userid, api_key = creds
if len(userid) == 36 and '-' in userid:
# For backward-compatibility
user = request.context['user'] = User()
user.participant = Participant._from_thing('api_key', userid)
if user.participant:
p = user.participant
today = date.today()
if p.old_auth_usage != today:
Participant.db.run("""
UPDATE participants
SET old_auth_usage = %s
WHERE id = %s
""", (today, p.id))
else:
try:
userid = int(userid)
except ValueError:
raise Response(401)
user = request.context['user'] = User.from_id(userid)
if user.ANON or not constant_time_compare(user.participant.api_key, api_key):
raise Response(401)

# We don't require CSRF if they basically authenticated.
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)
user = _get_user_via_basic_auth(header)
if not user.ANON:
_turn_off_csrf(request)
elif SESSION in request.headers.cookie:
token = request.headers.cookie[SESSION].value
request.context['user'] = User.from_session_token(token)
user = User.from_session_token(token)

request.context['user'] = user

def add_auth_to_response(response, request=None):
if request is None:
Expand Down

0 comments on commit bc2818d

Please sign in to comment.