Erase user data and statuses on account deletion

This commit is contained in:
Mouse Reeve 2023-11-05 07:50:28 -08:00
parent 95ba38524b
commit 93a7dd9cf3
2 changed files with 59 additions and 3 deletions

View file

@ -1,6 +1,7 @@
""" database schema for user data """ """ database schema for user data """
import re import re
from urllib.parse import urlparse from urllib.parse import urlparse
from uuid import uuid4
from django.apps import apps from django.apps import apps
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
@ -394,10 +395,31 @@ class User(OrderedCollectionPageMixin, AbstractUser):
"""We don't actually delete the database entry""" """We don't actually delete the database entry"""
# pylint: disable=attribute-defined-outside-init # pylint: disable=attribute-defined-outside-init
self.is_active = False 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() # skip the logic in this class's save()
super().save(*args, **kwargs) 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): def deactivate(self):
"""Disable the user but allow them to reactivate""" """Disable the user but allow them to reactivate"""
# pylint: disable=attribute-defined-outside-init # pylint: disable=attribute-defined-outside-init

View file

@ -26,6 +26,7 @@ class User(TestCase):
local=True, local=True,
localname="mouse", localname="mouse",
name="hi", name="hi",
summary="a summary",
bookwyrm_user=False, bookwyrm_user=False,
) )
self.another_user = models.User.objects.create_user( self.another_user = models.User.objects.create_user(
@ -218,19 +219,52 @@ class User(TestCase):
@patch("bookwyrm.suggested_users.remove_user_task.delay") @patch("bookwyrm.suggested_users.remove_user_task.delay")
def test_delete_user(self, _): def test_delete_user(self, _):
"""deactivate a user""" """permanently delete a user"""
self.assertTrue(self.user.is_active) 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( with patch(
"bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" "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.user.delete()
self.assertEqual(erase_statuses_mock.call_count, 1)
# make sure the deletion is broadcast
self.assertEqual(broadcast_mock.call_count, 1) self.assertEqual(broadcast_mock.call_count, 1)
activity = json.loads(broadcast_mock.call_args[1]["args"][1]) activity = json.loads(broadcast_mock.call_args[1]["args"][1])
self.assertEqual(activity["type"], "Delete") self.assertEqual(activity["type"], "Delete")
self.assertEqual(activity["object"], self.user.remote_id) 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) 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): def test_admins_no_admins(self):
"""list of admins""" """list of admins"""
result = models.User.admins() result = models.User.admins()