2013-09-20 15:55:44 +00:00
|
|
|
import logging
|
|
|
|
|
|
|
|
from functools import wraps
|
2014-03-17 16:01:13 +00:00
|
|
|
from flask import request, session
|
2013-09-20 15:55:44 +00:00
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
from app import metric_queue
|
|
|
|
from auth.basic import validate_basic_auth
|
|
|
|
from auth.oauth import validate_bearer_auth
|
|
|
|
from auth.cookie import validate_session_cookie
|
|
|
|
from auth.signedgrant import validate_signed_grant
|
2014-03-17 16:01:13 +00:00
|
|
|
|
2014-02-25 19:15:12 +00:00
|
|
|
from util.http import abort
|
2013-09-20 15:55:44 +00:00
|
|
|
|
2013-09-20 22:38:17 +00:00
|
|
|
|
2013-09-20 15:55:44 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
def _auth_decorator(pass_result=False, handlers=None):
|
|
|
|
""" Builds an auth decorator that runs the given handlers and, if any return successfully,
|
|
|
|
sets up the auth context. The wrapped function will be invoked *regardless of success or
|
|
|
|
failure of the auth handler(s)*
|
|
|
|
"""
|
|
|
|
def processor(func):
|
|
|
|
@wraps(func)
|
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
auth_header = request.headers.get('authorization', '')
|
|
|
|
result = None
|
2013-09-20 15:55:44 +00:00
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
for handler in handlers:
|
|
|
|
result = handler(auth_header)
|
|
|
|
# If the handler was missing the necessary information, skip it and try the next one.
|
|
|
|
if result.missing:
|
|
|
|
continue
|
2016-05-13 18:52:22 +00:00
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
# Check for a valid result.
|
|
|
|
if result.auth_valid:
|
2017-03-24 21:43:00 +00:00
|
|
|
logger.debug('Found valid auth result: %s', result.tuple())
|
2016-05-13 18:52:22 +00:00
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
# Set the various pieces of the auth context.
|
|
|
|
result.apply_to_context()
|
2016-05-13 18:52:22 +00:00
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
# Log the metric.
|
|
|
|
metric_queue.authentication_count.Inc(labelvalues=[result.kind, True])
|
|
|
|
break
|
2016-05-13 18:52:22 +00:00
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
# Otherwise, report the error.
|
|
|
|
if result.error_message is not None:
|
|
|
|
# Log the failure.
|
|
|
|
metric_queue.authentication_count.Inc(labelvalues=[result.kind, False])
|
|
|
|
break
|
2016-05-13 18:52:22 +00:00
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
if pass_result:
|
|
|
|
kwargs['auth_result'] = result
|
2013-09-20 15:55:44 +00:00
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
return func(*args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
return processor
|
2013-09-20 15:55:44 +00:00
|
|
|
|
|
|
|
|
2017-03-16 21:05:26 +00:00
|
|
|
process_oauth = _auth_decorator(handlers=[validate_bearer_auth, validate_session_cookie])
|
|
|
|
process_auth = _auth_decorator(handlers=[validate_signed_grant, validate_basic_auth])
|
|
|
|
process_auth_or_cookie = _auth_decorator(handlers=[validate_basic_auth, validate_session_cookie])
|
|
|
|
process_basic_auth = _auth_decorator(handlers=[validate_basic_auth], pass_result=True)
|
2016-05-13 18:52:22 +00:00
|
|
|
|
|
|
|
|
2014-03-26 19:52:24 +00:00
|
|
|
def require_session_login(func):
|
2017-03-16 21:05:26 +00:00
|
|
|
""" Decorates a function and ensures that a valid session cookie exists or a 401 is raised. If
|
|
|
|
a valid session cookie does exist, the authenticated user and identity are also set.
|
|
|
|
"""
|
2014-03-26 19:52:24 +00:00
|
|
|
@wraps(func)
|
|
|
|
def wrapper(*args, **kwargs):
|
2017-03-16 21:05:26 +00:00
|
|
|
result = validate_session_cookie()
|
2018-01-05 21:27:03 +00:00
|
|
|
if result.has_nonrobot_user:
|
2017-03-16 21:05:26 +00:00
|
|
|
result.apply_to_context()
|
|
|
|
metric_queue.authentication_count.Inc(labelvalues=[result.kind, True])
|
|
|
|
return func(*args, **kwargs)
|
|
|
|
elif not result.missing:
|
|
|
|
metric_queue.authentication_count.Inc(labelvalues=[result.kind, False])
|
|
|
|
|
|
|
|
abort(401, message='Method requires login and no valid login could be loaded.')
|
2013-09-20 15:55:44 +00:00
|
|
|
return wrapper
|
2013-09-26 00:00:22 +00:00
|
|
|
|
|
|
|
|
2014-03-26 19:52:24 +00:00
|
|
|
def extract_namespace_repo_from_session(func):
|
2017-03-16 21:05:26 +00:00
|
|
|
""" Extracts the namespace and repository name from the current session (which must exist)
|
|
|
|
and passes them into the decorated function as the first and second arguments. If the
|
|
|
|
session doesn't exist or does not contain these arugments, a 400 error is raised.
|
|
|
|
"""
|
2014-03-26 19:52:24 +00:00
|
|
|
@wraps(func)
|
2013-09-26 00:00:22 +00:00
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
if 'namespace' not in session or 'repository' not in session:
|
2015-07-15 21:25:41 +00:00
|
|
|
logger.error('Unable to load namespace or repository from session: %s', session)
|
2014-03-17 18:38:50 +00:00
|
|
|
abort(400, message='Missing namespace in request')
|
2013-09-26 00:00:22 +00:00
|
|
|
|
2014-03-26 19:52:24 +00:00
|
|
|
return func(session['namespace'], session['repository'], *args, **kwargs)
|
2013-09-26 20:32:09 +00:00
|
|
|
return wrapper
|