From 5bdb34651cf5cff86f41b4d1cc1cb52913432b8a Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 29 Mar 2021 11:32:38 -0400 Subject: [PATCH 1/7] Add an experimental room version. --- synapse/api/room_versions.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/synapse/api/room_versions.py b/synapse/api/room_versions.py index de2cc15d33df..c43b6660b39e 100644 --- a/synapse/api/room_versions.py +++ b/synapse/api/room_versions.py @@ -57,7 +57,7 @@ class RoomVersion: state_res = attr.ib(type=int) # one of the StateResolutionVersions enforce_key_validity = attr.ib(type=bool) - # bool: before MSC2261/MSC2432, m.room.aliases had special auth rules and redaction rules + # Before MSC2261/MSC2432, m.room.aliases had special auth rules and redaction rules special_case_aliases_auth = attr.ib(type=bool) # Strictly enforce canonicaljson, do not allow: # * Integers outside the range of [-2 ^ 53 + 1, 2 ^ 53 - 1] @@ -69,6 +69,8 @@ class RoomVersion: limit_notifications_power_levels = attr.ib(type=bool) # MSC2174/MSC2176: Apply updated redaction rules algorithm. msc2176_redaction_rules = attr.ib(type=bool) + # MSC2962: Support the 'restricted' join_rule. + msc2962_join_rules = attr.ib(type=bool) class RoomVersions: @@ -82,6 +84,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, + msc2962_join_rules=False, ) V2 = RoomVersion( "2", @@ -93,6 +96,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, + msc2962_join_rules=False, ) V3 = RoomVersion( "3", @@ -104,6 +108,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, + msc2962_join_rules=False, ) V4 = RoomVersion( "4", @@ -115,6 +120,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, + msc2962_join_rules=False, ) V5 = RoomVersion( "5", @@ -126,6 +132,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, + msc2962_join_rules=False, ) V6 = RoomVersion( "6", @@ -137,6 +144,7 @@ class RoomVersions: strict_canonicaljson=True, limit_notifications_power_levels=True, msc2176_redaction_rules=False, + msc2962_join_rules=False, ) MSC2176 = RoomVersion( "org.matrix.msc2176", @@ -148,6 +156,19 @@ class RoomVersions: strict_canonicaljson=True, limit_notifications_power_levels=True, msc2176_redaction_rules=True, + msc2962_join_rules=False, + ) + MSC2962 = RoomVersion( + "org.matrix.msc2962", + RoomDisposition.UNSTABLE, + EventFormatVersions.V3, + StateResolutionVersions.V2, + enforce_key_validity=True, + special_case_aliases_auth=False, + strict_canonicaljson=True, + limit_notifications_power_levels=True, + msc2176_redaction_rules=False, + msc2962_join_rules=True, ) @@ -161,5 +182,6 @@ class RoomVersions: RoomVersions.V5, RoomVersions.V6, RoomVersions.MSC2176, + RoomVersions.MSC2962, ) } # type: Dict[str, RoomVersion] From 3e795b65ea6d0ade17c7b5864737e05b7e248814 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 29 Mar 2021 11:42:29 -0400 Subject: [PATCH 2/7] Supports the 'restricted' join rule in the auth checks. --- synapse/api/constants.py | 2 ++ synapse/event_auth.py | 28 ++++++++++++++++++------ tests/test_event_auth.py | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/synapse/api/constants.py b/synapse/api/constants.py index 8f37d2cf3bdc..59d39af66be4 100644 --- a/synapse/api/constants.py +++ b/synapse/api/constants.py @@ -59,6 +59,8 @@ class JoinRules: KNOCK = "knock" INVITE = "invite" PRIVATE = "private" + # As defined for MSC2962. + MSC2962_RESTRICTED = "restricted" class LoginType: diff --git a/synapse/event_auth.py b/synapse/event_auth.py index 91ad5b3d3cf0..2ee13b7d4243 100644 --- a/synapse/event_auth.py +++ b/synapse/event_auth.py @@ -162,7 +162,7 @@ def check( logger.debug("Auth events: %s", [a.event_id for a in auth_events.values()]) if event.type == EventTypes.Member: - _is_membership_change_allowed(event, auth_events) + _is_membership_change_allowed(room_version_obj, event, auth_events) logger.debug("Allowing! %s", event) return @@ -220,8 +220,19 @@ def _can_federate(event: EventBase, auth_events: StateMap[EventBase]) -> bool: def _is_membership_change_allowed( - event: EventBase, auth_events: StateMap[EventBase] + room_version: RoomVersion, event: EventBase, auth_events: StateMap[EventBase] ) -> None: + """ + Confirms that the event which changes membership is an allowed change. + + Args: + room_version: The version of the room. + event: The event to check. + auth_events: The current auth events of the room. + + Raises: + AuthError if the event is not allowed. + """ membership = event.content["membership"] # Check if this is the room creator joining: @@ -315,14 +326,19 @@ def _is_membership_change_allowed( if user_level < invite_level: raise AuthError(403, "You don't have permission to invite users") elif Membership.JOIN == membership: - # Joins are valid iff caller == target and they were: - # invited: They are accepting the invitation - # joined: It's a NOOP + # Joins are valid iff caller == target and: + # * They are not banned. + # * They are accepting a previously sent invitation. + # * They are already joined (it's a NOOP). + # * The room is public or restricted. if event.user_id != target_user_id: raise AuthError(403, "Cannot force another user to join.") elif target_banned: raise AuthError(403, "You are banned from this room") - elif join_rule == JoinRules.PUBLIC: + elif join_rule == JoinRules.PUBLIC or ( + room_version.msc2962_join_rules + and join_rule == JoinRules.MSC2962_RESTRICTED + ): pass elif join_rule == JoinRules.INVITE: if not caller_in_room and not caller_invited: diff --git a/tests/test_event_auth.py b/tests/test_event_auth.py index 3f2691ee6bec..dcb66f5a6855 100644 --- a/tests/test_event_auth.py +++ b/tests/test_event_auth.py @@ -207,6 +207,36 @@ def test_msc2209(self): do_sig_check=False, ) + def test_msc2962_join_rules(self): + """ + Test joining an MSC2962 restricted join_rule room. + """ + creator = "@creator:example.com" + pleb = "@joiner:example.com" + + auth_events = { + ("m.room.create", ""): _create_event(creator), + ("m.room.member", creator): _join_event(creator), + ("m.room.join_rules", ""): _join_rules_event(creator, "restricted"), + } + + # Older room versions don't understand this join rule. + with self.assertRaises(AuthError): + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # This completes successfully. + event_auth.check( + RoomVersions.MSC2962, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + # helpers for making events @@ -234,6 +264,7 @@ def _join_event(user_id): "sender": user_id, "state_key": user_id, "content": {"membership": "join"}, + "prev_events": [], } ) @@ -277,6 +308,21 @@ def _random_state_event(sender): ) +def _join_rules_event(sender, join_rule): + return make_event_from_dict( + { + "room_id": TEST_ROOM_ID, + "event_id": _get_event_id(), + "type": "m.room.join_rules", + "sender": sender, + "state_key": "", + "content": { + "join_rule": join_rule, + }, + } + ) + + event_count = 0 From 2c5672d3dceedb515a531794006a95bdf91a8b81 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 30 Mar 2021 12:18:10 -0400 Subject: [PATCH 3/7] Add additional tests. --- tests/test_event_auth.py | 208 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 201 insertions(+), 7 deletions(-) diff --git a/tests/test_event_auth.py b/tests/test_event_auth.py index dcb66f5a6855..ee4fb989c142 100644 --- a/tests/test_event_auth.py +++ b/tests/test_event_auth.py @@ -207,9 +207,151 @@ def test_msc2209(self): do_sig_check=False, ) - def test_msc2962_join_rules(self): + def test_join_rules_public(self): """ - Test joining an MSC2962 restricted join_rule room. + Test joining a public room. + """ + creator = "@creator:example.com" + pleb = "@joiner:example.com" + + auth_events = { + ("m.room.create", ""): _create_event(creator), + ("m.room.member", creator): _join_event(creator), + ("m.room.join_rules", ""): _join_rules_event(creator, "public"), + } + + # Check join. + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user cannot be force-joined to a room. + with self.assertRaises(AuthError): + event_auth.check( + RoomVersions.V6, + _member_event(pleb, "join", sender=creator), + auth_events, + do_sig_check=False, + ) + + # Banned should be rejected. + auth_events[("m.room.member", pleb)] = _member_event(pleb, "ban") + with self.assertRaises(AuthError): + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user who left can re-join. + auth_events[("m.room.member", pleb)] = _member_event(pleb, "leave") + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user can send a join if they're in the room. + auth_events[("m.room.member", pleb)] = _member_event(pleb, "join") + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user can accept an invite. + auth_events[("m.room.member", pleb)] = _member_event( + pleb, "invite", sender=creator + ) + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + def test_join_rules_invite(self): + """ + Test joining an invite only room. + """ + creator = "@creator:example.com" + pleb = "@joiner:example.com" + + auth_events = { + ("m.room.create", ""): _create_event(creator), + ("m.room.member", creator): _join_event(creator), + ("m.room.join_rules", ""): _join_rules_event(creator, "invite"), + } + + # A join without an invite is rejected. + with self.assertRaises(AuthError): + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user cannot be force-joined to a room. + with self.assertRaises(AuthError): + event_auth.check( + RoomVersions.V6, + _member_event(pleb, "join", sender=creator), + auth_events, + do_sig_check=False, + ) + + # Banned should be rejected. + auth_events[("m.room.member", pleb)] = _member_event(pleb, "ban") + with self.assertRaises(AuthError): + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user who left cannot re-join. + auth_events[("m.room.member", pleb)] = _member_event(pleb, "leave") + with self.assertRaises(AuthError): + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user can send a join if they're in the room. + auth_events[("m.room.member", pleb)] = _member_event(pleb, "join") + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user can accept an invite. + auth_events[("m.room.member", pleb)] = _member_event( + pleb, "invite", sender=creator + ) + event_auth.check( + RoomVersions.V6, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + def test_join_rules_msc2962_restricted(self): + """ + Test joining a restricted room from MSC2962. + + This is pretty much the same test as public. """ creator = "@creator:example.com" pleb = "@joiner:example.com" @@ -220,7 +362,7 @@ def test_msc2962_join_rules(self): ("m.room.join_rules", ""): _join_rules_event(creator, "restricted"), } - # Older room versions don't understand this join rule. + # Older room versions don't understand this join rule with self.assertRaises(AuthError): event_auth.check( RoomVersions.V6, @@ -229,7 +371,55 @@ def test_msc2962_join_rules(self): do_sig_check=False, ) - # This completes successfully. + # Check join. + event_auth.check( + RoomVersions.MSC2962, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user cannot be force-joined to a room. + with self.assertRaises(AuthError): + event_auth.check( + RoomVersions.MSC2962, + _member_event(pleb, "join", sender=creator), + auth_events, + do_sig_check=False, + ) + + # Banned should be rejected. + auth_events[("m.room.member", pleb)] = _member_event(pleb, "ban") + with self.assertRaises(AuthError): + event_auth.check( + RoomVersions.MSC2962, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user who left can re-join. + auth_events[("m.room.member", pleb)] = _member_event(pleb, "leave") + event_auth.check( + RoomVersions.MSC2962, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user can send a join if they're in the room. + auth_events[("m.room.member", pleb)] = _member_event(pleb, "join") + event_auth.check( + RoomVersions.MSC2962, + _join_event(pleb), + auth_events, + do_sig_check=False, + ) + + # A user can accept an invite. + auth_events[("m.room.member", pleb)] = _member_event( + pleb, "invite", sender=creator + ) event_auth.check( RoomVersions.MSC2962, _join_event(pleb), @@ -255,20 +445,24 @@ def _create_event(user_id): ) -def _join_event(user_id): +def _member_event(user_id, membership, sender=None): return make_event_from_dict( { "room_id": TEST_ROOM_ID, "event_id": _get_event_id(), "type": "m.room.member", - "sender": user_id, + "sender": sender or user_id, "state_key": user_id, - "content": {"membership": "join"}, + "content": {"membership": membership}, "prev_events": [], } ) +def _join_event(user_id): + return _member_event(user_id, "join") + + def _power_levels_event(sender, content): return make_event_from_dict( { From bf98d9b9f6b62fdf823d284fbe61aee10ca19eaf Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 30 Mar 2021 12:29:15 -0400 Subject: [PATCH 4/7] Newsfragment --- changelog.d/9717.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/9717.feature diff --git a/changelog.d/9717.feature b/changelog.d/9717.feature new file mode 100644 index 000000000000..6cae05a9a4b4 --- /dev/null +++ b/changelog.d/9717.feature @@ -0,0 +1 @@ +Add experimental support for [MSC2962](https://github.com/matrix-org/matrix-doc/pull/2962): restricting room access via group membership. From cf792798fc3feacaead4108a3a327767f02ceb7a Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 31 Mar 2021 11:40:58 -0400 Subject: [PATCH 5/7] Update from MSC2962 to MSC3083. --- synapse/api/constants.py | 4 ++-- synapse/api/room_versions.py | 26 +++++++++++++------------- synapse/event_auth.py | 4 ++-- tests/test_event_auth.py | 16 ++++++++-------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/synapse/api/constants.py b/synapse/api/constants.py index 59d39af66be4..6856dab06c1b 100644 --- a/synapse/api/constants.py +++ b/synapse/api/constants.py @@ -59,8 +59,8 @@ class JoinRules: KNOCK = "knock" INVITE = "invite" PRIVATE = "private" - # As defined for MSC2962. - MSC2962_RESTRICTED = "restricted" + # As defined for MSC3083. + MSC3083_RESTRICTED = "restricted" class LoginType: diff --git a/synapse/api/room_versions.py b/synapse/api/room_versions.py index c43b6660b39e..2b67a874b920 100644 --- a/synapse/api/room_versions.py +++ b/synapse/api/room_versions.py @@ -69,8 +69,8 @@ class RoomVersion: limit_notifications_power_levels = attr.ib(type=bool) # MSC2174/MSC2176: Apply updated redaction rules algorithm. msc2176_redaction_rules = attr.ib(type=bool) - # MSC2962: Support the 'restricted' join_rule. - msc2962_join_rules = attr.ib(type=bool) + # MSC3083: Support the 'restricted' join_rule. + msc3083_join_rules = attr.ib(type=bool) class RoomVersions: @@ -84,7 +84,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, - msc2962_join_rules=False, + msc3083_join_rules=False, ) V2 = RoomVersion( "2", @@ -96,7 +96,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, - msc2962_join_rules=False, + msc3083_join_rules=False, ) V3 = RoomVersion( "3", @@ -108,7 +108,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, - msc2962_join_rules=False, + msc3083_join_rules=False, ) V4 = RoomVersion( "4", @@ -120,7 +120,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, - msc2962_join_rules=False, + msc3083_join_rules=False, ) V5 = RoomVersion( "5", @@ -132,7 +132,7 @@ class RoomVersions: strict_canonicaljson=False, limit_notifications_power_levels=False, msc2176_redaction_rules=False, - msc2962_join_rules=False, + msc3083_join_rules=False, ) V6 = RoomVersion( "6", @@ -144,7 +144,7 @@ class RoomVersions: strict_canonicaljson=True, limit_notifications_power_levels=True, msc2176_redaction_rules=False, - msc2962_join_rules=False, + msc3083_join_rules=False, ) MSC2176 = RoomVersion( "org.matrix.msc2176", @@ -156,10 +156,10 @@ class RoomVersions: strict_canonicaljson=True, limit_notifications_power_levels=True, msc2176_redaction_rules=True, - msc2962_join_rules=False, + msc3083_join_rules=False, ) - MSC2962 = RoomVersion( - "org.matrix.msc2962", + MSC3083 = RoomVersion( + "org.matrix.msc3083", RoomDisposition.UNSTABLE, EventFormatVersions.V3, StateResolutionVersions.V2, @@ -168,7 +168,7 @@ class RoomVersions: strict_canonicaljson=True, limit_notifications_power_levels=True, msc2176_redaction_rules=False, - msc2962_join_rules=True, + msc3083_join_rules=True, ) @@ -182,6 +182,6 @@ class RoomVersions: RoomVersions.V5, RoomVersions.V6, RoomVersions.MSC2176, - RoomVersions.MSC2962, + RoomVersions.MSC3083, ) } # type: Dict[str, RoomVersion] diff --git a/synapse/event_auth.py b/synapse/event_auth.py index 2ee13b7d4243..9863953f5c5d 100644 --- a/synapse/event_auth.py +++ b/synapse/event_auth.py @@ -336,8 +336,8 @@ def _is_membership_change_allowed( elif target_banned: raise AuthError(403, "You are banned from this room") elif join_rule == JoinRules.PUBLIC or ( - room_version.msc2962_join_rules - and join_rule == JoinRules.MSC2962_RESTRICTED + room_version.msc3083_join_rules + and join_rule == JoinRules.MSC3083_RESTRICTED ): pass elif join_rule == JoinRules.INVITE: diff --git a/tests/test_event_auth.py b/tests/test_event_auth.py index ee4fb989c142..b5f18344dcf1 100644 --- a/tests/test_event_auth.py +++ b/tests/test_event_auth.py @@ -347,9 +347,9 @@ def test_join_rules_invite(self): do_sig_check=False, ) - def test_join_rules_msc2962_restricted(self): + def test_join_rules_msc3083_restricted(self): """ - Test joining a restricted room from MSC2962. + Test joining a restricted room from MSC3083. This is pretty much the same test as public. """ @@ -373,7 +373,7 @@ def test_join_rules_msc2962_restricted(self): # Check join. event_auth.check( - RoomVersions.MSC2962, + RoomVersions.MSC3083, _join_event(pleb), auth_events, do_sig_check=False, @@ -382,7 +382,7 @@ def test_join_rules_msc2962_restricted(self): # A user cannot be force-joined to a room. with self.assertRaises(AuthError): event_auth.check( - RoomVersions.MSC2962, + RoomVersions.MSC3083, _member_event(pleb, "join", sender=creator), auth_events, do_sig_check=False, @@ -392,7 +392,7 @@ def test_join_rules_msc2962_restricted(self): auth_events[("m.room.member", pleb)] = _member_event(pleb, "ban") with self.assertRaises(AuthError): event_auth.check( - RoomVersions.MSC2962, + RoomVersions.MSC3083, _join_event(pleb), auth_events, do_sig_check=False, @@ -401,7 +401,7 @@ def test_join_rules_msc2962_restricted(self): # A user who left can re-join. auth_events[("m.room.member", pleb)] = _member_event(pleb, "leave") event_auth.check( - RoomVersions.MSC2962, + RoomVersions.MSC3083, _join_event(pleb), auth_events, do_sig_check=False, @@ -410,7 +410,7 @@ def test_join_rules_msc2962_restricted(self): # A user can send a join if they're in the room. auth_events[("m.room.member", pleb)] = _member_event(pleb, "join") event_auth.check( - RoomVersions.MSC2962, + RoomVersions.MSC3083, _join_event(pleb), auth_events, do_sig_check=False, @@ -421,7 +421,7 @@ def test_join_rules_msc2962_restricted(self): pleb, "invite", sender=creator ) event_auth.check( - RoomVersions.MSC2962, + RoomVersions.MSC3083, _join_event(pleb), auth_events, do_sig_check=False, From e284829003084d4b6092028ade69ebef46597d20 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 31 Mar 2021 11:49:54 -0400 Subject: [PATCH 6/7] Do not allow the experimental room version unless sapces is enabled. --- synapse/api/room_versions.py | 2 +- synapse/config/experimental.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/synapse/api/room_versions.py b/synapse/api/room_versions.py index 2b67a874b920..87038d436d28 100644 --- a/synapse/api/room_versions.py +++ b/synapse/api/room_versions.py @@ -182,6 +182,6 @@ class RoomVersions: RoomVersions.V5, RoomVersions.V6, RoomVersions.MSC2176, - RoomVersions.MSC3083, ) + # Note that we do not include MSC3083 here unless it is enabled in the config. } # type: Dict[str, RoomVersion] diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py index 86f4d9af9dda..eb96ecda74ea 100644 --- a/synapse/config/experimental.py +++ b/synapse/config/experimental.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersions from synapse.config._base import Config from synapse.types import JsonDict @@ -27,7 +28,11 @@ def read_config(self, config: JsonDict, **kwargs): # MSC2858 (multiple SSO identity providers) self.msc2858_enabled = experimental.get("msc2858_enabled", False) # type: bool - # Spaces (MSC1772, MSC2946, etc) + + # Spaces (MSC1772, MSC2946, MSC3083, etc) self.spaces_enabled = experimental.get("spaces_enabled", False) # type: bool + if self.spaces_enabled: + KNOWN_ROOM_VERSIONS[RoomVersions.MSC3083.identifier] = RoomVersions.MSC3083 + # MSC3026 (busy presence state) self.msc3026_enabled = experimental.get("msc3026_enabled", False) # type: bool From 22c0af3df7c6f054a46829a82f602bfd069fef8a Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 31 Mar 2021 16:38:35 -0400 Subject: [PATCH 7/7] Update changelog. --- changelog.d/9717.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/9717.feature b/changelog.d/9717.feature index 6cae05a9a4b4..c2c74f13d578 100644 --- a/changelog.d/9717.feature +++ b/changelog.d/9717.feature @@ -1 +1 @@ -Add experimental support for [MSC2962](https://github.com/matrix-org/matrix-doc/pull/2962): restricting room access via group membership. +Add experimental support for [MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083): restricting room access via group membership.