From 7d1eb5207adc7898f1af4351070ca7a2076ce3d4 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Fri, 21 Jun 2013 09:51:17 +0200 Subject: [PATCH] Fixes #765. Avoids the django AbstracUser username validator Also changed added a white-space in the default username again --- openslides/participant/api.py | 35 +++++++++++-------- openslides/participant/forms.py | 29 +++++++++++++--- openslides/participant/views.py | 7 +++- tests/participant/test_umlaut_user.py | 50 +++++++++++++++++++++++++++ tests/participant/test_utils.py | 37 ++++++++++++++++++++ 5 files changed, 139 insertions(+), 19 deletions(-) create mode 100644 tests/participant/test_umlaut_user.py create mode 100644 tests/participant/test_utils.py diff --git a/openslides/participant/api.py b/openslides/participant/api.py index 9534fb26d..621ae9b44 100644 --- a/openslides/participant/api.py +++ b/openslides/participant/api.py @@ -23,7 +23,7 @@ from openslides.participant.models import User, Group def gen_password(): """ - generates a random passwort. + Generates a random passwort. """ chars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789" size = 8 @@ -33,21 +33,28 @@ def gen_password(): def gen_username(first_name, last_name): """ - generates the username for new users. + Generates a username from a first- and lastname. """ - testname = "%s%s" % (first_name.strip(), last_name.strip()) - try: - User.objects.get(username=testname) - except User.DoesNotExist: - return testname - i = 0 + first_name = first_name.strip() + last_name = last_name.strip() + + if first_name and last_name: + base_name = " ".join((first_name, last_name)) + else: + base_name = first_name or last_name + if not base_name: + raise ValueError('Either \'first_name\' or \'last_name\' can not be ' + 'empty') + + if not User.objects.filter(username=base_name).exists(): + return base_name + + counter = 0 while True: - i += 1 - testname = "%s%s%s" % (first_name, last_name, i) - try: - User.objects.get(username=testname) - except User.DoesNotExist: - return testname + counter += 1 + test_name = "%s %d" % (base_name, counter) + if not User.objects.filter(username=test_name).exists(): + return test_name def import_users(csv_file): diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index 68a273aa2..6f494b989 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -52,14 +52,23 @@ class UserUpdateForm(UserCreateForm): user edits himself and removes the last group containing the permission to manage participants. """ + user_name = forms.CharField() + """ + Field to save the username. + + The field username (without the underscore) from the UserModel does not + allow whitespaces and umlauts. + """ + class Meta: model = User - fields = ('username', 'title', 'first_name', 'last_name', 'gender', 'email', + fields = ('user_name', 'title', 'first_name', 'last_name', 'gender', 'email', 'groups', 'structure_level', 'committee', 'about_me', 'comment', 'is_active', 'default_password') def __init__(self, *args, **kwargs): self.request = kwargs.pop('request') + kwargs['initial']['user_name'] = kwargs['instance'].username return super(UserUpdateForm, self).__init__(*args, **kwargs) def clean(self, *args, **kwargs): @@ -145,15 +154,27 @@ class GroupForm(forms.ModelForm, CssClassMixin): return super(GroupForm, self).clean(*args, **kwargs) -class UsersettingsForm(forms.ModelForm, CssClassMixin): +class UsersettingsForm(CssClassMixin, forms.ModelForm): + user_name = forms.CharField() + """ + Field to save the username. + + The field username (without the underscore) from the UserModel does not + allow whitespaces and umlauts. + """ + language = forms.ChoiceField(choices=settings.LANGUAGES) + def __init__(self, *args, **kwargs): + kwargs['initial']['user_name'] = kwargs['instance'].username + return super(UsersettingsForm, self).__init__(*args, **kwargs) + class Meta: model = User - fields = ('username', 'title', 'first_name', 'last_name', 'gender', 'email', + fields = ('user_name', 'title', 'first_name', 'last_name', 'gender', 'email', 'committee', 'about_me') -class UserImportForm(forms.Form, CssClassMixin): +class UserImportForm(CssClassMixin, forms.Form): csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}), label=ugettext_lazy('CSV File')) diff --git a/openslides/participant/views.py b/openslides/participant/views.py index 972a2b929..c8e19d8cd 100644 --- a/openslides/participant/views.py +++ b/openslides/participant/views.py @@ -132,6 +132,9 @@ class UserUpdateView(UpdateView): form_kwargs.update({'request': self.request}) return form_kwargs + def manipulate_object(self, form): + self.object.username = form.cleaned_data['user_name'] + def post_save(self, form): super(UserUpdateView, self).post_save(form) # TODO: find a better solution that makes the following lines obsolete @@ -485,7 +488,9 @@ def user_settings(request): if request.method == 'POST': form_user = UsersettingsForm(request.POST, instance=request.user) if form_user.is_valid(): - form_user.save() + user = form_user.save(commit=False) + user.username = form_user.cleaned_data['user_name'] + user.save() language = request.LANGUAGE_CODE = \ request.session['django_language'] = form_user.cleaned_data['language'] activate(language) diff --git a/tests/participant/test_umlaut_user.py b/tests/participant/test_umlaut_user.py new file mode 100644 index 000000000..f65e1314d --- /dev/null +++ b/tests/participant/test_umlaut_user.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Test Umlaut User + ~~~~~~~~~~~~~~~~ + + :copyright: 2011–2013 by OpenSlides team, see AUTHORS. + :license: GNU GPL, see LICENSE for more details. +""" + +from django.test.client import Client + +from openslides.utils.test import TestCase +from openslides.config.api import config +from openslides.participant.models import User, Group + + +class TestUmlautUser(TestCase): + """ + Tests persons with umlauts in there name. + """ + + def setUp(self): + self.user = User.objects.create(username='äöü') + self.user.reset_password('äöü') + self.client = Client() + self.client.login(username='äöü', password='äöü') + + def test_login(self): + client = Client() + response = client.post('/login/', {'username': 'äöü', + 'password': 'äöüß'}) + self.assertEqual(response.status_code, 200) + + response = client.post('/login/', {'username': 'äöü', + 'password': 'äöü'}) + self.assertEqual(response.status_code, 302) + + def test_logout(self): + response = self.client.get('/logout/') + self.assertEqual(response.status_code, 302) + + def test_permission(self): + response = self.client.get('/participant/1/edit/') + self.assertEqual(response.status_code, 403) + + self.user.groups.add(Group.objects.get(pk=4)) + + response = self.client.get('/participant/1/edit/') + self.assertEqual(response.status_code, 200) diff --git a/tests/participant/test_utils.py b/tests/participant/test_utils.py new file mode 100644 index 000000000..ace52e737 --- /dev/null +++ b/tests/participant/test_utils.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Tests for models of openslides.participant + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: 2011–2013 by OpenSlides team, see AUTHORS. + :license: GNU GPL, see LICENSE for more details. +""" + +from openslides.utils.test import TestCase + +from openslides.participant.models import User +from openslides.participant.api import gen_username + + +class UserGenUsername(TestCase): + """ + Tests for the function gen_username. + """ + + def test_base(self): + self.assertEqual(gen_username('foo', 'bar'), 'foo bar') + self.assertEqual(gen_username('foo ', ' bar\n'), 'foo bar') + self.assertEqual(gen_username('foobar', ''), 'foobar') + self.assertEqual(gen_username('', 'foobar'), 'foobar') + self.assertRaises(ValueError, gen_username, '', '') + + def test_used_username(self): + User.objects.create(username='user name') + self.assertEqual(gen_username('user', 'name'), 'user name 1') + + User.objects.create(username='user name 1') + self.assertEqual(gen_username('user', 'name'), 'user name 2') + + def test_umlauts(self): + self.assertEqual(gen_username('äöü', 'ßüäö'), 'äöü ßüäö')