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 #5394

Closed
Closed
Show file tree
Hide file tree
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
Expand Up @@ -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

Expand Down
9 changes: 9 additions & 0 deletions synapse/handlers/deactivate_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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
Expand Down Expand Up @@ -114,6 +116,13 @@ 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)

defer.returnValue(identity_server_supports_unbinding)

def _start_user_parting(self):
Expand Down
4 changes: 2 additions & 2 deletions synapse/storage/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, [])

Expand Down
14 changes: 14 additions & 0 deletions synapse/storage/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,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(
Expand Down
67 changes: 42 additions & 25 deletions tests/rest/client/v2_alpha/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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="kermit@example.com",
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())
Expand All @@ -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
Expand All @@ -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)


class AccountValidityBackgroundJobTestCase(unittest.HomeserverTestCase):
Expand Down