From d0c362c88d501bff8c62dc550c6193b47516f5e3 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 9 Aug 2019 10:43:29 +0300 Subject: [PATCH] version 0.0.5.2: added method to revoke an access or refresh token --- apps/authorization/serializers/common.py | 4 +- apps/authorization/urls/common.py | 3 +- apps/authorization/views/web.py | 90 +++++++++++++++++------- apps/utils/oauth2.py | 2 +- 4 files changed, 69 insertions(+), 30 deletions(-) diff --git a/apps/authorization/serializers/common.py b/apps/authorization/serializers/common.py index ac0247d1..d551fd07 100644 --- a/apps/authorization/serializers/common.py +++ b/apps/authorization/serializers/common.py @@ -3,8 +3,8 @@ from rest_framework import serializers from authorization.models import Application -class SocialSignUpSerialzier(serializers.Serializer): - """Serializer for signing up""" +class OAuth2Serialzier(serializers.Serializer): + """Serializer OAuth2 authorization""" source = serializers.ChoiceField(choices=Application.SOURCES) token = serializers.CharField(max_length=255) diff --git a/apps/authorization/urls/common.py b/apps/authorization/urls/common.py index 424a8449..2426fcc6 100644 --- a/apps/authorization/urls/common.py +++ b/apps/authorization/urls/common.py @@ -32,13 +32,12 @@ urlpatterns_rest_framework_social_oauth2 = [ url(r'^authorize/?$', AuthorizationView.as_view(), name="authorize"), url(r'^token/?$', TokenView.as_view(), name="token"), url('', include('social_django.urls', namespace="social")), - url(r'^convert-token/?$', ConvertTokenView.as_view(), name="convert_token"), - url(r'^revoke-token/?$', RevokeTokenView.as_view(), name="revoke_token"), url(r'^invalidate-sessions/?$', invalidate_sessions, name="invalidate_sessions") ] urlpatterns_api = [ path('social/signup/', views.SocialSignUpView.as_view(), name='signup'), + path('revoke-token/', views.RevokeTokenView.as_view(), name="revoke_token"), ] urlpatterns = urlpatterns_api + \ diff --git a/apps/authorization/views/web.py b/apps/authorization/views/web.py index e8beaa24..f4ca1d71 100644 --- a/apps/authorization/views/web.py +++ b/apps/authorization/views/web.py @@ -2,35 +2,25 @@ import json from braces.views import CsrfExemptMixin +from django.conf import settings +from django.utils.translation import gettext_lazy as _ +from oauth2_provider.oauth2_backends import OAuthLibCore from oauth2_provider.settings import oauth2_settings from oauth2_provider.views.mixins import OAuthLibMixin from rest_framework import permissions -from authorization.models import Application -from django.conf import settings -from django.utils.translation import gettext_lazy as _ -from rest_framework.response import Response from rest_framework.generics import GenericAPIView +from rest_framework.response import Response from rest_framework_social_oauth2.oauth2_backends import KeepRequestCore from rest_framework_social_oauth2.oauth2_endpoints import SocialTokenServer + +from authorization.models import Application from authorization.serializers import common as serializers from utils import exceptions as utils_exceptions -# Create your views here. -class SocialSignUpView(CsrfExemptMixin, OAuthLibMixin, GenericAPIView): - """ - Implements an endpoint to convert a provider token to an access token - - The endpoint is used in the following flows: - - * Authorization code - * Client credentials - """ - server_class = SocialTokenServer - validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS - oauthlib_backend_class = KeepRequestCore - permission_classes = (permissions.AllowAny,) - serializer_class = serializers.SocialSignUpSerialzier +# Mixins +class OAuth2ViewMixin(GenericAPIView): + """Basic mixin for OAuth2 views""" def get_client_id(self, source) -> str: """Get application client id""" @@ -50,16 +40,15 @@ class SocialSignUpView(CsrfExemptMixin, OAuthLibMixin, GenericAPIView): raise utils_exceptions.SerivceError( data=_('Not found an application with this source')) - def post(self, request, *args, **kwargs): - """Override POST method""" - # Serialize POST-data - serializer = self.get_serializer(data=request.data) + def prepare_request_data(self, request_data): + """Preparing request data""" + serializer = self.get_serializer(data=request_data) serializer.is_valid(raise_exception=True) # Set attributes source = serializer.validated_data.get('source') token = serializer.validated_data.get('token') # Set OAuth2 request parameters - request_data = { + _request_data = { 'grant_type': settings.OAUTH2_SOCIAL_AUTH_GRANT_TYPE, 'backend': settings.OAUTH2_SOCIAL_AUTH_BACKEND_NAME, 'token': token, @@ -67,8 +56,31 @@ class SocialSignUpView(CsrfExemptMixin, OAuthLibMixin, GenericAPIView): } # Fill client secret parameter by platform if source == Application.MOBILE: - request_data['client_secret'] = self.get_client_secret(source) + _request_data['client_secret'] = self.get_client_secret(source) + return _request_data + +# Create your views here. +class SocialSignUpView(CsrfExemptMixin, OAuthLibMixin, + OAuth2ViewMixin, GenericAPIView): + """ + Implements an endpoint to convert a provider token to an access token + + The endpoint is used in the following flows: + + * Authorization code + * Client credentials + """ + server_class = SocialTokenServer + validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS + oauthlib_backend_class = KeepRequestCore + permission_classes = (permissions.AllowAny,) + serializer_class = serializers.OAuth2Serialzier + + def post(self, request, *args, **kwargs): + """Override POST method""" + # Preparing request data + request_data = self.prepare_request_data(request_data=request.data) # Use the rest framework `.data` to fake the post body of the django request. request._request.POST = request._request.POST.copy() for key, value in request_data.items(): @@ -80,3 +92,31 @@ class SocialSignUpView(CsrfExemptMixin, OAuthLibMixin, GenericAPIView): for k, v in headers.items(): response[k] = v return response + + +class RevokeTokenView(CsrfExemptMixin, OAuthLibMixin, + OAuth2ViewMixin, GenericAPIView): + """ + Implements an endpoint to revoke access or refresh tokens + """ + server_class = oauth2_settings.OAUTH2_SERVER_CLASS + validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS + oauthlib_backend_class = OAuthLibCore + permission_classes = (permissions.AllowAny,) + serializer_class = serializers.OAuth2Serialzier + + def post(self, request, *args, **kwargs): + # Use the rest framework `.data` to fake the post body of the django request. + # Preparing request data + request_data = self.prepare_request_data(request_data=request.data) + # Use the rest framework `.data` to fake the post body of the django request. + request._request.POST = request._request.POST.copy() + for key, value in request_data.items(): + request._request.POST[key] = value + + url, headers, body, status = self.create_revocation_response(request._request) + response = Response(data=json.loads(body) if body else '', status=status if body else 204) + + for k, v in headers.items(): + response[k] = v + return response diff --git a/apps/utils/oauth2.py b/apps/utils/oauth2.py index ec5098c9..8740f13b 100644 --- a/apps/utils/oauth2.py +++ b/apps/utils/oauth2.py @@ -33,4 +33,4 @@ class GMOAuth2(DjangoOAuth2): user = User.objects.get(pk=response[self.ID_KEY]) return user else: - return None \ No newline at end of file + return None