Remove the experimental flag for knocking and use stable prefixes / endpoints. (#10167)

* Room version 7 for knocking.
* Stable prefixes and endpoints (both client and federation) for knocking.
* Removes the experimental configuration flag.
This commit is contained in:
Patrick Cloke 2021-06-15 07:45:14 -04:00 committed by GitHub
parent aac2c49b9b
commit 9e5ab6dd58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 33 additions and 96 deletions

View File

@ -0,0 +1 @@
Implement "room knocking" as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403). Contributed by Sorunome and anoa.

View File

@ -41,7 +41,7 @@ class Membership:
INVITE = "invite" INVITE = "invite"
JOIN = "join" JOIN = "join"
KNOCK = "xyz.amorgan.knock" KNOCK = "knock"
LEAVE = "leave" LEAVE = "leave"
BAN = "ban" BAN = "ban"
LIST = (INVITE, JOIN, KNOCK, LEAVE, BAN) LIST = (INVITE, JOIN, KNOCK, LEAVE, BAN)
@ -58,7 +58,7 @@ class PresenceState:
class JoinRules: class JoinRules:
PUBLIC = "public" PUBLIC = "public"
KNOCK = "xyz.amorgan.knock" KNOCK = "knock"
INVITE = "invite" INVITE = "invite"
PRIVATE = "private" PRIVATE = "private"
# As defined for MSC3083. # As defined for MSC3083.

View File

@ -180,9 +180,9 @@ class RoomVersions:
msc3083_join_rules=True, msc3083_join_rules=True,
msc2403_knocking=False, msc2403_knocking=False,
) )
MSC2403 = RoomVersion( V7 = RoomVersion(
"xyz.amorgan.knock", "7",
RoomDisposition.UNSTABLE, RoomDisposition.STABLE,
EventFormatVersions.V3, EventFormatVersions.V3,
StateResolutionVersions.V2, StateResolutionVersions.V2,
enforce_key_validity=True, enforce_key_validity=True,
@ -206,6 +206,7 @@ KNOWN_ROOM_VERSIONS = {
RoomVersions.V6, RoomVersions.V6,
RoomVersions.MSC2176, RoomVersions.MSC2176,
RoomVersions.MSC3083, RoomVersions.MSC3083,
RoomVersions.V7,
) )
# Note that we do not include MSC2043 here unless it is enabled in the config. # Note that we do not include MSC2043 here unless it is enabled in the config.
} # type: Dict[str, RoomVersion] } # type: Dict[str, RoomVersion]

View File

@ -12,7 +12,6 @@
# 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 synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersions
from synapse.config._base import Config from synapse.config._base import Config
from synapse.types import JsonDict from synapse.types import JsonDict
@ -30,9 +29,3 @@ class ExperimentalConfig(Config):
# MSC3026 (busy presence state) # MSC3026 (busy presence state)
self.msc3026_enabled = experimental.get("msc3026_enabled", False) # type: bool self.msc3026_enabled = experimental.get("msc3026_enabled", False) # type: bool
# MSC2403 (room knocking)
self.msc2403_enabled = experimental.get("msc2403_enabled", False) # type: bool
if self.msc2403_enabled:
# Enable the MSC2403 unstable room version
KNOWN_ROOM_VERSIONS[RoomVersions.MSC2403.identifier] = RoomVersions.MSC2403

View File

@ -90,7 +90,6 @@ class FederationClient(FederationBase):
self._clock.looping_call(self._clear_tried_cache, 60 * 1000) self._clock.looping_call(self._clear_tried_cache, 60 * 1000)
self.state = hs.get_state_handler() self.state = hs.get_state_handler()
self.transport_layer = hs.get_federation_transport_client() self.transport_layer = hs.get_federation_transport_client()
self._msc2403_enabled = hs.config.experimental.msc2403_enabled
self.hostname = hs.hostname self.hostname = hs.hostname
self.signing_key = hs.signing_key self.signing_key = hs.signing_key
@ -621,11 +620,7 @@ class FederationClient(FederationBase):
SynapseError: if the chosen remote server returns a 300/400 code, or SynapseError: if the chosen remote server returns a 300/400 code, or
no servers successfully handle the request. no servers successfully handle the request.
""" """
valid_memberships = {Membership.JOIN, Membership.LEAVE} valid_memberships = {Membership.JOIN, Membership.LEAVE, Membership.KNOCK}
# Allow knocking if the feature is enabled
if self._msc2403_enabled:
valid_memberships.add(Membership.KNOCK)
if membership not in valid_memberships: if membership not in valid_memberships:
raise RuntimeError( raise RuntimeError(
@ -989,7 +984,7 @@ class FederationClient(FederationBase):
return await self._do_send_knock(destination, pdu) return await self._do_send_knock(destination, pdu)
return await self._try_destination_list( return await self._try_destination_list(
"xyz.amorgan.knock/send_knock", destinations, send_request "send_knock", destinations, send_request
) )
async def _do_send_knock(self, destination: str, pdu: EventBase) -> JsonDict: async def _do_send_knock(self, destination: str, pdu: EventBase) -> JsonDict:

View File

@ -47,7 +47,6 @@ class TransportLayerClient:
def __init__(self, hs): def __init__(self, hs):
self.server_name = hs.hostname self.server_name = hs.hostname
self.client = hs.get_federation_http_client() self.client = hs.get_federation_http_client()
self._msc2403_enabled = hs.config.experimental.msc2403_enabled
@log_function @log_function
def get_room_state_ids(self, destination, room_id, event_id): def get_room_state_ids(self, destination, room_id, event_id):
@ -221,28 +220,13 @@ class TransportLayerClient:
Fails with ``FederationDeniedError`` if the remote destination Fails with ``FederationDeniedError`` if the remote destination
is not in our federation whitelist is not in our federation whitelist
""" """
valid_memberships = {Membership.JOIN, Membership.LEAVE} valid_memberships = {Membership.JOIN, Membership.LEAVE, Membership.KNOCK}
# Allow knocking if the feature is enabled
if self._msc2403_enabled:
valid_memberships.add(Membership.KNOCK)
if membership not in valid_memberships: if membership not in valid_memberships:
raise RuntimeError( raise RuntimeError(
"make_membership_event called with membership='%s', must be one of %s" "make_membership_event called with membership='%s', must be one of %s"
% (membership, ",".join(valid_memberships)) % (membership, ",".join(valid_memberships))
) )
# Knock currently uses an unstable prefix
if membership == Membership.KNOCK:
# Create a path in the form of /unstable/xyz.amorgan.knock/make_knock/...
path = _create_path(
FEDERATION_UNSTABLE_PREFIX + "/xyz.amorgan.knock",
"/make_knock/%s/%s",
room_id,
user_id,
)
else:
path = _create_v1_path("/make_%s/%s/%s", membership, room_id, user_id) path = _create_v1_path("/make_%s/%s/%s", membership, room_id, user_id)
ignore_backoff = False ignore_backoff = False
@ -366,12 +350,7 @@ class TransportLayerClient:
The list of state events may be empty. The list of state events may be empty.
""" """
path = _create_path( path = _create_v1_path("/send_knock/%s/%s", room_id, event_id)
FEDERATION_UNSTABLE_PREFIX + "/xyz.amorgan.knock",
"/send_knock/%s/%s",
room_id,
event_id,
)
return await self.client.put_json( return await self.client.put_json(
destination=destination, path=path, data=content destination=destination, path=path, data=content

View File

@ -567,8 +567,6 @@ class FederationV2SendLeaveServlet(BaseFederationServerServlet):
class FederationMakeKnockServlet(BaseFederationServerServlet): class FederationMakeKnockServlet(BaseFederationServerServlet):
PATH = "/make_knock/(?P<room_id>[^/]*)/(?P<user_id>[^/]*)" PATH = "/make_knock/(?P<room_id>[^/]*)/(?P<user_id>[^/]*)"
PREFIX = FEDERATION_UNSTABLE_PREFIX + "/xyz.amorgan.knock"
async def on_GET(self, origin, content, query, room_id, user_id): async def on_GET(self, origin, content, query, room_id, user_id):
try: try:
# Retrieve the room versions the remote homeserver claims to support # Retrieve the room versions the remote homeserver claims to support
@ -585,8 +583,6 @@ class FederationMakeKnockServlet(BaseFederationServerServlet):
class FederationV1SendKnockServlet(BaseFederationServerServlet): class FederationV1SendKnockServlet(BaseFederationServerServlet):
PATH = "/send_knock/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)" PATH = "/send_knock/(?P<room_id>[^/]*)/(?P<event_id>[^/]*)"
PREFIX = FEDERATION_UNSTABLE_PREFIX + "/xyz.amorgan.knock"
async def on_PUT(self, origin, content, query, room_id, event_id): async def on_PUT(self, origin, content, query, room_id, event_id):
content = await self.handler.on_send_knock_request(origin, content, room_id) content = await self.handler.on_send_knock_request(origin, content, room_id)
return 200, content return 200, content
@ -1610,6 +1606,8 @@ FEDERATION_SERVLET_CLASSES = (
FederationVersionServlet, FederationVersionServlet,
RoomComplexityServlet, RoomComplexityServlet,
FederationSpaceSummaryServlet, FederationSpaceSummaryServlet,
FederationV1SendKnockServlet,
FederationMakeKnockServlet,
) # type: Tuple[Type[BaseFederationServlet], ...] ) # type: Tuple[Type[BaseFederationServlet], ...]
OPENID_SERVLET_CLASSES = ( OPENID_SERVLET_CLASSES = (
@ -1652,12 +1650,6 @@ GROUP_ATTESTATION_SERVLET_CLASSES = (
) # type: Tuple[Type[BaseFederationServlet], ...] ) # type: Tuple[Type[BaseFederationServlet], ...]
MSC2403_SERVLET_CLASSES = (
FederationV1SendKnockServlet,
FederationMakeKnockServlet,
)
DEFAULT_SERVLET_GROUPS = ( DEFAULT_SERVLET_GROUPS = (
"federation", "federation",
"room_list", "room_list",
@ -1700,16 +1692,6 @@ def register_servlets(
server_name=hs.hostname, server_name=hs.hostname,
).register(resource) ).register(resource)
# Register msc2403 (knocking) servlets if the feature is enabled
if hs.config.experimental.msc2403_enabled:
for servletclass in MSC2403_SERVLET_CLASSES:
servletclass(
hs=hs,
authenticator=authenticator,
ratelimiter=ratelimiter,
server_name=hs.hostname,
).register(resource)
if "openid" in servlet_groups: if "openid" in servlet_groups:
for servletclass in OPENID_SERVLET_CLASSES: for servletclass in OPENID_SERVLET_CLASSES:
servletclass( servletclass(

View File

@ -2009,8 +2009,7 @@ class FederationHandler(BaseHandler):
""" """
if get_domain_from_id(user_id) != origin: if get_domain_from_id(user_id) != origin:
logger.info( logger.info(
"Get /xyz.amorgan.knock/make_knock request for user %r" "Get /make_knock request for user %r from different origin %s, ignoring",
"from different origin %s, ignoring",
user_id, user_id,
origin, origin,
) )
@ -2077,8 +2076,7 @@ class FederationHandler(BaseHandler):
if get_domain_from_id(event.sender) != origin: if get_domain_from_id(event.sender) != origin:
logger.info( logger.info(
"Got /xyz.amorgan.knock/send_knock request for user %r " "Got /send_knock request for user %r from different origin %s",
"from different origin %s",
event.sender, event.sender,
origin, origin,
) )

View File

@ -707,10 +707,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
knock.event_id, txn_id, requester, content knock.event_id, txn_id, requester, content
) )
elif ( elif effective_membership_state == Membership.KNOCK:
self.config.experimental.msc2403_enabled
and effective_membership_state == Membership.KNOCK
):
if not is_host_in_room: if not is_host_in_room:
# The knock needs to be sent over federation instead # The knock needs to be sent over federation instead
remote_room_hosts.append(get_domain_from_id(room_id)) remote_room_hosts.append(get_domain_from_id(room_id))

View File

@ -121,9 +121,6 @@ class ClientRestResource(JsonResource):
account_validity.register_servlets(hs, client_resource) account_validity.register_servlets(hs, client_resource)
relations.register_servlets(hs, client_resource) relations.register_servlets(hs, client_resource)
password_policy.register_servlets(hs, client_resource) password_policy.register_servlets(hs, client_resource)
# Register msc2403 (knocking) servlets if the feature is enabled
if hs.config.experimental.msc2403_enabled:
knock.register_servlets(hs, client_resource) knock.register_servlets(hs, client_resource)
# moving to /_synapse/admin # moving to /_synapse/admin

View File

@ -39,12 +39,10 @@ logger = logging.getLogger(__name__)
class KnockRoomAliasServlet(RestServlet): class KnockRoomAliasServlet(RestServlet):
""" """
POST /xyz.amorgan.knock/{roomIdOrAlias} POST /knock/{roomIdOrAlias}
""" """
PATTERNS = client_patterns( PATTERNS = client_patterns("/knock/(?P<room_identifier>[^/]*)")
"/xyz.amorgan.knock/(?P<room_identifier>[^/]*)", releases=()
)
def __init__(self, hs: "HomeServer"): def __init__(self, hs: "HomeServer"):
super().__init__() super().__init__()

View File

@ -25,9 +25,6 @@ from synapse.types import RoomAlias
from tests.test_utils import event_injection from tests.test_utils import event_injection
from tests.unittest import FederatingHomeserverTestCase, TestCase, override_config from tests.unittest import FederatingHomeserverTestCase, TestCase, override_config
# An identifier to use while MSC2304 is not in a stable release of the spec
KNOCK_UNSTABLE_IDENTIFIER = "xyz.amorgan.knock"
class KnockingStrippedStateEventHelperMixin(TestCase): class KnockingStrippedStateEventHelperMixin(TestCase):
def send_example_state_events_to_room( def send_example_state_events_to_room(
@ -61,7 +58,7 @@ class KnockingStrippedStateEventHelperMixin(TestCase):
self.get_success( self.get_success(
event_injection.inject_event( event_injection.inject_event(
hs, hs,
room_version=RoomVersions.MSC2403.identifier, room_version=RoomVersions.V7.identifier,
room_id=room_id, room_id=room_id,
sender=sender, sender=sender,
type="com.example.secret", type="com.example.secret",
@ -121,7 +118,7 @@ class KnockingStrippedStateEventHelperMixin(TestCase):
self.get_success( self.get_success(
event_injection.inject_event( event_injection.inject_event(
hs, hs,
room_version=RoomVersions.MSC2403.identifier, room_version=RoomVersions.V7.identifier,
room_id=room_id, room_id=room_id,
sender=sender, sender=sender,
type=event_type, type=event_type,
@ -135,7 +132,7 @@ class KnockingStrippedStateEventHelperMixin(TestCase):
room_state[EventTypes.Create] = { room_state[EventTypes.Create] = {
"content": { "content": {
"creator": sender, "creator": sender,
"room_version": RoomVersions.MSC2403.identifier, "room_version": RoomVersions.V7.identifier,
}, },
"state_key": "", "state_key": "",
} }
@ -232,7 +229,7 @@ class FederationKnockingTestCase(
room_id = self.helper.create_room_as( room_id = self.helper.create_room_as(
"u1", "u1",
is_public=False, is_public=False,
room_version=RoomVersions.MSC2403.identifier, room_version=RoomVersions.V7.identifier,
tok=user_token, tok=user_token,
) )
@ -243,14 +240,13 @@ class FederationKnockingTestCase(
channel = self.make_request( channel = self.make_request(
"GET", "GET",
"/_matrix/federation/unstable/%s/make_knock/%s/%s?ver=%s" "/_matrix/federation/v1/make_knock/%s/%s?ver=%s"
% ( % (
KNOCK_UNSTABLE_IDENTIFIER,
room_id, room_id,
fake_knocking_user_id, fake_knocking_user_id,
# Inform the remote that we support the room version of the room we're # Inform the remote that we support the room version of the room we're
# knocking on # knocking on
RoomVersions.MSC2403.identifier, RoomVersions.V7.identifier,
), ),
) )
self.assertEquals(200, channel.code, channel.result) self.assertEquals(200, channel.code, channel.result)
@ -275,7 +271,7 @@ class FederationKnockingTestCase(
self.clock, self.clock,
self.hs.hostname, self.hs.hostname,
self.hs.signing_key, self.hs.signing_key,
room_version=RoomVersions.MSC2403, room_version=RoomVersions.V7,
event_dict=knock_event, event_dict=knock_event,
) )
@ -287,8 +283,8 @@ class FederationKnockingTestCase(
# Send the signed knock event into the room # Send the signed knock event into the room
channel = self.make_request( channel = self.make_request(
"PUT", "PUT",
"/_matrix/federation/unstable/%s/send_knock/%s/%s" "/_matrix/federation/v1/send_knock/%s/%s"
% (KNOCK_UNSTABLE_IDENTIFIER, room_id, signed_knock_event.event_id), % (room_id, signed_knock_event.event_id),
signed_knock_event_json, signed_knock_event_json,
) )
self.assertEquals(200, channel.code, channel.result) self.assertEquals(200, channel.code, channel.result)

View File

@ -333,7 +333,7 @@ class SyncKnockTestCase(
self.room_id = self.helper.create_room_as( self.room_id = self.helper.create_room_as(
self.user_id, self.user_id,
is_public=False, is_public=False,
room_version="xyz.amorgan.knock", room_version="7",
tok=self.tok, tok=self.tok,
) )
@ -363,7 +363,7 @@ class SyncKnockTestCase(
# Knock on a room # Knock on a room
channel = self.make_request( channel = self.make_request(
"POST", "POST",
"/_matrix/client/unstable/xyz.amorgan.knock/%s" % (self.room_id,), "/_matrix/client/r0/knock/%s" % (self.room_id,),
b"{}", b"{}",
self.knocker_tok, self.knocker_tok,
) )
@ -371,7 +371,7 @@ class SyncKnockTestCase(
# We expect to see the knock event in the stripped room state later # We expect to see the knock event in the stripped room state later
self.expected_room_state[EventTypes.Member] = { self.expected_room_state[EventTypes.Member] = {
"content": {"membership": "xyz.amorgan.knock", "displayname": "knocker"}, "content": {"membership": "knock", "displayname": "knocker"},
"state_key": "@knocker:test", "state_key": "@knocker:test",
} }
@ -384,7 +384,7 @@ class SyncKnockTestCase(
self.assertEqual(channel.code, 200, channel.json_body) self.assertEqual(channel.code, 200, channel.json_body)
# Extract the stripped room state events from /sync # Extract the stripped room state events from /sync
knock_entry = channel.json_body["rooms"]["xyz.amorgan.knock"] knock_entry = channel.json_body["rooms"]["knock"]
room_state_events = knock_entry[self.room_id]["knock_state"]["events"] room_state_events = knock_entry[self.room_id]["knock_state"]["events"]
# Validate that the knock membership event came last # Validate that the knock membership event came last