123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728 |
- # Copyright 2016 OpenMarket Ltd
- # Copyright 2018 New Vector Ltd
- #
- # 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.
- from typing import Any, Dict
- from unittest.mock import Mock
- from parameterized import parameterized
- from twisted.test.proto_helpers import MemoryReactor
- import synapse.rest.admin
- from synapse.http.site import XForwardedForRequest
- from synapse.rest.client import login
- from synapse.server import HomeServer
- from synapse.storage.databases.main.client_ips import LAST_SEEN_GRANULARITY
- from synapse.types import UserID
- from synapse.util import Clock
- from tests import unittest
- from tests.server import make_request
- from tests.test_utils import make_awaitable
- from tests.unittest import override_config
- class ClientIpStoreTestCase(unittest.HomeserverTestCase):
- def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
- self.store = hs.get_datastores().main
- def test_insert_new_client_ip(self) -> None:
- self.reactor.advance(12345678)
- user_id = "@user:id"
- device_id = "MY_DEVICE"
- # Insert a user IP
- self.get_success(
- self.store.store_device(
- user_id,
- device_id,
- "display name",
- )
- )
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", device_id
- )
- )
- # Trigger the storage loop
- self.reactor.advance(10)
- result = self.get_success(
- self.store.get_last_client_ip_by_device(user_id, device_id)
- )
- r = result[(user_id, device_id)]
- self.assertDictContainsSubset(
- {
- "user_id": user_id,
- "device_id": device_id,
- "ip": "ip",
- "user_agent": "user_agent",
- "last_seen": 12345678000,
- },
- r,
- )
- def test_insert_new_client_ip_none_device_id(self) -> None:
- """
- An insert with a device ID of NULL will not create a new entry, but
- update an existing entry in the user_ips table.
- """
- self.reactor.advance(12345678)
- user_id = "@user:id"
- # Add & trigger the storage loop
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", None
- )
- )
- self.reactor.advance(200)
- self.pump(0)
- result = self.get_success(
- self.store.db_pool.simple_select_list(
- table="user_ips",
- keyvalues={"user_id": user_id},
- retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
- desc="get_user_ip_and_agents",
- )
- )
- self.assertEqual(
- result,
- [
- {
- "access_token": "access_token",
- "ip": "ip",
- "user_agent": "user_agent",
- "device_id": None,
- "last_seen": 12345678000,
- }
- ],
- )
- # Add another & trigger the storage loop
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", None
- )
- )
- self.reactor.advance(10)
- self.pump(0)
- result = self.get_success(
- self.store.db_pool.simple_select_list(
- table="user_ips",
- keyvalues={"user_id": user_id},
- retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
- desc="get_user_ip_and_agents",
- )
- )
- # Only one result, has been upserted.
- self.assertEqual(
- result,
- [
- {
- "access_token": "access_token",
- "ip": "ip",
- "user_agent": "user_agent",
- "device_id": None,
- "last_seen": 12345878000,
- }
- ],
- )
- @parameterized.expand([(False,), (True,)])
- def test_get_last_client_ip_by_device(self, after_persisting: bool) -> None:
- """Test `get_last_client_ip_by_device` for persisted and unpersisted data"""
- self.reactor.advance(12345678)
- user_id = "@user:id"
- device_id = "MY_DEVICE"
- # Insert a user IP
- self.get_success(
- self.store.store_device(
- user_id,
- device_id,
- "display name",
- )
- )
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", device_id
- )
- )
- if after_persisting:
- # Trigger the storage loop
- self.reactor.advance(10)
- else:
- # Check that the new IP and user agent has not been stored yet
- db_result = self.get_success(
- self.store.db_pool.simple_select_list(
- table="devices",
- keyvalues={},
- retcols=("user_id", "ip", "user_agent", "device_id", "last_seen"),
- ),
- )
- self.assertEqual(
- db_result,
- [
- {
- "user_id": user_id,
- "device_id": device_id,
- "ip": None,
- "user_agent": None,
- "last_seen": None,
- },
- ],
- )
- result = self.get_success(
- self.store.get_last_client_ip_by_device(user_id, device_id)
- )
- self.assertEqual(
- result,
- {
- (user_id, device_id): {
- "user_id": user_id,
- "device_id": device_id,
- "ip": "ip",
- "user_agent": "user_agent",
- "last_seen": 12345678000,
- },
- },
- )
- def test_get_last_client_ip_by_device_combined_data(self) -> None:
- """Test that `get_last_client_ip_by_device` combines persisted and unpersisted
- data together correctly
- """
- self.reactor.advance(12345678)
- user_id = "@user:id"
- device_id_1 = "MY_DEVICE_1"
- device_id_2 = "MY_DEVICE_2"
- # Insert user IPs
- self.get_success(
- self.store.store_device(
- user_id,
- device_id_1,
- "display name",
- )
- )
- self.get_success(
- self.store.store_device(
- user_id,
- device_id_2,
- "display name",
- )
- )
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token_1", "ip_1", "user_agent_1", device_id_1
- )
- )
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token_2", "ip_2", "user_agent_2", device_id_2
- )
- )
- # Trigger the storage loop and wait for the rate limiting period to be over
- self.reactor.advance(10 + LAST_SEEN_GRANULARITY / 1000)
- # Update the user agent for the second device, without running the storage loop
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token_2", "ip_2", "user_agent_3", device_id_2
- )
- )
- # Check that the new IP and user agent has not been stored yet
- db_result = self.get_success(
- self.store.db_pool.simple_select_list(
- table="devices",
- keyvalues={},
- retcols=("user_id", "ip", "user_agent", "device_id", "last_seen"),
- ),
- )
- self.assertCountEqual(
- db_result,
- [
- {
- "user_id": user_id,
- "device_id": device_id_1,
- "ip": "ip_1",
- "user_agent": "user_agent_1",
- "last_seen": 12345678000,
- },
- {
- "user_id": user_id,
- "device_id": device_id_2,
- "ip": "ip_2",
- "user_agent": "user_agent_2",
- "last_seen": 12345678000,
- },
- ],
- )
- # Check that data from the database and memory are combined together correctly
- result = self.get_success(
- self.store.get_last_client_ip_by_device(user_id, None)
- )
- self.assertEqual(
- result,
- {
- (user_id, device_id_1): {
- "user_id": user_id,
- "device_id": device_id_1,
- "ip": "ip_1",
- "user_agent": "user_agent_1",
- "last_seen": 12345678000,
- },
- (user_id, device_id_2): {
- "user_id": user_id,
- "device_id": device_id_2,
- "ip": "ip_2",
- "user_agent": "user_agent_3",
- "last_seen": 12345688000 + LAST_SEEN_GRANULARITY,
- },
- },
- )
- @parameterized.expand([(False,), (True,)])
- def test_get_user_ip_and_agents(self, after_persisting: bool) -> None:
- """Test `get_user_ip_and_agents` for persisted and unpersisted data"""
- self.reactor.advance(12345678)
- user_id = "@user:id"
- user = UserID.from_string(user_id)
- # Insert a user IP
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", "MY_DEVICE"
- )
- )
- if after_persisting:
- # Trigger the storage loop
- self.reactor.advance(10)
- else:
- # Check that the new IP and user agent has not been stored yet
- db_result = self.get_success(
- self.store.db_pool.simple_select_list(
- table="user_ips",
- keyvalues={},
- retcols=("access_token", "ip", "user_agent", "last_seen"),
- ),
- )
- self.assertEqual(db_result, [])
- self.assertEqual(
- self.get_success(self.store.get_user_ip_and_agents(user)),
- [
- {
- "access_token": "access_token",
- "ip": "ip",
- "user_agent": "user_agent",
- "last_seen": 12345678000,
- },
- ],
- )
- def test_get_user_ip_and_agents_combined_data(self) -> None:
- """Test that `get_user_ip_and_agents` combines persisted and unpersisted data
- together correctly
- """
- self.reactor.advance(12345678)
- user_id = "@user:id"
- user = UserID.from_string(user_id)
- # Insert user IPs
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip_1", "user_agent_1", "MY_DEVICE_1"
- )
- )
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip_2", "user_agent_2", "MY_DEVICE_2"
- )
- )
- # Trigger the storage loop and wait for the rate limiting period to be over
- self.reactor.advance(10 + LAST_SEEN_GRANULARITY / 1000)
- # Update the user agent for the second device, without running the storage loop
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip_2", "user_agent_3", "MY_DEVICE_2"
- )
- )
- # Check that the new IP and user agent has not been stored yet
- db_result = self.get_success(
- self.store.db_pool.simple_select_list(
- table="user_ips",
- keyvalues={},
- retcols=("access_token", "ip", "user_agent", "last_seen"),
- ),
- )
- self.assertEqual(
- db_result,
- [
- {
- "access_token": "access_token",
- "ip": "ip_1",
- "user_agent": "user_agent_1",
- "last_seen": 12345678000,
- },
- {
- "access_token": "access_token",
- "ip": "ip_2",
- "user_agent": "user_agent_2",
- "last_seen": 12345678000,
- },
- ],
- )
- # Check that data from the database and memory are combined together correctly
- self.assertCountEqual(
- self.get_success(self.store.get_user_ip_and_agents(user)),
- [
- {
- "access_token": "access_token",
- "ip": "ip_1",
- "user_agent": "user_agent_1",
- "last_seen": 12345678000,
- },
- {
- "access_token": "access_token",
- "ip": "ip_2",
- "user_agent": "user_agent_3",
- "last_seen": 12345688000 + LAST_SEEN_GRANULARITY,
- },
- ],
- )
- @override_config({"limit_usage_by_mau": False, "max_mau_value": 50})
- def test_disabled_monthly_active_user(self) -> None:
- user_id = "@user:server"
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", "device_id"
- )
- )
- active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
- self.assertFalse(active)
- @override_config({"limit_usage_by_mau": True, "max_mau_value": 50})
- def test_adding_monthly_active_user_when_full(self) -> None:
- lots_of_users = 100
- user_id = "@user:server"
- self.store.get_monthly_active_count = Mock(
- return_value=make_awaitable(lots_of_users)
- )
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", "device_id"
- )
- )
- active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
- self.assertFalse(active)
- @override_config({"limit_usage_by_mau": True, "max_mau_value": 50})
- def test_adding_monthly_active_user_when_space(self) -> None:
- user_id = "@user:server"
- active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
- self.assertFalse(active)
- # Trigger the saving loop
- self.reactor.advance(10)
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", "device_id"
- )
- )
- active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
- self.assertTrue(active)
- @override_config({"limit_usage_by_mau": True, "max_mau_value": 50})
- def test_updating_monthly_active_user_when_space(self) -> None:
- user_id = "@user:server"
- self.get_success(self.store.register_user(user_id=user_id, password_hash=None))
- active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
- self.assertFalse(active)
- # Trigger the saving loop
- self.reactor.advance(10)
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", "device_id"
- )
- )
- active = self.get_success(self.store.user_last_seen_monthly_active(user_id))
- self.assertTrue(active)
- def test_devices_last_seen_bg_update(self) -> None:
- # First make sure we have completed all updates.
- self.wait_for_background_updates()
- user_id = "@user:id"
- device_id = "MY_DEVICE"
- # Insert a user IP
- self.get_success(
- self.store.store_device(
- user_id,
- device_id,
- "display name",
- )
- )
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", device_id
- )
- )
- # Force persisting to disk
- self.reactor.advance(200)
- # But clear the associated entry in devices table
- self.get_success(
- self.store.db_pool.simple_update(
- table="devices",
- keyvalues={"user_id": user_id, "device_id": device_id},
- updatevalues={"last_seen": None, "ip": None, "user_agent": None},
- desc="test_devices_last_seen_bg_update",
- )
- )
- # We should now get nulls when querying
- result = self.get_success(
- self.store.get_last_client_ip_by_device(user_id, device_id)
- )
- r = result[(user_id, device_id)]
- self.assertDictContainsSubset(
- {
- "user_id": user_id,
- "device_id": device_id,
- "ip": None,
- "user_agent": None,
- "last_seen": None,
- },
- r,
- )
- # Register the background update to run again.
- self.get_success(
- self.store.db_pool.simple_insert(
- table="background_updates",
- values={
- "update_name": "devices_last_seen",
- "progress_json": "{}",
- "depends_on": None,
- },
- )
- )
- # ... and tell the DataStore that it hasn't finished all updates yet
- self.store.db_pool.updates._all_done = False
- # Now let's actually drive the updates to completion
- self.wait_for_background_updates()
- # We should now get the correct result again
- result = self.get_success(
- self.store.get_last_client_ip_by_device(user_id, device_id)
- )
- r = result[(user_id, device_id)]
- self.assertDictContainsSubset(
- {
- "user_id": user_id,
- "device_id": device_id,
- "ip": "ip",
- "user_agent": "user_agent",
- "last_seen": 0,
- },
- r,
- )
- def test_old_user_ips_pruned(self) -> None:
- # First make sure we have completed all updates.
- self.wait_for_background_updates()
- user_id = "@user:id"
- device_id = "MY_DEVICE"
- # Insert a user IP
- self.get_success(
- self.store.store_device(
- user_id,
- device_id,
- "display name",
- )
- )
- self.get_success(
- self.store.insert_client_ip(
- user_id, "access_token", "ip", "user_agent", device_id
- )
- )
- # Force persisting to disk
- self.reactor.advance(200)
- # We should see that in the DB
- result = self.get_success(
- self.store.db_pool.simple_select_list(
- table="user_ips",
- keyvalues={"user_id": user_id},
- retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
- desc="get_user_ip_and_agents",
- )
- )
- self.assertEqual(
- result,
- [
- {
- "access_token": "access_token",
- "ip": "ip",
- "user_agent": "user_agent",
- "device_id": device_id,
- "last_seen": 0,
- }
- ],
- )
- # Now advance by a couple of months
- self.reactor.advance(60 * 24 * 60 * 60)
- # We should get no results.
- result = self.get_success(
- self.store.db_pool.simple_select_list(
- table="user_ips",
- keyvalues={"user_id": user_id},
- retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
- desc="get_user_ip_and_agents",
- )
- )
- self.assertEqual(result, [])
- # But we should still get the correct values for the device
- result2 = self.get_success(
- self.store.get_last_client_ip_by_device(user_id, device_id)
- )
- r = result2[(user_id, device_id)]
- self.assertDictContainsSubset(
- {
- "user_id": user_id,
- "device_id": device_id,
- "ip": "ip",
- "user_agent": "user_agent",
- "last_seen": 0,
- },
- r,
- )
- class ClientIpAuthTestCase(unittest.HomeserverTestCase):
- servlets = [
- synapse.rest.admin.register_servlets,
- login.register_servlets,
- ]
- def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
- self.store = self.hs.get_datastores().main
- self.user_id = self.register_user("bob", "abc123", True)
- def test_request_with_xforwarded(self) -> None:
- """
- The IP in X-Forwarded-For is entered into the client IPs table.
- """
- self._runtest(
- {b"X-Forwarded-For": b"127.9.0.1"},
- "127.9.0.1",
- {"request": XForwardedForRequest},
- )
- def test_request_from_getPeer(self) -> None:
- """
- The IP returned by getPeer is entered into the client IPs table, if
- there's no X-Forwarded-For header.
- """
- self._runtest({}, "127.0.0.1", {})
- def _runtest(
- self,
- headers: Dict[bytes, bytes],
- expected_ip: str,
- make_request_args: Dict[str, Any],
- ) -> None:
- device_id = "bleb"
- access_token = self.login("bob", "abc123", device_id=device_id)
- # Advance to a known time
- self.reactor.advance(123456 - self.reactor.seconds())
- headers1 = {b"User-Agent": b"Mozzila pizza"}
- headers1.update(headers)
- make_request(
- self.reactor,
- self.site,
- "GET",
- "/_synapse/admin/v2/users/" + self.user_id,
- access_token=access_token,
- custom_headers=headers1.items(),
- **make_request_args,
- )
- # Advance so the save loop occurs
- self.reactor.advance(100)
- result = self.get_success(
- self.store.get_last_client_ip_by_device(self.user_id, device_id)
- )
- r = result[(self.user_id, device_id)]
- self.assertDictContainsSubset(
- {
- "user_id": self.user_id,
- "device_id": device_id,
- "ip": expected_ip,
- "user_agent": "Mozzila pizza",
- "last_seen": 123456100,
- },
- r,
- )
|