Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force resolver to use PUBLIC_DOMAIN over HTTPS if not Domain.https #4579

Merged
merged 7 commits into from
Sep 5, 2018
38 changes: 26 additions & 12 deletions readthedocs/core/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def resolve_domain(self, project, private=None):
return self._get_project_subdomain(canonical_project)
return getattr(settings, 'PRODUCTION_DOMAIN')

def resolve(self, project, protocol='http', filename='', private=None,
def resolve(self, project, require_https=False, filename='', private=None,
**kwargs):
if private is None:
version_slug = kwargs.get('version_slug')
Expand All @@ -146,24 +146,27 @@ def resolve(self, project, protocol='http', filename='', private=None,

canonical_project = self._get_canonical_project(project)
custom_domain = self._get_project_custom_domain(canonical_project)
use_custom_domain = self._use_custom_domain(custom_domain)

# This duplication from resolve_domain is for performance purposes
# in order to check whether a custom domain should be HTTPS
if custom_domain:
if use_custom_domain:
domain = custom_domain.domain
elif self._use_subdomain():
domain = self._get_project_subdomain(canonical_project)
else:
domain = getattr(settings, 'PRODUCTION_DOMAIN')

if custom_domain:
protocol = 'https' if custom_domain.https else 'http'
else:
# Use HTTPS if settings specify
public_domain = getattr(settings, 'PUBLIC_DOMAIN', None)
use_https = getattr(settings, 'PUBLIC_DOMAIN_USES_HTTPS', False)
if use_https and public_domain and public_domain in domain:
protocol = 'https'
public_domain = getattr(settings, 'PUBLIC_DOMAIN', None)
use_https = getattr(settings, 'PUBLIC_DOMAIN_USES_HTTPS', False)

use_https_protocol = any([
# Rely on the ``Domain.https`` field
use_custom_domain and custom_domain.https,
# or force it if specified
require_https,
# or fallback to settings
use_https and public_domain and public_domain in domain,
])
protocol = 'https' if use_https_protocol else 'http'

return '{protocol}://{domain}{path}'.format(
protocol=protocol,
Expand Down Expand Up @@ -249,6 +252,17 @@ def _fix_filename(self, project, filename):
path = ""
return path

def _use_custom_domain(self, custom_domain):
"""
Make decision about whether to use a custom domain to serve docs.

Always use the custom domain if it exists.

:param custom_domain: Domain instance or ``None``
:type custom_domain: readthedocs.projects.models.Domain
"""
return True if custom_domain is not None else False

def _use_subdomain(self):
"""Make decision about whether to use a subdomain to serve docs."""
use_subdomain = getattr(settings, 'USE_SUBDOMAIN', False)
Expand Down
69 changes: 30 additions & 39 deletions readthedocs/rtd_tests/tests/test_project_symlinks.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,26 @@ def get_filesystem(path, top_level_path=None):
return fs


class TempSiterootCase(object):
TEMP_SITE_ROOT = tempfile.mkdtemp(suffix='siteroot')
TEMP_DOCROOT = os.path.join(TEMP_SITE_ROOT, 'user_builds')

"""Override SITE_ROOT and patch necessary pieces to inspect symlink structure

@override_settings(
SITE_ROOT=TEMP_SITE_ROOT,
DOCROOT=TEMP_DOCROOT,
)
class TempSiteRootTestCase(TestCase):

"""
Override SITE_ROOT and patch necessary pieces to inspect symlink structure.

This uses some patches and overidden settings to build out symlinking in a
temporary path. Each test is therefore isolated, and cleanup will remove
these paths after the test case wraps up.

And subclasses that implement :py:class:`TestCase` should also make use of
:py:func:`override_settings`.
"""

def setUp(self):
self.maxDiff = None
self.site_root = os.path.realpath(tempfile.mkdtemp(suffix='siteroot'))
settings.SITE_ROOT = self.site_root
settings.DOCROOT = os.path.join(settings.SITE_ROOT, 'user_builds')
self.mocks = {
'PublicSymlinkBase.CNAME_ROOT': mock.patch(
'readthedocs.core.symlink.PublicSymlinkBase.CNAME_ROOT',
Expand Down Expand Up @@ -115,16 +118,16 @@ def setUp(self):
)

def tearDown(self):
shutil.rmtree(self.site_root)
shutil.rmtree(settings.SITE_ROOT)

def assertFilesystem(self, filesystem):
"""
Creates a nested dictionary that represents the folder structure of rootdir
"""
self.assertEqual(filesystem, get_filesystem(self.site_root))
self.assertEqual(filesystem, get_filesystem(settings.SITE_ROOT))


class BaseSymlinkCnames(TempSiterootCase):
class BaseSymlinkCnames(object):

def setUp(self):
super(BaseSymlinkCnames, self).setUp()
Expand Down Expand Up @@ -290,19 +293,17 @@ def test_symlink_cname_dont_link_missing_domains(self):
self.assertFilesystem(filesystem)


@override_settings()
class TestPublicSymlinkCnames(BaseSymlinkCnames, TestCase):
class TestPublicSymlinkCnames(BaseSymlinkCnames, TempSiteRootTestCase):
privacy = 'public'
symlink_class = PublicSymlink


@override_settings()
class TestPrivateSymlinkCnames(BaseSymlinkCnames, TestCase):
class TestPrivateSymlinkCnames(BaseSymlinkCnames, TempSiteRootTestCase):
privacy = 'private'
symlink_class = PrivateSymlink


class BaseSubprojects(TempSiterootCase):
class BaseSubprojects(object):

def setUp(self):
super(BaseSubprojects, self).setUp()
Expand Down Expand Up @@ -503,19 +504,17 @@ def test_remove_subprojects(self):
self.assertFilesystem(filesystem)


@override_settings()
class TestPublicSubprojects(BaseSubprojects, TestCase):
class TestPublicSubprojects(BaseSubprojects, TempSiteRootTestCase):
privacy = 'public'
symlink_class = PublicSymlink


@override_settings()
class TestPrivateSubprojects(BaseSubprojects, TestCase):
class TestPrivateSubprojects(BaseSubprojects, TempSiteRootTestCase):
privacy = 'private'
symlink_class = PrivateSymlink


class BaseSymlinkTranslations(TempSiterootCase):
class BaseSymlinkTranslations(object):

def setUp(self):
super(BaseSymlinkTranslations, self).setUp()
Expand Down Expand Up @@ -757,19 +756,17 @@ def test_remove_language(self):
self.assertFilesystem(filesystem)


@override_settings()
class TestPublicSymlinkTranslations(BaseSymlinkTranslations, TestCase):
class TestPublicSymlinkTranslations(BaseSymlinkTranslations, TempSiteRootTestCase):
privacy = 'public'
symlink_class = PublicSymlink


@override_settings()
class TestPrivateSymlinkTranslations(BaseSymlinkTranslations, TestCase):
class TestPrivateSymlinkTranslations(BaseSymlinkTranslations, TempSiteRootTestCase):
privacy = 'private'
symlink_class = PrivateSymlink


class BaseSymlinkSingleVersion(TempSiterootCase):
class BaseSymlinkSingleVersion(object):

def setUp(self):
super(BaseSymlinkSingleVersion, self).setUp()
Expand Down Expand Up @@ -834,19 +831,17 @@ def test_symlink_single_version_missing(self):
self.assertFilesystem(filesystem)


@override_settings()
class TestPublicSymlinkSingleVersion(BaseSymlinkSingleVersion, TestCase):
class TestPublicSymlinkSingleVersion(BaseSymlinkSingleVersion, TempSiteRootTestCase):
privacy = 'public'
symlink_class = PublicSymlink


@override_settings()
class TestPublicSymlinkSingleVersion(BaseSymlinkSingleVersion, TestCase):
class TestPublicSymlinkSingleVersion(BaseSymlinkSingleVersion, TempSiteRootTestCase):
privacy = 'private'
symlink_class = PrivateSymlink


class BaseSymlinkVersions(TempSiterootCase):
class BaseSymlinkVersions(object):

def setUp(self):
super(BaseSymlinkVersions, self).setUp()
Expand Down Expand Up @@ -962,20 +957,17 @@ def test_symlink_other_versions(self):
self.assertFilesystem(filesystem)


@override_settings()
class TestPublicSymlinkVersions(BaseSymlinkVersions, TestCase):
class TestPublicSymlinkVersions(BaseSymlinkVersions, TempSiteRootTestCase):
privacy = 'public'
symlink_class = PublicSymlink


@override_settings()
class TestPrivateSymlinkVersions(BaseSymlinkVersions, TestCase):
class TestPrivateSymlinkVersions(BaseSymlinkVersions, TempSiteRootTestCase):
privacy = 'private'
symlink_class = PrivateSymlink


@override_settings()
class TestPublicSymlinkUnicode(TempSiterootCase, TestCase):
class TestPublicSymlinkUnicode(TempSiteRootTestCase):

def setUp(self):
super(TestPublicSymlinkUnicode, self).setUp()
Expand Down Expand Up @@ -1040,8 +1032,7 @@ def test_symlink_broadcast_calls_on_project_save(self):
)


@override_settings()
class TestPublicPrivateSymlink(TempSiterootCase, TestCase):
class TestPublicPrivateSymlink(TempSiteRootTestCase):

def setUp(self):
super(TestPublicPrivateSymlink, self).setUp()
Expand Down