Compare commits

...

5 Commits

Author SHA1 Message Date
Patrick Cloke da11cc22be
Ensure that event.redacts is the proper type before handling it (#8457)
This fixes a bug when backfilling invalid events.
2020-10-05 10:24:17 -04:00
Andrew Morgan 0991a2da93
Allow ThirdPartyEventRules modules to manipulate public room state (#8292)
This PR allows `ThirdPartyEventRules` modules to view, manipulate and block changes to the state of whether a room is published in the public rooms directory.

While the idea of whether a room is in the public rooms list is not kept within an event in the room, `ThirdPartyEventRules` generally deal with controlling which modifications can happen to a room. Public rooms fits within that idea, even if its toggle state isn't controlled through a state event.
2020-10-05 14:57:46 +01:00
Richard van der Hoff f31f8e6319
Remove stream ordering from Metadata dict (#8452)
There's no need for it to be in the dict as well as the events table. Instead,
we store it in a separate attribute in the EventInternalMetadata object, and
populate that on load.

This means that we can rely on it being correctly populated for any event which
has been persited to the database.
2020-10-05 14:43:14 +01:00
Patrick Cloke f64c6aae68
Update manhole documentation for async/await. (#8462) 2020-10-05 09:40:19 -04:00
Patrick Cloke c5251c6fbd
Do not assume that account data is of the correct form. (#8454)
This fixes a bug where `m.ignored_user_list` was assumed to be a dict,
leading to odd behavior for users who set it to something else.
2020-10-05 09:28:05 -04:00
30 changed files with 319 additions and 75 deletions

View File

@ -75,6 +75,23 @@ for example:
wget https://packages.matrix.org/debian/pool/main/m/matrix-synapse-py3/matrix-synapse-py3_1.3.0+stretch1_amd64.deb
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
Upgrading to v1.22.0
====================
ThirdPartyEventRules breaking changes
-------------------------------------
This release introduces a backwards-incompatible change to modules making use of
``ThirdPartyEventRules`` in Synapse. If you make use of a module defined under the
``third_party_event_rules`` config option, please make sure it is updated to handle
the below change:
The ``http_client`` argument is no longer passed to modules as they are initialised. Instead,
modules are expected to make use of the ``http_client`` property on the ``ModuleApi`` class.
Modules are now passed a ``module_api`` argument during initialisation, which is an instance of
``ModuleApi``. ``ModuleApi`` instances have a ``http_client`` property which acts the same as
the ``http_client`` argument previously passed to ``ThirdPartyEventRules`` modules.
Upgrading to v1.21.0
====================

1
changelog.d/8292.feature Normal file
View File

@ -0,0 +1 @@
Allow `ThirdPartyEventRules` modules to query and manipulate whether a room is in the public rooms directory.

1
changelog.d/8452.misc Normal file
View File

@ -0,0 +1 @@
Remove redundant databae loads of stream_ordering for events we already have.

1
changelog.d/8454.bugfix Normal file
View File

@ -0,0 +1 @@
Fix a longstanding bug where invalid ignored users in account data could break clients.

1
changelog.d/8457.bugfix Normal file
View File

@ -0,0 +1 @@
Fix a bug where backfilling a room with an event that was missing the `redacts` field would break.

1
changelog.d/8462.doc Normal file
View File

@ -0,0 +1 @@
Update the directions for using the manhole with coroutines.

View File

@ -35,9 +35,12 @@ This gives a Python REPL in which `hs` gives access to the
`synapse.server.HomeServer` object - which in turn gives access to many other
parts of the process.
Note that any call which returns a coroutine will need to be wrapped in `ensureDeferred`.
As a simple example, retrieving an event from the database:
```
>>> hs.get_datastore().get_event('$1416420717069yeQaw:matrix.org')
```pycon
>>> from twisted.internet import defer
>>> defer.ensureDeferred(hs.get_datastore().get_event('$1416420717069yeQaw:matrix.org'))
<Deferred at 0x7ff253fc6998 current result: <FrozenEvent event_id='$1416420717069yeQaw:matrix.org', type='m.room.create', state_key=''>>
```

View File

@ -155,3 +155,8 @@ class EventContentFields:
class RoomEncryptionAlgorithms:
MEGOLM_V1_AES_SHA2 = "m.megolm.v1.aes-sha2"
DEFAULT = MEGOLM_V1_AES_SHA2
class AccountDataTypes:
DIRECT = "m.direct"
IGNORED_USER_LIST = "m.ignored_user_list"

View File

@ -446,6 +446,8 @@ def check_redaction(
if room_version_obj.event_format == EventFormatVersions.V1:
redacter_domain = get_domain_from_id(event.event_id)
if not isinstance(event.redacts, str):
return False
redactee_domain = get_domain_from_id(event.redacts)
if redacter_domain == redactee_domain:
return True

View File

@ -97,13 +97,16 @@ class DefaultDictProperty(DictProperty):
class _EventInternalMetadata:
__slots__ = ["_dict"]
__slots__ = ["_dict", "stream_ordering"]
def __init__(self, internal_metadata_dict: JsonDict):
# we have to copy the dict, because it turns out that the same dict is
# reused. TODO: fix that
self._dict = dict(internal_metadata_dict)
# the stream ordering of this event. None, until it has been persisted.
self.stream_ordering = None # type: Optional[int]
outlier = DictProperty("outlier") # type: bool
out_of_band_membership = DictProperty("out_of_band_membership") # type: bool
send_on_behalf_of = DictProperty("send_on_behalf_of") # type: str
@ -113,7 +116,6 @@ class _EventInternalMetadata:
redacted = DictProperty("redacted") # type: bool
txn_id = DictProperty("txn_id") # type: str
token_id = DictProperty("token_id") # type: str
stream_ordering = DictProperty("stream_ordering") # type: int
# XXX: These are set by StreamWorkerStore._set_before_and_after.
# I'm pretty sure that these are never persisted to the database, so shouldn't

View File

@ -12,10 +12,12 @@
# 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.
from typing import Callable
from synapse.events import EventBase
from synapse.events.snapshot import EventContext
from synapse.types import Requester
from synapse.module_api import ModuleApi
from synapse.types import Requester, StateMap
class ThirdPartyEventRules:
@ -38,7 +40,7 @@ class ThirdPartyEventRules:
if module is not None:
self.third_party_rules = module(
config=config, http_client=hs.get_simple_http_client()
config=config, module_api=ModuleApi(hs, hs.get_auth_handler()),
)
async def check_event_allowed(
@ -106,6 +108,46 @@ class ThirdPartyEventRules:
if self.third_party_rules is None:
return True
state_events = await self._get_state_map_for_room(room_id)
ret = await self.third_party_rules.check_threepid_can_be_invited(
medium, address, state_events
)
return ret
async def check_visibility_can_be_modified(
self, room_id: str, new_visibility: str
) -> bool:
"""Check if a room is allowed to be published to, or removed from, the public room
list.
Args:
room_id: The ID of the room.
new_visibility: The new visibility state. Either "public" or "private".
Returns:
True if the room's visibility can be modified, False if not.
"""
if self.third_party_rules is None:
return True
check_func = getattr(self.third_party_rules, "check_visibility_can_be_modified")
if not check_func or not isinstance(check_func, Callable):
return True
state_events = await self._get_state_map_for_room(room_id)
return await check_func(room_id, state_events, new_visibility)
async def _get_state_map_for_room(self, room_id: str) -> StateMap[EventBase]:
"""Given a room ID, return the state events of that room.
Args:
room_id: The ID of the room.
Returns:
A dict mapping (event type, state key) to state event.
"""
state_ids = await self.store.get_filtered_current_state_ids(room_id)
room_state_events = await self.store.get_events(state_ids.values())
@ -113,7 +155,4 @@ class ThirdPartyEventRules:
for key, event_id in state_ids.items():
state_events[key] = room_state_events[event_id]
ret = await self.third_party_rules.check_threepid_can_be_invited(
medium, address, state_events
)
return ret
return state_events

View File

@ -49,6 +49,11 @@ def prune_event(event: EventBase) -> EventBase:
pruned_event_dict, event.room_version, event.internal_metadata.get_dict()
)
# copy the internal fields
pruned_event.internal_metadata.stream_ordering = (
event.internal_metadata.stream_ordering
)
# Mark the event as redacted
pruned_event.internal_metadata.redacted = True

View File

@ -297,6 +297,8 @@ class FederationSender:
sent_pdus_destination_dist_total.inc(len(destinations))
sent_pdus_destination_dist_count.inc()
assert pdu.internal_metadata.stream_ordering
# track the fact that we have a PDU for these destinations,
# to allow us to perform catch-up later on if the remote is unreachable
# for a while.

View File

@ -158,6 +158,7 @@ class PerDestinationQueue:
# yet know if we have anything to catch up (None)
self._pending_pdus.append(pdu)
else:
assert pdu.internal_metadata.stream_ordering
self._catchup_last_skipped = pdu.internal_metadata.stream_ordering
self.attempt_new_transaction()
@ -361,6 +362,7 @@ class PerDestinationQueue:
last_successful_stream_ordering = (
final_pdu.internal_metadata.stream_ordering
)
assert last_successful_stream_ordering
await self._store.set_destination_last_successful_stream_ordering(
self._destination, last_successful_stream_ordering
)

View File

@ -46,6 +46,7 @@ class DirectoryHandler(BaseHandler):
self.config = hs.config
self.enable_room_list_search = hs.config.enable_room_list_search
self.require_membership = hs.config.require_membership_for_aliases
self.third_party_event_rules = hs.get_third_party_event_rules()
self.federation = hs.get_federation_client()
hs.get_federation_registry().register_query_handler(
@ -454,6 +455,15 @@ class DirectoryHandler(BaseHandler):
# per alias creation rule?
raise SynapseError(403, "Not allowed to publish room")
# Check if publishing is blocked by a third party module
allowed_by_third_party_rules = await (
self.third_party_event_rules.check_visibility_can_be_modified(
room_id, visibility
)
)
if not allowed_by_third_party_rules:
raise SynapseError(403, "Not allowed to publish room")
await self.store.set_room_is_public(room_id, making_public)
async def edit_published_appservice_room_list(

View File

@ -3008,6 +3008,9 @@ class FederationHandler(BaseHandler):
elif event.internal_metadata.is_outlier():
return
# the event has been persisted so it should have a stream ordering.
assert event.internal_metadata.stream_ordering
event_pos = PersistedEventPosition(
self._instance_name, event.internal_metadata.stream_ordering
)

View File

@ -682,7 +682,9 @@ class EventCreationHandler:
event.event_id,
prev_event.event_id,
)
return await self.store.get_stream_id_for_event(prev_event.event_id)
# we know it was persisted, so must have a stream ordering
assert prev_event.internal_metadata.stream_ordering
return prev_event.internal_metadata.stream_ordering
return await self.handle_new_client_event(
requester=requester, event=event, context=context, ratelimit=ratelimit

View File

@ -681,6 +681,15 @@ class RoomCreationHandler(BaseHandler):
creator_id=user_id, is_public=is_public, room_version=room_version,
)
# Check whether this visibility value is blocked by a third party module
allowed_by_third_party_rules = await (
self.third_party_event_rules.check_visibility_can_be_modified(
room_id, visibility
)
)
if not allowed_by_third_party_rules:
raise SynapseError(403, "Room visibility value not allowed.")
directory_handler = self.hs.get_handlers().directory_handler
if room_alias:
await directory_handler.create_association(

View File

@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple, Union
from unpaddedbase64 import encode_base64
from synapse import types
from synapse.api.constants import MAX_DEPTH, EventTypes, Membership
from synapse.api.constants import MAX_DEPTH, AccountDataTypes, EventTypes, Membership
from synapse.api.errors import (
AuthError,
Codes,
@ -194,8 +194,9 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
)
if duplicate is not None:
# Discard the new event since this membership change is a no-op.
_, stream_id = await self.store.get_event_ordering(duplicate.event_id)
return duplicate.event_id, stream_id
# we know it was persisted, so must have a stream ordering.
assert duplicate.internal_metadata.stream_ordering
return duplicate.event_id, duplicate.internal_metadata.stream_ordering
prev_state_ids = await context.get_prev_state_ids()
@ -247,7 +248,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
user_account_data, _ = await self.store.get_account_data_for_user(user_id)
# Copy direct message state if applicable
direct_rooms = user_account_data.get("m.direct", {})
direct_rooms = user_account_data.get(AccountDataTypes.DIRECT, {})
# Check which key this room is under
if isinstance(direct_rooms, dict):
@ -258,7 +259,7 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
# Save back to user's m.direct account data
await self.store.add_account_data_for_user(
user_id, "m.direct", direct_rooms
user_id, AccountDataTypes.DIRECT, direct_rooms
)
break
@ -441,12 +442,12 @@ class RoomMemberHandler(metaclass=abc.ABCMeta):
same_membership = old_membership == effective_membership_state
same_sender = requester.user.to_string() == old_state.sender
if same_sender and same_membership and same_content:
_, stream_id = await self.store.get_event_ordering(
old_state.event_id
)
# duplicate event.
# we know it was persisted, so must have a stream ordering.
assert old_state.internal_metadata.stream_ordering
return (
old_state.event_id,
stream_id,
old_state.internal_metadata.stream_ordering,
)
if old_membership in ["ban", "leave"] and action == "kick":

View File

@ -21,7 +21,7 @@ from typing import TYPE_CHECKING, Any, Dict, FrozenSet, List, Optional, Set, Tup
import attr
from prometheus_client import Counter
from synapse.api.constants import EventTypes, Membership
from synapse.api.constants import AccountDataTypes, EventTypes, Membership
from synapse.api.filtering import FilterCollection
from synapse.events import EventBase
from synapse.logging.context import current_context
@ -1378,13 +1378,16 @@ class SyncHandler:
return set(), set(), set(), set()
ignored_account_data = await self.store.get_global_account_data_by_type_for_user(
"m.ignored_user_list", user_id=user_id
AccountDataTypes.IGNORED_USER_LIST, user_id=user_id
)
# If there is ignored users account data and it matches the proper type,
# then use it.
ignored_users = frozenset() # type: FrozenSet[str]
if ignored_account_data:
ignored_users = ignored_account_data.get("ignored_users", {}).keys()
else:
ignored_users = frozenset()
ignored_users_data = ignored_account_data.get("ignored_users", {})
if isinstance(ignored_users_data, dict):
ignored_users = frozenset(ignored_users_data.keys())
if since_token:
room_changes = await self._get_rooms_changed(
@ -1478,7 +1481,7 @@ class SyncHandler:
return False
async def _get_rooms_changed(
self, sync_result_builder: "SyncResultBuilder", ignored_users: Set[str]
self, sync_result_builder: "SyncResultBuilder", ignored_users: FrozenSet[str]
) -> _RoomChanges:
"""Gets the the changes that have happened since the last sync.
"""
@ -1690,7 +1693,7 @@ class SyncHandler:
return _RoomChanges(room_entries, invited, newly_joined_rooms, newly_left_rooms)
async def _get_all_rooms(
self, sync_result_builder: "SyncResultBuilder", ignored_users: Set[str]
self, sync_result_builder: "SyncResultBuilder", ignored_users: FrozenSet[str]
) -> _RoomChanges:
"""Returns entries for all rooms for the user.
@ -1764,7 +1767,7 @@ class SyncHandler:
async def _generate_room_entry(
self,
sync_result_builder: "SyncResultBuilder",
ignored_users: Set[str],
ignored_users: FrozenSet[str],
room_builder: "RoomSyncResultBuilder",
ephemeral: List[JsonDict],
tags: Optional[Dict[str, Dict[str, Any]]],

View File

@ -14,13 +14,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from typing import TYPE_CHECKING
from twisted.internet import defer
from synapse.http.client import SimpleHttpClient
from synapse.http.site import SynapseRequest
from synapse.logging.context import make_deferred_yieldable, run_in_background
from synapse.types import UserID
if TYPE_CHECKING:
from synapse.server import HomeServer
"""
This package defines the 'stable' API which can be used by extension modules which
are loaded into Synapse.
@ -43,6 +48,27 @@ class ModuleApi:
self._auth = hs.get_auth()
self._auth_handler = auth_handler
# We expose these as properties below in order to attach a helpful docstring.
self._http_client = hs.get_simple_http_client() # type: SimpleHttpClient
self._public_room_list_manager = PublicRoomListManager(hs)
@property
def http_client(self):
"""Allows making outbound HTTP requests to remote resources.
An instance of synapse.http.client.SimpleHttpClient
"""
return self._http_client
@property
def public_room_list_manager(self):
"""Allows adding to, removing from and checking the status of rooms in the
public room list.
An instance of synapse.module_api.PublicRoomListManager
"""
return self._public_room_list_manager
def get_user_by_req(self, req, allow_guest=False):
"""Check the access_token provided for a request
@ -266,3 +292,44 @@ class ModuleApi:
await self._auth_handler.complete_sso_login(
registered_user_id, request, client_redirect_url,
)
class PublicRoomListManager:
"""Contains methods for adding to, removing from and querying whether a room
is in the public room list.
"""
def __init__(self, hs: "HomeServer"):
self._store = hs.get_datastore()
async def room_is_in_public_room_list(self, room_id: str) -> bool:
"""Checks whether a room is in the public room list.
Args:
room_id: The ID of the room.
Returns:
Whether the room is in the public room list. Returns False if the room does
not exist.
"""
room = await self._store.get_room(room_id)
if not room:
return False
return room.get("is_public", False)
async def add_room_to_public_room_list(self, room_id: str) -> None:
"""Publishes a room to the public room list.
Args:
room_id: The ID of the room.
"""
await self._store.set_room_is_public(room_id, True)
async def remove_room_from_public_room_list(self, room_id: str) -> None:
"""Removes a room from the public room list.
Args:
room_id: The ID of the room.
"""
await self._store.set_room_is_public(room_id, False)

View File

@ -57,6 +57,7 @@ from synapse.rest.admin.users import (
UsersRestServletV2,
WhoisRestServlet,
)
from synapse.types import RoomStreamToken
from synapse.util.versionstring import get_version_string
logger = logging.getLogger(__name__)
@ -109,7 +110,9 @@ class PurgeHistoryRestServlet(RestServlet):
if event.room_id != room_id:
raise SynapseError(400, "Event is for wrong room.")
room_token = await self.store.get_topological_token_for_event(event_id)
room_token = RoomStreamToken(
event.depth, event.internal_metadata.stream_ordering
)
token = await room_token.to_string(self.store)
logger.info("[purge] purging up to token %s (event_id %s)", token, event_id)

View File

@ -18,6 +18,7 @@ import abc
import logging
from typing import Dict, List, Optional, Tuple
from synapse.api.constants import AccountDataTypes
from synapse.storage._base import SQLBaseStore, db_to_json
from synapse.storage.database import DatabasePool
from synapse.storage.util.id_generators import StreamIdGenerator
@ -291,14 +292,18 @@ class AccountDataWorkerStore(SQLBaseStore, metaclass=abc.ABCMeta):
self, ignored_user_id: str, ignorer_user_id: str, cache_context: _CacheContext
) -> bool:
ignored_account_data = await self.get_global_account_data_by_type_for_user(
"m.ignored_user_list",
AccountDataTypes.IGNORED_USER_LIST,
ignorer_user_id,
on_invalidate=cache_context.invalidate,
)
if not ignored_account_data:
return False
return ignored_user_id in ignored_account_data.get("ignored_users", {})
try:
return ignored_user_id in ignored_account_data.get("ignored_users", {})
except TypeError:
# The type of the ignored_users field is invalid.
return False
class AccountDataStore(AccountDataWorkerStore):

View File

@ -331,6 +331,10 @@ class PersistEventsStore:
min_stream_order = events_and_contexts[0][0].internal_metadata.stream_ordering
max_stream_order = events_and_contexts[-1][0].internal_metadata.stream_ordering
# stream orderings should have been assigned by now
assert min_stream_order
assert max_stream_order
self._update_forward_extremities_txn(
txn,
new_forward_extremities=new_forward_extremeties,

View File

@ -723,6 +723,7 @@ class EventsWorkerStore(SQLBaseStore):
internal_metadata_dict=internal_metadata,
rejected_reason=rejected_reason,
)
original_ev.internal_metadata.stream_ordering = row["stream_ordering"]
event_map[event_id] = original_ev
@ -790,6 +791,8 @@ class EventsWorkerStore(SQLBaseStore):
* event_id (str)
* stream_ordering (int): stream ordering for this event
* json (str): json-encoded event structure
* internal_metadata (str): json-encoded internal metadata dict
@ -822,13 +825,15 @@ class EventsWorkerStore(SQLBaseStore):
sql = """\
SELECT
e.event_id,
e.internal_metadata,
e.json,
e.format_version,
e.stream_ordering,
ej.internal_metadata,
ej.json,
ej.format_version,
r.room_version,
rej.reason
FROM event_json as e
LEFT JOIN rooms r USING (room_id)
FROM events AS e
JOIN event_json AS ej USING (event_id)
LEFT JOIN rooms r ON r.room_id = e.room_id
LEFT JOIN rejections as rej USING (event_id)
WHERE """
@ -842,11 +847,12 @@ class EventsWorkerStore(SQLBaseStore):
event_id = row[0]
event_dict[event_id] = {
"event_id": event_id,
"internal_metadata": row[1],
"json": row[2],
"format_version": row[3],
"room_version_id": row[4],
"rejected_reason": row[5],
"stream_ordering": row[1],
"internal_metadata": row[2],
"json": row[3],
"format_version": row[4],
"room_version_id": row[5],
"rejected_reason": row[6],
"redactions": [],
}

View File

@ -589,19 +589,6 @@ class StreamWorkerStore(EventsWorkerStore, SQLBaseStore, metaclass=abc.ABCMeta):
)
return "t%d-%d" % (topo, token)
async def get_stream_id_for_event(self, event_id: str) -> int:
"""The stream ID for an event
Args:
event_id: The id of the event to look up a stream token for.
Raises:
StoreError if the event wasn't in the database.
Returns:
A stream ID.
"""
return await self.db_pool.runInteraction(
"get_stream_id_for_event", self.get_stream_id_for_event_txn, event_id,
)
def get_stream_id_for_event_txn(
self, txn: LoggingTransaction, event_id: str, allow_none=False,
) -> int:

View File

@ -248,6 +248,8 @@ class EventsPersistenceStorage:
await make_deferred_yieldable(deferred)
event_stream_id = event.internal_metadata.stream_ordering
# stream ordering should have been assigned by now
assert event_stream_id
pos = PersistedEventPosition(self._instance_name, event_stream_id)
return pos, self.main_store.get_room_max_token()

View File

@ -16,7 +16,7 @@
import logging
import operator
from synapse.api.constants import EventTypes, Membership
from synapse.api.constants import AccountDataTypes, EventTypes, Membership
from synapse.events.utils import prune_event
from synapse.storage import Storage
from synapse.storage.state import StateFilter
@ -77,15 +77,14 @@ async def filter_events_for_client(
)
ignore_dict_content = await storage.main.get_global_account_data_by_type_for_user(
"m.ignored_user_list", user_id
AccountDataTypes.IGNORED_USER_LIST, user_id
)
# FIXME: This will explode if people upload something incorrect.
ignore_list = frozenset(
ignore_dict_content.get("ignored_users", {}).keys()
if ignore_dict_content
else []
)
ignore_list = frozenset()
if ignore_dict_content:
ignored_users_dict = ignore_dict_content.get("ignored_users", {})
if isinstance(ignored_users_dict, dict):
ignore_list = frozenset(ignored_users_dict.keys())
erased_senders = await storage.main.are_users_erased((e.sender for e in events))

View File

@ -12,13 +12,20 @@
# 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.
from synapse.module_api import ModuleApi
from synapse.rest import admin
from synapse.rest.client.v1 import login, room
from tests.unittest import HomeserverTestCase
class ModuleApiTestCase(HomeserverTestCase):
servlets = [
admin.register_servlets,
login.register_servlets,
room.register_servlets,
]
def prepare(self, reactor, clock, homeserver):
self.store = homeserver.get_datastore()
self.module_api = ModuleApi(homeserver, homeserver.get_auth_handler())
@ -52,3 +59,50 @@ class ModuleApiTestCase(HomeserverTestCase):
# Check that the displayname was assigned
displayname = self.get_success(self.store.get_profile_displayname("bob"))
self.assertEqual(displayname, "Bobberino")
def test_public_rooms(self):
"""Tests that a room can be added and removed from the public rooms list,
as well as have its public rooms directory state queried.
"""
# Create a user and room to play with
user_id = self.register_user("kermit", "monkey")
tok = self.login("kermit", "monkey")
room_id = self.helper.create_room_as(user_id, tok=tok)
# The room should not currently be in the public rooms directory
is_in_public_rooms = self.get_success(
self.module_api.public_room_list_manager.room_is_in_public_room_list(
room_id
)
)
self.assertFalse(is_in_public_rooms)
# Let's try adding it to the public rooms directory
self.get_success(
self.module_api.public_room_list_manager.add_room_to_public_room_list(
room_id
)
)
# And checking whether it's in there...
is_in_public_rooms = self.get_success(
self.module_api.public_room_list_manager.room_is_in_public_room_list(
room_id
)
)
self.assertTrue(is_in_public_rooms)
# Let's remove it again
self.get_success(
self.module_api.public_room_list_manager.remove_room_from_public_room_list(
room_id
)
)
# Should be gone
is_in_public_rooms = self.get_success(
self.module_api.public_room_list_manager.room_is_in_public_room_list(
room_id
)
)
self.assertFalse(is_in_public_rooms)

View File

@ -12,18 +12,23 @@
# 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.
from synapse.rest import admin
from synapse.rest.client.v1 import login, room
from synapse.types import Requester
from tests import unittest
class ThirdPartyRulesTestModule:
def __init__(self, config):
def __init__(self, config, *args, **kwargs):
pass
def check_event_allowed(self, event, context):
async def on_create_room(
self, requester: Requester, config: dict, is_requester_admin: bool
):
return True
async def check_event_allowed(self, event, context):
if event.type == "foo.bar.forbidden":
return False
else:
@ -51,29 +56,31 @@ class ThirdPartyRulesTestCase(unittest.HomeserverTestCase):
self.hs = self.setup_test_homeserver(config=config)
return self.hs
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.
"""
user_id = self.register_user("kermit", "monkey")
tok = self.login("kermit", "monkey")
room_id = self.helper.create_room_as(user_id, tok=tok)
request, channel = self.make_request(
"PUT",
"/_matrix/client/r0/rooms/%s/send/foo.bar.allowed/1" % room_id,
"/_matrix/client/r0/rooms/%s/send/foo.bar.allowed/1" % self.room_id,
{},
access_token=tok,
access_token=self.tok,
)
self.render(request)
self.assertEquals(channel.result["code"], b"200", channel.result)
request, channel = self.make_request(
"PUT",
"/_matrix/client/r0/rooms/%s/send/foo.bar.forbidden/1" % room_id,
"/_matrix/client/r0/rooms/%s/send/foo.bar.forbidden/1" % self.room_id,
{},
access_token=tok,
access_token=self.tok,
)
self.render(request)
self.assertEquals(channel.result["code"], b"403", channel.result)