Merge remote-tracking branch 'origin/develop' into clokep/thread-api
This commit is contained in:
37
.github/workflows/latest_deps.yml
vendored
37
.github/workflows/latest_deps.yml
vendored
@@ -135,11 +135,42 @@ jobs:
|
||||
/logs/**/*.log*
|
||||
|
||||
|
||||
# TODO: run complement (as with twisted trunk, see #12473).
|
||||
complement:
|
||||
if: "${{ !failure() && !cancelled() }}"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# open an issue if the build fails, so we know about it.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arrangement: monolith
|
||||
database: SQLite
|
||||
|
||||
- arrangement: monolith
|
||||
database: Postgres
|
||||
|
||||
- arrangement: workers
|
||||
database: Postgres
|
||||
|
||||
steps:
|
||||
- name: Run actions/checkout@v2 for synapse
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: synapse
|
||||
|
||||
- name: Prepare Complement's Prerequisites
|
||||
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
||||
|
||||
- run: |
|
||||
set -o pipefail
|
||||
TEST_ONLY_IGNORE_POETRY_LOCKFILE=1 POSTGRES=${{ (matrix.database == 'Postgres') && 1 || '' }} WORKERS=${{ (matrix.arrangement == 'workers') && 1 || '' }} COMPLEMENT_DIR=`pwd`/complement synapse/scripts-dev/complement.sh -json 2>&1 | gotestfmt
|
||||
shell: bash
|
||||
name: Run Complement Tests
|
||||
|
||||
# Open an issue if the build fails, so we know about it.
|
||||
# Only do this if we're not experimenting with this action in a PR.
|
||||
open-issue:
|
||||
if: failure()
|
||||
if: "failure() && github.event_name != 'push' && github.event_name != 'pull_request'"
|
||||
needs:
|
||||
# TODO: should mypy be included here? It feels more brittle than the other two.
|
||||
- mypy
|
||||
|
||||
27
.github/workflows/tests.yml
vendored
27
.github/workflows/tests.yml
vendored
@@ -328,6 +328,9 @@ jobs:
|
||||
- arrangement: monolith
|
||||
database: Postgres
|
||||
|
||||
- arrangement: workers
|
||||
database: Postgres
|
||||
|
||||
steps:
|
||||
- name: Run actions/checkout@v2 for synapse
|
||||
uses: actions/checkout@v2
|
||||
@@ -343,30 +346,6 @@ jobs:
|
||||
shell: bash
|
||||
name: Run Complement Tests
|
||||
|
||||
# XXX When complement with workers is stable, move this back into the standard
|
||||
# "complement" matrix above.
|
||||
#
|
||||
# See https://github.com/matrix-org/synapse/issues/13161
|
||||
complement-workers:
|
||||
if: "${{ !failure() && !cancelled() }}"
|
||||
needs: linting-done
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Run actions/checkout@v2 for synapse
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: synapse
|
||||
|
||||
- name: Prepare Complement's Prerequisites
|
||||
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
||||
|
||||
- run: |
|
||||
set -o pipefail
|
||||
POSTGRES=1 WORKERS=1 COMPLEMENT_DIR=`pwd`/complement synapse/scripts-dev/complement.sh -json 2>&1 | gotestfmt
|
||||
shell: bash
|
||||
name: Run Complement Tests
|
||||
|
||||
# a job which marks all the other jobs as complete, thus allowing PRs to be merged.
|
||||
tests-done:
|
||||
if: ${{ always() }}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
Synapse 1.64.0rc2 (2022-07-29)
|
||||
==============================
|
||||
|
||||
This RC reintroduces support for `account_threepid_delegates.email`, which was removed in 1.64.0rc1. It remains deprecated and will be removed altogether in a future release. ([\#13406](https://github.com/matrix-org/synapse/issues/13406))
|
||||
|
||||
|
||||
Synapse 1.64.0rc1 (2022-07-26)
|
||||
==============================
|
||||
|
||||
As of this release, Synapse no longer allows the tasks of verifying email address ownership, and password reset confirmation, to be delegated to an identity server. For more information, see the [upgrade notes](https://matrix-org.github.io/synapse/v1.64/upgrade.html#upgrading-to-v1640).
|
||||
This RC removed the ability to delegate the tasks of verifying email address ownership, and password reset confirmation, to an identity server.
|
||||
|
||||
We have also stopped building `.deb` packages for Ubuntu 21.10 as it is no longer an active version of Ubuntu.
|
||||
|
||||
|
||||
1
changelog.d/13213.misc
Normal file
1
changelog.d/13213.misc
Normal file
@@ -0,0 +1 @@
|
||||
Enable Complement CI tests in the 'latest deps' test run.
|
||||
1
changelog.d/13221.doc
Normal file
1
changelog.d/13221.doc
Normal file
@@ -0,0 +1 @@
|
||||
Document which HTTP resources support gzip compression.
|
||||
1
changelog.d/13370.feature
Normal file
1
changelog.d/13370.feature
Normal file
@@ -0,0 +1 @@
|
||||
Use stable prefixes for [MSC3827](https://github.com/matrix-org/matrix-spec-proposals/pull/3827).
|
||||
1
changelog.d/13397.misc
Normal file
1
changelog.d/13397.misc
Normal file
@@ -0,0 +1 @@
|
||||
Adding missing type hints to tests.
|
||||
1
changelog.d/13413.bugfix
Normal file
1
changelog.d/13413.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Faster room joins: fix a bug which caused rejected events to become un-rejected during state syncing.
|
||||
1
changelog.d/13420.misc
Normal file
1
changelog.d/13420.misc
Normal file
@@ -0,0 +1 @@
|
||||
Re-enable running Complement tests against Synapse with workers.
|
||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@@ -1,3 +1,9 @@
|
||||
matrix-synapse-py3 (1.64.0~rc2) stable; urgency=medium
|
||||
|
||||
* New Synapse release 1.64.0rc2.
|
||||
|
||||
-- Synapse Packaging team <packages@matrix.org> Fri, 29 Jul 2022 12:22:53 +0100
|
||||
|
||||
matrix-synapse-py3 (1.64.0~rc1) stable; urgency=medium
|
||||
|
||||
* New Synapse release 1.64.0rc1.
|
||||
|
||||
@@ -68,7 +68,18 @@ COPY pyproject.toml poetry.lock /synapse/
|
||||
# reason, such as when a git repository is used directly as a dependency.
|
||||
ARG TEST_ONLY_SKIP_DEP_HASH_VERIFICATION
|
||||
|
||||
RUN /root/.local/bin/poetry export --extras all -o /synapse/requirements.txt ${TEST_ONLY_SKIP_DEP_HASH_VERIFICATION:+--without-hashes}
|
||||
# If specified, we won't use the Poetry lockfile.
|
||||
# Instead, we'll just install what a regular `pip install` would from PyPI.
|
||||
ARG TEST_ONLY_IGNORE_POETRY_LOCKFILE
|
||||
|
||||
# Export the dependencies, but only if we're actually going to use the Poetry lockfile.
|
||||
# Otherwise, just create an empty requirements file so that the Dockerfile can
|
||||
# proceed.
|
||||
RUN if [ -z "$TEST_ONLY_IGNORE_POETRY_LOCKFILE" ]; then \
|
||||
/root/.local/bin/poetry export --extras all -o /synapse/requirements.txt ${TEST_ONLY_SKIP_DEP_HASH_VERIFICATION:+--without-hashes}; \
|
||||
else \
|
||||
touch /synapse/requirements.txt; \
|
||||
fi
|
||||
|
||||
###
|
||||
### Stage 1: builder
|
||||
@@ -108,8 +119,17 @@ COPY synapse /synapse/synapse/
|
||||
# ... and what we need to `pip install`.
|
||||
COPY pyproject.toml README.rst /synapse/
|
||||
|
||||
# Repeat of earlier build argument declaration, as this is a new build stage.
|
||||
ARG TEST_ONLY_IGNORE_POETRY_LOCKFILE
|
||||
|
||||
# Install the synapse package itself.
|
||||
RUN pip install --prefix="/install" --no-deps --no-warn-script-location /synapse
|
||||
# If we have populated requirements.txt, we don't install any dependencies
|
||||
# as we should already have those from the previous `pip install` step.
|
||||
RUN if [ -z "$TEST_ONLY_IGNORE_POETRY_LOCKFILE" ]; then \
|
||||
pip install --prefix="/install" --no-deps --no-warn-script-location /synapse[all]; \
|
||||
else \
|
||||
pip install --prefix="/install" --no-warn-script-location /synapse[all]; \
|
||||
fi
|
||||
|
||||
###
|
||||
### Stage 2: runtime
|
||||
|
||||
@@ -91,19 +91,6 @@ process, for example:
|
||||
|
||||
# Upgrading to v1.64.0
|
||||
|
||||
## Delegation of email validation no longer supported
|
||||
|
||||
As of this version, Synapse no longer allows the tasks of verifying email address
|
||||
ownership, and password reset confirmation, to be delegated to an identity server.
|
||||
|
||||
To continue to allow users to add email addresses to their homeserver accounts,
|
||||
and perform password resets, make sure that Synapse is configured with a
|
||||
working email server in the `email` configuration section (including, at a
|
||||
minimum, a `notif_from` setting.)
|
||||
|
||||
Specifying an `email` setting under `account_threepid_delegates` will now cause
|
||||
an error at startup.
|
||||
|
||||
## Changes to the event replication streams
|
||||
|
||||
Synapse now includes a flag indicating if an event is an outlier when
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,7 +54,7 @@ skip_gitignore = true
|
||||
|
||||
[tool.poetry]
|
||||
name = "matrix-synapse"
|
||||
version = "1.64.0rc1"
|
||||
version = "1.64.0rc2"
|
||||
description = "Homeserver for the Matrix decentralised comms protocol"
|
||||
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -101,6 +101,7 @@ if [ -z "$skip_docker_build" ]; then
|
||||
echo_if_github "::group::Build Docker image: matrixdotorg/synapse"
|
||||
docker build -t matrixdotorg/synapse \
|
||||
--build-arg TEST_ONLY_SKIP_DEP_HASH_VERIFICATION \
|
||||
--build-arg TEST_ONLY_IGNORE_POETRY_LOCKFILE \
|
||||
-f "docker/Dockerfile" .
|
||||
echo_if_github "::endgroup::"
|
||||
|
||||
|
||||
@@ -268,4 +268,4 @@ class PublicRoomsFilterFields:
|
||||
"""
|
||||
|
||||
GENERIC_SEARCH_TERM: Final = "generic_search_term"
|
||||
ROOM_TYPES: Final = "org.matrix.msc3827.room_types"
|
||||
ROOM_TYPES: Final = "room_types"
|
||||
|
||||
@@ -44,6 +44,7 @@ from synapse.app._base import (
|
||||
register_start,
|
||||
)
|
||||
from synapse.config._base import ConfigError, format_config_error
|
||||
from synapse.config.emailconfig import ThreepidBehaviour
|
||||
from synapse.config.homeserver import HomeServerConfig
|
||||
from synapse.config.server import ListenerConfig
|
||||
from synapse.federation.transport.server import TransportLayerServer
|
||||
@@ -201,7 +202,7 @@ class SynapseHomeServer(HomeServer):
|
||||
}
|
||||
)
|
||||
|
||||
if self.config.email.can_verify_email:
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
|
||||
from synapse.rest.synapse.client.password_reset import (
|
||||
PasswordResetSubmitTokenResource,
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import email.utils
|
||||
import logging
|
||||
import os
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
import attr
|
||||
@@ -135,22 +136,40 @@ class EmailConfig(Config):
|
||||
|
||||
self.email_enable_notifs = email_config.get("enable_notifs", False)
|
||||
|
||||
self.threepid_behaviour_email = (
|
||||
# Have Synapse handle the email sending if account_threepid_delegates.email
|
||||
# is not defined
|
||||
# msisdn is currently always remote while Synapse does not support any method of
|
||||
# sending SMS messages
|
||||
ThreepidBehaviour.REMOTE
|
||||
if self.root.registration.account_threepid_delegate_email
|
||||
else ThreepidBehaviour.LOCAL
|
||||
)
|
||||
|
||||
if config.get("trust_identity_server_for_password_resets"):
|
||||
raise ConfigError(
|
||||
'The config option "trust_identity_server_for_password_resets" '
|
||||
"is no longer supported. Please remove it from the config file."
|
||||
'The config option "trust_identity_server_for_password_resets" has been removed.'
|
||||
"Please consult the configuration manual at docs/usage/configuration/config_documentation.md for "
|
||||
"details and update your config file."
|
||||
)
|
||||
|
||||
# If we have email config settings, assume that we can verify ownership of
|
||||
# email addresses.
|
||||
self.can_verify_email = email_config != {}
|
||||
self.local_threepid_handling_disabled_due_to_email_config = False
|
||||
if (
|
||||
self.threepid_behaviour_email == ThreepidBehaviour.LOCAL
|
||||
and email_config == {}
|
||||
):
|
||||
# We cannot warn the user this has happened here
|
||||
# Instead do so when a user attempts to reset their password
|
||||
self.local_threepid_handling_disabled_due_to_email_config = True
|
||||
|
||||
self.threepid_behaviour_email = ThreepidBehaviour.OFF
|
||||
|
||||
# Get lifetime of a validation token in milliseconds
|
||||
self.email_validation_token_lifetime = self.parse_duration(
|
||||
email_config.get("validation_token_lifetime", "1h")
|
||||
)
|
||||
|
||||
if self.can_verify_email:
|
||||
if self.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
|
||||
missing = []
|
||||
if not self.email_notif_from:
|
||||
missing.append("email.notif_from")
|
||||
@@ -341,3 +360,18 @@ class EmailConfig(Config):
|
||||
"Config option email.invite_client_location must be a http or https URL",
|
||||
path=("email", "invite_client_location"),
|
||||
)
|
||||
|
||||
|
||||
class ThreepidBehaviour(Enum):
|
||||
"""
|
||||
Enum to define the behaviour of Synapse with regards to when it contacts an identity
|
||||
server for 3pid registration and password resets
|
||||
|
||||
REMOTE = use an external server to send tokens
|
||||
LOCAL = send tokens ourselves
|
||||
OFF = disable registration via 3pid and password resets
|
||||
"""
|
||||
|
||||
REMOTE = "remote"
|
||||
LOCAL = "local"
|
||||
OFF = "off"
|
||||
|
||||
@@ -88,9 +88,6 @@ class ExperimentalConfig(Config):
|
||||
# MSC3715: dir param on /relations.
|
||||
self.msc3715_enabled: bool = experimental.get("msc3715_enabled", False)
|
||||
|
||||
# MSC3827: Filtering of /publicRooms by room type
|
||||
self.msc3827_enabled: bool = experimental.get("msc3827_enabled", False)
|
||||
|
||||
# MSC3848: Introduce errcodes for specific event sending failures
|
||||
self.msc3848_enabled: bool = experimental.get("msc3848_enabled", False)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import argparse
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
from synapse.api.constants import RoomCreationPreset
|
||||
@@ -20,11 +21,15 @@ from synapse.config._base import Config, ConfigError
|
||||
from synapse.types import JsonDict, RoomAlias, UserID
|
||||
from synapse.util.stringutils import random_string_with_symbols, strtobool
|
||||
|
||||
NO_EMAIL_DELEGATE_ERROR = """\
|
||||
Delegation of email verification to an identity server is no longer supported. To
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
LEGACY_EMAIL_DELEGATE_WARNING = """\
|
||||
Delegation of email verification to an identity server is now deprecated. To
|
||||
continue to allow users to add email addresses to their accounts, and use them for
|
||||
password resets, configure Synapse with an SMTP server via the `email` setting, and
|
||||
remove `account_threepid_delegates.email`.
|
||||
|
||||
This will be an error in a future version.
|
||||
"""
|
||||
|
||||
|
||||
@@ -59,8 +64,9 @@ class RegistrationConfig(Config):
|
||||
|
||||
account_threepid_delegates = config.get("account_threepid_delegates") or {}
|
||||
if "email" in account_threepid_delegates:
|
||||
raise ConfigError(NO_EMAIL_DELEGATE_ERROR)
|
||||
# self.account_threepid_delegate_email = account_threepid_delegates.get("email")
|
||||
logger.warning(LEGACY_EMAIL_DELEGATE_WARNING)
|
||||
|
||||
self.account_threepid_delegate_email = account_threepid_delegates.get("email")
|
||||
self.account_threepid_delegate_msisdn = account_threepid_delegates.get("msisdn")
|
||||
self.default_identity_server = config.get("default_identity_server")
|
||||
self.allow_guest_access = config.get("allow_guest_access", False)
|
||||
|
||||
@@ -581,6 +581,13 @@ class FederationEventHandler:
|
||||
event.event_id,
|
||||
)
|
||||
return
|
||||
|
||||
# since the state at this event has changed, we should now re-evaluate
|
||||
# whether it should have been rejected. We must already have all of the
|
||||
# auth events (from last time we went round this path), so there is no
|
||||
# need to pass the origin.
|
||||
await self._check_event_auth(None, event, context)
|
||||
|
||||
await self._store.update_state_for_partial_state_event(event, context)
|
||||
self._state_storage_controller.notify_event_un_partial_stated(
|
||||
event.event_id
|
||||
@@ -1624,13 +1631,15 @@ class FederationEventHandler:
|
||||
)
|
||||
|
||||
async def _check_event_auth(
|
||||
self, origin: str, event: EventBase, context: EventContext
|
||||
self, origin: Optional[str], event: EventBase, context: EventContext
|
||||
) -> None:
|
||||
"""
|
||||
Checks whether an event should be rejected (for failing auth checks).
|
||||
|
||||
Args:
|
||||
origin: The host the event originates from.
|
||||
origin: The host the event originates from. This is used to fetch
|
||||
any missing auth events. It can be set to None, but only if we are
|
||||
sure that we already have all the auth events.
|
||||
event: The event itself.
|
||||
context:
|
||||
The event context.
|
||||
@@ -1876,7 +1885,7 @@ class FederationEventHandler:
|
||||
event.internal_metadata.soft_failed = True
|
||||
|
||||
async def _load_or_fetch_auth_events_for_event(
|
||||
self, destination: str, event: EventBase
|
||||
self, destination: Optional[str], event: EventBase
|
||||
) -> Collection[EventBase]:
|
||||
"""Fetch this event's auth_events, from database or remote
|
||||
|
||||
@@ -1892,12 +1901,19 @@ class FederationEventHandler:
|
||||
Args:
|
||||
destination: where to send the /event_auth request. Typically the server
|
||||
that sent us `event` in the first place.
|
||||
|
||||
If this is None, no attempt is made to load any missing auth events:
|
||||
rather, an AssertionError is raised if there are any missing events.
|
||||
|
||||
event: the event whose auth_events we want
|
||||
|
||||
Returns:
|
||||
all of the events listed in `event.auth_events_ids`, after deduplication
|
||||
|
||||
Raises:
|
||||
AssertionError if some auth events were missing and no `destination` was
|
||||
supplied.
|
||||
|
||||
AuthError if we were unable to fetch the auth_events for any reason.
|
||||
"""
|
||||
event_auth_event_ids = set(event.auth_event_ids())
|
||||
@@ -1909,6 +1925,13 @@ class FederationEventHandler:
|
||||
)
|
||||
if not missing_auth_event_ids:
|
||||
return event_auth_events.values()
|
||||
if destination is None:
|
||||
# this shouldn't happen: destination must be set unless we know we have already
|
||||
# persisted the auth events.
|
||||
raise AssertionError(
|
||||
"_load_or_fetch_auth_events_for_event() called with no destination for "
|
||||
"an event with missing auth_events"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"Event %s refers to unknown auth events %s: fetching auth chain",
|
||||
|
||||
@@ -26,6 +26,7 @@ from synapse.api.errors import (
|
||||
SynapseError,
|
||||
)
|
||||
from synapse.api.ratelimiting import Ratelimiter
|
||||
from synapse.config.emailconfig import ThreepidBehaviour
|
||||
from synapse.http import RequestTimedOutError
|
||||
from synapse.http.client import SimpleHttpClient
|
||||
from synapse.http.site import SynapseRequest
|
||||
@@ -415,6 +416,48 @@ class IdentityHandler:
|
||||
|
||||
return session_id
|
||||
|
||||
async def request_email_token(
|
||||
self,
|
||||
id_server: str,
|
||||
email: str,
|
||||
client_secret: str,
|
||||
send_attempt: int,
|
||||
next_link: Optional[str] = None,
|
||||
) -> JsonDict:
|
||||
"""
|
||||
Request an external server send an email on our behalf for the purposes of threepid
|
||||
validation.
|
||||
|
||||
Args:
|
||||
id_server: The identity server to proxy to
|
||||
email: The email to send the message to
|
||||
client_secret: The unique client_secret sends by the user
|
||||
send_attempt: Which attempt this is
|
||||
next_link: A link to redirect the user to once they submit the token
|
||||
|
||||
Returns:
|
||||
The json response body from the server
|
||||
"""
|
||||
params = {
|
||||
"email": email,
|
||||
"client_secret": client_secret,
|
||||
"send_attempt": send_attempt,
|
||||
}
|
||||
if next_link:
|
||||
params["next_link"] = next_link
|
||||
|
||||
try:
|
||||
data = await self.http_client.post_json_get_json(
|
||||
id_server + "/_matrix/identity/api/v1/validate/email/requestToken",
|
||||
params,
|
||||
)
|
||||
return data
|
||||
except HttpResponseException as e:
|
||||
logger.info("Proxied requestToken failed: %r", e)
|
||||
raise e.to_synapse_error()
|
||||
except RequestTimedOutError:
|
||||
raise SynapseError(500, "Timed out contacting identity server")
|
||||
|
||||
async def requestMsisdnToken(
|
||||
self,
|
||||
id_server: str,
|
||||
@@ -488,7 +531,18 @@ class IdentityHandler:
|
||||
validation_session = None
|
||||
|
||||
# Try to validate as email
|
||||
if self.hs.config.email.can_verify_email:
|
||||
if self.hs.config.email.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
|
||||
# Remote emails will only be used if a valid identity server is provided.
|
||||
assert (
|
||||
self.hs.config.registration.account_threepid_delegate_email is not None
|
||||
)
|
||||
|
||||
# Ask our delegated email identity server
|
||||
validation_session = await self.threepid_from_creds(
|
||||
self.hs.config.registration.account_threepid_delegate_email,
|
||||
threepid_creds,
|
||||
)
|
||||
elif self.hs.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
|
||||
# Get a validated session matching these details
|
||||
validation_session = await self.store.get_threepid_validation_session(
|
||||
"email", client_secret, sid=sid, validated=True
|
||||
|
||||
@@ -182,7 +182,7 @@ class RoomListHandler:
|
||||
== HistoryVisibility.WORLD_READABLE,
|
||||
"guest_can_join": room["guest_access"] == "can_join",
|
||||
"join_rule": room["join_rules"],
|
||||
"org.matrix.msc3827.room_type": room["room_type"],
|
||||
"room_type": room["room_type"],
|
||||
}
|
||||
|
||||
# Filter out Nones – rather omit the field altogether
|
||||
|
||||
@@ -19,6 +19,7 @@ from twisted.web.client import PartialDownloadError
|
||||
|
||||
from synapse.api.constants import LoginType
|
||||
from synapse.api.errors import Codes, LoginError, SynapseError
|
||||
from synapse.config.emailconfig import ThreepidBehaviour
|
||||
from synapse.util import json_decoder
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -152,7 +153,7 @@ class _BaseThreepidAuthChecker:
|
||||
|
||||
logger.info("Getting validated threepid. threepidcreds: %r", (threepid_creds,))
|
||||
|
||||
# msisdns are currently always verified via the IS
|
||||
# msisdns are currently always ThreepidBehaviour.REMOTE
|
||||
if medium == "msisdn":
|
||||
if not self.hs.config.registration.account_threepid_delegate_msisdn:
|
||||
raise SynapseError(
|
||||
@@ -163,7 +164,18 @@ class _BaseThreepidAuthChecker:
|
||||
threepid_creds,
|
||||
)
|
||||
elif medium == "email":
|
||||
if self.hs.config.email.can_verify_email:
|
||||
if (
|
||||
self.hs.config.email.threepid_behaviour_email
|
||||
== ThreepidBehaviour.REMOTE
|
||||
):
|
||||
assert self.hs.config.registration.account_threepid_delegate_email
|
||||
threepid = await identity_handler.threepid_from_creds(
|
||||
self.hs.config.registration.account_threepid_delegate_email,
|
||||
threepid_creds,
|
||||
)
|
||||
elif (
|
||||
self.hs.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL
|
||||
):
|
||||
threepid = None
|
||||
row = await self.store.get_threepid_validation_session(
|
||||
medium,
|
||||
@@ -215,7 +227,10 @@ class EmailIdentityAuthChecker(UserInteractiveAuthChecker, _BaseThreepidAuthChec
|
||||
_BaseThreepidAuthChecker.__init__(self, hs)
|
||||
|
||||
def is_enabled(self) -> bool:
|
||||
return self.hs.config.email.can_verify_email
|
||||
return self.hs.config.email.threepid_behaviour_email in (
|
||||
ThreepidBehaviour.REMOTE,
|
||||
ThreepidBehaviour.LOCAL,
|
||||
)
|
||||
|
||||
async def check_auth(self, authdict: dict, clientip: str) -> Any:
|
||||
return await self._check_threepid("email", authdict)
|
||||
|
||||
@@ -28,6 +28,7 @@ from synapse.api.errors import (
|
||||
SynapseError,
|
||||
ThreepidValidationError,
|
||||
)
|
||||
from synapse.config.emailconfig import ThreepidBehaviour
|
||||
from synapse.handlers.ui_auth import UIAuthSessionDataConstants
|
||||
from synapse.http.server import HttpServer, finish_request, respond_with_html
|
||||
from synapse.http.servlet import (
|
||||
@@ -63,7 +64,7 @@ class EmailPasswordRequestTokenRestServlet(RestServlet):
|
||||
self.config = hs.config
|
||||
self.identity_handler = hs.get_identity_handler()
|
||||
|
||||
if self.config.email.can_verify_email:
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
|
||||
self.mailer = Mailer(
|
||||
hs=self.hs,
|
||||
app_name=self.config.email.email_app_name,
|
||||
@@ -72,10 +73,11 @@ class EmailPasswordRequestTokenRestServlet(RestServlet):
|
||||
)
|
||||
|
||||
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
||||
if not self.config.email.can_verify_email:
|
||||
logger.warning(
|
||||
"User password resets have been disabled due to lack of email config"
|
||||
)
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.OFF:
|
||||
if self.config.email.local_threepid_handling_disabled_due_to_email_config:
|
||||
logger.warning(
|
||||
"User password resets have been disabled due to lack of email config"
|
||||
)
|
||||
raise SynapseError(
|
||||
400, "Email-based password resets have been disabled on this server"
|
||||
)
|
||||
@@ -127,21 +129,35 @@ class EmailPasswordRequestTokenRestServlet(RestServlet):
|
||||
|
||||
raise SynapseError(400, "Email not found", Codes.THREEPID_NOT_FOUND)
|
||||
|
||||
# Send password reset emails from Synapse
|
||||
sid = await self.identity_handler.send_threepid_validation(
|
||||
email,
|
||||
client_secret,
|
||||
send_attempt,
|
||||
self.mailer.send_password_reset_mail,
|
||||
next_link,
|
||||
)
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
|
||||
assert self.hs.config.registration.account_threepid_delegate_email
|
||||
|
||||
# Have the configured identity server handle the request
|
||||
ret = await self.identity_handler.request_email_token(
|
||||
self.hs.config.registration.account_threepid_delegate_email,
|
||||
email,
|
||||
client_secret,
|
||||
send_attempt,
|
||||
next_link,
|
||||
)
|
||||
else:
|
||||
# Send password reset emails from Synapse
|
||||
sid = await self.identity_handler.send_threepid_validation(
|
||||
email,
|
||||
client_secret,
|
||||
send_attempt,
|
||||
self.mailer.send_password_reset_mail,
|
||||
next_link,
|
||||
)
|
||||
|
||||
# Wrap the session id in a JSON object
|
||||
ret = {"sid": sid}
|
||||
|
||||
threepid_send_requests.labels(type="email", reason="password_reset").observe(
|
||||
send_attempt
|
||||
)
|
||||
|
||||
# Wrap the session id in a JSON object
|
||||
return 200, {"sid": sid}
|
||||
return 200, ret
|
||||
|
||||
|
||||
class PasswordRestServlet(RestServlet):
|
||||
@@ -333,7 +349,7 @@ class EmailThreepidRequestTokenRestServlet(RestServlet):
|
||||
self.identity_handler = hs.get_identity_handler()
|
||||
self.store = self.hs.get_datastores().main
|
||||
|
||||
if self.config.email.can_verify_email:
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
|
||||
self.mailer = Mailer(
|
||||
hs=self.hs,
|
||||
app_name=self.config.email.email_app_name,
|
||||
@@ -342,10 +358,11 @@ class EmailThreepidRequestTokenRestServlet(RestServlet):
|
||||
)
|
||||
|
||||
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
||||
if not self.config.email.can_verify_email:
|
||||
logger.warning(
|
||||
"Adding emails have been disabled due to lack of an email config"
|
||||
)
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.OFF:
|
||||
if self.config.email.local_threepid_handling_disabled_due_to_email_config:
|
||||
logger.warning(
|
||||
"Adding emails have been disabled due to lack of an email config"
|
||||
)
|
||||
raise SynapseError(
|
||||
400, "Adding an email to your account is disabled on this server"
|
||||
)
|
||||
@@ -396,20 +413,35 @@ class EmailThreepidRequestTokenRestServlet(RestServlet):
|
||||
|
||||
raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE)
|
||||
|
||||
sid = await self.identity_handler.send_threepid_validation(
|
||||
email,
|
||||
client_secret,
|
||||
send_attempt,
|
||||
self.mailer.send_add_threepid_mail,
|
||||
next_link,
|
||||
)
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
|
||||
assert self.hs.config.registration.account_threepid_delegate_email
|
||||
|
||||
# Have the configured identity server handle the request
|
||||
ret = await self.identity_handler.request_email_token(
|
||||
self.hs.config.registration.account_threepid_delegate_email,
|
||||
email,
|
||||
client_secret,
|
||||
send_attempt,
|
||||
next_link,
|
||||
)
|
||||
else:
|
||||
# Send threepid validation emails from Synapse
|
||||
sid = await self.identity_handler.send_threepid_validation(
|
||||
email,
|
||||
client_secret,
|
||||
send_attempt,
|
||||
self.mailer.send_add_threepid_mail,
|
||||
next_link,
|
||||
)
|
||||
|
||||
# Wrap the session id in a JSON object
|
||||
ret = {"sid": sid}
|
||||
|
||||
threepid_send_requests.labels(type="email", reason="add_threepid").observe(
|
||||
send_attempt
|
||||
)
|
||||
|
||||
# Wrap the session id in a JSON object
|
||||
return 200, {"sid": sid}
|
||||
return 200, ret
|
||||
|
||||
|
||||
class MsisdnThreepidRequestTokenRestServlet(RestServlet):
|
||||
@@ -502,19 +534,25 @@ class AddThreepidEmailSubmitTokenServlet(RestServlet):
|
||||
self.config = hs.config
|
||||
self.clock = hs.get_clock()
|
||||
self.store = hs.get_datastores().main
|
||||
if self.config.email.can_verify_email:
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
|
||||
self._failure_email_template = (
|
||||
self.config.email.email_add_threepid_template_failure_html
|
||||
)
|
||||
|
||||
async def on_GET(self, request: Request) -> None:
|
||||
if not self.config.email.can_verify_email:
|
||||
logger.warning(
|
||||
"Adding emails have been disabled due to lack of an email config"
|
||||
)
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.OFF:
|
||||
if self.config.email.local_threepid_handling_disabled_due_to_email_config:
|
||||
logger.warning(
|
||||
"Adding emails have been disabled due to lack of an email config"
|
||||
)
|
||||
raise SynapseError(
|
||||
400, "Adding an email to your account is disabled on this server"
|
||||
)
|
||||
elif self.config.email.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
|
||||
raise SynapseError(
|
||||
400,
|
||||
"This homeserver is not validating threepids.",
|
||||
)
|
||||
|
||||
sid = parse_string(request, "sid", required=True)
|
||||
token = parse_string(request, "token", required=True)
|
||||
|
||||
@@ -31,6 +31,7 @@ from synapse.api.errors import (
|
||||
)
|
||||
from synapse.api.ratelimiting import Ratelimiter
|
||||
from synapse.config import ConfigError
|
||||
from synapse.config.emailconfig import ThreepidBehaviour
|
||||
from synapse.config.homeserver import HomeServerConfig
|
||||
from synapse.config.ratelimiting import FederationRateLimitConfig
|
||||
from synapse.config.server import is_threepid_reserved
|
||||
@@ -73,7 +74,7 @@ class EmailRegisterRequestTokenRestServlet(RestServlet):
|
||||
self.identity_handler = hs.get_identity_handler()
|
||||
self.config = hs.config
|
||||
|
||||
if self.hs.config.email.can_verify_email:
|
||||
if self.hs.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
|
||||
self.mailer = Mailer(
|
||||
hs=self.hs,
|
||||
app_name=self.config.email.email_app_name,
|
||||
@@ -82,10 +83,13 @@ class EmailRegisterRequestTokenRestServlet(RestServlet):
|
||||
)
|
||||
|
||||
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
||||
if not self.hs.config.email.can_verify_email:
|
||||
logger.warning(
|
||||
"Email registration has been disabled due to lack of email config"
|
||||
)
|
||||
if self.hs.config.email.threepid_behaviour_email == ThreepidBehaviour.OFF:
|
||||
if (
|
||||
self.hs.config.email.local_threepid_handling_disabled_due_to_email_config
|
||||
):
|
||||
logger.warning(
|
||||
"Email registration has been disabled due to lack of email config"
|
||||
)
|
||||
raise SynapseError(
|
||||
400, "Email-based registration has been disabled on this server"
|
||||
)
|
||||
@@ -134,21 +138,35 @@ class EmailRegisterRequestTokenRestServlet(RestServlet):
|
||||
|
||||
raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE)
|
||||
|
||||
# Send registration emails from Synapse
|
||||
sid = await self.identity_handler.send_threepid_validation(
|
||||
email,
|
||||
client_secret,
|
||||
send_attempt,
|
||||
self.mailer.send_registration_mail,
|
||||
next_link,
|
||||
)
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
|
||||
assert self.hs.config.registration.account_threepid_delegate_email
|
||||
|
||||
# Have the configured identity server handle the request
|
||||
ret = await self.identity_handler.request_email_token(
|
||||
self.hs.config.registration.account_threepid_delegate_email,
|
||||
email,
|
||||
client_secret,
|
||||
send_attempt,
|
||||
next_link,
|
||||
)
|
||||
else:
|
||||
# Send registration emails from Synapse,
|
||||
# wrapping the session id in a JSON object.
|
||||
ret = {
|
||||
"sid": await self.identity_handler.send_threepid_validation(
|
||||
email,
|
||||
client_secret,
|
||||
send_attempt,
|
||||
self.mailer.send_registration_mail,
|
||||
next_link,
|
||||
)
|
||||
}
|
||||
|
||||
threepid_send_requests.labels(type="email", reason="register").observe(
|
||||
send_attempt
|
||||
)
|
||||
|
||||
# Wrap the session id in a JSON object
|
||||
return 200, {"sid": sid}
|
||||
return 200, ret
|
||||
|
||||
|
||||
class MsisdnRegisterRequestTokenRestServlet(RestServlet):
|
||||
@@ -242,7 +260,7 @@ class RegistrationSubmitTokenServlet(RestServlet):
|
||||
self.clock = hs.get_clock()
|
||||
self.store = hs.get_datastores().main
|
||||
|
||||
if self.config.email.can_verify_email:
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
|
||||
self._failure_email_template = (
|
||||
self.config.email.email_registration_template_failure_html
|
||||
)
|
||||
@@ -252,10 +270,11 @@ class RegistrationSubmitTokenServlet(RestServlet):
|
||||
raise SynapseError(
|
||||
400, "This medium is currently not supported for registration"
|
||||
)
|
||||
if not self.config.email.can_verify_email:
|
||||
logger.warning(
|
||||
"User registration via email has been disabled due to lack of email config"
|
||||
)
|
||||
if self.config.email.threepid_behaviour_email == ThreepidBehaviour.OFF:
|
||||
if self.config.email.local_threepid_handling_disabled_due_to_email_config:
|
||||
logger.warning(
|
||||
"User registration via email has been disabled due to lack of email config"
|
||||
)
|
||||
raise SynapseError(
|
||||
400, "Email-based registration is disabled on this server"
|
||||
)
|
||||
|
||||
@@ -95,8 +95,8 @@ class VersionsRestServlet(RestServlet):
|
||||
"org.matrix.msc3026.busy_presence": self.config.experimental.msc3026_enabled,
|
||||
# Supports receiving private read receipts as per MSC2285
|
||||
"org.matrix.msc2285": self.config.experimental.msc2285_enabled,
|
||||
# Supports filtering of /publicRooms by room type MSC3827
|
||||
"org.matrix.msc3827": self.config.experimental.msc3827_enabled,
|
||||
# Supports filtering of /publicRooms by room type as per MSC3827
|
||||
"org.matrix.msc3827.stable": True,
|
||||
# Adds support for importing historical messages as per MSC2716
|
||||
"org.matrix.msc2716": self.config.experimental.msc2716_enabled,
|
||||
# Adds support for jump to date endpoints (/timestamp_to_event) as per MSC3030
|
||||
|
||||
@@ -17,6 +17,7 @@ from typing import TYPE_CHECKING, Tuple
|
||||
from twisted.web.server import Request
|
||||
|
||||
from synapse.api.errors import ThreepidValidationError
|
||||
from synapse.config.emailconfig import ThreepidBehaviour
|
||||
from synapse.http.server import DirectServeHtmlResource
|
||||
from synapse.http.servlet import parse_string
|
||||
from synapse.util.stringutils import assert_valid_client_secret
|
||||
@@ -45,6 +46,9 @@ class PasswordResetSubmitTokenResource(DirectServeHtmlResource):
|
||||
self.clock = hs.get_clock()
|
||||
self.store = hs.get_datastores().main
|
||||
|
||||
self._local_threepid_handling_disabled_due_to_email_config = (
|
||||
hs.config.email.local_threepid_handling_disabled_due_to_email_config
|
||||
)
|
||||
self._confirmation_email_template = (
|
||||
hs.config.email.email_password_reset_template_confirmation_html
|
||||
)
|
||||
@@ -55,8 +59,8 @@ class PasswordResetSubmitTokenResource(DirectServeHtmlResource):
|
||||
hs.config.email.email_password_reset_template_failure_html
|
||||
)
|
||||
|
||||
# This resource should only be mounted if email validation is enabled
|
||||
assert hs.config.email.can_verify_email
|
||||
# This resource should not be mounted if threepid behaviour is not LOCAL
|
||||
assert hs.config.email.threepid_behaviour_email == ThreepidBehaviour.LOCAL
|
||||
|
||||
async def _async_render_GET(self, request: Request) -> Tuple[int, bytes]:
|
||||
sid = parse_string(request, "sid", required=True)
|
||||
|
||||
@@ -207,7 +207,7 @@ class RoomWorkerStore(CacheInvalidationWorkerStore):
|
||||
def _construct_room_type_where_clause(
|
||||
self, room_types: Union[List[Union[str, None]], None]
|
||||
) -> Tuple[Union[str, None], List[str]]:
|
||||
if not room_types or not self.config.experimental.msc3827_enabled:
|
||||
if not room_types:
|
||||
return None, []
|
||||
else:
|
||||
# We use None when we want get rooms without a type
|
||||
|
||||
@@ -419,13 +419,15 @@ class StateGroupWorkerStore(EventsWorkerStore, SQLBaseStore):
|
||||
# anything that was rejected should have the same state as its
|
||||
# predecessor.
|
||||
if context.rejected:
|
||||
assert context.state_group == context.state_group_before_event
|
||||
state_group = context.state_group_before_event
|
||||
else:
|
||||
state_group = context.state_group
|
||||
|
||||
self.db_pool.simple_update_txn(
|
||||
txn,
|
||||
table="event_to_state_groups",
|
||||
keyvalues={"event_id": event.event_id},
|
||||
updatevalues={"state_group": context.state_group},
|
||||
updatevalues={"state_group": state_group},
|
||||
)
|
||||
|
||||
self.db_pool.simple_delete_one_txn(
|
||||
@@ -440,7 +442,7 @@ class StateGroupWorkerStore(EventsWorkerStore, SQLBaseStore):
|
||||
txn.call_after(
|
||||
self._get_state_group_for_event.prefill,
|
||||
(event.event_id,),
|
||||
context.state_group,
|
||||
state_group,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -481,17 +481,13 @@ class TestCreatePublishedRoomACL(unittest.HomeserverTestCase):
|
||||
|
||||
return config
|
||||
|
||||
def prepare(
|
||||
self, reactor: MemoryReactor, clock: Clock, hs: HomeServer
|
||||
) -> HomeServer:
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||
self.allowed_user_id = self.register_user(self.allowed_localpart, "pass")
|
||||
self.allowed_access_token = self.login(self.allowed_localpart, "pass")
|
||||
|
||||
self.denied_user_id = self.register_user("denied", "pass")
|
||||
self.denied_access_token = self.login("denied", "pass")
|
||||
|
||||
return hs
|
||||
|
||||
def test_denied_without_publication_permission(self) -> None:
|
||||
"""
|
||||
Try to create a room, register an alias for it, and publish it,
|
||||
@@ -575,9 +571,7 @@ class TestRoomListSearchDisabled(unittest.HomeserverTestCase):
|
||||
|
||||
servlets = [directory.register_servlets, room.register_servlets]
|
||||
|
||||
def prepare(
|
||||
self, reactor: MemoryReactor, clock: Clock, hs: HomeServer
|
||||
) -> HomeServer:
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||
room_id = self.helper.create_room_as(self.user_id)
|
||||
|
||||
channel = self.make_request(
|
||||
@@ -588,8 +582,6 @@ class TestRoomListSearchDisabled(unittest.HomeserverTestCase):
|
||||
self.room_list_handler = hs.get_room_list_handler()
|
||||
self.directory_handler = hs.get_directory_handler()
|
||||
|
||||
return hs
|
||||
|
||||
def test_disabling_room_list(self) -> None:
|
||||
self.room_list_handler.enable_room_list_search = True
|
||||
self.directory_handler.enable_room_list_search = True
|
||||
|
||||
@@ -586,9 +586,9 @@ class RegisterRestServletTestCase(unittest.HomeserverTestCase):
|
||||
"require_at_registration": True,
|
||||
},
|
||||
"account_threepid_delegates": {
|
||||
"email": "https://id_server",
|
||||
"msisdn": "https://id_server",
|
||||
},
|
||||
"email": {"notif_from": "Synapse <synapse@example.com>"},
|
||||
}
|
||||
)
|
||||
def test_advertised_flows_captcha_and_terms_and_3pids(self) -> None:
|
||||
|
||||
@@ -1060,6 +1060,7 @@ class BundledAggregationsTestCase(BaseRelationsTestCase):
|
||||
participated, bundled_aggregations.get("current_user_participated")
|
||||
)
|
||||
# The latest thread event has some fields that don't matter.
|
||||
self.assertIn("latest_event", bundled_aggregations)
|
||||
self.assert_dict(
|
||||
{
|
||||
"content": {
|
||||
@@ -1072,7 +1073,7 @@ class BundledAggregationsTestCase(BaseRelationsTestCase):
|
||||
"sender": self.user2_id,
|
||||
"type": "m.room.test",
|
||||
},
|
||||
bundled_aggregations.get("latest_event"),
|
||||
bundled_aggregations["latest_event"],
|
||||
)
|
||||
|
||||
return assert_thread
|
||||
@@ -1112,6 +1113,7 @@ class BundledAggregationsTestCase(BaseRelationsTestCase):
|
||||
self.assertEqual(2, bundled_aggregations.get("count"))
|
||||
self.assertTrue(bundled_aggregations.get("current_user_participated"))
|
||||
# The latest thread event has some fields that don't matter.
|
||||
self.assertIn("latest_event", bundled_aggregations)
|
||||
self.assert_dict(
|
||||
{
|
||||
"content": {
|
||||
@@ -1124,7 +1126,7 @@ class BundledAggregationsTestCase(BaseRelationsTestCase):
|
||||
"sender": self.user_id,
|
||||
"type": "m.room.test",
|
||||
},
|
||||
bundled_aggregations.get("latest_event"),
|
||||
bundled_aggregations["latest_event"],
|
||||
)
|
||||
# Check the unsigned field on the latest event.
|
||||
self.assert_dict(
|
||||
|
||||
@@ -496,7 +496,7 @@ class RoomStateTestCase(RoomBase):
|
||||
|
||||
self.assertEqual(HTTPStatus.OK, channel.code, msg=channel.result["body"])
|
||||
self.assertCountEqual(
|
||||
[state_event["type"] for state_event in channel.json_body],
|
||||
[state_event["type"] for state_event in channel.json_list],
|
||||
{
|
||||
"m.room.create",
|
||||
"m.room.power_levels",
|
||||
@@ -2070,7 +2070,6 @@ class PublicRoomsRoomTypeFilterTestCase(unittest.HomeserverTestCase):
|
||||
|
||||
config = self.default_config()
|
||||
config["allow_public_rooms_without_auth"] = True
|
||||
config["experimental_features"] = {"msc3827_enabled": True}
|
||||
self.hs = self.setup_test_homeserver(config=config)
|
||||
self.url = b"/_matrix/client/r0/publicRooms"
|
||||
|
||||
@@ -2123,13 +2122,13 @@ class PublicRoomsRoomTypeFilterTestCase(unittest.HomeserverTestCase):
|
||||
chunk, count = self.make_public_rooms_request([None])
|
||||
|
||||
self.assertEqual(count, 1)
|
||||
self.assertEqual(chunk[0].get("org.matrix.msc3827.room_type", None), None)
|
||||
self.assertEqual(chunk[0].get("room_type", None), None)
|
||||
|
||||
def test_returns_only_space_based_on_filter(self) -> None:
|
||||
chunk, count = self.make_public_rooms_request(["m.space"])
|
||||
|
||||
self.assertEqual(count, 1)
|
||||
self.assertEqual(chunk[0].get("org.matrix.msc3827.room_type", None), "m.space")
|
||||
self.assertEqual(chunk[0].get("room_type", None), "m.space")
|
||||
|
||||
def test_returns_both_rooms_and_space_based_on_filter(self) -> None:
|
||||
chunk, count = self.make_public_rooms_request(["m.space", None])
|
||||
|
||||
@@ -25,6 +25,7 @@ from typing import (
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
List,
|
||||
MutableMapping,
|
||||
Optional,
|
||||
Tuple,
|
||||
@@ -121,7 +122,15 @@ class FakeChannel:
|
||||
|
||||
@property
|
||||
def json_body(self) -> JsonDict:
|
||||
return json.loads(self.text_body)
|
||||
body = json.loads(self.text_body)
|
||||
assert isinstance(body, dict)
|
||||
return body
|
||||
|
||||
@property
|
||||
def json_list(self) -> List[JsonDict]:
|
||||
body = json.loads(self.text_body)
|
||||
assert isinstance(body, list)
|
||||
return body
|
||||
|
||||
@property
|
||||
def text_body(self) -> str:
|
||||
|
||||
@@ -28,6 +28,7 @@ from typing import (
|
||||
Generic,
|
||||
Iterable,
|
||||
List,
|
||||
NoReturn,
|
||||
Optional,
|
||||
Tuple,
|
||||
Type,
|
||||
@@ -39,7 +40,7 @@ from unittest.mock import Mock, patch
|
||||
import canonicaljson
|
||||
import signedjson.key
|
||||
import unpaddedbase64
|
||||
from typing_extensions import Protocol
|
||||
from typing_extensions import Concatenate, ParamSpec, Protocol
|
||||
|
||||
from twisted.internet.defer import Deferred, ensureDeferred
|
||||
from twisted.python.failure import Failure
|
||||
@@ -67,7 +68,7 @@ from synapse.logging.context import (
|
||||
from synapse.rest import RegisterServletsFunc
|
||||
from synapse.server import HomeServer
|
||||
from synapse.storage.keys import FetchKeyResult
|
||||
from synapse.types import JsonDict, UserID, create_requester
|
||||
from synapse.types import JsonDict, Requester, UserID, create_requester
|
||||
from synapse.util import Clock
|
||||
from synapse.util.httpresourcetree import create_resource_tree
|
||||
|
||||
@@ -88,6 +89,10 @@ setup_logging()
|
||||
TV = TypeVar("TV")
|
||||
_ExcType = TypeVar("_ExcType", bound=BaseException, covariant=True)
|
||||
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
S = TypeVar("S")
|
||||
|
||||
|
||||
class _TypedFailure(Generic[_ExcType], Protocol):
|
||||
"""Extension to twisted.Failure, where the 'value' has a certain type."""
|
||||
@@ -97,7 +102,7 @@ class _TypedFailure(Generic[_ExcType], Protocol):
|
||||
...
|
||||
|
||||
|
||||
def around(target):
|
||||
def around(target: TV) -> Callable[[Callable[Concatenate[S, P], R]], None]:
|
||||
"""A CLOS-style 'around' modifier, which wraps the original method of the
|
||||
given instance with another piece of code.
|
||||
|
||||
@@ -106,11 +111,11 @@ def around(target):
|
||||
return orig(*args, **kwargs)
|
||||
"""
|
||||
|
||||
def _around(code):
|
||||
def _around(code: Callable[Concatenate[S, P], R]) -> None:
|
||||
name = code.__name__
|
||||
orig = getattr(target, name)
|
||||
|
||||
def new(*args, **kwargs):
|
||||
def new(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
return code(orig, *args, **kwargs)
|
||||
|
||||
setattr(target, name, new)
|
||||
@@ -131,7 +136,7 @@ class TestCase(unittest.TestCase):
|
||||
level = getattr(method, "loglevel", getattr(self, "loglevel", None))
|
||||
|
||||
@around(self)
|
||||
def setUp(orig):
|
||||
def setUp(orig: Callable[[], R]) -> R:
|
||||
# if we're not starting in the sentinel logcontext, then to be honest
|
||||
# all future bets are off.
|
||||
if current_context():
|
||||
@@ -144,7 +149,7 @@ class TestCase(unittest.TestCase):
|
||||
if level is not None and old_level != level:
|
||||
|
||||
@around(self)
|
||||
def tearDown(orig):
|
||||
def tearDown(orig: Callable[[], R]) -> R:
|
||||
ret = orig()
|
||||
logging.getLogger().setLevel(old_level)
|
||||
return ret
|
||||
@@ -158,7 +163,7 @@ class TestCase(unittest.TestCase):
|
||||
return orig()
|
||||
|
||||
@around(self)
|
||||
def tearDown(orig):
|
||||
def tearDown(orig: Callable[[], R]) -> R:
|
||||
ret = orig()
|
||||
# force a GC to workaround problems with deferreds leaking logcontexts when
|
||||
# they are GCed (see the logcontext docs)
|
||||
@@ -167,7 +172,7 @@ class TestCase(unittest.TestCase):
|
||||
|
||||
return ret
|
||||
|
||||
def assertObjectHasAttributes(self, attrs, obj):
|
||||
def assertObjectHasAttributes(self, attrs: Dict[str, object], obj: object) -> None:
|
||||
"""Asserts that the given object has each of the attributes given, and
|
||||
that the value of each matches according to assertEqual."""
|
||||
for key in attrs.keys():
|
||||
@@ -178,12 +183,12 @@ class TestCase(unittest.TestCase):
|
||||
except AssertionError as e:
|
||||
raise (type(e))(f"Assert error for '.{key}':") from e
|
||||
|
||||
def assert_dict(self, required, actual):
|
||||
def assert_dict(self, required: dict, actual: dict) -> None:
|
||||
"""Does a partial assert of a dict.
|
||||
|
||||
Args:
|
||||
required (dict): The keys and value which MUST be in 'actual'.
|
||||
actual (dict): The test result. Extra keys will not be checked.
|
||||
required: The keys and value which MUST be in 'actual'.
|
||||
actual: The test result. Extra keys will not be checked.
|
||||
"""
|
||||
for key in required:
|
||||
self.assertEqual(
|
||||
@@ -191,31 +196,31 @@ class TestCase(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
def DEBUG(target):
|
||||
def DEBUG(target: TV) -> TV:
|
||||
"""A decorator to set the .loglevel attribute to logging.DEBUG.
|
||||
Can apply to either a TestCase or an individual test method."""
|
||||
target.loglevel = logging.DEBUG
|
||||
target.loglevel = logging.DEBUG # type: ignore[attr-defined]
|
||||
return target
|
||||
|
||||
|
||||
def INFO(target):
|
||||
def INFO(target: TV) -> TV:
|
||||
"""A decorator to set the .loglevel attribute to logging.INFO.
|
||||
Can apply to either a TestCase or an individual test method."""
|
||||
target.loglevel = logging.INFO
|
||||
target.loglevel = logging.INFO # type: ignore[attr-defined]
|
||||
return target
|
||||
|
||||
|
||||
def logcontext_clean(target):
|
||||
def logcontext_clean(target: TV) -> TV:
|
||||
"""A decorator which marks the TestCase or method as 'logcontext_clean'
|
||||
|
||||
... ie, any logcontext errors should cause a test failure
|
||||
"""
|
||||
|
||||
def logcontext_error(msg):
|
||||
def logcontext_error(msg: str) -> NoReturn:
|
||||
raise AssertionError("logcontext error: %s" % (msg))
|
||||
|
||||
patcher = patch("synapse.logging.context.logcontext_error", new=logcontext_error)
|
||||
return patcher(target)
|
||||
return patcher(target) # type: ignore[call-overload]
|
||||
|
||||
|
||||
class HomeserverTestCase(TestCase):
|
||||
@@ -255,7 +260,7 @@ class HomeserverTestCase(TestCase):
|
||||
method = getattr(self, methodName)
|
||||
self._extra_config = getattr(method, "_extra_config", None)
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self) -> None:
|
||||
"""
|
||||
Set up the TestCase by calling the homeserver constructor, optionally
|
||||
hijacking the authentication system to return a fixed user, and then
|
||||
@@ -306,7 +311,9 @@ class HomeserverTestCase(TestCase):
|
||||
)
|
||||
)
|
||||
|
||||
async def get_user_by_access_token(token=None, allow_guest=False):
|
||||
async def get_user_by_access_token(
|
||||
token: Optional[str] = None, allow_guest: bool = False
|
||||
) -> JsonDict:
|
||||
assert self.helper.auth_user_id is not None
|
||||
return {
|
||||
"user": UserID.from_string(self.helper.auth_user_id),
|
||||
@@ -314,7 +321,11 @@ class HomeserverTestCase(TestCase):
|
||||
"is_guest": False,
|
||||
}
|
||||
|
||||
async def get_user_by_req(request, allow_guest=False):
|
||||
async def get_user_by_req(
|
||||
request: SynapseRequest,
|
||||
allow_guest: bool = False,
|
||||
allow_expired: bool = False,
|
||||
) -> Requester:
|
||||
assert self.helper.auth_user_id is not None
|
||||
return create_requester(
|
||||
UserID.from_string(self.helper.auth_user_id),
|
||||
@@ -339,11 +350,11 @@ class HomeserverTestCase(TestCase):
|
||||
if hasattr(self, "prepare"):
|
||||
self.prepare(self.reactor, self.clock, self.hs)
|
||||
|
||||
def tearDown(self):
|
||||
def tearDown(self) -> None:
|
||||
# Reset to not use frozen dicts.
|
||||
events.USE_FROZEN_DICTS = False
|
||||
|
||||
def wait_on_thread(self, deferred, timeout=10):
|
||||
def wait_on_thread(self, deferred: Deferred, timeout: int = 10) -> None:
|
||||
"""
|
||||
Wait until a Deferred is done, where it's waiting on a real thread.
|
||||
"""
|
||||
@@ -374,7 +385,7 @@ class HomeserverTestCase(TestCase):
|
||||
clock (synapse.util.Clock): The Clock, associated with the reactor.
|
||||
|
||||
Returns:
|
||||
A homeserver (synapse.server.HomeServer) suitable for testing.
|
||||
A homeserver suitable for testing.
|
||||
|
||||
Function to be overridden in subclasses.
|
||||
"""
|
||||
@@ -408,7 +419,7 @@ class HomeserverTestCase(TestCase):
|
||||
"/_synapse/admin": servlet_resource,
|
||||
}
|
||||
|
||||
def default_config(self):
|
||||
def default_config(self) -> JsonDict:
|
||||
"""
|
||||
Get a default HomeServer config dict.
|
||||
"""
|
||||
@@ -421,7 +432,9 @@ class HomeserverTestCase(TestCase):
|
||||
|
||||
return config
|
||||
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, homeserver: HomeServer):
|
||||
def prepare(
|
||||
self, reactor: MemoryReactor, clock: Clock, homeserver: HomeServer
|
||||
) -> None:
|
||||
"""
|
||||
Prepare for the test. This involves things like mocking out parts of
|
||||
the homeserver, or building test data common across the whole test
|
||||
@@ -519,7 +532,7 @@ class HomeserverTestCase(TestCase):
|
||||
config_obj.parse_config_dict(config, "", "")
|
||||
kwargs["config"] = config_obj
|
||||
|
||||
async def run_bg_updates():
|
||||
async def run_bg_updates() -> None:
|
||||
with LoggingContext("run_bg_updates"):
|
||||
self.get_success(stor.db_pool.updates.run_background_updates(False))
|
||||
|
||||
@@ -538,11 +551,7 @@ class HomeserverTestCase(TestCase):
|
||||
"""
|
||||
self.reactor.pump([by] * 100)
|
||||
|
||||
def get_success(
|
||||
self,
|
||||
d: Awaitable[TV],
|
||||
by: float = 0.0,
|
||||
) -> TV:
|
||||
def get_success(self, d: Awaitable[TV], by: float = 0.0) -> TV:
|
||||
deferred: Deferred[TV] = ensureDeferred(d) # type: ignore[arg-type]
|
||||
self.pump(by=by)
|
||||
return self.successResultOf(deferred)
|
||||
@@ -755,7 +764,7 @@ class FederatingHomeserverTestCase(HomeserverTestCase):
|
||||
OTHER_SERVER_NAME = "other.example.com"
|
||||
OTHER_SERVER_SIGNATURE_KEY = signedjson.key.generate_signing_key("test")
|
||||
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer):
|
||||
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
|
||||
super().prepare(reactor, clock, hs)
|
||||
|
||||
# poke the other server's signing key into the key store, so that we don't
|
||||
@@ -879,7 +888,7 @@ def _auth_header_for_request(
|
||||
)
|
||||
|
||||
|
||||
def override_config(extra_config):
|
||||
def override_config(extra_config: JsonDict) -> Callable[[TV], TV]:
|
||||
"""A decorator which can be applied to test functions to give additional HS config
|
||||
|
||||
For use
|
||||
@@ -892,12 +901,13 @@ def override_config(extra_config):
|
||||
...
|
||||
|
||||
Args:
|
||||
extra_config(dict): Additional config settings to be merged into the default
|
||||
extra_config: Additional config settings to be merged into the default
|
||||
config dict before instantiating the test homeserver.
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
func._extra_config = extra_config
|
||||
def decorator(func: TV) -> TV:
|
||||
# This attribute is being defined.
|
||||
func._extra_config = extra_config # type: ignore[attr-defined]
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
Reference in New Issue
Block a user