Repository: akvo/akvo-rsr

Issue:
Allow users to delete their own project updates
Deleting project updates permissions were broken. Users should be allowed to delete their own project updates. 


Discussion:

Resolving patch:
diff --git a/akvo/rsr/models/__init__.py b/akvo/rsr/models/__init__.py
--- a/akvo/rsr/models/__init__.py
+++ b/akvo/rsr/models/__init__.py
@@ -185,7 +185,7 @@
 import rules
 from ..permissions import (is_rsr_admin, is_org_admin, is_org_user_manager,
                            is_org_me_manager, is_org_me_manager_or_project_editor,
-                           is_org_user, is_self, is_org_enumerator)
+                           is_org_user, is_self, is_org_enumerator, is_own)
 
 rules.add_perm('rsr', rules.always_allow)
 
@@ -261,9 +261,9 @@
 
 rules.add_perm('rsr.add_projectupdate', is_rsr_admin | is_org_admin | is_org_user_manager |
                is_org_me_manager_or_project_editor | is_org_enumerator | is_org_user)
-rules.add_perm('rsr.change_projectupdate', is_rsr_admin | is_org_admin | is_org_user_manager |
+rules.add_perm('rsr.change_projectupdate', is_own | is_rsr_admin | is_org_admin | is_org_user_manager |
                is_org_me_manager_or_project_editor | is_org_user | is_org_enumerator)
-rules.add_perm('rsr.delete_projectupdate', is_rsr_admin | is_org_admin)
+rules.add_perm('rsr.delete_projectupdate', is_own | is_rsr_admin | is_org_admin)
 
 rules.add_perm('rsr.add_projectupdatelocation', is_rsr_admin)
 rules.add_perm('rsr.change_projectupdatelocation', is_rsr_admin)
diff --git a/akvo/rsr/models/user.py b/akvo/rsr/models/user.py
--- a/akvo/rsr/models/user.py
+++ b/akvo/rsr/models/user.py
@@ -555,6 +555,8 @@ def get_permission_filter(self, permission, project_relation, include_user_owned
         if include_user_owned:
             permissions['is_org_user'] = permissions['is_org_user'] & Q(user=self)
             permissions['is_org_user_manager'] = permissions['is_org_user_manager'] & Q(user=self)
+            permissions['is_own'] = Q(user=self)
+
         operators = {'|': Q.OR, '&': Q.AND}
         return self.parse_permission_expression(permission_expression, permissions, operators)
 
diff --git a/akvo/rsr/permissions.py b/akvo/rsr/permissions.py
--- a/akvo/rsr/permissions.py
+++ b/akvo/rsr/permissions.py
@@ -41,9 +41,7 @@ def _user_has_group_permissions(user, obj, group_names):
         return True
 
     if isinstance(obj, ProjectUpdate):
-        if obj.user_id == user.id:
-            return True
-        elif group_names == [GROUP_NAME_ADMINS]:
+        if group_names == [GROUP_NAME_ADMINS]:
             # Check if user can admin the user making the update
 
             # NOTE: We could set `obj = user` and just see if the admin has the
@@ -162,6 +160,13 @@ def is_self(user, obj):
     return False
 
 
+@rules.predicate
+def is_own(user, obj):
+    if obj is None:
+        return True
+    return obj.user_id == user.id
+
+
 # Additional permission filtering
 
 def project_access_filter(user, projects):


Test patch:
diff --git a/akvo/rsr/tests/rest/test_project_update.py b/akvo/rsr/tests/rest/test_project_update.py
--- a/akvo/rsr/tests/rest/test_project_update.py
+++ b/akvo/rsr/tests/rest/test_project_update.py
@@ -115,6 +115,53 @@ def test_rest_post_project_update(self):
                                })
         self.assertEqual(response.status_code, 201)
 
+    def test_rest_patch_project_update(self):
+        """Checks the REST project update endpoint PATCH."""
+        self.c.login(username=self.user.username, password='password')
+        response = self.c.post('/rest/v1/project_update/',
+                               {
+                                   'project': self.project.pk,
+                                   'user': self.user.pk,
+                                   'title': 'Project Update Title'
+                               })
+        update_id = response.data['id']
+        updated_title = 'Updated Title'
+
+        # When
+        response = self.c.patch(
+            '/rest/v1/project_update/{}/?format=json'.format(update_id),
+            json.dumps({'title': updated_title}),
+            content_type='application/json'
+        )
+
+        # Then
+        self.assertEqual(response.data['title'], updated_title)
+        self.assertEqual(response.status_code, 200)
+
+    def test_rest_delete_project_update(self):
+        """Checks that user can delete their own project update."""
+        # Given
+        self.c.login(username=self.user.username, password='password')
+        response = self.c.post('/rest/v1/project_update/',
+                               {
+                                   'project': self.project.pk,
+                                   'user': self.user.pk,
+                                   'title': 'Allowed'
+                               })
+        self.assertEqual(response.status_code, 201)
+        update_id = response.data['id']
+
+        # When
+        response = self.c.delete(
+            '/rest/v1/project_update/{}/?format=json'.format(update_id),
+            content_type='application/json'
+        )
+
+        # Then
+        self.assertEqual(response.status_code, 204)
+        with self.assertRaises(ProjectUpdate.DoesNotExist):
+            ProjectUpdate.objects.get(id=update_id)
+
     def test_rest_cannot_post_project_update_to_random_projects(self):
         """
         Checks the REST project update endpoint POST functions.