test_client_ips.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 OpenMarket Ltd
  3. # Copyright 2018 New Vector Ltd
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. from mock import Mock
  17. import synapse.rest.admin
  18. from synapse.http.site import XForwardedForRequest
  19. from synapse.rest.client.v1 import login
  20. from tests import unittest
  21. from tests.test_utils import make_awaitable
  22. from tests.unittest import override_config
  23. class ClientIpStoreTestCase(unittest.HomeserverTestCase):
  24. def make_homeserver(self, reactor, clock):
  25. hs = self.setup_test_homeserver()
  26. return hs
  27. def prepare(self, hs, reactor, clock):
  28. self.store = self.hs.get_datastore()
  29. def test_insert_new_client_ip(self):
  30. self.reactor.advance(12345678)
  31. user_id = "@user:id"
  32. device_id = "MY_DEVICE"
  33. # Insert a user IP
  34. self.get_success(self.store.store_device(user_id, device_id, "display name",))
  35. self.get_success(
  36. self.store.insert_client_ip(
  37. user_id, "access_token", "ip", "user_agent", device_id
  38. )
  39. )
  40. # Trigger the storage loop
  41. self.reactor.advance(10)
  42. result = self.get_success(
  43. self.store.get_last_client_ip_by_device(user_id, device_id)
  44. )
  45. r = result[(user_id, device_id)]
  46. self.assertDictContainsSubset(
  47. {
  48. "user_id": user_id,
  49. "device_id": device_id,
  50. "ip": "ip",
  51. "user_agent": "user_agent",
  52. "last_seen": 12345678000,
  53. },
  54. r,
  55. )
  56. def test_insert_new_client_ip_none_device_id(self):
  57. """
  58. An insert with a device ID of NULL will not create a new entry, but
  59. update an existing entry in the user_ips table.
  60. """
  61. self.reactor.advance(12345678)
  62. user_id = "@user:id"
  63. # Add & trigger the storage loop
  64. self.get_success(
  65. self.store.insert_client_ip(
  66. user_id, "access_token", "ip", "user_agent", None
  67. )
  68. )
  69. self.reactor.advance(200)
  70. self.pump(0)
  71. result = self.get_success(
  72. self.store.db_pool.simple_select_list(
  73. table="user_ips",
  74. keyvalues={"user_id": user_id},
  75. retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
  76. desc="get_user_ip_and_agents",
  77. )
  78. )
  79. self.assertEqual(
  80. result,
  81. [
  82. {
  83. "access_token": "access_token",
  84. "ip": "ip",
  85. "user_agent": "user_agent",
  86. "device_id": None,
  87. "last_seen": 12345678000,
  88. }
  89. ],
  90. )
  91. # Add another & trigger the storage loop
  92. self.get_success(
  93. self.store.insert_client_ip(
  94. user_id, "access_token", "ip", "user_agent", None
  95. )
  96. )
  97. self.reactor.advance(10)
  98. self.pump(0)
  99. result = self.get_success(
  100. self.store.db_pool.simple_select_list(
  101. table="user_ips",
  102. keyvalues={"user_id": user_id},
  103. retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
  104. desc="get_user_ip_and_agents",
  105. )
  106. )
  107. # Only one result, has been upserted.
  108. self.assertEqual(
  109. result,
  110. [
  111. {
  112. "access_token": "access_token",
  113. "ip": "ip",
  114. "user_agent": "user_agent",
  115. "device_id": None,
  116. "last_seen": 12345878000,
  117. }
  118. ],
  119. )
  120. @override_config({"limit_usage_by_mau": False, "max_mau_value": 50})
  121. def test_disabled_monthly_active_user(self):
  122. user_id = "@user:server"
  123. self.get_success(
  124. self.store.insert_client_ip(
  125. user_id, "access_token", "ip", "user_agent", "device_id"
  126. )
  127. )
  128. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  129. self.assertFalse(active)
  130. @override_config({"limit_usage_by_mau": True, "max_mau_value": 50})
  131. def test_adding_monthly_active_user_when_full(self):
  132. lots_of_users = 100
  133. user_id = "@user:server"
  134. self.store.get_monthly_active_count = Mock(
  135. return_value=make_awaitable(lots_of_users)
  136. )
  137. self.get_success(
  138. self.store.insert_client_ip(
  139. user_id, "access_token", "ip", "user_agent", "device_id"
  140. )
  141. )
  142. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  143. self.assertFalse(active)
  144. @override_config({"limit_usage_by_mau": True, "max_mau_value": 50})
  145. def test_adding_monthly_active_user_when_space(self):
  146. user_id = "@user:server"
  147. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  148. self.assertFalse(active)
  149. # Trigger the saving loop
  150. self.reactor.advance(10)
  151. self.get_success(
  152. self.store.insert_client_ip(
  153. user_id, "access_token", "ip", "user_agent", "device_id"
  154. )
  155. )
  156. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  157. self.assertTrue(active)
  158. @override_config({"limit_usage_by_mau": True, "max_mau_value": 50})
  159. def test_updating_monthly_active_user_when_space(self):
  160. user_id = "@user:server"
  161. self.get_success(self.store.register_user(user_id=user_id, password_hash=None))
  162. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  163. self.assertFalse(active)
  164. # Trigger the saving loop
  165. self.reactor.advance(10)
  166. self.get_success(
  167. self.store.insert_client_ip(
  168. user_id, "access_token", "ip", "user_agent", "device_id"
  169. )
  170. )
  171. active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
  172. self.assertTrue(active)
  173. def test_devices_last_seen_bg_update(self):
  174. # First make sure we have completed all updates.
  175. while not self.get_success(
  176. self.store.db_pool.updates.has_completed_background_updates()
  177. ):
  178. self.get_success(
  179. self.store.db_pool.updates.do_next_background_update(100), by=0.1
  180. )
  181. user_id = "@user:id"
  182. device_id = "MY_DEVICE"
  183. # Insert a user IP
  184. self.get_success(self.store.store_device(user_id, device_id, "display name",))
  185. self.get_success(
  186. self.store.insert_client_ip(
  187. user_id, "access_token", "ip", "user_agent", device_id
  188. )
  189. )
  190. # Force persisting to disk
  191. self.reactor.advance(200)
  192. # But clear the associated entry in devices table
  193. self.get_success(
  194. self.store.db_pool.simple_update(
  195. table="devices",
  196. keyvalues={"user_id": user_id, "device_id": device_id},
  197. updatevalues={"last_seen": None, "ip": None, "user_agent": None},
  198. desc="test_devices_last_seen_bg_update",
  199. )
  200. )
  201. # We should now get nulls when querying
  202. result = self.get_success(
  203. self.store.get_last_client_ip_by_device(user_id, device_id)
  204. )
  205. r = result[(user_id, device_id)]
  206. self.assertDictContainsSubset(
  207. {
  208. "user_id": user_id,
  209. "device_id": device_id,
  210. "ip": None,
  211. "user_agent": None,
  212. "last_seen": None,
  213. },
  214. r,
  215. )
  216. # Register the background update to run again.
  217. self.get_success(
  218. self.store.db_pool.simple_insert(
  219. table="background_updates",
  220. values={
  221. "update_name": "devices_last_seen",
  222. "progress_json": "{}",
  223. "depends_on": None,
  224. },
  225. )
  226. )
  227. # ... and tell the DataStore that it hasn't finished all updates yet
  228. self.store.db_pool.updates._all_done = False
  229. # Now let's actually drive the updates to completion
  230. while not self.get_success(
  231. self.store.db_pool.updates.has_completed_background_updates()
  232. ):
  233. self.get_success(
  234. self.store.db_pool.updates.do_next_background_update(100), by=0.1
  235. )
  236. # We should now get the correct result again
  237. result = self.get_success(
  238. self.store.get_last_client_ip_by_device(user_id, device_id)
  239. )
  240. r = result[(user_id, device_id)]
  241. self.assertDictContainsSubset(
  242. {
  243. "user_id": user_id,
  244. "device_id": device_id,
  245. "ip": "ip",
  246. "user_agent": "user_agent",
  247. "last_seen": 0,
  248. },
  249. r,
  250. )
  251. def test_old_user_ips_pruned(self):
  252. # First make sure we have completed all updates.
  253. while not self.get_success(
  254. self.store.db_pool.updates.has_completed_background_updates()
  255. ):
  256. self.get_success(
  257. self.store.db_pool.updates.do_next_background_update(100), by=0.1
  258. )
  259. user_id = "@user:id"
  260. device_id = "MY_DEVICE"
  261. # Insert a user IP
  262. self.get_success(self.store.store_device(user_id, device_id, "display name",))
  263. self.get_success(
  264. self.store.insert_client_ip(
  265. user_id, "access_token", "ip", "user_agent", device_id
  266. )
  267. )
  268. # Force persisting to disk
  269. self.reactor.advance(200)
  270. # We should see that in the DB
  271. result = self.get_success(
  272. self.store.db_pool.simple_select_list(
  273. table="user_ips",
  274. keyvalues={"user_id": user_id},
  275. retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
  276. desc="get_user_ip_and_agents",
  277. )
  278. )
  279. self.assertEqual(
  280. result,
  281. [
  282. {
  283. "access_token": "access_token",
  284. "ip": "ip",
  285. "user_agent": "user_agent",
  286. "device_id": device_id,
  287. "last_seen": 0,
  288. }
  289. ],
  290. )
  291. # Now advance by a couple of months
  292. self.reactor.advance(60 * 24 * 60 * 60)
  293. # We should get no results.
  294. result = self.get_success(
  295. self.store.db_pool.simple_select_list(
  296. table="user_ips",
  297. keyvalues={"user_id": user_id},
  298. retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
  299. desc="get_user_ip_and_agents",
  300. )
  301. )
  302. self.assertEqual(result, [])
  303. # But we should still get the correct values for the device
  304. result = self.get_success(
  305. self.store.get_last_client_ip_by_device(user_id, device_id)
  306. )
  307. r = result[(user_id, device_id)]
  308. self.assertDictContainsSubset(
  309. {
  310. "user_id": user_id,
  311. "device_id": device_id,
  312. "ip": "ip",
  313. "user_agent": "user_agent",
  314. "last_seen": 0,
  315. },
  316. r,
  317. )
  318. class ClientIpAuthTestCase(unittest.HomeserverTestCase):
  319. servlets = [
  320. synapse.rest.admin.register_servlets_for_client_rest_resource,
  321. login.register_servlets,
  322. ]
  323. def make_homeserver(self, reactor, clock):
  324. hs = self.setup_test_homeserver()
  325. return hs
  326. def prepare(self, hs, reactor, clock):
  327. self.store = self.hs.get_datastore()
  328. self.user_id = self.register_user("bob", "abc123", True)
  329. def test_request_with_xforwarded(self):
  330. """
  331. The IP in X-Forwarded-For is entered into the client IPs table.
  332. """
  333. self._runtest(
  334. {b"X-Forwarded-For": b"127.9.0.1"},
  335. "127.9.0.1",
  336. {"request": XForwardedForRequest},
  337. )
  338. def test_request_from_getPeer(self):
  339. """
  340. The IP returned by getPeer is entered into the client IPs table, if
  341. there's no X-Forwarded-For header.
  342. """
  343. self._runtest({}, "127.0.0.1", {})
  344. def _runtest(self, headers, expected_ip, make_request_args):
  345. device_id = "bleb"
  346. access_token = self.login("bob", "abc123", device_id=device_id)
  347. # Advance to a known time
  348. self.reactor.advance(123456 - self.reactor.seconds())
  349. request, channel = self.make_request(
  350. "GET",
  351. "/_matrix/client/r0/admin/users/" + self.user_id,
  352. access_token=access_token,
  353. **make_request_args
  354. )
  355. request.requestHeaders.addRawHeader(b"User-Agent", b"Mozzila pizza")
  356. # Add the optional headers
  357. for h, v in headers.items():
  358. request.requestHeaders.addRawHeader(h, v)
  359. self.render(request)
  360. # Advance so the save loop occurs
  361. self.reactor.advance(100)
  362. result = self.get_success(
  363. self.store.get_last_client_ip_by_device(self.user_id, device_id)
  364. )
  365. r = result[(self.user_id, device_id)]
  366. self.assertDictContainsSubset(
  367. {
  368. "user_id": self.user_id,
  369. "device_id": device_id,
  370. "ip": expected_ip,
  371. "user_agent": "Mozzila pizza",
  372. "last_seen": 123456100,
  373. },
  374. r,
  375. )