diff --git a/arkindex/ponos/api.py b/arkindex/ponos/api.py index 5f6e1833f4800f7c7301fb9a8e3fc2fb136bba91..8f6176a8c13cd8adcef47342f0fc44d5331216ce 100644 --- a/arkindex/ponos/api.py +++ b/arkindex/ponos/api.py @@ -321,7 +321,11 @@ class TaskArtifacts(ListCreateAPIView): pagination_class = None def get_task(self): - task = get_object_or_404(Task, pk=self.kwargs["pk"]) + task = get_object_or_404( + # Select the required tables for permissions checking + Task.objects.select_related('workflow__process__corpus', 'workflow__process__revision'), + pk=self.kwargs["pk"], + ) self.check_object_permissions(self.request, task) return task diff --git a/arkindex/ponos/permissions.py b/arkindex/ponos/permissions.py index d059cd2d37a640010a9f2587b6b78c88746fbbc6..4fdb86dbf18908ae78f5a7f3ab1fd8a383bd8513 100644 --- a/arkindex/ponos/permissions.py +++ b/arkindex/ponos/permissions.py @@ -1,7 +1,8 @@ from rest_framework.permissions import SAFE_METHODS from arkindex.ponos.models import Task -from arkindex.project.mixins import CorpusACLMixin +from arkindex.process.models import Process +from arkindex.project.mixins import CorpusACLMixin, ProcessACLMixin from arkindex.project.permissions import IsAuthenticated from arkindex.users.models import Role @@ -75,7 +76,7 @@ class IsAgentOrTaskAdmin(CorpusACLMixin, IsAuthenticated): ) -class IsAgentOrTaskAdminOrReadOnly(CorpusACLMixin, IsAuthenticated): +class IsAgentOrTaskAdminOrReadOnly(ProcessACLMixin, IsAuthenticated): """ Instance admins, agents, process admins, and the task itself are always allowed. For GET/HEAD, only a Guest level on the process is required for regular users. @@ -92,7 +93,11 @@ class IsAgentOrTaskAdminOrReadOnly(CorpusACLMixin, IsAuthenticated): # Add request to attributes for the ACL mixin to work with self.user self.request = request - level = self.process_access_level(task.workflow.process) + try: + level = self.process_access_level(task.workflow.process) + except Process.DoesNotExist: + # Reject if the task has no process + return False # Require *some* access to the process if level is None: diff --git a/arkindex/ponos/tests/test_api.py b/arkindex/ponos/tests/test_api.py index cc69589586b62d0d1a1300cb2d4a853d01f1728e..71ab20e305ce45ee99d644661e3656682dceb3b0 100644 --- a/arkindex/ponos/tests/test_api.py +++ b/arkindex/ponos/tests/test_api.py @@ -16,15 +16,14 @@ from django.test import override_settings from django.urls import reverse from django.utils import timezone from rest_framework import status -from rest_framework.test import APITestCase from arkindex.documents.models import Corpus from arkindex.ponos.api import timezone as api_tz from arkindex.ponos.authentication import AgentUser from arkindex.ponos.models import FINAL_STATES, GPU, Agent, Farm, Secret, State, Task, Workflow, encrypt from arkindex.process.models import Process, ProcessMode +from arkindex.project.tests import FixtureAPITestCase from arkindex.project.tools import build_public_key -from arkindex.users.models import User from rest_framework_simplejwt.tokens import AccessToken, RefreshToken RECIPE = """ @@ -51,11 +50,12 @@ def str_date(d): @override_settings(PONOS_LOG_TAIL=42) -class TestAPI(APITestCase): +class TestAPI(FixtureAPITestCase): + @classmethod def setUpTestData(cls): super().setUpTestData() - cls.farm = Farm.objects.create(name="Wheat farm") + cls.farm = Farm.objects.get() pubkey = build_public_key() cls.agent = AgentUser.objects.create( id=uuid.UUID(hashlib.md5(pubkey.encode("utf-8")).hexdigest()), @@ -82,8 +82,6 @@ class TestAPI(APITestCase): index=1, ram_total=8 * 1024 * 1024 * 1024, ) - cls.superuser = User.objects.create_superuser('root@root.root', 'root') - cls.user = User.objects.create_user('user@user.lol', 'hunter2') def _build_workflow_response(self, response, **kwargs): """ @@ -1213,8 +1211,7 @@ class TestAPI(APITestCase): self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED) def test_agent_actions_no_user(self): - user = User.objects.create_superuser("root@root.fr", "hunter2", display_name='root') - self.client.force_login(user) + self.client.force_login(self.superuser) try: resp = self.client.get(reverse("api:agent-actions")) self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED) @@ -1387,6 +1384,37 @@ class TestAPI(APITestCase): ], ) + def test_list_artifacts_process_creator(self): + Process.objects.create( + creator=self.user, + corpus=self.corpus, + mode=ProcessMode.Workers, + workflow=self.workflow, + ) + artifact = self.task1.artifacts.create( + path="demo.txt", + content_type="text/plain", + size=12, + ) + self.client.force_login(self.user) + + with self.assertNumQueries(7): + response = self.client.get(reverse('api:task-artifacts', kwargs={'pk': self.task1.id})) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + self.assertListEqual(response.json(), [ + { + 'id': str(artifact.id), + 'created': artifact.created.isoformat().replace('+00:00', 'Z'), + 'updated': artifact.updated.isoformat().replace('+00:00', 'Z'), + 'content_type': 'text/plain', + 'path': 'demo.txt', + 'size': 12, + 's3_put_url': None, + 'url': f'http://testserver/api/v1/task/{self.task1.id}/artifact/demo.txt', + } + ]) + @override_settings(PONOS_ARTIFACT_MAX_SIZE=99999) def test_artifact_creation(self):