From 5257a2fb1c983158bbdee8be4e61066f1a83d4a8 Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 14:49:53 +0100
Subject: [PATCH 01/10] Reject pending invites on deactivation

---
 synapse/handlers/deactivate_account.py | 31 ++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/synapse/handlers/deactivate_account.py b/synapse/handlers/deactivate_account.py
index d83912c9a4..9815365f54 100644
--- a/synapse/handlers/deactivate_account.py
+++ b/synapse/handlers/deactivate_account.py
@@ -120,6 +120,10 @@ class DeactivateAccountHandler(BaseHandler):
         # parts users from rooms (if it isn't already running)
         self._start_user_parting()
 
+        # Reject all pending invites for the user, so that it doesn't show up in the
+        # invitees list of rooms.
+        self._reject_pending_invites_for_user(user_id)
+
         # 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)
@@ -129,6 +133,33 @@ class DeactivateAccountHandler(BaseHandler):
 
         return identity_server_supports_unbinding
 
+    def _reject_pending_invites_for_user(self, user_id):
+        """Reject pending invites addressed to a given user ID.
+
+        Args:
+            user_id (str): The user ID to reject pending invites for.
+        """
+        user = UserID.from_string(user_id)
+        pending_invites = yield self.store.get_invited_rooms_for_user(user_id)
+
+        for room in pending_invites:
+            try:
+                yield self._room_member_handler.update_membership(
+                    create_requester(user),
+                    user,
+                    room.room_id,
+                    "leave",
+                    ratelimit=False,
+                    require_consent=False,
+                )
+            except Exception:
+                logger.exception(
+                    "Failed to reject invite for user %r in room %r:"
+                    " ignoring and continuing",
+                    user_id,
+                    room.room_id,
+                )
+
     def _start_user_parting(self):
         """
         Start the process that goes through the table of users

From 72a2708ac6335985eb5171f5685f73d2ea120a2e Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 15:13:39 +0100
Subject: [PATCH 02/10] Fixup and add some logging

---
 synapse/handlers/deactivate_account.py | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/synapse/handlers/deactivate_account.py b/synapse/handlers/deactivate_account.py
index 9815365f54..763fea3a24 100644
--- a/synapse/handlers/deactivate_account.py
+++ b/synapse/handlers/deactivate_account.py
@@ -122,7 +122,7 @@ class DeactivateAccountHandler(BaseHandler):
 
         # Reject all pending invites for the user, so that it doesn't show up in the
         # invitees list of rooms.
-        self._reject_pending_invites_for_user(user_id)
+        yield self._reject_pending_invites_for_user(user_id)
 
         # Remove all information on the user from the account_validity table.
         if self._account_validity_enabled:
@@ -133,6 +133,7 @@ class DeactivateAccountHandler(BaseHandler):
 
         return identity_server_supports_unbinding
 
+    @defer.inlineCallbacks
     def _reject_pending_invites_for_user(self, user_id):
         """Reject pending invites addressed to a given user ID.
 
@@ -142,6 +143,8 @@ class DeactivateAccountHandler(BaseHandler):
         user = UserID.from_string(user_id)
         pending_invites = yield self.store.get_invited_rooms_for_user(user_id)
 
+        logger.info(pending_invites)
+
         for room in pending_invites:
             try:
                 yield self._room_member_handler.update_membership(
@@ -152,6 +155,11 @@ class DeactivateAccountHandler(BaseHandler):
                     ratelimit=False,
                     require_consent=False,
                 )
+                logger.info(
+                    "Rejected invite for user %r in room %r",
+                    user_id,
+                    room.room_id,
+                )
             except Exception:
                 logger.exception(
                     "Failed to reject invite for user %r in room %r:"

From e94ff67903c3370fc5bc8b6c336433057e38ff05 Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 15:14:02 +0100
Subject: [PATCH 03/10] Add test to validate the change

---
 tests/rest/client/v2_alpha/test_account.py | 70 ++++++++++++++++++----
 1 file changed, 57 insertions(+), 13 deletions(-)

diff --git a/tests/rest/client/v2_alpha/test_account.py b/tests/rest/client/v2_alpha/test_account.py
index 920de41de4..69c33dfd8a 100644
--- a/tests/rest/client/v2_alpha/test_account.py
+++ b/tests/rest/client/v2_alpha/test_account.py
@@ -23,8 +23,8 @@ from email.parser import Parser
 import pkg_resources
 
 import synapse.rest.admin
-from synapse.api.constants import LoginType
-from synapse.rest.client.v1 import login
+from synapse.api.constants import LoginType, Membership
+from synapse.rest.client.v1 import login, room
 from synapse.rest.client.v2_alpha import account, register
 
 from tests import unittest
@@ -244,16 +244,69 @@ class DeactivateTestCase(unittest.HomeserverTestCase):
         synapse.rest.admin.register_servlets_for_client_rest_resource,
         login.register_servlets,
         account.register_servlets,
+        room.register_servlets,
     ]
 
     def make_homeserver(self, reactor, clock):
-        hs = self.setup_test_homeserver()
-        return hs
+        self.hs = self.setup_test_homeserver()
+        return self.hs
 
     def test_deactivate_account(self):
         user_id = self.register_user("kermit", "test")
         tok = self.login("kermit", "test")
 
+        self.deactivate(user_id, tok)
+
+        store = self.hs.get_datastore()
+
+        # Check that the user has been marked as deactivated.
+        self.assertTrue(self.get_success(store.get_user_deactivated_status(user_id)))
+
+        # Check that this access token has been invalidated.
+        request, channel = self.make_request("GET", "account/whoami")
+        self.render(request)
+        self.assertEqual(request.code, 401)
+
+    @unittest.INFO
+    def test_pending_invites(self):
+        """Tests that deactivating a user rejects every pending invite for them."""
+        store = self.hs.get_datastore()
+
+        inviter_id = self.register_user("inviter", "test")
+        inviter_tok = self.login("inviter", "test")
+
+        invitee_id = self.register_user("invitee", "test")
+        invitee_tok = self.login("invitee", "test")
+
+        # Make @inviter:test invite @invitee:test in a new room.
+        room_id = self.helper.create_room_as(inviter_id, tok=inviter_tok)
+        self.helper.invite(
+            room=room_id,
+            src=inviter_id,
+            targ=invitee_id,
+            tok=inviter_tok,
+        )
+
+        # Make sure the invite is here.
+        pending_invites = self.get_success(store.get_invited_rooms_for_user(invitee_id))
+        self.assertEqual(len(pending_invites), 1, pending_invites)
+        self.assertEqual(pending_invites[0].room_id, room_id, pending_invites)
+
+        # Deactivate @invitee:test.
+        self.deactivate(invitee_id, invitee_tok)
+
+        # Check that the invite isn't there anymore.
+        pending_invites = self.get_success(store.get_invited_rooms_for_user(invitee_id))
+        self.assertEqual(len(pending_invites), 0, pending_invites)
+
+        # Check that the membership of @invitee:test in the room is now "leave".
+        memberships = self.get_success(
+            store.get_rooms_for_user_where_membership_is(invitee_id, [Membership.LEAVE])
+        )
+        self.assertEqual(len(memberships), 1, memberships)
+        self.assertEqual(memberships[0].room_id, room_id, memberships)
+
+    def deactivate(self, user_id, tok):
         request_data = json.dumps(
             {
                 "auth": {
@@ -270,12 +323,3 @@ class DeactivateTestCase(unittest.HomeserverTestCase):
         self.render(request)
         self.assertEqual(request.code, 200)
 
-        store = self.hs.get_datastore()
-
-        # Check that the user has been marked as deactivated.
-        self.assertTrue(self.get_success(store.get_user_deactivated_status(user_id)))
-
-        # Check that this access token has been invalidated.
-        request, channel = self.make_request("GET", "account/whoami")
-        self.render(request)
-        self.assertEqual(request.code, 401)

From 0804a27c8c7c2cc9f0adbb0329bffcd8ce10e1bd Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 15:14:34 +0100
Subject: [PATCH 04/10] Changelog

---
 changelog.d/6125.feature | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 changelog.d/6125.feature

diff --git a/changelog.d/6125.feature b/changelog.d/6125.feature
new file mode 100644
index 0000000000..432e255ad4
--- /dev/null
+++ b/changelog.d/6125.feature
@@ -0,0 +1 @@
+Reject all pending invite for a user during deactivation.

From 873fe7883cf0d7cf5346a9a55d40967a35848e33 Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 15:21:03 +0100
Subject: [PATCH 05/10] Lint

---
 synapse/handlers/deactivate_account.py     | 4 +---
 tests/rest/client/v2_alpha/test_account.py | 8 +-------
 2 files changed, 2 insertions(+), 10 deletions(-)

diff --git a/synapse/handlers/deactivate_account.py b/synapse/handlers/deactivate_account.py
index 763fea3a24..148d1424ca 100644
--- a/synapse/handlers/deactivate_account.py
+++ b/synapse/handlers/deactivate_account.py
@@ -156,9 +156,7 @@ class DeactivateAccountHandler(BaseHandler):
                     require_consent=False,
                 )
                 logger.info(
-                    "Rejected invite for user %r in room %r",
-                    user_id,
-                    room.room_id,
+                    "Rejected invite for user %r in room %r", user_id, room.room_id
                 )
             except Exception:
                 logger.exception(
diff --git a/tests/rest/client/v2_alpha/test_account.py b/tests/rest/client/v2_alpha/test_account.py
index 69c33dfd8a..434b730faf 100644
--- a/tests/rest/client/v2_alpha/test_account.py
+++ b/tests/rest/client/v2_alpha/test_account.py
@@ -280,12 +280,7 @@ class DeactivateTestCase(unittest.HomeserverTestCase):
 
         # Make @inviter:test invite @invitee:test in a new room.
         room_id = self.helper.create_room_as(inviter_id, tok=inviter_tok)
-        self.helper.invite(
-            room=room_id,
-            src=inviter_id,
-            targ=invitee_id,
-            tok=inviter_tok,
-        )
+        self.helper.invite(room=room_id, src=inviter_id, targ=invitee_id, tok=inviter_tok)
 
         # Make sure the invite is here.
         pending_invites = self.get_success(store.get_invited_rooms_for_user(invitee_id))
@@ -322,4 +317,3 @@ class DeactivateTestCase(unittest.HomeserverTestCase):
         )
         self.render(request)
         self.assertEqual(request.code, 200)
-

From fbb8ff3088abab48bd5815a1acaeb9243ada7431 Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 15:23:07 +0100
Subject: [PATCH 06/10] ok

---
 tests/rest/client/v2_alpha/test_account.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tests/rest/client/v2_alpha/test_account.py b/tests/rest/client/v2_alpha/test_account.py
index 434b730faf..0f51895b81 100644
--- a/tests/rest/client/v2_alpha/test_account.py
+++ b/tests/rest/client/v2_alpha/test_account.py
@@ -280,7 +280,9 @@ class DeactivateTestCase(unittest.HomeserverTestCase):
 
         # Make @inviter:test invite @invitee:test in a new room.
         room_id = self.helper.create_room_as(inviter_id, tok=inviter_tok)
-        self.helper.invite(room=room_id, src=inviter_id, targ=invitee_id, tok=inviter_tok)
+        self.helper.invite(
+            room=room_id, src=inviter_id, targ=invitee_id, tok=inviter_tok
+        )
 
         # Make sure the invite is here.
         pending_invites = self.get_success(store.get_invited_rooms_for_user(invitee_id))

From 25a0a36ad9b63aa2becabc5c311025cb612d466f Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 16:10:24 +0100
Subject: [PATCH 07/10] Update changelog.d/6125.feature

Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
---
 changelog.d/6125.feature | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/changelog.d/6125.feature b/changelog.d/6125.feature
index 432e255ad4..cbe5f8d3c8 100644
--- a/changelog.d/6125.feature
+++ b/changelog.d/6125.feature
@@ -1 +1 @@
-Reject all pending invite for a user during deactivation.
+Reject all pending invites for a user during deactivation.

From bbe2a0f33916d7b01179c56b230307c46843625a Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 16:10:36 +0100
Subject: [PATCH 08/10] Update synapse/handlers/deactivate_account.py

Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
---
 synapse/handlers/deactivate_account.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/synapse/handlers/deactivate_account.py b/synapse/handlers/deactivate_account.py
index 148d1424ca..5cf01479db 100644
--- a/synapse/handlers/deactivate_account.py
+++ b/synapse/handlers/deactivate_account.py
@@ -120,7 +120,7 @@ class DeactivateAccountHandler(BaseHandler):
         # parts users from rooms (if it isn't already running)
         self._start_user_parting()
 
-        # Reject all pending invites for the user, so that it doesn't show up in the
+        # Reject all pending invites for the user, so that they do not show up in the
         # invitees list of rooms.
         yield self._reject_pending_invites_for_user(user_id)
 

From af92110c465ea7cf4d04e1193b58f16ae26a75d6 Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 16:12:15 +0100
Subject: [PATCH 09/10] Update synapse/handlers/deactivate_account.py

Co-Authored-By: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
---
 synapse/handlers/deactivate_account.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/synapse/handlers/deactivate_account.py b/synapse/handlers/deactivate_account.py
index 5cf01479db..5f142f82c2 100644
--- a/synapse/handlers/deactivate_account.py
+++ b/synapse/handlers/deactivate_account.py
@@ -156,7 +156,9 @@ class DeactivateAccountHandler(BaseHandler):
                     require_consent=False,
                 )
                 logger.info(
-                    "Rejected invite for user %r in room %r", user_id, room.room_id
+                    "Rejected invite for deactivated user %r in room %r",
+                    user_id,
+                    room.room_id,
                 )
             except Exception:
                 logger.exception(

From 3e42d47a5a06ea5d353b75a42040107bf401d8ba Mon Sep 17 00:00:00 2001
From: Brendan Abolivier <babolivier@matrix.org>
Date: Fri, 27 Sep 2019 16:15:01 +0100
Subject: [PATCH 10/10] Incorporate review

---
 synapse/handlers/deactivate_account.py | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/synapse/handlers/deactivate_account.py b/synapse/handlers/deactivate_account.py
index 5f142f82c2..63267a0a4c 100644
--- a/synapse/handlers/deactivate_account.py
+++ b/synapse/handlers/deactivate_account.py
@@ -120,8 +120,8 @@ class DeactivateAccountHandler(BaseHandler):
         # parts users from rooms (if it isn't already running)
         self._start_user_parting()
 
-        # Reject all pending invites for the user, so that they do not show up in the
-        # invitees list of rooms.
+        # Reject all pending invites for the user, so that the user doesn't show up in the
+        # "invited" section of rooms' members list.
         yield self._reject_pending_invites_for_user(user_id)
 
         # Remove all information on the user from the account_validity table.
@@ -143,8 +143,6 @@ class DeactivateAccountHandler(BaseHandler):
         user = UserID.from_string(user_id)
         pending_invites = yield self.store.get_invited_rooms_for_user(user_id)
 
-        logger.info(pending_invites)
-
         for room in pending_invites:
             try:
                 yield self._room_member_handler.update_membership(