You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

618 lines
23 KiB

  1. # Copyright 2015, 2016 OpenMarket Ltd
  2. # Copyright 2017 Vector Creations Ltd
  3. # Copyright 2018-2019 New Vector Ltd
  4. # Copyright 2019 The Matrix.org Foundation C.I.C.
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License");
  7. # you may not use this file except in compliance with the License.
  8. # You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. from typing import List
  18. from unittest.mock import patch
  19. import jsonschema
  20. from twisted.test.proto_helpers import MemoryReactor
  21. from synapse.api.constants import EduTypes, EventContentFields
  22. from synapse.api.errors import SynapseError
  23. from synapse.api.filtering import Filter
  24. from synapse.api.presence import UserPresenceState
  25. from synapse.server import HomeServer
  26. from synapse.types import JsonDict, UserID
  27. from synapse.util import Clock
  28. from synapse.util.frozenutils import freeze
  29. from tests import unittest
  30. from tests.events.test_utils import MockEvent
  31. user_id = UserID.from_string("@test_user:test")
  32. user2_id = UserID.from_string("@test_user2:test")
  33. class FilteringTestCase(unittest.HomeserverTestCase):
  34. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  35. self.filtering = hs.get_filtering()
  36. self.datastore = hs.get_datastores().main
  37. def test_errors_on_invalid_filters(self) -> None:
  38. # See USER_FILTER_SCHEMA for the filter schema.
  39. invalid_filters: List[JsonDict] = [
  40. # `account_data` must be a dictionary
  41. {"account_data": "Hello World"},
  42. # `event_format` must be "client" or "federation"
  43. {"event_format": "other"},
  44. # `not_rooms` must contain valid room IDs
  45. {"room": {"not_rooms": ["#foo:pik-test"]}},
  46. # `senders` must contain valid user IDs
  47. {"presence": {"senders": ["@bar;pik.test.com"]}},
  48. ]
  49. for filter in invalid_filters:
  50. with self.assertRaises(SynapseError):
  51. self.filtering.check_valid_filter(filter)
  52. def test_ignores_unknown_filter_fields(self) -> None:
  53. # For forward compatibility, we must ignore unknown filter fields.
  54. # See USER_FILTER_SCHEMA for the filter schema.
  55. filters: List[JsonDict] = [
  56. {"org.matrix.msc9999.future_option": True},
  57. {"presence": {"org.matrix.msc9999.future_option": True}},
  58. {"room": {"org.matrix.msc9999.future_option": True}},
  59. {"room": {"timeline": {"org.matrix.msc9999.future_option": True}}},
  60. ]
  61. for filter in filters:
  62. self.filtering.check_valid_filter(filter)
  63. # Must not raise.
  64. def test_valid_filters(self) -> None:
  65. valid_filters: List[JsonDict] = [
  66. {
  67. "room": {
  68. "timeline": {"limit": 20},
  69. "state": {"not_types": ["m.room.member"]},
  70. "ephemeral": {"limit": 0, "not_types": ["*"]},
  71. "include_leave": False,
  72. "rooms": ["!dee:pik-test"],
  73. "not_rooms": ["!gee:pik-test"],
  74. "account_data": {"limit": 0, "types": ["*"]},
  75. }
  76. },
  77. {
  78. "room": {
  79. "state": {
  80. "types": ["m.room.*"],
  81. "not_rooms": ["!726s6s6q:example.com"],
  82. },
  83. "timeline": {
  84. "limit": 10,
  85. "types": ["m.room.message"],
  86. "not_rooms": ["!726s6s6q:example.com"],
  87. "not_senders": ["@spam:example.com"],
  88. "org.matrix.labels": ["#fun"],
  89. "org.matrix.not_labels": ["#work"],
  90. },
  91. "ephemeral": {
  92. "types": [EduTypes.RECEIPT, EduTypes.TYPING],
  93. "not_rooms": ["!726s6s6q:example.com"],
  94. "not_senders": ["@spam:example.com"],
  95. },
  96. },
  97. "presence": {
  98. "types": [EduTypes.PRESENCE],
  99. "not_senders": ["@alice:example.com"],
  100. },
  101. "event_format": "client",
  102. "event_fields": ["type", "content", "sender"],
  103. },
  104. # (note that event_fields is implemented in
  105. # synapse.events.utils.serialize_event, and so whether this actually works
  106. # is tested elsewhere. We just want to check that it is allowed through the
  107. # filter validation)
  108. {"event_fields": [r"foo\.bar"]},
  109. ]
  110. for filter in valid_filters:
  111. try:
  112. self.filtering.check_valid_filter(filter)
  113. except jsonschema.ValidationError as e:
  114. self.fail(e)
  115. def test_limits_are_applied(self) -> None:
  116. # TODO
  117. pass
  118. def test_definition_types_works_with_literals(self) -> None:
  119. definition = {"types": ["m.room.message", "org.matrix.foo.bar"]}
  120. event = MockEvent(sender="@foo:bar", type="m.room.message", room_id="!foo:bar")
  121. self.assertTrue(Filter(self.hs, definition)._check(event))
  122. def test_definition_types_works_with_wildcards(self) -> None:
  123. definition = {"types": ["m.*", "org.matrix.foo.bar"]}
  124. event = MockEvent(sender="@foo:bar", type="m.room.message", room_id="!foo:bar")
  125. self.assertTrue(Filter(self.hs, definition)._check(event))
  126. def test_definition_types_works_with_unknowns(self) -> None:
  127. definition = {"types": ["m.room.message", "org.matrix.foo.bar"]}
  128. event = MockEvent(
  129. sender="@foo:bar",
  130. type="now.for.something.completely.different",
  131. room_id="!foo:bar",
  132. )
  133. self.assertFalse(Filter(self.hs, definition)._check(event))
  134. def test_definition_not_types_works_with_literals(self) -> None:
  135. definition = {"not_types": ["m.room.message", "org.matrix.foo.bar"]}
  136. event = MockEvent(sender="@foo:bar", type="m.room.message", room_id="!foo:bar")
  137. self.assertFalse(Filter(self.hs, definition)._check(event))
  138. def test_definition_not_types_works_with_wildcards(self) -> None:
  139. definition = {"not_types": ["m.room.message", "org.matrix.*"]}
  140. event = MockEvent(
  141. sender="@foo:bar", type="org.matrix.custom.event", room_id="!foo:bar"
  142. )
  143. self.assertFalse(Filter(self.hs, definition)._check(event))
  144. def test_definition_not_types_works_with_unknowns(self) -> None:
  145. definition = {"not_types": ["m.*", "org.*"]}
  146. event = MockEvent(sender="@foo:bar", type="com.nom.nom.nom", room_id="!foo:bar")
  147. self.assertTrue(Filter(self.hs, definition)._check(event))
  148. def test_definition_not_types_takes_priority_over_types(self) -> None:
  149. definition = {
  150. "not_types": ["m.*", "org.*"],
  151. "types": ["m.room.message", "m.room.topic"],
  152. }
  153. event = MockEvent(sender="@foo:bar", type="m.room.topic", room_id="!foo:bar")
  154. self.assertFalse(Filter(self.hs, definition)._check(event))
  155. def test_definition_senders_works_with_literals(self) -> None:
  156. definition = {"senders": ["@flibble:wibble"]}
  157. event = MockEvent(
  158. sender="@flibble:wibble", type="com.nom.nom.nom", room_id="!foo:bar"
  159. )
  160. self.assertTrue(Filter(self.hs, definition)._check(event))
  161. def test_definition_senders_works_with_unknowns(self) -> None:
  162. definition = {"senders": ["@flibble:wibble"]}
  163. event = MockEvent(
  164. sender="@challenger:appears", type="com.nom.nom.nom", room_id="!foo:bar"
  165. )
  166. self.assertFalse(Filter(self.hs, definition)._check(event))
  167. def test_definition_not_senders_works_with_literals(self) -> None:
  168. definition = {"not_senders": ["@flibble:wibble"]}
  169. event = MockEvent(
  170. sender="@flibble:wibble", type="com.nom.nom.nom", room_id="!foo:bar"
  171. )
  172. self.assertFalse(Filter(self.hs, definition)._check(event))
  173. def test_definition_not_senders_works_with_unknowns(self) -> None:
  174. definition = {"not_senders": ["@flibble:wibble"]}
  175. event = MockEvent(
  176. sender="@challenger:appears", type="com.nom.nom.nom", room_id="!foo:bar"
  177. )
  178. self.assertTrue(Filter(self.hs, definition)._check(event))
  179. def test_definition_not_senders_takes_priority_over_senders(self) -> None:
  180. definition = {
  181. "not_senders": ["@misspiggy:muppets"],
  182. "senders": ["@kermit:muppets", "@misspiggy:muppets"],
  183. }
  184. event = MockEvent(
  185. sender="@misspiggy:muppets", type="m.room.topic", room_id="!foo:bar"
  186. )
  187. self.assertFalse(Filter(self.hs, definition)._check(event))
  188. def test_definition_rooms_works_with_literals(self) -> None:
  189. definition = {"rooms": ["!secretbase:unknown"]}
  190. event = MockEvent(
  191. sender="@foo:bar", type="m.room.message", room_id="!secretbase:unknown"
  192. )
  193. self.assertTrue(Filter(self.hs, definition)._check(event))
  194. def test_definition_rooms_works_with_unknowns(self) -> None:
  195. definition = {"rooms": ["!secretbase:unknown"]}
  196. event = MockEvent(
  197. sender="@foo:bar",
  198. type="m.room.message",
  199. room_id="!anothersecretbase:unknown",
  200. )
  201. self.assertFalse(Filter(self.hs, definition)._check(event))
  202. def test_definition_not_rooms_works_with_literals(self) -> None:
  203. definition = {"not_rooms": ["!anothersecretbase:unknown"]}
  204. event = MockEvent(
  205. sender="@foo:bar",
  206. type="m.room.message",
  207. room_id="!anothersecretbase:unknown",
  208. )
  209. self.assertFalse(Filter(self.hs, definition)._check(event))
  210. def test_definition_not_rooms_works_with_unknowns(self) -> None:
  211. definition = {"not_rooms": ["!secretbase:unknown"]}
  212. event = MockEvent(
  213. sender="@foo:bar",
  214. type="m.room.message",
  215. room_id="!anothersecretbase:unknown",
  216. )
  217. self.assertTrue(Filter(self.hs, definition)._check(event))
  218. def test_definition_not_rooms_takes_priority_over_rooms(self) -> None:
  219. definition = {
  220. "not_rooms": ["!secretbase:unknown"],
  221. "rooms": ["!secretbase:unknown"],
  222. }
  223. event = MockEvent(
  224. sender="@foo:bar", type="m.room.message", room_id="!secretbase:unknown"
  225. )
  226. self.assertFalse(Filter(self.hs, definition)._check(event))
  227. def test_definition_combined_event(self) -> None:
  228. definition = {
  229. "not_senders": ["@misspiggy:muppets"],
  230. "senders": ["@kermit:muppets"],
  231. "rooms": ["!stage:unknown"],
  232. "not_rooms": ["!piggyshouse:muppets"],
  233. "types": ["m.room.message", "muppets.kermit.*"],
  234. "not_types": ["muppets.misspiggy.*"],
  235. }
  236. event = MockEvent(
  237. sender="@kermit:muppets", # yup
  238. type="m.room.message", # yup
  239. room_id="!stage:unknown", # yup
  240. )
  241. self.assertTrue(Filter(self.hs, definition)._check(event))
  242. def test_definition_combined_event_bad_sender(self) -> None:
  243. definition = {
  244. "not_senders": ["@misspiggy:muppets"],
  245. "senders": ["@kermit:muppets"],
  246. "rooms": ["!stage:unknown"],
  247. "not_rooms": ["!piggyshouse:muppets"],
  248. "types": ["m.room.message", "muppets.kermit.*"],
  249. "not_types": ["muppets.misspiggy.*"],
  250. }
  251. event = MockEvent(
  252. sender="@misspiggy:muppets", # nope
  253. type="m.room.message", # yup
  254. room_id="!stage:unknown", # yup
  255. )
  256. self.assertFalse(Filter(self.hs, definition)._check(event))
  257. def test_definition_combined_event_bad_room(self) -> None:
  258. definition = {
  259. "not_senders": ["@misspiggy:muppets"],
  260. "senders": ["@kermit:muppets"],
  261. "rooms": ["!stage:unknown"],
  262. "not_rooms": ["!piggyshouse:muppets"],
  263. "types": ["m.room.message", "muppets.kermit.*"],
  264. "not_types": ["muppets.misspiggy.*"],
  265. }
  266. event = MockEvent(
  267. sender="@kermit:muppets", # yup
  268. type="m.room.message", # yup
  269. room_id="!piggyshouse:muppets", # nope
  270. )
  271. self.assertFalse(Filter(self.hs, definition)._check(event))
  272. def test_definition_combined_event_bad_type(self) -> None:
  273. definition = {
  274. "not_senders": ["@misspiggy:muppets"],
  275. "senders": ["@kermit:muppets"],
  276. "rooms": ["!stage:unknown"],
  277. "not_rooms": ["!piggyshouse:muppets"],
  278. "types": ["m.room.message", "muppets.kermit.*"],
  279. "not_types": ["muppets.misspiggy.*"],
  280. }
  281. event = MockEvent(
  282. sender="@kermit:muppets", # yup
  283. type="muppets.misspiggy.kisses", # nope
  284. room_id="!stage:unknown", # yup
  285. )
  286. self.assertFalse(Filter(self.hs, definition)._check(event))
  287. def test_filter_labels(self) -> None:
  288. definition = {"org.matrix.labels": ["#fun"]}
  289. event = MockEvent(
  290. sender="@foo:bar",
  291. type="m.room.message",
  292. room_id="!secretbase:unknown",
  293. content={EventContentFields.LABELS: ["#fun"]},
  294. )
  295. self.assertTrue(Filter(self.hs, definition)._check(event))
  296. event = MockEvent(
  297. sender="@foo:bar",
  298. type="m.room.message",
  299. room_id="!secretbase:unknown",
  300. content={EventContentFields.LABELS: ["#notfun"]},
  301. )
  302. self.assertFalse(Filter(self.hs, definition)._check(event))
  303. # check it works with frozen dictionaries too
  304. event = MockEvent(
  305. sender="@foo:bar",
  306. type="m.room.message",
  307. room_id="!secretbase:unknown",
  308. content=freeze({EventContentFields.LABELS: ["#fun"]}),
  309. )
  310. self.assertTrue(Filter(self.hs, definition)._check(event))
  311. def test_filter_not_labels(self) -> None:
  312. definition = {"org.matrix.not_labels": ["#fun"]}
  313. event = MockEvent(
  314. sender="@foo:bar",
  315. type="m.room.message",
  316. room_id="!secretbase:unknown",
  317. content={EventContentFields.LABELS: ["#fun"]},
  318. )
  319. self.assertFalse(Filter(self.hs, definition)._check(event))
  320. event = MockEvent(
  321. sender="@foo:bar",
  322. type="m.room.message",
  323. room_id="!secretbase:unknown",
  324. content={EventContentFields.LABELS: ["#notfun"]},
  325. )
  326. self.assertTrue(Filter(self.hs, definition)._check(event))
  327. @unittest.override_config({"experimental_features": {"msc3874_enabled": True}})
  328. def test_filter_rel_type(self) -> None:
  329. definition = {"org.matrix.msc3874.rel_types": ["m.thread"]}
  330. event = MockEvent(
  331. sender="@foo:bar",
  332. type="m.room.message",
  333. room_id="!secretbase:unknown",
  334. content={},
  335. )
  336. self.assertFalse(Filter(self.hs, definition)._check(event))
  337. event = MockEvent(
  338. sender="@foo:bar",
  339. type="m.room.message",
  340. room_id="!secretbase:unknown",
  341. content={"m.relates_to": {"event_id": "$abc", "rel_type": "m.reference"}},
  342. )
  343. self.assertFalse(Filter(self.hs, definition)._check(event))
  344. event = MockEvent(
  345. sender="@foo:bar",
  346. type="m.room.message",
  347. room_id="!secretbase:unknown",
  348. content={"m.relates_to": {"event_id": "$abc", "rel_type": "m.thread"}},
  349. )
  350. self.assertTrue(Filter(self.hs, definition)._check(event))
  351. @unittest.override_config({"experimental_features": {"msc3874_enabled": True}})
  352. def test_filter_not_rel_type(self) -> None:
  353. definition = {"org.matrix.msc3874.not_rel_types": ["m.thread"]}
  354. event = MockEvent(
  355. sender="@foo:bar",
  356. type="m.room.message",
  357. room_id="!secretbase:unknown",
  358. content={"m.relates_to": {"event_id": "$abc", "rel_type": "m.thread"}},
  359. )
  360. self.assertFalse(Filter(self.hs, definition)._check(event))
  361. event = MockEvent(
  362. sender="@foo:bar",
  363. type="m.room.message",
  364. room_id="!secretbase:unknown",
  365. content={},
  366. )
  367. self.assertTrue(Filter(self.hs, definition)._check(event))
  368. event = MockEvent(
  369. sender="@foo:bar",
  370. type="m.room.message",
  371. room_id="!secretbase:unknown",
  372. content={"m.relates_to": {"event_id": "$abc", "rel_type": "m.reference"}},
  373. )
  374. self.assertTrue(Filter(self.hs, definition)._check(event))
  375. def test_filter_presence_match(self) -> None:
  376. """Check that filter_presence return events which matches the filter."""
  377. user_filter_json = {"presence": {"senders": ["@foo:bar"]}}
  378. filter_id = self.get_success(
  379. self.datastore.add_user_filter(
  380. user_id=user_id, user_filter=user_filter_json
  381. )
  382. )
  383. presence_states = [
  384. UserPresenceState(
  385. user_id="@foo:bar",
  386. state="unavailable",
  387. last_active_ts=0,
  388. last_federation_update_ts=0,
  389. last_user_sync_ts=0,
  390. status_msg=None,
  391. currently_active=False,
  392. ),
  393. ]
  394. user_filter = self.get_success(
  395. self.filtering.get_user_filter(user_id=user_id, filter_id=filter_id)
  396. )
  397. results = self.get_success(user_filter.filter_presence(presence_states))
  398. self.assertEqual(presence_states, results)
  399. def test_filter_presence_no_match(self) -> None:
  400. """Check that filter_presence does not return events rejected by the filter."""
  401. user_filter_json = {"presence": {"not_senders": ["@foo:bar"]}}
  402. filter_id = self.get_success(
  403. self.datastore.add_user_filter(
  404. user_id=user2_id, user_filter=user_filter_json
  405. )
  406. )
  407. presence_states = [
  408. UserPresenceState(
  409. user_id="@foo:bar",
  410. state="unavailable",
  411. last_active_ts=0,
  412. last_federation_update_ts=0,
  413. last_user_sync_ts=0,
  414. status_msg=None,
  415. currently_active=False,
  416. ),
  417. ]
  418. user_filter = self.get_success(
  419. self.filtering.get_user_filter(user_id=user2_id, filter_id=filter_id)
  420. )
  421. results = self.get_success(user_filter.filter_presence(presence_states))
  422. self.assertEqual([], results)
  423. def test_filter_room_state_match(self) -> None:
  424. user_filter_json = {"room": {"state": {"types": ["m.*"]}}}
  425. filter_id = self.get_success(
  426. self.datastore.add_user_filter(
  427. user_id=user_id, user_filter=user_filter_json
  428. )
  429. )
  430. event = MockEvent(sender="@foo:bar", type="m.room.topic", room_id="!foo:bar")
  431. events = [event]
  432. user_filter = self.get_success(
  433. self.filtering.get_user_filter(user_id=user_id, filter_id=filter_id)
  434. )
  435. results = self.get_success(user_filter.filter_room_state(events=events))
  436. self.assertEqual(events, results)
  437. def test_filter_room_state_no_match(self) -> None:
  438. user_filter_json = {"room": {"state": {"types": ["m.*"]}}}
  439. filter_id = self.get_success(
  440. self.datastore.add_user_filter(
  441. user_id=user_id, user_filter=user_filter_json
  442. )
  443. )
  444. event = MockEvent(
  445. sender="@foo:bar", type="org.matrix.custom.event", room_id="!foo:bar"
  446. )
  447. events = [event]
  448. user_filter = self.get_success(
  449. self.filtering.get_user_filter(user_id=user_id, filter_id=filter_id)
  450. )
  451. results = self.get_success(user_filter.filter_room_state(events))
  452. self.assertEqual([], results)
  453. def test_filter_rooms(self) -> None:
  454. definition = {
  455. "rooms": ["!allowed:example.com", "!excluded:example.com"],
  456. "not_rooms": ["!excluded:example.com"],
  457. }
  458. room_ids = [
  459. "!allowed:example.com", # Allowed because in rooms and not in not_rooms.
  460. "!excluded:example.com", # Disallowed because in not_rooms.
  461. "!not_included:example.com", # Disallowed because not in rooms.
  462. ]
  463. filtered_room_ids = list(Filter(self.hs, definition).filter_rooms(room_ids))
  464. self.assertEqual(filtered_room_ids, ["!allowed:example.com"])
  465. def test_filter_relations(self) -> None:
  466. events = [
  467. # An event without a relation.
  468. MockEvent(
  469. event_id="$no_relation",
  470. sender="@foo:bar",
  471. type="org.matrix.custom.event",
  472. room_id="!foo:bar",
  473. ),
  474. # An event with a relation.
  475. MockEvent(
  476. event_id="$with_relation",
  477. sender="@foo:bar",
  478. type="org.matrix.custom.event",
  479. room_id="!foo:bar",
  480. ),
  481. ]
  482. jsondicts: List[JsonDict] = [{}]
  483. # For the following tests we patch the datastore method (intead of injecting
  484. # events). This is a bit cheeky, but tests the logic of _check_event_relations.
  485. # Filter for a particular sender.
  486. definition = {"related_by_senders": ["@foo:bar"]}
  487. async def events_have_relations(*args: object, **kwargs: object) -> List[str]:
  488. return ["$with_relation"]
  489. with patch.object(
  490. self.datastore, "events_have_relations", new=events_have_relations
  491. ):
  492. filtered_events = list(
  493. self.get_success(
  494. Filter(self.hs, definition)._check_event_relations(events)
  495. )
  496. )
  497. # Non-EventBase objects get passed through.
  498. filtered_jsondicts = list(
  499. self.get_success(
  500. Filter(self.hs, definition)._check_event_relations(jsondicts)
  501. )
  502. )
  503. self.assertEqual(filtered_events, events[1:])
  504. self.assertEqual(filtered_jsondicts, [{}])
  505. def test_add_filter(self) -> None:
  506. user_filter_json = {"room": {"state": {"types": ["m.*"]}}}
  507. filter_id = self.get_success(
  508. self.filtering.add_user_filter(
  509. user_id=user_id, user_filter=user_filter_json
  510. )
  511. )
  512. self.assertEqual(filter_id, 0)
  513. self.assertEqual(
  514. user_filter_json,
  515. (
  516. self.get_success(
  517. self.datastore.get_user_filter(user_id=user_id, filter_id=0)
  518. )
  519. ),
  520. )
  521. def test_get_filter(self) -> None:
  522. user_filter_json = {"room": {"state": {"types": ["m.*"]}}}
  523. filter_id = self.get_success(
  524. self.datastore.add_user_filter(
  525. user_id=user_id, user_filter=user_filter_json
  526. )
  527. )
  528. filter = self.get_success(
  529. self.filtering.get_user_filter(user_id=user_id, filter_id=filter_id)
  530. )
  531. self.assertEqual(filter.get_filter_json(), user_filter_json)
  532. self.assertRegex(repr(filter), r"<FilterCollection \{.*\}>")