From 3070f2ad274030f13c63c9184198ce7afef0bd29 Mon Sep 17 00:00:00 2001 From: Mitsukuni Sato Date: Mon, 9 Apr 2012 17:04:29 +0900 Subject: [PATCH 1/3] Add OAUTH2_USE_NATIVE_APPLICATION option in settings, to support individual protocol for some native applications (iOS / Android). --- oauth2app/authorize.py | 3 ++- oauth2app/consts.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/oauth2app/authorize.py b/oauth2app/authorize.py index 5b563b0..f4279d3 100644 --- a/oauth2app/authorize.py +++ b/oauth2app/authorize.py @@ -10,6 +10,7 @@ from .consts import ACCESS_TOKEN_EXPIRATION, REFRESHABLE from .consts import CODE, TOKEN, CODE_AND_TOKEN from .consts import AUTHENTICATION_METHOD, MAC, BEARER, MAC_KEY_LENGTH +from .consts import USE_NATIVE_APPLICATION from .exceptions import OAuth2Exception from .lib.uri import add_parameters, add_fragments, normalize from .models import Client, AccessRange, Code, AccessToken, KeyGenerator @@ -195,7 +196,7 @@ def _validate(self): if self.authorized_response_type & RESPONSE_TYPES[self.response_type] == 0: raise UnauthorizedClient("Response type %s not allowed." % self.response_type) - if not absolute_http_url_re.match(self.redirect_uri): + if not USE_NATIVE_APPLICATION and not absolute_http_url_re.match(self.redirect_uri): raise InvalidRequest('Absolute URI required for redirect_uri') # Scope if self.authorized_scope is not None and self.scope is None: diff --git a/oauth2app/consts.py b/oauth2app/consts.py index 1004271..4db7b68 100644 --- a/oauth2app/consts.py +++ b/oauth2app/consts.py @@ -51,4 +51,6 @@ # Grants code style parameters. CODE = 2 # Grants both style parameters. -CODE_AND_TOKEN = CODE | TOKEN \ No newline at end of file +CODE_AND_TOKEN = CODE | TOKEN +# +USE_NATIVE_APPLICATION = getattr(settings, "OAUTH2_USE_NATIVE_APPLICATION", False) From 6d3da124e6be58efc1453aa1e1d640feec66b947 Mon Sep 17 00:00:00 2001 From: Mitsukuni Sato Date: Thu, 13 Jun 2013 01:59:10 +0900 Subject: [PATCH 2/3] use redirect class for recirect_uri checking --- oauth2app/authorize.py | 15 ++++++++++----- oauth2app/consts.py | 16 ++++++++++++++-- setup.py | 2 +- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/oauth2app/authorize.py b/oauth2app/authorize.py index f4279d3..c2e0548 100644 --- a/oauth2app/authorize.py +++ b/oauth2app/authorize.py @@ -5,12 +5,15 @@ import simplejson as json -from django.http import absolute_http_url_re, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest from urllib import urlencode + +from django.http import absolute_http_url_re, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest +from django.core.exceptions import SuspiciousOperation + from .consts import ACCESS_TOKEN_EXPIRATION, REFRESHABLE from .consts import CODE, TOKEN, CODE_AND_TOKEN from .consts import AUTHENTICATION_METHOD, MAC, BEARER, MAC_KEY_LENGTH -from .consts import USE_NATIVE_APPLICATION +from .consts import REDIRECT_CLASS from .exceptions import OAuth2Exception from .lib.uri import add_parameters, add_fragments, normalize from .models import Client, AccessRange, Code, AccessToken, KeyGenerator @@ -196,7 +199,9 @@ def _validate(self): if self.authorized_response_type & RESPONSE_TYPES[self.response_type] == 0: raise UnauthorizedClient("Response type %s not allowed." % self.response_type) - if not USE_NATIVE_APPLICATION and not absolute_http_url_re.match(self.redirect_uri): + try: + REDIRECT_CLASS(self.redirect_uri) + except SuspiciousOperation: raise InvalidRequest('Absolute URI required for redirect_uri') # Scope if self.authorized_scope is not None and self.scope is None: @@ -218,7 +223,7 @@ def _check_redirect_uri(self): if self.redirect_uri is None: raise MissingRedirectURI('No redirect_uri to send response.') if not absolute_http_url_re.match(self.redirect_uri): - raise MissingRedirectURI('Absolute redirect_uri required.') + raise MissingRedirectURI('Absolute redirect_uri required.') def error_redirect(self): """In the event of an error, return a Django HttpResponseRedirect @@ -314,7 +319,7 @@ def grant_redirect(self): parameters['state'] = self.state redirect_uri = add_parameters(self.redirect_uri, parameters) redirect_uri = add_fragments(redirect_uri, fragments) - return HttpResponseRedirect(redirect_uri) + return REDIRECT_CLASS(redirect_uri) else: raise UnauthenticatedUser("Django user object associated with the " "request is not authenticated.") diff --git a/oauth2app/consts.py b/oauth2app/consts.py index 4db7b68..582ae72 100644 --- a/oauth2app/consts.py +++ b/oauth2app/consts.py @@ -3,8 +3,11 @@ """OAuth 2.0 Default Values.""" +import sys from django.conf import settings +from django.http import HttpResponseRedirect + from .exceptions import OAuth2Exception @@ -52,5 +55,14 @@ CODE = 2 # Grants both style parameters. CODE_AND_TOKEN = CODE | TOKEN -# -USE_NATIVE_APPLICATION = getattr(settings, "OAUTH2_USE_NATIVE_APPLICATION", False) +# redirect_class +REDIRECT_CLASS = getattr(settings, "OAUTH2_REDIRECT_CLASS", HttpResponseRedirect) +if isinstance(REDIRECT_CLASS, basestring): + pos = REDIRECT_CLASS.rfind('.') + module_name = REDIRECT_CLASS[:pos] + cls_name = REDIRECT_CLASS[pos+1:] + + __import__(module_name, {}, {}, []) + module = sys.modules[module_name] + + REDIRECT_CLASS = getattr(module, cls_name) diff --git a/setup.py b/setup.py index 329956f..f3761c1 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ packages = find_packages(), - install_requires = ['Django>=1.2.3', 'simplejson>=2.1.5', "django-uni-form>=0.8.0"], + install_requires = ['Django>=1.2.3', 'simplejson>=2.1.5'], include_package_data = True, # metadata for upload to PyPI From 6c8918f2f24ddd5dc500e31b37f4f52e61e3280f Mon Sep 17 00:00:00 2001 From: Mitsukuni Sato Date: Tue, 30 Jul 2013 01:38:17 +0900 Subject: [PATCH 3/3] add // --- oauth2app/lib/uri.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/oauth2app/lib/uri.py b/oauth2app/lib/uri.py index 9308cb5..fdde9fb 100644 --- a/oauth2app/lib/uri.py +++ b/oauth2app/lib/uri.py @@ -4,7 +4,7 @@ """OAuth 2.0 URI Helper Functions""" -from urlparse import urlparse, urlunparse, parse_qsl +from urlparse import urlparse, urlunparse, parse_qsl, uses_netloc from urllib import urlencode from url_normalize import url_normalize @@ -19,9 +19,12 @@ def add_parameters(url, parameters): *Returns str*""" parts = list(urlparse(url)) - parts[4] = urlencode(parse_qsl(parts[4]) + parameters.items()) - return urlunparse(parts) - + if parts[0] in uses_netloc: + parts[4] = urlencode(parse_qsl(parts[4]) + parameters.items()) + return urlunparse(parts) + else: + return '%s?%s' % (url, urlencode(parse_qsl(parts[4]) + parameters.items())) + def add_fragments(url, fragments): """Parses URL and appends fragments. @@ -33,9 +36,12 @@ def add_fragments(url, fragments): *Returns str*""" parts = list(urlparse(url)) - parts[5] = urlencode(parse_qsl(parts[5]) + fragments.items()) - return urlunparse(parts) - + if parts[0] in uses_netloc: + parts[5] = urlencode(parse_qsl(parts[5]) + fragments.items()) + return urlunparse(parts) + else: + return '%s#%s' % (url, urlencode(parse_qsl(parts[5]) + fragments.items())) + def normalize(url): """Normalizes URL.