diff options
author | Tom Usher | 2022-12-07 16:25:10 +0000 |
---|---|---|
committer | GitHub | 2022-12-07 09:25:10 -0700 |
commit | 1440ee9cebe2599239ed910b3f19cba6f79ff92c (patch) | |
tree | 495d1a309d1da46da6821bbdba70d4e4af157641 /users | |
parent | 25b8bf6a2e5cdbf921ca8ba4a994f57c1c859844 (diff) | |
download | takahe-1440ee9cebe2599239ed910b3f19cba6f79ff92c.tar.gz takahe-1440ee9cebe2599239ed910b3f19cba6f79ff92c.tar.bz2 takahe-1440ee9cebe2599239ed910b3f19cba6f79ff92c.zip |
Support deeper subdomains in domain validation (#110)
Use a new validator class with regex based on the URLValidator from Django
Diffstat (limited to 'users')
-rw-r--r-- | users/views/admin/domains.py | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/users/views/admin/domains.py b/users/views/admin/domains.py index c42137c..ed4d184 100644 --- a/users/views/admin/domains.py +++ b/users/views/admin/domains.py @@ -1,6 +1,5 @@ -import re - from django import forms +from django.core.validators import RegexValidator from django.db import models from django.shortcuts import get_object_or_404, redirect from django.utils.decorators import method_decorator @@ -10,6 +9,27 @@ from users.decorators import admin_required from users.models import Domain +class DomainValidator(RegexValidator): + ul = "\u00a1-\uffff" # Unicode letters range (must not be a raw string). + + # Host patterns + hostname_re = ( + r"[a-z" + ul + r"0-9](?:[a-z" + ul + r"0-9-]{0,61}[a-z" + ul + r"0-9])?" + ) + # Max length for domain name labels is 63 characters per RFC 1034 sec. 3.1 + domain_re = r"(?:\.(?!-)[a-z" + ul + r"0-9-]{1,63}(?<!-))*" + tld_re = ( + r"\." # dot + r"(?!-)" # can't start with a dash + r"(?:[a-z" + ul + "-]{2,63}" # domain label + r"|xn--[a-z0-9]{1,59})" # or punycode label + r"(?<!-)" # can't end with a dash + r"\.?" # may have a trailing dot + ) + regex = "^" + hostname_re + domain_re + tld_re + "$" + message = "This does not look like a domain name" + + @method_decorator(admin_required, name="dispatch") class Domains(TemplateView): @@ -31,10 +51,12 @@ class DomainCreate(FormView): class form_class(forms.Form): domain = forms.CharField( help_text="The domain displayed as part of a user's identity.\nCannot be changed after the domain has been created.", + validators=[DomainValidator()], ) service_domain = forms.CharField( help_text="Optional - a domain that serves Takahē if it is not running on the main domain.\nCannot be changed after the domain has been created.", required=False, + validators=[DomainValidator()], ) public = forms.BooleanField( help_text="If any user on this server can create identities here", @@ -47,13 +69,7 @@ class DomainCreate(FormView): required=False, ) - domain_regex = re.compile( - r"^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})$" - ) - def clean_domain(self): - if not self.domain_regex.match(self.cleaned_data["domain"]): - raise forms.ValidationError("This does not look like a domain name") if Domain.objects.filter( models.Q(domain=self.cleaned_data["domain"]) | models.Q(service_domain=self.cleaned_data["domain"]) @@ -64,8 +80,6 @@ class DomainCreate(FormView): def clean_service_domain(self): if not self.cleaned_data["service_domain"]: return None - if not self.domain_regex.match(self.cleaned_data["service_domain"]): - raise forms.ValidationError("This does not look like a domain name") if Domain.objects.filter( models.Q(domain=self.cleaned_data["service_domain"]) | models.Q(service_domain=self.cleaned_data["service_domain"]) |