diff --git a/modoboa/core/forms.py b/modoboa/core/forms.py index 8779ec0ac..719badc5a 100644 --- a/modoboa/core/forms.py +++ b/modoboa/core/forms.py @@ -12,6 +12,9 @@ from modoboa.lib import parameters class LoginForm(forms.Form): + + """User login form.""" + username = forms.CharField( label=ugettext_lazy("Username"), widget=forms.TextInput(attrs={"class": "form-control"}) diff --git a/modoboa/core/migrations/0007_auto_20151116_1349.py b/modoboa/core/migrations/0007_auto_20151116_1349.py new file mode 100644 index 000000000..b7d45d14d --- /dev/null +++ b/modoboa/core/migrations/0007_auto_20151116_1349.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.contrib.auth.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0006_auto_20151114_1518'), + ] + + operations = [ + migrations.AlterModelManagers( + name='user', + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.AlterField( + model_name='user', + name='groups', + field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups'), + ), + migrations.AlterField( + model_name='user', + name='last_login', + field=models.DateTimeField(null=True, verbose_name='last login', blank=True), + ), + ] diff --git a/modoboa/core/models.py b/modoboa/core/models.py index 69684d9fe..2a787369e 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -59,7 +59,7 @@ class User(PermissionsMixin): ) password = models.CharField(ugettext_lazy('password'), max_length=256) last_login = models.DateTimeField( - ugettext_lazy('last login'), default=timezone.now + ugettext_lazy('last login'), blank=True, null=True ) language = models.CharField( diff --git a/modoboa/core/tests.py b/modoboa/core/tests.py index af5794290..0c27dd593 100644 --- a/modoboa/core/tests.py +++ b/modoboa/core/tests.py @@ -6,6 +6,35 @@ from modoboa.lib.tests import ModoTestCase from . import factories +class AuthenticationTestCase(ModoTestCase): + + """Validate authentication scenarios.""" + + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(AuthenticationTestCase, cls).setUpTestData() + cls.account = factories.UserFactory( + username="user@test.com", groups=('SimpleUsers',) + ) + + def test_authentication(self): + """Validate simple case.""" + self.clt.logout() + data = {"username": "user@test.com", "password": "toto"} + response = self.clt.post(reverse("core:login"), data) + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith(reverse("core:user_index"))) + + response = self.clt.post(reverse("core:logout"), {}) + self.assertEqual(response.status_code, 302) + + data = {"username": "admin", "password": "password"} + response = self.clt.post(reverse("core:login"), data) + self.assertEqual(response.status_code, 302) + self.assertTrue(response.url.endswith(reverse("admin:domain_list"))) + + class ProfileTestCase(ModoTestCase): @classmethod diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py index 2fe6a4062..ad115044f 100644 --- a/modoboa/core/views/auth.py +++ b/modoboa/core/views/auth.py @@ -23,6 +23,10 @@ def dologin(request): user = authenticate(username=form.cleaned_data["username"], password=form.cleaned_data["password"]) if user and user.is_active: + nextlocation = None + if not user.last_login: + # Redirect to profile on first login + nextlocation = reverse("core:user_index") login(request, user) if not form.cleaned_data["rememberme"]: request.session.set_expiry(0) @@ -38,12 +42,13 @@ def dologin(request): form.cleaned_data["username"], form.cleaned_data["password"]) - nextlocation = request.POST.get("next", None) - if nextlocation is None or nextlocation == "None": - if user.group == "SimpleUsers": - nextlocation = reverse("topredirection") - else: - nextlocation = reverse("admin:domain_list") + if nextlocation is None: + nextlocation = request.POST.get("next", None) + if nextlocation is None or nextlocation == "None": + if user.group == "SimpleUsers": + nextlocation = reverse("topredirection") + else: + nextlocation = reverse("admin:domain_list") return HttpResponseRedirect(nextlocation) error = _( "Your username and password didn't match. Please try again.")