[PATCH v3 10/16] REST: Make 'User.first_name', 'last_name' editable

Stephen Finucane stephen at that.guru
Sat Nov 26 05:18:29 AEDT 2016


In an ideal world, everything that can be done with the UI should also
be doable from the command line. The first and last names of the User
are low-value fields to allow customization of, but they are easily
implmented and move us towards the above goal.

Signed-off-by: Stephen Finucane <stephen at that.guru>
Reviewed-by: Daniel Axtens <dja at axtens.net>
Reviewed-by: Andy Doan <andy.doan at linaro.org>
---
 patchwork/api/user.py            | 20 ++++++++++++++++----
 patchwork/tests/test_rest_api.py | 16 +++++++++++-----
 2 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/patchwork/api/user.py b/patchwork/api/user.py
index 8fe3e74..c5f7c05 100644
--- a/patchwork/api/user.py
+++ b/patchwork/api/user.py
@@ -19,16 +19,28 @@
 
 from django.contrib.auth.models import User
 from rest_framework.generics import ListAPIView
-from rest_framework.generics import RetrieveAPIView
-from rest_framework.permissions import IsAuthenticated
+from rest_framework.generics import RetrieveUpdateAPIView
+from rest_framework import permissions
 from rest_framework.serializers import HyperlinkedModelSerializer
 
 
+class IsOwnerOrReadOnly(permissions.BasePermission):
+
+    def has_object_permission(self, request, view, obj):
+        if request.method in permissions.SAFE_METHODS:
+            return True
+
+        return obj == request.user
+
+
 class UserSerializer(HyperlinkedModelSerializer):
 
     class Meta:
         model = User
         fields = ('url', 'username', 'first_name', 'last_name', 'email')
+        # we don't allow updating of emails via the API, as we need to
+        # validate that the User actually owns said email first
+        read_only_fields = ('username', 'email')
         extra_kwargs = {
             'url': {'view_name': 'api-user-detail'},
         }
@@ -37,7 +49,7 @@ class UserSerializer(HyperlinkedModelSerializer):
 class UserMixin(object):
 
     queryset = User.objects.all()
-    permission_classes = (IsAuthenticated,)
+    permission_classes = (permissions.IsAuthenticated, IsOwnerOrReadOnly)
     serializer_class = UserSerializer
 
 
@@ -47,7 +59,7 @@ class UserList(UserMixin, ListAPIView):
     pass
 
 
-class UserDetail(UserMixin, RetrieveAPIView):
+class UserDetail(UserMixin, RetrieveUpdateAPIView):
     """Show a user."""
 
     pass
diff --git a/patchwork/tests/test_rest_api.py b/patchwork/tests/test_rest_api.py
index d764945..195a71a 100644
--- a/patchwork/tests/test_rest_api.py
+++ b/patchwork/tests/test_rest_api.py
@@ -233,19 +233,25 @@ class TestUserAPI(APITestCase):
         self.assertNotIn('password', resp.data[0])
         self.assertNotIn('is_superuser', resp.data[0])
 
-    def test_readonly(self):
+    def test_update(self):
         user = create_maintainer()
         user.is_superuser = True
         user.save()
         self.client.force_authenticate(user=user)
 
-        resp = self.client.delete(self.api_url(1))
-        self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code)
+        resp = self.client.patch(self.api_url(user.id), {'first_name': 'Tan'})
+        self.assertEqual(status.HTTP_200_OK, resp.status_code)
 
-        resp = self.client.patch(self.api_url(1), {'email': 'foo at f.com'})
+    def test_create_delete(self):
+        user = create_maintainer()
+        user.is_superuser = True
+        user.save()
+        self.client.force_authenticate(user=user)
+
+        resp = self.client.delete(self.api_url(user.id))
         self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code)
 
-        resp = self.client.post(self.api_url(), {'email': 'foo at f.com'})
+        resp = self.client.post(self.api_url(user.id), {'email': 'foo at f.com'})
         self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code)
 
 
-- 
2.7.4



More information about the Patchwork mailing list