test_mau.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. # Copyright 2018 New Vector Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Tests REST events for /rooms paths."""
  15. from typing import List, Optional
  16. from twisted.test.proto_helpers import MemoryReactor
  17. from synapse.api.constants import APP_SERVICE_REGISTRATION_TYPE, LoginType
  18. from synapse.api.errors import Codes, HttpResponseException, SynapseError
  19. from synapse.appservice import ApplicationService
  20. from synapse.rest.client import register, sync
  21. from synapse.server import HomeServer
  22. from synapse.types import JsonDict
  23. from synapse.util import Clock
  24. from tests import unittest
  25. from tests.unittest import override_config
  26. from tests.utils import default_config
  27. class TestMauLimit(unittest.HomeserverTestCase):
  28. servlets = [register.register_servlets, sync.register_servlets]
  29. def default_config(self) -> JsonDict:
  30. config = default_config("test")
  31. config.update(
  32. {
  33. "registrations_require_3pid": [],
  34. "limit_usage_by_mau": True,
  35. "max_mau_value": 2,
  36. "mau_trial_days": 0,
  37. "server_notices": {
  38. "system_mxid_localpart": "server",
  39. "room_name": "Test Server Notice Room",
  40. },
  41. }
  42. )
  43. # apply any additional config which was specified via the override_config
  44. # decorator.
  45. if self._extra_config is not None:
  46. config.update(self._extra_config)
  47. return config
  48. def prepare(
  49. self, reactor: MemoryReactor, clock: Clock, homeserver: HomeServer
  50. ) -> None:
  51. self.store = homeserver.get_datastores().main
  52. def test_simple_deny_mau(self) -> None:
  53. # Create and sync so that the MAU counts get updated
  54. token1 = self.create_user("kermit1")
  55. self.do_sync_for_user(token1)
  56. token2 = self.create_user("kermit2")
  57. self.do_sync_for_user(token2)
  58. # check we're testing what we think we are: there should be two active users
  59. self.assertEqual(self.get_success(self.store.get_monthly_active_count()), 2)
  60. # We've created and activated two users, we shouldn't be able to
  61. # register new users
  62. with self.assertRaises(SynapseError) as cm:
  63. self.create_user("kermit3")
  64. e = cm.exception
  65. self.assertEqual(e.code, 403)
  66. self.assertEqual(e.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
  67. def test_as_ignores_mau(self) -> None:
  68. """Test that application services can still create users when the MAU
  69. limit has been reached. This only works when application service
  70. user ip tracking is disabled.
  71. """
  72. # Create and sync so that the MAU counts get updated
  73. token1 = self.create_user("kermit1")
  74. self.do_sync_for_user(token1)
  75. token2 = self.create_user("kermit2")
  76. self.do_sync_for_user(token2)
  77. # check we're testing what we think we are: there should be two active users
  78. self.assertEqual(self.get_success(self.store.get_monthly_active_count()), 2)
  79. # We've created and activated two users, we shouldn't be able to
  80. # register new users
  81. with self.assertRaises(SynapseError) as cm:
  82. self.create_user("kermit3")
  83. e = cm.exception
  84. self.assertEqual(e.code, 403)
  85. self.assertEqual(e.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
  86. # Cheekily add an application service that we use to register a new user
  87. # with.
  88. as_token = "foobartoken"
  89. self.store.services_cache.append(
  90. ApplicationService(
  91. token=as_token,
  92. id="SomeASID",
  93. sender="@as_sender:test",
  94. namespaces={"users": [{"regex": "@as_*", "exclusive": True}]},
  95. )
  96. )
  97. self.create_user("as_kermit4", token=as_token, appservice=True)
  98. def test_allowed_after_a_month_mau(self) -> None:
  99. # Create and sync so that the MAU counts get updated
  100. token1 = self.create_user("kermit1")
  101. self.do_sync_for_user(token1)
  102. token2 = self.create_user("kermit2")
  103. self.do_sync_for_user(token2)
  104. # Advance time by 31 days
  105. self.reactor.advance(31 * 24 * 60 * 60)
  106. self.get_success(self.store.reap_monthly_active_users())
  107. self.reactor.advance(0)
  108. # We should be able to register more users
  109. token3 = self.create_user("kermit3")
  110. self.do_sync_for_user(token3)
  111. @override_config({"mau_trial_days": 1})
  112. def test_trial_delay(self) -> None:
  113. # We should be able to register more than the limit initially
  114. token1 = self.create_user("kermit1")
  115. self.do_sync_for_user(token1)
  116. token2 = self.create_user("kermit2")
  117. self.do_sync_for_user(token2)
  118. token3 = self.create_user("kermit3")
  119. self.do_sync_for_user(token3)
  120. # Advance time by 2 days
  121. self.reactor.advance(2 * 24 * 60 * 60)
  122. # Two users should be able to sync
  123. self.do_sync_for_user(token1)
  124. self.do_sync_for_user(token2)
  125. # But the third should fail
  126. with self.assertRaises(SynapseError) as cm:
  127. self.do_sync_for_user(token3)
  128. e = cm.exception
  129. self.assertEqual(e.code, 403)
  130. self.assertEqual(e.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
  131. # And new registrations are now denied too
  132. with self.assertRaises(SynapseError) as cm:
  133. self.create_user("kermit4")
  134. e = cm.exception
  135. self.assertEqual(e.code, 403)
  136. self.assertEqual(e.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
  137. @override_config({"mau_trial_days": 1})
  138. def test_trial_users_cant_come_back(self) -> None:
  139. self.hs.config.server.mau_trial_days = 1
  140. # We should be able to register more than the limit initially
  141. token1 = self.create_user("kermit1")
  142. self.do_sync_for_user(token1)
  143. token2 = self.create_user("kermit2")
  144. self.do_sync_for_user(token2)
  145. token3 = self.create_user("kermit3")
  146. self.do_sync_for_user(token3)
  147. # Advance time by 2 days
  148. self.reactor.advance(2 * 24 * 60 * 60)
  149. # Two users should be able to sync
  150. self.do_sync_for_user(token1)
  151. self.do_sync_for_user(token2)
  152. # Advance by 2 months so everyone falls out of MAU
  153. self.reactor.advance(60 * 24 * 60 * 60)
  154. self.get_success(self.store.reap_monthly_active_users())
  155. # We can create as many new users as we want
  156. token4 = self.create_user("kermit4")
  157. self.do_sync_for_user(token4)
  158. token5 = self.create_user("kermit5")
  159. self.do_sync_for_user(token5)
  160. token6 = self.create_user("kermit6")
  161. self.do_sync_for_user(token6)
  162. # users 2 and 3 can come back to bring us back up to MAU limit
  163. self.do_sync_for_user(token2)
  164. self.do_sync_for_user(token3)
  165. # New trial users can still sync
  166. self.do_sync_for_user(token4)
  167. self.do_sync_for_user(token5)
  168. self.do_sync_for_user(token6)
  169. # But old user can't
  170. with self.assertRaises(SynapseError) as cm:
  171. self.do_sync_for_user(token1)
  172. e = cm.exception
  173. self.assertEqual(e.code, 403)
  174. self.assertEqual(e.errcode, Codes.RESOURCE_LIMIT_EXCEEDED)
  175. @override_config(
  176. # max_mau_value should not matter
  177. {"max_mau_value": 1, "limit_usage_by_mau": False, "mau_stats_only": True}
  178. )
  179. def test_tracked_but_not_limited(self) -> None:
  180. # Simply being able to create 2 users indicates that the
  181. # limit was not reached.
  182. token1 = self.create_user("kermit1")
  183. self.do_sync_for_user(token1)
  184. token2 = self.create_user("kermit2")
  185. self.do_sync_for_user(token2)
  186. # We do want to verify that the number of tracked users
  187. # matches what we want though
  188. count = self.store.get_monthly_active_count()
  189. self.reactor.advance(100)
  190. self.assertEqual(2, self.successResultOf(count))
  191. @override_config(
  192. {
  193. "mau_trial_days": 3,
  194. "mau_appservice_trial_days": {"SomeASID": 1, "AnotherASID": 2},
  195. }
  196. )
  197. def test_as_trial_days(self) -> None:
  198. user_tokens: List[str] = []
  199. def advance_time_and_sync() -> None:
  200. self.reactor.advance(24 * 60 * 61)
  201. for token in user_tokens:
  202. self.do_sync_for_user(token)
  203. # Cheekily add an application service that we use to register a new user
  204. # with.
  205. as_token_1 = "foobartoken1"
  206. self.store.services_cache.append(
  207. ApplicationService(
  208. token=as_token_1,
  209. id="SomeASID",
  210. sender="@as_sender_1:test",
  211. namespaces={"users": [{"regex": "@as_1.*", "exclusive": True}]},
  212. )
  213. )
  214. as_token_2 = "foobartoken2"
  215. self.store.services_cache.append(
  216. ApplicationService(
  217. token=as_token_2,
  218. id="AnotherASID",
  219. sender="@as_sender_2:test",
  220. namespaces={"users": [{"regex": "@as_2.*", "exclusive": True}]},
  221. )
  222. )
  223. user_tokens.append(self.create_user("kermit1"))
  224. user_tokens.append(self.create_user("kermit2"))
  225. user_tokens.append(
  226. self.create_user("as_1kermit3", token=as_token_1, appservice=True)
  227. )
  228. user_tokens.append(
  229. self.create_user("as_2kermit4", token=as_token_2, appservice=True)
  230. )
  231. # Advance time by 1 day to include the first appservice
  232. advance_time_and_sync()
  233. self.assertEqual(
  234. self.get_success(self.store.get_monthly_active_count_by_service()),
  235. {"SomeASID": 1},
  236. )
  237. # Advance time by 1 day to include the next appservice
  238. advance_time_and_sync()
  239. self.assertEqual(
  240. self.get_success(self.store.get_monthly_active_count_by_service()),
  241. {"SomeASID": 1, "AnotherASID": 1},
  242. )
  243. # Advance time by 1 day to include the native users
  244. advance_time_and_sync()
  245. self.assertEqual(
  246. self.get_success(self.store.get_monthly_active_count_by_service()),
  247. {
  248. "SomeASID": 1,
  249. "AnotherASID": 1,
  250. "native": 2,
  251. },
  252. )
  253. def create_user(
  254. self, localpart: str, token: Optional[str] = None, appservice: bool = False
  255. ) -> str:
  256. request_data = {
  257. "username": localpart,
  258. "password": "monkey",
  259. "auth": {"type": LoginType.DUMMY},
  260. }
  261. if appservice:
  262. request_data["type"] = APP_SERVICE_REGISTRATION_TYPE
  263. channel = self.make_request(
  264. "POST",
  265. "/register",
  266. request_data,
  267. access_token=token,
  268. )
  269. if channel.code != 200:
  270. raise HttpResponseException(
  271. channel.code, channel.result["reason"], channel.result["body"]
  272. ).to_synapse_error()
  273. access_token = channel.json_body["access_token"]
  274. return access_token
  275. def do_sync_for_user(self, token: str) -> None:
  276. channel = self.make_request("GET", "/sync", access_token=token)
  277. if channel.code != 200:
  278. raise HttpResponseException(
  279. channel.code, channel.result["reason"], channel.result["body"]
  280. ).to_synapse_error()