From b0a91241761fa7d56fe66546d21d5415c9a71a48 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 6 Aug 2019 16:51:31 +0200 Subject: [PATCH] authenticators: select default OU based on service's ACL (#35213) --- src/authentic2/authenticators.py | 32 +++++++++++++++++++++++-------- tests/test_login.py | 33 +++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/authentic2/authenticators.py b/src/authentic2/authenticators.py index 76c6af67..5d905d4c 100644 --- a/src/authentic2/authenticators.py +++ b/src/authentic2/authenticators.py @@ -17,8 +17,9 @@ 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 authentic2.a2_rbac.models import OrganizationalUnit as OU, Role +from authentic2.custom_user.models import User +from . import views, app_settings, utils, constants, models from .forms import authentication as authentication_forms @@ -35,15 +36,30 @@ class LoginPasswordAuthenticator(object): return 'password' def login(self, request, *args, **kwargs): + service_slug = request.GET.get(constants.SERVICE_FIELD_NAME) context = kwargs.get('context', {}) is_post = request.method == 'POST' and self.submit_name in request.POST data = request.POST if is_post else None - 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] + preferred_ous = [] + + # Special handling when the form contains an OU selector + if app_settings.A2_LOGIN_FORM_OU_SELECTOR: + default_ou = None + # for the special case of services open to only one OU, pre-select it + if service_slug: + roles = Role.objects.filter(allowed_services__slug=service_slug).children() + ous = OU.objects.filter(id__in=User.objects.filter(roles__in=roles).values_list('ou_id', flat=True)) + if len(ous) == 1: + default_ou = ous[0] + preferred_ous = utils.get_remember_cookie(request, 'preferred-ous') + if preferred_ous: + preferred_ous = OU.objects.filter(pk__in=preferred_ous) + if not default_ou and preferred_ous: + default_ou = preferred_ous[0] + if default_ou: + initial['ou'] = default_ou + form = authentication_forms.AuthenticationForm( request=request, data=data, @@ -66,7 +82,7 @@ class LoginPasswordAuthenticator(object): request.session['remember_me'] = True request.session.set_expiry(app_settings.A2_USER_REMEMBER_ME) response = utils.login(request, form.get_user(), how, - service_slug=request.GET.get(constants.SERVICE_FIELD_NAME)) + service_slug=service_slug) if 'ou' in form.fields: utils.prepend_remember_cookie(request, response, 'preferred-ous', form.cleaned_data['ou'].pk) return response diff --git a/tests/test_login.py b/tests/test_login.py index 5c8f2cd4..91478326 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -19,6 +19,8 @@ import pytest from django.utils.six.moves.urllib.parse import quote from django.contrib.auth import get_user_model +from authentic2 import models + from utils import login @@ -135,7 +137,7 @@ def test_session_remember_me_nok(app, settings, simple_user, freezer): assert simple_user.first_name not in response -def test_ou_selector(app, settings, simple_user, ou1): +def test_ou_selector(app, settings, simple_user, ou1, ou2, user_ou1, role_ou1): settings.A2_LOGIN_FORM_OU_SELECTOR = True response = app.get('/login/') # Check selector is here and there are no errors @@ -143,7 +145,7 @@ def test_ou_selector(app, settings, simple_user, ou1): 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'---------'])) + == set([u'Default organizational unit', u'OU1', u'OU2', u'---------'])) # Check selector is required response.form.set('username', simple_user.username) response.form.set('password', simple_user.username) @@ -170,5 +172,30 @@ def test_ou_selector(app, settings, simple_user, ou1): 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'---------'])) + == set([u'Default organizational unit', u'OU1', u'OU2', u'---------'])) + assert response.pyquery.find('select#id_ou option[selected]')[0].text == u'Default organizational unit' + + # Create a service + service = models.Service.objects.create(name='Service', slug='service', ou=ou1) + response = app.get('/login/') + assert response.pyquery.find('select#id_ou option[selected]')[0].text == u'Default organizational unit' + + # service is specified but not access-control is defined, default for user is selected + response = app.get('/login/?service=service') + assert response.pyquery.find('select#id_ou option[selected]')[0].text == u'Default organizational unit' + + # service is specified, access control is defined but role is empty, default for user is selected + service.authorized_roles.through.objects.create(service=service, role=role_ou1) + response = app.get('/login/?service=service') assert response.pyquery.find('select#id_ou option[selected]')[0].text == u'Default organizational unit' + + # user is added to role_ou1, now the OU of the only user is selected + user_ou1.roles.add(role_ou1) + response = app.get('/login/?service=service') + assert response.pyquery.find('select#id_ou option[selected]')[0].text == u'OU1' + + # if we change the user's ou, then default selected OU changes + user_ou1.ou = ou2 + user_ou1.save() + response = app.get('/login/?service=service') + assert response.pyquery.find('select#id_ou option[selected]')[0].text == u'OU2' -- 2.22.0