This repository has been archived by the owner on Apr 26, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow modules to create and send events into rooms (#8479)
This PR allows Synapse modules making use of the `ModuleApi` to create and send non-membership events into a room. This can useful to have modules send messages, or change power levels in a room etc. Note that they must send event through a user that's already in the room. The non-membership event limitation is currently arbitrary, as it's another chunk of work and not necessary at the moment. This commit has been cherry-picked from mainline.
- Loading branch information
1 parent
11523b5
commit 62c7b10
Showing
7 changed files
with
299 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add the ability to send non-membership events into a room via the `ModuleApi`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
# -*- coding: utf-8 -*- | ||
# Copyright 2019 The Matrix.org Foundation C.I.C. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the 'License'); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an 'AS IS' BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
import threading | ||
from typing import Dict | ||
|
||
from mock import Mock | ||
|
||
from synapse.events import EventBase | ||
from synapse.module_api import ModuleApi | ||
from synapse.rest import admin | ||
from synapse.rest.client.v1 import login, room | ||
from synapse.types import Requester, StateMap | ||
|
||
from tests import unittest | ||
|
||
thread_local = threading.local() | ||
|
||
|
||
class ThirdPartyRulesTestModule: | ||
def __init__(self, config: Dict, module_api: ModuleApi): | ||
# keep a record of the "current" rules module, so that the test can patch | ||
# it if desired. | ||
thread_local.rules_module = self | ||
self.module_api = module_api | ||
|
||
async def on_create_room( | ||
self, requester: Requester, config: dict, is_requester_admin: bool | ||
): | ||
return True | ||
|
||
async def check_event_allowed(self, event: EventBase, state: StateMap[EventBase]): | ||
return True | ||
|
||
@staticmethod | ||
def parse_config(config): | ||
return config | ||
|
||
|
||
def current_rules_module() -> ThirdPartyRulesTestModule: | ||
return thread_local.rules_module | ||
|
||
|
||
class ThirdPartyRulesTestCase(unittest.HomeserverTestCase): | ||
servlets = [ | ||
admin.register_servlets, | ||
login.register_servlets, | ||
room.register_servlets, | ||
] | ||
|
||
def default_config(self): | ||
config = super().default_config() | ||
config["third_party_event_rules"] = { | ||
"module": __name__ + ".ThirdPartyRulesTestModule", | ||
"config": {}, | ||
} | ||
return config | ||
|
||
def prepare(self, reactor, clock, homeserver): | ||
# Create a user and room to play with during the tests | ||
self.user_id = self.register_user("kermit", "monkey") | ||
self.tok = self.login("kermit", "monkey") | ||
|
||
self.room_id = self.helper.create_room_as(self.user_id, tok=self.tok) | ||
|
||
def test_third_party_rules(self): | ||
"""Tests that a forbidden event is forbidden from being sent, but an allowed one | ||
can be sent. | ||
""" | ||
# patch the rules module with a Mock which will return False for some event | ||
# types | ||
async def check(ev, state): | ||
return ev.type != "foo.bar.forbidden" | ||
|
||
callback = Mock(spec=[], side_effect=check) | ||
current_rules_module().check_event_allowed = callback | ||
|
||
request, channel = self.make_request( | ||
"PUT", | ||
"/_matrix/client/r0/rooms/%s/send/foo.bar.allowed/1" % self.room_id, | ||
{}, | ||
access_token=self.tok, | ||
) | ||
self.render(request) | ||
self.assertEquals(channel.result["code"], b"200", channel.result) | ||
|
||
callback.assert_called_once() | ||
|
||
# there should be various state events in the state arg: do some basic checks | ||
state_arg = callback.call_args[0][1] | ||
for k in (("m.room.create", ""), ("m.room.member", self.user_id)): | ||
self.assertIn(k, state_arg) | ||
ev = state_arg[k] | ||
self.assertEqual(ev.type, k[0]) | ||
self.assertEqual(ev.state_key, k[1]) | ||
|
||
request, channel = self.make_request( | ||
"PUT", | ||
"/_matrix/client/r0/rooms/%s/send/foo.bar.forbidden/1" % self.room_id, | ||
{}, | ||
access_token=self.tok, | ||
) | ||
self.render(request) | ||
self.assertEquals(channel.result["code"], b"403", channel.result) | ||
|
||
def test_modify_event(self): | ||
"""Tests that the module can successfully tweak an event before it is persisted. | ||
""" | ||
# first patch the event checker so that it will modify the event | ||
async def check(ev: EventBase, state): | ||
ev.content = {"x": "y"} | ||
return True | ||
|
||
current_rules_module().check_event_allowed = check | ||
|
||
# now send the event | ||
request, channel = self.make_request( | ||
"PUT", | ||
"/_matrix/client/r0/rooms/%s/send/modifyme/1" % self.room_id, | ||
{"x": "x"}, | ||
access_token=self.tok, | ||
) | ||
self.render(request) | ||
self.assertEqual(channel.result["code"], b"200", channel.result) | ||
event_id = channel.json_body["event_id"] | ||
|
||
# ... and check that it got modified | ||
request, channel = self.make_request( | ||
"GET", | ||
"/_matrix/client/r0/rooms/%s/event/%s" % (self.room_id, event_id), | ||
access_token=self.tok, | ||
) | ||
self.render(request) | ||
self.assertEqual(channel.result["code"], b"200", channel.result) | ||
ev = channel.json_body | ||
self.assertEqual(ev["content"]["x"], "y") | ||
|
||
def test_send_event(self): | ||
"""Tests that the module can send an event into a room via the module api""" | ||
content = { | ||
"msgtype": "m.text", | ||
"body": "Hello!", | ||
} | ||
event_dict = { | ||
"room_id": self.room_id, | ||
"type": "m.room.message", | ||
"content": content, | ||
"sender": self.user_id, | ||
} | ||
event = self.get_success( | ||
current_rules_module().module_api.create_and_send_event_into_room( | ||
event_dict | ||
) | ||
) # type: EventBase | ||
|
||
self.assertEquals(event.sender, self.user_id) | ||
self.assertEquals(event.room_id, self.room_id) | ||
self.assertEquals(event.type, "m.room.message") | ||
self.assertEquals(event.content, content) |
Oops, something went wrong.