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
168 changes: 130 additions & 38 deletions admin/nodes/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ def post(self, request, *args, **kwargs):

node.add_log(
action=NodeLog.REGISTRATION_DATE_UPDATED,
auth=request,
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params={
'last_date': str(last_date),
'new_date': str(new_date)
Expand Down Expand Up @@ -225,22 +226,18 @@ def post(self, request, *args, **kwargs):
message=f'User {user.pk} removed from {node.__class__.__name__.lower()} {node.pk}.',
action_flag=CONTRIBUTOR_REMOVED
)
# Log invisibly on the OSF.
self.add_contributor_removed_log(node, user)
return redirect(self.get_success_url())
params = dict(node.log_params)
params['contributors'] = user.pk
node.add_log(
action=NodeLog.CONTRIB_REMOVED,
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params=params,
log_date=timezone.now(),
should_hide=False,
)

def add_contributor_removed_log(self, node, user):
NodeLog(
action=NodeLog.CONTRIB_REMOVED,
user=None,
params={
'project': node.parent_id,
'node': node.pk,
'contributors': user.pk
},
date=timezone.now(),
should_hide=True,
).save()
return redirect(self.get_success_url())


class NodeUpdatePermissionsView(NodeMixin, View):
Expand All @@ -266,6 +263,7 @@ def post(self, request, *args, **kwargs):
new_permissions_to_add = data.get('new-permissions', [])

new_permission_indexes_to_remove = []
added_contributor_ids = []
for email, permission in zip(new_emails_to_add, new_permissions_to_add):
contributor_user = OSFUser.objects.filter(emails__address=email.lower()).first()
if not contributor_user:
Expand All @@ -281,8 +279,10 @@ def post(self, request, *args, **kwargs):
auth=request,
user_id=contributor_user._id,
permissions=permission,
notification_type=None
notification_type=None,
log=False,
)
added_contributor_ids.append(contributor_user._id)
messages.success(self.request, f'User with email {email} was successfully added.')

# should remove permissions of invalid emails because
Expand All @@ -291,13 +291,27 @@ def post(self, request, *args, **kwargs):
for permission_index in new_permission_indexes_to_remove:
new_permissions_to_add.pop(permission_index)

# Log support-added contributors, if any
if added_contributor_ids:
params = resource.log_params
params['contributors'] = added_contributor_ids
resource.add_log(
action=NodeLog.CONTRIB_ADDED,
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params=params,
log_date=timezone.now(),
should_hide=False,
)

updated_permissions = data.get('updated-permissions', [])
all_permissions = updated_permissions + new_permissions_to_add
has_admin = list(filter(lambda permission: ADMIN in permission, all_permissions))
if not has_admin:
messages.error(self.request, 'Must be at least one admin on this node.')
return redirect(self.get_success_url())

permissions_changed = {}
for contributor_permission in updated_permissions:
guid, permission = contributor_permission.split('-')
user = OSFUser.load(guid)
Expand All @@ -307,7 +321,21 @@ def post(self, request, *args, **kwargs):
resource.get_visible(user),
request,
save=True,
skip_permission=True
skip_permission=True,
log=False,
)
permissions_changed[user._id] = permission

if permissions_changed:
params = resource.log_params
params['contributors'] = permissions_changed
resource.add_log(
action=NodeLog.PERMISSIONS_UPDATED,
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params=params,
log_date=timezone.now(),
should_hide=False,
)

return redirect(self.get_success_url())
Expand All @@ -332,15 +360,14 @@ def post(self, request, *args, **kwargs):
message=f'Node {node.pk} restored.',
action_flag=NODE_RESTORED
)
NodeLog(
node.add_log(
action=NodeLog.NODE_CREATED,
user=None,
params={
'project': node.parent_id,
},
date=timezone.now(),
should_hide=True,
).save()
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params=dict(node.log_params),
log_date=timezone.now(),
should_hide=False,
)
else:
node.is_deleted = True
node.deleted = timezone.now()
Expand All @@ -352,15 +379,14 @@ def post(self, request, *args, **kwargs):
message=f'Node {node.pk} removed.',
action_flag=NODE_REMOVED
)
NodeLog(
node.add_log(
action=NodeLog.NODE_REMOVED,
user=None,
params={
'project': node.parent_id,
},
date=timezone.now(),
should_hide=True,
).save()
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params=dict(node.log_params),
log_date=timezone.now(),
should_hide=False,
)
node.save()

return redirect(self.get_success_url())
Expand Down Expand Up @@ -852,6 +878,18 @@ def post(self, request, *args, **kwargs):

node.save()

node.add_log(
action=NodeLog.MADE_PRIVATE,
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params={
'project': node.parent_id,
'node': node._primary_key,
},
log_date=timezone.now(),
should_hide=False,
)

return redirect(self.get_success_url())


Expand All @@ -863,9 +901,21 @@ class NodeMakePublic(NodeMixin, View):
def post(self, request, *args, **kwargs):
node = self.get_object()
try:
node.set_privacy('public')
node.set_privacy('public', auth=None, log=False)
except NodeStateError as e:
messages.error(request, str(e))
else:
node.add_log(
action=NodeLog.MADE_PUBLIC,
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params={
'project': node.parent_id,
'node': node._primary_key,
},
log_date=timezone.now(),
should_hide=False,
)
return redirect(self.get_success_url())


Expand All @@ -892,10 +942,26 @@ def _remove_file_from_schema_response_blocks(registration, removed_file_id):

# delete file from registration and metadata and keep it for original project
if guid and (file := guid.referent) and (file.target == node) and not isinstance(file, TrashedFile):
file_id = file._id
file_path = getattr(file, 'materialized_path', None) or getattr(file, 'path', None) or ''
copied_from_id = getattr(file, 'copied_from_id', None) or getattr(getattr(file, 'copied_from', None), '_id', None)
with transaction.atomic():
file.delete()
_update_schema_meta(file.target)
_remove_file_from_schema_response_blocks(node, [file._id, file.copied_from._id])
_remove_file_from_schema_response_blocks(node, [file_id, copied_from_id])
node.add_log(
action=NodeLog.FILE_REMOVED,
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params={
'project': node.parent_id,
'node': node._primary_key,
'pathType': 'file',
'path': file_path,
},
log_date=timezone.now(),
should_hide=False,
)
return redirect(self.get_success_url())


Expand Down Expand Up @@ -932,7 +998,20 @@ def post(self, request, *args, **kwargs):
parent=osfstorage.get_root(),
name=osfstorage.archive_folder_name
).first()
file.copy_under(archive_folder)
copied = file.copy_under(archive_folder)
copied_path = getattr(copied, 'materialized_path', None) or getattr(copied, 'path', None) or ''
registration.add_log(
action=NodeLog.FILE_ADDED,
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params={
'project': registration.parent_id,
'node': registration._primary_key,
'path': copied_path,
},
log_date=timezone.now(),
should_hide=False,
)
messages.success(request, 'The file was successfully added.')
return redirect(self.get_success_url())

Expand All @@ -959,8 +1038,21 @@ def post(self, request, *args, **kwargs):
if not registration_file.exists():
messages.error(request, 'The file with the provided guid is not part of the registration.')
return redirect(self.get_success_url())

file_path = getattr(file, 'materialized_path', None) or getattr(file, 'path', None) or ''
registration_file.delete()
registration.add_log(
action=NodeLog.FILE_REMOVED,
auth=None,
foreign_user=NodeLog.SUPPORT_USER_LABEL,
params={
'project': registration.parent_id,
'node': registration._primary_key,
'pathType': 'file',
'path': file_path,
},
log_date=timezone.now(),
should_hide=False,
)
messages.success(request, 'The file was successfully removed.')
return redirect(self.get_success_url())

Expand Down
5 changes: 3 additions & 2 deletions admin_tests/nodes/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,11 @@ def test_do_not_remove_last_admin(self):
assert len(list(self.node.get_admin_contributors(self.node.contributors))) == 1
assert AdminLogEntry.objects.count() == count

def test_no_log(self):
def test_log(self):
assert not self.node.logs.filter(action=NodeLog.CONTRIB_REMOVED).exists()
view = setup_log_view(self.view(), self.request, guid=self.node._id, user_id=self.user_2.id)
view.post(self.request)
assert self.node.logs.latest().action != NodeLog.CONTRIB_REMOVED
assert self.node.logs.filter(action=NodeLog.CONTRIB_REMOVED).exists()

def test_no_user_permissions_raises_error(self):
guid = self.node._id
Expand Down
5 changes: 3 additions & 2 deletions admin_tests/preprints/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,15 +542,16 @@ def test_do_not_remove_last_admin(self):
assert len(list(self.preprint.get_admin_contributors(self.preprint.contributors))) == 1
assert AdminLogEntry.objects.count() == count

def test_no_log(self):
def test_log(self):
assert not self.preprint.logs.filter(action=PreprintLog.CONTRIB_REMOVED).exists()
view = setup_log_view(
self.view(),
self.request,
guid=self.preprint._id,
user_id=self.user_2.id
)
view.post(self.request)
assert self.preprint.logs.latest().action != PreprintLog.CONTRIB_REMOVED
assert self.preprint.logs.filter(action=PreprintLog.CONTRIB_REMOVED).exists()


@pytest.mark.urls('admin.base.urls')
Expand Down
2 changes: 2 additions & 0 deletions api/logs/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,13 @@ class NodeLogSerializer(JSONAPISerializer):
'id',
'date',
'action',
'foreign_user',
]

id = ser.CharField(read_only=True, source='_id')
date = VersionedDateTimeField(read_only=True)
action = ser.CharField(read_only=True)
foreign_user = ser.CharField(read_only=True)
params = ser.SerializerMethodField(read_only=True)
links = LinksField({'self': 'get_absolute_url'})

Expand Down
25 changes: 13 additions & 12 deletions osf/models/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,7 @@ def copy_unclaimed_records(self, resource):
contributor.save()

# TODO: optimize me
def update_contributor(self, user, permission, visible, auth, save=False, skip_permission=False):
def update_contributor(self, user, permission, visible, auth, save=False, skip_permission=False, log=True):
""" TODO: this method should be updated as a replacement for the main loop of
Node#manage_contributors. Right now there are redundancies, but to avoid major
feature creep this will not be included as this time.
Expand All @@ -1823,17 +1823,18 @@ def update_contributor(self, user, permission, visible, auth, save=False, skip_p
)
if not self.get_group(permission).user_set.filter(id=user.id).exists():
self.set_permissions(user, permission, save=False)
permissions_changed = {
user._id: permission
}
params = self.log_params
params['contributors'] = permissions_changed
self.add_log(
action=self.log_class.PERMISSIONS_UPDATED,
params=params,
auth=auth,
save=False
)
if log:
permissions_changed = {
user._id: permission
}
params = self.log_params
params['contributors'] = permissions_changed
self.add_log(
action=self.log_class.PERMISSIONS_UPDATED,
params=params,
auth=auth,
save=False
)
with transaction.atomic():
if [READ] in permissions_changed.values():
project_signals.write_permissions_revoked.send(self)
Expand Down
1 change: 1 addition & 0 deletions osf/models/nodelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class NodeLog(ObjectIDMixin, BaseModel):
}

DATE_FORMAT = '%m/%d/%Y %H:%M UTC'
SUPPORT_USER_LABEL = 'an OSF Support Team Member'

# Log action constants -- NOTE: templates stored in log_templates.mako
CREATED_FROM = 'created_from'
Expand Down
Loading