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

Don't send renewal emails to deactivated users (second attempt) #5462

Merged
Merged
Changes from all 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/5394.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a bug where deactivated users could receive renewal emails if the account validity feature is on.
3 changes: 3 additions & 0 deletions synapse/handlers/account_validity.py
Original file line number Diff line number Diff line change
@@ -110,6 +110,9 @@ def _send_renewal_email(self, user_id, expiration_ts):
# Stop right here if the user doesn't have at least one email address.
# In this case, they will have to ask their server admin to renew their
# account manually.
# We don't need to do a specific check to make sure the account isn't
# deactivated, as a deactivated account isn't supposed to have any
# email address attached to it.
if not addresses:
return

6 changes: 6 additions & 0 deletions synapse/handlers/deactivate_account.py
Original file line number Diff line number Diff line change
@@ -43,6 +43,8 @@ def __init__(self, hs):
# it left off (if it has work left to do).
hs.get_reactor().callWhenRunning(self._start_user_parting)

self._account_validity_enabled = hs.config.account_validity.enabled

@defer.inlineCallbacks
def deactivate_account(self, user_id, erase_data, id_server=None):
"""Deactivate a user's account
@@ -115,6 +117,10 @@ def deactivate_account(self, user_id, erase_data, id_server=None):
# parts users from rooms (if it isn't already running)
self._start_user_parting()

# Remove all information on the user from the account_validity table.
if self._account_validity_enabled:
yield self.store.delete_account_validity_for_user(user_id)

# Mark the user as deactivated.
yield self.store.set_user_deactivated_status(user_id, True)

4 changes: 2 additions & 2 deletions synapse/storage/_base.py
Original file line number Diff line number Diff line change
@@ -299,12 +299,12 @@ def _set_expiration_date_when_missing(self):

def select_users_with_no_expiration_date_txn(txn):
"""Retrieves the list of registered users with no expiration date from the
database.
database, filtering out deactivated users.
"""
sql = (
"SELECT users.name FROM users"
" LEFT JOIN account_validity ON (users.name = account_validity.user_id)"
" WHERE account_validity.user_id is NULL;"
" WHERE account_validity.user_id is NULL AND users.deactivated = 0;"
)
txn.execute(sql, [])

14 changes: 14 additions & 0 deletions synapse/storage/registration.py
Original file line number Diff line number Diff line change
@@ -251,6 +251,20 @@ def set_renewal_mail_status(self, user_id, email_sent):
desc="set_renewal_mail_status",
)

@defer.inlineCallbacks
def delete_account_validity_for_user(self, user_id):
"""Deletes the entry for the given user in the account validity table, removing
their expiration date and renewal token.

Args:
user_id (str): ID of the user to remove from the account validity table.
"""
yield self._simple_delete_one(
table="account_validity",
keyvalues={"user_id": user_id},
desc="delete_account_validity_for_user",
)

@defer.inlineCallbacks
def is_server_admin(self, user):
res = yield self._simple_select_one_onecol(
67 changes: 42 additions & 25 deletions tests/rest/client/v2_alpha/test_register.py
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
from synapse.api.errors import Codes
from synapse.appservice import ApplicationService
from synapse.rest.client.v1 import login
from synapse.rest.client.v2_alpha import account_validity, register, sync
from synapse.rest.client.v2_alpha import account, account_validity, register, sync

from tests import unittest

@@ -308,6 +308,7 @@ class AccountValidityRenewalByEmailTestCase(unittest.HomeserverTestCase):
login.register_servlets,
sync.register_servlets,
account_validity.register_servlets,
account.register_servlets,
]

def make_homeserver(self, reactor, clock):
@@ -358,20 +359,7 @@ def sendmail(*args, **kwargs):
def test_renewal_email(self):
self.email_attempts = []

user_id = self.register_user("kermit", "monkey")
tok = self.login("kermit", "monkey")
# We need to manually add an email address otherwise the handler will do
# nothing.
now = self.hs.clock.time_msec()
self.get_success(
self.store.user_add_threepid(
user_id=user_id,
medium="email",
address="[email protected]",
validated_at=now,
added_at=now,
)
)
(user_id, tok) = self.create_user()

# Move 6 days forward. This should trigger a renewal email to be sent.
self.reactor.advance(datetime.timedelta(days=6).total_seconds())
@@ -396,6 +384,44 @@ def test_renewal_email(self):
def test_manual_email_send(self):
self.email_attempts = []

(user_id, tok) = self.create_user()
request, channel = self.make_request(
b"POST",
"/_matrix/client/unstable/account_validity/send_mail",
access_token=tok,
)
self.render(request)
self.assertEquals(channel.result["code"], b"200", channel.result)

self.assertEqual(len(self.email_attempts), 1)

def test_deactivated_user(self):
self.email_attempts = []

(user_id, tok) = self.create_user()

request_data = json.dumps({
"auth": {
"type": "m.login.password",
"user": user_id,
"password": "monkey",
},
"erase": False,
})
request, channel = self.make_request(
"POST",
"account/deactivate",
request_data,
access_token=tok,
)
self.render(request)
self.assertEqual(request.code, 200)

self.reactor.advance(datetime.timedelta(days=8).total_seconds())

self.assertEqual(len(self.email_attempts), 0)

def create_user(self):
user_id = self.register_user("kermit", "monkey")
tok = self.login("kermit", "monkey")
# We need to manually add an email address otherwise the handler will do
@@ -410,16 +436,7 @@ def test_manual_email_send(self):
added_at=now,
)
)

request, channel = self.make_request(
b"POST",
"/_matrix/client/unstable/account_validity/send_mail",
access_token=tok,
)
self.render(request)
self.assertEquals(channel.result["code"], b"200", channel.result)

self.assertEqual(len(self.email_attempts), 1)
return (user_id, tok)

def test_manual_email_send_expired_account(self):
user_id = self.register_user("kermit", "monkey")