Skip to content

Commit 60b5d25

Browse files
committed
expose foreign_user in logs response; add missing logs for admin actions
1 parent a1aa899 commit 60b5d25

4 files changed

Lines changed: 147 additions & 44 deletions

File tree

admin/nodes/views.py

Lines changed: 131 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ def post(self, request, *args, **kwargs):
112112

113113
node.add_log(
114114
action=NodeLog.REGISTRATION_DATE_UPDATED,
115-
auth=request,
115+
auth=None,
116+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
116117
params={
117118
'last_date': str(last_date),
118119
'new_date': str(new_date)
@@ -225,22 +226,20 @@ def post(self, request, *args, **kwargs):
225226
message=f'User {user.pk} removed from {node.__class__.__name__.lower()} {node.pk}.',
226227
action_flag=CONTRIBUTOR_REMOVED
227228
)
228-
# Log invisibly on the OSF.
229-
self.add_contributor_removed_log(node, user)
230-
return redirect(self.get_success_url())
229+
node.add_log(
230+
action=NodeLog.CONTRIB_REMOVED,
231+
auth=None,
232+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
233+
params={
234+
'project': node.parent_id,
235+
'node': node.pk,
236+
'contributors': user.pk
237+
},
238+
log_date=timezone.now(),
239+
should_hide=False,
240+
)
231241

232-
def add_contributor_removed_log(self, node, user):
233-
NodeLog(
234-
action=NodeLog.CONTRIB_REMOVED,
235-
user=None,
236-
params={
237-
'project': node.parent_id,
238-
'node': node.pk,
239-
'contributors': user.pk
240-
},
241-
date=timezone.now(),
242-
should_hide=True,
243-
).save()
242+
return redirect(self.get_success_url())
244243

245244

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

268267
new_permission_indexes_to_remove = []
268+
added_contributor_ids = []
269269
for email, permission in zip(new_emails_to_add, new_permissions_to_add):
270270
contributor_user = OSFUser.objects.filter(emails__address=email.lower()).first()
271271
if not contributor_user:
@@ -281,8 +281,10 @@ def post(self, request, *args, **kwargs):
281281
auth=request,
282282
user_id=contributor_user._id,
283283
permissions=permission,
284-
notification_type=None
284+
notification_type=None,
285+
log=False,
285286
)
287+
added_contributor_ids.append(contributor_user._id)
286288
messages.success(self.request, f'User with email {email} was successfully added.')
287289

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

296+
# Log support-added contributors, if any
297+
if added_contributor_ids:
298+
params = resource.log_params
299+
params['contributors'] = added_contributor_ids
300+
resource.add_log(
301+
action=NodeLog.CONTRIB_ADDED,
302+
auth=None,
303+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
304+
params=params,
305+
log_date=timezone.now(),
306+
should_hide=False,
307+
)
308+
294309
updated_permissions = data.get('updated-permissions', [])
295310
all_permissions = updated_permissions + new_permissions_to_add
296311
has_admin = list(filter(lambda permission: ADMIN in permission, all_permissions))
297312
if not has_admin:
298313
messages.error(self.request, 'Must be at least one admin on this node.')
299314
return redirect(self.get_success_url())
300315

316+
permissions_changed = {}
301317
for contributor_permission in updated_permissions:
302318
guid, permission = contributor_permission.split('-')
303319
user = OSFUser.load(guid)
@@ -307,7 +323,21 @@ def post(self, request, *args, **kwargs):
307323
resource.get_visible(user),
308324
request,
309325
save=True,
310-
skip_permission=True
326+
skip_permission=True,
327+
log=False,
328+
)
329+
permissions_changed[user._id] = permission
330+
331+
if permissions_changed:
332+
params = resource.log_params
333+
params['contributors'] = permissions_changed
334+
resource.add_log(
335+
action=NodeLog.PERMISSIONS_UPDATED,
336+
auth=None,
337+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
338+
params=params,
339+
log_date=timezone.now(),
340+
should_hide=False,
311341
)
312342

313343
return redirect(self.get_success_url())
@@ -332,15 +362,16 @@ def post(self, request, *args, **kwargs):
332362
message=f'Node {node.pk} restored.',
333363
action_flag=NODE_RESTORED
334364
)
335-
NodeLog(
365+
node.add_log(
336366
action=NodeLog.NODE_CREATED,
337-
user=None,
367+
auth=None,
368+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
338369
params={
339370
'project': node.parent_id,
340371
},
341-
date=timezone.now(),
342-
should_hide=True,
343-
).save()
372+
log_date=timezone.now(),
373+
should_hide=False,
374+
)
344375
else:
345376
node.is_deleted = True
346377
node.deleted = timezone.now()
@@ -352,15 +383,17 @@ def post(self, request, *args, **kwargs):
352383
message=f'Node {node.pk} removed.',
353384
action_flag=NODE_REMOVED
354385
)
355-
NodeLog(
386+
node.add_log(
356387
action=NodeLog.NODE_REMOVED,
357-
user=None,
388+
auth=None,
389+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
358390
params={
359391
'project': node.parent_id,
392+
'node': node.pk,
360393
},
361-
date=timezone.now(),
362-
should_hide=True,
363-
).save()
394+
log_date=timezone.now(),
395+
should_hide=False,
396+
)
364397
node.save()
365398

366399
return redirect(self.get_success_url())
@@ -852,6 +885,18 @@ def post(self, request, *args, **kwargs):
852885

853886
node.save()
854887

888+
node.add_log(
889+
action=NodeLog.MADE_PRIVATE,
890+
auth=None,
891+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
892+
params={
893+
'project': node.parent_id,
894+
'node': node._primary_key,
895+
},
896+
log_date=timezone.now(),
897+
should_hide=False,
898+
)
899+
855900
return redirect(self.get_success_url())
856901

857902

@@ -863,9 +908,21 @@ class NodeMakePublic(NodeMixin, View):
863908
def post(self, request, *args, **kwargs):
864909
node = self.get_object()
865910
try:
866-
node.set_privacy('public')
911+
node.set_privacy('public', auth=None, log=False)
867912
except NodeStateError as e:
868913
messages.error(request, str(e))
914+
else:
915+
node.add_log(
916+
action=NodeLog.MADE_PUBLIC,
917+
auth=None,
918+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
919+
params={
920+
'project': node.parent_id,
921+
'node': node._primary_key,
922+
},
923+
log_date=timezone.now(),
924+
should_hide=False,
925+
)
869926
return redirect(self.get_success_url())
870927

871928

@@ -892,10 +949,26 @@ def _remove_file_from_schema_response_blocks(registration, removed_file_id):
892949

893950
# delete file from registration and metadata and keep it for original project
894951
if guid and (file := guid.referent) and (file.target == node) and not isinstance(file, TrashedFile):
952+
file_id = file._id
953+
file_path = getattr(file, 'materialized_path', None) or getattr(file, 'path', None) or ''
954+
copied_from_id = getattr(file, 'copied_from_id', None) or getattr(getattr(file, 'copied_from', None), '_id', None)
895955
with transaction.atomic():
896956
file.delete()
897957
_update_schema_meta(file.target)
898-
_remove_file_from_schema_response_blocks(node, [file._id, file.copied_from._id])
958+
_remove_file_from_schema_response_blocks(node, [file_id, copied_from_id])
959+
node.add_log(
960+
action=NodeLog.FILE_REMOVED,
961+
auth=None,
962+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
963+
params={
964+
'project': node.parent_id,
965+
'node': node._primary_key,
966+
'pathType': 'file',
967+
'path': file_path,
968+
},
969+
log_date=timezone.now(),
970+
should_hide=False,
971+
)
899972
return redirect(self.get_success_url())
900973

901974

@@ -932,7 +1005,20 @@ def post(self, request, *args, **kwargs):
9321005
parent=osfstorage.get_root(),
9331006
name=osfstorage.archive_folder_name
9341007
).first()
935-
file.copy_under(archive_folder)
1008+
copied = file.copy_under(archive_folder)
1009+
copied_path = getattr(copied, 'materialized_path', None) or getattr(copied, 'path', None) or ''
1010+
registration.add_log(
1011+
action=NodeLog.FILE_ADDED,
1012+
auth=None,
1013+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
1014+
params={
1015+
'project': registration.parent_id,
1016+
'node': registration._primary_key,
1017+
'path': copied_path,
1018+
},
1019+
log_date=timezone.now(),
1020+
should_hide=False,
1021+
)
9361022
messages.success(request, 'The file was successfully added.')
9371023
return redirect(self.get_success_url())
9381024

@@ -959,8 +1045,21 @@ def post(self, request, *args, **kwargs):
9591045
if not registration_file.exists():
9601046
messages.error(request, 'The file with the provided guid is not part of the registration.')
9611047
return redirect(self.get_success_url())
962-
1048+
file_path = getattr(file, 'materialized_path', None) or getattr(file, 'path', None) or ''
9631049
registration_file.delete()
1050+
registration.add_log(
1051+
action=NodeLog.FILE_REMOVED,
1052+
auth=None,
1053+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
1054+
params={
1055+
'project': registration.parent_id,
1056+
'node': registration._primary_key,
1057+
'pathType': 'file',
1058+
'path': file_path,
1059+
},
1060+
log_date=timezone.now(),
1061+
should_hide=False,
1062+
)
9641063
messages.success(request, 'The file was successfully removed.')
9651064
return redirect(self.get_success_url())
9661065

api/logs/serializers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,13 @@ class NodeLogSerializer(JSONAPISerializer):
203203
'id',
204204
'date',
205205
'action',
206+
'foreign_user',
206207
]
207208

208209
id = ser.CharField(read_only=True, source='_id')
209210
date = VersionedDateTimeField(read_only=True)
210211
action = ser.CharField(read_only=True)
212+
foreign_user = ser.SerializerMethodField(read_only=True)
211213
params = ser.SerializerMethodField(read_only=True)
212214
links = LinksField({'self': 'get_absolute_url'})
213215

osf/models/mixins.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,7 +1796,7 @@ def copy_unclaimed_records(self, resource):
17961796
contributor.save()
17971797

17981798
# TODO: optimize me
1799-
def update_contributor(self, user, permission, visible, auth, save=False, skip_permission=False):
1799+
def update_contributor(self, user, permission, visible, auth, save=False, skip_permission=False, log=True):
18001800
""" TODO: this method should be updated as a replacement for the main loop of
18011801
Node#manage_contributors. Right now there are redundancies, but to avoid major
18021802
feature creep this will not be included as this time.
@@ -1823,17 +1823,18 @@ def update_contributor(self, user, permission, visible, auth, save=False, skip_p
18231823
)
18241824
if not self.get_group(permission).user_set.filter(id=user.id).exists():
18251825
self.set_permissions(user, permission, save=False)
1826-
permissions_changed = {
1827-
user._id: permission
1828-
}
1829-
params = self.log_params
1830-
params['contributors'] = permissions_changed
1831-
self.add_log(
1832-
action=self.log_class.PERMISSIONS_UPDATED,
1833-
params=params,
1834-
auth=auth,
1835-
save=False
1836-
)
1826+
if log:
1827+
permissions_changed = {
1828+
user._id: permission
1829+
}
1830+
params = self.log_params
1831+
params['contributors'] = permissions_changed
1832+
self.add_log(
1833+
action=self.log_class.PERMISSIONS_UPDATED,
1834+
params=params,
1835+
auth=auth,
1836+
save=False
1837+
)
18371838
with transaction.atomic():
18381839
if [READ] in permissions_changed.values():
18391840
project_signals.write_permissions_revoked.send(self)

osf/models/nodelog.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class NodeLog(ObjectIDMixin, BaseModel):
1616
}
1717

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

2021
# Log action constants -- NOTE: templates stored in log_templates.mako
2122
CREATED_FROM = 'created_from'

0 commit comments

Comments
 (0)