123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- # 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 unittest.mock import Mock
- import synapse.rest.admin
- from synapse.http.site import XForwardedForRequest
- from synapse.rest.client import login
- 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 make_homeserver(self, reactor, clock):
- hs = self.setup_test_homeserver()
- return hs
- def prepare(self, hs, reactor, clock):
- self.store = self.hs.get_datastore()
- def test_insert_new_client_ip(self):
- 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):
- """
- 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,
- }
- ],
- )
- @override_config({"limit_usage_by_mau": False, "max_mau_value": 50})
- def test_disabled_monthly_active_user(self):
- 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):
- 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):
- 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):
- 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):
- # First make sure we have completed all updates.
- while not self.get_success(
- self.store.db_pool.updates.has_completed_background_updates()
- ):
- self.get_success(
- self.store.db_pool.updates.do_next_background_update(100), by=0.1
- )
- 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
- while not self.get_success(
- self.store.db_pool.updates.has_completed_background_updates()
- ):
- self.get_success(
- self.store.db_pool.updates.do_next_background_update(100), by=0.1
- )
- # 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):
- # First make sure we have completed all updates.
- while not self.get_success(
- self.store.db_pool.updates.has_completed_background_updates()
- ):
- self.get_success(
- self.store.db_pool.updates.do_next_background_update(100), by=0.1
- )
- 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
- 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,
- )
- class ClientIpAuthTestCase(unittest.HomeserverTestCase):
- servlets = [
- synapse.rest.admin.register_servlets,
- login.register_servlets,
- ]
- def make_homeserver(self, reactor, clock):
- hs = self.setup_test_homeserver()
- return hs
- def prepare(self, hs, reactor, clock):
- self.store = self.hs.get_datastore()
- self.user_id = self.register_user("bob", "abc123", True)
- def test_request_with_xforwarded(self):
- """
- 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):
- """
- 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, expected_ip, make_request_args):
- 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,
- )
|