From bfcd921fad2cbf193aa4de333d9ef3cad6a7a176 Mon Sep 17 00:00:00 2001 From: Mayo Faulkner Date: Wed, 20 Mar 2024 15:42:07 +0000 Subject: [PATCH] add view to check protected datasets --- alyx/data/tests_rest.py | 31 ++++++++++++++++++ alyx/data/urls.py | 6 ++++ alyx/data/views.py | 71 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/alyx/data/tests_rest.py b/alyx/data/tests_rest.py index 305de8ae0..794495772 100644 --- a/alyx/data/tests_rest.py +++ b/alyx/data/tests_rest.py @@ -761,6 +761,37 @@ def test_protected_view(self): self.assertEqual(name, 'test_prot/a.b.e1') self.assertEqual(prot_info, []) + def test_check_protected(self): + self.post(reverse('datarepository-list'), {'name': 'drb1', 'hostname': 'hostb1'}) + self.post(reverse('lab-list'), {'name': 'labb', 'repositories': ['drb1']}) + + # Create protected tag + self.client.post(reverse('tag-list'), {'name': 'tag1', 'protected': True}) + + # Create some datasets and register + data = {'path': '%s/2018-01-01/002/' % self.subject, + 'filenames': 'test_prot/a.c.e2', + 'name': 'drb1', # this is the repository name + } + + d = self.client.post(reverse('register-file'), data) + + # Check the same dataset to see if it is protected, should be unprotected + # and get a status 200 respons + _ = data.pop('name') + r = self.client.post(reverse('check-protected'), data) + self.assertEqual(r['status'], 200) + + # add protected tag to the first dataset + dataset1 = Dataset.objects.get(pk=d[0]['id']) + tag1 = Tag.objects.get(name='tag1') + dataset1.tags.add(tag1) + + # Check the same dataset to see if it is protected + r = self.client.post(reverse('check-protected'), data) + self.assertEqual(r['status'], 403) + self.assertEqual(r['error'], 'One or more datasets is protected') + def test_revisions(self): # Check revision lookup with name self.post(reverse('revision-list'), {'name': 'v2'}) diff --git a/alyx/data/urls.py b/alyx/data/urls.py index b67ce0a6c..9218ff3f6 100644 --- a/alyx/data/urls.py +++ b/alyx/data/urls.py @@ -14,6 +14,9 @@ 'post': 'create' }) +check_protected = dv.ProtectedFileViewSet.as_view({ + 'get': 'list' +}) urlpatterns = [ path('data-formats', dv.DataFormatList.as_view(), @@ -78,4 +81,7 @@ path('sync-file-status', sync_file_status, name="sync-file-status"), + path('check-protected', check_protected, + name="check-protected"), + ] diff --git a/alyx/data/views.py b/alyx/data/views.py index ce76021f0..869e9dd87 100644 --- a/alyx/data/views.py +++ b/alyx/data/views.py @@ -327,6 +327,77 @@ def _parse_path(path): return subject, date, session_number +class ProtectedFileViewSet(mixins.ListModelMixin, + viewsets.GenericViewSet): + + serializer_class = serializers.Serializer + + def list(self, request): + """ + Endpoint to check if set of files are protected or not + + The session is retrieved by the ALF convention in the relative path, so this field has to + match the format Subject/Date/Number as shown below. + + The client side REST query should look like this: + + ```python + r_ = {'created_by': 'user_name_alyx', + 'path': 'ZM_1085/2019-02-12/002/alf', # relative path to repo path + 'filenames': ['file1', 'file2'], + } + ``` + + Returns a response indicating if any of the datasets are protected or not + - Status 403 if a dataset is protected, details contains a list of protected datasets + - Status 200 is none of the datasets are protected + """ + + user = request.data.get('created_by', None) + if user: + user = get_user_model().objects.get(username=user) + else: + user = request.user + + rel_dir_path = request.data.get('path', '') + if not rel_dir_path: + raise ValueError("The path argument is required.") + + # Extract the data repository from the hostname, the subject, the directory path. + rel_dir_path = rel_dir_path.replace('\\', '/') + rel_dir_path = rel_dir_path.replace('//', '/') + subject, date, session_number = _parse_path(rel_dir_path) + + filenames = request.data.get('filenames', ()) + if isinstance(filenames, str): + filenames = filenames.split(',') + + session = _get_session( + subject=subject, date=date, number=session_number, user=user) + assert session + + # Loop through the files to see if any are protected + prot_response = [] + protected = [] + for file in filenames: + info, resp = _get_name_collection_revision(file, rel_dir_path) + if resp: + return resp + prot, prot_info = _check_dataset_protected( + session, info['collection'], info['filename']) + protected.append(prot) + prot_response.append({file: prot_info}) + if any(protected): + data = {'status_code': 403, + 'error': 'One or more datasets is protected', + 'details': prot_response} + return Response(data=data) + else: + data = {'status_code': 200, + 'details': 'None of the datasets are protected'} + return Response(data=data) + + class RegisterFileViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):