From 2404d8950ce5b758553413db028041da4eec11d8 Mon Sep 17 00:00:00 2001 From: Kunal Date: Sun, 27 Sep 2020 21:33:07 +0530 Subject: [PATCH 1/4] Overwrite default AuthorizationView to skip authorization even after token expiry --- omniport/core/open_auth/http_urls.py | 5 +- .../core/open_auth/serializers/application.py | 1 + omniport/core/open_auth/views/application.py | 86 +++++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/omniport/core/open_auth/http_urls.py b/omniport/core/open_auth/http_urls.py index 101f6454..095658d3 100644 --- a/omniport/core/open_auth/http_urls.py +++ b/omniport/core/open_auth/http_urls.py @@ -1,13 +1,12 @@ from django.urls import path, include from django.views.decorators.http import require_POST from oauth2_provider.views import ( - AuthorizationView, TokenView, RevokeTokenView, ) from rest_framework import routers -from open_auth.views.application import ApplicationViewSet +from open_auth.views.application import ApplicationViewSet, OmniportAuthorizationView from open_auth.views.retrieve_data import GetUserData router = routers.SimpleRouter() @@ -18,7 +17,7 @@ urlpatterns = [ path( 'authorise/', - require_POST(AuthorizationView.as_view()), + require_POST(OmniportAuthorizationView.as_view()), name='authorise' ), path( diff --git a/omniport/core/open_auth/serializers/application.py b/omniport/core/open_auth/serializers/application.py index 749cf5bc..105aac0a 100644 --- a/omniport/core/open_auth/serializers/application.py +++ b/omniport/core/open_auth/serializers/application.py @@ -46,6 +46,7 @@ class Meta: model = Application fields = [ + 'id', 'client_id', 'redirect_uris', 'name', diff --git a/omniport/core/open_auth/views/application.py b/omniport/core/open_auth/views/application.py index 4389fef2..b30deaf1 100644 --- a/omniport/core/open_auth/views/application.py +++ b/omniport/core/open_auth/views/application.py @@ -17,3 +17,89 @@ class ApplicationViewSet( queryset = Application.objects.filter(is_approved=True) lookup_field = 'client_id' pagination_class = None + + + + + +class OmniportAuthorizationView(AuthorizationView): + """ + Overrided the default implementation + of get method of AuthorizationView in order + to skip authorization even after the access + token has expired. + """ + def get(self, request, *args, **kwargs): + + """ + The code is similar to https://github.com/jazzband/django-oauth-toolkit/blob/master/oauth2_provider/views/base.py + Significant Changes marked with *** in the code + + """ + try: + require_approval = request.GET.get( + "approval_prompt", + oauth2_settings.REQUEST_APPROVAL_PROMPT, + ) + + if require_approval != 'auto_even_if_expired': + return super(OmniportAuthorizationView, self).get(request, *args, **kwargs) + + scopes, credentials = self.validate_authorization_request(request) + all_scopes = get_scopes_backend().get_all_scopes() + kwargs["scopes_descriptions"] = [all_scopes[scope] for scope in scopes] + kwargs['scopes'] = scopes + + # at this point we know an Application instance with such client_id exists in the database + application = get_application_model().objects.get(client_id=credentials['client_id']) + kwargs['application'] = application + kwargs['client_id'] = credentials['client_id'] + kwargs['redirect_uri'] = credentials['redirect_uri'] + kwargs['response_type'] = credentials['response_type'] + kwargs['state'] = credentials['state'] + + self.oauth2_data = kwargs + # following two loc are here only because of https://code.djangoproject.com/ticket/17795 + form = self.get_form(self.get_form_class()) + kwargs['form'] = form + + """ + If skip_authorization field is True, skip the authorization screen even + if this is the first use of the application and there was no previous authorization. + This is useful for in-house applications-> assume an in-house applications + are already approved. + """ + + if application.skip_authorization: + uri, headers, body, status = self.create_authorization_response( + request=self.request, scopes=" ".join(scopes), + credentials=credentials, allow=True) + return self.redirect(uri, application) + + #check for require_approval + + assert require_approval == 'auto_even_if_expired' + tokens = ( + get_access_token_model() + .objects.filter( + user=request.user, + application=kwargs["application"], + #expires__gt=timezone.now(), *** + ) + .all() + ) + + # check past authorizations regarded the same scopes as the current one + for token in tokens: + if token.allow_scopes(scopes): + uri, headers, body, status = self.create_authorization_response( + request=self.request, scopes=" ".join(scopes), + credentials=credentials, allow=True) + return self.redirect(uri, application) + + # render an authorization prompt so the user can approve + # the application's requested scopes + return self.render_to_response(self.get_context_data(**kwargs)) + + except OAuthToolkitError as error: + return self.error_response(error) \ No newline at end of file From ab46deaa681d1c2907cb332c205778819f0a1793 Mon Sep 17 00:00:00 2001 From: Kunal Date: Sun, 27 Sep 2020 21:38:18 +0530 Subject: [PATCH 2/4] Add action view to check for token availability (#112) --- omniport/core/open_auth/views/application.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/omniport/core/open_auth/views/application.py b/omniport/core/open_auth/views/application.py index b30deaf1..782108e0 100644 --- a/omniport/core/open_auth/views/application.py +++ b/omniport/core/open_auth/views/application.py @@ -18,6 +18,21 @@ class ApplicationViewSet( lookup_field = 'client_id' pagination_class = None + """ + Action view for getting the availabilty of token + for a particular view in order to skip authorization + after first consent + """ + @action(methods=['get'], detail=False, url_name="get_token", url_path="get_token") + def get_token(self, request): + + token=get_access_token_model().objects.filter( + user=request.user, + application=request.GET.get('id') + ).exists() + + + return HttpResponse(token) From 718ec3a9bf49b39ec4628fea558863bb0096c6c1 Mon Sep 17 00:00:00 2001 From: Kunal Date: Sun, 27 Sep 2020 21:56:36 +0530 Subject: [PATCH 3/4] Add imports in views/application.py and settings added in authentication.py --- omniport/core/open_auth/views/application.py | 8 +++++++- omniport/omniport/settings/base/authentication.py | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/omniport/core/open_auth/views/application.py b/omniport/core/open_auth/views/application.py index 782108e0..a193cc65 100644 --- a/omniport/core/open_auth/views/application.py +++ b/omniport/core/open_auth/views/application.py @@ -1,8 +1,14 @@ from rest_framework import viewsets, mixins, permissions +from rest_framework.decorators import action +from django.http import HttpResponse from open_auth.models import Application from open_auth.serializers.application import ApplicationAuthoriseSerializer - +from oauth2_provider.exceptions import OAuthToolkitError +from oauth2_provider.models import get_application_model,get_access_token_model +from oauth2_provider.scopes import get_scopes_backend +from oauth2_provider.settings import oauth2_settings +from oauth2_provider.views import AuthorizationView class ApplicationViewSet( mixins.RetrieveModelMixin, diff --git a/omniport/omniport/settings/base/authentication.py b/omniport/omniport/settings/base/authentication.py index d2516b3f..4f6468b5 100644 --- a/omniport/omniport/settings/base/authentication.py +++ b/omniport/omniport/settings/base/authentication.py @@ -49,3 +49,6 @@ SESSION_COOKIE_NAME = 'omniport_session' OAUTH2_PROVIDER_APPLICATION_MODEL = 'open_auth.Application' +OAUTH2_PROVIDER={ + 'REQUEST_APPROVAL_PROMPT':'auto_even_if_expired', + } From a01682ed6f4bcf203596cb626ebf395d9523af6f Mon Sep 17 00:00:00 2001 From: Kunal Date: Mon, 28 Sep 2020 19:33:34 +0530 Subject: [PATCH 4/4] Reformat code in application.py --- omniport/core/open_auth/views/application.py | 43 +++++++++----------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/omniport/core/open_auth/views/application.py b/omniport/core/open_auth/views/application.py index a193cc65..dbae2d53 100644 --- a/omniport/core/open_auth/views/application.py +++ b/omniport/core/open_auth/views/application.py @@ -1,15 +1,16 @@ from rest_framework import viewsets, mixins, permissions from rest_framework.decorators import action - from django.http import HttpResponse -from open_auth.models import Application -from open_auth.serializers.application import ApplicationAuthoriseSerializer from oauth2_provider.exceptions import OAuthToolkitError from oauth2_provider.models import get_application_model,get_access_token_model from oauth2_provider.scopes import get_scopes_backend from oauth2_provider.settings import oauth2_settings from oauth2_provider.views import AuthorizationView +from open_auth.models import Application +from open_auth.serializers.application import ApplicationAuthoriseSerializer + + class ApplicationViewSet( mixins.RetrieveModelMixin, viewsets.GenericViewSet, @@ -24,25 +25,22 @@ class ApplicationViewSet( lookup_field = 'client_id' pagination_class = None - """ - Action view for getting the availabilty of token - for a particular view in order to skip authorization - after first consent - """ @action(methods=['get'], detail=False, url_name="get_token", url_path="get_token") def get_token(self, request): - + """ + Action view for getting the availabilty of token + for a particular view in order to skip authorization + after first consent + """ + token=get_access_token_model().objects.filter( user=request.user, application=request.GET.get('id') ).exists() - return HttpResponse(token) - - class OmniportAuthorizationView(AuthorizationView): """ Overrided the default implementation @@ -50,13 +48,13 @@ class OmniportAuthorizationView(AuthorizationView): to skip authorization even after the access token has expired. """ + def get(self, request, *args, **kwargs): - """ The code is similar to https://github.com/jazzband/django-oauth-toolkit/blob/master/oauth2_provider/views/base.py Significant Changes marked with *** in the code - """ + try: require_approval = request.GET.get( "approval_prompt", @@ -70,9 +68,10 @@ def get(self, request, *args, **kwargs): all_scopes = get_scopes_backend().get_all_scopes() kwargs["scopes_descriptions"] = [all_scopes[scope] for scope in scopes] kwargs['scopes'] = scopes - # at this point we know an Application instance with such client_id exists in the database + application = get_application_model().objects.get(client_id=credentials['client_id']) + kwargs['application'] = application kwargs['client_id'] = credentials['client_id'] kwargs['redirect_uri'] = credentials['redirect_uri'] @@ -84,13 +83,11 @@ def get(self, request, *args, **kwargs): form = self.get_form(self.get_form_class()) kwargs['form'] = form - """ - If skip_authorization field is True, skip the authorization screen even - if this is the first use of the application and there was no previous authorization. - This is useful for in-house applications-> assume an in-house applications - are already approved. - """ + #If skip_authorization field is True, skip the authorization screen even + #if this is the first use of the application and there was no previous authorization. + #This is useful for in-house applications-> assume an in-house applications + #re already approved. if application.skip_authorization: uri, headers, body, status = self.create_authorization_response( request=self.request, scopes=" ".join(scopes), @@ -98,8 +95,8 @@ def get(self, request, *args, **kwargs): return self.redirect(uri, application) #check for require_approval - assert require_approval == 'auto_even_if_expired' + tokens = ( get_access_token_model() .objects.filter( @@ -123,4 +120,4 @@ def get(self, request, *args, **kwargs): return self.render_to_response(self.get_context_data(**kwargs)) except OAuthToolkitError as error: - return self.error_response(error) \ No newline at end of file + return self.error_response(error)