diff --git a/src/pretix/base/forms/user.py b/src/pretix/base/forms/user.py index ee93e8a523..9cecbe05e9 100644 --- a/src/pretix/base/forms/user.py +++ b/src/pretix/base/forms/user.py @@ -55,6 +55,7 @@ class UserSettingsForm(forms.ModelForm): 'pw_current_wrong': _("The current password you entered was not correct."), 'pw_mismatch': _("Please enter the same password twice"), 'rate_limit': _("For security reasons, please wait 5 minutes before you try again."), + 'pw_equal': _("Please choose a password different to your current one.") } old_pw = forms.CharField(max_length=255, @@ -158,6 +159,12 @@ class UserSettingsForm(forms.ModelForm): code='pw_current' ) + if password1 and password1 == old_pw: + raise forms.ValidationError( + self.error_messages['pw_equal'], + code='pw_equal' + ) + if password1: self.instance.set_password(password1) diff --git a/src/pretix/base/migrations/0202_user_needs_password_change.py b/src/pretix/base/migrations/0202_user_needs_password_change.py new file mode 100644 index 0000000000..164ea0f7f2 --- /dev/null +++ b/src/pretix/base/migrations/0202_user_needs_password_change.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2021-11-04 13:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0201_invoiceline_event_location'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='needs_password_change', + field=models.BooleanField(default=False), + ), + ] diff --git a/src/pretix/base/models/auth.py b/src/pretix/base/models/auth.py index 1c126df228..10f7a84d34 100644 --- a/src/pretix/base/models/auth.py +++ b/src/pretix/base/models/auth.py @@ -113,6 +113,8 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin): :type date_joined: datetime :param locale: The user's preferred locale code. :type locale: str + :param needs_password_change: Whether this user's password needs to be changed. + :type needs_password_change: bool :param timezone: The user's preferred timezone. :type timezone: str """ @@ -130,6 +132,8 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin): verbose_name=_('Is site admin')) date_joined = models.DateTimeField(auto_now_add=True, verbose_name=_('Date joined')) + needs_password_change = models.BooleanField(default=False, + verbose_name=_('Force user to select a new password')) locale = models.CharField(max_length=50, choices=settings.LANGUAGES, default=settings.LANGUAGE_CODE, diff --git a/src/pretix/control/forms/users.py b/src/pretix/control/forms/users.py index 6e133d3327..f1cf45e3b9 100644 --- a/src/pretix/control/forms/users.py +++ b/src/pretix/control/forms/users.py @@ -70,6 +70,7 @@ class UserEditForm(forms.ModelForm): 'require_2fa', 'is_active', 'is_staff', + 'needs_password_change', 'last_login' ] diff --git a/src/pretix/control/middleware.py b/src/pretix/control/middleware.py index cc30e3faea..7cdcda66cd 100644 --- a/src/pretix/control/middleware.py +++ b/src/pretix/control/middleware.py @@ -69,6 +69,11 @@ class PermissionMiddleware: "user.settings.notifications.off", ) + EXCEPTIONS_FORCED_PW_CHANGE = ( + "user.settings", + "auth.logout" + ) + EXCEPTIONS_2FA = ( "user.settings.2fa", "user.settings.2fa.add", @@ -130,6 +135,9 @@ class PermissionMiddleware: if url_name not in ('user.reauth', 'auth.logout'): return redirect(reverse('control:user.reauth') + '?next=' + quote(request.get_full_path())) + if request.user.needs_password_change and url_name not in self.EXCEPTIONS_FORCED_PW_CHANGE: + return redirect(reverse('control:user.settings') + '?next=' + quote(request.get_full_path())) + if not request.user.require_2fa and settings.PRETIX_OBLIGATORY_2FA \ and url_name not in self.EXCEPTIONS_2FA: return redirect(reverse('control:user.settings.2fa')) diff --git a/src/pretix/control/templates/pretixcontrol/base.html b/src/pretix/control/templates/pretixcontrol/base.html index 36413577b3..d0b1f621e2 100644 --- a/src/pretix/control/templates/pretixcontrol/base.html +++ b/src/pretix/control/templates/pretixcontrol/base.html @@ -429,6 +429,15 @@ {% endif %} + {% if request.user.needs_password_change %} +
+ {% blocktrans trimmed %} + For security reasons, please change your password before you continue. Afterwards you + will be redirected to your original destination. + {% endblocktrans %} +
+ {% endif %} + {% block content %} {% endblock %}