From 3ef5f29dd39a9e9c60dc022238f9c84f9e1f781c Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Tue, 19 Jun 2018 18:48:43 +0200
Subject: [PATCH] See #212: API viewset

---
 api/funkwhale_api/manage/filters.py     | 19 +++++++++++++++
 api/funkwhale_api/manage/serializers.py | 32 +++++++++++++++++++++++++
 api/funkwhale_api/manage/urls.py        |  5 +++-
 api/funkwhale_api/manage/views.py       | 21 ++++++++++++++++
 api/funkwhale_api/users/models.py       |  4 ++--
 api/tests/manage/test_views.py          | 21 +++++++++++++++-
 6 files changed, 98 insertions(+), 4 deletions(-)

diff --git a/api/funkwhale_api/manage/filters.py b/api/funkwhale_api/manage/filters.py
index 2f2bde83..e4cda18c 100644
--- a/api/funkwhale_api/manage/filters.py
+++ b/api/funkwhale_api/manage/filters.py
@@ -3,6 +3,7 @@ from django_filters import rest_framework as filters
 
 from funkwhale_api.common import fields
 from funkwhale_api.music import models as music_models
+from funkwhale_api.users import models as users_models
 
 
 class ManageTrackFileFilterSet(filters.FilterSet):
@@ -18,3 +19,21 @@ class ManageTrackFileFilterSet(filters.FilterSet):
     class Meta:
         model = music_models.TrackFile
         fields = ["q", "track__album", "track__artist", "track", "library_track"]
+
+
+class ManageUserFilterSet(filters.FilterSet):
+    q = fields.SearchFilter(search_fields=["username", "email", "name"])
+
+    class Meta:
+        model = users_models.User
+        fields = [
+            "q",
+            "is_active",
+            "privacy_level",
+            "is_staff",
+            "is_superuser",
+            "permission_upload",
+            "permission_library",
+            "permission_settings",
+            "permission_federation",
+        ]
diff --git a/api/funkwhale_api/manage/serializers.py b/api/funkwhale_api/manage/serializers.py
index 1c94cf55..13f886a7 100644
--- a/api/funkwhale_api/manage/serializers.py
+++ b/api/funkwhale_api/manage/serializers.py
@@ -3,6 +3,7 @@ from rest_framework import serializers
 
 from funkwhale_api.common import serializers as common_serializers
 from funkwhale_api.music import models as music_models
+from funkwhale_api.users import models as users_models
 
 from . import filters
 
@@ -67,3 +68,34 @@ class ManageTrackFileActionSerializer(common_serializers.ActionSerializer):
     @transaction.atomic
     def handle_delete(self, objects):
         return objects.delete()
+
+
+class ManageUserSerializer(serializers.ModelSerializer):
+    permissions = serializers.SerializerMethodField()
+
+    class Meta:
+        model = users_models.User
+        fields = (
+            "id",
+            "username",
+            "email",
+            "name",
+            "is_active",
+            "is_staff",
+            "is_superuser",
+            "date_joined",
+            "last_activity",
+            "permissions",
+            "privacy_level",
+        )
+        read_only_fields = [
+            "id",
+            "email",
+            "privacy_level",
+            "username",
+            "date_joined",
+            "last_activity",
+        ]
+
+    def get_permissions(self, o):
+        return o.get_permissions(defaults=self.context.get("default_permissions"))
diff --git a/api/funkwhale_api/manage/urls.py b/api/funkwhale_api/manage/urls.py
index 60853034..f208fb85 100644
--- a/api/funkwhale_api/manage/urls.py
+++ b/api/funkwhale_api/manage/urls.py
@@ -5,7 +5,10 @@ from . import views
 
 library_router = routers.SimpleRouter()
 library_router.register(r"track-files", views.ManageTrackFileViewSet, "track-files")
+users_router = routers.SimpleRouter()
+users_router.register(r"users", views.ManageUserViewSet, "users")
 
 urlpatterns = [
-    url(r"^library/", include((library_router.urls, "instance"), namespace="library"))
+    url(r"^library/", include((library_router.urls, "instance"), namespace="library")),
+    url(r"^users/", include((users_router.urls, "instance"), namespace="users")),
 ]
diff --git a/api/funkwhale_api/manage/views.py b/api/funkwhale_api/manage/views.py
index 8511732c..f9b78ef8 100644
--- a/api/funkwhale_api/manage/views.py
+++ b/api/funkwhale_api/manage/views.py
@@ -1,7 +1,9 @@
 from rest_framework import mixins, response, viewsets
 from rest_framework.decorators import list_route
 
+from funkwhale_api.common import preferences
 from funkwhale_api.music import models as music_models
+from funkwhale_api.users import models as users_models
 from funkwhale_api.users.permissions import HasUserPermission
 
 from . import filters, serializers
@@ -41,3 +43,22 @@ class ManageTrackFileViewSet(
         serializer.is_valid(raise_exception=True)
         result = serializer.save()
         return response.Response(result, status=200)
+
+
+class ManageUserViewSet(
+    mixins.ListModelMixin,
+    mixins.RetrieveModelMixin,
+    mixins.UpdateModelMixin,
+    viewsets.GenericViewSet,
+):
+    queryset = users_models.User.objects.all().order_by("-id")
+    serializer_class = serializers.ManageUserSerializer
+    filter_class = filters.ManageUserFilterSet
+    permission_classes = (HasUserPermission,)
+    required_permissions = ["settings"]
+    ordering_fields = ["date_joined", "last_activity", "username"]
+
+    def get_serializer_context(self):
+        context = super().get_serializer_context()
+        context["default_permissions"] = preferences.get("users__default_permissions")
+        return context
diff --git a/api/funkwhale_api/users/models.py b/api/funkwhale_api/users/models.py
index d37656c1..055a971b 100644
--- a/api/funkwhale_api/users/models.py
+++ b/api/funkwhale_api/users/models.py
@@ -82,8 +82,8 @@ class User(AbstractUser):
     def __str__(self):
         return self.username
 
-    def get_permissions(self):
-        defaults = preferences.get("users__default_permissions")
+    def get_permissions(self, defaults=None):
+        defaults = defaults or preferences.get("users__default_permissions")
         perms = {}
         for p in PERMISSIONS:
             v = (
diff --git a/api/tests/manage/test_views.py b/api/tests/manage/test_views.py
index e2bfbf3a..a72bcf5a 100644
--- a/api/tests/manage/test_views.py
+++ b/api/tests/manage/test_views.py
@@ -5,7 +5,11 @@ from funkwhale_api.manage import serializers, views
 
 
 @pytest.mark.parametrize(
-    "view,permissions,operator", [(views.ManageTrackFileViewSet, ["library"], "and")]
+    "view,permissions,operator",
+    [
+        (views.ManageTrackFileViewSet, ["library"], "and"),
+        (views.ManageUserViewSet, ["settings"], "and"),
+    ],
 )
 def test_permissions(assert_user_permission, view, permissions, operator):
     assert_user_permission(view, permissions, operator)
@@ -23,3 +27,18 @@ def test_track_file_view(factories, superuser_api_client):
 
     assert response.data["count"] == len(tfs)
     assert response.data["results"] == expected
+
+
+def test_user_view(factories, superuser_api_client, mocker):
+    mocker.patch("funkwhale_api.users.models.User.record_activity")
+    users = factories["users.User"].create_batch(size=5) + [superuser_api_client.user]
+    qs = users[0].__class__.objects.order_by("-id")
+    url = reverse("api:v1:manage:users:users-list")
+
+    response = superuser_api_client.get(url, {"sort": "-id"})
+    expected = serializers.ManageUserSerializer(
+        qs, many=True, context={"request": response.wsgi_request}
+    ).data
+
+    assert response.data["count"] == len(users)
+    assert response.data["results"] == expected
-- 
GitLab