diff --git a/CHANGES.md b/CHANGES.md
index 29711c60c..5d4e80499 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,20 @@
+Synapse 1.21.0rc2 (2020-10-02)
+==============================
+
+Features
+--------
+
+- Convert additional templates from inline HTML to Jinja2 templates. ([\#8444](https://github.com/matrix-org/synapse/issues/8444))
+
+Bugfixes
+--------
+
+- Fix a regression in v1.21.0rc1 which broke thumbnails of remote media. ([\#8438](https://github.com/matrix-org/synapse/issues/8438))
+- Do not expose the experimental `uk.half-shot.msc2778.login.application_service` flow in the login API, which caused a compatibility problem with Element iOS. ([\#8440](https://github.com/matrix-org/synapse/issues/8440))
+- Fix malformed log line in new federation "catch up" logic. ([\#8442](https://github.com/matrix-org/synapse/issues/8442))
+- Fix DB query on startup for negative streams which caused long start up times. Introduced in [\#8374](https://github.com/matrix-org/synapse/issues/8374). ([\#8447](https://github.com/matrix-org/synapse/issues/8447))
+
+
Synapse 1.21.0rc1 (2020-10-01)
==============================
diff --git a/synapse/__init__.py b/synapse/__init__.py
index 470697450..500558bbd 100644
--- a/synapse/__init__.py
+++ b/synapse/__init__.py
@@ -48,7 +48,7 @@ try:
except ImportError:
pass
-__version__ = "1.21.0rc1"
+__version__ = "1.21.0rc2"
if bool(os.environ.get("SYNAPSE_TEST_PATCH_LOG_CONTEXTS", False)):
# We import here so that we don't have to install a bunch of deps when
diff --git a/synapse/config/_base.py b/synapse/config/_base.py
index 05a66841c..85f65da4d 100644
--- a/synapse/config/_base.py
+++ b/synapse/config/_base.py
@@ -242,12 +242,11 @@ class Config:
env = jinja2.Environment(loader=loader, autoescape=autoescape)
# Update the environment with our custom filters
- env.filters.update(
- {
- "format_ts": _format_ts_filter,
- "mxc_to_http": _create_mxc_to_http_filter(self.public_baseurl),
- }
- )
+ env.filters.update({"format_ts": _format_ts_filter})
+ if self.public_baseurl:
+ env.filters.update(
+ {"mxc_to_http": _create_mxc_to_http_filter(self.public_baseurl)}
+ )
for filename in filenames:
# Load the template
diff --git a/synapse/config/captcha.py b/synapse/config/captcha.py
index 82f04d796..cb0095816 100644
--- a/synapse/config/captcha.py
+++ b/synapse/config/captcha.py
@@ -28,6 +28,9 @@ class CaptchaConfig(Config):
"recaptcha_siteverify_api",
"https://www.recaptcha.net/recaptcha/api/siteverify",
)
+ self.recaptcha_template = self.read_templates(
+ ["recaptcha.html"], autoescape=True
+ )[0]
def generate_config_section(self, **kwargs):
return """\
diff --git a/synapse/config/consent_config.py b/synapse/config/consent_config.py
index fbddebeea..6efa59b11 100644
--- a/synapse/config/consent_config.py
+++ b/synapse/config/consent_config.py
@@ -89,6 +89,8 @@ class ConsentConfig(Config):
def read_config(self, config, **kwargs):
consent_config = config.get("user_consent")
+ self.terms_template = self.read_templates(["terms.html"], autoescape=True)[0]
+
if consent_config is None:
return
self.user_consent_version = str(consent_config["version"])
diff --git a/synapse/config/registration.py b/synapse/config/registration.py
index 5ffbb934f..d7e3690a3 100644
--- a/synapse/config/registration.py
+++ b/synapse/config/registration.py
@@ -187,6 +187,11 @@ class RegistrationConfig(Config):
session_lifetime = self.parse_duration(session_lifetime)
self.session_lifetime = session_lifetime
+ # The success template used during fallback auth.
+ self.fallback_success_template = self.read_templates(
+ ["auth_success.html"], autoescape=True
+ )[0]
+
def generate_config_section(self, generate_secrets=False, **kwargs):
if generate_secrets:
registration_shared_secret = 'registration_shared_secret: "%s"' % (
diff --git a/synapse/federation/sender/per_destination_queue.py b/synapse/federation/sender/per_destination_queue.py
index 2657767fd..bc99af3fd 100644
--- a/synapse/federation/sender/per_destination_queue.py
+++ b/synapse/federation/sender/per_destination_queue.py
@@ -490,7 +490,7 @@ class PerDestinationQueue:
)
if logger.isEnabledFor(logging.INFO):
- rooms = (p.room_id for p in catchup_pdus)
+ rooms = [p.room_id for p in catchup_pdus]
logger.info("Catching up rooms to %s: %r", self._destination, rooms)
success = await self._transaction_manager.send_new_transaction(
diff --git a/synapse/res/templates/auth_success.html b/synapse/res/templates/auth_success.html
new file mode 100644
index 000000000..baf463314
--- /dev/null
+++ b/synapse/res/templates/auth_success.html
@@ -0,0 +1,21 @@
+
+
+Success!
+
+
+
+
+
+
+
Thank you
+
You may now close this window and return to the application
+
+
+
diff --git a/synapse/res/templates/recaptcha.html b/synapse/res/templates/recaptcha.html
new file mode 100644
index 000000000..63944dc60
--- /dev/null
+++ b/synapse/res/templates/recaptcha.html
@@ -0,0 +1,38 @@
+
+
+Authentication
+
+
+
+
+
+
+
+
+
+
diff --git a/synapse/res/templates/terms.html b/synapse/res/templates/terms.html
new file mode 100644
index 000000000..dfef9897e
--- /dev/null
+++ b/synapse/res/templates/terms.html
@@ -0,0 +1,20 @@
+
+
+Authentication
+
+
+
+
+
+
+
diff --git a/synapse/rest/client/v1/login.py b/synapse/rest/client/v1/login.py
index b9347b87c..3d1693d7a 100644
--- a/synapse/rest/client/v1/login.py
+++ b/synapse/rest/client/v1/login.py
@@ -111,8 +111,6 @@ class LoginRestServlet(RestServlet):
({"type": t} for t in self.auth_handler.get_supported_login_types())
)
- flows.append({"type": LoginRestServlet.APPSERVICE_TYPE})
-
return 200, {"flows": flows}
def on_OPTIONS(self, request: SynapseRequest):
diff --git a/synapse/rest/client/v2_alpha/auth.py b/synapse/rest/client/v2_alpha/auth.py
index 097538f96..5fbfae599 100644
--- a/synapse/rest/client/v2_alpha/auth.py
+++ b/synapse/rest/client/v2_alpha/auth.py
@@ -25,94 +25,6 @@ from ._base import client_patterns
logger = logging.getLogger(__name__)
-RECAPTCHA_TEMPLATE = """
-
-
-Authentication
-
-
-
-
-
-
-
-
-
-
-"""
-
-TERMS_TEMPLATE = """
-
-
-Authentication
-
-
-
-
-
-
-
-"""
-
-SUCCESS_TEMPLATE = """
-
-
-Success!
-
-
-
-
-
-
-
Thank you
-
You may now close this window and return to the application
-
-
-
-"""
-
class AuthRestServlet(RestServlet):
"""
@@ -145,26 +57,30 @@ class AuthRestServlet(RestServlet):
self._cas_server_url = hs.config.cas_server_url
self._cas_service_url = hs.config.cas_service_url
+ self.recaptcha_template = hs.config.recaptcha_template
+ self.terms_template = hs.config.terms_template
+ self.success_template = hs.config.fallback_success_template
+
async def on_GET(self, request, stagetype):
session = parse_string(request, "session")
if not session:
raise SynapseError(400, "No session supplied")
if stagetype == LoginType.RECAPTCHA:
- html = RECAPTCHA_TEMPLATE % {
- "session": session,
- "myurl": "%s/r0/auth/%s/fallback/web"
+ html = self.recaptcha_template.render(
+ session=session,
+ myurl="%s/r0/auth/%s/fallback/web"
% (CLIENT_API_PREFIX, LoginType.RECAPTCHA),
- "sitekey": self.hs.config.recaptcha_public_key,
- }
+ sitekey=self.hs.config.recaptcha_public_key,
+ )
elif stagetype == LoginType.TERMS:
- html = TERMS_TEMPLATE % {
- "session": session,
- "terms_url": "%s_matrix/consent?v=%s"
+ html = self.terms_template.render(
+ session=session,
+ terms_url="%s_matrix/consent?v=%s"
% (self.hs.config.public_baseurl, self.hs.config.user_consent_version),
- "myurl": "%s/r0/auth/%s/fallback/web"
+ myurl="%s/r0/auth/%s/fallback/web"
% (CLIENT_API_PREFIX, LoginType.TERMS),
- }
+ )
elif stagetype == LoginType.SSO:
# Display a confirmation page which prompts the user to
@@ -222,14 +138,14 @@ class AuthRestServlet(RestServlet):
)
if success:
- html = SUCCESS_TEMPLATE
+ html = self.success_template.render()
else:
- html = RECAPTCHA_TEMPLATE % {
- "session": session,
- "myurl": "%s/r0/auth/%s/fallback/web"
+ html = self.recaptcha_template.render(
+ session=session,
+ myurl="%s/r0/auth/%s/fallback/web"
% (CLIENT_API_PREFIX, LoginType.RECAPTCHA),
- "sitekey": self.hs.config.recaptcha_public_key,
- }
+ sitekey=self.hs.config.recaptcha_public_key,
+ )
elif stagetype == LoginType.TERMS:
authdict = {"session": session}
@@ -238,18 +154,18 @@ class AuthRestServlet(RestServlet):
)
if success:
- html = SUCCESS_TEMPLATE
+ html = self.success_template.render()
else:
- html = TERMS_TEMPLATE % {
- "session": session,
- "terms_url": "%s_matrix/consent?v=%s"
+ html = self.terms_template.render(
+ session=session,
+ terms_url="%s_matrix/consent?v=%s"
% (
self.hs.config.public_baseurl,
self.hs.config.user_consent_version,
),
- "myurl": "%s/r0/auth/%s/fallback/web"
+ myurl="%s/r0/auth/%s/fallback/web"
% (CLIENT_API_PREFIX, LoginType.TERMS),
- }
+ )
elif stagetype == LoginType.SSO:
# The SSO fallback workflow should not post here,
raise SynapseError(404, "Fallback SSO auth does not support POST requests.")
diff --git a/synapse/rest/media/v1/media_storage.py b/synapse/rest/media/v1/media_storage.py
index 5681677fc..a9586fb0b 100644
--- a/synapse/rest/media/v1/media_storage.py
+++ b/synapse/rest/media/v1/media_storage.py
@@ -141,31 +141,34 @@ class MediaStorage:
Returns:
Returns a Responder if the file was found, otherwise None.
"""
+ paths = [self._file_info_to_path(file_info)]
- path = self._file_info_to_path(file_info)
- local_path = os.path.join(self.local_media_directory, path)
- if os.path.exists(local_path):
- return FileResponder(open(local_path, "rb"))
-
- # Fallback for paths without method names
- # Should be removed in the future
+ # fallback for remote thumbnails with no method in the filename
if file_info.thumbnail and file_info.server_name:
- legacy_path = self.filepaths.remote_media_thumbnail_rel_legacy(
- server_name=file_info.server_name,
- file_id=file_info.file_id,
- width=file_info.thumbnail_width,
- height=file_info.thumbnail_height,
- content_type=file_info.thumbnail_type,
+ paths.append(
+ self.filepaths.remote_media_thumbnail_rel_legacy(
+ server_name=file_info.server_name,
+ file_id=file_info.file_id,
+ width=file_info.thumbnail_width,
+ height=file_info.thumbnail_height,
+ content_type=file_info.thumbnail_type,
+ )
)
- legacy_local_path = os.path.join(self.local_media_directory, legacy_path)
- if os.path.exists(legacy_local_path):
- return FileResponder(open(legacy_local_path, "rb"))
+
+ for path in paths:
+ local_path = os.path.join(self.local_media_directory, path)
+ if os.path.exists(local_path):
+ logger.debug("responding with local file %s", local_path)
+ return FileResponder(open(local_path, "rb"))
+ logger.debug("local file %s did not exist", local_path)
for provider in self.storage_providers:
- res = await provider.fetch(path, file_info) # type: Any
- if res:
- logger.debug("Streaming %s from %s", path, provider)
- return res
+ for path in paths:
+ res = await provider.fetch(path, file_info) # type: Any
+ if res:
+ logger.debug("Streaming %s from %s", path, provider)
+ return res
+ logger.debug("%s not found on %s", path, provider)
return None
diff --git a/synapse/storage/util/id_generators.py b/synapse/storage/util/id_generators.py
index ec356b2e4..c92cd4a6b 100644
--- a/synapse/storage/util/id_generators.py
+++ b/synapse/storage/util/id_generators.py
@@ -341,7 +341,7 @@ class MultiWriterIdGenerator:
"cmp": "<=" if self._positive else ">=",
}
sql = self._db.engine.convert_param_style(sql)
- cur.execute(sql, (min_stream_id,))
+ cur.execute(sql, (min_stream_id * self._return_factor,))
self._persisted_upto_position = min_stream_id