diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index 2c0822b1b5a..3287b847def 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -818,8 +818,15 @@ def clean_domain(self): domain_string = parsed.netloc - # Don't allow production or public domain to be set as custom domain - for invalid_domain in [settings.PRODUCTION_DOMAIN, settings.PUBLIC_DOMAIN]: + # Don't allow internal domains to be added, we have: + # - Dashboard domain + # - Public domain (from where documentation pages are served) + # - External version domain (from where PR previews are served) + for invalid_domain in [ + settings.PRODUCTION_DOMAIN, + settings.PUBLIC_DOMAIN, + settings.RTD_EXTERNAL_VERSION_DOMAIN, + ]: if invalid_domain and domain_string.endswith(invalid_domain): raise forms.ValidationError( f'{invalid_domain} is not a valid domain.' diff --git a/readthedocs/rtd_tests/tests/test_domains.py b/readthedocs/rtd_tests/tests/test_domains.py index 49c3843b16d..2af9ab831f7 100644 --- a/readthedocs/rtd_tests/tests/test_domains.py +++ b/readthedocs/rtd_tests/tests/test_domains.py @@ -92,6 +92,18 @@ def test_public_domain_not_allowed(self): f"{settings.PUBLIC_DOMAIN} is not a valid domain.", ) + @override_settings(RTD_EXTERNAL_VERSION_DOMAIN="readthedocs.build") + def test_external_domain_not_allowed(self): + for domain in ["readthedocs.build", "test.readthedocs.build"]: + form = DomainForm( + {"domain": domain}, + project=self.project, + ) + self.assertFalse(form.is_valid()) + self.assertEqual( + form.errors["domain"][0], "readthedocs.build is not a valid domain." + ) + def test_domain_with_path(self): form = DomainForm( {"domain": "domain.com/foo/bar"},