Source code for ferrosoft.apps.ferrobase.views.password
# Copyright (c) 2025 Ferrosoft GmbH. All rights reserved.
import logging
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.urls import reverse
from ferrosoft.apps.ferrobase.models import User, PasswordResetRequest
from ferrosoft.apps.ferrobase.session.forms import ResetPasswordForm, SetNewPasswordForm
from ferrosoft.apps.ferrobase.session.password_reset import send_password_reset_email
[docs]
def reset_password(request):
"""Begin the password-reset flow.
On POST the function always reports ``sent_mail = True`` regardless of
whether the user exists, which prevents user enumeration through the
public form. Mail-sending exceptions are logged but never surfaced to
the caller.
"""
sent_mail = False
if request.method == "POST":
# Set this regardless whether mail was actually sent as to not
# reveal any information about existing users.
sent_mail = True
form = ResetPasswordForm(request.POST)
if not form.is_valid():
return HttpResponse(status=400)
user = User.objects.filter(username=form.cleaned_data["username"]).first()
if user:
reset_request = PasswordResetRequest.create(user)
try:
send_password_reset_email(reset_request, request)
except Exception as e:
logging.error("could not send password reset email: %s" % e)
return render(
request,
"ferrobase/session/reset-password.html",
context={
"sent_mail": sent_mail,
},
)
[docs]
def set_new_password(request, access_key):
"""Complete the password-reset flow for a token-bearing user.
Validates the token, applies the new password (both confirmation
fields must match — enforced by :meth:`set_password_validated`),
persists the user record and deletes the consumed reset request.
Redirects to the login page with ``?reset=1`` on success.
"""
reset_request = PasswordResetRequest.get_if_not_expired(access_key)
if not reset_request:
return render(request, "ferrobase/session/reset-expired.html")
failure = False
if request.method == "POST":
form = SetNewPasswordForm(request.POST)
if not form.is_valid():
return HttpResponse(status=400)
try:
reset_request.user.set_password_validated(
form.cleaned_data["password"],
form.cleaned_data["password_again"],
)
reset_request.user.save()
reset_request.delete()
return redirect(reverse("ferrobase:login") + "?reset=1")
except Exception as e:
failure = str(e)
return render(
request,
"ferrobase/session/set-new-password.html",
context={
"reset_request": reset_request,
"min_chars": User.MINIMUM_PASSWORD_LENGTH,
"failure": failure,
},
)