From fcc8eaac896b012834929d1fc882e5d678579e9c Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 8 Oct 2019 16:46:34 +0200 Subject: [PATCH] authenticators: add easy accesible OU based on service's ACL (#36783) It replaces changes from #35213. OU are added after OU remembered through cookies; they are ordered based on their user subset's count (how many of their users can access the targeted service). --- src/authentic2/authenticators.py | 45 ++++++++++++++++++-------- src/authentic2/forms/authentication.py | 4 +-- tests/test_login.py | 7 +++- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/authentic2/authenticators.py b/src/authentic2/authenticators.py index 5d905d4c..1d27a103 100644 --- a/src/authentic2/authenticators.py +++ b/src/authentic2/authenticators.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from django.db.models import Count from django.shortcuts import render from django.utils.translation import ugettext as _, ugettext_lazy @@ -35,6 +36,35 @@ class LoginPasswordAuthenticator(object): def id(self): return 'password' + def get_service_ous(self, request): + service_slug = request.GET.get(constants.SERVICE_FIELD_NAME) + if not service_slug: + return [] + roles = Role.objects.filter(allowed_services__slug=service_slug).children() + if not roles: + return [] + service_ou_ids = [] + qs = User.objects.filter(roles__in=roles).values_list('ou').annotate(count=Count('ou')).order_by('-count') + for ou_id, count in qs: + if not ou_id: + continue + service_ou_ids.append(ou_id) + if not service_ou_ids: + return [] + return OU.objects.filter(pk__in=service_ou_ids) + + def get_preferred_ous(self, request): + preferred_ous_cookie = utils.get_remember_cookie(request, 'preferred-ous') + preferred_ous = [] + if preferred_ous_cookie: + preferred_ous.extend(OU.objects.filter(pk__in=preferred_ous_cookie)) + # for the special case of services open to only one OU, pre-select it + for ou in self.get_service_ous(request): + if ou in preferred_ous: + continue + preferred_ous.append(ou) + return preferred_ous + def login(self, request, *args, **kwargs): service_slug = request.GET.get(constants.SERVICE_FIELD_NAME) context = kwargs.get('context', {}) @@ -45,20 +75,9 @@ class LoginPasswordAuthenticator(object): # 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') + preferred_ous = self.get_preferred_ous(request) 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 + initial['ou'] = preferred_ous[0] form = authentication_forms.AuthenticationForm( request=request, diff --git a/src/authentic2/forms/authentication.py b/src/authentic2/forms/authentication.py index e57a9ac2..18ec420f 100644 --- a/src/authentic2/forms/authentication.py +++ b/src/authentic2/forms/authentication.py @@ -58,10 +58,10 @@ class AuthenticationForm(auth_forms.AuthenticationForm): else: if preferred_ous: choices = self.fields['ou'].choices - new_choices = [ + new_choices = list(choices)[:1] + [ (ugettext('Preferred organizational units'), [ (ou.pk, ou.name) for ou in preferred_ous]), - (ugettext('All organizational units'), list(choices)), + (ugettext('All organizational units'), list(choices)[1:]), ] self.fields['ou'].choices = new_choices diff --git a/tests/test_login.py b/tests/test_login.py index 91478326..0fc2661b 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -189,9 +189,14 @@ def test_ou_selector(app, settings, simple_user, ou1, ou2, user_ou1, 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 is added to role_ou1, default for user is still 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'Default organizational unit' + + # Clear cookies, OU1 is selected + app.cookiejar.clear() + 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 -- 2.23.0