Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 3fe1c84

Browse files
Make AccessRules use the public rooms directory instead of checking a room's join rules on rule change (#63)
This PR switches several conditions regarding room access rules to check against the status of the room's inclusion in the public room list instead of its join rules. The code includes a snapshot of matrix-org/synapse#8292, which will likely change in time and need merging in again.
1 parent 319d073 commit 3fe1c84

File tree

10 files changed

+579
-116
lines changed

10 files changed

+579
-116
lines changed

UPGRADE.rst

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,70 @@ for example:
7575
wget https://packages.matrix.org/debian/pool/main/m/matrix-synapse-py3/matrix-synapse-py3_1.3.0+stretch1_amd64.deb
7676
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
7777
78+
Upgrading to v1.21.0
79+
====================
80+
81+
Forwarding ``/_synapse/client`` through your reverse proxy
82+
----------------------------------------------------------
83+
84+
The `reverse proxy documentation
85+
<https://github.com/matrix-org/synapse/blob/develop/docs/reverse_proxy.md>`_ has been updated
86+
to include reverse proxy directives for ``/_synapse/client/*`` endpoints. As the user password
87+
reset flow now uses endpoints under this prefix, **you must update your reverse proxy
88+
configurations for user password reset to work**.
89+
90+
Additionally, note that the `Synapse worker documentation
91+
<https://github.com/matrix-org/synapse/blob/develop/docs/workers.md>`_ has been updated to
92+
state that the ``/_synapse/client/password_reset/email/submit_token`` endpoint can be handled
93+
by all workers. If you make use of Synapse's worker feature, please update your reverse proxy
94+
configuration to reflect this change.
95+
96+
New HTML templates
97+
------------------
98+
99+
A new HTML template,
100+
`password_reset_confirmation.html <https://github.com/matrix-org/synapse/blob/develop/synapse/res/templates/password_reset_confirmation.html>`_,
101+
has been added to the ``synapse/res/templates`` directory. If you are using a
102+
custom template directory, you may want to copy the template over and modify it.
103+
104+
Note that as of v1.20.0, templates do not need to be included in custom template
105+
directories for Synapse to start. The default templates will be used if a custom
106+
template cannot be found.
107+
108+
This page will appear to the user after clicking a password reset link that has
109+
been emailed to them.
110+
111+
To complete password reset, the page must include a way to make a `POST`
112+
request to
113+
``/_synapse/client/password_reset/{medium}/submit_token``
114+
with the query parameters from the original link, presented as a URL-encoded form. See the file
115+
itself for more details.
116+
117+
Updated Single Sign-on HTML Templates
118+
-------------------------------------
119+
120+
The ``saml_error.html`` template was removed from Synapse and replaced with the
121+
``sso_error.html`` template. If your Synapse is configured to use SAML and a
122+
custom ``sso_redirect_confirm_template_dir`` configuration then any customisations
123+
of the ``saml_error.html`` template will need to be merged into the ``sso_error.html``
124+
template. These templates are similar, but the parameters are slightly different:
125+
126+
* The ``msg`` parameter should be renamed to ``error_description``.
127+
* There is no longer a ``code`` parameter for the response code.
128+
* A string ``error`` parameter is available that includes a short hint of why a
129+
user is seeing the error page.
130+
131+
ThirdPartyEventRules breaking changes
132+
-------------------------------------
133+
134+
This release introduces a backwards-incompatible change to modules making use of
135+
`ThirdPartyEventRules` in Synapse.
136+
137+
The `http_client` argument is no longer passed to modules as they are initialised. Instead,
138+
modules are expected to make use of the `http_client` property on the `ModuleApi` class.
139+
Modules are now passed a `module_api` argument during initialisation, which is an instance of
140+
`ModuleApi`.
141+
78142
Upgrading to v1.18.0
79143
====================
80144

changelog.d/63.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make AccessRules use the public rooms directory instead of checking a room's join rules on rule change.

synapse/events/third_party_rules.py

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
15+
from typing import Callable
1516

16-
from twisted.internet import defer
17+
from synapse.events import EventBase
18+
from synapse.module_api import ModuleApi
19+
from synapse.types import StateMap
1720

1821

1922
class ThirdPartyEventRules(object):
@@ -36,11 +39,10 @@ def __init__(self, hs):
3639

3740
if module is not None:
3841
self.third_party_rules = module(
39-
config=config, http_client=hs.get_simple_http_client()
42+
config=config, module_api=ModuleApi(hs, hs.get_auth_handler()),
4043
)
4144

42-
@defer.inlineCallbacks
43-
def check_event_allowed(self, event, context):
45+
async def check_event_allowed(self, event, context):
4446
"""Check if a provided event should be allowed in the given context.
4547
4648
Args:
@@ -53,18 +55,17 @@ def check_event_allowed(self, event, context):
5355
if self.third_party_rules is None:
5456
return True
5557

56-
prev_state_ids = yield context.get_prev_state_ids()
58+
prev_state_ids = await context.get_prev_state_ids()
5759

5860
# Retrieve the state events from the database.
5961
state_events = {}
6062
for key, event_id in prev_state_ids.items():
61-
state_events[key] = yield self.store.get_event(event_id, allow_none=True)
63+
state_events[key] = await self.store.get_event(event_id, allow_none=True)
6264

63-
ret = yield self.third_party_rules.check_event_allowed(event, state_events)
65+
ret = await self.third_party_rules.check_event_allowed(event, state_events)
6466
return ret
6567

66-
@defer.inlineCallbacks
67-
def on_create_room(self, requester, config, is_requester_admin):
68+
async def on_create_room(self, requester, config, is_requester_admin):
6869
"""Intercept requests to create room to allow, deny or update the
6970
request config.
7071
@@ -80,13 +81,12 @@ def on_create_room(self, requester, config, is_requester_admin):
8081
if self.third_party_rules is None:
8182
return True
8283

83-
ret = yield self.third_party_rules.on_create_room(
84+
ret = await self.third_party_rules.on_create_room(
8485
requester, config, is_requester_admin
8586
)
8687
return ret
8788

88-
@defer.inlineCallbacks
89-
def check_threepid_can_be_invited(self, medium, address, room_id):
89+
async def check_threepid_can_be_invited(self, medium, address, room_id):
9090
"""Check if a provided 3PID can be invited in the given room.
9191
9292
Args:
@@ -101,14 +101,51 @@ def check_threepid_can_be_invited(self, medium, address, room_id):
101101
if self.third_party_rules is None:
102102
return True
103103

104-
state_ids = yield self.store.get_filtered_current_state_ids(room_id)
105-
room_state_events = yield self.store.get_events(state_ids.values())
104+
state_events = await self._get_state_map_for_room(room_id)
105+
106+
ret = await self.third_party_rules.check_threepid_can_be_invited(
107+
medium, address, state_events
108+
)
109+
return ret
110+
111+
async def check_visibility_can_be_modified(
112+
self, room_id: str, new_visibility: str
113+
) -> bool:
114+
"""Check if a room is allowed to be published to, or removed from, the public room
115+
list.
116+
117+
Args:
118+
room_id: The ID of the room.
119+
new_visibility: The new visibility state. Either "public" or "private".
120+
121+
Returns:
122+
True if the room's visibility can be modified, False if not.
123+
"""
124+
if self.third_party_rules is None:
125+
return True
126+
127+
check_func = getattr(self.third_party_rules, "check_visibility_can_be_modified")
128+
if not check_func or not isinstance(check_func, Callable):
129+
return True
130+
131+
state_events = await self._get_state_map_for_room(room_id)
132+
133+
return await check_func(room_id, state_events, new_visibility)
134+
135+
async def _get_state_map_for_room(self, room_id: str) -> StateMap[EventBase]:
136+
"""Given a room ID, return the state events of that room.
137+
138+
Args:
139+
room_id: The ID of the room.
140+
141+
Returns:
142+
A dict mapping (event type, state key) to state event.
143+
"""
144+
state_ids = await self.store.get_filtered_current_state_ids(room_id)
145+
room_state_events = await self.store.get_events(state_ids.values())
106146

107147
state_events = {}
108148
for key, event_id in state_ids.items():
109149
state_events[key] = room_state_events[event_id]
110150

111-
ret = yield self.third_party_rules.check_threepid_can_be_invited(
112-
medium, address, state_events
113-
)
114-
return ret
151+
return state_events

synapse/handlers/directory.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def __init__(self, hs):
4545
self.config = hs.config
4646
self.enable_room_list_search = hs.config.enable_room_list_search
4747
self.require_membership = hs.config.require_membership_for_aliases
48+
self.third_party_event_rules = hs.get_third_party_event_rules()
4849

4950
self.federation = hs.get_federation_client()
5051
hs.get_federation_registry().register_query_handler(
@@ -448,6 +449,15 @@ async def edit_published_room_list(
448449
# per alias creation rule?
449450
raise SynapseError(403, "Not allowed to publish room")
450451

452+
# Check if publishing is blocked by a third party module
453+
allowed_by_third_party_rules = await (
454+
self.third_party_event_rules.check_visibility_can_be_modified(
455+
room_id, visibility
456+
)
457+
)
458+
if not allowed_by_third_party_rules:
459+
raise SynapseError(403, "Not allowed to publish room")
460+
451461
await self.store.set_room_is_public(room_id, making_public)
452462

453463
async def edit_published_appservice_room_list(

synapse/handlers/room.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,15 @@ async def create_room(
660660
creator_id=user_id, is_public=is_public, room_version=room_version,
661661
)
662662

663+
# Check whether this visibility value is blocked by a third party module
664+
allowed_by_third_party_rules = await (
665+
self.third_party_event_rules.check_visibility_can_be_modified(
666+
room_id, visibility
667+
)
668+
)
669+
if not allowed_by_third_party_rules:
670+
raise SynapseError(403, "Room visibility value not allowed.")
671+
663672
directory_handler = self.hs.get_handlers().directory_handler
664673
if room_alias:
665674
await directory_handler.create_association(

synapse/module_api/__init__.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,18 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616
import logging
17+
from typing import TYPE_CHECKING
1718

1819
from twisted.internet import defer
1920

21+
from synapse.http.client import SimpleHttpClient
2022
from synapse.http.site import SynapseRequest
2123
from synapse.logging.context import make_deferred_yieldable, run_in_background
2224
from synapse.types import UserID
2325

26+
if TYPE_CHECKING:
27+
from synapse.server import HomeServer
28+
2429
"""
2530
This package defines the 'stable' API which can be used by extension modules which
2631
are loaded into Synapse.
@@ -31,6 +36,50 @@
3136
logger = logging.getLogger(__name__)
3237

3338

39+
class PublicRoomListManager:
40+
"""Contains methods for adding to, removing from and querying whether a room
41+
is in the public room list.
42+
43+
Args:
44+
hs: The Homeserver object
45+
"""
46+
47+
def __init__(self, hs: "HomeServer"):
48+
self._store = hs.get_datastore()
49+
50+
async def room_is_in_public_room_list(self, room_id: str) -> bool:
51+
"""Checks whether a room is in the public room list.
52+
53+
Args:
54+
room_id: The ID of the room.
55+
56+
Returns:
57+
Whether the room is in the public room list. Returns False if the room does
58+
not exist.
59+
"""
60+
room = await self._store.get_room(room_id)
61+
if not room:
62+
return False
63+
64+
return room.get("is_public", False)
65+
66+
async def add_room_to_public_room_list(self, room_id: str) -> None:
67+
"""Publishes a room to the public room list.
68+
69+
Args:
70+
room_id: The ID of the room.
71+
"""
72+
await self._store.set_room_is_public(room_id, True)
73+
74+
async def remove_room_from_public_room_list(self, room_id: str) -> None:
75+
"""Removes a room from the public room list.
76+
77+
Args:
78+
room_id: The ID of the room.
79+
"""
80+
await self._store.set_room_is_public(room_id, False)
81+
82+
3483
class ModuleApi(object):
3584
"""A proxy object that gets passed to various plugin modules so they
3685
can register new users etc if necessary.
@@ -43,6 +92,9 @@ def __init__(self, hs, auth_handler):
4392
self._auth = hs.get_auth()
4493
self._auth_handler = auth_handler
4594

95+
self.http_client = hs.get_simple_http_client() # type: SimpleHttpClient
96+
self.public_room_list_manager = PublicRoomListManager(hs)
97+
4698
def get_user_by_req(self, req, allow_guest=False):
4799
"""Check the access_token provided for a request
48100

0 commit comments

Comments
 (0)