From 93a7dd9cf3ee3b67982ab63965050c67ee2bc829 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 5 Nov 2023 07:50:28 -0800 Subject: [PATCH] Erase user data and statuses on account deletion --- bookwyrm/models/user.py | 24 ++++++++++++++- bookwyrm/tests/models/test_user_model.py | 38 ++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index c152cf445..625a7d289 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -1,6 +1,7 @@ """ database schema for user data """ import re from urllib.parse import urlparse +from uuid import uuid4 from django.apps import apps from django.contrib.auth.models import AbstractUser @@ -394,10 +395,31 @@ class User(OrderedCollectionPageMixin, AbstractUser): """We don't actually delete the database entry""" # pylint: disable=attribute-defined-outside-init self.is_active = False - self.avatar = "" + self.allow_reactivation = False + + self.erase_user_data() + self.erase_user_statuses() + # skip the logic in this class's save() super().save(*args, **kwargs) + def erase_user_data(self): + """Wipe a user's custom data""" + # mangle email address + self.email = f"{uuid4()}@deleted.user" + + # erase data fields + self.avatar = "" + self.preview_image = "" + self.summary = None + self.name = None + self.favorites.set([]) + + def erase_user_statuses(self): + """Wipe the data on all the user's statuses""" + for status in self.status_set.all(): + status.delete() + def deactivate(self): """Disable the user but allow them to reactivate""" # pylint: disable=attribute-defined-outside-init diff --git a/bookwyrm/tests/models/test_user_model.py b/bookwyrm/tests/models/test_user_model.py index 838dd2e49..de39d5467 100644 --- a/bookwyrm/tests/models/test_user_model.py +++ b/bookwyrm/tests/models/test_user_model.py @@ -26,6 +26,7 @@ class User(TestCase): local=True, localname="mouse", name="hi", + summary="a summary", bookwyrm_user=False, ) self.another_user = models.User.objects.create_user( @@ -218,19 +219,52 @@ class User(TestCase): @patch("bookwyrm.suggested_users.remove_user_task.delay") def test_delete_user(self, _): - """deactivate a user""" + """permanently delete a user""" self.assertTrue(self.user.is_active) + self.assertEqual(self.user.name, "hi") + self.assertEqual(self.user.summary, "a summary") + self.assertEqual(self.user.email, "mouse@mouse.mouse") with patch( "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" - ) as broadcast_mock: + ) as broadcast_mock, patch( + "bookwyrm.models.user.User.erase_user_statuses" + ) as erase_statuses_mock: self.user.delete() + self.assertEqual(erase_statuses_mock.call_count, 1) + + # make sure the deletion is broadcast self.assertEqual(broadcast_mock.call_count, 1) activity = json.loads(broadcast_mock.call_args[1]["args"][1]) self.assertEqual(activity["type"], "Delete") self.assertEqual(activity["object"], self.user.remote_id) + + self.user.refresh_from_db() + + # the user's account data should be deleted + self.assertIsNone(self.user.name) + self.assertIsNone(self.user.summary) + self.assertNotEqual(self.user.email, "mouse@mouse.mouse") self.assertFalse(self.user.is_active) + @patch("bookwyrm.suggested_users.remove_user_task.delay") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") + @patch("bookwyrm.activitystreams.add_status_task.delay") + @patch("bookwyrm.activitystreams.remove_status_task.delay") + def test_delete_user_erase_statuses(self, *_): + """erase user statuses when user is deleted""" + status = models.Status.objects.create(user=self.user, content="hello") + self.assertFalse(status.deleted) + self.assertIsNotNone(status.content) + self.assertIsNone(status.deleted_date) + + self.user.delete() + status.refresh_from_db() + + self.assertTrue(status.deleted) + self.assertIsNone(status.content) + self.assertIsNotNone(status.deleted_date) + def test_admins_no_admins(self): """list of admins""" result = models.User.admins()