Repository: bookwyrm-social/bookwyrm

Issue:
Reactivation of permanently deleted user account
**Describe the bug**
Reactivation of any permanently deleted user account can be possible to restore by the user via confirmation of the email address or using the reactivation method of a deactivated user account.

Detailed report: https://huntr.dev/bounties/d294ea37-b067-49bd-a806-2a893a1c8b18/


Discussion:

Resolving patch:
diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py
--- a/bookwyrm/models/user.py
+++ b/bookwyrm/models/user.py
@@ -394,6 +394,8 @@ def deactivate(self):
     def reactivate(self):
         """Now you want to come back, huh?"""
         # pylint: disable=attribute-defined-outside-init
+        if not self.allow_reactivation:
+            return
         self.is_active = True
         self.deactivation_reason = None
         self.allow_reactivation = False
diff --git a/bookwyrm/views/landing/register.py b/bookwyrm/views/landing/register.py
--- a/bookwyrm/views/landing/register.py
+++ b/bookwyrm/views/landing/register.py
@@ -74,6 +74,7 @@ def post(self, request):
             password,
             localname=localname,
             local=True,
+            allow_reactivation=settings.require_confirm_email,
             deactivation_reason="pending" if settings.require_confirm_email else None,
             is_active=not settings.require_confirm_email,
             preferred_timezone=preferred_timezone,
@@ -105,7 +106,9 @@ def get(self, request, code):  # pylint: disable=unused-argument
 
         # look up the user associated with this code
         try:
-            user = models.User.objects.get(confirmation_code=code)
+            user = models.User.objects.get(
+                confirmation_code=code, deactivation_reason="pending"
+            )
         except models.User.DoesNotExist:
             return TemplateResponse(
                 request, "confirm_email/confirm_email.html", {"valid": False}


Test patch:
diff --git a/bookwyrm/tests/views/landing/test_register.py b/bookwyrm/tests/views/landing/test_register.py
--- a/bookwyrm/tests/views/landing/test_register.py
+++ b/bookwyrm/tests/views/landing/test_register.py
@@ -347,11 +347,17 @@ def test_confirm_email_code_get(self, *_):
         self.settings.save()
 
         self.local_user.is_active = False
+        self.local_user.allow_reactivation = True
         self.local_user.deactivation_reason = "pending"
         self.local_user.confirmation_code = "12345"
         self.local_user.save(
             broadcast=False,
-            update_fields=["is_active", "deactivation_reason", "confirmation_code"],
+            update_fields=[
+                "is_active",
+                "allow_reactivation",
+                "deactivation_reason",
+                "confirmation_code",
+            ],
         )
         view = views.ConfirmEmailCode.as_view()
         request = self.factory.get("")
diff --git a/bookwyrm/tests/views/preferences/test_delete_user.py b/bookwyrm/tests/views/preferences/test_delete_user.py
--- a/bookwyrm/tests/views/preferences/test_delete_user.py
+++ b/bookwyrm/tests/views/preferences/test_delete_user.py
@@ -141,3 +141,24 @@ def test_reactivate_user_post(self, _):
         self.local_user.refresh_from_db()
         self.assertTrue(self.local_user.is_active)
         self.assertIsNone(self.local_user.deactivation_reason)
+
+    def test_reactivate_user_post_disallowed(self, _):
+        """Reactivate action under the wrong circumstances"""
+        self.local_user.is_active = False
+        self.local_user.save(broadcast=False)
+
+        view = views.ReactivateUser.as_view()
+        form = forms.LoginForm()
+        form.data["localname"] = "mouse"
+        form.data["password"] = "password"
+        request = self.factory.post("", form.data)
+        request.user = self.local_user
+        middleware = SessionMiddleware()
+        middleware.process_request(request)
+        request.session.save()
+
+        with patch("bookwyrm.views.preferences.delete_user.login"):
+            view(request)
+
+        self.local_user.refresh_from_db()
+        self.assertFalse(self.local_user.is_active)