Commit e3bf5bb8 authored by Jean-Baptiste Pasquier's avatar Jean-Baptiste Pasquier

Merge branch 'default-redirect-uri' into 'master'

Default redirect uri

See merge request !37
parents 5e87c731 a21c268a
Pipeline #7129 passed with stage
in 27 seconds
......@@ -41,7 +41,7 @@ MIDDLEWARE = [ ..., 'djangoldp_account.auth.middleware.JWTUserMiddleware']
```
You should also ensure that `SITE_URL` and `BASE_URL` are set correctly.
You should also ensure that `SITE_URL` and `BASE_URL` are set correctly.
## User registration
User registration use django_registration module : https://django-registration.readthedocs.io/en/3.0.1/
......@@ -72,6 +72,14 @@ Modify the `AUTHENTICATION_BACKENDS` variable in `settings.py` to replace
Ensure `'djangoldp_account.auth.backends.EmailOrUsernameAuthBackend'` is
first in the list.
## Enabling default_redirect_uri behaviour
For many linked-data platforms, there will be multiple authentication providers, and potential multiple client-side apps/websites. In this case it can be difficult to know where to redirect the user. On login it's possible to set `next` as a query parameter pointing to the new site, but the user may have come from sources without this parameter set (e.g. a forgot password link in an email). For user experience, we offer a system which keeps track of the location the user previously logged in from, and falls back onto this if there is not a `next` specified in the URL. To use system this simply add the following to your settings.py
```
LOGIN_REDIRECT_URL = '/redirect-default/'
```
## Authenticate from an external provider
- go to mysite.org/accounts/login
......
......@@ -11,7 +11,7 @@ class LDPUserAdmin(UserAdmin):
form = LDPUserChangeForm
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('slug',)}),
(None, {'fields': ('slug', 'default_redirect_uri')}),
)
......
......@@ -10,7 +10,7 @@ from djangoldp.permissions import LDPPermissions
from djangoldp.views import LDPViewSet
from djangoldp_account.forms import LDPUserForm
from .models import ChatProfile, Account
from .views import userinfocustom, RPLoginView, RPLoginCallBackView, check_user
from .views import userinfocustom, RPLoginView, RPLoginCallBackView, check_user, LDPAccountLoginView, RedirectView
Group._meta.serializer_fields = ['name']
Group._meta.anonymous_perms = getattr(settings, 'GROUP_ANONYMOUS_PERMISSIONS', ['view'])
......@@ -29,6 +29,7 @@ urlpatterns = [
),
name='django_registration_register',
),
url(r'^auth/login/$', LDPAccountLoginView.as_view(),name='login'),
url(r'^auth/', include('django_registration.backends.activation.urls')),
url(r'^auth/', include('django.contrib.auth.urls')),
url(r'^accounts/', LDPViewSet.urls(model=Account, permission_classes=[LDPPermissions])),
......@@ -37,5 +38,6 @@ urlpatterns = [
url(r'^oidc/login/?$', RPLoginView.as_view(), name='oidc_login'),
url(r'^userinfo/?$', csrf_exempt(userinfocustom)),
url(r'^check-user/?$', csrf_exempt(check_user)),
url(r'^redirect-default/$', RedirectView.as_view(),name='redirect-default'),
url(r'^', include('oidc_provider.urls', namespace='oidc_provider'))
]
......@@ -59,6 +59,7 @@ class RPLoginEndpoint(object):
def op_login_url(self):
subject = self.params['subject']
redirect_uris = [settings.SITE_URL + reverse('oidc_login_callback'), settings.SITE_URL]
# resolve provider
provider_info = None
try:
issuer = self.client.discover(subject)
......@@ -119,6 +120,9 @@ class RPLoginEndpoint(object):
return auth_req.request(self.client.authorization_endpoint)
def check_on_known_providers(self, subject):
"""
attempts to find a provider/OP for parameterised subject
"""
provider_info = None
for opclient in OPClient.objects.all():
try:
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2020-01-21 13:09
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('djangoldp_account', '0004_auto_20191203_0921'),
]
operations = [
migrations.AddField(
model_name='ldpuser',
name='default_redirect_uri',
field=models.URLField(blank=True, null=True),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2020-01-21 13:21
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('djangoldp_account', '0005_ldpuser_default_redirect_uri'),
]
operations = [
migrations.AlterField(
model_name='ldpuser',
name='default_redirect_uri',
field=models.URLField(blank=True, help_text='A URL to redirect to (home page) when none other is provided in a request, e.g. on forgot password. This will be automatically updated on login', null=True),
),
]
......@@ -63,6 +63,8 @@ class LDPUser(AbstractUser, Model):
'unique': _("A user with that uses this email address already exists."),
},
)
# updated automatically on login - the default uri to redirect to if none has been passed
default_redirect_uri = models.URLField(blank=True, null=True, help_text='A URL to redirect to (home page) when none other is provided in a request, e.g. on forgot password. This will be automatically updated on login')
class Meta:
verbose_name = _('user')
......
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans "Lost" %}
{% endblock %}
{% block supplementary_css %}
<link rel="stylesheet" href="{% static 'base.css' %}"/>
<link rel="stylesheet" href="{% static 'registration/login.css' %}"/>
{% endblock %}
{% block content %}
<div class="sib-login-title">
<h2>
{% blocktrans %}
Sorry, we couldn't find a page to redirect you to. Please navigate to your
preferred application manually
{% endblocktrans %}
</h2>
<p>
{% blocktrans %}
If you are not sure, contact a system administrator
{% endblocktrans %}
</p>
</div>
{% endblock %}
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect, HttpResponseNotFound
from django.views import View
from django.contrib.auth.views import LoginView
from django.shortcuts import redirect, render
from django.urls import reverse
from djangoldp_account import settings
from djangoldp_account.endpoints.rp_login import RPLoginCallBackEndpoint, RPLoginEndpoint
......@@ -41,6 +44,49 @@ def check_user(request, *args, **kwargs):
return HttpResponseNotFound()
class RedirectView(View):
"""
View for managing where to redirect the user after a successful login
In the event that 'next' is not set (they may be coming from one of multiple front-end apps)
To use this functionality set LOGIN_REDIRECT_URL to a url which uses this View. Then if the user
has not set 'next' parameter in the login request, Django will redirect them here
"""
def get(self, request, *args, **kwargs):
next = request.user.default_redirect_uri
# attempt to redirect to the user's default_redirect_uri
if next is not None and next != '':
return redirect(next, permanent=False)
# there is no default to fall back on
# redirect admins to the admin panel
if request.user.is_superuser:
return redirect(reverse('admin:index'), permanent=False)
# redirect other users to a page apologising
return render(request, template_name='registration/lost_user.html')
class LDPAccountLoginView(LoginView):
"""
Extension of django.contrib.auth.views.LoginView for managing user's default_redirect_uri
"""
# Save login url as preferred redirect
def post(self, request, *args, **kwargs):
from django.conf import settings
return_value = super(LDPAccountLoginView, self).post(request, *args, **kwargs)
# if the user has 'next' set which is not default, update their preference
next = request.POST.get('next')
if next != settings.LOGIN_REDIRECT_URL:
request.user.default_redirect_uri = next
request.user.save()
return return_value
class RPLoginView(View):
"""
RP authentication workflow
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment