From fae296f844dccc778f5b544232573b25d2a0903c Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 18 Jan 2021 15:34:28 +0000 Subject: [PATCH 1/8] Move `parse_and_validate_server_name` to stringutils I want to add more things to `stringutils` which will use it, and don't want to add a depenedency from `synapse.util` to `synapse.http`. --- synapse/config/server.py | 2 +- synapse/federation/federation_server.py | 2 +- synapse/federation/transport/server.py | 2 +- synapse/handlers/room.py | 2 +- synapse/http/endpoint.py | 79 ------------------------- synapse/types.py | 2 +- synapse/util/stringutils.py | 61 +++++++++++++++++++ tests/http/test_endpoint.py | 2 +- 8 files changed, 67 insertions(+), 85 deletions(-) delete mode 100644 synapse/http/endpoint.py diff --git a/synapse/config/server.py b/synapse/config/server.py index 75ba161f3551..47a037017332 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -26,7 +26,7 @@ from netaddr import IPSet from synapse.api.room_versions import KNOWN_ROOM_VERSIONS -from synapse.http.endpoint import parse_and_validate_server_name +from synapse.util.stringutils import parse_and_validate_server_name from ._base import Config, ConfigError diff --git a/synapse/federation/federation_server.py b/synapse/federation/federation_server.py index e5339aca239d..171d25c9454a 100644 --- a/synapse/federation/federation_server.py +++ b/synapse/federation/federation_server.py @@ -49,7 +49,6 @@ from synapse.federation.federation_base import FederationBase, event_from_pdu_json from synapse.federation.persistence import TransactionActions from synapse.federation.units import Edu, Transaction -from synapse.http.endpoint import parse_server_name from synapse.http.servlet import assert_params_in_dict from synapse.logging.context import ( make_deferred_yieldable, @@ -66,6 +65,7 @@ from synapse.util import glob_to_regex, json_decoder, unwrapFirstError from synapse.util.async_helpers import Linearizer, concurrently_execute from synapse.util.caches.response_cache import ResponseCache +from synapse.util.stringutils import parse_server_name if TYPE_CHECKING: from synapse.server import HomeServer diff --git a/synapse/federation/transport/server.py b/synapse/federation/transport/server.py index cfd094e58ff7..95c64510a927 100644 --- a/synapse/federation/transport/server.py +++ b/synapse/federation/transport/server.py @@ -28,7 +28,6 @@ FEDERATION_V1_PREFIX, FEDERATION_V2_PREFIX, ) -from synapse.http.endpoint import parse_and_validate_server_name from synapse.http.server import JsonResource from synapse.http.servlet import ( parse_boolean_from_args, @@ -45,6 +44,7 @@ ) from synapse.server import HomeServer from synapse.types import ThirdPartyInstanceID, get_domain_from_id +from synapse.util.stringutils import parse_and_validate_server_name from synapse.util.versionstring import get_version_string logger = logging.getLogger(__name__) diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 3bece6d66809..ee27d991351b 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -38,7 +38,6 @@ from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersion from synapse.events import EventBase from synapse.events.utils import copy_power_levels_contents -from synapse.http.endpoint import parse_and_validate_server_name from synapse.storage.state import StateFilter from synapse.types import ( JsonDict, @@ -55,6 +54,7 @@ from synapse.util import stringutils from synapse.util.async_helpers import Linearizer from synapse.util.caches.response_cache import ResponseCache +from synapse.util.stringutils import parse_and_validate_server_name from synapse.visibility import filter_events_for_client from ._base import BaseHandler diff --git a/synapse/http/endpoint.py b/synapse/http/endpoint.py deleted file mode 100644 index 92a5b606c8cd..000000000000 --- a/synapse/http/endpoint.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2014-2016 OpenMarket Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import logging -import re - -logger = logging.getLogger(__name__) - - -def parse_server_name(server_name): - """Split a server name into host/port parts. - - Args: - server_name (str): server name to parse - - Returns: - Tuple[str, int|None]: host/port parts. - - Raises: - ValueError if the server name could not be parsed. - """ - try: - if server_name[-1] == "]": - # ipv6 literal, hopefully - return server_name, None - - domain_port = server_name.rsplit(":", 1) - domain = domain_port[0] - port = int(domain_port[1]) if domain_port[1:] else None - return domain, port - except Exception: - raise ValueError("Invalid server name '%s'" % server_name) - - -VALID_HOST_REGEX = re.compile("\\A[0-9a-zA-Z.-]+\\Z") - - -def parse_and_validate_server_name(server_name): - """Split a server name into host/port parts and do some basic validation. - - Args: - server_name (str): server name to parse - - Returns: - Tuple[str, int|None]: host/port parts. - - Raises: - ValueError if the server name could not be parsed. - """ - host, port = parse_server_name(server_name) - - # these tests don't need to be bulletproof as we'll find out soon enough - # if somebody is giving us invalid data. What we *do* need is to be sure - # that nobody is sneaking IP literals in that look like hostnames, etc. - - # look for ipv6 literals - if host[0] == "[": - if host[-1] != "]": - raise ValueError("Mismatched [...] in server name '%s'" % (server_name,)) - return host, port - - # otherwise it should only be alphanumerics. - if not VALID_HOST_REGEX.match(host): - raise ValueError( - "Server name '%s' contains invalid characters" % (server_name,) - ) - - return host, port diff --git a/synapse/types.py b/synapse/types.py index 20a43d05bfd1..eafe729dfe7e 100644 --- a/synapse/types.py +++ b/synapse/types.py @@ -37,7 +37,7 @@ from unpaddedbase64 import decode_base64 from synapse.api.errors import Codes, SynapseError -from synapse.http.endpoint import parse_and_validate_server_name +from synapse.util.stringutils import parse_and_validate_server_name if TYPE_CHECKING: from synapse.appservice.api import ApplicationService diff --git a/synapse/util/stringutils.py b/synapse/util/stringutils.py index b103c8694cab..12d64f20af79 100644 --- a/synapse/util/stringutils.py +++ b/synapse/util/stringutils.py @@ -59,6 +59,67 @@ def assert_valid_client_secret(client_secret): ) +def parse_server_name(server_name): + """Split a server name into host/port parts. + + Args: + server_name (str): server name to parse + + Returns: + Tuple[str, int|None]: host/port parts. + + Raises: + ValueError if the server name could not be parsed. + """ + try: + if server_name[-1] == "]": + # ipv6 literal, hopefully + return server_name, None + + domain_port = server_name.rsplit(":", 1) + domain = domain_port[0] + port = int(domain_port[1]) if domain_port[1:] else None + return domain, port + except Exception: + raise ValueError("Invalid server name '%s'" % server_name) + + +VALID_HOST_REGEX = re.compile("\\A[0-9a-zA-Z.-]+\\Z") + + +def parse_and_validate_server_name(server_name): + """Split a server name into host/port parts and do some basic validation. + + Args: + server_name (str): server name to parse + + Returns: + Tuple[str, int|None]: host/port parts. + + Raises: + ValueError if the server name could not be parsed. + """ + host, port = parse_server_name(server_name) + + # these tests don't need to be bulletproof as we'll find out soon enough + # if somebody is giving us invalid data. What we *do* need is to be sure + # that nobody is sneaking IP literals in that look like hostnames, etc. + + # look for ipv6 literals + if host[0] == "[": + if host[-1] != "]": + raise ValueError("Mismatched [...] in server name '%s'" % (server_name,)) + return host, port + + # otherwise it should only be alphanumerics. + if not VALID_HOST_REGEX.match(host): + raise ValueError( + "Server name '%s' contains invalid characters" % (server_name,) + ) + + return host, port + + def shortstr(iterable: Iterable, maxitems: int = 5) -> str: """If iterable has maxitems or fewer, return the stringification of a list containing those items. diff --git a/tests/http/test_endpoint.py b/tests/http/test_endpoint.py index b2e9533b07ca..d06ea518cee6 100644 --- a/tests/http/test_endpoint.py +++ b/tests/http/test_endpoint.py @@ -12,7 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from synapse.http.endpoint import parse_and_validate_server_name, parse_server_name +from synapse.util.stringutils import parse_and_validate_server_name, parse_server_name from tests import unittest From abdac9a077c201a055bbf61b03be4fbddd2e0a6c Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 18 Jan 2021 18:04:28 +0000 Subject: [PATCH 2/8] add some type hits to stringutils.py --- mypy.ini | 1 + synapse/util/stringutils.py | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mypy.ini b/mypy.ini index b996867121e5..bd99069c8108 100644 --- a/mypy.ini +++ b/mypy.ini @@ -100,6 +100,7 @@ files = synapse/util/async_helpers.py, synapse/util/caches, synapse/util/metrics.py, + synapse/util/stringutils.py, tests/replication, tests/test_utils, tests/handlers/test_password_providers.py, diff --git a/synapse/util/stringutils.py b/synapse/util/stringutils.py index 12d64f20af79..b4e789307dca 100644 --- a/synapse/util/stringutils.py +++ b/synapse/util/stringutils.py @@ -18,6 +18,7 @@ import re import string from collections.abc import Iterable +from typing import Optional, Tuple from synapse.api.errors import Codes, SynapseError @@ -59,14 +60,14 @@ def assert_valid_client_secret(client_secret): ) -def parse_server_name(server_name): +def parse_server_name(server_name: str) -> Tuple[str, Optional[int]]: """Split a server name into host/port parts. Args: - server_name (str): server name to parse + server_name: server name to parse Returns: - Tuple[str, int|None]: host/port parts. + host/port parts. Raises: ValueError if the server name could not be parsed. @@ -87,14 +88,14 @@ def parse_server_name(server_name): VALID_HOST_REGEX = re.compile("\\A[0-9a-zA-Z.-]+\\Z") -def parse_and_validate_server_name(server_name): +def parse_and_validate_server_name(server_name: str) -> Tuple[str, Optional[int]]: """Split a server name into host/port parts and do some basic validation. Args: - server_name (str): server name to parse + server_name: server name to parse Returns: - Tuple[str, int|None]: host/port parts. + host/port parts. Raises: ValueError if the server name could not be parsed. From 91636f7a16bfb1658a8ed04c906c30e953da30f0 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 18 Jan 2021 18:04:56 +0000 Subject: [PATCH 3/8] move mxc_re to stringutils.py --- synapse/storage/databases/main/room.py | 4 +--- synapse/util/stringutils.py | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py index 284f2ce77c29..90ee664b887a 100644 --- a/synapse/storage/databases/main/room.py +++ b/synapse/storage/databases/main/room.py @@ -16,7 +16,6 @@ import collections import logging -import re from abc import abstractmethod from enum import Enum from typing import Any, Dict, List, Optional, Tuple @@ -30,6 +29,7 @@ from synapse.types import JsonDict, ThirdPartyInstanceID from synapse.util import json_encoder from synapse.util.caches.descriptors import cached +from synapse.util.stringutils import mxc_re logger = logging.getLogger(__name__) @@ -660,8 +660,6 @@ def _get_media_mxcs_in_room_txn(self, txn, room_id): The local and remote media as a lists of tuples where the key is the hostname and the value is the media ID. """ - mxc_re = re.compile("^mxc://([^/]+)/([^/#?]+)") - sql = """ SELECT stream_ordering, json FROM events JOIN event_json USING (room_id, event_id) diff --git a/synapse/util/stringutils.py b/synapse/util/stringutils.py index b4e789307dca..f22741ab3fd9 100644 --- a/synapse/util/stringutils.py +++ b/synapse/util/stringutils.py @@ -27,6 +27,15 @@ # https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-register-email-requesttoken client_secret_regex = re.compile(r"^[0-9a-zA-Z\.\=\_\-]+$") +# https://matrix.org/docs/spec/client_server/r0.6.1#matrix-content-mxc-uris, +# together with https://github.com/matrix-org/matrix-doc/issues/2177 which basically +# says "there is no grammar for media ids" +# +# The server_name part of this is purposely lax: use parse_and_validate_mxc for +# additional validation. +# +mxc_re = re.compile("^mxc://([^/]+)/([^/#?]+)$") + # random_string and random_string_with_symbols are used for a range of things, # some cryptographically important, some less so. We use SystemRandom to make sure # we get cryptographically-secure randoms. From 12aea6f1888a5588a70372ae298db1e552fc253d Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 18 Jan 2021 18:20:01 +0000 Subject: [PATCH 4/8] Add support for icons for identity providers --- docs/sample_config.yaml | 4 ++++ synapse/config/oidc_config.py | 20 ++++++++++++++++++ synapse/handlers/oidc_handler.py | 3 +++ synapse/handlers/sso.py | 5 +++++ .../res/templates/sso_login_idp_picker.html | 3 +++ synapse/util/stringutils.py | 21 +++++++++++++++++++ 6 files changed, 56 insertions(+) diff --git a/docs/sample_config.yaml b/docs/sample_config.yaml index 7fdd798d7041..b49a5da8cc27 100644 --- a/docs/sample_config.yaml +++ b/docs/sample_config.yaml @@ -1726,6 +1726,10 @@ saml2_config: # idp_name: A user-facing name for this identity provider, which is used to # offer the user a choice of login mechanisms. # +# idp_icon: An optional icon for this identity provider, which is presented +# by identity picker pages. If given, must be an MXC URI of the format +# mxc:/// +# # discover: set to 'false' to disable the use of the OIDC discovery mechanism # to discover endpoints. Defaults to true. # diff --git a/synapse/config/oidc_config.py b/synapse/config/oidc_config.py index df55367434af..f257fcd412ba 100644 --- a/synapse/config/oidc_config.py +++ b/synapse/config/oidc_config.py @@ -23,6 +23,7 @@ from synapse.python_dependencies import DependencyException, check_requirements from synapse.types import Collection, JsonDict from synapse.util.module_loader import load_module +from synapse.util.stringutils import parse_and_validate_mxc_uri from ._base import Config, ConfigError @@ -66,6 +67,10 @@ def generate_config_section(self, config_dir_path, server_name, **kwargs): # idp_name: A user-facing name for this identity provider, which is used to # offer the user a choice of login mechanisms. # + # idp_icon: An optional icon for this identity provider, which is presented + # by identity picker pages. If given, must be an MXC URI of the format + # mxc:/// + # # discover: set to 'false' to disable the use of the OIDC discovery mechanism # to discover endpoints. Defaults to true. # @@ -207,6 +212,7 @@ def generate_config_section(self, config_dir_path, server_name, **kwargs): "properties": { "idp_id": {"type": "string", "minLength": 1, "maxLength": 128}, "idp_name": {"type": "string"}, + "idp_icon": {"type": "string"}, "discover": {"type": "boolean"}, "issuer": {"type": "string"}, "client_id": {"type": "string"}, @@ -336,9 +342,20 @@ def _parse_oidc_config_dict( config_path + ("idp_id",), ) + # MSC2858 also specifies that the idp_icon must be a valid MXC uri + idp_icon = oidc_config.get("idp_icon") + if idp_icon is not None: + try: + parse_and_validate_mxc_uri(idp_icon) + except ValueError as e: + raise ConfigError( + "idp_icon must be a valid MXC URI", config_path + ("idp_icon",) + ) from e + return OidcProviderConfig( idp_id=idp_id, idp_name=oidc_config.get("idp_name", "OIDC"), + idp_icon=idp_icon, discover=oidc_config.get("discover", True), issuer=oidc_config["issuer"], client_id=oidc_config["client_id"], @@ -366,6 +383,9 @@ class OidcProviderConfig: # user-facing name for this identity provider. idp_name = attr.ib(type=str) + # Optional MXC URI for icon for this IdP. + idp_icon = attr.ib(type=Optional[str]) + # whether the OIDC discovery mechanism is used to discover endpoints discover = attr.ib(type=bool) diff --git a/synapse/handlers/oidc_handler.py b/synapse/handlers/oidc_handler.py index ba686d74b2f5..1607e1293592 100644 --- a/synapse/handlers/oidc_handler.py +++ b/synapse/handlers/oidc_handler.py @@ -271,6 +271,9 @@ def __init__( # user-facing name of this auth provider self.idp_name = provider.idp_name + # MXC URI for icon for this auth provider + self.idp_icon = provider.idp_icon + self._sso_handler = hs.get_sso_handler() self._sso_handler.register_identity_provider(self) diff --git a/synapse/handlers/sso.py b/synapse/handlers/sso.py index dcc85e9871e5..d493327a10d8 100644 --- a/synapse/handlers/sso.py +++ b/synapse/handlers/sso.py @@ -75,6 +75,11 @@ def idp_id(self) -> str: def idp_name(self) -> str: """User-facing name for this provider""" + @property + def idp_icon(self) -> Optional[str]: + """Optional MXC URI for user-facing icon""" + return None + @abc.abstractmethod async def handle_redirect_request( self, diff --git a/synapse/res/templates/sso_login_idp_picker.html b/synapse/res/templates/sso_login_idp_picker.html index f53c9cd67971..5b384810123f 100644 --- a/synapse/res/templates/sso_login_idp_picker.html +++ b/synapse/res/templates/sso_login_idp_picker.html @@ -17,6 +17,9 @@

{{server_name | e}} Login

  • +{% if p.idp_icon %} + +{% endif %}
  • {% endfor %} diff --git a/synapse/util/stringutils.py b/synapse/util/stringutils.py index f22741ab3fd9..42b1b1a35163 100644 --- a/synapse/util/stringutils.py +++ b/synapse/util/stringutils.py @@ -130,6 +130,27 @@ def parse_and_validate_server_name(server_name: str) -> Tuple[str, Optional[int] return host, port +def parse_and_validate_mxc_uri(mxc: str) -> Tuple[str, Optional[int], str]: + """Parse the given string as an MXC URI + + Checks that the "server name" part is a valid server name + + Args: + mxc: the (alleged) MXC URI to be checked + Returns: + hostname, port, media id + Raises: + ValueError if the URI cannot be parsed + """ + m = mxc_re.match(mxc) + if not m: + raise ValueError("mxc URI %r did not match expected format" % (mxc,)) + server_name = m.group(1) + media_id = m.group(2) + host, port = parse_and_validate_server_name(server_name) + return host, port, media_id + + def shortstr(iterable: Iterable, maxitems: int = 5) -> str: """If iterable has maxitems or fewer, return the stringification of a list containing those items. From 3b2bf8e27afbf82510e4eac2e047f0c61cac0a66 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 18 Jan 2021 18:25:38 +0000 Subject: [PATCH 5/8] changelog --- changelog.d/9154.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/9154.feature diff --git a/changelog.d/9154.feature b/changelog.d/9154.feature new file mode 100644 index 000000000000..01a24dcf4900 --- /dev/null +++ b/changelog.d/9154.feature @@ -0,0 +1 @@ +Add support for multiple SSO Identity Providers. From 50ef1ccd03203a141b1fac269cc68ff1abbab0e4 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 19 Jan 2021 15:06:01 +0000 Subject: [PATCH 6/8] Add missing idp_icon attributes to CasHandler and SamlHandler --- synapse/handlers/cas_handler.py | 4 ++++ synapse/handlers/saml_handler.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/synapse/handlers/cas_handler.py b/synapse/handlers/cas_handler.py index f3430c671399..0f342c607b27 100644 --- a/synapse/handlers/cas_handler.py +++ b/synapse/handlers/cas_handler.py @@ -80,6 +80,10 @@ def __init__(self, hs: "HomeServer"): # user-facing name of this auth provider self.idp_name = "CAS" + # we do not currently support icons for CAS auth, but this is required by + # the SsoIdentityProvider protocol type. + self.idp_icon = None + self._sso_handler = hs.get_sso_handler() self._sso_handler.register_identity_provider(self) diff --git a/synapse/handlers/saml_handler.py b/synapse/handlers/saml_handler.py index a8376543c9c1..38461cf79db8 100644 --- a/synapse/handlers/saml_handler.py +++ b/synapse/handlers/saml_handler.py @@ -78,6 +78,10 @@ def __init__(self, hs: "HomeServer"): # user-facing name of this auth provider self.idp_name = "SAML" + # we do not currently support icons for SAML auth, but this is required by + # the SsoIdentityProvider protocol type. + self.idp_icon = None + # a map from saml session id to Saml2SessionData object self._outstanding_requests_dict = {} # type: Dict[str, Saml2SessionData] From 19ff6b7fc3c0bbca92fe313a800d5059603a3ae3 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 20 Jan 2021 12:35:48 +0000 Subject: [PATCH 7/8] fix broken ref to parse_and_validate_server_name --- synapse/rest/client/v1/room.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/synapse/rest/client/v1/room.py b/synapse/rest/client/v1/room.py index e6725b03b02b..f95627ee615a 100644 --- a/synapse/rest/client/v1/room.py +++ b/synapse/rest/client/v1/room.py @@ -32,7 +32,6 @@ ) from synapse.api.filtering import Filter from synapse.events.utils import format_event_for_client_v2 -from synapse.http.endpoint import parse_and_validate_server_name from synapse.http.servlet import ( RestServlet, assert_params_in_dict, @@ -47,7 +46,7 @@ from synapse.streams.config import PaginationConfig from synapse.types import RoomAlias, RoomID, StreamToken, ThirdPartyInstanceID, UserID from synapse.util import json_decoder -from synapse.util.stringutils import random_string +from synapse.util.stringutils import parse_and_validate_server_name, random_string if TYPE_CHECKING: import synapse.server From 2a18da5dc3f6a345a94ba0188f6a586f298a55e9 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 20 Jan 2021 12:44:26 +0000 Subject: [PATCH 8/8] rename `mxc_re` -> `MXC_REGEX` --- synapse/storage/databases/main/room.py | 4 ++-- synapse/util/stringutils.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py index 90ee664b887a..a9fcb5f59c8a 100644 --- a/synapse/storage/databases/main/room.py +++ b/synapse/storage/databases/main/room.py @@ -29,7 +29,7 @@ from synapse.types import JsonDict, ThirdPartyInstanceID from synapse.util import json_encoder from synapse.util.caches.descriptors import cached -from synapse.util.stringutils import mxc_re +from synapse.util.stringutils import MXC_REGEX logger = logging.getLogger(__name__) @@ -686,7 +686,7 @@ def _get_media_mxcs_in_room_txn(self, txn, room_id): for url in (content_url, thumbnail_url): if not url: continue - matches = mxc_re.match(url) + matches = MXC_REGEX.match(url) if matches: hostname = matches.group(1) media_id = matches.group(2) diff --git a/synapse/util/stringutils.py b/synapse/util/stringutils.py index 42b1b1a35163..f8038bf86185 100644 --- a/synapse/util/stringutils.py +++ b/synapse/util/stringutils.py @@ -34,7 +34,7 @@ # The server_name part of this is purposely lax: use parse_and_validate_mxc for # additional validation. # -mxc_re = re.compile("^mxc://([^/]+)/([^/#?]+)$") +MXC_REGEX = re.compile("^mxc://([^/]+)/([^/#?]+)$") # random_string and random_string_with_symbols are used for a range of things, # some cryptographically important, some less so. We use SystemRandom to make sure @@ -142,7 +142,7 @@ def parse_and_validate_mxc_uri(mxc: str) -> Tuple[str, Optional[int], str]: Raises: ValueError if the URI cannot be parsed """ - m = mxc_re.match(mxc) + m = MXC_REGEX.match(mxc) if not m: raise ValueError("mxc URI %r did not match expected format" % (mxc,)) server_name = m.group(1)