requirements.py 8.8 KB

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