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.
 
 
 
 
 
 

790 lines
25 KiB

  1. # Copyright 2016 OpenMarket Ltd
  2. # Copyright 2018 New Vector Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from typing import Any, Dict
  16. from unittest.mock import AsyncMock
  17. from parameterized import parameterized
  18. from twisted.test.proto_helpers import MemoryReactor
  19. import synapse.rest.admin
  20. from synapse.http.site import XForwardedForRequest
  21. from synapse.rest.client import login
  22. from synapse.server import HomeServer
  23. from synapse.storage.databases.main.client_ips import LAST_SEEN_GRANULARITY
  24. from synapse.types import UserID
  25. from synapse.util import Clock
  26. from tests import unittest
  27. from tests.server import make_request
  28. from tests.unittest import override_config
  29. class ClientIpStoreTestCase(unittest.HomeserverTestCase):
  30. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  31. self.store = hs.get_datastores().main
  32. def test_insert_new_client_ip(self) -> None:
  33. self.reactor.advance(12345678)
  34. user_id = "@user:id"
  35. device_id = "MY_DEVICE"
  36. # Insert a user IP
  37. self.get_success(
  38. self.store.store_device(
  39. user_id,
  40. device_id,
  41. "display name",
  42. )
  43. )
  44. self.get_success(
  45. self.store.insert_client_ip(
  46. user_id, "access_token", "ip", "user_agent", device_id
  47. )
  48. )
  49. # Trigger the storage loop
  50. self.reactor.advance(10)
  51. result = self.get_success(
  52. self.store.get_last_client_ip_by_device(user_id, device_id)
  53. )
  54. r = result[(user_id, device_id)]
  55. self.assertLessEqual(
  56. {
  57. "user_id": user_id,
  58. "device_id": device_id,
  59. "ip": "ip",
  60. "user_agent": "user_agent",
  61. "last_seen": 12345678000,
  62. }.items(),
  63. r.items(),
  64. )
  65. def test_insert_new_client_ip_none_device_id(self) -> None:
  66. """
  67. An insert with a device ID of NULL will not create a new entry, but
  68. update an existing entry in the user_ips table.
  69. """
  70. self.reactor.advance(12345678)
  71. user_id = "@user:id"
  72. # Add & trigger the storage loop
  73. self.get_success(
  74. self.store.insert_client_ip(
  75. user_id, "access_token", "ip", "user_agent", None
  76. )
  77. )
  78. self.reactor.advance(200)
  79. self.pump(0)
  80. result = self.get_success(
  81. self.store.db_pool.simple_select_list(
  82. table="user_ips",
  83. keyvalues={"user_id": user_id},
  84. retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
  85. desc="get_user_ip_and_agents",
  86. )
  87. )
  88. self.assertEqual(
  89. result,
  90. [
  91. {
  92. "access_token": "access_token",
  93. "ip": "ip",
  94. "user_agent": "user_agent",
  95. "device_id": None,
  96. "last_seen": 12345678000,
  97. }
  98. ],
  99. )
  100. # Add another & trigger the storage loop
  101. self.get_success(
  102. self.store.insert_client_ip(
  103. user_id, "access_token", "ip", "user_agent", None
  104. )
  105. )
  106. self.reactor.advance(10)
  107. self.pump(0)
  108. result = self.get_success(
  109. self.store.db_pool.simple_select_list(
  110. table="user_ips",
  111. keyvalues={"user_id": user_id},
  112. retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
  113. desc="get_user_ip_and_agents",
  114. )
  115. )
  116. # Only one result, has been upserted.
  117. self.assertEqual(
  118. result,
  119. [
  120. {
  121. "access_token": "access_token",
  122. "ip": "ip",
  123. "user_agent": "user_agent",
  124. "device_id": None,
  125. "last_seen": 12345878000,
  126. }
  127. ],
  128. )
  129. @parameterized.expand([(False,), (True,)])
  130. def test_get_last_client_ip_by_device(self, after_persisting: bool) -> None:
  131. """Test `get_last_client_ip_by_device` for persisted and unpersisted data"""
  132. self.reactor.advance(12345678)
  133. user_id = "@user:id"
  134. device_id = "MY_DEVICE"
  135. # Insert a user IP
  136. self.get_success(
  137. self.store.store_device(
  138. user_id,
  139. device_id,
  140. "display name",
  141. )
  142. )
  143. self.get_success(
  144. self.store.insert_client_ip(
  145. user_id, "access_token", "ip", "user_agent", device_id
  146. )
  147. )
  148. if after_persisting:
  149. # Trigger the storage loop
  150. self.reactor.advance(10)
  151. else:
  152. # Check that the new IP and user agent has not been stored yet
  153. db_result = self.get_success(
  154. self.store.db_pool.simple_select_list(
  155. table="devices",
  156. keyvalues={},
  157. retcols=("user_id", "ip", "user_agent", "device_id", "last_seen"),
  158. ),
  159. )
  160. self.assertEqual(
  161. db_result,
  162. [
  163. {
  164. "user_id": user_id,
  165. "device_id": device_id,
  166. "ip": None,
  167. "user_agent": None,
  168. "last_seen": None,
  169. },
  170. ],
  171. )
  172. result = self.get_success(
  173. self.store.get_last_client_ip_by_device(user_id, device_id)
  174. )
  175. self.assertEqual(
  176. result,
  177. {
  178. (user_id, device_id): {
  179. "user_id": user_id,
  180. "device_id": device_id,
  181. "ip": "ip",
  182. "user_agent": "user_agent",
  183. "last_seen": 12345678000,
  184. },
  185. },
  186. )
  187. def test_get_last_client_ip_by_device_combined_data(self) -> None:
  188. """Test that `get_last_client_ip_by_device` combines persisted and unpersisted
  189. data together correctly
  190. """
  191. self.reactor.advance(12345678)
  192. user_id = "@user:id"
  193. device_id_1 = "MY_DEVICE_1"
  194. device_id_2 = "MY_DEVICE_2"
  195. # Insert user IPs
  196. self.get_success(
  197. self.store.store_device(
  198. user_id,
  199. device_id_1,
  200. "display name",
  201. )
  202. )
  203. self.get_success(
  204. self.store.store_device(
  205. user_id,
  206. device_id_2,
  207. "display name",
  208. )
  209. )
  210. self.get_success(
  211. self.store.insert_client_ip(
  212. user_id, "access_token_1", "ip_1", "user_agent_1", device_id_1
  213. )
  214. )
  215. self.get_success(
  216. self.store.insert_client_ip(
  217. user_id, "access_token_2", "ip_2", "user_agent_2", device_id_2
  218. )
  219. )
  220. # Trigger the storage loop and wait for the rate limiting period to be over
  221. self.reactor.advance(10 + LAST_SEEN_GRANULARITY / 1000)
  222. # Update the user agent for the second device, without running the storage loop
  223. self.get_success(
  224. self.store.insert_client_ip(
  225. user_id, "access_token_2", "ip_2", "user_agent_3", device_id_2
  226. )
  227. )
  228. # Check that the new IP and user agent has not been stored yet
  229. db_result = self.get_success(
  230. self.store.db_pool.simple_select_list(
  231. table="devices",
  232. keyvalues={},
  233. retcols=("user_id", "ip", "user_agent", "device_id", "last_seen"),
  234. ),
  235. )
  236. self.assertCountEqual(
  237. db_result,
  238. [
  239. {
  240. "user_id": user_id,
  241. "device_id": device_id_1,
  242. "ip": "ip_1",
  243. "user_agent": "user_agent_1",
  244. "last_seen": 12345678000,
  245. },
  246. {
  247. "user_id": user_id,
  248. "device_id": device_id_2,
  249. "ip": "ip_2",
  250. "user_agent": "user_agent_2",
  251. "last_seen": 12345678000,
  252. },
  253. ],
  254. )
  255. # Check that data from the database and memory are combined together correctly
  256. result = self.get_success(
  257. self.store.get_last_client_ip_by_device(user_id, None)
  258. )
  259. self.assertEqual(
  260. result,
  261. {
  262. (user_id, device_id_1): {
  263. "user_id": user_id,
  264. "device_id": device_id_1,
  265. "ip": "ip_1",
  266. "user_agent": "user_agent_1",
  267. "last_seen": 12345678000,
  268. },
  269. (user_id, device_id_2): {
  270. "user_id": user_id,
  271. "device_id": device_id_2,
  272. "ip": "ip_2",
  273. "user_agent": "user_agent_3",
  274. "last_seen": 12345688000 + LAST_SEEN_GRANULARITY,
  275. },
  276. },
  277. )
  278. @parameterized.expand([(False,), (True,)])
  279. def test_get_user_ip_and_agents(self, after_persisting: bool) -> None:
  280. """Test `get_user_ip_and_agents` for persisted and unpersisted data"""
  281. self.reactor.advance(12345678)
  282. user_id = "@user:id"
  283. user = UserID.from_string(user_id)
  284. # Insert a user IP
  285. self.get_success(
  286. self.store.insert_client_ip(
  287. user_id, "access_token", "ip", "user_agent", "MY_DEVICE"
  288. )
  289. )
  290. if after_persisting:
  291. # Trigger the storage loop
  292. self.reactor.advance(10)
  293. else:
  294. # Check that the new IP and user agent has not been stored yet
  295. db_result = self.get_success(
  296. self.store.db_pool.simple_select_list(
  297. table="user_ips",
  298. keyvalues={},
  299. retcols=("access_token", "ip", "user_agent", "last_seen"),
  300. ),
  301. )
  302. self.assertEqual(db_result, [])
  303. self.assertEqual(
  304. self.get_success(self.store.get_user_ip_and_agents(user)),
  305. [
  306. {
  307. "access_token": "access_token",
  308. "ip": "ip",
  309. "user_agent": "user_agent",
  310. "last_seen": 12345678000,
  311. },
  312. ],
  313. )
  314. def test_get_user_ip_and_agents_combined_data(self) -> None:
  315. """Test that `get_user_ip_and_agents` combines persisted and unpersisted data
  316. together correctly
  317. """
  318. self.reactor.advance(12345678)
  319. user_id = "@user:id"
  320. user = UserID.from_string(user_id)
  321. # Insert user IPs
  322. self.get_success(
  323. self.store.insert_client_ip(
  324. user_id, "access_token", "ip_1", "user_agent_1", "MY_DEVICE_1"
  325. )
  326. )
  327. self.get_success(
  328. self.store.insert_client_ip(
  329. user_id, "access_token", "ip_2", "user_agent_2", "MY_DEVICE_2"
  330. )
  331. )
  332. # Trigger the storage loop and wait for the rate limiting period to be over
  333. self.reactor.advance(10 + LAST_SEEN_GRANULARITY / 1000)
  334. # Update the user agent for the second device, without running the storage loop
  335. self.get_success(
  336. self.store.insert_client_ip(
  337. user_id, "access_token", "ip_2", "user_agent_3", "MY_DEVICE_2"
  338. )
  339. )
  340. # Check that the new IP and user agent has not been stored yet
  341. db_result = self.get_success(
  342. self.store.db_pool.simple_select_list(
  343. table="user_ips",
  344. keyvalues={},
  345. retcols=("access_token", "ip", "user_agent", "last_seen"),
  346. ),
  347. )
  348. self.assertEqual(
  349. db_result,
  350. [
  351. {
  352. "access_token": "access_token",
  353. "ip": "ip_1",
  354. "user_agent": "user_agent_1",
  355. "last_seen": 12345678000,
  356. },
  357. {
  358. "access_token": "access_token",
  359. "ip": "ip_2",
  360. "user_agent": "user_agent_2",
  361. "last_seen": 12345678000,
  362. },
  363. ],
  364. )
  365. # Check that data from the database and memory are combined together correctly
  366. self.assertCountEqual(
  367. self.get_success(self.store.get_user_ip_and_agents(user)),
  368. [
  369. {
  370. "access_token": "access_token",
  371. "ip": "ip_1",
  372. "user_agent": "user_agent_1",
  373. "last_seen": 12345678000,
  374. },
  375. {
  376. "access_token": "access_token",
  377. "ip": "ip_2",
  378. "user_agent": "user_agent_3",
  379. "last_seen": 12345688000 + LAST_SEEN_GRANULARITY,
  380. },
  381. ],
  382. )
  383. @override_config({"limit_usage_by_mau": False, "max_mau_value": 50})
  384. def test_disabled_monthly_active_user(self) -> None:
  385. user_id = "@user:server"
  386. self.get_success(
  387. self.store.insert_client_ip(
  388. user_id, "access_token", "ip", "user_agent", "device_id"
  389. )
  390. )
  391. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  392. self.assertFalse(active)
  393. @override_config({"limit_usage_by_mau": True, "max_mau_value": 50})
  394. def test_adding_monthly_active_user_when_full(self) -> None:
  395. lots_of_users = 100
  396. user_id = "@user:server"
  397. self.store.get_monthly_active_count = AsyncMock(return_value=lots_of_users)
  398. self.get_success(
  399. self.store.insert_client_ip(
  400. user_id, "access_token", "ip", "user_agent", "device_id"
  401. )
  402. )
  403. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  404. self.assertFalse(active)
  405. @override_config({"limit_usage_by_mau": True, "max_mau_value": 50})
  406. def test_adding_monthly_active_user_when_space(self) -> None:
  407. user_id = "@user:server"
  408. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  409. self.assertFalse(active)
  410. # Trigger the saving loop
  411. self.reactor.advance(10)
  412. self.get_success(
  413. self.store.insert_client_ip(
  414. user_id, "access_token", "ip", "user_agent", "device_id"
  415. )
  416. )
  417. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  418. self.assertTrue(active)
  419. @override_config({"limit_usage_by_mau": True, "max_mau_value": 50})
  420. def test_updating_monthly_active_user_when_space(self) -> None:
  421. user_id = "@user:server"
  422. self.get_success(self.store.register_user(user_id=user_id, password_hash=None))
  423. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  424. self.assertFalse(active)
  425. # Trigger the saving loop
  426. self.reactor.advance(10)
  427. self.get_success(
  428. self.store.insert_client_ip(
  429. user_id, "access_token", "ip", "user_agent", "device_id"
  430. )
  431. )
  432. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  433. self.assertTrue(active)
  434. def test_devices_last_seen_bg_update(self) -> None:
  435. # First make sure we have completed all updates.
  436. self.wait_for_background_updates()
  437. user_id = "@user:id"
  438. device_id = "MY_DEVICE"
  439. # Insert a user IP
  440. self.get_success(
  441. self.store.store_device(
  442. user_id,
  443. device_id,
  444. "display name",
  445. )
  446. )
  447. self.get_success(
  448. self.store.insert_client_ip(
  449. user_id, "access_token", "ip", "user_agent", device_id
  450. )
  451. )
  452. # Force persisting to disk
  453. self.reactor.advance(200)
  454. # But clear the associated entry in devices table
  455. self.get_success(
  456. self.store.db_pool.simple_update(
  457. table="devices",
  458. keyvalues={"user_id": user_id, "device_id": device_id},
  459. updatevalues={"last_seen": None, "ip": None, "user_agent": None},
  460. desc="test_devices_last_seen_bg_update",
  461. )
  462. )
  463. # We should now get nulls when querying
  464. result = self.get_success(
  465. self.store.get_last_client_ip_by_device(user_id, device_id)
  466. )
  467. r = result[(user_id, device_id)]
  468. self.assertLessEqual(
  469. {
  470. "user_id": user_id,
  471. "device_id": device_id,
  472. "ip": None,
  473. "user_agent": None,
  474. "last_seen": None,
  475. }.items(),
  476. r.items(),
  477. )
  478. # Register the background update to run again.
  479. self.get_success(
  480. self.store.db_pool.simple_insert(
  481. table="background_updates",
  482. values={
  483. "update_name": "devices_last_seen",
  484. "progress_json": "{}",
  485. "depends_on": None,
  486. },
  487. )
  488. )
  489. # ... and tell the DataStore that it hasn't finished all updates yet
  490. self.store.db_pool.updates._all_done = False
  491. # Now let's actually drive the updates to completion
  492. self.wait_for_background_updates()
  493. # We should now get the correct result again
  494. result = self.get_success(
  495. self.store.get_last_client_ip_by_device(user_id, device_id)
  496. )
  497. r = result[(user_id, device_id)]
  498. self.assertLessEqual(
  499. {
  500. "user_id": user_id,
  501. "device_id": device_id,
  502. "ip": "ip",
  503. "user_agent": "user_agent",
  504. "last_seen": 0,
  505. }.items(),
  506. r.items(),
  507. )
  508. def test_old_user_ips_pruned(self) -> None:
  509. # First make sure we have completed all updates.
  510. self.wait_for_background_updates()
  511. user_id = "@user:id"
  512. device_id = "MY_DEVICE"
  513. # Insert a user IP
  514. self.get_success(
  515. self.store.store_device(
  516. user_id,
  517. device_id,
  518. "display name",
  519. )
  520. )
  521. self.get_success(
  522. self.store.insert_client_ip(
  523. user_id, "access_token", "ip", "user_agent", device_id
  524. )
  525. )
  526. # Force persisting to disk
  527. self.reactor.advance(200)
  528. # We should see that in the DB
  529. result = self.get_success(
  530. self.store.db_pool.simple_select_list(
  531. table="user_ips",
  532. keyvalues={"user_id": user_id},
  533. retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
  534. desc="get_user_ip_and_agents",
  535. )
  536. )
  537. self.assertEqual(
  538. result,
  539. [
  540. {
  541. "access_token": "access_token",
  542. "ip": "ip",
  543. "user_agent": "user_agent",
  544. "device_id": device_id,
  545. "last_seen": 0,
  546. }
  547. ],
  548. )
  549. # Now advance by a couple of months
  550. self.reactor.advance(60 * 24 * 60 * 60)
  551. # We should get no results.
  552. result = self.get_success(
  553. self.store.db_pool.simple_select_list(
  554. table="user_ips",
  555. keyvalues={"user_id": user_id},
  556. retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
  557. desc="get_user_ip_and_agents",
  558. )
  559. )
  560. self.assertEqual(result, [])
  561. # But we should still get the correct values for the device
  562. result2 = self.get_success(
  563. self.store.get_last_client_ip_by_device(user_id, device_id)
  564. )
  565. r = result2[(user_id, device_id)]
  566. self.assertLessEqual(
  567. {
  568. "user_id": user_id,
  569. "device_id": device_id,
  570. "ip": "ip",
  571. "user_agent": "user_agent",
  572. "last_seen": 0,
  573. }.items(),
  574. r.items(),
  575. )
  576. def test_invalid_user_agents_are_ignored(self) -> None:
  577. # First make sure we have completed all updates.
  578. self.wait_for_background_updates()
  579. user_id1 = "@user1:id"
  580. user_id2 = "@user2:id"
  581. device_id1 = "MY_DEVICE1"
  582. device_id2 = "MY_DEVICE2"
  583. access_token1 = "access_token1"
  584. access_token2 = "access_token2"
  585. # Insert a user IP 1
  586. self.get_success(
  587. self.store.store_device(
  588. user_id1,
  589. device_id1,
  590. "display name1",
  591. )
  592. )
  593. # Insert a user IP 2
  594. self.get_success(
  595. self.store.store_device(
  596. user_id2,
  597. device_id2,
  598. "display name2",
  599. )
  600. )
  601. self.get_success(
  602. self.store.insert_client_ip(
  603. user_id1, access_token1, "ip", "sync-v3-proxy-", device_id1
  604. )
  605. )
  606. self.get_success(
  607. self.store.insert_client_ip(
  608. user_id2, access_token2, "ip", "user_agent", device_id2
  609. )
  610. )
  611. # Force persisting to disk
  612. self.reactor.advance(200)
  613. # We should see that in the DB
  614. result = self.get_success(
  615. self.store.db_pool.simple_select_list(
  616. table="user_ips",
  617. keyvalues={},
  618. retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
  619. desc="get_user_ip_and_agents",
  620. )
  621. )
  622. # ensure user1 is filtered out
  623. self.assertEqual(
  624. result,
  625. [
  626. {
  627. "access_token": access_token2,
  628. "ip": "ip",
  629. "user_agent": "user_agent",
  630. "device_id": device_id2,
  631. "last_seen": 0,
  632. }
  633. ],
  634. )
  635. class ClientIpAuthTestCase(unittest.HomeserverTestCase):
  636. servlets = [
  637. synapse.rest.admin.register_servlets,
  638. login.register_servlets,
  639. ]
  640. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  641. self.store = self.hs.get_datastores().main
  642. self.user_id = self.register_user("bob", "abc123", True)
  643. def test_request_with_xforwarded(self) -> None:
  644. """
  645. The IP in X-Forwarded-For is entered into the client IPs table.
  646. """
  647. self._runtest(
  648. {b"X-Forwarded-For": b"127.9.0.1"},
  649. "127.9.0.1",
  650. {"request": XForwardedForRequest},
  651. )
  652. def test_request_from_getPeer(self) -> None:
  653. """
  654. The IP returned by getPeer is entered into the client IPs table, if
  655. there's no X-Forwarded-For header.
  656. """
  657. self._runtest({}, "127.0.0.1", {})
  658. def _runtest(
  659. self,
  660. headers: Dict[bytes, bytes],
  661. expected_ip: str,
  662. make_request_args: Dict[str, Any],
  663. ) -> None:
  664. device_id = "bleb"
  665. access_token = self.login("bob", "abc123", device_id=device_id)
  666. # Advance to a known time
  667. self.reactor.advance(123456 - self.reactor.seconds())
  668. headers1 = {b"User-Agent": b"Mozzila pizza"}
  669. headers1.update(headers)
  670. make_request(
  671. self.reactor,
  672. self.site,
  673. "GET",
  674. "/_synapse/admin/v2/users/" + self.user_id,
  675. access_token=access_token,
  676. custom_headers=headers1.items(),
  677. **make_request_args,
  678. )
  679. # Advance so the save loop occurs
  680. self.reactor.advance(100)
  681. result = self.get_success(
  682. self.store.get_last_client_ip_by_device(self.user_id, device_id)
  683. )
  684. r = result[(self.user_id, device_id)]
  685. self.assertLessEqual(
  686. {
  687. "user_id": self.user_id,
  688. "device_id": device_id,
  689. "ip": expected_ip,
  690. "user_agent": "Mozzila pizza",
  691. "last_seen": 123456100,
  692. }.items(),
  693. r.items(),
  694. )