Skip to content

Commit a002dd4

Browse files
committed
fix(controller): improve domain name validation per RFC 1123
1 parent f281f8b commit a002dd4

2 files changed

Lines changed: 54 additions & 45 deletions

File tree

controller/api/serializers.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -239,23 +239,22 @@ def validate_domain(self, value):
239239
"""
240240
Check that the hostname is valid
241241
"""
242-
match = re.match(
243-
r'^(\*\.)?(' + settings.APP_URL_REGEX + r'\.)*([a-z0-9-]+)\.([a-z0-9]{2,})$',
244-
value)
245-
if not match:
242+
if len(value) > 255:
243+
raise serializers.ValidationError('Hostname must be 255 characters or less.')
244+
if value[-1:] == ".":
245+
value = value[:-1] # strip exactly one dot from the right, if present
246+
labels = value.split('.')
247+
if labels[0] == '*':
246248
raise serializers.ValidationError(
247-
"Hostname does not look like a valid hostname. "
248-
"Only lowercase characters are allowed.")
249-
249+
'Adding a wildcard subdomain is currently not supported.')
250+
allowed = re.compile("^(?!-)[a-z0-9-]{1,63}(?<!-)$", re.IGNORECASE)
251+
for label in labels:
252+
match = allowed.match(label)
253+
if not match or '--' in label or label[-1].isdigit() or label.isdigit():
254+
raise serializers.ValidationError('Hostname does not look valid.')
250255
if models.Domain.objects.filter(domain=value).exists():
251256
raise serializers.ValidationError(
252257
"The domain {} is already in use by another app".format(value))
253-
254-
domain_parts = value.split('.')
255-
if domain_parts[0] == '*':
256-
raise serializers.ValidationError(
257-
"Adding a wildcard subdomain is currently not supported".format(value))
258-
259258
return value
260259

261260

controller/api/tests/test_domain.py

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,34 @@ def test_response_data(self):
4848

4949
def test_manage_domain(self):
5050
url = '/v1/apps/{app_id}/domains'.format(app_id=self.app_id)
51-
body = {'domain': 'test-domain.example.com'}
52-
response = self.client.post(url, json.dumps(body), content_type='application/json',
53-
HTTP_AUTHORIZATION='token {}'.format(self.token))
54-
self.assertEqual(response.status_code, 201)
55-
url = '/v1/apps/{app_id}/domains'.format(app_id=self.app_id)
56-
response = self.client.get(url, content_type='application/json',
57-
HTTP_AUTHORIZATION='token {}'.format(self.token))
58-
result = response.data['results'][0]
59-
self.assertEqual('test-domain.example.com', result['domain'])
60-
url = '/v1/apps/{app_id}/domains/{hostname}'.format(hostname='test-domain.example.com',
61-
app_id=self.app_id)
62-
response = self.client.delete(url, content_type='application/json',
63-
HTTP_AUTHORIZATION='token {}'.format(self.token))
64-
self.assertEqual(response.status_code, 204)
65-
url = '/v1/apps/{app_id}/domains'.format(app_id=self.app_id)
66-
response = self.client.get(url, content_type='application/json',
67-
HTTP_AUTHORIZATION='token {}'.format(self.token))
68-
self.assertEqual(0, response.data['count'])
51+
test_domains = [
52+
'test-domain.example.com',
53+
'django.paas-sandbox',
54+
'domain',
55+
'not.too.loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong',
56+
'3com.com',
57+
'MYDOMAIN.NET',
58+
]
59+
for domain in test_domains:
60+
body = {'domain': domain}
61+
msg = "failed on \"{}\"".format(domain)
62+
response = self.client.post(url, json.dumps(body), content_type='application/json',
63+
HTTP_AUTHORIZATION='token {}'.format(self.token))
64+
self.assertEqual(response.status_code, 201, msg)
65+
url = '/v1/apps/{app_id}/domains'.format(app_id=self.app_id)
66+
response = self.client.get(url, content_type='application/json',
67+
HTTP_AUTHORIZATION='token {}'.format(self.token))
68+
result = response.data['results'][0]
69+
self.assertEqual(domain, result['domain'], msg)
70+
url = '/v1/apps/{app_id}/domains/{hostname}'.format(hostname=domain,
71+
app_id=self.app_id)
72+
response = self.client.delete(url, content_type='application/json',
73+
HTTP_AUTHORIZATION='token {}'.format(self.token))
74+
self.assertEqual(response.status_code, 204, msg)
75+
url = '/v1/apps/{app_id}/domains'.format(app_id=self.app_id)
76+
response = self.client.get(url, content_type='application/json',
77+
HTTP_AUTHORIZATION='token {}'.format(self.token))
78+
self.assertEqual(0, response.data['count'], msg)
6979

7080
def test_manage_domain_invalid_app(self):
7181
url = '/v1/apps/{app_id}/domains'.format(app_id="this-app-does-not-exist")
@@ -80,20 +90,20 @@ def test_manage_domain_invalid_app(self):
8090

8191
def test_manage_domain_invalid_domain(self):
8292
url = '/v1/apps/{app_id}/domains'.format(app_id=self.app_id)
83-
body = {'domain': 'this_is_an.invalid.domain'}
84-
response = self.client.post(url, json.dumps(body), content_type='application/json',
85-
HTTP_AUTHORIZATION='token {}'.format(self.token))
86-
self.assertEqual(response.status_code, 400)
87-
url = '/v1/apps/{app_id}/domains'.format(app_id=self.app_id)
88-
body = {'domain': 'this-is-an.invalid.a'}
89-
response = self.client.post(url, json.dumps(body), content_type='application/json',
90-
HTTP_AUTHORIZATION='token {}'.format(self.token))
91-
self.assertEqual(response.status_code, 400)
92-
url = '/v1/apps/{app_id}/domains'.format(app_id=self.app_id)
93-
body = {'domain': 'domain'}
94-
response = self.client.post(url, json.dumps(body), content_type='application/json',
95-
HTTP_AUTHORIZATION='token {}'.format(self.token))
96-
self.assertEqual(response.status_code, 400)
93+
test_domains = [
94+
'this_is_an.invalid.domain',
95+
'this-is-an.invalid.1',
96+
'django.pass--sandbox',
97+
'domain1',
98+
'3333.com',
99+
'too.looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong',
100+
]
101+
for domain in test_domains:
102+
msg = "failed on \"{}\"".format(domain)
103+
body = {'domain': domain}
104+
response = self.client.post(url, json.dumps(body), content_type='application/json',
105+
HTTP_AUTHORIZATION='token {}'.format(self.token))
106+
self.assertEqual(response.status_code, 400, msg)
97107

98108
def test_manage_domain_wildcard(self):
99109
"""Wildcards are not allowed for now."""

0 commit comments

Comments
 (0)