mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
feat(admin): a password an administrator sets can be one the user must change
The administrator's password form has "The user must change it at their next login". With it, the user is sent to the change form until they choose their own, as the generated admin password already does.
This commit is contained in:
parent
0e8130f1b0
commit
24896aaecd
4 changed files with 33 additions and 7 deletions
|
|
@ -7,7 +7,8 @@ ALLOWED_VIEWS = {"accounts:change_password", "accounts:logout", "accounts:login"
|
|||
|
||||
|
||||
class ForcePasswordChangeMiddleware:
|
||||
"""Send users who still have the generated admin password to the change form."""
|
||||
"""Send users who must change their password (the generated admin one, or one
|
||||
an administrator set) to the change form."""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from django import forms
|
||||
from django.contrib.auth import password_validation
|
||||
from django.contrib.auth.forms import AdminPasswordChangeForm
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
|
@ -123,3 +124,12 @@ class UserAttributesForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = UserAttributes
|
||||
exclude = ["user", "can_clone_instances", "must_change_password"]
|
||||
|
||||
|
||||
class AdminPasswordForm(AdminPasswordChangeForm):
|
||||
"""A password an administrator sets; the user may have to change it at their next login."""
|
||||
must_change_password = forms.BooleanField(label=_("The user must change it at their next login"), required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["must_change_password"] = self.fields.pop("must_change_password") # last, after Django's own
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"""The Users pages: an administrator cannot lock their own account out, and
|
||||
the user form says what its fields mean."""
|
||||
"""The Users pages: an administrator cannot lock their own account out, the
|
||||
user form says what its fields mean, and a password set by an administrator
|
||||
can be one the user must change."""
|
||||
|
||||
from accounts.models import UserAttributes
|
||||
from django.contrib.auth import get_user_model
|
||||
|
|
@ -65,3 +66,16 @@ class UserPagesTests(TestCase):
|
|||
page = html.fromstring(self.client.get(reverse("admin:user_list")).content)
|
||||
(row,) = [r for r in page.xpath("//tbody/tr") if r.xpath("td")[0].text_content().strip() == "up-alice"]
|
||||
self.assertEqual(row.xpath("td")[2].text_content().strip(), "never")
|
||||
|
||||
def test_a_password_set_by_an_administrator_can_be_one_to_change(self):
|
||||
url = reverse("admin:user_update_password", args=[self.alice.pk])
|
||||
self.client.post(url, {"password1": "New-pw-4567x", "password2": "New-pw-4567x", "must_change_password": "on"})
|
||||
self.assertTrue(UserAttributes.objects.get(user=self.alice).must_change_password)
|
||||
# the user is sent to the change form until they choose their own
|
||||
self.alice.refresh_from_db() # the new password's hash, or the session is not hers
|
||||
self.client.force_login(self.alice)
|
||||
self.assertRedirects(self.client.get(reverse("instances:index")), reverse("accounts:change_password"),
|
||||
fetch_redirect_response=False)
|
||||
self.client.force_login(self.admin)
|
||||
self.client.post(url, {"password1": "New-pw-4567y", "password2": "New-pw-4567y"})
|
||||
self.assertFalse(UserAttributes.objects.get(user=self.alice).must_change_password)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from appsettings.settings import app_settings
|
|||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
from django.contrib.auth.forms import AdminPasswordChangeForm
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.core.paginator import Paginator
|
||||
from django.db import transaction
|
||||
|
|
@ -185,10 +184,12 @@ def user_update(request, pk):
|
|||
def user_update_password(request, pk):
|
||||
user = get_object_or_404(User, pk=pk)
|
||||
if request.method == "POST":
|
||||
form = AdminPasswordChangeForm(user, request.POST)
|
||||
form = forms.AdminPasswordForm(user, request.POST)
|
||||
if form.is_valid():
|
||||
with audit(request, None, f"User {user.username}", "set password"):
|
||||
with audit(request, None, f"User {user.username}", "set password"), transaction.atomic():
|
||||
user = form.save()
|
||||
UserAttributes.objects.update_or_create(
|
||||
user=user, defaults={"must_change_password": form.cleaned_data["must_change_password"]})
|
||||
update_session_auth_hash(request, user) # Important!
|
||||
messages.success(
|
||||
request, _("Password changed for %(user)s") % {"user": user.username}
|
||||
|
|
@ -197,7 +198,7 @@ def user_update_password(request, pk):
|
|||
else:
|
||||
messages.error(request, _("Wrong Data Provided"))
|
||||
else:
|
||||
form = AdminPasswordChangeForm(user)
|
||||
form = forms.AdminPasswordForm(user)
|
||||
|
||||
return render(
|
||||
request,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue