Enhances registration workflow and error handling

Introduces a new registration status 'Started' with adjustments
to default status handling. Adds a missing check in email
verification to prevent unauthorized users from accessing
the registration process. Updates forbidden response status
codes to improve security and debugging clarity.

Corrects unintended user modification warning logic and
ensures registration session keys are cleared post-creation
to prevent potential data leaks.

Improves code style consistency and readability across forms.
This commit is contained in:
Kumi 2024-11-16 20:58:23 +01:00
parent 61555f9e34
commit 15ccc877ae
Signed by: kumi
GPG key ID: ECBCC9082395383F
3 changed files with 35 additions and 13 deletions

View file

@ -15,7 +15,7 @@ class UsernameForm(forms.Form):
username = cleaned_data.get("username")
if username.startswith("@") and username.endswith(f":{settings.MATRIX_DOMAIN}"):
username = username[1:-len(f":{settings.MATRIX_DOMAIN}")]
username = username[1 : -len(f":{settings.MATRIX_DOMAIN}")]
if not username:
self.add_error("username", "Username cannot be empty.")
@ -45,13 +45,13 @@ class RegistrationForm(forms.Form):
label="Password",
widget=forms.PasswordInput(
attrs={"class": "input", "placeholder": "Enter password"}
)
),
)
password2 = forms.CharField(
label="Confirm password",
widget=forms.PasswordInput(
attrs={"class": "input", "placeholder": "Re-enter password"}
)
),
)
registration_reason = forms.CharField(
min_length=30,
@ -60,7 +60,7 @@ class RegistrationForm(forms.Form):
"class": "textarea",
"placeholder": "Why do you want to join our server? If you were referred by a current member, who referred you? If you found us through a different means, how did you find us?",
}
)
),
)
def clean(self):

View file

@ -1,23 +1,26 @@
from django.db import models
class UserRegistration(models.Model):
# Status constants
STATUS_STARTED = 0
STATUS_REQUESTED = 1
STATUS_APPROVED = 2
STATUS_DENIED = 3
# Status choices
STATUS_CHOICES = [
(STATUS_REQUESTED, 'Requested'),
(STATUS_APPROVED, 'Approved'),
(STATUS_DENIED, 'Denied'),
(STATUS_STARTED, "Started"),
(STATUS_REQUESTED, "Requested"),
(STATUS_APPROVED, "Approved"),
(STATUS_DENIED, "Denied"),
]
username = models.CharField(max_length=150)
email = models.EmailField()
registration_reason = models.TextField()
ip_address = models.GenericIPAddressField()
status = models.IntegerField(choices=STATUS_CHOICES, default=STATUS_REQUESTED)
status = models.IntegerField(choices=STATUS_CHOICES, default=STATUS_STARTED)
token = models.CharField(max_length=64, unique=True)
email_verified = models.BooleanField(default=False)

View file

@ -79,9 +79,11 @@ class EmailInputView(FormView):
class VerifyEmailView(View):
def get(self, request, token):
registration = get_object_or_404(UserRegistration, token=token)
if registration.status != UserRegistration.STATUS_STARTED:
return render(request, "registration/registration_forbidden.html", status=403)
request.session["registration"] = registration.id
if registration.email_verified:
return render(request, "registration/already_verified.html")
registration.email_verified = True
registration.save()
return redirect("complete_registration")
@ -107,7 +109,7 @@ class CompleteRegistrationView(FormView):
)
if not response.json().get("available"):
return render(self.request, "registration/registration_forbidden.html")
return render(self.request, "registration/registration_forbidden.html", status=403)
response = requests.put(
f"{settings.SYNAPSE_SERVER}/_synapse/admin/v2/users/@{username}:{settings.MATRIX_DOMAIN}",
@ -120,7 +122,18 @@ class CompleteRegistrationView(FormView):
headers={"Authorization": f"Bearer {settings.SYNAPSE_ADMIN_TOKEN}"},
)
if response.status_code in (200, 201):
if response.status_code == 200:
# Oops. This should never happen. It means that an existing user was altered.
send_mail(
"Critical Registration Error",
f"Something went horribly wrong. The existing user {username} was altered. Please investigate.",
settings.DEFAULT_FROM_EMAIL,
[settings.ADMIN_EMAIL],
)
return render(self.request, "registration/registration_forbidden.html", status=403)
if response.status_code == 201:
# The "locked" field doesn't seem to work when creating a user, so we need to lock the user after creation
response = requests.put(
f"{settings.SYNAPSE_SERVER}/_synapse/admin/v2/users/@{username}:{settings.MATRIX_DOMAIN}",
@ -145,6 +158,12 @@ class CompleteRegistrationView(FormView):
registration.registration_reason = registration_reason
registration.save()
try:
self.request.session.pop("registration")
self.request.session.pop("username")
except KeyError:
pass
send_mail(
"New Registration Request",
f"Approve the new user {username}",
@ -165,5 +184,5 @@ class CompleteRegistrationView(FormView):
self.registration.status != UserRegistration.STATUS_REQUESTED
or not self.registration.email_verified
):
return render(request, "registration/registration_forbidden.html")
return render(request, "registration/registration_forbidden.html", status=403)
return super().dispatch(request, *args, **kwargs)