Preparing v0.5.2 (#201)
* Fix infinite login loop if "prompt=login" (#198) * Fix Django 2.0 deprecation warnings (#185)
This commit is contained in:
parent
2e1efc41ed
commit
8e26248022
7 changed files with 56 additions and 16 deletions
|
@ -1,6 +1,12 @@
|
||||||
from hashlib import sha224
|
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 django.http import HttpResponse
|
||||||
|
|
||||||
from oidc_provider import settings
|
from oidc_provider import settings
|
||||||
|
|
|
@ -34,7 +34,7 @@ class Migration(migrations.Migration):
|
||||||
('expires_at', models.DateTimeField()),
|
('expires_at', models.DateTimeField()),
|
||||||
('_scope', models.TextField(default=b'')),
|
('_scope', models.TextField(default=b'')),
|
||||||
('code', models.CharField(unique=True, max_length=255)),
|
('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={
|
options={
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
|
@ -49,7 +49,7 @@ class Migration(migrations.Migration):
|
||||||
('_scope', models.TextField(default=b'')),
|
('_scope', models.TextField(default=b'')),
|
||||||
('access_token', models.CharField(unique=True, max_length=255)),
|
('access_token', models.CharField(unique=True, max_length=255)),
|
||||||
('_id_token', models.TextField()),
|
('_id_token', models.TextField()),
|
||||||
('client', models.ForeignKey(to='oidc_provider.Client')),
|
('client', models.ForeignKey(to='oidc_provider.Client', on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
|
@ -59,7 +59,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='UserInfo',
|
name='UserInfo',
|
||||||
fields=[
|
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)),
|
('given_name', models.CharField(max_length=255, null=True, blank=True)),
|
||||||
('family_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)),
|
('middle_name', models.CharField(max_length=255, null=True, blank=True)),
|
||||||
|
@ -89,13 +89,13 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='token',
|
model_name='token',
|
||||||
name='user',
|
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,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='code',
|
model_name='code',
|
||||||
name='user',
|
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,
|
preserve_default=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -19,8 +19,8 @@ class Migration(migrations.Migration):
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
('expires_at', models.DateTimeField()),
|
('expires_at', models.DateTimeField()),
|
||||||
('_scope', models.TextField(default=b'')),
|
('_scope', models.TextField(default=b'')),
|
||||||
('client', models.ForeignKey(to='oidc_provider.Client')),
|
('client', models.ForeignKey(to='oidc_provider.Client', on_delete=models.CASCADE)),
|
||||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
|
|
|
@ -83,8 +83,8 @@ class Client(models.Model):
|
||||||
|
|
||||||
class BaseCodeTokenModel(models.Model):
|
class BaseCodeTokenModel(models.Model):
|
||||||
|
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_(u'User'))
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_(u'User'), on_delete=models.CASCADE)
|
||||||
client = models.ForeignKey(Client, verbose_name=_(u'Client'))
|
client = models.ForeignKey(Client, verbose_name=_(u'Client'), on_delete=models.CASCADE)
|
||||||
expires_at = models.DateTimeField(verbose_name=_(u'Expiration Date'))
|
expires_at = models.DateTimeField(verbose_name=_(u'Expiration Date'))
|
||||||
_scope = models.TextField(default='', verbose_name=_(u'Scopes'))
|
_scope = models.TextField(default='', verbose_name=_(u'Scopes'))
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
from oidc_provider.lib.errors import RedirectUriError
|
from oidc_provider.lib.errors import RedirectUriError
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode, quote
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from urllib import urlencode
|
from urllib import urlencode, quote
|
||||||
try:
|
try:
|
||||||
from urllib.parse import parse_qs, urlsplit
|
from urllib.parse import parse_qs, urlsplit
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -369,10 +369,20 @@ class AuthorizationCodeFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
|
|
||||||
response = self._auth_request('get', data)
|
response = self._auth_request('get', data)
|
||||||
self.assertIn(settings.get('OIDC_LOGIN_URL'), response['Location'])
|
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)
|
response = self._auth_request('get', data, is_user_authenticated=True)
|
||||||
self.assertIn(settings.get('OIDC_LOGIN_URL'), response['Location'])
|
self.assertIn(settings.get('OIDC_LOGIN_URL'), response['Location'])
|
||||||
self.assertTrue(logout_function.called_once())
|
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):
|
def test_prompt_login_none_parameter(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6,7 +6,7 @@ from oidc_provider import (
|
||||||
views,
|
views,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app_name = 'oidc_provider'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^authorize/?$', views.AuthorizeView.as_view(), name='authorize'),
|
url(r'^authorize/?$', views.AuthorizeView.as_view(), name='authorize'),
|
||||||
url(r'^token/?$', csrf_exempt(views.TokenView.as_view()), name='token'),
|
url(r'^token/?$', csrf_exempt(views.TokenView.as_view()), name='token'),
|
||||||
|
|
|
@ -11,8 +11,14 @@ from django.contrib.auth.views import (
|
||||||
redirect_to_login,
|
redirect_to_login,
|
||||||
logout,
|
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.contrib.auth import logout as django_user_logout
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
@ -72,7 +78,8 @@ class AuthorizeView(View):
|
||||||
raise AuthorizeError(authorize.params['redirect_uri'], 'login_required', authorize.grant_type)
|
raise AuthorizeError(authorize.params['redirect_uri'], 'login_required', authorize.grant_type)
|
||||||
else:
|
else:
|
||||||
django_user_logout(request)
|
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']:
|
if 'select_account' in authorize.params['prompt']:
|
||||||
# TODO: see how we can support multiple accounts for the end-user.
|
# TODO: see how we can support multiple accounts for the end-user.
|
||||||
|
@ -127,6 +134,9 @@ class AuthorizeView(View):
|
||||||
else:
|
else:
|
||||||
if 'none' in authorize.params['prompt']:
|
if 'none' in authorize.params['prompt']:
|
||||||
raise AuthorizeError(authorize.params['redirect_uri'], 'login_required', authorize.grant_type)
|
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'))
|
return redirect_to_login(request.get_full_path(), settings.get('OIDC_LOGIN_URL'))
|
||||||
|
|
||||||
|
@ -174,6 +184,20 @@ class AuthorizeView(View):
|
||||||
|
|
||||||
return redirect(uri)
|
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):
|
class TokenView(View):
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
Loading…
Reference in a new issue