From f60ba4072d7b9d737a02516e4bce5436461cf547 Mon Sep 17 00:00:00 2001
From: Eva Bardou <ebardou@teklia.com>
Date: Tue, 12 Jan 2021 14:34:59 +0000
Subject: [PATCH] Check user permissions before he can access TaskUpdate ponos
 endpoint

---
 arkindex/project/openapi/patch.yml        | 20 +++++++++++++--
 arkindex/project/tests/test_ponos_view.py | 30 ++++++++++++++++++++++-
 arkindex/project/urls.py                  |  2 ++
 arkindex/project/views.py                 | 14 ++++++++++-
 4 files changed, 62 insertions(+), 4 deletions(-)

diff --git a/arkindex/project/openapi/patch.yml b/arkindex/project/openapi/patch.yml
index f00115c81c..53db57d3d6 100644
--- a/arkindex/project/openapi/patch.yml
+++ b/arkindex/project/openapi/patch.yml
@@ -368,19 +368,35 @@ paths:
                 aTgNWsA3WFlqjSIEGz+PAVHSNMobBaJm
                 -----END PUBLIC KEY-----
   /ponos/v1/task/{id}/:
+    put:
+      description: Update a task, allowing humans to change the task's state
+      operationId: UpdateTask
+      security: []
+      tags:
+        - ponos
+    patch:
+      description: Partially update a task, allowing humans to change the task's state
+      operationId: PartialUpdateTask
+      security: []
+      tags:
+        - ponos
+  /ponos/v1/task/{id}/from-agent/:
     get:
       description: Retrieve a Ponos task status
+      operationId: RetrieveTaskFromAgent
       security: []
       tags:
         - ponos
     put:
-      description: Update a task
+      description: Update a task, from an agent
+      operationId: UpdateTaskFromAgent
       security:
         - agentAuth: []
       tags:
         - ponos
     patch:
-      description: Partially update a task
+      description: Partially update a task, from an agent
+      operationId: PartialUpdateTaskFromAgent
       security:
         - agentAuth: []
       tags:
diff --git a/arkindex/project/tests/test_ponos_view.py b/arkindex/project/tests/test_ponos_view.py
index 326f6ba6e0..1fd6887090 100644
--- a/arkindex/project/tests/test_ponos_view.py
+++ b/arkindex/project/tests/test_ponos_view.py
@@ -5,8 +5,9 @@ from django.test import TestCase, override_settings
 from django.urls import reverse
 from rest_framework import status
 
+from arkindex.dataimport.models import DataImport, DataImportMode
 from arkindex.users.models import User
-from ponos.models import Agent, Farm, Secret, encrypt
+from ponos.models import Agent, Farm, Secret, Workflow, encrypt
 
 
 @override_settings(PONOS_PRIVATE_KEY='staging')
@@ -78,3 +79,30 @@ class TestPonosView(TestCase):
         self.client.force_login(User.objects.create())
         response = self.client.get(reverse('ponos-agent-details', kwargs={'pk': str(agent.id)}))
         self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+    def test_update_task(self):
+        """
+        Only admin users or the task creator should have the ability to update a task
+        """
+        creator = User.objects.create(email="creator@user.fr")
+        workflow = Workflow.objects.create(
+            recipe="tasks: {test: {image: alpine}}"
+        )
+        workflow.start()
+        task = workflow.tasks.get()
+        DataImport.objects.create(mode=DataImportMode.Repository, creator=creator, workflow=workflow)
+
+        response = self.client.patch(reverse('ponos-task-update', kwargs={'pk': str(task.id)}), json={"state": "stopping"})
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+        self.client.force_login(User.objects.create(email="lambda@user.fr"))
+        response = self.client.patch(reverse('ponos-task-update', kwargs={'pk': str(task.id)}), json={"state": "stopping"})
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+        self.client.force_login(User.objects.create(email='admin@admin.fr', is_admin=True))
+        response = self.client.patch(reverse('ponos-task-update', kwargs={'pk': str(task.id)}), json={"state": "stopping"})
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+        self.client.force_login(creator)
+        response = self.client.patch(reverse('ponos-task-update', kwargs={'pk': str(task.id)}), json={"state": "stopping"})
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
diff --git a/arkindex/project/urls.py b/arkindex/project/urls.py
index 02f199ae58..b6c81bde83 100644
--- a/arkindex/project/urls.py
+++ b/arkindex/project/urls.py
@@ -11,6 +11,7 @@ from arkindex.project.views import (
     PonosAgentDetails,
     PonosAgentsState,
     PonosSecretDetails,
+    PonosTaskUpdate,
 )
 
 # Fallback to the dummy frontend view when CDN_ASSETS_URL is not set
@@ -23,6 +24,7 @@ urlpatterns = [
     path('ponos/v1/secret/<path:name>', PonosSecretDetails.as_view(), name='secret-details'),
     path('ponos/v1/agents/', PonosAgentsState.as_view(), name='ponos-agents'),
     path('ponos/v1/agent/<uuid:pk>/', PonosAgentDetails.as_view(), name='ponos-agent-details'),
+    path('ponos/v1/task/<uuid:pk>/', PonosTaskUpdate.as_view(), name='ponos-task-update'),
     path('ponos/', include('ponos.urls')),
     path('admin/', admin.site.urls),
     path('rq/', include('django_rq.urls')),
diff --git a/arkindex/project/views.py b/arkindex/project/views.py
index 06fc7f2141..cc7473e347 100644
--- a/arkindex/project/views.py
+++ b/arkindex/project/views.py
@@ -4,7 +4,7 @@ from rest_framework import permissions
 
 from arkindex.project.mixins import CachedViewMixin
 from arkindex.project.permissions import IsVerified
-from ponos.api import AgentDetails, AgentsState, SecretDetails
+from ponos.api import AgentDetails, AgentsState, SecretDetails, TaskUpdate
 
 
 class FrontendView(View):
@@ -66,3 +66,15 @@ class PonosAgentDetails(AgentDetails):
     Allow any verified user to see the details of an agent including all its running tasks
     """
     permission_classes = (IsVerified, )
+
+
+class PonosTaskUpdate(TaskUpdate):
+    """
+    Allow any super admin or the task creator to update a task.
+    """
+    class IsAdminOrCreator(permissions.BasePermission):
+
+        def has_object_permission(self, request, view, obj):
+            return request.user.is_authenticated and (request.user.is_admin or request.user == obj.workflow.dataimport.creator)
+
+    permission_classes = (IsAdminOrCreator, )
-- 
GitLab