2018-04-20 15:00:38 +00:00
|
|
|
from base64 import b64decode
|
2016-02-15 20:13:19 +00:00
|
|
|
import logging
|
|
|
|
import re
|
|
|
|
|
|
|
|
from django.http import HttpResponse
|
|
|
|
|
|
|
|
from oidc_provider.lib.errors import BearerTokenError
|
|
|
|
from oidc_provider.models import Token
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
def extract_access_token(request):
|
|
|
|
"""
|
|
|
|
Get the access token using Authorization Request Header Field method.
|
|
|
|
Or try getting via GET.
|
|
|
|
See: http://tools.ietf.org/html/rfc6750#section-2.1
|
|
|
|
|
|
|
|
Return a string.
|
|
|
|
"""
|
|
|
|
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
|
|
|
|
|
|
|
|
if re.compile('^Bearer\s{1}.+$').match(auth_header):
|
|
|
|
access_token = auth_header.split()[1]
|
|
|
|
else:
|
|
|
|
access_token = request.GET.get('access_token', '')
|
|
|
|
|
|
|
|
return access_token
|
|
|
|
|
|
|
|
|
2018-04-20 15:00:38 +00:00
|
|
|
def extract_client_auth(request):
|
|
|
|
"""
|
|
|
|
Get client credentials using HTTP Basic Authentication method.
|
|
|
|
Or try getting parameters via POST.
|
|
|
|
See: http://tools.ietf.org/html/rfc6750#section-2.1
|
|
|
|
|
|
|
|
Return a tuple `(client_id, client_secret)`.
|
|
|
|
"""
|
|
|
|
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
|
|
|
|
|
|
|
|
if re.compile('^Basic\s{1}.+$').match(auth_header):
|
|
|
|
b64_user_pass = auth_header.split()[1]
|
|
|
|
try:
|
|
|
|
user_pass = b64decode(b64_user_pass).decode('utf-8').split(':')
|
|
|
|
client_id, client_secret = tuple(user_pass)
|
|
|
|
except Exception:
|
|
|
|
client_id = client_secret = ''
|
|
|
|
else:
|
|
|
|
client_id = request.POST.get('client_id', '')
|
|
|
|
client_secret = request.POST.get('client_secret', '')
|
|
|
|
|
|
|
|
return (client_id, client_secret)
|
|
|
|
|
|
|
|
|
2017-08-08 22:41:42 +00:00
|
|
|
def protected_resource_view(scopes=None):
|
2016-02-15 20:13:19 +00:00
|
|
|
"""
|
|
|
|
View decorator. The client accesses protected resources by presenting the
|
|
|
|
access token to the resource server.
|
|
|
|
https://tools.ietf.org/html/rfc6749#section-7
|
|
|
|
"""
|
2017-08-08 22:41:42 +00:00
|
|
|
if scopes is None:
|
|
|
|
scopes = []
|
|
|
|
|
2016-02-15 20:13:19 +00:00
|
|
|
def wrapper(view):
|
|
|
|
def view_wrapper(request, *args, **kwargs):
|
|
|
|
access_token = extract_access_token(request)
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
kwargs['token'] = Token.objects.get(access_token=access_token)
|
|
|
|
except Token.DoesNotExist:
|
2016-03-17 18:31:41 +00:00
|
|
|
logger.debug('[UserInfo] Token does not exist: %s', access_token)
|
2016-02-15 20:13:19 +00:00
|
|
|
raise BearerTokenError('invalid_token')
|
|
|
|
|
|
|
|
if kwargs['token'].has_expired():
|
2016-03-17 18:31:41 +00:00
|
|
|
logger.debug('[UserInfo] Token has expired: %s', access_token)
|
2016-02-15 20:13:19 +00:00
|
|
|
raise BearerTokenError('invalid_token')
|
|
|
|
|
|
|
|
if not set(scopes).issubset(set(kwargs['token'].scope)):
|
2016-03-17 18:31:41 +00:00
|
|
|
logger.debug('[UserInfo] Missing openid scope.')
|
2016-02-15 20:13:19 +00:00
|
|
|
raise BearerTokenError('insufficient_scope')
|
2017-08-08 22:41:42 +00:00
|
|
|
except BearerTokenError as error:
|
2016-02-15 20:13:19 +00:00
|
|
|
response = HttpResponse(status=error.status)
|
2017-08-08 22:41:42 +00:00
|
|
|
response['WWW-Authenticate'] = 'error="{0}", error_description="{1}"'.format(
|
|
|
|
error.code, error.description)
|
2016-02-15 20:13:19 +00:00
|
|
|
return response
|
|
|
|
|
|
|
|
return view(request, *args, **kwargs)
|
|
|
|
|
|
|
|
return view_wrapper
|
|
|
|
|
|
|
|
return wrapper
|