mirror of
https://git.anonymousland.org/anonymousland/synapse-product.git
synced 2025-04-25 19:29:06 -04:00
Merge remote-tracking branch 'upstream/release-v1.27.0'
This commit is contained in:
commit
30ec814d3b
24
CHANGES.md
24
CHANGES.md
@ -1,7 +1,29 @@
|
|||||||
|
Synapse 1.27.0rc2 (2021-02-11)
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Further improvements to the user experience of registration via single sign-on. ([\#9297](https://github.com/matrix-org/synapse/issues/9297))
|
||||||
|
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Fix ratelimiting introduced in v1.27.0rc1 for invites to respect the `ratelimit` flag on application services. ([\#9302](https://github.com/matrix-org/synapse/issues/9302))
|
||||||
|
- Do not automatically calculate `public_baseurl` since it can be wrong in some situations. Reverts behaviour introduced in v1.26.0. ([\#9313](https://github.com/matrix-org/synapse/issues/9313))
|
||||||
|
|
||||||
|
|
||||||
|
Improved Documentation
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- Clarify the sample configuration for changes made to the template loading code. ([\#9310](https://github.com/matrix-org/synapse/issues/9310))
|
||||||
|
|
||||||
|
|
||||||
Synapse 1.27.0rc1 (2021-02-02)
|
Synapse 1.27.0rc1 (2021-02-02)
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Note that this release includes a change in Synapse to use Redis as a cache ─ as well as a pub/sub mechanism ─ if Redis support is enabled. No action is needed by server administrators, and we do not expect resource usage of the Redis instance to change dramatically.
|
Note that this release includes a change in Synapse to use Redis as a cache ─ as well as a pub/sub mechanism ─ if Redis support is enabled for workers. No action is needed by server administrators, and we do not expect resource usage of the Redis instance to change dramatically.
|
||||||
|
|
||||||
This release also changes the callback URI for OpenID Connect (OIDC) identity providers. If your server is configured to use single sign-on via an OIDC/OAuth2 IdP, you may need to make configuration changes. Please review [UPGRADE.rst](UPGRADE.rst) for more details on these changes.
|
This release also changes the callback URI for OpenID Connect (OIDC) identity providers. If your server is configured to use single sign-on via an OIDC/OAuth2 IdP, you may need to make configuration changes. Please review [UPGRADE.rst](UPGRADE.rst) for more details on these changes.
|
||||||
|
|
||||||
|
@ -74,10 +74,6 @@ pid_file: DATADIR/homeserver.pid
|
|||||||
# Otherwise, it should be the URL to reach Synapse's client HTTP listener (see
|
# Otherwise, it should be the URL to reach Synapse's client HTTP listener (see
|
||||||
# 'listeners' below).
|
# 'listeners' below).
|
||||||
#
|
#
|
||||||
# If this is left unset, it defaults to 'https://<server_name>/'. (Note that
|
|
||||||
# that will not work unless you configure Synapse or a reverse-proxy to listen
|
|
||||||
# on port 443.)
|
|
||||||
#
|
|
||||||
#public_baseurl: https://example.com/
|
#public_baseurl: https://example.com/
|
||||||
|
|
||||||
# Set the soft limit on the number of file descriptors synapse can use
|
# Set the soft limit on the number of file descriptors synapse can use
|
||||||
@ -1169,9 +1165,8 @@ account_validity:
|
|||||||
# send an email to the account's email address with a renewal link. By
|
# send an email to the account's email address with a renewal link. By
|
||||||
# default, no such emails are sent.
|
# default, no such emails are sent.
|
||||||
#
|
#
|
||||||
# If you enable this setting, you will also need to fill out the 'email'
|
# If you enable this setting, you will also need to fill out the 'email' and
|
||||||
# configuration section. You should also check that 'public_baseurl' is set
|
# 'public_baseurl' configuration sections.
|
||||||
# correctly.
|
|
||||||
#
|
#
|
||||||
#renew_at: 1w
|
#renew_at: 1w
|
||||||
|
|
||||||
@ -1262,7 +1257,8 @@ account_validity:
|
|||||||
# The identity server which we suggest that clients should use when users log
|
# The identity server which we suggest that clients should use when users log
|
||||||
# in on this server.
|
# in on this server.
|
||||||
#
|
#
|
||||||
# (By default, no suggestion is made, so it is left up to the client.)
|
# (By default, no suggestion is made, so it is left up to the client.
|
||||||
|
# This setting is ignored unless public_baseurl is also set.)
|
||||||
#
|
#
|
||||||
#default_identity_server: https://matrix.org
|
#default_identity_server: https://matrix.org
|
||||||
|
|
||||||
@ -1287,6 +1283,8 @@ account_validity:
|
|||||||
# by the Matrix Identity Service API specification:
|
# by the Matrix Identity Service API specification:
|
||||||
# https://matrix.org/docs/spec/identity_service/latest
|
# https://matrix.org/docs/spec/identity_service/latest
|
||||||
#
|
#
|
||||||
|
# If a delegate is specified, the config option public_baseurl must also be filled out.
|
||||||
|
#
|
||||||
account_threepid_delegates:
|
account_threepid_delegates:
|
||||||
#email: https://example.com # Delegate email sending to example.com
|
#email: https://example.com # Delegate email sending to example.com
|
||||||
#msisdn: http://localhost:8090 # Delegate SMS sending to this local process
|
#msisdn: http://localhost:8090 # Delegate SMS sending to this local process
|
||||||
@ -1938,9 +1936,9 @@ sso:
|
|||||||
# phishing attacks from evil.site. To avoid this, include a slash after the
|
# phishing attacks from evil.site. To avoid this, include a slash after the
|
||||||
# hostname: "https://my.client/".
|
# hostname: "https://my.client/".
|
||||||
#
|
#
|
||||||
# The login fallback page (used by clients that don't natively support the
|
# If public_baseurl is set, then the login fallback page (used by clients
|
||||||
# required login flows) is automatically whitelisted in addition to any URLs
|
# that don't natively support the required login flows) is whitelisted in
|
||||||
# in this list.
|
# addition to any URLs in this list.
|
||||||
#
|
#
|
||||||
# By default, this list is empty.
|
# By default, this list is empty.
|
||||||
#
|
#
|
||||||
@ -1961,8 +1959,7 @@ sso:
|
|||||||
#
|
#
|
||||||
# When rendering, this template is given the following variables:
|
# When rendering, this template is given the following variables:
|
||||||
# * redirect_url: the URL that the user will be redirected to after
|
# * redirect_url: the URL that the user will be redirected to after
|
||||||
# login. Needs manual escaping (see
|
# login.
|
||||||
# https://jinja.palletsprojects.com/en/2.11.x/templates/#html-escaping).
|
|
||||||
#
|
#
|
||||||
# * server_name: the homeserver's name.
|
# * server_name: the homeserver's name.
|
||||||
#
|
#
|
||||||
@ -2040,15 +2037,12 @@ sso:
|
|||||||
#
|
#
|
||||||
# When rendering, this template is given the following variables:
|
# When rendering, this template is given the following variables:
|
||||||
#
|
#
|
||||||
# * redirect_url: the URL the user is about to be redirected to. Needs
|
# * redirect_url: the URL the user is about to be redirected to.
|
||||||
# manual escaping (see
|
|
||||||
# https://jinja.palletsprojects.com/en/2.11.x/templates/#html-escaping).
|
|
||||||
#
|
#
|
||||||
# * display_url: the same as `redirect_url`, but with the query
|
# * display_url: the same as `redirect_url`, but with the query
|
||||||
# parameters stripped. The intention is to have a
|
# parameters stripped. The intention is to have a
|
||||||
# human-readable URL to show to users, not to use it as
|
# human-readable URL to show to users, not to use it as
|
||||||
# the final address to redirect to. Needs manual escaping
|
# the final address to redirect to.
|
||||||
# (see https://jinja.palletsprojects.com/en/2.11.x/templates/#html-escaping).
|
|
||||||
#
|
#
|
||||||
# * server_name: the homeserver's name.
|
# * server_name: the homeserver's name.
|
||||||
#
|
#
|
||||||
@ -2068,9 +2062,7 @@ sso:
|
|||||||
# process: 'sso_auth_confirm.html'.
|
# process: 'sso_auth_confirm.html'.
|
||||||
#
|
#
|
||||||
# When rendering, this template is given the following variables:
|
# When rendering, this template is given the following variables:
|
||||||
# * redirect_url: the URL the user is about to be redirected to. Needs
|
# * redirect_url: the URL the user is about to be redirected to.
|
||||||
# manual escaping (see
|
|
||||||
# https://jinja.palletsprojects.com/en/2.11.x/templates/#html-escaping).
|
|
||||||
#
|
#
|
||||||
# * description: the operation which the user is being asked to confirm
|
# * description: the operation which the user is being asked to confirm
|
||||||
#
|
#
|
||||||
|
@ -48,7 +48,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
__version__ = "1.27.0rc1"
|
__version__ = "1.27.0rc2"
|
||||||
|
|
||||||
if bool(os.environ.get("SYNAPSE_TEST_PATCH_LOG_CONTEXTS", False)):
|
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
|
# We import here so that we don't have to install a bunch of deps when
|
||||||
|
@ -42,6 +42,8 @@ class ConsentURIBuilder:
|
|||||||
"""
|
"""
|
||||||
if hs_config.form_secret is None:
|
if hs_config.form_secret is None:
|
||||||
raise ConfigError("form_secret not set in config")
|
raise ConfigError("form_secret not set in config")
|
||||||
|
if hs_config.public_baseurl is None:
|
||||||
|
raise ConfigError("public_baseurl not set in config")
|
||||||
|
|
||||||
self._hmac_secret = hs_config.form_secret.encode("utf-8")
|
self._hmac_secret = hs_config.form_secret.encode("utf-8")
|
||||||
self._public_baseurl = hs_config.public_baseurl
|
self._public_baseurl = hs_config.public_baseurl
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from ._base import Config
|
from ._base import Config, ConfigError
|
||||||
|
|
||||||
|
|
||||||
class CasConfig(Config):
|
class CasConfig(Config):
|
||||||
@ -30,13 +30,15 @@ class CasConfig(Config):
|
|||||||
|
|
||||||
if self.cas_enabled:
|
if self.cas_enabled:
|
||||||
self.cas_server_url = cas_config["server_url"]
|
self.cas_server_url = cas_config["server_url"]
|
||||||
public_base_url = cas_config.get("service_url") or self.public_baseurl
|
|
||||||
if public_base_url[-1] != "/":
|
# The public baseurl is required because it is used by the redirect
|
||||||
public_base_url += "/"
|
# template.
|
||||||
|
public_baseurl = self.public_baseurl
|
||||||
|
if not public_baseurl:
|
||||||
|
raise ConfigError("cas_config requires a public_baseurl to be set")
|
||||||
|
|
||||||
# TODO Update this to a _synapse URL.
|
# TODO Update this to a _synapse URL.
|
||||||
self.cas_service_url = (
|
self.cas_service_url = public_baseurl + "_matrix/client/r0/login/cas/ticket"
|
||||||
public_base_url + "_matrix/client/r0/login/cas/ticket"
|
|
||||||
)
|
|
||||||
self.cas_displayname_attribute = cas_config.get("displayname_attribute")
|
self.cas_displayname_attribute = cas_config.get("displayname_attribute")
|
||||||
self.cas_required_attributes = cas_config.get("required_attributes") or {}
|
self.cas_required_attributes = cas_config.get("required_attributes") or {}
|
||||||
else:
|
else:
|
||||||
|
@ -166,6 +166,11 @@ class EmailConfig(Config):
|
|||||||
if not self.email_notif_from:
|
if not self.email_notif_from:
|
||||||
missing.append("email.notif_from")
|
missing.append("email.notif_from")
|
||||||
|
|
||||||
|
# public_baseurl is required to build password reset and validation links that
|
||||||
|
# will be emailed to users
|
||||||
|
if config.get("public_baseurl") is None:
|
||||||
|
missing.append("public_baseurl")
|
||||||
|
|
||||||
if missing:
|
if missing:
|
||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
MISSING_PASSWORD_RESET_CONFIG_ERROR % (", ".join(missing),)
|
MISSING_PASSWORD_RESET_CONFIG_ERROR % (", ".join(missing),)
|
||||||
@ -264,6 +269,9 @@ class EmailConfig(Config):
|
|||||||
if not self.email_notif_from:
|
if not self.email_notif_from:
|
||||||
missing.append("email.notif_from")
|
missing.append("email.notif_from")
|
||||||
|
|
||||||
|
if config.get("public_baseurl") is None:
|
||||||
|
missing.append("public_baseurl")
|
||||||
|
|
||||||
if missing:
|
if missing:
|
||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
"email.enable_notifs is True but required keys are missing: %s"
|
"email.enable_notifs is True but required keys are missing: %s"
|
||||||
|
@ -53,7 +53,10 @@ class OIDCConfig(Config):
|
|||||||
"Multiple OIDC providers have the idp_id %r." % idp_id
|
"Multiple OIDC providers have the idp_id %r." % idp_id
|
||||||
)
|
)
|
||||||
|
|
||||||
self.oidc_callback_url = self.public_baseurl + "_synapse/client/oidc/callback"
|
public_baseurl = self.public_baseurl
|
||||||
|
if public_baseurl is None:
|
||||||
|
raise ConfigError("oidc_config requires a public_baseurl to be set")
|
||||||
|
self.oidc_callback_url = public_baseurl + "_synapse/client/oidc/callback"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def oidc_enabled(self) -> bool:
|
def oidc_enabled(self) -> bool:
|
||||||
|
@ -49,6 +49,10 @@ class AccountValidityConfig(Config):
|
|||||||
|
|
||||||
self.startup_job_max_delta = self.period * 10.0 / 100.0
|
self.startup_job_max_delta = self.period * 10.0 / 100.0
|
||||||
|
|
||||||
|
if self.renew_by_email_enabled:
|
||||||
|
if "public_baseurl" not in synapse_config:
|
||||||
|
raise ConfigError("Can't send renewal emails without 'public_baseurl'")
|
||||||
|
|
||||||
template_dir = config.get("template_dir")
|
template_dir = config.get("template_dir")
|
||||||
|
|
||||||
if not template_dir:
|
if not template_dir:
|
||||||
@ -105,6 +109,13 @@ class RegistrationConfig(Config):
|
|||||||
account_threepid_delegates = config.get("account_threepid_delegates") or {}
|
account_threepid_delegates = config.get("account_threepid_delegates") or {}
|
||||||
self.account_threepid_delegate_email = account_threepid_delegates.get("email")
|
self.account_threepid_delegate_email = account_threepid_delegates.get("email")
|
||||||
self.account_threepid_delegate_msisdn = account_threepid_delegates.get("msisdn")
|
self.account_threepid_delegate_msisdn = account_threepid_delegates.get("msisdn")
|
||||||
|
if self.account_threepid_delegate_msisdn and not self.public_baseurl:
|
||||||
|
raise ConfigError(
|
||||||
|
"The configuration option `public_baseurl` is required if "
|
||||||
|
"`account_threepid_delegate.msisdn` is set, such that "
|
||||||
|
"clients know where to submit validation tokens to. Please "
|
||||||
|
"configure `public_baseurl`."
|
||||||
|
)
|
||||||
|
|
||||||
self.default_identity_server = config.get("default_identity_server")
|
self.default_identity_server = config.get("default_identity_server")
|
||||||
self.allow_guest_access = config.get("allow_guest_access", False)
|
self.allow_guest_access = config.get("allow_guest_access", False)
|
||||||
@ -227,9 +238,8 @@ class RegistrationConfig(Config):
|
|||||||
# send an email to the account's email address with a renewal link. By
|
# send an email to the account's email address with a renewal link. By
|
||||||
# default, no such emails are sent.
|
# default, no such emails are sent.
|
||||||
#
|
#
|
||||||
# If you enable this setting, you will also need to fill out the 'email'
|
# If you enable this setting, you will also need to fill out the 'email' and
|
||||||
# configuration section. You should also check that 'public_baseurl' is set
|
# 'public_baseurl' configuration sections.
|
||||||
# correctly.
|
|
||||||
#
|
#
|
||||||
#renew_at: 1w
|
#renew_at: 1w
|
||||||
|
|
||||||
@ -320,7 +330,8 @@ class RegistrationConfig(Config):
|
|||||||
# The identity server which we suggest that clients should use when users log
|
# The identity server which we suggest that clients should use when users log
|
||||||
# in on this server.
|
# in on this server.
|
||||||
#
|
#
|
||||||
# (By default, no suggestion is made, so it is left up to the client.)
|
# (By default, no suggestion is made, so it is left up to the client.
|
||||||
|
# This setting is ignored unless public_baseurl is also set.)
|
||||||
#
|
#
|
||||||
#default_identity_server: https://matrix.org
|
#default_identity_server: https://matrix.org
|
||||||
|
|
||||||
@ -345,6 +356,8 @@ class RegistrationConfig(Config):
|
|||||||
# by the Matrix Identity Service API specification:
|
# by the Matrix Identity Service API specification:
|
||||||
# https://matrix.org/docs/spec/identity_service/latest
|
# https://matrix.org/docs/spec/identity_service/latest
|
||||||
#
|
#
|
||||||
|
# If a delegate is specified, the config option public_baseurl must also be filled out.
|
||||||
|
#
|
||||||
account_threepid_delegates:
|
account_threepid_delegates:
|
||||||
#email: https://example.com # Delegate email sending to example.com
|
#email: https://example.com # Delegate email sending to example.com
|
||||||
#msisdn: http://localhost:8090 # Delegate SMS sending to this local process
|
#msisdn: http://localhost:8090 # Delegate SMS sending to this local process
|
||||||
|
@ -189,6 +189,8 @@ class SAML2Config(Config):
|
|||||||
import saml2
|
import saml2
|
||||||
|
|
||||||
public_baseurl = self.public_baseurl
|
public_baseurl = self.public_baseurl
|
||||||
|
if public_baseurl is None:
|
||||||
|
raise ConfigError("saml2_config requires a public_baseurl to be set")
|
||||||
|
|
||||||
if self.saml2_grandfathered_mxid_source_attribute:
|
if self.saml2_grandfathered_mxid_source_attribute:
|
||||||
optional_attributes.add(self.saml2_grandfathered_mxid_source_attribute)
|
optional_attributes.add(self.saml2_grandfathered_mxid_source_attribute)
|
||||||
|
@ -161,11 +161,7 @@ class ServerConfig(Config):
|
|||||||
self.print_pidfile = config.get("print_pidfile")
|
self.print_pidfile = config.get("print_pidfile")
|
||||||
self.user_agent_suffix = config.get("user_agent_suffix")
|
self.user_agent_suffix = config.get("user_agent_suffix")
|
||||||
self.use_frozen_dicts = config.get("use_frozen_dicts", False)
|
self.use_frozen_dicts = config.get("use_frozen_dicts", False)
|
||||||
self.public_baseurl = config.get("public_baseurl") or "https://%s/" % (
|
self.public_baseurl = config.get("public_baseurl")
|
||||||
self.server_name,
|
|
||||||
)
|
|
||||||
if self.public_baseurl[-1] != "/":
|
|
||||||
self.public_baseurl += "/"
|
|
||||||
|
|
||||||
# Whether to enable user presence.
|
# Whether to enable user presence.
|
||||||
self.use_presence = config.get("use_presence", True)
|
self.use_presence = config.get("use_presence", True)
|
||||||
@ -321,6 +317,9 @@ class ServerConfig(Config):
|
|||||||
# Always blacklist 0.0.0.0, ::
|
# Always blacklist 0.0.0.0, ::
|
||||||
self.federation_ip_range_blacklist.update(["0.0.0.0", "::"])
|
self.federation_ip_range_blacklist.update(["0.0.0.0", "::"])
|
||||||
|
|
||||||
|
if self.public_baseurl is not None:
|
||||||
|
if self.public_baseurl[-1] != "/":
|
||||||
|
self.public_baseurl += "/"
|
||||||
self.start_pushers = config.get("start_pushers", True)
|
self.start_pushers = config.get("start_pushers", True)
|
||||||
|
|
||||||
# (undocumented) option for torturing the worker-mode replication a bit,
|
# (undocumented) option for torturing the worker-mode replication a bit,
|
||||||
@ -748,10 +747,6 @@ class ServerConfig(Config):
|
|||||||
# Otherwise, it should be the URL to reach Synapse's client HTTP listener (see
|
# Otherwise, it should be the URL to reach Synapse's client HTTP listener (see
|
||||||
# 'listeners' below).
|
# 'listeners' below).
|
||||||
#
|
#
|
||||||
# If this is left unset, it defaults to 'https://<server_name>/'. (Note that
|
|
||||||
# that will not work unless you configure Synapse or a reverse-proxy to listen
|
|
||||||
# on port 443.)
|
|
||||||
#
|
|
||||||
#public_baseurl: https://example.com/
|
#public_baseurl: https://example.com/
|
||||||
|
|
||||||
# Set the soft limit on the number of file descriptors synapse can use
|
# Set the soft limit on the number of file descriptors synapse can use
|
||||||
|
@ -64,6 +64,9 @@ class SSOConfig(Config):
|
|||||||
# gracefully to the client). This would make it pointless to ask the user for
|
# gracefully to the client). This would make it pointless to ask the user for
|
||||||
# confirmation, since the URL the confirmation page would be showing wouldn't be
|
# confirmation, since the URL the confirmation page would be showing wouldn't be
|
||||||
# the client's.
|
# the client's.
|
||||||
|
# public_baseurl is an optional setting, so we only add the fallback's URL to the
|
||||||
|
# list if it's provided (because we can't figure out what that URL is otherwise).
|
||||||
|
if self.public_baseurl:
|
||||||
login_fallback_url = self.public_baseurl + "_matrix/static/client/login"
|
login_fallback_url = self.public_baseurl + "_matrix/static/client/login"
|
||||||
self.sso_client_whitelist.append(login_fallback_url)
|
self.sso_client_whitelist.append(login_fallback_url)
|
||||||
|
|
||||||
@ -83,9 +86,9 @@ class SSOConfig(Config):
|
|||||||
# phishing attacks from evil.site. To avoid this, include a slash after the
|
# phishing attacks from evil.site. To avoid this, include a slash after the
|
||||||
# hostname: "https://my.client/".
|
# hostname: "https://my.client/".
|
||||||
#
|
#
|
||||||
# The login fallback page (used by clients that don't natively support the
|
# If public_baseurl is set, then the login fallback page (used by clients
|
||||||
# required login flows) is automatically whitelisted in addition to any URLs
|
# that don't natively support the required login flows) is whitelisted in
|
||||||
# in this list.
|
# addition to any URLs in this list.
|
||||||
#
|
#
|
||||||
# By default, this list is empty.
|
# By default, this list is empty.
|
||||||
#
|
#
|
||||||
@ -106,8 +109,7 @@ class SSOConfig(Config):
|
|||||||
#
|
#
|
||||||
# When rendering, this template is given the following variables:
|
# When rendering, this template is given the following variables:
|
||||||
# * redirect_url: the URL that the user will be redirected to after
|
# * redirect_url: the URL that the user will be redirected to after
|
||||||
# login. Needs manual escaping (see
|
# login.
|
||||||
# https://jinja.palletsprojects.com/en/2.11.x/templates/#html-escaping).
|
|
||||||
#
|
#
|
||||||
# * server_name: the homeserver's name.
|
# * server_name: the homeserver's name.
|
||||||
#
|
#
|
||||||
@ -185,15 +187,12 @@ class SSOConfig(Config):
|
|||||||
#
|
#
|
||||||
# When rendering, this template is given the following variables:
|
# When rendering, this template is given the following variables:
|
||||||
#
|
#
|
||||||
# * redirect_url: the URL the user is about to be redirected to. Needs
|
# * redirect_url: the URL the user is about to be redirected to.
|
||||||
# manual escaping (see
|
|
||||||
# https://jinja.palletsprojects.com/en/2.11.x/templates/#html-escaping).
|
|
||||||
#
|
#
|
||||||
# * display_url: the same as `redirect_url`, but with the query
|
# * display_url: the same as `redirect_url`, but with the query
|
||||||
# parameters stripped. The intention is to have a
|
# parameters stripped. The intention is to have a
|
||||||
# human-readable URL to show to users, not to use it as
|
# human-readable URL to show to users, not to use it as
|
||||||
# the final address to redirect to. Needs manual escaping
|
# the final address to redirect to.
|
||||||
# (see https://jinja.palletsprojects.com/en/2.11.x/templates/#html-escaping).
|
|
||||||
#
|
#
|
||||||
# * server_name: the homeserver's name.
|
# * server_name: the homeserver's name.
|
||||||
#
|
#
|
||||||
@ -213,9 +212,7 @@ class SSOConfig(Config):
|
|||||||
# process: 'sso_auth_confirm.html'.
|
# process: 'sso_auth_confirm.html'.
|
||||||
#
|
#
|
||||||
# When rendering, this template is given the following variables:
|
# When rendering, this template is given the following variables:
|
||||||
# * redirect_url: the URL the user is about to be redirected to. Needs
|
# * redirect_url: the URL the user is about to be redirected to.
|
||||||
# manual escaping (see
|
|
||||||
# https://jinja.palletsprojects.com/en/2.11.x/templates/#html-escaping).
|
|
||||||
#
|
#
|
||||||
# * description: the operation which the user is being asked to confirm
|
# * description: the operation which the user is being asked to confirm
|
||||||
#
|
#
|
||||||
|
@ -1619,7 +1619,9 @@ class FederationHandler(BaseHandler):
|
|||||||
|
|
||||||
# We retrieve the room member handler here as to not cause a cyclic dependency
|
# We retrieve the room member handler here as to not cause a cyclic dependency
|
||||||
member_handler = self.hs.get_room_member_handler()
|
member_handler = self.hs.get_room_member_handler()
|
||||||
member_handler.ratelimit_invite(event.room_id, event.state_key)
|
# We don't rate limit based on room ID, as that should be done by
|
||||||
|
# sending server.
|
||||||
|
member_handler.ratelimit_invite(None, event.state_key)
|
||||||
|
|
||||||
# keep a record of the room version, if we don't yet know it.
|
# keep a record of the room version, if we don't yet know it.
|
||||||
# (this may get overwritten if we later get a different room version in a
|
# (this may get overwritten if we later get a different room version in a
|
||||||
|
@ -504,6 +504,10 @@ class IdentityHandler(BaseHandler):
|
|||||||
except RequestTimedOutError:
|
except RequestTimedOutError:
|
||||||
raise SynapseError(500, "Timed out contacting identity server")
|
raise SynapseError(500, "Timed out contacting identity server")
|
||||||
|
|
||||||
|
# It is already checked that public_baseurl is configured since this code
|
||||||
|
# should only be used if account_threepid_delegate_msisdn is true.
|
||||||
|
assert self.hs.config.public_baseurl
|
||||||
|
|
||||||
# we need to tell the client to send the token back to us, since it doesn't
|
# we need to tell the client to send the token back to us, since it doesn't
|
||||||
# otherwise know where to send it, so add submit_url response parameter
|
# otherwise know where to send it, so add submit_url response parameter
|
||||||
# (see also MSC2078)
|
# (see also MSC2078)
|
||||||
|
@ -155,10 +155,14 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def ratelimit_invite(self, room_id: str, invitee_user_id: str):
|
def ratelimit_invite(self, room_id: Optional[str], invitee_user_id: str):
|
||||||
"""Ratelimit invites by room and by target user.
|
"""Ratelimit invites by room and by target user.
|
||||||
|
|
||||||
|
If room ID is missing then we just rate limit by target user.
|
||||||
"""
|
"""
|
||||||
|
if room_id:
|
||||||
self._invites_per_room_limiter.ratelimit(room_id)
|
self._invites_per_room_limiter.ratelimit(room_id)
|
||||||
|
|
||||||
self._invites_per_user_limiter.ratelimit(invitee_user_id)
|
self._invites_per_user_limiter.ratelimit(invitee_user_id)
|
||||||
|
|
||||||
async def _local_membership_update(
|
async def _local_membership_update(
|
||||||
@ -406,6 +410,8 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
|
|||||||
if effective_membership_state == Membership.INVITE:
|
if effective_membership_state == Membership.INVITE:
|
||||||
target_id = target.to_string()
|
target_id = target.to_string()
|
||||||
if ratelimit:
|
if ratelimit:
|
||||||
|
# Don't ratelimit application services.
|
||||||
|
if not requester.app_service or requester.app_service.is_rate_limited():
|
||||||
self.ratelimit_invite(room_id, target_id)
|
self.ratelimit_invite(room_id, target_id)
|
||||||
|
|
||||||
# block any attempts to invite the server notices mxid
|
# block any attempts to invite the server notices mxid
|
||||||
|
@ -18,6 +18,19 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.username_input.invalid {
|
||||||
|
border-color: #FE2928;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username_input.invalid input, .username_input.invalid label {
|
||||||
|
color: #FE2928;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username_input div, .username_input input {
|
||||||
|
line-height: 18px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.username_input label {
|
.username_input label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -8px;
|
top: -8px;
|
||||||
@ -78,6 +91,15 @@
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
padding: 0 14px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.error {
|
||||||
|
color: #FE2928;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -87,12 +109,13 @@
|
|||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<form method="post" class="form__input" id="form">
|
<form method="post" class="form__input" id="form">
|
||||||
<div class="username_input">
|
<div class="username_input" id="username_input">
|
||||||
<label for="field-username">Username</label>
|
<label for="field-username">Username</label>
|
||||||
<div class="prefix">@</div>
|
<div class="prefix">@</div>
|
||||||
<input type="text" name="username" id="field-username" autofocus required pattern="[a-z0-9\-=_\/\.]+">
|
<input type="text" name="username" id="field-username" autofocus>
|
||||||
<div class="postfix">:{{ server_name }}</div>
|
<div class="postfix">:{{ server_name }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<output for="username_input" id="field-username-output"></output>
|
||||||
<input type="submit" value="Continue" class="primary-button">
|
<input type="submit" value="Continue" class="primary-button">
|
||||||
{% if user_attributes %}
|
{% if user_attributes %}
|
||||||
<section class="idp-pick-details">
|
<section class="idp-pick-details">
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
const usernameField = document.getElementById("field-username");
|
const usernameField = document.getElementById("field-username");
|
||||||
|
const usernameOutput = document.getElementById("field-username-output");
|
||||||
|
const form = document.getElementById("form");
|
||||||
|
|
||||||
|
// needed to validate on change event when no input was changed
|
||||||
|
let needsValidation = true;
|
||||||
|
let isValid = false;
|
||||||
|
|
||||||
function throttle(fn, wait) {
|
function throttle(fn, wait) {
|
||||||
let timeout;
|
let timeout;
|
||||||
return function() {
|
const throttleFn = function() {
|
||||||
const args = Array.from(arguments);
|
const args = Array.from(arguments);
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
timeout = setTimeout(fn.bind.apply(fn, [null].concat(args)), wait);
|
timeout = setTimeout(fn.bind.apply(fn, [null].concat(args)), wait);
|
||||||
}
|
};
|
||||||
|
throttleFn.cancelQueued = function() {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
};
|
||||||
|
return throttleFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkUsernameAvailable(username) {
|
function checkUsernameAvailable(username) {
|
||||||
@ -16,14 +26,14 @@ function checkUsernameAvailable(username) {
|
|||||||
return fetch(check_uri, {
|
return fetch(check_uri, {
|
||||||
// include the cookie
|
// include the cookie
|
||||||
"credentials": "same-origin",
|
"credentials": "same-origin",
|
||||||
}).then((response) => {
|
}).then(function(response) {
|
||||||
if(!response.ok) {
|
if(!response.ok) {
|
||||||
// for non-200 responses, raise the body of the response as an exception
|
// for non-200 responses, raise the body of the response as an exception
|
||||||
return response.text().then((text) => { throw new Error(text); });
|
return response.text().then((text) => { throw new Error(text); });
|
||||||
} else {
|
} else {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
}).then((json) => {
|
}).then(function(json) {
|
||||||
if(json.error) {
|
if(json.error) {
|
||||||
return {message: json.error};
|
return {message: json.error};
|
||||||
} else if(json.available) {
|
} else if(json.available) {
|
||||||
@ -34,33 +44,49 @@ function checkUsernameAvailable(username) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allowedUsernameCharacters = new RegExp("^[a-z0-9\\.\\_\\-\\/\\=]+$");
|
||||||
|
const allowedCharactersString = "lowercase letters, digits, ., _, -, /, =";
|
||||||
|
|
||||||
|
function reportError(error) {
|
||||||
|
throttledCheckUsernameAvailable.cancelQueued();
|
||||||
|
usernameOutput.innerText = error;
|
||||||
|
usernameOutput.classList.add("error");
|
||||||
|
usernameField.parentElement.classList.add("invalid");
|
||||||
|
usernameField.focus();
|
||||||
|
}
|
||||||
|
|
||||||
function validateUsername(username) {
|
function validateUsername(username) {
|
||||||
usernameField.setCustomValidity("");
|
isValid = false;
|
||||||
if (usernameField.validity.valueMissing) {
|
needsValidation = false;
|
||||||
usernameField.setCustomValidity("Please provide a username");
|
usernameOutput.innerText = "";
|
||||||
return;
|
usernameField.parentElement.classList.remove("invalid");
|
||||||
|
usernameOutput.classList.remove("error");
|
||||||
|
if (!username) {
|
||||||
|
return reportError("Please provide a username");
|
||||||
}
|
}
|
||||||
if (usernameField.validity.patternMismatch) {
|
if (username.length > 255) {
|
||||||
usernameField.setCustomValidity("Invalid username, please only use " + allowedCharactersString);
|
return reportError("Too long, please choose something shorter");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
usernameField.setCustomValidity("Checking if username is available …");
|
if (!allowedUsernameCharacters.test(username)) {
|
||||||
|
return reportError("Invalid username, please only use " + allowedCharactersString);
|
||||||
|
}
|
||||||
|
usernameOutput.innerText = "Checking if username is available …";
|
||||||
throttledCheckUsernameAvailable(username);
|
throttledCheckUsernameAvailable(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
const throttledCheckUsernameAvailable = throttle(function(username) {
|
const throttledCheckUsernameAvailable = throttle(function(username) {
|
||||||
const handleError = function(err) {
|
const handleError = function(err) {
|
||||||
// don't prevent form submission on error
|
// don't prevent form submission on error
|
||||||
usernameField.setCustomValidity("");
|
usernameOutput.innerText = "";
|
||||||
console.log(err.message);
|
isValid = true;
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
checkUsernameAvailable(username).then(function(result) {
|
checkUsernameAvailable(username).then(function(result) {
|
||||||
if (!result.available) {
|
if (!result.available) {
|
||||||
usernameField.setCustomValidity(result.message);
|
reportError(result.message);
|
||||||
usernameField.reportValidity();
|
|
||||||
} else {
|
} else {
|
||||||
usernameField.setCustomValidity("");
|
isValid = true;
|
||||||
|
usernameOutput.innerText = "";
|
||||||
}
|
}
|
||||||
}, handleError);
|
}, handleError);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -68,9 +94,23 @@ const throttledCheckUsernameAvailable = throttle(function(username) {
|
|||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
|
form.addEventListener("submit", function(evt) {
|
||||||
|
if (needsValidation) {
|
||||||
|
validateUsername(usernameField.value);
|
||||||
|
evt.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isValid) {
|
||||||
|
evt.preventDefault();
|
||||||
|
usernameField.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
usernameField.addEventListener("input", function(evt) {
|
usernameField.addEventListener("input", function(evt) {
|
||||||
validateUsername(usernameField.value);
|
validateUsername(usernameField.value);
|
||||||
});
|
});
|
||||||
usernameField.addEventListener("change", function(evt) {
|
usernameField.addEventListener("change", function(evt) {
|
||||||
|
if (needsValidation) {
|
||||||
validateUsername(usernameField.value);
|
validateUsername(usernameField.value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -34,6 +34,10 @@ class WellKnownBuilder:
|
|||||||
self._config = hs.config
|
self._config = hs.config
|
||||||
|
|
||||||
def get_well_known(self):
|
def get_well_known(self):
|
||||||
|
# if we don't have a public_baseurl, we can't help much here.
|
||||||
|
if self._config.public_baseurl is None:
|
||||||
|
return None
|
||||||
|
|
||||||
result = {"m.homeserver": {"base_url": self._config.public_baseurl}}
|
result = {"m.homeserver": {"base_url": self._config.public_baseurl}}
|
||||||
|
|
||||||
if self._config.default_identity_server:
|
if self._config.default_identity_server:
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from typing import TYPE_CHECKING, Callable, Iterable, Union
|
from typing import TYPE_CHECKING, Callable, Iterable, Optional, Union
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
@ -74,14 +74,23 @@ def build_jinja_env(
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def _create_mxc_to_http_filter(public_baseurl: str) -> Callable:
|
def _create_mxc_to_http_filter(
|
||||||
|
public_baseurl: Optional[str],
|
||||||
|
) -> Callable[[str, int, int, str], str]:
|
||||||
"""Create and return a jinja2 filter that converts MXC urls to HTTP
|
"""Create and return a jinja2 filter that converts MXC urls to HTTP
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
public_baseurl: The public, accessible base URL of the homeserver
|
public_baseurl: The public, accessible base URL of the homeserver
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def mxc_to_http_filter(value, width, height, resize_method="crop"):
|
def mxc_to_http_filter(
|
||||||
|
value: str, width: int, height: int, resize_method: str = "crop"
|
||||||
|
) -> str:
|
||||||
|
if not public_baseurl:
|
||||||
|
raise RuntimeError(
|
||||||
|
"public_baseurl must be set in the homeserver config to convert MXC URLs to HTTP URLs."
|
||||||
|
)
|
||||||
|
|
||||||
if value[0:6] != "mxc://":
|
if value[0:6] != "mxc://":
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -191,53 +191,6 @@ class FederationTestCase(unittest.HomeserverTestCase):
|
|||||||
|
|
||||||
self.assertEqual(sg, sg2)
|
self.assertEqual(sg, sg2)
|
||||||
|
|
||||||
@unittest.override_config(
|
|
||||||
{"rc_invites": {"per_room": {"per_second": 0.5, "burst_count": 3}}}
|
|
||||||
)
|
|
||||||
def test_invite_by_room_ratelimit(self):
|
|
||||||
"""Tests that invites from federation in a room are actually rate-limited.
|
|
||||||
"""
|
|
||||||
other_server = "otherserver"
|
|
||||||
other_user = "@otheruser:" + other_server
|
|
||||||
|
|
||||||
# create the room
|
|
||||||
user_id = self.register_user("kermit", "test")
|
|
||||||
tok = self.login("kermit", "test")
|
|
||||||
room_id = self.helper.create_room_as(room_creator=user_id, tok=tok)
|
|
||||||
room_version = self.get_success(self.store.get_room_version(room_id))
|
|
||||||
|
|
||||||
def create_invite_for(local_user):
|
|
||||||
return event_from_pdu_json(
|
|
||||||
{
|
|
||||||
"type": EventTypes.Member,
|
|
||||||
"content": {"membership": "invite"},
|
|
||||||
"room_id": room_id,
|
|
||||||
"sender": other_user,
|
|
||||||
"state_key": local_user,
|
|
||||||
"depth": 32,
|
|
||||||
"prev_events": [],
|
|
||||||
"auth_events": [],
|
|
||||||
"origin_server_ts": self.clock.time_msec(),
|
|
||||||
},
|
|
||||||
room_version,
|
|
||||||
)
|
|
||||||
|
|
||||||
for i in range(3):
|
|
||||||
self.get_success(
|
|
||||||
self.handler.on_invite_request(
|
|
||||||
other_server,
|
|
||||||
create_invite_for("@user-%d:test" % (i,)),
|
|
||||||
room_version,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.get_failure(
|
|
||||||
self.handler.on_invite_request(
|
|
||||||
other_server, create_invite_for("@user-4:test"), room_version,
|
|
||||||
),
|
|
||||||
exc=LimitExceededError,
|
|
||||||
)
|
|
||||||
|
|
||||||
@unittest.override_config(
|
@unittest.override_config(
|
||||||
{"rc_invites": {"per_user": {"per_second": 0.5, "burst_count": 3}}}
|
{"rc_invites": {"per_user": {"per_second": 0.5, "burst_count": 3}}}
|
||||||
)
|
)
|
||||||
|
@ -672,10 +672,12 @@ class CASTestCase(unittest.HomeserverTestCase):
|
|||||||
self.redirect_path = "_synapse/client/login/sso/redirect/confirm"
|
self.redirect_path = "_synapse/client/login/sso/redirect/confirm"
|
||||||
|
|
||||||
config = self.default_config()
|
config = self.default_config()
|
||||||
|
config["public_baseurl"] = (
|
||||||
|
config.get("public_baseurl") or "https://matrix.goodserver.com:8448"
|
||||||
|
)
|
||||||
config["cas_config"] = {
|
config["cas_config"] = {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"server_url": CAS_SERVER,
|
"server_url": CAS_SERVER,
|
||||||
"service_url": "https://matrix.goodserver.com:8448",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cas_user_id = "username"
|
cas_user_id = "username"
|
||||||
|
@ -40,3 +40,12 @@ class WellKnownTests(unittest.HomeserverTestCase):
|
|||||||
"m.identity_server": {"base_url": "https://testis"},
|
"m.identity_server": {"base_url": "https://testis"},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_well_known_no_public_baseurl(self):
|
||||||
|
self.hs.config.public_baseurl = None
|
||||||
|
|
||||||
|
channel = self.make_request(
|
||||||
|
"GET", "/.well-known/matrix/client", shorthand=False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(channel.code, 404)
|
||||||
|
@ -159,6 +159,7 @@ def default_config(name, parse=False):
|
|||||||
},
|
},
|
||||||
"rc_3pid_validation": {"per_second": 10000, "burst_count": 10000},
|
"rc_3pid_validation": {"per_second": 10000, "burst_count": 10000},
|
||||||
"saml2_enabled": False,
|
"saml2_enabled": False,
|
||||||
|
"public_baseurl": None,
|
||||||
"default_identity_server": None,
|
"default_identity_server": None,
|
||||||
"key_refresh_interval": 24 * 60 * 60 * 1000,
|
"key_refresh_interval": 24 * 60 * 60 * 1000,
|
||||||
"old_signing_keys": {},
|
"old_signing_keys": {},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user