From 0fd98047eef0b035bb0a7357eeb6a770aad3ff3d Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 2 Aug 2019 13:53:46 +0200 Subject: [PATCH] remember 5 last selected ous on login form (#35209) --- src/authentic2/authenticators.py | 20 ++++++++++++++--- src/authentic2/forms/authentication.py | 14 +++++++++++- src/authentic2/utils.py | 27 +++++++++++++++++++++++ tests/test_login.py | 12 +++++++++++ tests/test_utils.py | 30 ++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 4 deletions(-) diff --git a/src/authentic2/authenticators.py b/src/authentic2/authenticators.py index b521dfeb..76c6af67 100644 --- a/src/authentic2/authenticators.py +++ b/src/authentic2/authenticators.py @@ -17,6 +17,7 @@ from django.shortcuts import render from django.utils.translation import ugettext as _, ugettext_lazy +from authentic2.a2_rbac.models import OrganizationalUnit as OU from . import views, app_settings, utils, constants from .forms import authentication as authentication_forms @@ -37,7 +38,17 @@ class LoginPasswordAuthenticator(object): context = kwargs.get('context', {}) is_post = request.method == 'POST' and self.submit_name in request.POST data = request.POST if is_post else None - form = authentication_forms.AuthenticationForm(request=request, data=data) + preferred_ous = utils.get_remember_cookie(request, 'preferred-ous') + if preferred_ous: + preferred_ous = OU.objects.filter(pk__in=preferred_ous) + initial = {} + if preferred_ous: + initial['ou'] = preferred_ous[0] + form = authentication_forms.AuthenticationForm( + request=request, + data=data, + initial=initial, + preferred_ous=preferred_ous) if app_settings.A2_ACCEPT_EMAIL_AUTHENTICATION: form.fields['username'].label = _('Username or email') if app_settings.A2_USERNAME_LABEL: @@ -54,8 +65,11 @@ class LoginPasswordAuthenticator(object): if form.cleaned_data.get('remember_me'): request.session['remember_me'] = True request.session.set_expiry(app_settings.A2_USER_REMEMBER_ME) - return utils.login(request, form.get_user(), how, - service_slug=request.GET.get(constants.SERVICE_FIELD_NAME)) + response = utils.login(request, form.get_user(), how, + service_slug=request.GET.get(constants.SERVICE_FIELD_NAME)) + if 'ou' in form.fields: + utils.prepend_remember_cookie(request, response, 'preferred-ous', form.cleaned_data['ou'].pk) + return response context['form'] = form return render(request, 'authentic2/login_password_form.html', context) diff --git a/src/authentic2/forms/authentication.py b/src/authentic2/forms/authentication.py index 864c6549..e57a9ac2 100644 --- a/src/authentic2/forms/authentication.py +++ b/src/authentic2/forms/authentication.py @@ -17,7 +17,7 @@ import math from django import forms -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext_lazy as _, ugettext from django.contrib.auth import forms as auth_forms from django.utils import html @@ -41,7 +41,10 @@ class AuthenticationForm(auth_forms.AuthenticationForm): queryset=OU.objects.all()) def __init__(self, *args, **kwargs): + preferred_ous = kwargs.pop('preferred_ous', []) + super(AuthenticationForm, self).__init__(*args, **kwargs) + self.exponential_backoff = ExponentialRetryTimeout( key_prefix='login-exp-backoff-', duration=app_settings.A2_LOGIN_EXPONENTIAL_RETRY_TIMEOUT_DURATION, @@ -52,6 +55,15 @@ class AuthenticationForm(auth_forms.AuthenticationForm): if not app_settings.A2_LOGIN_FORM_OU_SELECTOR: del self.fields['ou'] + else: + if preferred_ous: + choices = self.fields['ou'].choices + new_choices = [ + (ugettext('Preferred organizational units'), [ + (ou.pk, ou.name) for ou in preferred_ous]), + (ugettext('All organizational units'), list(choices)), + ] + self.fields['ou'].choices = new_choices if self.request: self.remote_addr = self.request.META['REMOTE_ADDR'] diff --git a/src/authentic2/utils.py b/src/authentic2/utils.py index c0c9d954..851bdf79 100644 --- a/src/authentic2/utils.py +++ b/src/authentic2/utils.py @@ -1152,3 +1152,30 @@ def gettid(): def authenticate(request=None, **kwargs): # Compatibility layer with Django 1.8 return dj_authenticate(request=request, **kwargs) + + +def get_remember_cookie(request, name, count=5): + value = request.COOKIES.get(name) + if not value: + return [] + try: + parsed = value.split() + except Exception: + return [] + + values = [] + for i, v in zip(range(count), parsed): + try: + values.append(int(v)) + except ValueError: + return [] + return values + + +def prepend_remember_cookie(request, response, name, value, count=5): + values = get_remember_cookie(request, name, count=count) + values = [value] + values[:count - 1] + response.set_cookie(name, ' '.join(map(str, values)), + max_age=86400 * 365, # keep preferences for 1 year + path=request.path, + httponly=True) diff --git a/tests/test_login.py b/tests/test_login.py index 8d18b277..5c8f2cd4 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -141,6 +141,7 @@ def test_ou_selector(app, settings, simple_user, ou1): # Check selector is here and there are no errors assert not response.pyquery('.errorlist') assert response.pyquery.find('select#id_ou') + assert len(response.pyquery.find('select#id_ou optgroup')) == 0 assert (set([elt.text for elt in response.pyquery.find('select#id_ou option')]) == set([u'Default organizational unit', u'OU1', u'---------'])) # Check selector is required @@ -160,3 +161,14 @@ def test_ou_selector(app, settings, simple_user, ou1): response.form.set('ou', str(simple_user.ou.pk)) response = response.form.submit(name='login-password-submit').follow() assert '_auth_user_id' in app.session + response = response.click('Logout').maybe_follow() + assert '_auth_user_id' not in app.session + assert app.cookies['preferred-ous'] == str(simple_user.ou.pk) + + # Check last ou is preselected and shown first + response = app.get('/login/') + assert response.pyquery.find('select#id_ou') + assert len(response.pyquery.find('select#id_ou optgroup')) == 2 + assert (set([elt.text for elt in response.pyquery.find('select#id_ou option')]) + == set([u'Default organizational unit', u'OU1', u'---------'])) + assert response.pyquery.find('select#id_ou option[selected]')[0].text == u'Default organizational unit' diff --git a/tests/test_utils.py b/tests/test_utils.py index 693ff1a0..69c2c7b6 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -86,3 +86,33 @@ def test_get_authentication_events_hows(rf, simple_user): assert 'password' not in [ev['how'] for ev in get_authentication_events(request)] login(request, user, 'password') assert 'password' in [ev['how'] for ev in get_authentication_events(request)] + + +def test_remember_cookie(rf): + from authentic2.utils import get_remember_cookie, prepend_remember_cookie + from django.http import HttpResponse + + request = rf.get('/') + request.COOKIES['preferrence'] = '1 2' + assert get_remember_cookie(request, 'preferrence') == [1, 2] + request.COOKIES['preferrence'] = '1 2 3 4 5 6' + assert get_remember_cookie(request, 'preferrence') == [1, 2, 3, 4, 5] + request.COOKIES['preferrence'] = '1 2 3 4 x' + assert get_remember_cookie(request, 'preferrence') == [] + request.COOKIES['preferrence'] = 'aaa' + assert get_remember_cookie(request, 'preferrence') == [] + + response = HttpResponse() + request.COOKIES['preferrence'] = '1 2' + prepend_remember_cookie(request, response, 'preferrence', 4) + assert response.cookies['preferrence'].value == '4 1 2' + + response = HttpResponse() + request.COOKIES['preferrence'] = '1 2 a' + prepend_remember_cookie(request, response, 'preferrence', 4) + assert response.cookies['preferrence'].value == '4' + + response = HttpResponse() + request.COOKIES['preferrence'] = '1 2 3 4 5' + prepend_remember_cookie(request, response, 'preferrence', 7) + assert response.cookies['preferrence'].value == '7 1 2 3 4' -- 2.22.0