Browse Source

Some guards on post specific actions, delete event endpoint

Rafał Pitoń 8 years ago
parent
commit
ff20edcd75

+ 10 - 3
misago/threads/api/threadposts.py

@@ -11,7 +11,8 @@ from misago.users.online.utils import make_users_status_aware
 
 from ..models import Post
 from ..moderation import posts as moderation
-from ..permissions.threads import allow_delete_post, allow_edit_post, allow_reply_thread
+from ..permissions.threads import (
+    allow_delete_event, allow_delete_post, allow_edit_post, allow_reply_thread)
 from ..serializers import PostSerializer
 from ..viewmodels.post import ThreadPost
 from ..viewmodels.posts import ThreadPosts
@@ -132,7 +133,11 @@ class ViewSet(viewsets.ViewSet):
         thread = self.get_thread_for_update(request, thread_pk)
         post = self.get_post_for_update(request, thread, pk).post
 
-        allow_delete_post(request.user, post)
+        if post.is_event:
+            allow_delete_event(request.user, post)
+        else:
+            allow_delete_post(request.user, post)
+
         moderation.delete_post(request.user, post)
 
         thread.thread.synchronize()
@@ -167,8 +172,10 @@ class ViewSet(viewsets.ViewSet):
         if 'reply' in request.query_params:
             reply_to = self.post(request, thread, get_int_or_404(request.query_params['reply'])).post
 
+            if reply_to.is_event:
+                raise PermissionDenied(_("You can't reply to events."))
             if reply_to.is_hidden and not reply_to.acl['can_see_hidden']:
-                raise PermissionDenied(_("You can't reply to hidden posts"))
+                raise PermissionDenied(_("You can't reply to hidden posts."))
 
             return Response({
                 'id': reply_to.pk,

+ 13 - 11
misago/threads/permissions/threads.py

@@ -25,8 +25,6 @@ __all__ = [
     'can_reply_thread',
     'allow_edit_thread',
     'can_edit_thread',
-    'allow_see_post',
-    'can_see_post',
     'allow_edit_post',
     'can_edit_post',
     'allow_unhide_post',
@@ -548,19 +546,13 @@ def allow_edit_thread(user, target):
 can_edit_thread = return_boolean(allow_edit_thread)
 
 
-def allow_see_post(user, target):
-    if target.is_unapproved:
-        category_acl = user.acl['categories'].get(target.category_id, {})
-        if not category_acl.get('can_approve_content'):
-            if user.is_anonymous() or user.pk != target.poster_id:
-                raise Http404()
-can_see_post = return_boolean(allow_see_post)
-
-
 def allow_edit_post(user, target):
     if user.is_anonymous():
         raise PermissionDenied(_("You have to sign in to edit posts."))
 
+    if target.is_event:
+        raise PermissionDenied(_("Events can't be edited."))
+
     category_acl = user.acl['categories'].get(target.category_id, {})
 
     if not category_acl['can_edit_posts']:
@@ -697,6 +689,16 @@ def allow_delete_post(user, target):
 can_delete_post = return_boolean(allow_delete_post)
 
 
+def allow_delete_event(user, target):
+    if user.is_anonymous():
+        raise PermissionDenied(_("You have to sign in to delete events."))
+
+    category_acl = user.acl['categories'].get(target.category_id, {})
+    if category_acl['can_hide_events'] != 2:
+        raise PermissionDenied(_("You can't delete events in this category."))
+can_delete_event = return_boolean(allow_delete_event)
+
+
 """
 Permission check helpers
 """

+ 11 - 0
misago/threads/tests/test_thread_editreply_api.py

@@ -162,6 +162,17 @@ class EditReplyTests(AuthenticatedUserTestCase):
             ]
         })
 
+    def test_edit_event(self):
+        """events can't be edited"""
+        self.override_acl()
+
+        self.post.is_event = True
+        self.post.save()
+
+        response = self.put(self.api_link, data={})
+
+        self.assertContains(response, "Events can't be edited.", status_code=403)
+
     def test_post_is_validated(self):
         """post is validated"""
         self.override_acl()

+ 66 - 5
misago/threads/tests/test_threads_posts_api.py → misago/threads/tests/test_thread_postdelete_api.py

@@ -18,9 +18,6 @@ class PostDeleteApiTests(ThreadsApiTestCase):
             'pk': self.post.pk
         })
 
-    def refresh_thread(self):
-        self.thread = Thread.objects.get(pk=self.thread.pk)
-
     def test_delete_anonymous(self):
         """api validates if deleting user is authenticated"""
         self.logout_user()
@@ -53,6 +50,21 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         response = self.client.delete(api_link)
         self.assertContains(response, "You can't delete thread's first post.", status_code=403)
 
+    def test_delete_event(self):
+        """api differs posts from events"""
+        self.override_acl({
+            'can_hide_own_posts': 2,
+            'can_hide_posts': 2,
+
+            'can_hide_events': 0
+        })
+
+        self.post.is_event = True
+        self.post.save()
+
+        response = self.client.delete(self.api_link)
+        self.assertContains(response, "You can't delete events in this category.", status_code=403)
+
     def test_delete_owned_post(self):
         """api deletes owned thread post"""
         self.override_acl({
@@ -64,7 +76,7 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         response = self.client.delete(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        self.refresh_thread()
+        self.thread = Thread.objects.get(pk=self.thread.pk)
 
         self.assertNotEqual(self.thread.last_post_id, self.post.pk)
         with self.assertRaises(Post.DoesNotExist):
@@ -80,8 +92,57 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         response = self.client.delete(self.api_link)
         self.assertEqual(response.status_code, 200)
 
-        self.refresh_thread()
+        self.thread = Thread.objects.get(pk=self.thread.pk)
 
         self.assertNotEqual(self.thread.last_post_id, self.post.pk)
         with self.assertRaises(Post.DoesNotExist):
             self.thread.post_set.get(pk=self.post.pk)
+
+
+class EventDeleteApiTests(ThreadsApiTestCase):
+    def setUp(self):
+        super(EventDeleteApiTests, self).setUp()
+
+        self.event = testutils.reply_thread(self.thread, poster=self.user, is_event=True)
+
+        self.api_link = reverse('misago:api:thread-post-detail', kwargs={
+            'thread_pk': self.thread.pk,
+            'pk': self.event.pk
+        })
+
+    def test_delete_anonymous(self):
+        """api validates if deleting user is authenticated"""
+        self.logout_user()
+
+        response = self.client.delete(self.api_link)
+        self.assertContains(response, "This action is not available to guests.", status_code=403)
+
+    def test_no_permission(self):
+        """api validates permission to delete event"""
+        self.override_acl({
+            'can_hide_own_posts': 2,
+            'can_hide_posts': 2,
+
+            'can_hide_events': 0
+        })
+
+        response = self.client.delete(self.api_link)
+        self.assertContains(response, "You can't delete events in this category.", status_code=403)
+
+    def test_delete_event(self):
+        """api differs posts from events"""
+        self.override_acl({
+            'can_hide_own_posts': 0,
+            'can_hide_posts': 0,
+
+            'can_hide_events': 2
+        })
+
+        response = self.client.delete(self.api_link)
+        self.assertEqual(response.status_code, 200)
+
+        self.thread = Thread.objects.get(pk=self.thread.pk)
+
+        self.assertNotEqual(self.thread.last_post_id, self.event.pk)
+        with self.assertRaises(Post.DoesNotExist):
+            self.thread.post_set.get(pk=self.event.pk)

+ 23 - 0
misago/threads/tests/test_threads_editor_api.py

@@ -382,6 +382,18 @@ class ThreadReplyEditorApiTests(EditorApiTestCase):
         response = self.client.get('{}?reply={}'.format(self.api_link, reply_to.pk))
         self.assertEqual(response.status_code, 404)
 
+    def test_reply_to_event(self):
+        """events can't be edited"""
+        self.override_acl({
+            'can_reply_threads': 1
+        })
+
+        reply_to = testutils.reply_thread(self.thread, is_event=True)
+
+        response = self.client.get('{}?reply={}'.format(self.api_link, reply_to.pk))
+
+        self.assertContains(response, "You can't reply to events.", status_code=403)
+
     def test_reply_to(self):
         """api includes replied to post details in response"""
         self.override_acl({
@@ -554,6 +566,17 @@ class EditReplyEditorApiTests(EditorApiTestCase):
         response = self.client.get(self.api_link)
         self.assertEqual(response.status_code, 200)
 
+    def test_post_is_event(self):
+        """events can't be edited"""
+        self.override_acl()
+
+        self.post.is_event = True
+        self.post.save()
+
+        response = self.client.get(self.api_link)
+
+        self.assertContains(response, "Events can't be edited.", status_code=403)
+
     def test_other_user_post(self):
         """api validates if other user's post can be edited"""
         self.override_acl({

+ 4 - 3
misago/threads/testutils.py

@@ -21,7 +21,7 @@ def post_thread(category, title='Test thread', poster='Tester',
         'last_post_on': started_on,
         'is_unapproved': is_unapproved,
         'is_hidden': is_hidden,
-        'is_closed': is_closed,
+        'is_closed': is_closed
     }
 
     if is_global:
@@ -59,8 +59,8 @@ def post_thread(category, title='Test thread', poster='Tester',
 
 
 def reply_thread(thread, poster="Tester", message="I am test message",
-                 is_unapproved=False, is_hidden=False, has_reports=False,
-                 has_open_reports=False, posted_on=None, poster_ip='127.0.0.1'):
+                 is_unapproved=False, is_hidden=False, is_event=False,
+                 has_reports=False, has_open_reports=False, posted_on=None, poster_ip='127.0.0.1'):
     posted_on = posted_on or thread.last_post_on + timedelta(minutes=5)
 
     kwargs = {
@@ -72,6 +72,7 @@ def reply_thread(thread, poster="Tester", message="I am test message",
         'poster_ip': poster_ip,
         'posted_on': posted_on,
         'updated_on': posted_on,
+        'is_event': is_event,
         'is_unapproved': is_unapproved,
         'is_hidden': is_hidden,
         'has_reports': has_reports,

+ 2 - 4
misago/threads/viewmodels/post.py

@@ -2,7 +2,7 @@ from django.shortcuts import get_object_or_404
 
 from misago.acl import add_acl
 
-from ..permissions.threads import allow_see_post, exclude_invisible_posts
+from ..permissions.threads import exclude_invisible_posts
 
 
 class ViewModel(object):
@@ -24,12 +24,10 @@ class ViewModel(object):
                 'poster__ban_cache'
             )
 
-        post = get_object_or_404(queryset, pk=pk, is_event=False)
+        post = get_object_or_404(queryset, pk=pk)
 
         post.category = thread.category
 
-        allow_see_post(request.user, post)
-
         return post
 
     def get_queryset(self, request, thread):