From 8d55040e9e2dc61ad3fc0d965ea333c8a8cfcc95 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Thu, 24 May 2018 22:39:32 +0200
Subject: [PATCH] See #230: users with upload permission can now launch import
 and manage their own imports

---
 api/funkwhale_api/music/views.py         | 24 +++++++++++++++--
 api/tests/music/test_views.py            | 34 ++++++++++++++++++++----
 front/src/components/Sidebar.vue         |  9 ++++++-
 front/src/components/library/Library.vue | 10 ++++---
 4 files changed, 66 insertions(+), 11 deletions(-)

diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py
index aa07ad52..d8b173b4 100644
--- a/api/funkwhale_api/music/views.py
+++ b/api/funkwhale_api/music/views.py
@@ -91,12 +91,21 @@ class ImportBatchViewSet(
     )
     serializer_class = serializers.ImportBatchSerializer
     permission_classes = (HasUserPermission,)
-    required_permissions = ['library']
+    required_permissions = ['library', 'upload']
+    permission_operator = 'or'
     filter_class = filters.ImportBatchFilter
 
     def perform_create(self, serializer):
         serializer.save(submitted_by=self.request.user)
 
+    def get_queryset(self):
+        qs = super().get_queryset()
+        # if user do not have library permission, we limit to their
+        # own jobs
+        if not self.request.user.has_permissions('library'):
+            qs = qs.filter(submitted_by=self.request.user)
+        return qs
+
 
 class ImportJobViewSet(
         mixins.CreateModelMixin,
@@ -105,11 +114,22 @@ class ImportJobViewSet(
     queryset = (models.ImportJob.objects.all().select_related())
     serializer_class = serializers.ImportJobSerializer
     permission_classes = (HasUserPermission,)
-    required_permissions = ['library']
+    required_permissions = ['library', 'upload']
+    permission_operator = 'or'
     filter_class = filters.ImportJobFilter
 
+    def get_queryset(self):
+        qs = super().get_queryset()
+        # if user do not have library permission, we limit to their
+        # own jobs
+        if not self.request.user.has_permissions('library'):
+            qs = qs.filter(batch__submitted_by=self.request.user)
+        return qs
+
     @list_route(methods=['get'])
     def stats(self, request, *args, **kwargs):
+        if not request.user.has_permissions('library'):
+            return Response(status=403)
         qs = models.ImportJob.objects.all()
         filterset = filters.ImportJobFilter(request.GET, queryset=qs)
         qs = filterset.qs
diff --git a/api/tests/music/test_views.py b/api/tests/music/test_views.py
index 9328ba32..1fe11383 100644
--- a/api/tests/music/test_views.py
+++ b/api/tests/music/test_views.py
@@ -9,12 +9,12 @@ from funkwhale_api.music import views
 from funkwhale_api.federation import actors
 
 
-@pytest.mark.parametrize('view,permissions', [
-    (views.ImportBatchViewSet, ['library']),
-    (views.ImportJobViewSet, ['library']),
+@pytest.mark.parametrize('view,permissions,operator', [
+    (views.ImportBatchViewSet, ['library', 'upload'], 'or'),
+    (views.ImportJobViewSet, ['library', 'upload'], 'or'),
 ])
-def test_permissions(assert_user_permission, view, permissions):
-    assert_user_permission(view, permissions)
+def test_permissions(assert_user_permission, view, permissions, operator):
+    assert_user_permission(view, permissions, operator)
 
 
 def test_artist_list_serializer(api_request, factories, logged_in_api_client):
@@ -351,3 +351,27 @@ def test_import_batch_and_job_run_via_api(
 
     run.assert_any_call(import_job_id=job1.pk)
     run.assert_any_call(import_job_id=job2.pk)
+
+
+def test_import_job_viewset_get_queryset_upload_filters_user(
+        factories, logged_in_api_client):
+    logged_in_api_client.user.permission_upload = True
+    logged_in_api_client.user.save()
+
+    job = factories['music.ImportJob']()
+    url = reverse('api:v1:import-jobs-list')
+    response = logged_in_api_client.get(url)
+
+    assert response.data['count'] == 0
+
+
+def test_import_batch_viewset_get_queryset_upload_filters_user(
+        factories, logged_in_api_client):
+    logged_in_api_client.user.permission_upload = True
+    logged_in_api_client.user.save()
+
+    job = factories['music.ImportBatch']()
+    url = reverse('api:v1:import-batches-list')
+    response = logged_in_api_client.get(url)
+
+    assert response.data['count'] == 0
diff --git a/front/src/components/Sidebar.vue b/front/src/components/Sidebar.vue
index 9f3134c2..e8f330c3 100644
--- a/front/src/components/Sidebar.vue
+++ b/front/src/components/Sidebar.vue
@@ -68,6 +68,12 @@
                 :title="$t('Pending import requests')">
                 {{ notifications.importRequests }}</div>
             </router-link>
+            <router-link
+              class="item"
+              v-else-if="$store.state.auth.availablePermissions['upload']"
+              to="/library/import/launch">
+              <i class="download icon"></i>{{ $t('Import music') }}
+            </router-link>
             <router-link
               class="item"
               v-if="$store.state.auth.availablePermissions['federation']"
@@ -193,7 +199,8 @@ export default {
     showAdmin () {
       let adminPermissions = [
         this.$store.state.auth.availablePermissions['federation'],
-        this.$store.state.auth.availablePermissions['library']
+        this.$store.state.auth.availablePermissions['library'],
+        this.$store.state.auth.availablePermissions['upload']
       ]
       return adminPermissions.filter(e => {
         return e
diff --git a/front/src/components/library/Library.vue b/front/src/components/library/Library.vue
index e360ccb1..50337b22 100644
--- a/front/src/components/library/Library.vue
+++ b/front/src/components/library/Library.vue
@@ -13,10 +13,10 @@
           exact>
           <i18next path="Requests"/>
         </router-link>
-        <router-link v-if="$store.state.auth.availablePermissions['library']" class="ui item" to="/library/import/launch" exact>
+        <router-link v-if="showImports" class="ui item" to="/library/import/launch" exact>
           <i18next path="Import"/>
         </router-link>
-        <router-link v-if="$store.state.auth.availablePermissions['library']" class="ui item" to="/library/import/batches">
+        <router-link v-if="showImports" class="ui item" to="/library/import/batches">
           <i18next path="Import batches"/>
         </router-link>
       </div>
@@ -27,7 +27,11 @@
 
 <script>
 export default {
-  name: 'library'
+  computed: {
+    showImports () {
+      return this.$store.state.auth.availablePermissions['upload'] || this.$store.state.auth.availablePermissions['library']
+    }
+  }
 }
 </script>
 
-- 
GitLab