Skip to content

Commit

Permalink
Merge branch 'main' of github.com:readthedocs/readthedocs.org into hu…
Browse files Browse the repository at this point in the history
…mitos/output-variable
  • Loading branch information
humitos committed Jan 19, 2023
2 parents 7b19e28 + 239d890 commit 2246320
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 21 deletions.
3 changes: 1 addition & 2 deletions docs/user/tutorial/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ and it contains the following files:

``docs/``
Directory holding all the Sphinx documentation sources,
including some required dependencies in ``docs/requirements.txt``,
the Sphinx configuration ``docs/source/conf.py``,
including the Sphinx configuration ``docs/source/conf.py``
and the root document ``docs/source/index.rst`` written in reStructuredText.

.. figure:: /_static/images/tutorial/github-template.png
Expand Down
12 changes: 12 additions & 0 deletions readthedocs/doc_builder/backends/sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ def __init__(self, *args, **kwargs):
self.config_file,
)
except ProjectConfigurationError:
# NOTE: this exception handling here is weird to me.
# We are raising this exception from inside `project.confi_file` when:
# - the repository has multiple config files (none of them with `doc` in its filename)
# - there is no config file at all
#
# IMO, if there are multiple config files,
# the build should fail immediately communicating this to the user.
# This can be achived by unhandle the exception here
# and leaving `on_failure` Celery handle to deal with it.
#
# In case there is no config file, we should continue the build
# because Read the Docs will automatically create one for it.
pass

def _write_config(self, master_doc='index'):
Expand Down
54 changes: 37 additions & 17 deletions readthedocs/rtd_tests/tests/test_middleware.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from unittest import mock

from corsheaders.middleware import CorsMiddleware
from corsheaders.middleware import (
ACCESS_CONTROL_ALLOW_CREDENTIALS,
ACCESS_CONTROL_ALLOW_ORIGIN,
CorsMiddleware,
)
from django.conf import settings
from django.http import HttpResponse
from django.test import TestCase, override_settings
Expand Down Expand Up @@ -73,7 +77,8 @@ def test_allow_linked_domain_from_public_version(self):
HTTP_ORIGIN='http://my.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertIn('Access-Control-Allow-Origin', resp)
self.assertIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

def test_dont_allow_linked_domain_from_private_version(self):
self.version.privacy_level = PRIVATE
Expand All @@ -84,7 +89,8 @@ def test_dont_allow_linked_domain_from_private_version(self):
HTTP_ORIGIN='http://my.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

def test_allowed_api_public_version_from_another_domain(self):
request = self.factory.get(
Expand All @@ -93,15 +99,17 @@ def test_allowed_api_public_version_from_another_domain(self):
HTTP_ORIGIN='http://docs.another.domain',
)
resp = self.middleware.process_response(request, {})
self.assertIn('Access-Control-Allow-Origin', resp)
self.assertIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

request = self.factory.get(
self.url,
{'project': self.project.slug, 'version': self.version.slug},
HTTP_ORIGIN='http://another.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertIn('Access-Control-Allow-Origin', resp)
self.assertIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

def test_not_allowed_api_private_version_from_another_domain(self):
self.version.privacy_level = PRIVATE
Expand All @@ -112,15 +120,17 @@ def test_not_allowed_api_private_version_from_another_domain(self):
HTTP_ORIGIN='http://docs.another.domain',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

request = self.factory.get(
self.url,
{'project': self.project.slug, 'version': self.version.slug},
HTTP_ORIGIN='http://another.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

def test_valid_subproject(self):
self.assertTrue(
Expand All @@ -135,7 +145,8 @@ def test_valid_subproject(self):
HTTP_ORIGIN='http://my.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertIn('Access-Control-Allow-Origin', resp)
self.assertIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

def test_embed_api_private_version_linked_domain(self):
self.version.privacy_level = PRIVATE
Expand All @@ -146,7 +157,8 @@ def test_embed_api_private_version_linked_domain(self):
HTTP_ORIGIN='http://my.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

def test_embed_api_external_url(self):
request = self.factory.get(
Expand Down Expand Up @@ -174,15 +186,17 @@ def test_sustainability_endpoint_allways_allowed(self, has_donate_app):
HTTP_ORIGIN='http://invalid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertIn('Access-Control-Allow-Origin', resp)
self.assertIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

request = self.factory.get(
'/api/v2/sustainability/',
{'project': self.project.slug, 'active': True, 'version': self.version.slug},
HTTP_ORIGIN='http://my.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertIn('Access-Control-Allow-Origin', resp)
self.assertIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

@mock.patch('readthedocs.core.signals._has_donate_app')
def test_sustainability_endpoint_no_ext(self, has_donate_app):
Expand All @@ -193,15 +207,17 @@ def test_sustainability_endpoint_no_ext(self, has_donate_app):
HTTP_ORIGIN='http://invalid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

request = self.factory.get(
'/api/v2/sustainability/',
{'project': self.project.slug, 'active': True, 'version': self.version.slug},
HTTP_ORIGIN='http://my.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

def test_apiv2_endpoint_not_allowed(self):
request = self.factory.get(
Expand All @@ -210,7 +226,8 @@ def test_apiv2_endpoint_not_allowed(self):
HTTP_ORIGIN='http://invalid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

# This also doesn't work on registered domains.
request = self.factory.get(
Expand All @@ -219,7 +236,8 @@ def test_apiv2_endpoint_not_allowed(self):
HTTP_ORIGIN='http://my.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

# Or from our public domain.
request = self.factory.get(
Expand All @@ -228,7 +246,8 @@ def test_apiv2_endpoint_not_allowed(self):
HTTP_ORIGIN='http://docs.readthedocs.io/',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)

# POST is not allowed
request = self.factory.post(
Expand All @@ -237,7 +256,8 @@ def test_apiv2_endpoint_not_allowed(self):
HTTP_ORIGIN='http://my.valid.domain',
)
resp = self.middleware.process_response(request, {})
self.assertNotIn('Access-Control-Allow-Origin', resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, resp)
self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, resp)


class TestSessionMiddleware(TestCase):
Expand Down
7 changes: 5 additions & 2 deletions readthedocs/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,11 @@ def DOCKER_LIMITS(self):
}

# CORS
# So cookies can be included in cross-domain requests where needed (eg. sustainability API).
CORS_ALLOW_CREDENTIALS = True
# Don't allow sending cookies in cross-domain requests, this is so we can
# relax our CORS headers for more views, but at the same time not opening
# users to CSRF attacks. The sustainability API is the only view that requires
# cookies to be send cross-site, we override that for that view only.
CORS_ALLOW_CREDENTIALS = False
CORS_ALLOW_HEADERS = (
'x-requested-with',
'content-type',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% extends "core/email/common.html" %}

{% block content %}
<p>
You are receiving this email because you or someone else tried to signup for
an account using email address: {{ email}}.
</p>

<p>
However, an account using that email address already exists. In case you have
forgotten about this, please use the password forgotten procedure to recover
your account:
</p>

<p>
<a href="{{ password_reset_url }}">{{ password_reset_url }}</a>
</p>

<p>
If you did not sign up for an account with Read the Docs, you can disregard this email.
</p>

{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% extends "core/email/common.txt" %}

{% block content %}

You are receiving this email because you or someone else tried to signup for
an account using email address:

{{ email }}

However, an account using that email address already exists. In case you have
forgotten about this, please use the password forgotten procedure to recover
your account:

{{ password_reset_url }

If you did not sign up for an account with Read the Docs, you can disregard this email.
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Account Already Exists
21 changes: 21 additions & 0 deletions readthedocs/templates/account/email/unknown_account_message.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% extends "core/email/common.html" %}

{% block content %}
<p>
You are receiving this email because you or someone else has requested a
password for your user account. However, we do not have any record of a user
with email {{ email }} in our database.
</p>

<p>
This mail can be safely ignored if you did not request a password reset.
</p>

<p>
If it was you, you can sign up for an account using the link below.
</p>

<p>
<a href="{{ signup_url }}">{{ signup_url }}</a>
</p>
{% endblock %}
13 changes: 13 additions & 0 deletions readthedocs/templates/account/email/unknown_account_message.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% extends "core/email/common.txt" %}

{% block content %}
You are receiving this email because you or someone else has requested a
password for your user account. However, we do not have any record of a user
with email {{ email }} in our database.

This mail can be safely ignored if you did not request a password reset.

If it was you, you can sign up for an account using the link below.

{{ signup_url }}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Password Reset Email

0 comments on commit 2246320

Please sign in to comment.