venv added, updated
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# flake8: noqa
|
||||
|
||||
from .authorization_server import AuthorizationServer
|
||||
from .resource_protector import ResourceProtector, BearerTokenValidator
|
||||
from .endpoints import RevocationEndpoint
|
||||
from .signals import (
|
||||
client_authenticated,
|
||||
token_authenticated,
|
||||
token_revoked
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,118 @@
|
||||
from django.http import HttpResponse
|
||||
from django.utils.module_loading import import_string
|
||||
from django.conf import settings
|
||||
from authlib.oauth2 import (
|
||||
AuthorizationServer as _AuthorizationServer,
|
||||
)
|
||||
from authlib.oauth2.rfc6750 import BearerTokenGenerator
|
||||
from authlib.common.security import generate_token as _generate_token
|
||||
from authlib.common.encoding import json_dumps
|
||||
from .requests import DjangoOAuth2Request, DjangoJsonRequest
|
||||
from .signals import client_authenticated, token_revoked
|
||||
|
||||
|
||||
class AuthorizationServer(_AuthorizationServer):
|
||||
"""Django implementation of :class:`authlib.oauth2.rfc6749.AuthorizationServer`.
|
||||
Initialize it with client model and token model::
|
||||
|
||||
from authlib.integrations.django_oauth2 import AuthorizationServer
|
||||
from your_project.models import OAuth2Client, OAuth2Token
|
||||
|
||||
server = AuthorizationServer(OAuth2Client, OAuth2Token)
|
||||
"""
|
||||
|
||||
def __init__(self, client_model, token_model):
|
||||
self.config = getattr(settings, 'AUTHLIB_OAUTH2_PROVIDER', {})
|
||||
self.client_model = client_model
|
||||
self.token_model = token_model
|
||||
scopes_supported = self.config.get('scopes_supported')
|
||||
super().__init__(scopes_supported=scopes_supported)
|
||||
# add default token generator
|
||||
self.register_token_generator('default', self.create_bearer_token_generator())
|
||||
|
||||
def query_client(self, client_id):
|
||||
"""Default method for ``AuthorizationServer.query_client``. Developers MAY
|
||||
rewrite this function to meet their own needs.
|
||||
"""
|
||||
try:
|
||||
return self.client_model.objects.get(client_id=client_id)
|
||||
except self.client_model.DoesNotExist:
|
||||
return None
|
||||
|
||||
def save_token(self, token, request):
|
||||
"""Default method for ``AuthorizationServer.save_token``. Developers MAY
|
||||
rewrite this function to meet their own needs.
|
||||
"""
|
||||
client = request.client
|
||||
if request.user:
|
||||
user_id = request.user.pk
|
||||
else:
|
||||
user_id = client.user_id
|
||||
item = self.token_model(
|
||||
client_id=client.client_id,
|
||||
user_id=user_id,
|
||||
**token
|
||||
)
|
||||
item.save()
|
||||
return item
|
||||
|
||||
def create_oauth2_request(self, request):
|
||||
return DjangoOAuth2Request(request)
|
||||
|
||||
def create_json_request(self, request):
|
||||
return DjangoJsonRequest(request)
|
||||
|
||||
def handle_response(self, status_code, payload, headers):
|
||||
if isinstance(payload, dict):
|
||||
payload = json_dumps(payload)
|
||||
resp = HttpResponse(payload, status=status_code)
|
||||
for k, v in headers:
|
||||
resp[k] = v
|
||||
return resp
|
||||
|
||||
def send_signal(self, name, *args, **kwargs):
|
||||
if name == 'after_authenticate_client':
|
||||
client_authenticated.send(sender=self.__class__, *args, **kwargs)
|
||||
elif name == 'after_revoke_token':
|
||||
token_revoked.send(sender=self.__class__, *args, **kwargs)
|
||||
|
||||
def create_bearer_token_generator(self):
|
||||
"""Default method to create BearerToken generator."""
|
||||
conf = self.config.get('access_token_generator', True)
|
||||
access_token_generator = create_token_generator(conf, 42)
|
||||
|
||||
conf = self.config.get('refresh_token_generator', False)
|
||||
refresh_token_generator = create_token_generator(conf, 48)
|
||||
|
||||
conf = self.config.get('token_expires_in')
|
||||
expires_generator = create_token_expires_in_generator(conf)
|
||||
|
||||
return BearerTokenGenerator(
|
||||
access_token_generator=access_token_generator,
|
||||
refresh_token_generator=refresh_token_generator,
|
||||
expires_generator=expires_generator,
|
||||
)
|
||||
|
||||
|
||||
def create_token_generator(token_generator_conf, length=42):
|
||||
if callable(token_generator_conf):
|
||||
return token_generator_conf
|
||||
|
||||
if isinstance(token_generator_conf, str):
|
||||
return import_string(token_generator_conf)
|
||||
elif token_generator_conf is True:
|
||||
def token_generator(*args, **kwargs):
|
||||
return _generate_token(length)
|
||||
return token_generator
|
||||
|
||||
|
||||
def create_token_expires_in_generator(expires_in_conf=None):
|
||||
data = {}
|
||||
data.update(BearerTokenGenerator.GRANT_TYPES_EXPIRES_IN)
|
||||
if expires_in_conf:
|
||||
data.update(expires_in_conf)
|
||||
|
||||
def expires_in(client, grant_type):
|
||||
return data.get(grant_type, BearerTokenGenerator.DEFAULT_EXPIRES_IN)
|
||||
|
||||
return expires_in
|
||||
@@ -0,0 +1,56 @@
|
||||
from authlib.oauth2.rfc7009 import RevocationEndpoint as _RevocationEndpoint
|
||||
|
||||
|
||||
class RevocationEndpoint(_RevocationEndpoint):
|
||||
"""The revocation endpoint for OAuth authorization servers allows clients
|
||||
to notify the authorization server that a previously obtained refresh or
|
||||
access token is no longer needed.
|
||||
|
||||
Register it into authorization server, and create token endpoint response
|
||||
for token revocation::
|
||||
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
# see register into authorization server instance
|
||||
server.register_endpoint(RevocationEndpoint)
|
||||
|
||||
@require_http_methods(["POST"])
|
||||
def revoke_token(request):
|
||||
return server.create_endpoint_response(
|
||||
RevocationEndpoint.ENDPOINT_NAME,
|
||||
request
|
||||
)
|
||||
"""
|
||||
|
||||
def query_token(self, token, token_type_hint):
|
||||
"""Query requested token from database."""
|
||||
token_model = self.server.token_model
|
||||
if token_type_hint == 'access_token':
|
||||
rv = _query_access_token(token_model, token)
|
||||
elif token_type_hint == 'refresh_token':
|
||||
rv = _query_refresh_token(token_model, token)
|
||||
else:
|
||||
rv = _query_access_token(token_model, token)
|
||||
if not rv:
|
||||
rv = _query_refresh_token(token_model, token)
|
||||
|
||||
return rv
|
||||
|
||||
def revoke_token(self, token, request):
|
||||
"""Mark the give token as revoked."""
|
||||
token.revoked = True
|
||||
token.save()
|
||||
|
||||
|
||||
def _query_access_token(token_model, token):
|
||||
try:
|
||||
return token_model.objects.get(access_token=token)
|
||||
except token_model.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
def _query_refresh_token(token_model, token):
|
||||
try:
|
||||
return token_model.objects.get(refresh_token=token)
|
||||
except token_model.DoesNotExist:
|
||||
return None
|
||||
@@ -0,0 +1,46 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from django.http import HttpRequest
|
||||
from django.utils.functional import cached_property
|
||||
from authlib.common.encoding import json_loads
|
||||
from authlib.oauth2.rfc6749 import OAuth2Request, JsonRequest
|
||||
|
||||
|
||||
class DjangoOAuth2Request(OAuth2Request):
|
||||
def __init__(self, request: HttpRequest):
|
||||
super().__init__(request.method, request.build_absolute_uri(), None, request.headers)
|
||||
self._request = request
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self._request.GET
|
||||
|
||||
@property
|
||||
def form(self):
|
||||
return self._request.POST
|
||||
|
||||
@cached_property
|
||||
def data(self):
|
||||
data = {}
|
||||
data.update(self._request.GET.dict())
|
||||
data.update(self._request.POST.dict())
|
||||
return data
|
||||
|
||||
@cached_property
|
||||
def datalist(self):
|
||||
values = defaultdict(list)
|
||||
for k in self.args:
|
||||
values[k].extend(self.args.getlist(k))
|
||||
for k in self.form:
|
||||
values[k].extend(self.form.getlist(k))
|
||||
return values
|
||||
|
||||
|
||||
class DjangoJsonRequest(JsonRequest):
|
||||
def __init__(self, request: HttpRequest):
|
||||
super().__init__(request.method, request.build_absolute_uri(), None, request.headers)
|
||||
self._request = request
|
||||
|
||||
@cached_property
|
||||
def data(self):
|
||||
return json_loads(self._request.body)
|
||||
@@ -0,0 +1,75 @@
|
||||
import functools
|
||||
from django.http import JsonResponse
|
||||
from authlib.oauth2 import (
|
||||
OAuth2Error,
|
||||
ResourceProtector as _ResourceProtector,
|
||||
)
|
||||
from authlib.oauth2.rfc6749 import (
|
||||
MissingAuthorizationError,
|
||||
)
|
||||
from authlib.oauth2.rfc6750 import (
|
||||
BearerTokenValidator as _BearerTokenValidator
|
||||
)
|
||||
from .requests import DjangoJsonRequest
|
||||
from .signals import token_authenticated
|
||||
|
||||
|
||||
class ResourceProtector(_ResourceProtector):
|
||||
def acquire_token(self, request, scopes=None, **kwargs):
|
||||
"""A method to acquire current valid token with the given scope.
|
||||
|
||||
:param request: Django HTTP request instance
|
||||
:param scopes: a list of scope values
|
||||
:return: token object
|
||||
"""
|
||||
req = DjangoJsonRequest(request)
|
||||
# backward compatibility
|
||||
kwargs['scopes'] = scopes
|
||||
for claim in kwargs:
|
||||
if isinstance(kwargs[claim], str):
|
||||
kwargs[claim] = [kwargs[claim]]
|
||||
token = self.validate_request(request=req, **kwargs)
|
||||
token_authenticated.send(sender=self.__class__, token=token)
|
||||
return token
|
||||
|
||||
def __call__(self, scopes=None, optional=False, **kwargs):
|
||||
claims = kwargs
|
||||
# backward compatibility
|
||||
claims['scopes'] = scopes
|
||||
def wrapper(f):
|
||||
@functools.wraps(f)
|
||||
def decorated(request, *args, **kwargs):
|
||||
try:
|
||||
token = self.acquire_token(request, **claims)
|
||||
request.oauth_token = token
|
||||
except MissingAuthorizationError as error:
|
||||
if optional:
|
||||
request.oauth_token = None
|
||||
return f(request, *args, **kwargs)
|
||||
return return_error_response(error)
|
||||
except OAuth2Error as error:
|
||||
return return_error_response(error)
|
||||
return f(request, *args, **kwargs)
|
||||
return decorated
|
||||
return wrapper
|
||||
|
||||
|
||||
class BearerTokenValidator(_BearerTokenValidator):
|
||||
def __init__(self, token_model, realm=None, **extra_attributes):
|
||||
self.token_model = token_model
|
||||
super().__init__(realm, **extra_attributes)
|
||||
|
||||
def authenticate_token(self, token_string):
|
||||
try:
|
||||
return self.token_model.objects.get(access_token=token_string)
|
||||
except self.token_model.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
def return_error_response(error):
|
||||
body = dict(error.get_body())
|
||||
resp = JsonResponse(body, status=error.status_code)
|
||||
headers = error.get_headers()
|
||||
for k, v in headers:
|
||||
resp[k] = v
|
||||
return resp
|
||||
@@ -0,0 +1,11 @@
|
||||
from django.dispatch import Signal
|
||||
|
||||
|
||||
#: signal when client is authenticated
|
||||
client_authenticated = Signal()
|
||||
|
||||
#: signal when token is revoked
|
||||
token_revoked = Signal()
|
||||
|
||||
#: signal when token is authenticated
|
||||
token_authenticated = Signal()
|
||||
Reference in New Issue
Block a user