Repository: oppia/oppia

Issue:
[Delete Account] Able to login again after the account has been deleted
Expected:
The user can delete their account from the preferences page, after their account is deleted they shouldn’t be able to log-in and should always be redirected to the pending deletion page.

Observed:
The user, after deletion, is able to sign-in again with a different user name and navigate through pages.



Discussion:
`get_user_id_from_auth_id` in gae_auth_services.py needs to use `get_by_id` or some other way that retrieves models marked as deleted for getting `UserIdentifiersModel`.

Resolving patch:
diff --git a/core/platform/auth/gae_auth_services.py b/core/platform/auth/gae_auth_services.py
--- a/core/platform/auth/gae_auth_services.py
+++ b/core/platform/auth/gae_auth_services.py
@@ -150,7 +150,7 @@ def get_user_id_from_auth_id(auth_id):
         association exists.
     """
     assoc_by_auth_id_model = (
-        auth_models.UserIdentifiersModel.get(auth_id, strict=False))
+        auth_models.UserIdentifiersModel.get_by_gae_id(auth_id))
     return (
         None if assoc_by_auth_id_model is None else
         assoc_by_auth_id_model.user_id)
diff --git a/core/storage/auth/gae_models.py b/core/storage/auth/gae_models.py
--- a/core/storage/auth/gae_models.py
+++ b/core/storage/auth/gae_models.py
@@ -199,6 +199,19 @@ def has_reference_to_user_id(cls, user_id):
         """
         return cls.query(cls.user_id == user_id).get(keys_only=True) is not None
 
+    @classmethod
+    def get_by_gae_id(cls, gae_id):
+        """Fetch an entry by user ID.
+
+        Args:
+            gae_id: str. The GAE ID.
+
+        Returns:
+            UserIdentifiersModel. The model with user_id field equal to user_id
+            argument.
+        """
+        return cls.get_by_id(gae_id)
+
     @classmethod
     def get_by_user_id(cls, user_id):
         """Fetch an entry by user ID.


Test patch:
diff --git a/core/platform/auth/gae_auth_services_test.py b/core/platform/auth/gae_auth_services_test.py
--- a/core/platform/auth/gae_auth_services_test.py
+++ b/core/platform/auth/gae_auth_services_test.py
@@ -73,6 +73,20 @@ def test_get_association_that_is_present(self):
         self.assertEqual(
             gae_auth_services.get_auth_id_from_user_id('uid'), 'aid')
 
+    def test_get_association_that_is_present_and_marked_as_deleted(self):
+        gae_auth_services.associate_auth_id_with_user_id(
+            auth_domain.AuthIdUserIdPair('aid', 'uid'))
+
+        assoc_by_auth_id_model = auth_models.UserIdentifiersModel.get('aid')
+        assoc_by_auth_id_model.deleted = True
+        assoc_by_auth_id_model.update_timestamps()
+        assoc_by_auth_id_model.put()
+
+        self.assertEqual(
+            gae_auth_services.get_user_id_from_auth_id('aid'), 'uid')
+        self.assertEqual(
+            gae_auth_services.get_auth_id_from_user_id('uid'), 'aid')
+
     def test_get_association_that_is_missing(self):
         self.assertIsNone(
             gae_auth_services.get_user_id_from_auth_id('does_not_exist'))
diff --git a/core/tests/protractor_desktop/wipeout.js b/core/tests/protractor_desktop/wipeout.js
--- a/core/tests/protractor_desktop/wipeout.js
+++ b/core/tests/protractor_desktop/wipeout.js
@@ -46,6 +46,10 @@ describe('When account is deleted it', function() {
     await deleteAccountPage.requestAccountDeletion('userToDelete1');
     expect(await browser.getCurrentUrl()).toEqual(
       'http://localhost:9001/pending-account-deletion');
+    await users.login('user1@delete.com');
+    await browser.get('/signup?return_url=http%3A%2F%2Flocalhost%3A9001%2F');
+    expect(await browser.getCurrentUrl()).toEqual(
+      'http://localhost:9001/pending-account-deletion');
   });
 
   it('should delete private exploration', async function() {