diff --git a/modoboa/core/tests/test_authentication.py b/modoboa/core/tests/test_authentication.py index 020f3015b..f6826f551 100644 --- a/modoboa/core/tests/test_authentication.py +++ b/modoboa/core/tests/test_authentication.py @@ -12,6 +12,7 @@ from django.test import override_settings from django.urls import reverse from modoboa.lib.tests import NO_SMTP, ModoTestCase +from modoboa.core.password_hashers import get_password_hasher from .. import factories, models try: @@ -67,6 +68,33 @@ class AuthenticationTestCase(ModoTestCase): self.assertEqual(response.status_code, 302) self.assertTrue(response.url.endswith(reverse("core:dashboard"))) + def test_password_scheme(self): + """Validate password scheme changes.""" + url = reverse("core:parameters") + username = "user@test.com" + password = "toto" + data = {"username": username, "password": password} + user = models.User.objects.get(username=username) + pw_hash = get_password_hasher('fallback_scheme')() + + self.client.logout() + self.set_global_parameter("password_scheme", "sha512crypt") + response = self.client.post(reverse("core:login"), data) + user.refresh_from_db() + self.assertTrue(user.password.startswith("{SHA512-CRYPT}")) + + self.client.logout() + self.set_global_parameter("password_scheme", "sha256") + response = self.client.post(reverse("core:login"), data) + user.refresh_from_db() + self.assertTrue(user.password.startswith("{SHA256}")) + + self.client.logout() + self.set_global_parameter("password_scheme", "fallback_scheme") + response = self.client.post(reverse("core:login"), data) + user.refresh_from_db() + self.assertTrue(user.password.startswith(pw_hash.scheme)) + class PasswordResetTestCase(ModoTestCase): """Test password reset service.""" diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py index 8bcac9731..0280f1652 100644 --- a/modoboa/core/views/auth.py +++ b/modoboa/core/views/auth.py @@ -17,6 +17,8 @@ from django.utils.translation import ugettext as _ from django.views.decorators.cache import never_cache from modoboa.core import forms +from modoboa.core.password_hashers import get_password_hasher +from modoboa.parameters import tools as param_tools from .. import signals from .base import find_nextlocation @@ -33,6 +35,21 @@ def dologin(request): user = authenticate(username=form.cleaned_data["username"], password=form.cleaned_data["password"]) if user and user.is_active: + # check if password scheme is correct + scheme = param_tools.get_global_parameter("password_scheme", + raise_exception=False) + # use SHA512CRYPT as default fallback + if scheme is None: + pwhash = get_password_hasher('sha512crypt')() + else: + pwhash = get_password_hasher(scheme)() + if not user.password.startswith(pwhash.scheme): + logging.info( + _("Password scheme mismatch. Updating %s password") % user.username + ) + user.set_password(form.cleaned_data["password"]) + user.save() + login(request, user) if not form.cleaned_data["rememberme"]: request.session.set_expiry(0)