From 8ae42a05d8faa2898bd6ab0a9765435ad7b6301a Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Mon, 5 Oct 2020 16:13:00 +0200 Subject: [PATCH] manager: look for duplicates on user creation (#45419) --- src/authentic2/manager/app_settings.py | 1 + .../manager/duplicate_user_add.html | 17 +++++++ .../authentic2/manager/user_add.html | 14 ++++++ src/authentic2/manager/user_views.py | 16 +++++++ tests/test_user_manager.py | 48 +++++++++++++++++++ 5 files changed, 96 insertions(+) create mode 100644 src/authentic2/manager/templates/authentic2/manager/duplicate_user_add.html diff --git a/src/authentic2/manager/app_settings.py b/src/authentic2/manager/app_settings.py index 587c99c27..3a2f338f8 100644 --- a/src/authentic2/manager/app_settings.py +++ b/src/authentic2/manager/app_settings.py @@ -28,6 +28,7 @@ class AppSettings(object): 'USER_SEARCH_MINIMUM_CHARS': 0, 'LOGIN_URL': None, 'SITE_TITLE': None, + 'CHECK_DUPLICATE_USERS': False, } def __getattr__(self, name): diff --git a/src/authentic2/manager/templates/authentic2/manager/duplicate_user_add.html b/src/authentic2/manager/templates/authentic2/manager/duplicate_user_add.html new file mode 100644 index 000000000..54bd6b726 --- /dev/null +++ b/src/authentic2/manager/templates/authentic2/manager/duplicate_user_add.html @@ -0,0 +1,17 @@ +{% load i18n %} + +
  • + {{ user.get_full_name }} + {% if user.email %}- {{ user.email }}{% endif %} + {% for attribute in user.attribute_values.all %} + {% if attribute.content and attribute.attribute.name != "first_name" and attribute.attribute.name != "last_name" %} + - {{ attribute.content }} + {% endif %} + {% endfor %} + - {% blocktrans with date=user.date_joined %}Created on {{ date }}{% endblocktrans %} + {% if user.last_login %} + - {% blocktrans with date=user.last_login %}Last login on {{ date }}{% endblocktrans %} + {% else %} + - {% trans "Never logged in" %} + {% endif %} +
  • diff --git a/src/authentic2/manager/templates/authentic2/manager/user_add.html b/src/authentic2/manager/templates/authentic2/manager/user_add.html index 2e4f31927..df1be0306 100644 --- a/src/authentic2/manager/templates/authentic2/manager/user_add.html +++ b/src/authentic2/manager/templates/authentic2/manager/user_add.html @@ -5,6 +5,20 @@ {% trans "Add an user" %} {% endblock %} +{% block beforeform %} +{% if duplicate_users %} + +
    +

    {% trans "This user may already exist, please check the list below before creating it :" %}

    + +
    +{% endif %} +{% endblock %} + {% block hidden_inputs %} {{ block.super }} {% if next %}{% endif %} diff --git a/src/authentic2/manager/user_views.py b/src/authentic2/manager/user_views.py index 8d82d84ab..78d9fb79c 100644 --- a/src/authentic2/manager/user_views.py +++ b/src/authentic2/manager/user_views.py @@ -148,6 +148,7 @@ class UserAddView(BaseAddView): form_class = UserAddForm permissions = ['custom_user.add_user'] template_name = 'authentic2/manager/user_add.html' + duplicate_users = None def dispatch(self, request, *args, **kwargs): qs = request.user.ous_with_perm('custom_user.add_user') @@ -193,9 +194,24 @@ class UserAddView(BaseAddView): field_name='cancel') context['next'] = select_next_url(self.request, default=None, include_post=True) context['ou'] = self.ou + context['duplicate_users'] = self.duplicate_users return context def form_valid(self, form): + if app_settings.CHECK_DUPLICATE_USERS: + first_name = form.cleaned_data['first_name'] + last_name = form.cleaned_data['last_name'] + duplicate_users = User.objects.find_duplicates( + first_name=first_name, + last_name=last_name, + birthdate=form.cleaned_data.get('birthdate'), + ) + token = self.request.POST.get('confirm-creation-token') + valid_confirmation_token = bool(token == '%s %s' % (first_name, last_name)) + if duplicate_users and not valid_confirmation_token: + self.duplicate_users = duplicate_users + return self.form_invalid(form) + response = super(UserAddView, self).form_valid(form) hooks.call_hooks('event', name='manager-add-user', user=self.request.user, instance=form.instance, form=form) diff --git a/tests/test_user_manager.py b/tests/test_user_manager.py index 327042aa0..34fbf8b94 100644 --- a/tests/test_user_manager.py +++ b/tests/test_user_manager.py @@ -18,6 +18,7 @@ from __future__ import unicode_literals import csv +import datetime import re import time from urllib.parse import urlparse @@ -987,3 +988,50 @@ def test_manager_user_roles_breadcrumb(app, superuser, simple_user): assert [x.text for x in resp.html.find('span', {'id': 'breadcrumb'}).find_all('a')] == [ 'Homepage', 'Administration', 'Users', 'super user', 'Roles'] + + +def test_manager_create_user_duplicates(admin, app, ou1, settings): + settings.A2_MANAGER_CHECK_DUPLICATE_USERS = True + Attribute.objects.create( + kind='birthdate', name='birthdate', label='birthdate', required=False, searchable=True + ) + + user = User.objects.create( + first_name='Alexander', last_name='Longname', email='alexandre.longname@entrouvert.com' + ) + user.attributes.birthdate = datetime.date(1980, 1, 2) + user2 = User.objects.create(first_name='Alexandra', last_name='Longname') + user3 = User.objects.create(first_name='Alex', last_name='Shortname') + + login(app, admin) + resp = app.get('/manage/users/%s/add/' % ou1.pk) + + form = resp.form + form.set('first_name', 'Alexandre') + form.set('last_name', 'Longname') + form.set('email', 'alex@entrouvert.com') + form.set('password1', 'ABcd1234') + form.set('password2', 'ABcd1234') + resp = form.submit() + + assert 'user may already exist' in resp.text + assert 'Alexander Longname' in resp.text + assert '- alexandre.longname@entrouvert.com' in resp.text + assert '- 1980-01-02' in resp.text + assert '/users/%s/' % user.pk in resp.text + assert 'Alexandra Longname' in resp.text + assert '/users/%s/' % user2.pk in resp.text + + # This user was in fact duplicate. Agent reuses the form to fill details on another user + form = resp.form + form.set('first_name', 'Alexa') + form.set('last_name', 'Shortname') + form.set('email', 'ashortname@entrouvert.com') + resp = form.submit() + + assert 'user may already exist' in resp.text + assert '/users/%s/' % user3.pk in resp.text + + # Not a duplicate this time. Simply submitting again creates user + resp = resp.form.submit().follow() + assert User.objects.filter(first_name='Alexa').count() == 1