diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index ee27d99135..15e90fdd4e 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -1008,6 +1008,7 @@ class RoomContextHandler:
event_id: str,
limit: int,
event_filter: Optional[Filter],
+ use_admin_priviledge: bool = False,
) -> Optional[JsonDict]:
"""Retrieves events, pagination tokens and state around a given event
in a room.
@@ -1020,7 +1021,9 @@ class RoomContextHandler:
(excluding state).
event_filter: the filter to apply to the events returned
(excluding the target event_id)
-
+ use_admin_priviledge: if `True`, return all events, regardless
+ of whether `user` has access to them. To be used **ONLY**
+ from the admin API.
Returns:
dict, or None if the event isn't found
"""
@@ -1032,7 +1035,11 @@ class RoomContextHandler:
def filter_evts(events):
return filter_events_for_client(
- self.storage, user.to_string(), events, is_peeking=is_peeking
+ self.storage,
+ user.to_string(),
+ events,
+ is_peeking=is_peeking,
+ use_admin_priviledge=use_admin_priviledge,
)
event = await self.store.get_event(
diff --git a/synapse/rest/admin/__init__.py b/synapse/rest/admin/__init__.py
index 57e0a10837..cb24998b47 100644
--- a/synapse/rest/admin/__init__.py
+++ b/synapse/rest/admin/__init__.py
@@ -42,6 +42,7 @@ from synapse.rest.admin.rooms import (
JoinRoomAliasServlet,
ListRoomRestServlet,
MakeRoomAdminRestServlet,
+ RoomEventContextServlet,
RoomMembersRestServlet,
RoomRestServlet,
ShutdownRoomRestServlet,
@@ -236,6 +237,7 @@ def register_servlets(hs, http_server):
MakeRoomAdminRestServlet(hs).register(http_server)
ShadowBanRestServlet(hs).register(http_server)
ForwardExtremitiesRestServlet(hs).register(http_server)
+ RoomEventContextServlet(hs).register(http_server)
def register_servlets_for_client_rest_resource(hs, http_server):
diff --git a/synapse/rest/admin/rooms.py b/synapse/rest/admin/rooms.py
index f14915d47e..6539655289 100644
--- a/synapse/rest/admin/rooms.py
+++ b/synapse/rest/admin/rooms.py
@@ -566,3 +566,56 @@ class ForwardExtremitiesRestServlet(RestServlet):
extremities = await self.store.get_forward_extremities_for_room(room_id)
return 200, {"count": len(extremities), "results": extremities}
+
+class RoomEventContextServlet(RestServlet):
+ PATTERNS = admin_patterns("/rooms/(?P<room_id>[^/]*)/context/(?P<event_id>[^/]*)$")
+
+ def __init__(self, hs):
+ super().__init__()
+ self.clock = hs.get_clock()
+ self.room_context_handler = hs.get_room_context_handler()
+ self._event_serializer = hs.get_event_client_serializer()
+ self.auth = hs.get_auth()
+
+ async def on_GET(self, request, room_id, event_id):
+ requester = await self.auth.get_user_by_req(request, allow_guest=True)
+
+ limit = parse_integer(request, "limit", default=10)
+
+ # picking the API shape for symmetry with /messages
+ filter_str = parse_string(request, b"filter", encoding="utf-8")
+ if filter_str:
+ filter_json = urlparse.unquote(filter_str)
+ event_filter = Filter(
+ json_decoder.decode(filter_json)
+ ) # type: Optional[Filter]
+ else:
+ event_filter = None
+
+ results = await self.room_context_handler.get_event_context(
+ requester.user,
+ room_id,
+ event_id,
+ limit,
+ event_filter,
+ use_admin_priviledge=True,
+ )
+
+ if not results:
+ raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
+
+ time_now = self.clock.time_msec()
+ results["events_before"] = await self._event_serializer.serialize_events(
+ results["events_before"], time_now
+ )
+ results["event"] = await self._event_serializer.serialize_event(
+ results["event"], time_now
+ )
+ results["events_after"] = await self._event_serializer.serialize_events(
+ results["events_after"], time_now
+ )
+ results["state"] = await self._event_serializer.serialize_events(
+ results["state"], time_now
+ )
+
+ return 200, results
diff --git a/synapse/visibility.py b/synapse/visibility.py
index ec50e7e977..80c304562d 100644
--- a/synapse/visibility.py
+++ b/synapse/visibility.py
@@ -53,6 +53,7 @@ async def filter_events_for_client(
is_peeking=False,
always_include_ids=frozenset(),
filter_send_to_client=True,
+ use_admin_priviledge=False,
):
"""
Check which events a user is allowed to see. If the user can see the event but its
@@ -71,6 +72,9 @@ async def filter_events_for_client(
filter_send_to_client (bool): Whether we're checking an event that's going to be
sent to a client. This might not always be the case since this function can
also be called to check whether a user can see the state at a given point.
+ use_admin_priviledge: if `True`, return all events, regardless
+ of whether `user` has access to them. To be used **ONLY**
+ from the admin API.
Returns:
list[synapse.events.EventBase]
@@ -79,15 +83,23 @@ async def filter_events_for_client(
# to clients.
events = [e for e in events if not e.internal_metadata.is_soft_failed()]
- types = ((EventTypes.RoomHistoryVisibility, ""), (EventTypes.Member, user_id))
+ types = None
+ if use_admin_priviledge:
+ # Administrators can access all events.
+ types = ((EventTypes.RoomHistoryVisibility, ""), (EventTypes.Member, None))
+ else:
+ types = ((EventTypes.RoomHistoryVisibility, ""), (EventTypes.Member, user_id))
+
event_id_to_state = await storage.state.get_state_for_events(
frozenset(e.event_id for e in events),
state_filter=StateFilter.from_types(types),
)
- ignore_dict_content = await storage.main.get_global_account_data_by_type_for_user(
- AccountDataTypes.IGNORED_USER_LIST, user_id
- )
+ ignore_dict_content = None
+ if not use_admin_priviledge:
+ ignore_dict_content = await storage.main.get_global_account_data_by_type_for_user(
+ AccountDataTypes.IGNORED_USER_LIST, user_id
+ )
ignore_list = frozenset()
if ignore_dict_content:
@@ -183,10 +195,12 @@ async def filter_events_for_client(
if old_priority < new_priority:
visibility = prev_visibility
+ membership = None
+ if use_admin_priviledge:
+ membership = Membership.JOIN
# likewise, if the event is the user's own membership event, use
# the 'most joined' membership
- membership = None
- if event.type == EventTypes.Member and event.state_key == user_id:
+ elif event.type == EventTypes.Member and event.state_key == user_id:
membership = event.content.get("membership", None)
if membership not in MEMBERSHIP_PRIORITY:
membership = "leave"
|