1
0

Revert shadow HS support (#104)

Revert shadow HS support added in matrix-org/synapse#4145

Fixes matrix-org/matrix-dinsic#803

Part of that PR isn't reverted because it relates to matrix-org/matrix-dinsic#793
This commit is contained in:
Brendan Abolivier
2021-09-27 16:12:12 +02:00
committed by GitHub
parent 6b439a7d65
commit 95f29bce7f
9 changed files with 70 additions and 272 deletions

1
changelog.d/104.misc Normal file
View File

@@ -0,0 +1 @@
Remove shadow HS support.

View File

@@ -1343,15 +1343,6 @@ url_preview_accept_language:
#
#replicate_user_profiles_to: example.com
# If specified, attempt to replay registrations, profile changes & 3pid
# bindings on the given target homeserver via the AS API. The HS is authed
# via a given AS token.
#
#shadow_server:
# hs_url: https://shadow.example.com
# hs: shadow.example.com
# as_token: 12u394refgbdhivsia
# If enabled, don't let users set their own display names/avatars
# other than for the very first time (unless they are a server admin).
# Useful when provisioning users based on the contents of a 3rd party

View File

@@ -139,7 +139,6 @@ class RegistrationConfig(Config):
if not isinstance(self.replicate_user_profiles_to, list):
self.replicate_user_profiles_to = [self.replicate_user_profiles_to]
self.shadow_server = config.get("shadow_server", None)
self.rewrite_identity_server_urls = (
config.get("rewrite_identity_server_urls") or {}
)
@@ -312,15 +311,6 @@ class RegistrationConfig(Config):
#
#replicate_user_profiles_to: example.com
# If specified, attempt to replay registrations, profile changes & 3pid
# bindings on the given target homeserver via the AS API. The HS is authed
# via a given AS token.
#
#shadow_server:
# hs_url: https://shadow.example.com
# hs: shadow.example.com
# as_token: 12u394refgbdhivsia
# If enabled, don't let users set their own display names/avatars
# other than for the very first time (unless they are a server admin).
# Useful when provisioning users based on the contents of a 3rd party

View File

@@ -610,9 +610,7 @@ class RegistrationHandler(BaseHandler):
"""
await self._auto_join_rooms(user_id)
async def appservice_register(
self, user_localpart: str, as_token: str, password_hash: str, display_name: str
):
async def appservice_register(self, user_localpart: str, as_token: str):
# FIXME: this should be factored out and merged with normal register()
user = UserID(user_localpart, self.hs.hostname)
user_id = user.to_string()
@@ -630,26 +628,12 @@ class RegistrationHandler(BaseHandler):
self.check_user_id_not_appservice_exclusive(user_id, allowed_appservice=service)
display_name = display_name or user.localpart
await self.register_with_store(
user_id=user_id,
password_hash=password_hash,
password_hash="",
appservice_id=service_id,
create_profile_with_displayname=display_name,
create_profile_with_displayname=user.localpart,
)
requester = create_requester(user)
await self.profile_handler.set_displayname(
user, requester, display_name, by_admin=True
)
if self.hs.config.user_directory_search_all_users:
profile = await self.store.get_profileinfo(user_localpart)
await self.user_directory_handler.handle_local_profile_change(
user_id, profile
)
return user_id
def check_user_id_not_appservice_exclusive(
@@ -678,37 +662,6 @@ class RegistrationHandler(BaseHandler):
errcode=Codes.EXCLUSIVE,
)
async def shadow_register(self, localpart, display_name, auth_result, params):
"""Invokes the current registration on another server, using
shared secret registration, passing in any auth_results from
other registration UI auth flows (e.g. validated 3pids)
Useful for setting up shadow/backup accounts on a parallel deployment.
"""
# TODO: retries
shadow_hs_url = self.hs.config.shadow_server.get("hs_url")
as_token = self.hs.config.shadow_server.get("as_token")
await self.http_client.post_json_get_json(
"%s/_matrix/client/r0/register?access_token=%s" % (shadow_hs_url, as_token),
{
# XXX: auth_result is an unspecified extension for shadow registration
"auth_result": auth_result,
# XXX: another unspecified extension for shadow registration to ensure
# that the displayname is correctly set by the masters erver
"display_name": display_name,
"username": localpart,
"password": params.get("password"),
"bind_msisdn": params.get("bind_msisdn"),
"device_id": params.get("device_id"),
"initial_device_display_name": params.get(
"initial_device_display_name"
),
"inhibit_login": False,
"access_token": as_token,
},
)
async def check_registration_ratelimit(self, address: Optional[str]) -> None:
"""A simple helper method to check whether the registration rate limit has been hit
for a given IP address

View File

@@ -17,7 +17,7 @@ import logging
import random
import re
from http import HTTPStatus
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING
from urllib.parse import urlparse
from synapse.api.constants import LoginType
@@ -38,7 +38,6 @@ from synapse.http.servlet import (
)
from synapse.metrics import threepid_send_requests
from synapse.push.mailer import Mailer
from synapse.types import UserID
from synapse.util.msisdn import phone_number_to_msisdn
from synapse.util.stringutils import assert_valid_client_secret, random_string
from synapse.util.threepids import check_3pid_allowed, validate_email
@@ -196,31 +195,30 @@ class PasswordRestServlet(RestServlet):
if self.auth.has_access_token(request):
requester = await self.auth.get_user_by_req(request)
# blindly trust ASes without UI-authing them
if requester.app_service:
params = body
else:
try:
(
params,
session_id,
) = await self.auth_handler.validate_user_via_ui_auth(
requester,
request,
body,
"modify your account password",
try:
(
params,
session_id,
) = await self.auth_handler.validate_user_via_ui_auth(
requester,
request,
body,
"modify your account password",
)
except InteractiveAuthIncompleteError as e:
# The user needs to provide more steps to complete auth, but
# they're not required to provide the password again.
#
# If a password is available now, hash the provided password and
# store it for later.
if new_password:
password_hash = await self.auth_handler.hash(new_password)
await self.auth_handler.set_session_data(
e.session_id,
UIAuthSessionDataConstants.PASSWORD_HASH,
password_hash,
)
except InteractiveAuthIncompleteError as e:
# The user needs to provide more steps to complete auth, but
# they're not required to provide the password again.
#
# If a password is available now, hash the provided password and
# store it for later.
if new_password:
password_hash = await self.auth_handler.hash(new_password)
await self.auth_handler.set_session_data(
e.session_id, "password_hash", password_hash
)
raise
raise
user_id = requester.user.to_string()
else:
requester = None
@@ -290,28 +288,11 @@ class PasswordRestServlet(RestServlet):
user_id, password_hash, logout_devices, requester
)
if self.hs.config.shadow_server:
shadow_user = UserID(
requester.user.localpart, self.hs.config.shadow_server.get("hs")
)
await self.shadow_password(params, shadow_user.to_string())
return 200, {}
def on_OPTIONS(self, _):
return 200, {}
async def shadow_password(self, body, user_id):
# TODO: retries
shadow_hs_url = self.hs.config.shadow_server.get("hs_url")
as_token = self.hs.config.shadow_server.get("as_token")
await self.http_client.post_json_get_json(
"%s/_matrix/client/r0/account/password?access_token=%s&user_id=%s"
% (shadow_hs_url, as_token, user_id),
body,
)
class DeactivateAccountRestServlet(RestServlet):
PATTERNS = client_patterns("/account/deactivate$")
@@ -667,7 +648,6 @@ class ThreepidRestServlet(RestServlet):
self.auth = hs.get_auth()
self.auth_handler = hs.get_auth_handler()
self.datastore = hs.get_datastore()
self.http_client = hs.get_simple_http_client()
async def on_GET(self, request):
requester = await self.auth.get_user_by_req(request)
@@ -686,32 +666,6 @@ class ThreepidRestServlet(RestServlet):
user_id = requester.user.to_string()
body = parse_json_object_from_request(request)
# skip validation if this is a shadow 3PID from an AS
if requester.app_service:
# XXX: ASes pass in a validated threepid directly to bypass the IS.
# This makes the API entirely change shape when we have an AS token;
# it really should be an entirely separate API - perhaps
# /account/3pid/replicate or something.
threepid: Optional[dict] = body.get("threepid")
if not threepid:
raise SynapseError(400, "Missing param 'threepid'")
await self.auth_handler.add_threepid(
user_id,
threepid["medium"],
threepid["address"],
threepid["validated_at"],
)
if self.hs.config.shadow_server:
shadow_user = UserID(
requester.user.localpart, self.hs.config.shadow_server.get("hs")
)
await self.shadow_3pid({"threepid": threepid}, shadow_user.to_string())
return 200, {}
threepid_creds = body.get("threePidCreds") or body.get("three_pid_creds")
if threepid_creds is None:
raise SynapseError(
@@ -733,35 +687,12 @@ class ThreepidRestServlet(RestServlet):
validation_session["address"],
validation_session["validated_at"],
)
if self.hs.config.shadow_server:
shadow_user = UserID(
requester.user.localpart, self.hs.config.shadow_server.get("hs")
)
threepid = {
"medium": validation_session["medium"],
"address": validation_session["address"],
"validated_at": validation_session["validated_at"],
}
await self.shadow_3pid({"threepid": threepid}, shadow_user.to_string())
return 200, {}
raise SynapseError(
400, "No validated 3pid session found", Codes.THREEPID_AUTH_FAILED
)
async def shadow_3pid(self, body, user_id):
# TODO: retries
shadow_hs_url = self.hs.config.shadow_server.get("hs_url")
as_token = self.hs.config.shadow_server.get("as_token")
await self.http_client.post_json_get_json(
"%s/_matrix/client/r0/account/3pid?access_token=%s&user_id=%s"
% (shadow_hs_url, as_token, user_id),
body,
)
class ThreepidAddRestServlet(RestServlet):
PATTERNS = client_patterns("/account/3pid/add$")
@@ -807,33 +738,12 @@ class ThreepidAddRestServlet(RestServlet):
validation_session["address"],
validation_session["validated_at"],
)
if self.hs.config.shadow_server:
shadow_user = UserID(
requester.user.localpart, self.hs.config.shadow_server.get("hs")
)
threepid = {
"medium": validation_session["medium"],
"address": validation_session["address"],
"validated_at": validation_session["validated_at"],
}
await self.shadow_3pid({"threepid": threepid}, shadow_user.to_string())
return 200, {}
raise SynapseError(
400, "No validated 3pid session found", Codes.THREEPID_AUTH_FAILED
)
async def shadow_3pid(self, body, user_id):
# TODO: retries
shadow_hs_url = self.hs.config.shadow_server.get("hs_url")
as_token = self.hs.config.shadow_server.get("as_token")
await self.http_client.post_json_get_json(
"%s/_matrix/client/r0/account/3pid?access_token=%s&user_id=%s"
% (shadow_hs_url, as_token, user_id),
body,
)
class ThreepidBindRestServlet(RestServlet):
PATTERNS = client_patterns("/account/3pid/bind$")
@@ -903,7 +813,6 @@ class ThreepidDeleteRestServlet(RestServlet):
self.hs = hs
self.auth = hs.get_auth()
self.auth_handler = hs.get_auth_handler()
self.http_client = hs.get_simple_http_client()
async def on_POST(self, request):
if not self.hs.config.enable_3pid_changes:
@@ -928,12 +837,6 @@ class ThreepidDeleteRestServlet(RestServlet):
logger.exception("Failed to remove threepid")
raise SynapseError(500, "Failed to remove threepid")
if self.hs.config.shadow_server:
shadow_user = UserID(
requester.user.localpart, self.hs.config.shadow_server.get("hs")
)
await self.shadow_3pid_delete(body, shadow_user.to_string())
if ret:
id_server_unbind_result = "success"
else:
@@ -941,17 +844,6 @@ class ThreepidDeleteRestServlet(RestServlet):
return 200, {"id_server_unbind_result": id_server_unbind_result}
async def shadow_3pid_delete(self, body, user_id):
# TODO: retries
shadow_hs_url = self.hs.config.shadow_server.get("hs_url")
as_token = self.hs.config.shadow_server.get("as_token")
await self.http_client.post_json_get_json(
"%s/_matrix/client/r0/account/3pid/delete?access_token=%s&user_id=%s"
% (shadow_hs_url, as_token, user_id),
body,
)
class ThreepidLookupRestServlet(RestServlet):
PATTERNS = [re.compile("^/_matrix/client/unstable/account/3pid/lookup$")]

View File

@@ -13,7 +13,6 @@
# limitations under the License.
""" This module contains REST servlets to do with profile: /profile/<paths> """
from twisted.internet import defer
from synapse.api.errors import Codes, SynapseError
from synapse.http.servlet import RestServlet, parse_json_object_from_request
@@ -28,7 +27,6 @@ class ProfileDisplaynameRestServlet(RestServlet):
super().__init__()
self.hs = hs
self.profile_handler = hs.get_profile_handler()
self.http_client = hs.get_simple_http_client()
self.auth = hs.get_auth()
async def on_GET(self, request, user_id):
@@ -68,27 +66,11 @@ class ProfileDisplaynameRestServlet(RestServlet):
await self.profile_handler.set_displayname(user, requester, new_name, is_admin)
if self.hs.config.shadow_server:
shadow_user = UserID(user.localpart, self.hs.config.shadow_server.get("hs"))
self.shadow_displayname(shadow_user.to_string(), content)
return 200, {}
def on_OPTIONS(self, request, user_id):
return 200, {}
@defer.inlineCallbacks
def shadow_displayname(self, user_id, body):
# TODO: retries
shadow_hs_url = self.hs.config.shadow_server.get("hs_url")
as_token = self.hs.config.shadow_server.get("as_token")
yield self.http_client.put_json(
"%s/_matrix/client/r0/profile/%s/displayname?access_token=%s&user_id=%s"
% (shadow_hs_url, user_id, as_token, user_id),
body,
)
class ProfileAvatarURLRestServlet(RestServlet):
PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)/avatar_url", v1=True)
@@ -97,7 +79,6 @@ class ProfileAvatarURLRestServlet(RestServlet):
super().__init__()
self.hs = hs
self.profile_handler = hs.get_profile_handler()
self.http_client = hs.get_simple_http_client()
self.auth = hs.get_auth()
async def on_GET(self, request, user_id):
@@ -136,27 +117,11 @@ class ProfileAvatarURLRestServlet(RestServlet):
user, requester, new_avatar_url, is_admin
)
if self.hs.config.shadow_server:
shadow_user = UserID(user.localpart, self.hs.config.shadow_server.get("hs"))
self.shadow_avatar_url(shadow_user.to_string(), content)
return 200, {}
def on_OPTIONS(self, request, user_id):
return 200, {}
@defer.inlineCallbacks
def shadow_avatar_url(self, user_id, body):
# TODO: retries
shadow_hs_url = self.hs.config.shadow_server.get("hs_url")
as_token = self.hs.config.shadow_server.get("as_token")
yield self.http_client.put_json(
"%s/_matrix/client/r0/profile/%s/avatar_url?access_token=%s&user_id=%s"
% (shadow_hs_url, user_id, as_token, user_id),
body,
)
class ProfileRestServlet(RestServlet):
PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)", v1=True)

View File

@@ -481,8 +481,6 @@ class RegisterRestServlet(RestServlet):
result = await self._do_appservice_registration(
desired_username,
password,
desired_display_name,
access_token,
body,
should_issue_refresh_token=should_issue_refresh_token,
@@ -738,14 +736,6 @@ class RegisterRestServlet(RestServlet):
):
await self.store.upsert_monthly_active_user(registered_user_id)
if self.hs.config.shadow_server:
await self.registration_handler.shadow_register(
localpart=desired_username,
display_name=desired_display_name,
auth_result=auth_result,
params=params,
)
# Remember that the user account has been registered (and the user
# ID it was registered with, since it might not have been specified).
await self.auth_handler.set_session_data(
@@ -774,44 +764,20 @@ class RegisterRestServlet(RestServlet):
async def _do_appservice_registration(
self,
username,
password,
display_name,
as_token,
body,
should_issue_refresh_token: bool = False,
):
if password:
# Hash the password
#
# In mainline hashing of the password was moved further on in the registration
# flow, but we need it here for the AS use-case of shadow servers
password = await self.auth_handler.hash(password)
user_id = await self.registration_handler.appservice_register(
username, as_token, password, display_name
username, as_token
)
result = await self._create_registration_details(
return await self._create_registration_details(
user_id,
body,
is_appservice_ghost=True,
should_issue_refresh_token=should_issue_refresh_token,
)
auth_result = body.get("auth_result")
if auth_result and LoginType.EMAIL_IDENTITY in auth_result:
threepid = auth_result[LoginType.EMAIL_IDENTITY]
await self.registration_handler.register_email_threepid(
user_id, threepid, result["access_token"]
)
if auth_result and LoginType.MSISDN in auth_result:
threepid = auth_result[LoginType.MSISDN]
await self.registration_handler.register_msisdn_threepid(
user_id, threepid, result["access_token"]
)
return result
async def _create_registration_details(
self,
user_id: str,

View File

@@ -0,0 +1,29 @@
import time
from synapse.api.constants import EventTypes
from synapse.events import EventBase
from synapse.module_api import ModuleApi
from synapse.types import StateMap
class MySuperModule:
def __init__(self, config: dict, api: ModuleApi):
self.api = api
self.api.register_third_party_rules_callbacks(
check_event_allowed=self.check_event_allowed,
)
async def check_event_allowed(self, event: EventBase, state: StateMap[EventBase]):
if event.is_state() and event.type == EventTypes.Member:
await self.api.create_and_send_event_into_room(
{
"room_id": event.room_id,
"sender": event.sender,
"type": "bzh.abolivier.test3",
"content": {"now": int(time.time())},
"state_key": "",
}
)
return True, None

View File

@@ -0,0 +1,11 @@
from typing import Optional
from synapse.module_api import ModuleApi
class DummyAccountValidity:
def __init__(self, config: dict, api: ModuleApi):
api.register_account_validity_callbacks(is_user_expired=self.is_user_expired)
async def is_user_expired(self, user_id: str) -> Optional[bool]:
return False