1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 03:36:48 +00:00

fix(admin): a new user's password reaches the database only hashed

The admin page that creates a user saved the form first, which wrote
the user with the raw password in the password column, and hashed it
with a second save afterwards. The raw password was in the database,
and in its journal and any copy taken meanwhile, and stayed there if
anything failed between the two writes.

- UserCreateForm hashes the password before the first save; groups
  and permissions are saved as before.
- The form applies AUTH_PASSWORD_VALIDATORS, after the user fields are
  set (so the similarity check sees the new username), as the password
  change forms already did. The settings template configures no
  validators, so nothing changes unless an installation sets them.

The tests record the SQL parameters of the request: the previous code
wrote the raw password in the INSERT; it also accepted passwords that
configured validators reject.
This commit is contained in:
catborise 2026-10-05 18:39:33 +03:00
parent 731d14efd7
commit 405e6f1445
3 changed files with 88 additions and 3 deletions

View file

@ -1,6 +1,8 @@
from django import forms
from django.contrib.auth import password_validation
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.models import Group, User
from django.core.exceptions import ValidationError
from django.urls import reverse_lazy
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
@ -102,6 +104,27 @@ class UserCreateForm(UserForm):
content_type__app_label="accounts", codename="change_password"
)
def _post_clean(self):
# The validators in AUTH_PASSWORD_VALIDATORS, as on the password change
# forms; after the instance has the form's values (username, email).
super()._post_clean()
password = self.cleaned_data.get("password")
if password:
try:
password_validation.validate_password(password, self.instance)
except ValidationError as error:
self.add_error("password", error)
def save(self, commit=True):
# hashed before the user is first written: the raw password never
# reaches the database
user = super().save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
self.save_m2m()
return user
class Meta:
model = User
fields = [