test_register.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. # Copyright 2017-2018 New Vector Ltd
  4. # Copyright 2019 The Matrix.org Foundation C.I.C.
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License");
  7. # you may not use this file except in compliance with the License.
  8. # You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. import datetime
  18. import json
  19. import os
  20. import pkg_resources
  21. import synapse.rest.admin
  22. from synapse.api.constants import LoginType
  23. from synapse.api.errors import Codes
  24. from synapse.appservice import ApplicationService
  25. from synapse.rest.client.v1 import login, logout
  26. from synapse.rest.client.v2_alpha import account, account_validity, register, sync
  27. from tests import unittest
  28. from tests.unittest import override_config
  29. class RegisterRestServletTestCase(unittest.HomeserverTestCase):
  30. servlets = [
  31. login.register_servlets,
  32. register.register_servlets,
  33. synapse.rest.admin.register_servlets,
  34. ]
  35. url = b"/_matrix/client/r0/register"
  36. def default_config(self):
  37. config = super().default_config()
  38. config["allow_guest_access"] = True
  39. return config
  40. def test_POST_appservice_registration_valid(self):
  41. user_id = "@as_user_kermit:test"
  42. as_token = "i_am_an_app_service"
  43. appservice = ApplicationService(
  44. as_token,
  45. self.hs.config.server_name,
  46. id="1234",
  47. namespaces={"users": [{"regex": r"@as_user.*", "exclusive": True}]},
  48. sender="@as:test",
  49. )
  50. self.hs.get_datastore().services_cache.append(appservice)
  51. request_data = json.dumps({"username": "as_user_kermit"})
  52. channel = self.make_request(
  53. b"POST", self.url + b"?access_token=i_am_an_app_service", request_data
  54. )
  55. self.assertEquals(channel.result["code"], b"200", channel.result)
  56. det_data = {"user_id": user_id, "home_server": self.hs.hostname}
  57. self.assertDictContainsSubset(det_data, channel.json_body)
  58. def test_POST_appservice_registration_invalid(self):
  59. self.appservice = None # no application service exists
  60. request_data = json.dumps({"username": "kermit"})
  61. channel = self.make_request(
  62. b"POST", self.url + b"?access_token=i_am_an_app_service", request_data
  63. )
  64. self.assertEquals(channel.result["code"], b"401", channel.result)
  65. def test_POST_bad_password(self):
  66. request_data = json.dumps({"username": "kermit", "password": 666})
  67. channel = self.make_request(b"POST", self.url, request_data)
  68. self.assertEquals(channel.result["code"], b"400", channel.result)
  69. self.assertEquals(channel.json_body["error"], "Invalid password")
  70. def test_POST_bad_username(self):
  71. request_data = json.dumps({"username": 777, "password": "monkey"})
  72. channel = self.make_request(b"POST", self.url, request_data)
  73. self.assertEquals(channel.result["code"], b"400", channel.result)
  74. self.assertEquals(channel.json_body["error"], "Invalid username")
  75. def test_POST_user_valid(self):
  76. user_id = "@kermit:test"
  77. device_id = "frogfone"
  78. params = {
  79. "username": "kermit",
  80. "password": "monkey",
  81. "device_id": device_id,
  82. "auth": {"type": LoginType.DUMMY},
  83. }
  84. request_data = json.dumps(params)
  85. channel = self.make_request(b"POST", self.url, request_data)
  86. det_data = {
  87. "user_id": user_id,
  88. "home_server": self.hs.hostname,
  89. "device_id": device_id,
  90. }
  91. self.assertEquals(channel.result["code"], b"200", channel.result)
  92. self.assertDictContainsSubset(det_data, channel.json_body)
  93. @override_config({"enable_registration": False})
  94. def test_POST_disabled_registration(self):
  95. request_data = json.dumps({"username": "kermit", "password": "monkey"})
  96. self.auth_result = (None, {"username": "kermit", "password": "monkey"}, None)
  97. channel = self.make_request(b"POST", self.url, request_data)
  98. self.assertEquals(channel.result["code"], b"403", channel.result)
  99. self.assertEquals(channel.json_body["error"], "Registration has been disabled")
  100. self.assertEquals(channel.json_body["errcode"], "M_FORBIDDEN")
  101. def test_POST_guest_registration(self):
  102. self.hs.config.macaroon_secret_key = "test"
  103. self.hs.config.allow_guest_access = True
  104. channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}")
  105. det_data = {"home_server": self.hs.hostname, "device_id": "guest_device"}
  106. self.assertEquals(channel.result["code"], b"200", channel.result)
  107. self.assertDictContainsSubset(det_data, channel.json_body)
  108. def test_POST_disabled_guest_registration(self):
  109. self.hs.config.allow_guest_access = False
  110. channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}")
  111. self.assertEquals(channel.result["code"], b"403", channel.result)
  112. self.assertEquals(channel.json_body["error"], "Guest access is disabled")
  113. @override_config({"rc_registration": {"per_second": 0.17, "burst_count": 5}})
  114. def test_POST_ratelimiting_guest(self):
  115. for i in range(0, 6):
  116. url = self.url + b"?kind=guest"
  117. channel = self.make_request(b"POST", url, b"{}")
  118. if i == 5:
  119. self.assertEquals(channel.result["code"], b"429", channel.result)
  120. retry_after_ms = int(channel.json_body["retry_after_ms"])
  121. else:
  122. self.assertEquals(channel.result["code"], b"200", channel.result)
  123. self.reactor.advance(retry_after_ms / 1000.0 + 1.0)
  124. channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}")
  125. self.assertEquals(channel.result["code"], b"200", channel.result)
  126. @override_config({"rc_registration": {"per_second": 0.17, "burst_count": 5}})
  127. def test_POST_ratelimiting(self):
  128. for i in range(0, 6):
  129. params = {
  130. "username": "kermit" + str(i),
  131. "password": "monkey",
  132. "device_id": "frogfone",
  133. "auth": {"type": LoginType.DUMMY},
  134. }
  135. request_data = json.dumps(params)
  136. channel = self.make_request(b"POST", self.url, request_data)
  137. if i == 5:
  138. self.assertEquals(channel.result["code"], b"429", channel.result)
  139. retry_after_ms = int(channel.json_body["retry_after_ms"])
  140. else:
  141. self.assertEquals(channel.result["code"], b"200", channel.result)
  142. self.reactor.advance(retry_after_ms / 1000.0 + 1.0)
  143. channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}")
  144. self.assertEquals(channel.result["code"], b"200", channel.result)
  145. def test_advertised_flows(self):
  146. channel = self.make_request(b"POST", self.url, b"{}")
  147. self.assertEquals(channel.result["code"], b"401", channel.result)
  148. flows = channel.json_body["flows"]
  149. # with the stock config, we only expect the dummy flow
  150. self.assertCountEqual([["m.login.dummy"]], (f["stages"] for f in flows))
  151. @unittest.override_config(
  152. {
  153. "public_baseurl": "https://test_server",
  154. "enable_registration_captcha": True,
  155. "user_consent": {
  156. "version": "1",
  157. "template_dir": "/",
  158. "require_at_registration": True,
  159. },
  160. "account_threepid_delegates": {
  161. "email": "https://id_server",
  162. "msisdn": "https://id_server",
  163. },
  164. }
  165. )
  166. def test_advertised_flows_captcha_and_terms_and_3pids(self):
  167. channel = self.make_request(b"POST", self.url, b"{}")
  168. self.assertEquals(channel.result["code"], b"401", channel.result)
  169. flows = channel.json_body["flows"]
  170. self.assertCountEqual(
  171. [
  172. ["m.login.recaptcha", "m.login.terms", "m.login.dummy"],
  173. ["m.login.recaptcha", "m.login.terms", "m.login.email.identity"],
  174. ["m.login.recaptcha", "m.login.terms", "m.login.msisdn"],
  175. [
  176. "m.login.recaptcha",
  177. "m.login.terms",
  178. "m.login.msisdn",
  179. "m.login.email.identity",
  180. ],
  181. ],
  182. (f["stages"] for f in flows),
  183. )
  184. @unittest.override_config(
  185. {
  186. "public_baseurl": "https://test_server",
  187. "registrations_require_3pid": ["email"],
  188. "disable_msisdn_registration": True,
  189. "email": {
  190. "smtp_host": "mail_server",
  191. "smtp_port": 2525,
  192. "notif_from": "sender@host",
  193. },
  194. }
  195. )
  196. def test_advertised_flows_no_msisdn_email_required(self):
  197. channel = self.make_request(b"POST", self.url, b"{}")
  198. self.assertEquals(channel.result["code"], b"401", channel.result)
  199. flows = channel.json_body["flows"]
  200. # with the stock config, we expect all four combinations of 3pid
  201. self.assertCountEqual(
  202. [["m.login.email.identity"]], (f["stages"] for f in flows)
  203. )
  204. @unittest.override_config(
  205. {
  206. "request_token_inhibit_3pid_errors": True,
  207. "public_baseurl": "https://test_server",
  208. "email": {
  209. "smtp_host": "mail_server",
  210. "smtp_port": 2525,
  211. "notif_from": "sender@host",
  212. },
  213. }
  214. )
  215. def test_request_token_existing_email_inhibit_error(self):
  216. """Test that requesting a token via this endpoint doesn't leak existing
  217. associations if configured that way.
  218. """
  219. user_id = self.register_user("kermit", "monkey")
  220. self.login("kermit", "monkey")
  221. email = "test@example.com"
  222. # Add a threepid
  223. self.get_success(
  224. self.hs.get_datastore().user_add_threepid(
  225. user_id=user_id,
  226. medium="email",
  227. address=email,
  228. validated_at=0,
  229. added_at=0,
  230. )
  231. )
  232. channel = self.make_request(
  233. "POST",
  234. b"register/email/requestToken",
  235. {"client_secret": "foobar", "email": email, "send_attempt": 1},
  236. )
  237. self.assertEquals(200, channel.code, channel.result)
  238. self.assertIsNotNone(channel.json_body.get("sid"))
  239. class AccountValidityTestCase(unittest.HomeserverTestCase):
  240. servlets = [
  241. register.register_servlets,
  242. synapse.rest.admin.register_servlets_for_client_rest_resource,
  243. login.register_servlets,
  244. sync.register_servlets,
  245. logout.register_servlets,
  246. account_validity.register_servlets,
  247. ]
  248. def make_homeserver(self, reactor, clock):
  249. config = self.default_config()
  250. # Test for account expiring after a week.
  251. config["enable_registration"] = True
  252. config["account_validity"] = {
  253. "enabled": True,
  254. "period": 604800000, # Time in ms for 1 week
  255. }
  256. self.hs = self.setup_test_homeserver(config=config)
  257. return self.hs
  258. def test_validity_period(self):
  259. self.register_user("kermit", "monkey")
  260. tok = self.login("kermit", "monkey")
  261. # The specific endpoint doesn't matter, all we need is an authenticated
  262. # endpoint.
  263. channel = self.make_request(b"GET", "/sync", access_token=tok)
  264. self.assertEquals(channel.result["code"], b"200", channel.result)
  265. self.reactor.advance(datetime.timedelta(weeks=1).total_seconds())
  266. channel = self.make_request(b"GET", "/sync", access_token=tok)
  267. self.assertEquals(channel.result["code"], b"403", channel.result)
  268. self.assertEquals(
  269. channel.json_body["errcode"], Codes.EXPIRED_ACCOUNT, channel.result
  270. )
  271. def test_manual_renewal(self):
  272. user_id = self.register_user("kermit", "monkey")
  273. tok = self.login("kermit", "monkey")
  274. self.reactor.advance(datetime.timedelta(weeks=1).total_seconds())
  275. # If we register the admin user at the beginning of the test, it will
  276. # expire at the same time as the normal user and the renewal request
  277. # will be denied.
  278. self.register_user("admin", "adminpassword", admin=True)
  279. admin_tok = self.login("admin", "adminpassword")
  280. url = "/_synapse/admin/v1/account_validity/validity"
  281. params = {"user_id": user_id}
  282. request_data = json.dumps(params)
  283. channel = self.make_request(b"POST", url, request_data, access_token=admin_tok)
  284. self.assertEquals(channel.result["code"], b"200", channel.result)
  285. # The specific endpoint doesn't matter, all we need is an authenticated
  286. # endpoint.
  287. channel = self.make_request(b"GET", "/sync", access_token=tok)
  288. self.assertEquals(channel.result["code"], b"200", channel.result)
  289. def test_manual_expire(self):
  290. user_id = self.register_user("kermit", "monkey")
  291. tok = self.login("kermit", "monkey")
  292. self.register_user("admin", "adminpassword", admin=True)
  293. admin_tok = self.login("admin", "adminpassword")
  294. url = "/_synapse/admin/v1/account_validity/validity"
  295. params = {
  296. "user_id": user_id,
  297. "expiration_ts": 0,
  298. "enable_renewal_emails": False,
  299. }
  300. request_data = json.dumps(params)
  301. channel = self.make_request(b"POST", url, request_data, access_token=admin_tok)
  302. self.assertEquals(channel.result["code"], b"200", channel.result)
  303. # The specific endpoint doesn't matter, all we need is an authenticated
  304. # endpoint.
  305. channel = self.make_request(b"GET", "/sync", access_token=tok)
  306. self.assertEquals(channel.result["code"], b"403", channel.result)
  307. self.assertEquals(
  308. channel.json_body["errcode"], Codes.EXPIRED_ACCOUNT, channel.result
  309. )
  310. def test_logging_out_expired_user(self):
  311. user_id = self.register_user("kermit", "monkey")
  312. tok = self.login("kermit", "monkey")
  313. self.register_user("admin", "adminpassword", admin=True)
  314. admin_tok = self.login("admin", "adminpassword")
  315. url = "/_synapse/admin/v1/account_validity/validity"
  316. params = {
  317. "user_id": user_id,
  318. "expiration_ts": 0,
  319. "enable_renewal_emails": False,
  320. }
  321. request_data = json.dumps(params)
  322. channel = self.make_request(b"POST", url, request_data, access_token=admin_tok)
  323. self.assertEquals(channel.result["code"], b"200", channel.result)
  324. # Try to log the user out
  325. channel = self.make_request(b"POST", "/logout", access_token=tok)
  326. self.assertEquals(channel.result["code"], b"200", channel.result)
  327. # Log the user in again (allowed for expired accounts)
  328. tok = self.login("kermit", "monkey")
  329. # Try to log out all of the user's sessions
  330. channel = self.make_request(b"POST", "/logout/all", access_token=tok)
  331. self.assertEquals(channel.result["code"], b"200", channel.result)
  332. class AccountValidityRenewalByEmailTestCase(unittest.HomeserverTestCase):
  333. servlets = [
  334. register.register_servlets,
  335. synapse.rest.admin.register_servlets_for_client_rest_resource,
  336. login.register_servlets,
  337. sync.register_servlets,
  338. account_validity.register_servlets,
  339. account.register_servlets,
  340. ]
  341. def make_homeserver(self, reactor, clock):
  342. config = self.default_config()
  343. # Test for account expiring after a week and renewal emails being sent 2
  344. # days before expiry.
  345. config["enable_registration"] = True
  346. config["account_validity"] = {
  347. "enabled": True,
  348. "period": 604800000, # Time in ms for 1 week
  349. "renew_at": 172800000, # Time in ms for 2 days
  350. "renew_by_email_enabled": True,
  351. "renew_email_subject": "Renew your account",
  352. "account_renewed_html_path": "account_renewed.html",
  353. "invalid_token_html_path": "invalid_token.html",
  354. }
  355. # Email config.
  356. self.email_attempts = []
  357. async def sendmail(*args, **kwargs):
  358. self.email_attempts.append((args, kwargs))
  359. config["email"] = {
  360. "enable_notifs": True,
  361. "template_dir": os.path.abspath(
  362. pkg_resources.resource_filename("synapse", "res/templates")
  363. ),
  364. "expiry_template_html": "notice_expiry.html",
  365. "expiry_template_text": "notice_expiry.txt",
  366. "notif_template_html": "notif_mail.html",
  367. "notif_template_text": "notif_mail.txt",
  368. "smtp_host": "127.0.0.1",
  369. "smtp_port": 20,
  370. "require_transport_security": False,
  371. "smtp_user": None,
  372. "smtp_pass": None,
  373. "notif_from": "test@example.com",
  374. }
  375. config["public_baseurl"] = "aaa"
  376. self.hs = self.setup_test_homeserver(config=config, sendmail=sendmail)
  377. self.store = self.hs.get_datastore()
  378. return self.hs
  379. def test_renewal_email(self):
  380. self.email_attempts = []
  381. (user_id, tok) = self.create_user()
  382. # Move 6 days forward. This should trigger a renewal email to be sent.
  383. self.reactor.advance(datetime.timedelta(days=6).total_seconds())
  384. self.assertEqual(len(self.email_attempts), 1)
  385. # Retrieving the URL from the email is too much pain for now, so we
  386. # retrieve the token from the DB.
  387. renewal_token = self.get_success(self.store.get_renewal_token_for_user(user_id))
  388. url = "/_matrix/client/unstable/account_validity/renew?token=%s" % renewal_token
  389. channel = self.make_request(b"GET", url)
  390. self.assertEquals(channel.result["code"], b"200", channel.result)
  391. # Check that we're getting HTML back.
  392. content_type = None
  393. for header in channel.result.get("headers", []):
  394. if header[0] == b"Content-Type":
  395. content_type = header[1]
  396. self.assertEqual(content_type, b"text/html; charset=utf-8", channel.result)
  397. # Check that the HTML we're getting is the one we expect on a successful renewal.
  398. expected_html = self.hs.config.account_validity.account_renewed_html_content
  399. self.assertEqual(
  400. channel.result["body"], expected_html.encode("utf8"), channel.result
  401. )
  402. # Move 3 days forward. If the renewal failed, every authed request with
  403. # our access token should be denied from now, otherwise they should
  404. # succeed.
  405. self.reactor.advance(datetime.timedelta(days=3).total_seconds())
  406. channel = self.make_request(b"GET", "/sync", access_token=tok)
  407. self.assertEquals(channel.result["code"], b"200", channel.result)
  408. def test_renewal_invalid_token(self):
  409. # Hit the renewal endpoint with an invalid token and check that it behaves as
  410. # expected, i.e. that it responds with 404 Not Found and the correct HTML.
  411. url = "/_matrix/client/unstable/account_validity/renew?token=123"
  412. channel = self.make_request(b"GET", url)
  413. self.assertEquals(channel.result["code"], b"404", channel.result)
  414. # Check that we're getting HTML back.
  415. content_type = None
  416. for header in channel.result.get("headers", []):
  417. if header[0] == b"Content-Type":
  418. content_type = header[1]
  419. self.assertEqual(content_type, b"text/html; charset=utf-8", channel.result)
  420. # Check that the HTML we're getting is the one we expect when using an
  421. # invalid/unknown token.
  422. expected_html = self.hs.config.account_validity.invalid_token_html_content
  423. self.assertEqual(
  424. channel.result["body"], expected_html.encode("utf8"), channel.result
  425. )
  426. def test_manual_email_send(self):
  427. self.email_attempts = []
  428. (user_id, tok) = self.create_user()
  429. channel = self.make_request(
  430. b"POST",
  431. "/_matrix/client/unstable/account_validity/send_mail",
  432. access_token=tok,
  433. )
  434. self.assertEquals(channel.result["code"], b"200", channel.result)
  435. self.assertEqual(len(self.email_attempts), 1)
  436. def test_deactivated_user(self):
  437. self.email_attempts = []
  438. (user_id, tok) = self.create_user()
  439. request_data = json.dumps(
  440. {
  441. "auth": {
  442. "type": "m.login.password",
  443. "user": user_id,
  444. "password": "monkey",
  445. },
  446. "erase": False,
  447. }
  448. )
  449. channel = self.make_request(
  450. "POST", "account/deactivate", request_data, access_token=tok
  451. )
  452. self.assertEqual(channel.code, 200)
  453. self.reactor.advance(datetime.timedelta(days=8).total_seconds())
  454. self.assertEqual(len(self.email_attempts), 0)
  455. def create_user(self):
  456. user_id = self.register_user("kermit", "monkey")
  457. tok = self.login("kermit", "monkey")
  458. # We need to manually add an email address otherwise the handler will do
  459. # nothing.
  460. now = self.hs.get_clock().time_msec()
  461. self.get_success(
  462. self.store.user_add_threepid(
  463. user_id=user_id,
  464. medium="email",
  465. address="kermit@example.com",
  466. validated_at=now,
  467. added_at=now,
  468. )
  469. )
  470. return user_id, tok
  471. def test_manual_email_send_expired_account(self):
  472. user_id = self.register_user("kermit", "monkey")
  473. tok = self.login("kermit", "monkey")
  474. # We need to manually add an email address otherwise the handler will do
  475. # nothing.
  476. now = self.hs.get_clock().time_msec()
  477. self.get_success(
  478. self.store.user_add_threepid(
  479. user_id=user_id,
  480. medium="email",
  481. address="kermit@example.com",
  482. validated_at=now,
  483. added_at=now,
  484. )
  485. )
  486. # Make the account expire.
  487. self.reactor.advance(datetime.timedelta(days=8).total_seconds())
  488. # Ignore all emails sent by the automatic background task and only focus on the
  489. # ones sent manually.
  490. self.email_attempts = []
  491. # Test that we're still able to manually trigger a mail to be sent.
  492. channel = self.make_request(
  493. b"POST",
  494. "/_matrix/client/unstable/account_validity/send_mail",
  495. access_token=tok,
  496. )
  497. self.assertEquals(channel.result["code"], b"200", channel.result)
  498. self.assertEqual(len(self.email_attempts), 1)
  499. class AccountValidityBackgroundJobTestCase(unittest.HomeserverTestCase):
  500. servlets = [synapse.rest.admin.register_servlets_for_client_rest_resource]
  501. def make_homeserver(self, reactor, clock):
  502. self.validity_period = 10
  503. self.max_delta = self.validity_period * 10.0 / 100.0
  504. config = self.default_config()
  505. config["enable_registration"] = True
  506. config["account_validity"] = {"enabled": False}
  507. self.hs = self.setup_test_homeserver(config=config)
  508. self.hs.config.account_validity.period = self.validity_period
  509. self.store = self.hs.get_datastore()
  510. return self.hs
  511. def test_background_job(self):
  512. """
  513. Tests the same thing as test_background_job, except that it sets the
  514. startup_job_max_delta parameter and checks that the expiration date is within the
  515. allowed range.
  516. """
  517. user_id = self.register_user("kermit_delta", "user")
  518. self.hs.config.account_validity.startup_job_max_delta = self.max_delta
  519. now_ms = self.hs.get_clock().time_msec()
  520. self.get_success(self.store._set_expiration_date_when_missing())
  521. res = self.get_success(self.store.get_expiration_ts_for_user(user_id))
  522. self.assertGreaterEqual(res, now_ms + self.validity_period - self.max_delta)
  523. self.assertLessEqual(res, now_ms + self.validity_period)