Skip to content
Merged
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
3 changes: 2 additions & 1 deletion admin/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,13 @@ class UserGDPRDeleteView(UserMixin, View):
permission_required = 'osf.change_osfuser'

def post(self, request, *args, **kwargs):
user = self.get_object()
try:
user = self.get_object()
user.gdpr_delete()
user.save()
except UserStateError as e:
messages.warning(request, str(e))
return redirect(self.get_success_url())

messages.success(request, f'User {user._id} was successfully GDPR deleted')

Expand Down
15 changes: 15 additions & 0 deletions admin_tests/users/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
UserFactory,
AuthUserFactory,
ProjectFactory,
PreprintFactory,
UnconfirmedUserFactory
)
from admin_tests.utilities import setup_view, setup_log_view, setup_form_view
Expand Down Expand Up @@ -157,6 +158,20 @@ def test_user_with_deleted_node_is_deleted(self):
self.user.reload()
assert self.user.deleted

def test_gdpr_delete_blocked_by_resource_without_alternate_admin_does_not_report_success(self):
patch_messages(self.request)

preprint = PreprintFactory(creator=self.user)
other_contrib = UserFactory()
preprint.add_contributor(other_contrib, auth=Auth(self.user), save=True)

count = AdminLogEntry.objects.count()
self.view().post(self.request)

self.user.reload()
assert not self.user.deleted
assert AdminLogEntry.objects.count() == count


class TestDisableUser(AdminTestCase):
def setUp(self):
Expand Down
5 changes: 5 additions & 0 deletions osf/models/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,11 @@ def get_addons(self, service_type: str | None = None, in_request_context: bool =
lambda x: x is not None,
(self.get_addon(addon) for addon in self.OSF_HOSTED_ADDONS)
)
if not self._id:
# Resources without a guid (e.g. legacy nodes without guids) can't be
# associated with addons in GravyValet, which needs a guid to build
# the resource's semantic IRI.
return osf_addons
return itertools.chain(osf_addons, self._get_addons_from_gv(requesting_user_id=user_id, service_type=service_type, auth=auth))

return [_f for _f in [
Expand Down
12 changes: 12 additions & 0 deletions osf_tests/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import itsdangerous
import pytest
from importlib import import_module
from waffle.testutils import override_flag

from framework.auth.exceptions import ExpiredTokenError, InvalidTokenError, ChangePasswordError
from framework.auth.signals import user_account_merged
Expand Down Expand Up @@ -42,6 +43,7 @@
from osf.utils.names import impute_names_model
from osf.utils import permissions
from osf.exceptions import ValidationError, BlockedEmailError, UserStateError, InstitutionAffiliationStateError
from osf.features import ENABLE_GV

from .utils import capture_signals
from .factories import (
Expand Down Expand Up @@ -2326,6 +2328,16 @@ def test_cant_gdpr_delete_shared_node_if_only_admin(self, user, project_user_is_
assert exc_info.value.args[0] == 'You cannot delete Node {} because it would' \
' be a Node with contributors, but with no admin.'.format(project_user_is_only_admin._id)

def test_can_gdpr_delete_shared_node_without_guid(self, user, project_with_two_admins):
project_with_two_admins.guids.all().delete()
node = AbstractNode.objects.get(pk=project_with_two_admins.pk)
assert node._id is None

with override_flag(ENABLE_GV, active=True):
user.gdpr_delete()

assert user.nodes.all().count() == 0

def test_cant_gdpr_delete_with_addon_credentials(self, user, project_with_two_admins_and_addon_credentials):

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