Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions osf/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -1979,13 +1979,34 @@ def gdpr_delete(self):
def _validate_no_public_entities(self):
"""
Ensure that the user doesn't have any public facing resources like Registrations or Preprints
that would be left with other contributors after this deletion.

Allow GDPR deletion if the user is the sole contributor on a public Registration or Preprint.
"""
from osf.models import Preprint
from osf.models import Preprint, AbstractNode

registrations_with_others = AbstractNode.objects.annotate(
contrib_count=Count('_contributors', distinct=True),
).filter(
_contributors=self,
deleted__isnull=True,
type='osf.registration',
contrib_count__gt=1
).exists()

if self.nodes.filter(deleted__isnull=True, type='osf.registration').exists():
if registrations_with_others:
raise UserStateError('You cannot delete this user because they have one or more registrations.')

if Preprint.objects.filter(_contributors=self, ever_public=True, deleted__isnull=True).exists():
preprints_with_others = Preprint.objects.annotate(
contrib_count=Count('_contributors', distinct=True),
).filter(
_contributors=self,
ever_public=True,
deleted__isnull=True,
contrib_count__gt=1
).exists()

if preprints_with_others:
raise UserStateError('You cannot delete this user because they have one or more preprints.')

def _validate_and_remove_resource_for_gdpr_delete(self, resources, hard_delete):
Expand Down
61 changes: 36 additions & 25 deletions osf_tests/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
NotableDomain,
PreprintContributor,
DraftRegistrationContributor,
DraftRegistration,
DraftNode,
UserSessionMap, NotificationType,
)
from osf.models.institution_affiliation import get_user_by_institution_identity
Expand Down Expand Up @@ -2134,26 +2132,6 @@ def test_can_gdpr_delete_personal_nodes(self, user):
user.gdpr_delete()
assert user.nodes.exclude(is_deleted=True).count() == 0

def test_can_gdpr_delete_personal_registrations(self, user, registration_with_draft_node):
assert DraftRegistration.objects.all().count() == 1
assert DraftNode.objects.all().count() == 1

with pytest.raises(UserStateError) as exc_info:
user.gdpr_delete()

assert exc_info.value.args[0] == 'You cannot delete this user because they have one or more registrations.'
assert DraftRegistration.objects.all().count() == 1
assert DraftNode.objects.all().count() == 1

registration_with_draft_node.remove_node(Auth(user))
assert DraftRegistration.objects.all().count() == 1
assert DraftNode.objects.all().count() == 1
user.gdpr_delete()

# DraftNodes soft-deleted, DraftRegistions hard-deleted
assert user.nodes.exclude(is_deleted=True).count() == 0
assert DraftRegistration.objects.all().count() == 0

def test_can_gdpr_delete_shared_nodes_with_multiple_admins(self, user, project_with_two_admins):

user.gdpr_delete()
Expand All @@ -2162,28 +2140,61 @@ def test_can_gdpr_delete_shared_nodes_with_multiple_admins(self, user, project_w
def test_can_gdpr_delete_shared_draft_registration_with_multiple_admins(self, user, registration):
other_admin = AuthUserFactory()
draft_registrations = user.draft_registrations.get()
draft_registrations.add_contributor(other_admin, permissions='admin')
draft_registrations.add_contributor(other_admin, auth=Auth(user), permissions='admin')
assert draft_registrations.contributors.all().count() == 2
registration.delete_registration_tree(save=True)

user.gdpr_delete()
assert draft_registrations.contributors.get() == other_admin
assert user.nodes.filter(deleted__isnull=True).count() == 0

def test_cant_gdpr_delete_registrations(self, user, registration):
def test_cant_gdpr_delete_multiple_contributors_registrations(self, user, registration):
registration.is_public = True
other_user = AuthUserFactory()
registration.add_contributor(other_user, auth=Auth(user), permissions='admin')
registration.save()

assert registration.contributors.count() == 2

with pytest.raises(UserStateError) as exc_info:
user.gdpr_delete()

assert exc_info.value.args[0] == 'You cannot delete this user because they have one or more registrations.'

def test_cant_gdpr_delete_preprints(self, user, preprint):
def test_cant_gdpr_delete_multiple_contributors_preprints(self, user, preprint):
other_user = AuthUserFactory()
preprint.add_contributor(other_user, auth=Auth(user), permissions='admin')
preprint.save()

with pytest.raises(UserStateError) as exc_info:
user.gdpr_delete()

assert exc_info.value.args[0] == 'You cannot delete this user because they have one or more preprints.'

def test_can_gdpr_delete_sole_contributor_registration(self, user):
registration = RegistrationFactory(creator=user)
registration.save()

assert registration.contributors.count() == 1
assert registration.contributors.first() == user

user.gdpr_delete()

assert user.fullname == 'Deleted user'
assert user.deleted is not None

def test_can_gdpr_delete_sole_contributor_preprint(self, user):
preprint = PreprintFactory(creator=user)
preprint.save()

assert preprint.contributors.count() == 1
assert preprint.contributors.first() == user

user.gdpr_delete()

assert user.fullname == 'Deleted user'
assert user.deleted is not None

def test_cant_gdpr_delete_shared_node_if_only_admin(self, user, project_user_is_only_admin):

with pytest.raises(UserStateError) as exc_info:
Expand Down
Loading