Skip to content

Commit 025c02e

Browse files
authored
Merge pull request #7523 from readthedocs/fix-form-domains
Domains: more robust form
2 parents 2a519f1 + 8f10337 commit 025c02e

File tree

3 files changed

+30
-37
lines changed

3 files changed

+30
-37
lines changed

readthedocs/projects/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ def clean_project(self):
621621
return self.project
622622

623623
def clean_domain(self):
624-
domain = self.cleaned_data['domain']
624+
domain = self.cleaned_data['domain'].lower()
625625
parsed = urlparse(domain)
626626

627627
# Force the scheme to have a valid netloc.

readthedocs/projects/validators.py

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# -*- coding: utf-8 -*-
2-
31
"""Validators for projects app."""
42

53
import re
@@ -12,39 +10,13 @@
1210
from django.utils.translation import ugettext_lazy as _
1311

1412

15-
domain_regex = (
16-
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}(?<!-)\.?)|'
17-
r'localhost|' # localhost...
18-
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4
19-
r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6
20-
)
21-
22-
2313
@deconstructible
2414
class DomainNameValidator(RegexValidator):
2515
message = _('Enter a valid plain or internationalized domain name value')
26-
regex = re.compile(domain_regex, re.IGNORECASE)
27-
28-
def __init__(self, accept_idna=True, **kwargs):
29-
message = kwargs.get('message')
30-
self.accept_idna = accept_idna
31-
super().__init__(**kwargs)
32-
if not self.accept_idna and message is None:
33-
self.message = _('Enter a valid domain name value')
34-
35-
def __call__(self, value):
36-
try:
37-
super().__call__(value)
38-
except ValidationError as exc:
39-
if not self.accept_idna:
40-
raise
41-
if not value:
42-
raise
43-
try:
44-
idnavalue = value.encode('idna')
45-
except UnicodeError:
46-
raise exc
47-
super().__call__(idnavalue)
16+
# Based on the domain name pattern from https://api.cloudflare.com/#zone-list-zones.
17+
regex = re.compile(
18+
r'^([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9-]{2,20}$'
19+
)
4820

4921

5022
validate_domain_name = DomainNameValidator()

readthedocs/rtd_tests/tests/test_domains.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,34 @@ def test_domain_with_path(self):
106106
domain = form.save()
107107
self.assertEqual(domain.domain, 'domain.com')
108108

109+
def test_valid_domains(self):
110+
domains = [
111+
'python.org',
112+
'a.io',
113+
'a.e.i.o.org',
114+
'my.domain.com.edu',
115+
'my-domain.fav',
116+
]
117+
for domain in domains:
118+
form = DomainForm(
119+
{'domain': domain},
120+
project=self.project,
121+
)
122+
self.assertTrue(form.is_valid(), domain)
123+
109124
def test_invalid_domains(self):
110-
invalid = [
125+
domains = [
111126
'python..org',
112-
# FIXME: '****.foo.com', current validator says this is valid :shrug:
113-
'invalid-.com'
127+
'****.foo.com',
128+
'domain',
129+
'domain.com.',
130+
'My domain.org',
131+
'i.o',
132+
'[special].com',
133+
'some_thing.org',
134+
'invalid-.com',
114135
]
115-
for domain in invalid:
136+
for domain in domains:
116137
form = DomainForm(
117138
{'domain': domain},
118139
project=self.project,

0 commit comments

Comments
 (0)