requirements.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. """
  2. flaskbb.utils.requirements
  3. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. Authorization requirements for FlaskBB.
  5. :copyright: (c) 2015 by the FlaskBB Team.
  6. :license: BSD, see LICENSE for more details
  7. """
  8. from flask_allows import And, Or, Requirement
  9. from flaskbb.exceptions import FlaskBBError
  10. from flaskbb.forum.locals import current_forum, current_post, current_topic
  11. from flaskbb.forum.models import Forum, Post, Topic
  12. class Has(Requirement):
  13. def __init__(self, permission):
  14. self.permission = permission
  15. def __repr__(self):
  16. return "<Has({!s})>".format(self.permission)
  17. def fulfill(self, user, request):
  18. return user.permissions.get(self.permission, False)
  19. class IsAuthed(Requirement):
  20. def fulfill(self, user, request):
  21. return user.is_authenticated
  22. class IsModeratorInForum(IsAuthed):
  23. def __init__(self, forum=None, forum_id=None):
  24. self.forum_id = forum_id
  25. self.forum = forum
  26. def fulfill(self, user, request):
  27. moderators = self._get_forum_moderators(request)
  28. return (super(IsModeratorInForum, self).fulfill(user, request) and
  29. self._user_is_forum_moderator(user, moderators))
  30. def _user_is_forum_moderator(self, user, moderators):
  31. return user in moderators
  32. def _get_forum_moderators(self, request):
  33. return self._get_forum(request).moderators
  34. def _get_forum(self, request):
  35. if self.forum is not None:
  36. return self.forum
  37. elif self.forum_id is not None:
  38. return self._get_forum_from_id()
  39. return self._get_forum_from_request(request)
  40. def _get_forum_from_id(self):
  41. return Forum.query.get(self.forum_id)
  42. def _get_forum_from_request(self, request):
  43. if not current_forum:
  44. raise FlaskBBError('Could not load forum data')
  45. return current_forum
  46. class IsSameUser(IsAuthed):
  47. def __init__(self, topic_or_post=None):
  48. self._topic_or_post = topic_or_post
  49. def fulfill(self, user, request):
  50. return (super(IsSameUser, self).fulfill(user, request) and
  51. user.id == self._determine_user(request))
  52. def _determine_user(self, request):
  53. if self._topic_or_post is not None:
  54. return self._topic_or_post.user_id
  55. return self._get_user_id_from_post(request)
  56. def _get_user_id_from_post(self, request):
  57. if current_post:
  58. return current_post.user_id
  59. elif current_topic:
  60. return current_topic.user_id
  61. else:
  62. raise FlaskBBError
  63. class TopicNotLocked(Requirement):
  64. def __init__(self, topic=None, topic_id=None, post_id=None, post=None):
  65. self._topic = topic
  66. self._topic_id = topic_id
  67. self._post = post
  68. self._post_id = post_id
  69. def fulfill(self, user, request):
  70. return not any(self._determine_locked(request))
  71. def _determine_locked(self, request):
  72. """
  73. Returns a pair of booleans:
  74. * Is the topic locked?
  75. * Is the forum the topic belongs to locked?
  76. Except in the case of a topic instance being provided to the
  77. constructor, all of these tuples are SQLA KeyedTuples.
  78. """
  79. if self._topic is not None:
  80. return self._topic.locked, self._topic.forum.locked
  81. elif self._post is not None:
  82. return self._post.topic.locked, self._post.topic.forum.locked
  83. elif self._topic_id is not None:
  84. return (
  85. Topic.query.join(Forum, Forum.id == Topic.forum_id)
  86. .filter(Topic.id == self._topic_id)
  87. .with_entities(Topic.locked, Forum.locked)
  88. .first()
  89. )
  90. else:
  91. return self._get_topic_from_request(request)
  92. def _get_topic_from_request(self, request):
  93. if current_topic:
  94. return current_topic.locked, current_forum.locked
  95. else:
  96. raise FlaskBBError("How did you get this to happen?")
  97. class ForumNotLocked(Requirement):
  98. def __init__(self, forum=None, forum_id=None):
  99. self._forum = forum
  100. self._forum_id = forum_id
  101. def fulfill(self, user, request):
  102. return self._is_forum_locked(request)
  103. def _is_forum_locked(self, request):
  104. forum = self._determine_forum(request)
  105. return not forum.locked
  106. def _determine_forum(self, request):
  107. if self._forum is not None:
  108. return self._forum
  109. elif self._forum_id is not None:
  110. return Forum.query.get(self._forum_id)
  111. else:
  112. return self._get_forum_from_request(request)
  113. def _get_forum_from_request(self, request):
  114. if current_forum:
  115. return current_forum.locked
  116. raise FlaskBBError
  117. class CanAccessForum(Requirement):
  118. def fulfill(self, user, request):
  119. if not current_forum:
  120. raise FlaskBBError('Could not load forum data')
  121. return set([g.id for g in current_forum.groups]) & set([g.id for g in user.groups])
  122. class CanAccessTopic(Requirement):
  123. def fulfill(self, user, request):
  124. if not current_forum:
  125. raise FlaskBBError('Could not load topic data')
  126. return set([g.id for g in current_forum.groups]) & set([g.id for g in user.groups])
  127. def IsAtleastModeratorInForum(forum_id=None, forum=None):
  128. return Or(IsAtleastSuperModerator, IsModeratorInForum(forum_id=forum_id,
  129. forum=forum))
  130. IsMod = And(IsAuthed(), Has('mod'))
  131. IsSuperMod = And(IsAuthed(), Has('super_mod'))
  132. IsAdmin = And(IsAuthed(), Has('admin'))
  133. IsAtleastModerator = Or(IsAdmin, IsSuperMod, IsMod)
  134. IsAtleastSuperModerator = Or(IsAdmin, IsSuperMod)
  135. CanBanUser = Or(IsAtleastSuperModerator, Has('mod_banuser'))
  136. CanEditUser = Or(IsAtleastSuperModerator, Has('mod_edituser'))
  137. CanEditPost = Or(IsAtleastSuperModerator,
  138. And(IsModeratorInForum(), Has('editpost')),
  139. And(IsSameUser(), Has('editpost'), TopicNotLocked()))
  140. CanDeletePost = CanEditPost
  141. CanPostReply = Or(And(Has('postreply'), TopicNotLocked()),
  142. IsModeratorInForum(),
  143. IsAtleastSuperModerator)
  144. CanPostTopic = Or(And(Has('posttopic'), ForumNotLocked()),
  145. IsAtleastSuperModerator,
  146. IsModeratorInForum())
  147. CanDeleteTopic = Or(IsAtleastSuperModerator,
  148. And(IsModeratorInForum(), Has('deletetopic')),
  149. And(IsSameUser(), Has('deletetopic'), TopicNotLocked()))
  150. # Template Allowances -- gross, I know
  151. def TplCanModerate(request):
  152. def _(user, forum):
  153. kwargs = {}
  154. if isinstance(forum, int):
  155. kwargs['forum_id'] = forum
  156. elif isinstance(forum, Forum):
  157. kwargs['forum'] = forum
  158. return IsAtleastModeratorInForum(**kwargs)(user, request)
  159. return _
  160. def TplCanPostReply(request):
  161. def _(user, topic=None):
  162. kwargs = {}
  163. if isinstance(topic, int):
  164. kwargs['topic_id'] = topic
  165. elif isinstance(topic, Topic):
  166. kwargs['topic'] = topic
  167. return Or(
  168. IsAtleastSuperModerator,
  169. IsModeratorInForum(),
  170. And(Has('postreply'), TopicNotLocked(**kwargs))
  171. )(user, request)
  172. return _
  173. def TplCanEditPost(request):
  174. def _(user, topic_or_post=None):
  175. kwargs = {}
  176. if isinstance(topic_or_post, int):
  177. kwargs['topic_id'] = topic_or_post
  178. elif isinstance(topic_or_post, Topic):
  179. kwargs['topic'] = topic_or_post
  180. elif isinstance(topic_or_post, Post):
  181. kwargs['post'] = topic_or_post
  182. return Or(
  183. IsAtleastSuperModerator,
  184. And(IsModeratorInForum(), Has('editpost')),
  185. And(
  186. IsSameUser(topic_or_post),
  187. Has('editpost'),
  188. TopicNotLocked(**kwargs)
  189. ),
  190. )(user, request)
  191. return _
  192. TplCanDeletePost = TplCanEditPost
  193. def TplCanPostTopic(request):
  194. def _(user, forum):
  195. kwargs = {}
  196. if isinstance(forum, int):
  197. kwargs['forum_id'] = forum
  198. elif isinstance(forum, Forum):
  199. kwargs['forum'] = forum
  200. return Or(
  201. IsAtleastSuperModerator,
  202. IsModeratorInForum(**kwargs),
  203. And(Has('posttopic'), ForumNotLocked(**kwargs))
  204. )(user, request)
  205. return _
  206. def TplCanDeleteTopic(request):
  207. def _(user, topic=None):
  208. kwargs = {}
  209. if isinstance(topic, int):
  210. kwargs['topic_id'] = topic
  211. elif isinstance(topic, Topic):
  212. kwargs['topic'] = topic
  213. return Or(
  214. IsAtleastSuperModerator,
  215. And(IsModeratorInForum(), Has('deletetopic')),
  216. And(IsSameUser(), Has('deletetopic'), TopicNotLocked(**kwargs))
  217. )(user, request)
  218. return _