From ea1a61fee3439aa6b3101b3ce8bf483810f13eb0 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Tue, 24 Mar 2015 12:34:23 -0500 Subject: [PATCH 01/15] use all fields instead of a hard coded list, so that extended models don't also require an extended form --- oauth2_provider/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_provider/forms.py b/oauth2_provider/forms.py index a8b398554..8fe85aefa 100644 --- a/oauth2_provider/forms.py +++ b/oauth2_provider/forms.py @@ -25,4 +25,4 @@ class RegistrationForm(forms.ModelForm): """ class Meta: model = Application - fields = ('name', 'client_id', 'client_secret', 'client_type', 'authorization_grant_type', 'redirect_uris') + fields = ('name', 'client_id', 'client_secret', 'client_type', 'authorization_grant_type', 'redirect_uris') \ No newline at end of file From d2e50c606941ea041fca9a15baac094151cf9f14 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Wed, 25 Mar 2015 20:49:38 -0500 Subject: [PATCH 02/15] add app specific scope setting --- oauth2_provider/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/oauth2_provider/settings.py b/oauth2_provider/settings.py index db5768686..420dcdd36 100644 --- a/oauth2_provider/settings.py +++ b/oauth2_provider/settings.py @@ -43,6 +43,7 @@ 'APPLICATION_MODEL': getattr(settings, 'OAUTH2_PROVIDER_APPLICATION_MODEL', 'oauth2_provider.Application'), 'REQUEST_APPROVAL_PROMPT': 'force', 'ALLOWED_REDIRECT_URI_SCHEMES': ['http', 'https'], + 'APP_SPECIFIC_SCOPES': False, # Special settings that will be evaluated at runtime '_SCOPES': [], From b3c7c88c241e71e4e02ffd6f1e20d524d835e37c Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Wed, 25 Mar 2015 20:50:15 -0500 Subject: [PATCH 03/15] use allowed_scopes if APP_SPECIFIC_SCOPES is enabled --- oauth2_provider/oauth2_validators.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index d40dbdfdf..4ab5ffe62 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -265,7 +265,10 @@ def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): return set(scopes).issubset(set(oauth2_settings._SCOPES)) def get_default_scopes(self, client_id, request, *args, **kwargs): - return oauth2_settings._SCOPES + if oauth2_settings.APP_SPECIFIC_SCOPES: + return request.client.allowed_scopes + else: + return oauth2_settings._SCOPES def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs): return request.client.redirect_uri_allowed(redirect_uri) From 62e091fc7b23bdeb1244af6875d79af535a4cfc9 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Wed, 25 Mar 2015 20:51:01 -0500 Subject: [PATCH 04/15] add allowed_scopes field --- oauth2_provider/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index d3091fd2a..be2dced81 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -66,6 +66,8 @@ class AbstractApplication(models.Model): default=generate_client_secret, db_index=True) name = models.CharField(max_length=255, blank=True) skip_authorization = models.BooleanField(default=False) + # only used if oauth2_settings.APP_SPECIFIC_SCOPES is True + allowed_scopes = models.TextField(help_text="List of allowed scopes for this application, space separated") class Meta: abstract = True From 9447e2ddcb04a0dbeecce4ddb943615ce073a2a5 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Wed, 25 Mar 2015 20:51:09 -0500 Subject: [PATCH 05/15] handle app specific scopes if enabled --- oauth2_provider/views/base.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/oauth2_provider/views/base.py b/oauth2_provider/views/base.py index 5d80f1d9a..42ed4ee5d 100644 --- a/oauth2_provider/views/base.py +++ b/oauth2_provider/views/base.py @@ -78,8 +78,12 @@ class AuthorizationView(BaseAuthorizationView, FormView): skip_authorization_completely = False def get_initial(self): - # TODO: move this scopes conversion from and to string into a utils function - scopes = self.oauth2_data.get('scope', self.oauth2_data.get('scopes', [])) + if oauth2_settings.APP_SPECIFIC_SCOPES: + application = Application.objects.get(client_id=self.request.GET['client_id']) + scopes = application.allowed_scopes.split(' ') + else: + # TODO: move this scopes conversion from and to string into a utils function + scopes = self.oauth2_data.get('scope', self.oauth2_data.get('scopes', [])) initial_data = { 'redirect_uri': self.oauth2_data.get('redirect_uri', None), 'scope': ' '.join(scopes), From bc09c2067d06422117b7c05b8fc1f477f9372cb3 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Fri, 24 Apr 2015 14:06:16 -0500 Subject: [PATCH 06/15] name fix --- oauth2_provider/views/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/views/base.py b/oauth2_provider/views/base.py index 42ed4ee5d..6f991b5cc 100644 --- a/oauth2_provider/views/base.py +++ b/oauth2_provider/views/base.py @@ -79,7 +79,8 @@ class AuthorizationView(BaseAuthorizationView, FormView): def get_initial(self): if oauth2_settings.APP_SPECIFIC_SCOPES: - application = Application.objects.get(client_id=self.request.GET['client_id']) + ApplicationModel = get_application_model() + application = ApplicationModel.objects.get(client_id=self.request.GET['client_id']) scopes = application.allowed_scopes.split(' ') else: # TODO: move this scopes conversion from and to string into a utils function From 3ae0de413cba87972034c05d78d1292562324730 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Tue, 24 Mar 2015 12:34:23 -0500 Subject: [PATCH 07/15] use all fields instead of a hard coded list, so that extended models don't also require an extended form --- oauth2_provider/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_provider/forms.py b/oauth2_provider/forms.py index a8b398554..8fe85aefa 100644 --- a/oauth2_provider/forms.py +++ b/oauth2_provider/forms.py @@ -25,4 +25,4 @@ class RegistrationForm(forms.ModelForm): """ class Meta: model = Application - fields = ('name', 'client_id', 'client_secret', 'client_type', 'authorization_grant_type', 'redirect_uris') + fields = ('name', 'client_id', 'client_secret', 'client_type', 'authorization_grant_type', 'redirect_uris') \ No newline at end of file From 057bb8679ea841c4d177801d4433309cb4f411ce Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Wed, 25 Mar 2015 20:49:38 -0500 Subject: [PATCH 08/15] add app specific scope setting --- oauth2_provider/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/oauth2_provider/settings.py b/oauth2_provider/settings.py index db5768686..420dcdd36 100644 --- a/oauth2_provider/settings.py +++ b/oauth2_provider/settings.py @@ -43,6 +43,7 @@ 'APPLICATION_MODEL': getattr(settings, 'OAUTH2_PROVIDER_APPLICATION_MODEL', 'oauth2_provider.Application'), 'REQUEST_APPROVAL_PROMPT': 'force', 'ALLOWED_REDIRECT_URI_SCHEMES': ['http', 'https'], + 'APP_SPECIFIC_SCOPES': False, # Special settings that will be evaluated at runtime '_SCOPES': [], From 16694cefb772f03aeb3c1037669a622d8dacea7f Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Wed, 25 Mar 2015 20:50:15 -0500 Subject: [PATCH 09/15] use allowed_scopes if APP_SPECIFIC_SCOPES is enabled --- oauth2_provider/oauth2_validators.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index d40dbdfdf..4ab5ffe62 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -265,7 +265,10 @@ def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): return set(scopes).issubset(set(oauth2_settings._SCOPES)) def get_default_scopes(self, client_id, request, *args, **kwargs): - return oauth2_settings._SCOPES + if oauth2_settings.APP_SPECIFIC_SCOPES: + return request.client.allowed_scopes + else: + return oauth2_settings._SCOPES def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs): return request.client.redirect_uri_allowed(redirect_uri) From e6c6fe50d08eaeb89297bb83ae2b0e8d7390b1af Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Wed, 25 Mar 2015 20:51:01 -0500 Subject: [PATCH 10/15] add allowed_scopes field --- oauth2_provider/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index d3091fd2a..be2dced81 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -66,6 +66,8 @@ class AbstractApplication(models.Model): default=generate_client_secret, db_index=True) name = models.CharField(max_length=255, blank=True) skip_authorization = models.BooleanField(default=False) + # only used if oauth2_settings.APP_SPECIFIC_SCOPES is True + allowed_scopes = models.TextField(help_text="List of allowed scopes for this application, space separated") class Meta: abstract = True From 502d8adc03b0f6913d3a1eb409b7a8418aec4e04 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Wed, 25 Mar 2015 20:51:09 -0500 Subject: [PATCH 11/15] handle app specific scopes if enabled --- oauth2_provider/views/base.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/oauth2_provider/views/base.py b/oauth2_provider/views/base.py index 5d80f1d9a..42ed4ee5d 100644 --- a/oauth2_provider/views/base.py +++ b/oauth2_provider/views/base.py @@ -78,8 +78,12 @@ class AuthorizationView(BaseAuthorizationView, FormView): skip_authorization_completely = False def get_initial(self): - # TODO: move this scopes conversion from and to string into a utils function - scopes = self.oauth2_data.get('scope', self.oauth2_data.get('scopes', [])) + if oauth2_settings.APP_SPECIFIC_SCOPES: + application = Application.objects.get(client_id=self.request.GET['client_id']) + scopes = application.allowed_scopes.split(' ') + else: + # TODO: move this scopes conversion from and to string into a utils function + scopes = self.oauth2_data.get('scope', self.oauth2_data.get('scopes', [])) initial_data = { 'redirect_uri': self.oauth2_data.get('redirect_uri', None), 'scope': ' '.join(scopes), From 204d69a36d5a6cce18d5d5d5a86d0889e888f201 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Mon, 27 Apr 2015 17:56:31 -0500 Subject: [PATCH 12/15] patch app specific scopes while waiting for merge from evonove --- oauth2_provider/views/mixins.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/views/mixins.py b/oauth2_provider/views/mixins.py index 168d5f42d..27791c6ec 100644 --- a/oauth2_provider/views/mixins.py +++ b/oauth2_provider/views/mixins.py @@ -155,9 +155,14 @@ def error_response(self, error, **kwargs): :param error: :attr:`OAuthToolkitError` """ oauthlib_error = error.oauthlib_error + + separator = '?' + if '?' in oauthlib_error.redirect_uri: + separator = '&' + error_response = { 'error': oauthlib_error, - 'url': "{0}?{1}".format(oauthlib_error.redirect_uri, oauthlib_error.urlencoded) + 'url': "{0}{1}{2}".format(oauthlib_error.redirect_uri, separator, oauthlib_error.urlencoded) } error_response.update(kwargs) From 921b64bb9dc5efc13eeed2c200e13567f21bd17a Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Tue, 28 Apr 2015 09:26:10 -0500 Subject: [PATCH 13/15] safer --- oauth2_provider/views/mixins.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/oauth2_provider/views/mixins.py b/oauth2_provider/views/mixins.py index 27791c6ec..919878a9f 100644 --- a/oauth2_provider/views/mixins.py +++ b/oauth2_provider/views/mixins.py @@ -157,8 +157,11 @@ def error_response(self, error, **kwargs): oauthlib_error = error.oauthlib_error separator = '?' - if '?' in oauthlib_error.redirect_uri: - separator = '&' + try: + if '?' in oauthlib_error.redirect_uri: + separator = '&' + except: + pass error_response = { 'error': oauthlib_error, From 9a9019ccd11578dc9ea16d420740371d7feb72f9 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Mon, 18 May 2015 19:59:32 -0500 Subject: [PATCH 14/15] allow blank allowed_scopes --- oauth2_provider/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index be2dced81..e8315eace 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -67,7 +67,7 @@ class AbstractApplication(models.Model): name = models.CharField(max_length=255, blank=True) skip_authorization = models.BooleanField(default=False) # only used if oauth2_settings.APP_SPECIFIC_SCOPES is True - allowed_scopes = models.TextField(help_text="List of allowed scopes for this application, space separated") + allowed_scopes = models.TextField(help_text="List of allowed scopes for this application, space separated", blank=True) class Meta: abstract = True From 73d32b623a47d2a76e121b1eea9923067f00ec72 Mon Sep 17 00:00:00 2001 From: honestbleeps Date: Mon, 8 Feb 2016 16:19:27 -0600 Subject: [PATCH 15/15] fix allowed/requested scopes reconciliation on auth page --- oauth2_provider/views/base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/views/base.py b/oauth2_provider/views/base.py index 6f991b5cc..3ebb53da4 100644 --- a/oauth2_provider/views/base.py +++ b/oauth2_provider/views/base.py @@ -81,7 +81,10 @@ def get_initial(self): if oauth2_settings.APP_SPECIFIC_SCOPES: ApplicationModel = get_application_model() application = ApplicationModel.objects.get(client_id=self.request.GET['client_id']) - scopes = application.allowed_scopes.split(' ') + allowed_scopes = application.allowed_scopes.split(' ') + requested_scopes = self.oauth2_data.get('scope', self.oauth2_data.get('scopes', [])) + # now reduce down to allowed scopes + scopes = list(set(allowed_scopes) & set(requested_scopes)) else: # TODO: move this scopes conversion from and to string into a utils function scopes = self.oauth2_data.get('scope', self.oauth2_data.get('scopes', []))