Browse Source

Add wrappers for template permissions

Alec Nikolas Reiter 9 years ago
parent
commit
39117e1a8e
2 changed files with 161 additions and 25 deletions
  1. 11 10
      flaskbb/app.py
  2. 150 15
      flaskbb/utils/requirements.py

+ 11 - 10
flaskbb/app.py

@@ -43,9 +43,9 @@ from flaskbb.utils.helpers import format_date, time_since, crop_title, \
 from flaskbb.utils.translations import FlaskBBDomain
 # permission checks (here they are used for the jinja filters)
 from flaskbb.utils.requirements import (
-    IsAdmin, IsAtleastModerator, IsAtleastModeratorInForum,
-    CanBanUser, CanEditUser, CanEditPost, CanDeletePost, CanDeleteTopic,
-    CanPostReply, CanPostTopic
+    IsAdmin, IsAtleastModerator, TplCanModerate,
+    CanBanUser, CanEditUser, TplCanDeletePost, TplCanDeleteTopic,
+    TplCanEditPost, TplCanPostTopic, TplCanPostReply
 )
 # app specific configurations
 from flaskbb.utils.settings import flaskbb_config
@@ -176,19 +176,20 @@ def configure_template_filters(app):
         ('is_admin_or_moderator', IsAtleastModerator),
         ('can_edit_user', CanEditUser),
         ('can_ban_user', CanBanUser),
-        ('edit_post', CanEditPost),
-        ('delete_post', CanDeletePost),
-        ('delete_topic', CanDeleteTopic),
-        ('post_reply', CanPostReply),
-        ('post_topic', CanPostTopic),
-        # will pull forum ID from request
-        ('can_moderate', IsAtleastModeratorInForum(None))
     ]
 
     filters.update([
         (name, partial(perm, request=request)) for name, perm in permissions
     ])
 
+    # these create closures
+    filters['can_moderate'] = TplCanModerate(request)
+    filters['post_reply'] = TplCanPostReply(request)
+    filters['edit_post'] = TplCanEditPost(request)
+    filters['delete_post'] = TplCanDeletePost(request)
+    filters['post_topic'] = TplCanPostTopic(request)
+    filters['delete_topic'] = TplCanDeleteTopic(request)
+
     app.jinja_env.filters.update(filters)
 
 

+ 150 - 15
flaskbb/utils/requirements.py

@@ -27,8 +27,9 @@ class IsAuthed(Requirement):
 
 
 class IsModeratorInForum(IsAuthed):
-    def __init__(self, forum_id=None):
+    def __init__(self, forum=None, forum_id=None):
         self.forum_id = forum_id
+        self.forum = forum
 
     def fulfill(self, user, request):
         moderators = self._get_forum_moderators(request)
@@ -42,7 +43,9 @@ class IsModeratorInForum(IsAuthed):
         return self._get_forum(request).moderators
 
     def _get_forum(self, request):
-        if self.forum_id is not None:
+        if self.forum is not None:
+            return self.forum
+        elif self.forum_id is not None:
             return self._get_forum_from_id()
         return self._get_forum_from_request(request)
 
@@ -77,42 +80,91 @@ class IsSameUser(IsAuthed):
 
 
 class TopicNotLocked(Requirement):
+    def __init__(self, topic=None, topic_id=None):
+        self._topic = topic
+        self._topic_id = topic_id
+
     def fulfill(self, user, request):
         return not self._is_topic_or_forum_locked(request)
 
+    def _is_topic_or_forum_locked(self, request):
+        topic = self._determine_topic(request)
+        return topic.locked or topic.forum.locked
+
+    def _determine_topic(self, request):
+        if self._topic is not None:
+            return self._topic
+        elif self._topic_id is not None:
+            return Topic.query.get(self._topic_id)
+        else:
+            return self._get_topic_from_request(request)
+
     def _get_topic_from_request(self, request):
         view_args = request.view_args
         if 'post_id' in view_args:
-            return Post.query.get(view_args['post_id']).topic
+            return (
+                Topic.query.join(Post, Post.topic_id == Topic.id)
+                .filter(Post.id == view_args['post_id'])
+                .with_entities(Topic.locked)
+                .first()
+            )
         elif 'topic_id' in view_args:
             return Topic.query.get(view_args['topic_id'])
         else:
             raise FlaskBBError
 
-    def _is_topic_or_forum_locked(self, request):
-        topic = self._get_topic_from_request(request)
-        return topic.locked or topic.forum.locked
-
 
 class ForumNotLocked(Requirement):
+    def __init__(self, forum=None, forum_id=None):
+        self._forum = forum
+        self._forum_id = forum_id
+
     def fulfill(self, user, request):
         return not self._is_forum_locked(request)
 
     def _is_forum_locked(self, request):
-        return self._get_forum_from_request(request).locked
+        forum = self._determine_forum(request)
+        return not forum.locked
+
+    def _determine_forum(self, request):
+        if self._forum is not None:
+            return self._forum
+        elif self._forum_id is not None:
+            return Forum.query.get(self._forum_id)
+        else:
+            return self._get_forum_from_request(request)
 
     def _get_forum_from_request(self, request):
         view_args = request.view_args
+
+        # These queries look big and nasty, but they really aren't that bad
+        # Basically, find the forum this post or topic belongs to
+        # with_entities returns a KeyedTuple with only the locked status
+
         if 'post_id' in view_args:
-            return Post.query.get(view_args['post_id']).topic.forum
+            return (
+                Forum.query.join(Topic, Topic.forum_id == Forum.id)
+                .join(Post, Post.topic_id == Topic.id)
+                .filter(Post.id == view_args['post_id'])
+                .with_entities(Forum.locked)
+                .first()
+            )
+
         elif 'topic_id' in view_args:
-            return Topic.query.get(view_args['topic_id']).forum
+            return (
+                Forum.query.join(Topic, Topic.forum_id == Forum.id)
+                .filter(Topic.id == view_args['topic_id'])
+                .with_entities(Forum.locked)
+                .first()
+            )
+
         elif 'forum_id' in view_args:
             return Forum.query.get(view_args['forum_id'])
 
 
-def IsAtleastModeratorInForum(forum_id=None):
-    return Or(IsAtleastSuperModerator, IsModeratorInForum(forum_id))
+def IsAtleastModeratorInForum(forum_id=None, forum=None):
+    return Or(IsAtleastSuperModerator, IsModeratorInForum(forum_id=forum_id,
+                                                          forum=forum))
 
 IsMod = And(IsAuthed(), Has('mod'))
 IsSuperMod = And(IsAuthed(), Has('super_mod'))
@@ -130,9 +182,7 @@ CanEditPost = Or(And(IsSameUser(), Has('editpost'), TopicNotLocked()),
                  IsAtleastSuperModerator,
                  And(IsModeratorInForum(), Has('editpost')))
 
-CanDeletePost = Or(And(IsSameUser(), Has('editpost'), TopicNotLocked()),
-                   IsAtleastSuperModerator,
-                   And(IsModeratorInForum(), Has('editpost')))
+CanDeletePost = CanEditPost
 
 CanPostReply = Or(And(Has('postreply'), TopicNotLocked()),
                   IsModeratorInForum(),
@@ -145,3 +195,88 @@ CanPostTopic = Or(And(Has('posttopic'), ForumNotLocked()),
 CanDeleteTopic = Or(And(IsSameUser(), Has('deletetopic'), TopicNotLocked()),
                     IsAtleastSuperModerator,
                     And(IsModeratorInForum(), Has('deletetopic')))
+
+
+# Template Allowances -- gross, I know
+
+def TplCanModerate(request):
+    def _(user, forum):
+        kwargs = {}
+
+        if isinstance(forum, int):
+            kwargs['forum_id'] = forum
+        elif isinstance(forum, Forum):
+            kwargs['forum'] = forum
+
+        return IsAtleastModeratorInForum(**kwargs)(user, request)
+    return _
+
+
+def TplCanPostReply(request):
+    def _(user, topic=None):
+        kwargs = {}
+
+        if isinstance(topic, int):
+            kwargs['topic_id'] = topic
+        elif isinstance(topic, Topic):
+            kwargs['topic'] = topic
+
+        return Or(
+            IsAtleastSuperModerator,
+            IsModeratorInForum(),
+            And(Has('postreply'), TopicNotLocked(**kwargs))
+        )(user, request)
+    return _
+
+
+def TplCanEditPost(request):
+    def _(user, topic=None):
+        kwargs = {}
+
+        if isinstance(topic, int):
+            kwargs['topic_id'] = topic
+        elif isinstance(topic, Topic):
+            kwargs['topic'] = topic
+
+        return Or(
+            IsAtleastSuperModerator,
+            And(IsModeratorInForum(), Has('editpost')),
+            And(IsSameUser(), Has('editpost'), TopicNotLocked(**kwargs)),
+        )(user, request)
+    return _
+
+TplCanDeletePost = TplCanEditPost
+
+
+def TplCanPostTopic(request):
+    def _(user, forum):
+        kwargs = {}
+
+        if isinstance(forum, int):
+            kwargs['forum_id'] = forum
+        elif isinstance(forum, Forum):
+            kwargs['forum'] = forum
+
+        return Or(
+            IsAtleastSuperModerator,
+            IsModeratorInForum(**kwargs),
+            And(Has('posttopic'), ForumNotLocked(**kwargs))
+        )(user, request)
+    return _
+
+
+def TplCanDeleteTopic(request):
+    def _(user, topic=None):
+        kwargs = {}
+
+        if isinstance(topic, int):
+            kwargs['topic_id'] = topic
+        elif isinstance(topic, Topic):
+            kwargs['topic'] = topic
+
+        return Or(
+            IsAtleastSuperModerator,
+            And(IsModeratorInForum(), Has('deletetopic')),
+            And(IsSameUser(), Has('deletetopic'), TopicNotLocked(**kwargs))
+        )(user, request)
+    return _