mirror of
https://github.com/element-hq/synapse.git
synced 2024-11-25 19:15:51 +03:00
Add knock information in admin exported data (#11171)
Signed-off-by: Rafael Goncalves <rafaelgoncalves@riseup.net>
This commit is contained in:
parent
e002faee01
commit
0e16b418f6
5 changed files with 99 additions and 2 deletions
1
changelog.d/11171.misc
Normal file
1
changelog.d/11171.misc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add knock information in admin export. Contributed by Rafael Gonçalves.
|
|
@ -145,6 +145,20 @@ class FileExfiltrationWriter(ExfiltrationWriter):
|
||||||
for event in state.values():
|
for event in state.values():
|
||||||
print(json.dumps(event), file=f)
|
print(json.dumps(event), file=f)
|
||||||
|
|
||||||
|
def write_knock(self, room_id, event, state):
|
||||||
|
self.write_events(room_id, [event])
|
||||||
|
|
||||||
|
# We write the knock state somewhere else as they aren't full events
|
||||||
|
# and are only a subset of the state at the event.
|
||||||
|
room_directory = os.path.join(self.base_directory, "rooms", room_id)
|
||||||
|
os.makedirs(room_directory, exist_ok=True)
|
||||||
|
|
||||||
|
knock_state = os.path.join(room_directory, "knock_state")
|
||||||
|
|
||||||
|
with open(knock_state, "a") as f:
|
||||||
|
for event in state.values():
|
||||||
|
print(json.dumps(event), file=f)
|
||||||
|
|
||||||
def finished(self):
|
def finished(self):
|
||||||
return self.base_directory
|
return self.base_directory
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@ class AdminHandler:
|
||||||
Membership.LEAVE,
|
Membership.LEAVE,
|
||||||
Membership.BAN,
|
Membership.BAN,
|
||||||
Membership.INVITE,
|
Membership.INVITE,
|
||||||
|
Membership.KNOCK,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -122,6 +123,13 @@ class AdminHandler:
|
||||||
invited_state = invite.unsigned["invite_room_state"]
|
invited_state = invite.unsigned["invite_room_state"]
|
||||||
writer.write_invite(room_id, invite, invited_state)
|
writer.write_invite(room_id, invite, invited_state)
|
||||||
|
|
||||||
|
if room.membership == Membership.KNOCK:
|
||||||
|
event_id = room.event_id
|
||||||
|
knock = await self.store.get_event(event_id, allow_none=True)
|
||||||
|
if knock:
|
||||||
|
knock_state = knock.unsigned["knock_room_state"]
|
||||||
|
writer.write_knock(room_id, knock, knock_state)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# We only want to bother fetching events up to the last time they
|
# We only want to bother fetching events up to the last time they
|
||||||
|
@ -238,6 +246,20 @@ class ExfiltrationWriter(metaclass=abc.ABCMeta):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def write_knock(
|
||||||
|
self, room_id: str, event: EventBase, state: StateMap[dict]
|
||||||
|
) -> None:
|
||||||
|
"""Write a knock for the room, with associated knock state.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room_id: The room ID the knock is for.
|
||||||
|
event: The knock event.
|
||||||
|
state: A subset of the state at the knock, with a subset of the
|
||||||
|
event keys (type, state_key content and sender).
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def finished(self) -> Any:
|
def finished(self) -> Any:
|
||||||
"""Called when all data has successfully been exported and written.
|
"""Called when all data has successfully been exported and written.
|
||||||
|
|
|
@ -17,8 +17,9 @@ from unittest.mock import Mock
|
||||||
|
|
||||||
import synapse.rest.admin
|
import synapse.rest.admin
|
||||||
import synapse.storage
|
import synapse.storage
|
||||||
from synapse.api.constants import EventTypes
|
from synapse.api.constants import EventTypes, JoinRules
|
||||||
from synapse.rest.client import login, room
|
from synapse.api.room_versions import RoomVersions
|
||||||
|
from synapse.rest.client import knock, login, room
|
||||||
|
|
||||||
from tests import unittest
|
from tests import unittest
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ class ExfiltrateData(unittest.HomeserverTestCase):
|
||||||
synapse.rest.admin.register_servlets_for_client_rest_resource,
|
synapse.rest.admin.register_servlets_for_client_rest_resource,
|
||||||
login.register_servlets,
|
login.register_servlets,
|
||||||
room.register_servlets,
|
room.register_servlets,
|
||||||
|
knock.register_servlets,
|
||||||
]
|
]
|
||||||
|
|
||||||
def prepare(self, reactor, clock, hs):
|
def prepare(self, reactor, clock, hs):
|
||||||
|
@ -201,3 +203,32 @@ class ExfiltrateData(unittest.HomeserverTestCase):
|
||||||
self.assertEqual(args[0], room_id)
|
self.assertEqual(args[0], room_id)
|
||||||
self.assertEqual(args[1].content["membership"], "invite")
|
self.assertEqual(args[1].content["membership"], "invite")
|
||||||
self.assertTrue(args[2]) # Assert there is at least one bit of state
|
self.assertTrue(args[2]) # Assert there is at least one bit of state
|
||||||
|
|
||||||
|
def test_knock(self):
|
||||||
|
"""Tests that knock get handled correctly."""
|
||||||
|
# create a knockable v7 room
|
||||||
|
room_id = self.helper.create_room_as(
|
||||||
|
self.user1, room_version=RoomVersions.V7.identifier, tok=self.token1
|
||||||
|
)
|
||||||
|
self.helper.send_state(
|
||||||
|
room_id,
|
||||||
|
EventTypes.JoinRules,
|
||||||
|
{"join_rule": JoinRules.KNOCK},
|
||||||
|
tok=self.token1,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.helper.send(room_id, body="Hello!", tok=self.token1)
|
||||||
|
self.helper.knock(room_id, self.user2, tok=self.token2)
|
||||||
|
|
||||||
|
writer = Mock()
|
||||||
|
|
||||||
|
self.get_success(self.admin_handler.export_user_data(self.user2, writer))
|
||||||
|
|
||||||
|
writer.write_events.assert_not_called()
|
||||||
|
writer.write_state.assert_not_called()
|
||||||
|
writer.write_knock.assert_called_once()
|
||||||
|
|
||||||
|
args = writer.write_knock.call_args[0]
|
||||||
|
self.assertEqual(args[0], room_id)
|
||||||
|
self.assertEqual(args[1].content["membership"], "knock")
|
||||||
|
self.assertTrue(args[2]) # Assert there is at least one bit of state
|
||||||
|
|
|
@ -120,6 +120,35 @@ class RestHelper:
|
||||||
expect_code=expect_code,
|
expect_code=expect_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def knock(self, room=None, user=None, reason=None, expect_code=200, tok=None):
|
||||||
|
temp_id = self.auth_user_id
|
||||||
|
self.auth_user_id = user
|
||||||
|
path = "/knock/%s" % room
|
||||||
|
if tok:
|
||||||
|
path = path + "?access_token=%s" % tok
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
if reason:
|
||||||
|
data["reason"] = reason
|
||||||
|
|
||||||
|
channel = make_request(
|
||||||
|
self.hs.get_reactor(),
|
||||||
|
self.site,
|
||||||
|
"POST",
|
||||||
|
path,
|
||||||
|
json.dumps(data).encode("utf8"),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
int(channel.result["code"]) == expect_code
|
||||||
|
), "Expected: %d, got: %d, resp: %r" % (
|
||||||
|
expect_code,
|
||||||
|
int(channel.result["code"]),
|
||||||
|
channel.result["body"],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.auth_user_id = temp_id
|
||||||
|
|
||||||
def leave(self, room=None, user=None, expect_code=200, tok=None):
|
def leave(self, room=None, user=None, expect_code=200, tok=None):
|
||||||
self.change_membership(
|
self.change_membership(
|
||||||
room=room,
|
room=room,
|
||||||
|
|
Loading…
Reference in a new issue