acl.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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_poll_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_pin_threads'] = forms.ChoiceField(choices=(
  48. ('0', _("No")),
  49. ('1', _("Yes, to stickies")),
  50. ('2', _("Yes, to annoucements")),
  51. ))
  52. form.base_fields['can_edit_threads_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  53. form.base_fields['can_move_threads_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  54. form.base_fields['can_close_threads'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  55. form.base_fields['can_protect_posts'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  56. form.base_fields['can_delete_threads'] = forms.ChoiceField(choices=(
  57. ('0', _("No")),
  58. ('1', _("Yes, soft-delete")),
  59. ('2', _("Yes, hard-delete")),
  60. ))
  61. form.base_fields['can_delete_posts'] = forms.ChoiceField(choices=(
  62. ('0', _("No")),
  63. ('1', _("Yes, soft-delete")),
  64. ('2', _("Yes, hard-delete")),
  65. ))
  66. form.base_fields['can_delete_polls'] = forms.ChoiceField(choices=(
  67. ('0', _("No")),
  68. ('1', _("Yes, soft-delete")),
  69. ('2', _("Yes, hard-delete")),
  70. ))
  71. form.base_fields['can_delete_attachments'] = forms.BooleanField(widget=YesNoSwitch,initial=False,required=False)
  72. form.layout.append((
  73. _("Threads"),
  74. (
  75. ('can_read_threads', {'label': _("Can read threads")}),
  76. ('can_start_threads', {'label': _("Can start new threads")}),
  77. ('can_edit_own_threads', {'label': _("Can edit own threads")}),
  78. ('can_soft_delete_own_threads', {'label': _("Can soft-delete own threads")}),
  79. ),
  80. ),)
  81. form.layout.append((
  82. _("Posts"),
  83. (
  84. ('can_write_posts', {'label': _("Can write posts")}),
  85. ('can_edit_own_posts', {'label': _("Can edit own posts")}),
  86. ('can_soft_delete_own_posts', {'label': _("Can soft-delete own posts")}),
  87. ),
  88. ),)
  89. form.layout.append((
  90. _("Karma"),
  91. (
  92. ('can_upvote_posts', {'label': _("Can upvote posts")}),
  93. ('can_downvote_posts', {'label': _("Can downvote posts")}),
  94. ('can_see_posts_scores', {'label': _("Can see post score")}),
  95. ('can_see_votes', {'label': _("Can see who voted on post")}),
  96. ),
  97. ),)
  98. form.layout.append((
  99. _("Polls"),
  100. (
  101. ('can_make_polls', {'label': _("Can make polls")}),
  102. ('can_vote_in_polls', {'label': _("Can vote in polls")}),
  103. ('can_see_poll_votes', {'label': _("Can see who voted in poll")}),
  104. ),
  105. ),)
  106. form.layout.append((
  107. _("Attachments"),
  108. (
  109. ('can_see_attachments', {'label': _("Can see attachments")}),
  110. ('can_upload_attachments', {'label': _("Can upload attachments")}),
  111. ('can_download_attachments', {'label': _("Can download attachments")}),
  112. ('attachment_size', {'label': _("Max size of single attachment (in Kb)"), 'help_text': _("Enter zero for no limit.")}),
  113. ('attachment_limit', {'label': _("Max number of attachments per post"), 'help_text': _("Enter zero for no limit.")}),
  114. ),
  115. ),)
  116. form.layout.append((
  117. _("Moderation"),
  118. (
  119. ('can_approve', {'label': _("Can accept threads and posts")}),
  120. ('can_edit_labels', {'label': _("Can edit thread labels")}),
  121. ('can_see_changelog', {'label': _("Can see edits history")}),
  122. ('can_make_annoucements', {'label': _("Can make annoucements")}),
  123. ('can_pin_threads', {'label': _("Can change threads weight")}),
  124. ('can_edit_threads_posts', {'label': _("Can edit threads and posts")}),
  125. ('can_move_threads_posts', {'label': _("Can move, merge and split threads and posts")}),
  126. ('can_close_threads', {'label': _("Can close threads")}),
  127. ('can_protect_posts', {'label': _("Can protect posts"), 'help_text': _("Protected posts cannot be changed by their owners.")}),
  128. ('can_delete_threads', {'label': _("Can delete threads")}),
  129. ('can_delete_posts', {'label': _("Can delete posts")}),
  130. ('can_delete_polls', {'label': _("Can delete polls")}),
  131. ('can_delete_attachments', {'label': _("Can delete attachments")}),
  132. ),
  133. ),)
  134. class ThreadsACL(BaseACL):
  135. def get_role(self, forum):
  136. try:
  137. return self.acl[forum.pk]
  138. except KeyError:
  139. return {}
  140. def allow_thread_view(self, user, thread):
  141. try:
  142. forum_role = self.acl[thread.forum_id]
  143. if forum_role['can_read_threads'] == 0:
  144. raise ACLError403(_("You don't have permission to read threads in this forum."))
  145. if forum_role['can_read_threads'] == 1 and thread.weight < 2 and thread.start_poster_id != user.id:
  146. raise ACLError404()
  147. if thread.moderated and not (forum_role['can_approve'] or (user.is_authenticated() and user == thread.start_poster)):
  148. raise ACLError404()
  149. except KeyError:
  150. raise ACLError403(_("You don't have permission to read threads in this forum."))
  151. def allow_post_view(self, user, thread, post):
  152. forum_role = self.acl[thread.forum_id]
  153. if post.moderated and not (forum_role['can_approve'] or (user.is_authenticated() and user == post.user)):
  154. raise ACLError404()
  155. if post.deleted and not (forum_role['can_delete_posts'] or (user.is_authenticated() and user == post.user)):
  156. raise ACLError404()
  157. def get_readable_forums(self, acl):
  158. readable = []
  159. for forum in self.acl:
  160. if acl.forums.can_browse(forum) and self.acl[forum]['can_read_threads']:
  161. readable.append(forum)
  162. return readable
  163. def filter_threads(self, request, forum, queryset):
  164. try:
  165. forum_role = self.acl[forum.pk]
  166. if not forum_role['can_approve']:
  167. if request.user.is_authenticated():
  168. queryset = queryset.filter(Q(moderated=0) | Q(start_poster=request.user))
  169. else:
  170. queryset = queryset.filter(moderated=0)
  171. if forum_role['can_read_threads'] == 1:
  172. queryset = queryset.filter(Q(weight=2) | Q(start_poster_id=request.user.id))
  173. except KeyError:
  174. return False
  175. return queryset
  176. def filter_posts(self, request, thread, queryset):
  177. try:
  178. forum_role = self.acl[thread.forum.pk]
  179. if not forum_role['can_approve']:
  180. if request.user.is_authenticated():
  181. queryset = queryset.filter(Q(moderated=0) | Q(user=request.user))
  182. else:
  183. queryset = queryset.filter(moderated=0)
  184. except KeyError:
  185. return False
  186. return queryset
  187. def can_start_threads(self, forum):
  188. try:
  189. forum_role = self.acl[forum.pk]
  190. if forum_role['can_read_threads'] == 0 or forum_role['can_start_threads'] == 0:
  191. return False
  192. if forum.closed and forum_role['can_close_threads'] == 0:
  193. return False
  194. return True
  195. except KeyError:
  196. return False
  197. def allow_new_threads(self, forum):
  198. try:
  199. forum_role = self.acl[forum.pk]
  200. if forum_role['can_read_threads'] == 0 or forum_role['can_start_threads'] == 0:
  201. raise ACLError403(_("You don't have permission to start new threads in this forum."))
  202. if forum.closed and forum_role['can_close_threads'] == 0:
  203. raise ACLError403(_("This forum is closed, you can't start new threads in it."))
  204. except KeyError:
  205. raise ACLError403(_("You don't have permission to start new threads in this forum."))
  206. def can_edit_thread(self, user, forum, thread, post):
  207. try:
  208. forum_role = self.acl[thread.forum_id]
  209. if forum_role['can_close_threads'] == 0 and (forum.closed or thread.closed):
  210. return False
  211. if forum_role['can_edit_threads_posts']:
  212. return True
  213. if forum_role['can_edit_own_threads'] and not post.protected and post.user_id == user.pk:
  214. return True
  215. return False
  216. except KeyError:
  217. return False
  218. def allow_thread_edit(self, user, forum, thread, post):
  219. try:
  220. forum_role = self.acl[thread.forum_id]
  221. if not forum_role['can_close_threads']:
  222. if forum.closed:
  223. raise ACLError403(_("You can't edit threads in closed forums."))
  224. if thread.closed:
  225. raise ACLError403(_("You can't edit closed threads."))
  226. if not forum_role['can_edit_threads_posts']:
  227. if post.user_id != user.pk:
  228. raise ACLError403(_("You can't edit other members threads."))
  229. if not forum_role['can_edit_own_threads']:
  230. raise ACLError403(_("You can't edit your threads."))
  231. if post.protected:
  232. raise ACLError403(_("This thread is protected, you cannot edit it."))
  233. except KeyError:
  234. raise ACLError403(_("You don't have permission to edit threads in this forum."))
  235. def can_reply(self, forum, thread):
  236. try:
  237. forum_role = self.acl[forum.pk]
  238. if forum_role['can_write_posts'] == 0:
  239. return False
  240. if (forum.closed or thread.closed) and forum_role['can_close_threads'] == 0:
  241. return False
  242. return True
  243. except KeyError:
  244. return False
  245. def allow_reply(self, forum, thread):
  246. try:
  247. forum_role = self.acl[thread.forum.pk]
  248. if forum_role['can_write_posts'] == 0:
  249. raise ACLError403(_("You don't have permission to write replies in this forum."))
  250. if forum_role['can_close_threads'] == 0:
  251. if forum.closed:
  252. raise ACLError403(_("You can't write replies in closed forums."))
  253. if thread.closed:
  254. raise ACLError403(_("You can't write replies in closed threads."))
  255. except KeyError:
  256. raise ACLError403(_("You don't have permission to write replies in this forum."))
  257. def can_edit_reply(self, user, forum, thread, post):
  258. try:
  259. forum_role = self.acl[thread.forum_id]
  260. if forum_role['can_close_threads'] == 0 and (forum.closed or thread.closed):
  261. return False
  262. if forum_role['can_edit_threads_posts']:
  263. return True
  264. if forum_role['can_edit_own_posts'] and not post.protected and post.user_id == user.pk:
  265. return True
  266. return False
  267. except KeyError:
  268. return False
  269. def allow_reply_edit(self, user, forum, thread, post):
  270. try:
  271. forum_role = self.acl[thread.forum_id]
  272. if not forum_role['can_close_threads']:
  273. if forum.closed:
  274. raise ACLError403(_("You can't edit replies in closed forums."))
  275. if thread.closed:
  276. raise ACLError403(_("You can't edit replies in closed threads."))
  277. if not forum_role['can_edit_threads_posts']:
  278. if post.user_id != user.pk:
  279. raise ACLError403(_("You can't edit other members replies."))
  280. if not forum_role['can_edit_own_posts']:
  281. raise ACLError403(_("You can't edit your replies."))
  282. if post.protected:
  283. raise ACLError403(_("This reply is protected, you cannot edit it."))
  284. except KeyError:
  285. raise ACLError403(_("You don't have permission to edit replies in this forum."))
  286. def can_see_changelog(self, user, forum, post):
  287. try:
  288. forum_role = self.acl[forum.pk]
  289. return forum_role['can_see_changelog'] or user.pk == post.user_id
  290. except KeyError:
  291. return False
  292. def allow_changelog_view(self, user, forum, post):
  293. try:
  294. forum_role = self.acl[forum.pk]
  295. if not (forum_role['can_see_changelog'] or user.pk == post.user_id):
  296. raise ACLError403(_("You don't have permission to see history of changes made to this post."))
  297. except KeyError:
  298. raise ACLError403(_("You don't have permission to see history of changes made to this post."))
  299. def can_make_revert(self, forum, thread):
  300. try:
  301. forum_role = self.acl[forum.pk]
  302. if not forum_role['can_close_threads'] and (forum.closed or thread.closed):
  303. return False
  304. return forum_role['can_edit_threads_posts']
  305. except KeyError:
  306. return False
  307. def allow_revert(self, forum, thread):
  308. try:
  309. forum_role = self.acl[forum.pk]
  310. if not forum_role['can_close_threads']:
  311. if forum.closed:
  312. raise ACLError403(_("You can't make reverts in closed forums."))
  313. if thread.closed:
  314. raise ACLError403(_("You can't make reverts in closed threads."))
  315. if not forum_role['can_edit_threads_posts']:
  316. raise ACLError403(_("You don't have permission to make reverts in this forum."))
  317. except KeyError:
  318. raise ACLError403(_("You don't have permission to make reverts in this forum."))
  319. def can_mod_threads(self, forum):
  320. try:
  321. forum_role = self.acl[forum.pk]
  322. return (
  323. forum_role['can_approve']
  324. or forum_role['can_pin_threads']
  325. or forum_role['can_move_threads_posts']
  326. or forum_role['can_close_threads']
  327. or forum_role['can_delete_threads']
  328. )
  329. except KeyError:
  330. return False
  331. def can_mod_posts(self, thread):
  332. try:
  333. forum_role = self.acl[thread.forum.pk]
  334. return (
  335. forum_role['can_edit_threads_posts']
  336. or forum_role['can_move_threads_posts']
  337. or forum_role['can_close_threads']
  338. or forum_role['can_delete_threads']
  339. or forum_role['can_delete_posts']
  340. )
  341. except KeyError:
  342. return False
  343. def can_mod_thread(self, thread):
  344. pass
  345. def can_approve(self, forum):
  346. try:
  347. forum_role = self.acl[forum.pk]
  348. return forum_role['can_approve']
  349. except KeyError:
  350. return False
  351. def build_forums(acl, perms, forums, forum_roles):
  352. acl.threads = ThreadsACL()
  353. for forum in forums:
  354. forum_role = {
  355. 'can_read_threads': 0,
  356. 'can_start_threads': 0,
  357. 'can_edit_own_threads': False,
  358. 'can_soft_delete_own_threads': False,
  359. 'can_write_posts': 0,
  360. 'can_edit_own_posts': False,
  361. 'can_soft_delete_own_posts': False,
  362. 'can_upvote_posts': False,
  363. 'can_downvote_posts': False,
  364. 'can_see_posts_scores': 0,
  365. 'can_see_votes': False,
  366. 'can_make_polls': False,
  367. 'can_vote_in_polls': False,
  368. 'can_see_poll_votes': False,
  369. 'can_see_attachments': False,
  370. 'can_upload_attachments': False,
  371. 'can_download_attachments': False,
  372. 'attachment_size': 100,
  373. 'attachment_limit': 3,
  374. 'can_approve': False,
  375. 'can_edit_labels': False,
  376. 'can_see_changelog': False,
  377. 'can_make_annoucements': False,
  378. 'can_pin_threads': 0,
  379. 'can_edit_threads_posts': False,
  380. 'can_move_threads_posts': False,
  381. 'can_close_threads': False,
  382. 'can_protect_posts': False,
  383. 'can_delete_threads': 0,
  384. 'can_delete_posts': 0,
  385. 'can_delete_polls': 0,
  386. 'can_delete_attachments': False,
  387. }
  388. for perm in perms:
  389. try:
  390. role = forum_roles[perm['forums'][forum.pk]]
  391. for p in forum_role:
  392. try:
  393. if p in ['attachment_size', 'attachment_limit'] and role[p] == 0:
  394. forum_role[p] = 0
  395. elif int(role[p]) > forum_role[p]:
  396. forum_role[p] = int(role[p])
  397. except KeyError:
  398. pass
  399. except KeyError:
  400. pass
  401. acl.threads.acl[forum.pk] = forum_role