acl.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. from django import forms
  2. from django.db import models
  3. from django.db.models import Q
  4. from django.utils.translation import ugettext_lazy as _
  5. from misago.acl.builder import BaseACL
  6. from misago.acl.utils import ACLError403, ACLError404
  7. from misago.forms import YesNoSwitch
  8. def make_forum_form(request, role, form):
  9. form.base_fields['can_read_threads'] = forms.ChoiceField(choices=(
  10. ('0', _("No")),
  11. ('1', _("Yes, owned")),
  12. ('2', _("Yes, all")),
  13. ))
  14. form.base_fields['can_start_threads'] = forms.ChoiceField(choices=(
  15. ('0', _("No")),
  16. ('1', _("Yes, with moderation")),
  17. ('2', _("Yes")),
  18. ))
  19. form.base_fields['can_edit_own_threads'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  20. form.base_fields['can_soft_delete_own_threads'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  21. form.base_fields['can_write_posts'] = forms.ChoiceField(choices=(
  22. ('0', _("No")),
  23. ('1', _("Yes, with moderation")),
  24. ('2', _("Yes")),
  25. ))
  26. form.base_fields['can_edit_own_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  27. form.base_fields['can_soft_delete_own_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  28. form.base_fields['can_upvote_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  29. form.base_fields['can_downvote_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  30. form.base_fields['can_see_posts_scores'] = forms.ChoiceField(choices=(
  31. ('0', _("No")),
  32. ('1', _("Yes, final score")),
  33. ('2', _("Yes, both up and down-votes")),
  34. ))
  35. form.base_fields['can_see_votes'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  36. form.base_fields['can_make_polls'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  37. form.base_fields['can_vote_in_polls'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  38. form.base_fields['can_see_votes'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  39. form.base_fields['can_see_attachments'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  40. form.base_fields['can_upload_attachments'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  41. form.base_fields['can_download_attachments'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  42. form.base_fields['attachment_size'] = forms.IntegerField(min_value=0,initial=100)
  43. form.base_fields['attachment_limit'] = forms.IntegerField(min_value=0,initial=3)
  44. form.base_fields['can_approve'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  45. form.base_fields['can_edit_labels'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  46. form.base_fields['can_see_changelog'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  47. form.base_fields['can_make_annoucements'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  48. form.base_fields['can_pin_threads'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  49. form.base_fields['can_edit_threads_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  50. form.base_fields['can_move_threads_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  51. form.base_fields['can_close_threads'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  52. form.base_fields['can_protect_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  53. form.base_fields['can_delete_threads'] = forms.ChoiceField(choices=(
  54. ('0', _("No")),
  55. ('1', _("Yes, soft-delete")),
  56. ('2', _("Yes, hard-delete")),
  57. ))
  58. form.base_fields['can_delete_posts'] = forms.ChoiceField(choices=(
  59. ('0', _("No")),
  60. ('1', _("Yes, soft-delete")),
  61. ('2', _("Yes, hard-delete")),
  62. ))
  63. form.base_fields['can_delete_polls'] = forms.ChoiceField(choices=(
  64. ('0', _("No")),
  65. ('1', _("Yes, soft-delete")),
  66. ('2', _("Yes, hard-delete")),
  67. ))
  68. form.base_fields['can_delete_attachments'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  69. form.layout.append((
  70. _("Threads"),
  71. (
  72. ('can_read_threads', {'label': _("Can read threads")}),
  73. ('can_start_threads', {'label': _("Can start new threads")}),
  74. ('can_edit_own_threads', {'label': _("Can edit own threads")}),
  75. ('can_soft_delete_own_threads', {'label': _("Can soft-delete own threads")}),
  76. ),
  77. ),)
  78. form.layout.append((
  79. _("Posts"),
  80. (
  81. ('can_write_posts', {'label': _("Can write posts")}),
  82. ('can_edit_own_posts', {'label': _("Can edit own posts")}),
  83. ('can_soft_delete_own_posts', {'label': _("Can soft-delete own posts")}),
  84. ),
  85. ),)
  86. form.layout.append((
  87. _("Karma"),
  88. (
  89. ('can_upvote_posts', {'label': _("Can upvote posts")}),
  90. ('can_downvote_posts', {'label': _("Can downvote posts")}),
  91. ('can_see_posts_scores', {'label': _("Can see post score")}),
  92. ('can_see_votes', {'label': _("Can see who voted on post")}),
  93. ),
  94. ),)
  95. form.layout.append((
  96. _("Polls"),
  97. (
  98. ('can_make_polls', {'label': _("Can make polls")}),
  99. ('can_vote_in_polls', {'label': _("Can vote in polls")}),
  100. ('can_see_votes', {'label': _("Can see who voted in poll")}),
  101. ),
  102. ),)
  103. form.layout.append((
  104. _("Attachments"),
  105. (
  106. ('can_see_attachments', {'label': _("Can see attachments")}),
  107. ('can_upload_attachments', {'label': _("Can upload attachments")}),
  108. ('can_download_attachments', {'label': _("Can download attachments")}),
  109. ('attachment_size', {'label': _("Max size of single attachment (in Kb)"), 'help_text': _("Enter zero for no limit.")}),
  110. ('attachment_limit', {'label': _("Max number of attachments per post"), 'help_text': _("Enter zero for no limit.")}),
  111. ),
  112. ),)
  113. form.layout.append((
  114. _("Moderation"),
  115. (
  116. ('can_approve', {'label': _("Can accept threads and posts")}),
  117. ('can_edit_labels', {'label': _("Can edit thread labels")}),
  118. ('can_see_changelog', {'label': _("Can see edits history")}),
  119. ('can_make_annoucements', {'label': _("Can make annoucements")}),
  120. ('can_pin_threads', {'label': _("Can make threads sticky")}),
  121. ('can_edit_threads_posts', {'label': _("Can edit threads and posts")}),
  122. ('can_move_threads_posts', {'label': _("Can move, merge and split threads and posts")}),
  123. ('can_close_threads', {'label': _("Can close threads")}),
  124. ('can_protect_posts', {'label': _("Can protect posts"), 'help_text': _("Protected posts cannot be changed by their owners.")}),
  125. ('can_delete_threads', {'label': _("Can delete threads")}),
  126. ('can_delete_posts', {'label': _("Can delete posts")}),
  127. ('can_delete_polls', {'label': _("Can delete polls")}),
  128. ('can_delete_attachments', {'label': _("Can delete attachments")}),
  129. ),
  130. ),)
  131. class ThreadsACL(BaseACL):
  132. def get_role(self, forum):
  133. try:
  134. return self.acl[forum.pk]
  135. except KeyError:
  136. return {}
  137. def allow_thread_view(self, user, thread):
  138. try:
  139. forum_role = self.acl[thread.forum.pk]
  140. if forum_role['can_read_threads'] == 0:
  141. raise ACLError403(_("You don't have permission to read threads in this forum."))
  142. if thread.moderated and not (forum_role['can_approve'] or (user.is_authenticated() and user == thread.start_poster)):
  143. raise ACLError404()
  144. except KeyError:
  145. raise ACLError403(_("You don't have permission to read threads in this forum."))
  146. def filter_threads(self, request, forum, queryset):
  147. try:
  148. forum_role = self.acl[forum.pk]
  149. if not forum_role['can_approve']:
  150. if request.user.is_authenticated():
  151. queryset = queryset.filter(Q(moderated=0) | Q(start_poster=request.user))
  152. else:
  153. queryset = queryset.filter(moderated=0)
  154. except KeyError:
  155. return False
  156. return queryset
  157. def filter_posts(self, request, thread, queryset):
  158. try:
  159. forum_role = self.acl[thread.forum.pk]
  160. if not forum_role['can_approve']:
  161. if request.user.is_authenticated():
  162. queryset = queryset.filter(Q(moderated=0) | Q(user=request.user))
  163. else:
  164. queryset = queryset.filter(moderated=0)
  165. except KeyError:
  166. return False
  167. return queryset
  168. def can_start_threads(self, forum):
  169. try:
  170. forum_role = self.acl[forum.pk]
  171. if forum_role['can_read_threads'] == 0 or forum_role['can_start_threads'] == 0:
  172. return False
  173. if forum.closed and forum_role['can_close_threads'] == 0:
  174. return False
  175. return True
  176. except KeyError:
  177. return False
  178. def allow_new_threads(self, forum):
  179. try:
  180. forum_role = self.acl[forum.pk]
  181. if forum_role['can_read_threads'] == 0 or forum_role['can_start_threads'] == 0:
  182. raise ACLError403(_("You don't have permission to start new threads in this forum."))
  183. if forum.closed and forum_role['can_close_threads'] == 0:
  184. raise ACLError403(_("This forum is closed, you can't start new threads in it."))
  185. except KeyError:
  186. raise ACLError403(_("You don't have permission to start new threads in this forum."))
  187. def can_reply(self, thread):
  188. try:
  189. forum_role = self.acl[thread.forum.pk]
  190. if forum_role['can_write_posts'] == 0:
  191. return False
  192. if thread.closed and forum_role['can_close_threads'] == 0:
  193. return False
  194. return True
  195. except KeyError:
  196. return False
  197. def allow_reply(self, thread):
  198. try:
  199. forum_role = self.acl[thread.forum.pk]
  200. if forum_role['can_write_posts'] == 0:
  201. raise ACLError403(_("You don't have permission to write replies in this forum."))
  202. if forum_role['can_close_threads'] == 0:
  203. if forum.closed:
  204. raise ACLError403(_("You can't write replies in closed forums."))
  205. if thread.closed:
  206. raise ACLError403(_("You can't write replies in closed threads."))
  207. except KeyError:
  208. raise ACLError403(_("You don't have permission to write replies in this forum."))
  209. def can_approve(self, forum):
  210. try:
  211. forum_role = self.acl[forum.pk]
  212. return forum_role['can_approve']
  213. except KeyError:
  214. return False
  215. def can_mod_threads(self, forum):
  216. try:
  217. forum_role = self.acl[forum.pk]
  218. return (
  219. forum_role['can_approve']
  220. or forum_role['can_make_annoucements']
  221. or forum_role['can_pin_threads']
  222. or forum_role['can_move_threads_posts']
  223. or forum_role['can_close_threads']
  224. or forum_role['can_delete_threads']
  225. )
  226. except KeyError:
  227. return False
  228. def can_mod_posts(self, thread):
  229. try:
  230. forum_role = self.acl[thread.forum.pk]
  231. return (
  232. forum_role['can_edit_threads_posts']
  233. or forum_role['can_move_threads_posts']
  234. or forum_role['can_close_threads']
  235. or forum_role['can_delete_threads']
  236. or forum_role['can_delete_posts']
  237. )
  238. except KeyError:
  239. return False
  240. def can_mod_thread(self, thread):
  241. pass
  242. def build_forums(acl, perms, forums, forum_roles):
  243. acl.threads = ThreadsACL()
  244. for forum in forums:
  245. forum_role = {
  246. 'can_read_threads': 0,
  247. 'can_start_threads': 0,
  248. 'can_edit_own_threads': False,
  249. 'can_soft_delete_own_threads': False,
  250. 'can_write_posts': 0,
  251. 'can_edit_own_posts': False,
  252. 'can_soft_delete_own_posts': False,
  253. 'can_upvote_posts': False,
  254. 'can_downvote_posts': False,
  255. 'can_see_posts_scores': 0,
  256. 'can_see_votes': False,
  257. 'can_make_polls': False,
  258. 'can_vote_in_polls': False,
  259. 'can_see_votes': False,
  260. 'can_see_attachments': False,
  261. 'can_upload_attachments': False,
  262. 'can_download_attachments': False,
  263. 'attachment_size': 100,
  264. 'attachment_limit': 3,
  265. 'can_approve': False,
  266. 'can_edit_labels': False,
  267. 'can_see_changelog': False,
  268. 'can_make_annoucements': False,
  269. 'can_pin_threads': False,
  270. 'can_edit_threads_posts': False,
  271. 'can_move_threads_posts': False,
  272. 'can_close_threads': False,
  273. 'can_protect_posts': False,
  274. 'can_delete_threads': 0,
  275. 'can_delete_posts': 0,
  276. 'can_delete_polls': 0,
  277. 'can_delete_attachments': False,
  278. }
  279. for perm in perms:
  280. try:
  281. role = forum_roles[perm['forums'][forum.pk]]
  282. for p in forum_role:
  283. if p in ['attachment_size', 'attachment_limit'] and role[p] == 0:
  284. forum_role[p] = 0
  285. elif int(role[p]) > forum_role[p]:
  286. forum_role[p] = int(role[p])
  287. except KeyError:
  288. pass
  289. acl.threads.acl[forum.pk] = forum_role