Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Allow configuring a range for the account validity startup job #5276

Merged
merged 8 commits into from
May 31, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/5276.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow configuring a range for the account validity startup job.
4 changes: 3 additions & 1 deletion docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,9 @@ uploads_path: "DATADIR/uploads"
# This means that, if a validity period is set, and Synapse is restarted (it will
# then derive an expiration date from the current validity period), and some time
# after that the validity period changes and Synapse is restarted, the users'
# expiration dates won't be updated unless their account is manually renewed.
# expiration dates won't be updated unless their account is manually renewed. This
# date will be randomly selected within a range [now + period ; now + period + d],
# where d is equal to 10% of the validity period.
#
#account_validity:
# enabled: True
Expand Down
6 changes: 5 additions & 1 deletion synapse/config/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def __init__(self, config, synapse_config):
else:
self.renew_email_subject = "Renew your %(app)s account"

self.startup_job_max_delta = self.period * 10. / 100.

if self.renew_by_email_enabled and "public_baseurl" not in synapse_config:
raise ConfigError("Can't send renewal emails without 'public_baseurl'")

Expand Down Expand Up @@ -129,7 +131,9 @@ def default_config(self, generate_secrets=False, **kwargs):
# This means that, if a validity period is set, and Synapse is restarted (it will
# then derive an expiration date from the current validity period), and some time
# after that the validity period changes and Synapse is restarted, the users'
# expiration dates won't be updated unless their account is manually renewed.
# expiration dates won't be updated unless their account is manually renewed. This
# date will be randomly selected within a range [now + period ; now + period + d],
# where d is equal to 10%% of the validity period.
#
#account_validity:
# enabled: True
Expand Down
22 changes: 20 additions & 2 deletions synapse/storage/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# limitations under the License.
import itertools
import logging
import random
import sys
import threading
import time
Expand Down Expand Up @@ -247,6 +248,8 @@ def __init__(self, db_conn, hs):
self._check_safe_to_upsert,
)

self.rand = random.SystemRandom()

if self._account_validity.enabled:
self._clock.call_later(
0.0,
Expand Down Expand Up @@ -308,21 +311,36 @@ def select_users_with_no_expiration_date_txn(txn):
res = self.cursor_to_dict(txn)
if res:
for user in res:
self.set_expiration_date_for_user_txn(txn, user["name"])
self.set_expiration_date_for_user_txn(
txn,
user["name"],
use_delta=True,
)

yield self.runInteraction(
"get_users_with_no_expiration_date",
select_users_with_no_expiration_date_txn,
)

def set_expiration_date_for_user_txn(self, txn, user_id):
def set_expiration_date_for_user_txn(self, txn, user_id, use_delta=False):
"""Sets an expiration date to the account with the given user ID.

Args:
user_id (str): User ID to set an expiration date for.
use_delta (bool): If set to False, the expiration date for the user will be
now + validity period. If set to True, this expiration date will be a
random value in the [now + period; now + period + d] range, d being a
delta equal to 10% of the validity period.
"""
now_ms = self._clock.time_msec()
expiration_ts = now_ms + self._account_validity.period

if use_delta:
expiration_ts = self.rand.randrange(
expiration_ts,
expiration_ts + self._account_validity.startup_job_max_delta,
)

self._simple_insert_txn(
txn,
"account_validity",
Expand Down
15 changes: 10 additions & 5 deletions tests/rest/client/v2_alpha/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ class AccountValidityBackgroundJobTestCase(unittest.HomeserverTestCase):

def make_homeserver(self, reactor, clock):
self.validity_period = 10
self.max_delta = self.validity_period * 10. / 100.

config = self.default_config()

Expand All @@ -453,14 +454,18 @@ def make_homeserver(self, reactor, clock):

def test_background_job(self):
"""
Tests whether the account validity startup background job does the right thing,
which is sticking an expiration date to every account that doesn't already have
one.
Tests the same thing as test_background_job, except that it sets the
startup_job_max_delta parameter and checks that the expiration date is within the
allowed range.
"""
user_id = self.register_user("kermit", "user")
user_id = self.register_user("kermit_delta", "user")

self.hs.config.account_validity.startup_job_max_delta = self.max_delta

now_ms = self.hs.clock.time_msec()
self.get_success(self.store._set_expiration_date_when_missing())

res = self.get_success(self.store.get_expiration_ts_for_user(user_id))
self.assertEqual(res, now_ms + self.validity_period)

self.assertLessEqual(res, now_ms + self.validity_period + self.max_delta)
self.assertGreaterEqual(res, now_ms + self.validity_period)