mirror of
https://github.com/element-hq/synapse.git
synced 2024-12-14 11:57:44 +00:00
Clean-up the template loading code. (#9200)
* Enables autoescape by default for HTML files. * Adds a new read_template method for reading a single template. * Some logic clean-up.
This commit is contained in:
parent
93b61589b0
commit
e54746bdf7
12 changed files with 96 additions and 38 deletions
37
UPGRADE.rst
37
UPGRADE.rst
|
@ -85,6 +85,43 @@ for example:
|
||||||
wget https://packages.matrix.org/debian/pool/main/m/matrix-synapse-py3/matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
wget https://packages.matrix.org/debian/pool/main/m/matrix-synapse-py3/matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
||||||
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
||||||
|
|
||||||
|
Upgrading to v1.27.0
|
||||||
|
====================
|
||||||
|
|
||||||
|
Changes to HTML templates
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The HTML templates for SSO and email notifications now have `Jinja2's autoescape <https://jinja.palletsprojects.com/en/2.11.x/api/#autoescaping>`_
|
||||||
|
enabled for files ending in ``.html``, ``.htm``, and ``.xml``. If you hae customised
|
||||||
|
these templates and see issues when viewing them you might need to update them.
|
||||||
|
It is expected that most configurations will need no changes.
|
||||||
|
|
||||||
|
If you have customised the templates *names* for these templates it is recommended
|
||||||
|
to verify they end in ``.html`` to ensure autoescape is enabled.
|
||||||
|
|
||||||
|
The above applies to the following templates:
|
||||||
|
|
||||||
|
* ``add_threepid.html``
|
||||||
|
* ``add_threepid_failure.html``
|
||||||
|
* ``add_threepid_success.html``
|
||||||
|
* ``notice_expiry.html``
|
||||||
|
* ``notice_expiry.html``
|
||||||
|
* ``notif_mail.html`` (which, by default, includes ``room.html`` and ``notif.html``)
|
||||||
|
* ``password_reset.html``
|
||||||
|
* ``password_reset_confirmation.html``
|
||||||
|
* ``password_reset_failure.html``
|
||||||
|
* ``password_reset_success.html``
|
||||||
|
* ``registration.html``
|
||||||
|
* ``registration_failure.html``
|
||||||
|
* ``registration_success.html``
|
||||||
|
* ``sso_account_deactivated.html``
|
||||||
|
* ``sso_auth_bad_user.html``
|
||||||
|
* ``sso_auth_confirm.html``
|
||||||
|
* ``sso_auth_success.html``
|
||||||
|
* ``sso_error.html``
|
||||||
|
* ``sso_login_idp_picker.html``
|
||||||
|
* ``sso_redirect_confirm.html``
|
||||||
|
|
||||||
Upgrading to v1.26.0
|
Upgrading to v1.26.0
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
|
1
changelog.d/9200.misc
Normal file
1
changelog.d/9200.misc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Clean-up template loading code.
|
|
@ -203,11 +203,28 @@ class Config:
|
||||||
with open(file_path) as file_stream:
|
with open(file_path) as file_stream:
|
||||||
return file_stream.read()
|
return file_stream.read()
|
||||||
|
|
||||||
|
def read_template(self, filename: str) -> jinja2.Template:
|
||||||
|
"""Load a template file from disk.
|
||||||
|
|
||||||
|
This function will attempt to load the given template from the default Synapse
|
||||||
|
template directory.
|
||||||
|
|
||||||
|
Files read are treated as Jinja templates. The templates is not rendered yet
|
||||||
|
and has autoescape enabled.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename: A template filename to read.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ConfigError: if the file's path is incorrect or otherwise cannot be read.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A jinja2 template.
|
||||||
|
"""
|
||||||
|
return self.read_templates([filename])[0]
|
||||||
|
|
||||||
def read_templates(
|
def read_templates(
|
||||||
self,
|
self, filenames: List[str], custom_template_directory: Optional[str] = None,
|
||||||
filenames: List[str],
|
|
||||||
custom_template_directory: Optional[str] = None,
|
|
||||||
autoescape: bool = False,
|
|
||||||
) -> List[jinja2.Template]:
|
) -> List[jinja2.Template]:
|
||||||
"""Load a list of template files from disk using the given variables.
|
"""Load a list of template files from disk using the given variables.
|
||||||
|
|
||||||
|
@ -215,7 +232,8 @@ class Config:
|
||||||
template directory. If `custom_template_directory` is supplied, that directory
|
template directory. If `custom_template_directory` is supplied, that directory
|
||||||
is tried first.
|
is tried first.
|
||||||
|
|
||||||
Files read are treated as Jinja templates. These templates are not rendered yet.
|
Files read are treated as Jinja templates. The templates are not rendered yet
|
||||||
|
and have autoescape enabled.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filenames: A list of template filenames to read.
|
filenames: A list of template filenames to read.
|
||||||
|
@ -223,16 +241,12 @@ class Config:
|
||||||
custom_template_directory: A directory to try to look for the templates
|
custom_template_directory: A directory to try to look for the templates
|
||||||
before using the default Synapse template directory instead.
|
before using the default Synapse template directory instead.
|
||||||
|
|
||||||
autoescape: Whether to autoescape variables before inserting them into the
|
|
||||||
template.
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ConfigError: if the file's path is incorrect or otherwise cannot be read.
|
ConfigError: if the file's path is incorrect or otherwise cannot be read.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A list of jinja2 templates.
|
A list of jinja2 templates.
|
||||||
"""
|
"""
|
||||||
templates = []
|
|
||||||
search_directories = [self.default_template_dir]
|
search_directories = [self.default_template_dir]
|
||||||
|
|
||||||
# The loader will first look in the custom template directory (if specified) for the
|
# The loader will first look in the custom template directory (if specified) for the
|
||||||
|
@ -249,7 +263,7 @@ class Config:
|
||||||
search_directories.insert(0, custom_template_directory)
|
search_directories.insert(0, custom_template_directory)
|
||||||
|
|
||||||
loader = jinja2.FileSystemLoader(search_directories)
|
loader = jinja2.FileSystemLoader(search_directories)
|
||||||
env = jinja2.Environment(loader=loader, autoescape=autoescape)
|
env = jinja2.Environment(loader=loader, autoescape=jinja2.select_autoescape(),)
|
||||||
|
|
||||||
# Update the environment with our custom filters
|
# Update the environment with our custom filters
|
||||||
env.filters.update(
|
env.filters.update(
|
||||||
|
@ -259,12 +273,8 @@ class Config:
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
for filename in filenames:
|
# Load the templates
|
||||||
# Load the template
|
return [env.get_template(filename) for filename in filenames]
|
||||||
template = env.get_template(filename)
|
|
||||||
templates.append(template)
|
|
||||||
|
|
||||||
return templates
|
|
||||||
|
|
||||||
|
|
||||||
def _format_ts_filter(value: int, format: str):
|
def _format_ts_filter(value: int, format: str):
|
||||||
|
|
|
@ -28,9 +28,7 @@ class CaptchaConfig(Config):
|
||||||
"recaptcha_siteverify_api",
|
"recaptcha_siteverify_api",
|
||||||
"https://www.recaptcha.net/recaptcha/api/siteverify",
|
"https://www.recaptcha.net/recaptcha/api/siteverify",
|
||||||
)
|
)
|
||||||
self.recaptcha_template = self.read_templates(
|
self.recaptcha_template = self.read_template("recaptcha.html")
|
||||||
["recaptcha.html"], autoescape=True
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
def generate_config_section(self, **kwargs):
|
def generate_config_section(self, **kwargs):
|
||||||
return """\
|
return """\
|
||||||
|
|
|
@ -89,7 +89,7 @@ class ConsentConfig(Config):
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
consent_config = config.get("user_consent")
|
consent_config = config.get("user_consent")
|
||||||
self.terms_template = self.read_templates(["terms.html"], autoescape=True)[0]
|
self.terms_template = self.read_template("terms.html")
|
||||||
|
|
||||||
if consent_config is None:
|
if consent_config is None:
|
||||||
return
|
return
|
||||||
|
|
|
@ -176,9 +176,7 @@ class RegistrationConfig(Config):
|
||||||
self.session_lifetime = session_lifetime
|
self.session_lifetime = session_lifetime
|
||||||
|
|
||||||
# The success template used during fallback auth.
|
# The success template used during fallback auth.
|
||||||
self.fallback_success_template = self.read_templates(
|
self.fallback_success_template = self.read_template("auth_success.html")
|
||||||
["auth_success.html"], autoescape=True
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
def generate_config_section(self, generate_secrets=False, **kwargs):
|
def generate_config_section(self, generate_secrets=False, **kwargs):
|
||||||
if generate_secrets:
|
if generate_secrets:
|
||||||
|
|
|
@ -668,6 +668,15 @@ class Mailer:
|
||||||
|
|
||||||
|
|
||||||
def safe_markup(raw_html: str) -> jinja2.Markup:
|
def safe_markup(raw_html: str) -> jinja2.Markup:
|
||||||
|
"""
|
||||||
|
Sanitise a raw HTML string to a set of allowed tags and attributes, and linkify any bare URLs.
|
||||||
|
|
||||||
|
Args
|
||||||
|
raw_html: Unsafe HTML.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A Markup object ready to safely use in a Jinja template.
|
||||||
|
"""
|
||||||
return jinja2.Markup(
|
return jinja2.Markup(
|
||||||
bleach.linkify(
|
bleach.linkify(
|
||||||
bleach.clean(
|
bleach.clean(
|
||||||
|
@ -684,8 +693,13 @@ def safe_markup(raw_html: str) -> jinja2.Markup:
|
||||||
|
|
||||||
def safe_text(raw_text: str) -> jinja2.Markup:
|
def safe_text(raw_text: str) -> jinja2.Markup:
|
||||||
"""
|
"""
|
||||||
Process text: treat it as HTML but escape any tags (ie. just escape the
|
Sanitise text (escape any HTML tags), and then linkify any bare URLs.
|
||||||
HTML) then linkify it.
|
|
||||||
|
Args
|
||||||
|
raw_text: Unsafe text which might include HTML markup.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A Markup object ready to safely use in a Jinja template.
|
||||||
"""
|
"""
|
||||||
return jinja2.Markup(
|
return jinja2.Markup(
|
||||||
bleach.linkify(bleach.clean(raw_text, tags=[], attributes={}, strip=False))
|
bleach.linkify(bleach.clean(raw_text, tags=[], attributes={}, strip=False))
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
We were unable to validate your <tt>{{server_name | e}}</tt> account via
|
We were unable to validate your <tt>{{ server_name }}</tt> account via
|
||||||
single-sign-on (SSO), because the SSO Identity Provider returned
|
single-sign-on (SSO), because the SSO Identity Provider returned
|
||||||
different details than when you logged in.
|
different details than when you logged in.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
A client is trying to {{ description | e }}. To confirm this action,
|
A client is trying to {{ description }}. To confirm this action,
|
||||||
<a href="{{ redirect_url | e }}">re-authenticate with single sign-on</a>.
|
<a href="{{ redirect_url }}">re-authenticate with single sign-on</a>.
|
||||||
If you did not expect this, your account may be compromised!
|
If you did not expect this, your account may be compromised!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<p>
|
<p>
|
||||||
There was an error during authentication:
|
There was an error during authentication:
|
||||||
</p>
|
</p>
|
||||||
<div id="errormsg" style="margin:20px 80px">{{ error_description | e }}</div>
|
<div id="errormsg" style="margin:20px 80px">{{ error_description }}</div>
|
||||||
<p>
|
<p>
|
||||||
If you are seeing this page after clicking a link sent to you via email, make
|
If you are seeing this page after clicking a link sent to you via email, make
|
||||||
sure you only click the confirmation link once, and that you open the
|
sure you only click the confirmation link once, and that you open the
|
||||||
|
|
|
@ -3,22 +3,22 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="/_matrix/static/client/login/style.css">
|
<link rel="stylesheet" href="/_matrix/static/client/login/style.css">
|
||||||
<title>{{server_name | e}} Login</title>
|
<title>{{ server_name }} Login</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<h1 id="title">{{server_name | e}} Login</h1>
|
<h1 id="title">{{ server_name }} Login</h1>
|
||||||
<div class="login_flow">
|
<div class="login_flow">
|
||||||
<p>Choose one of the following identity providers:</p>
|
<p>Choose one of the following identity providers:</p>
|
||||||
<form>
|
<form>
|
||||||
<input type="hidden" name="redirectUrl" value="{{redirect_url | e}}">
|
<input type="hidden" name="redirectUrl" value="{{ redirect_url }}">
|
||||||
<ul class="radiobuttons">
|
<ul class="radiobuttons">
|
||||||
{% for p in providers %}
|
{% for p in providers %}
|
||||||
<li>
|
<li>
|
||||||
<input type="radio" name="idp" id="prov{{loop.index}}" value="{{p.idp_id}}">
|
<input type="radio" name="idp" id="prov{{ loop.index }}" value="{{ p.idp_id }}">
|
||||||
<label for="prov{{loop.index}}">{{p.idp_name | e}}</label>
|
<label for="prov{{ loop.index }}">{{ p.idp_name }}</label>
|
||||||
{% if p.idp_icon %}
|
{% if p.idp_icon %}
|
||||||
<img src="{{p.idp_icon | mxc_to_http(32, 32)}}"/>
|
<img src="{{ p.idp_icon | mxc_to_http(32, 32) }}"/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
<title>SSO redirect confirmation</title>
|
<title>SSO redirect confirmation</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p>The application at <span style="font-weight:bold">{{ display_url | e }}</span> is requesting full access to your <span style="font-weight:bold">{{ server_name }}</span> Matrix account.</p>
|
<p>The application at <span style="font-weight:bold">{{ display_url }}</span> is requesting full access to your <span style="font-weight:bold">{{ server_name }}</span> Matrix account.</p>
|
||||||
<p>If you don't recognise this address, you should ignore this and close this tab.</p>
|
<p>If you don't recognise this address, you should ignore this and close this tab.</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ redirect_url | e }}">I trust this address</a>
|
<a href="{{ redirect_url }}">I trust this address</a>
|
||||||
</p>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue