diff --git a/kobo/apps/audit_log/audit_actions.py b/kobo/apps/audit_log/audit_actions.py index 45c8f52426..9fabce4186 100644 --- a/kobo/apps/audit_log/audit_actions.py +++ b/kobo/apps/audit_log/audit_actions.py @@ -6,6 +6,7 @@ class AuditAction(models.TextChoices): ALLOW_ANONYMOUS_SUBMISSIONS = 'allow-anonymous-submissions' ARCHIVE = 'archive' AUTH = 'auth' + CLONE_PERMISSIONS = 'clone-permissions' CONNECT_PROJECT = 'connect-project' CREATE = 'create' DELETE = 'delete' diff --git a/kobo/apps/audit_log/models.py b/kobo/apps/audit_log/models.py index ddb6ccc848..34656d4f07 100644 --- a/kobo/apps/audit_log/models.py +++ b/kobo/apps/audit_log/models.py @@ -21,6 +21,7 @@ ACCESS_LOG_SUBMISSION_AUTH_TYPE, ACCESS_LOG_SUBMISSION_GROUP_AUTH_TYPE, ACCESS_LOG_UNKNOWN_AUTH_TYPE, + CLONE_ARG_NAME, PERM_ADD_SUBMISSIONS, PERM_VIEW_ASSET, PERM_VIEW_SUBMISSIONS, @@ -351,6 +352,7 @@ def create_from_request(cls, request): 'asset-permission-assignment-bulk-assignments': cls.create_from_permissions_request, # noqa 'asset-permission-assignment-detail': cls.create_from_permissions_request, 'asset-permission-assignment-list': cls.create_from_permissions_request, + 'asset-permission-assignment-clone': cls.handle_cloned_permissions, } url_name = request.resolver_match.url_name method = url_name_to_action.get(url_name, None) @@ -798,3 +800,23 @@ def handle_anonymous_user_permissions( action=AuditAction.MODIFY_USER_PERMISSIONS, ) ) + + @classmethod + def handle_cloned_permissions(cls, request): + initial_data = getattr(request, 'initial_data', None) + if initial_data is None: + return + asset_uid = request.resolver_match.kwargs['parent_lookup_asset'] + asset_id = initial_data['asset.id'] + ProjectHistoryLog.objects.create( + object_id=asset_id, + action=AuditAction.CLONE_PERMISSIONS, + user=request.user, + metadata={ + 'asset_uid': asset_uid, + 'log_subtype': PROJECT_HISTORY_LOG_PERMISSION_SUBTYPE, + 'ip_address': get_client_ip(request), + 'source': get_human_readable_client_user_agent(request), + 'cloned_from': request._data[CLONE_ARG_NAME], + }, + ) diff --git a/kobo/apps/audit_log/tests/test_project_history_logs.py b/kobo/apps/audit_log/tests/test_project_history_logs.py index bbb6f88bd6..c35d5ab55d 100644 --- a/kobo/apps/audit_log/tests/test_project_history_logs.py +++ b/kobo/apps/audit_log/tests/test_project_history_logs.py @@ -17,6 +17,7 @@ from kobo.apps.hook.models import Hook from kobo.apps.kobo_auth.shortcuts import User from kpi.constants import ( + CLONE_ARG_NAME, PERM_ADD_SUBMISSIONS, PERM_CHANGE_SUBMISSIONS, PERM_PARTIAL_SUBMISSIONS, @@ -1287,3 +1288,17 @@ def test_no_logs_if_bulk_request_fails(self): format='json', ) self.assertEqual(ProjectHistoryLog.objects.count(), 0) + + def test_clone_permissions_creates_logs(self): + second_asset = Asset.objects.get(pk=2) + log_metadata = self._base_project_history_log_test( + method=self.client.patch, + url=reverse( + 'api_v2:asset-permission-assignment-clone', + kwargs={'parent_lookup_asset': self.asset.uid}, + ), + request_data={CLONE_ARG_NAME: second_asset.uid}, + expected_action=AuditAction.CLONE_PERMISSIONS, + expected_subtype=PROJECT_HISTORY_LOG_PERMISSION_SUBTYPE, + ) + self.assertEqual(log_metadata['cloned_from'], second_asset.uid) diff --git a/kpi/views/v2/asset_permission_assignment.py b/kpi/views/v2/asset_permission_assignment.py index e9f8b756bb..b5ed0d9a5e 100644 --- a/kpi/views/v2/asset_permission_assignment.py +++ b/kpi/views/v2/asset_permission_assignment.py @@ -199,6 +199,7 @@ def clone(self, request, *args, **kwargs): source_asset_uid = self.request.data[CLONE_ARG_NAME] source_asset = get_object_or_404(Asset, uid=source_asset_uid) user = request.user + request._request.initial_data = {'asset.id': self.asset.id} if user.has_perm(PERM_MANAGE_ASSET, self.asset) and user.has_perm( PERM_VIEW_ASSET, source_asset