From 7407e2c5b09736bb905ca2d817454cb4157e1c18 Mon Sep 17 00:00:00 2001 From: Wojciech Bartosiak Date: Tue, 11 Jul 2017 07:29:24 +0200 Subject: [PATCH 1/6] Bump version --- CHANGELOG.md | 14 ++++++++++++++ docs/conf.py | 6 +++--- docs/sections/installation.rst | 4 ++-- setup.py | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f572a6d..a21dc8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. ### [Unreleased] +### [0.5.1] - 2017-07-11 + +##### Changed +- Documentation template to `Read The Docs`. + +##### Fixed +- `install_requires` has not longer pinned versions. +- Removed infinity loop during authorization przez `prompt=login` has been send. +- Changed `prompt` handling as set of options instead of regular string. +- Redirect URIs must match exactly with query params. +- Stored user consent are usefull for public clients too. +- Fixed documentation for custom scopes handling. +- Scopes during refresh and code exchange are being taken from authorization request and not from query params + ### [0.5.0] - 2017-05-18 ##### Added diff --git a/docs/conf.py b/docs/conf.py index e3760f6..1d0828f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -53,9 +53,9 @@ author = u'Juan Ignacio Fiorentino' # built documents. # # The short X.Y version. -version = u'0.3' +version = u'0.5' # The full version, including alpha/beta/rc tags. -release = u'0.3.x' +release = u'0.5.x' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -106,7 +106,7 @@ todo_include_todos = False # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'alabaster' +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/sections/installation.rst b/docs/sections/installation.rst index 53db3ed..ebfe61e 100644 --- a/docs/sections/installation.rst +++ b/docs/sections/installation.rst @@ -6,8 +6,8 @@ Installation Requirements ============ -* Python: ``2.7`` ``3.4`` ``3.5`` -* Django: ``1.7`` ``1.8`` ``1.9`` ``1.10`` +* Python: ``2.7`` ``3.4`` ``3.5`` ``3.6`` +* Django: ``1.7`` ``1.8`` ``1.9`` ``1.10`` ``1.11`` Quick Installation ================== diff --git a/setup.py b/setup.py index df451ac..cd0abc6 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) setup( name='django-oidc-provider', - version='0.5.0', + version='0.5.1', packages=find_packages(), include_package_data=True, license='MIT License', From 2e1efc41ed6f00b511e1a6b79127d3d60143e32c Mon Sep 17 00:00:00 2001 From: Wojciech Bartosiak Date: Tue, 11 Jul 2017 16:44:24 +0200 Subject: [PATCH 2/6] fixed typos --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a21dc8b..9656603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,16 +7,16 @@ All notable changes to this project will be documented in this file. ### [0.5.1] - 2017-07-11 ##### Changed -- Documentation template to `Read The Docs`. +- Documentation template changed to `Read The Docs`. ##### Fixed - `install_requires` has not longer pinned versions. -- Removed infinity loop during authorization przez `prompt=login` has been send. +- Removed infinity loop during authorization stage when `prompt=login` has been send. - Changed `prompt` handling as set of options instead of regular string. -- Redirect URIs must match exactly with query params. -- Stored user consent are usefull for public clients too. +- Redirect URI must match exactly with given in query parameter. +- Stored user consent are useful for public clients too. - Fixed documentation for custom scopes handling. -- Scopes during refresh and code exchange are being taken from authorization request and not from query params +- Scopes during refresh and code exchange are being taken from authorization request and not from query parameters. ### [0.5.0] - 2017-05-18 From 8e26248022e026cf4911b8ecaa330ac492d42478 Mon Sep 17 00:00:00 2001 From: Wojciech Bartosiak Date: Tue, 22 Aug 2017 17:33:13 +0200 Subject: [PATCH 3/6] Preparing v0.5.2 (#201) * Fix infinite login loop if "prompt=login" (#198) * Fix Django 2.0 deprecation warnings (#185) --- oidc_provider/lib/utils/common.py | 8 ++++- oidc_provider/migrations/0001_initial.py | 10 +++---- oidc_provider/migrations/0002_userconsent.py | 4 +-- oidc_provider/models.py | 4 +-- .../tests/test_authorize_endpoint.py | 14 +++++++-- oidc_provider/urls.py | 2 +- oidc_provider/views.py | 30 +++++++++++++++++-- 7 files changed, 56 insertions(+), 16 deletions(-) diff --git a/oidc_provider/lib/utils/common.py b/oidc_provider/lib/utils/common.py index 09bdf00..ee1bfa4 100644 --- a/oidc_provider/lib/utils/common.py +++ b/oidc_provider/lib/utils/common.py @@ -1,6 +1,12 @@ from hashlib import sha224 -from django.core.urlresolvers import reverse +import django + +if django.VERSION >= (1, 11): + from django.urls import reverse +else: + from django.core.urlresolvers import reverse + from django.http import HttpResponse from oidc_provider import settings diff --git a/oidc_provider/migrations/0001_initial.py b/oidc_provider/migrations/0001_initial.py index ca32b7e..913f47d 100644 --- a/oidc_provider/migrations/0001_initial.py +++ b/oidc_provider/migrations/0001_initial.py @@ -34,7 +34,7 @@ class Migration(migrations.Migration): ('expires_at', models.DateTimeField()), ('_scope', models.TextField(default=b'')), ('code', models.CharField(unique=True, max_length=255)), - ('client', models.ForeignKey(to='oidc_provider.Client')), + ('client', models.ForeignKey(to='oidc_provider.Client', on_delete=models.CASCADE)), ], options={ 'abstract': False, @@ -49,7 +49,7 @@ class Migration(migrations.Migration): ('_scope', models.TextField(default=b'')), ('access_token', models.CharField(unique=True, max_length=255)), ('_id_token', models.TextField()), - ('client', models.ForeignKey(to='oidc_provider.Client')), + ('client', models.ForeignKey(to='oidc_provider.Client', on_delete=models.CASCADE)), ], options={ 'abstract': False, @@ -59,7 +59,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name='UserInfo', fields=[ - ('user', models.OneToOneField(primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), + ('user', models.OneToOneField(primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ('given_name', models.CharField(max_length=255, null=True, blank=True)), ('family_name', models.CharField(max_length=255, null=True, blank=True)), ('middle_name', models.CharField(max_length=255, null=True, blank=True)), @@ -89,13 +89,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='token', name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='code', name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/oidc_provider/migrations/0002_userconsent.py b/oidc_provider/migrations/0002_userconsent.py index 4cdf6e3..d2a0f12 100644 --- a/oidc_provider/migrations/0002_userconsent.py +++ b/oidc_provider/migrations/0002_userconsent.py @@ -19,8 +19,8 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('expires_at', models.DateTimeField()), ('_scope', models.TextField(default=b'')), - ('client', models.ForeignKey(to='oidc_provider.Client')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('client', models.ForeignKey(to='oidc_provider.Client', on_delete=models.CASCADE)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], options={ 'abstract': False, diff --git a/oidc_provider/models.py b/oidc_provider/models.py index a196239..cfee5d5 100644 --- a/oidc_provider/models.py +++ b/oidc_provider/models.py @@ -83,8 +83,8 @@ class Client(models.Model): class BaseCodeTokenModel(models.Model): - user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_(u'User')) - client = models.ForeignKey(Client, verbose_name=_(u'Client')) + user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_(u'User'), on_delete=models.CASCADE) + client = models.ForeignKey(Client, verbose_name=_(u'Client'), on_delete=models.CASCADE) expires_at = models.DateTimeField(verbose_name=_(u'Expiration Date')) _scope = models.TextField(default='', verbose_name=_(u'Scopes')) diff --git a/oidc_provider/tests/test_authorize_endpoint.py b/oidc_provider/tests/test_authorize_endpoint.py index 41fbb19..361f27f 100644 --- a/oidc_provider/tests/test_authorize_endpoint.py +++ b/oidc_provider/tests/test_authorize_endpoint.py @@ -1,9 +1,9 @@ from oidc_provider.lib.errors import RedirectUriError try: - from urllib.parse import urlencode + from urllib.parse import urlencode, quote except ImportError: - from urllib import urlencode + from urllib import urlencode, quote try: from urllib.parse import parse_qs, urlsplit except ImportError: @@ -369,10 +369,20 @@ class AuthorizationCodeFlowTestCase(TestCase, AuthorizeEndpointMixin): response = self._auth_request('get', data) self.assertIn(settings.get('OIDC_LOGIN_URL'), response['Location']) + self.assertNotIn( + quote('prompt=login'), + response['Location'], + "Found prompt=login, this leads to infinite login loop. See https://github.com/juanifioren/django-oidc-provider/issues/197." + ) response = self._auth_request('get', data, is_user_authenticated=True) self.assertIn(settings.get('OIDC_LOGIN_URL'), response['Location']) self.assertTrue(logout_function.called_once()) + self.assertNotIn( + quote('prompt=login'), + response['Location'], + "Found prompt=login, this leads to infinite login loop. See https://github.com/juanifioren/django-oidc-provider/issues/197." + ) def test_prompt_login_none_parameter(self): """ diff --git a/oidc_provider/urls.py b/oidc_provider/urls.py index 6b62883..d501e88 100644 --- a/oidc_provider/urls.py +++ b/oidc_provider/urls.py @@ -6,7 +6,7 @@ from oidc_provider import ( views, ) - +app_name = 'oidc_provider' urlpatterns = [ url(r'^authorize/?$', views.AuthorizeView.as_view(), name='authorize'), url(r'^token/?$', csrf_exempt(views.TokenView.as_view()), name='token'), diff --git a/oidc_provider/views.py b/oidc_provider/views.py index a5f8cc6..f1b90f0 100644 --- a/oidc_provider/views.py +++ b/oidc_provider/views.py @@ -11,8 +11,14 @@ from django.contrib.auth.views import ( redirect_to_login, logout, ) + +import django +if django.VERSION >= (1, 11): + from django.urls import reverse +else: + from django.core.urlresolvers import reverse + from django.contrib.auth import logout as django_user_logout -from django.core.urlresolvers import reverse from django.http import JsonResponse from django.shortcuts import render from django.template.loader import render_to_string @@ -66,13 +72,14 @@ class AuthorizeView(View): client=authorize.client) if hook_resp: return hook_resp - + if 'login' in authorize.params['prompt']: if 'none' in authorize.params['prompt']: raise AuthorizeError(authorize.params['redirect_uri'], 'login_required', authorize.grant_type) else: django_user_logout(request) - return redirect_to_login(request.get_full_path(), settings.get('OIDC_LOGIN_URL')) + next_page = self.strip_prompt_login(request.get_full_path()) + return redirect_to_login(next_page, settings.get('OIDC_LOGIN_URL')) if 'select_account' in authorize.params['prompt']: # TODO: see how we can support multiple accounts for the end-user. @@ -127,6 +134,9 @@ class AuthorizeView(View): else: if 'none' in authorize.params['prompt']: raise AuthorizeError(authorize.params['redirect_uri'], 'login_required', authorize.grant_type) + if 'login' in authorize.params['prompt']: + next_page = self.strip_prompt_login(request.get_full_path()) + return redirect_to_login(next_page, settings.get('OIDC_LOGIN_URL')) return redirect_to_login(request.get_full_path(), settings.get('OIDC_LOGIN_URL')) @@ -174,6 +184,20 @@ class AuthorizeView(View): return redirect(uri) + @staticmethod + def strip_prompt_login(path): + """ + Strips 'login' from the 'prompt' query parameter. + """ + uri = urlsplit(path) + query_params = parse_qs(uri.query) + if 'login' in query_params['prompt']: + query_params['prompt'].remove('login') + if not query_params['prompt']: + del query_params['prompt'] + uri = uri._replace(query=urlencode(query_params, doseq=True)) + return urlunsplit(uri) + class TokenView(View): def post(self, request, *args, **kwargs): From 0e4ba169dfb27b2d40d0e3fc0a0a9812e3eafe9a Mon Sep 17 00:00:00 2001 From: Wojciech Bartosiak Date: Tue, 22 Aug 2017 17:36:18 +0200 Subject: [PATCH 4/6] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9656603..c0951b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. ### [Unreleased] + +### [0.5.2] - 2017-08-22 + +##### Fixed +- Fix infinite login loop if "prompt=login" (#198) +- Fix Django 2.0 deprecation warnings (#185) + + ### [0.5.1] - 2017-07-11 ##### Changed From f052f694c9602102ccd9e693b5d54b30e1ebf69f Mon Sep 17 00:00:00 2001 From: Wojciech Bartosiak Date: Tue, 22 Aug 2017 17:36:54 +0200 Subject: [PATCH 5/6] Bump version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index cd0abc6..61ce8cc 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) setup( name='django-oidc-provider', - version='0.5.1', + version='0.5.2', packages=find_packages(), include_package_data=True, license='MIT License', From 6bb42a17317b34906c31489bf8cbf51f13a1b5e6 Mon Sep 17 00:00:00 2001 From: Wojciech Bartosiak Date: Wed, 23 Aug 2017 14:01:32 +0200 Subject: [PATCH 6/6] removed tab char --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0951b8..21447c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. ### [0.5.2] - 2017-08-22 ##### Fixed -- Fix infinite login loop if "prompt=login" (#198) +- Fix infinite login loop if "prompt=login" (#198) - Fix Django 2.0 deprecation warnings (#185)