feat: add e-mail change with confirmation and undo
Page at /email asks for the new address and the current password (shared failure counter with the password page, five requests per hour) and mails a one-hour confirmation link to the new address. Confirming writes the address and mails the old address a 24-hour undo link; undoing restores it, ends every session of the user and notifies the Admin (ADMIN_NOTIFY_EMAIL). Link pages show on GET and apply on POST; tokens are stored only as hashes, single use, and are blanked from every log line. New table owned by profile, German text, tests; review findings logged in the plan. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FocPJ75XPma7TcuYEMy8zm
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
3fd8f27127
commit
94c1d39c9d
@@ -5,6 +5,8 @@ from django import forms
|
||||
from django.utils.translation import gettext
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from portal.mailer.address import is_valid_address
|
||||
|
||||
MAX_PASSWORD_LENGTH = 256 # Active Directory's limit
|
||||
|
||||
|
||||
@@ -163,3 +165,27 @@ class DetailsForm(forms.Form):
|
||||
def apply_errors(self, errors: dict[str, str]) -> None:
|
||||
for name, message in errors.items():
|
||||
self.add_error(name, message)
|
||||
|
||||
|
||||
# -- e-mail change ----------------------------------------------------------------------------
|
||||
|
||||
MAX_ADDRESS_LENGTH = 254
|
||||
|
||||
|
||||
class EmailChangeForm(forms.Form):
|
||||
new_email = forms.CharField(
|
||||
strip=True,
|
||||
required=False, # the messages below say it better than the default
|
||||
widget=forms.EmailInput(),
|
||||
)
|
||||
current_password = _password_field()
|
||||
|
||||
def clean_new_email(self):
|
||||
value = self.cleaned_data["new_email"]
|
||||
if not value:
|
||||
raise forms.ValidationError(_("Please fill in this field."))
|
||||
if len(value) > MAX_ADDRESS_LENGTH:
|
||||
raise forms.ValidationError(_("This e-mail address is too long."))
|
||||
if not is_valid_address(value):
|
||||
raise forms.ValidationError(_("Please enter a valid e-mail address."))
|
||||
return value
|
||||
|
||||
Reference in New Issue
Block a user