|
1 from django.conf import settings |
|
2 from django.contrib.auth import REDIRECT_FIELD_NAME |
|
3 from django.contrib.auth.models import Permission |
|
4 from django.contrib.contenttypes.models import ContentType |
|
5 from django.shortcuts import get_object_or_404 |
|
6 from django.core.urlresolvers import reverse |
|
7 from django.http import HttpResponseRedirect |
|
8 from django.utils.http import urlquote |
|
9 from django.db.models import Q |
|
10 |
|
11 import logging |
|
12 |
|
13 from cm.models import * |
|
14 from cm import cm_settings |
|
15 from cm.exception import UnauthorizedException |
|
16 |
|
17 def get_request_user(request): |
|
18 if request and request.user and not request.user.is_anonymous(): |
|
19 user = request.user |
|
20 else: |
|
21 user = None |
|
22 return user |
|
23 |
|
24 ## Permission functions |
|
25 class FakeRequest(object): |
|
26 def __init__(self, user): |
|
27 self.user = user |
|
28 |
|
29 def user_has_perm(user, perm_name, text=None): |
|
30 return has_perm(FakeRequest(user),perm_name, text) |
|
31 |
|
32 def has_perm(request, perm_name, text=None): |
|
33 # bypass sec if NO_SECURITY |
|
34 if cm_settings.NO_SECURITY: |
|
35 return True |
|
36 |
|
37 # make sure perm exist |
|
38 assert Permission.objects.get(codename=perm_name) |
|
39 |
|
40 user = get_request_user(request) |
|
41 |
|
42 if user and user.is_staff: |
|
43 return True |
|
44 |
|
45 if not text: |
|
46 return UserRole.objects.filter(user=user, text=None).filter(Q(role__permissions__codename__exact=perm_name)).count() != 0 |
|
47 else: |
|
48 # local role only ADDS permissions: |
|
49 # either a global or a local role with appropriate permissions |
|
50 #return UserRole.objects.filter(user=user).filter(Q(text=text) | Q(text=None)).filter(Q(role__permissions__codename__exact=perm_name)).count() != 0 |
|
51 |
|
52 # local role OVERRIDES global role: |
|
53 if UserRole.objects.filter(Q(user=user),Q(text=text),~Q(role=None)): # if non void local role |
|
54 return UserRole.objects.filter(user=user).filter(text=text).filter(Q(role__permissions__codename__exact=perm_name)).count() != 0 |
|
55 else: |
|
56 return UserRole.objects.filter(user=user).filter(text=None).filter(Q(role__permissions__codename__exact=perm_name)).count() != 0 |
|
57 |
|
58 def has_own_perm(request, perm_name, text, comment): |
|
59 |
|
60 user = get_request_user(request) |
|
61 |
|
62 if not user: |
|
63 return False |
|
64 |
|
65 # bypass sec if NO_SECURITY |
|
66 if cm_settings.NO_SECURITY: |
|
67 return True |
|
68 |
|
69 # make sure perm exist |
|
70 assert Permission.objects.get(codename=perm_name) |
|
71 |
|
72 # 2 special cases for comment own edition: |
|
73 |
|
74 # 1 |
|
75 # if perm = can_edit_own_comment and |
|
76 # text is a priori moderated and |
|
77 # comment is approved and |
|
78 # don't have moderation rights and |
|
79 if comment and comment.state == 'approved' and \ |
|
80 perm_name == 'can_edit_comment_own' and \ |
|
81 text.last_text_version.mod_posteriori == False and \ |
|
82 not has_perm(request, 'can_manage_text', text=text): |
|
83 return False |
|
84 |
|
85 # 2 |
|
86 # if perm = can_edit_own_comment and and |
|
87 # text is a posteriori moderated and |
|
88 # there is a reply |
|
89 # don't have moderation rights and |
|
90 if comment and comment.state == 'approved' and \ |
|
91 perm_name == 'can_edit_comment_own' and \ |
|
92 text.last_text_version.mod_posteriori == True and \ |
|
93 comment.comment_set.count() != 0 and \ |
|
94 not has_perm(request, 'can_manage_text', text=text): |
|
95 return False |
|
96 |
|
97 return (comment.user == request.user and has_perm(request, perm_name, text=text)) |
|
98 |
|
99 def is_authenticated(request): |
|
100 # We customize this to be able to monkey patch it if needed |
|
101 return request.user.is_authenticated() |
|
102 |
|
103 |
|
104 ## Content access functions |
|
105 |
|
106 def get_texts_with_perm(request, perm_name): |
|
107 assert Permission.objects.get(codename=perm_name) |
|
108 |
|
109 user = get_request_user(request) |
|
110 |
|
111 if user and user.is_staff: |
|
112 return Text.objects.all() |
|
113 |
|
114 # local role only ADDS permissions: |
|
115 ## global perm |
|
116 # if UserRole.objects.filter(text=None).filter(role__permissions__codename__exact=perm_name).filter(Q(user=user) | Q(user=None) ).count() != 0: |
|
117 # return Text.objects.all().distinct() |
|
118 ## only texts with role with perm |
|
119 #else: |
|
120 # return Text.objects.filter(Q(userrole__role__permissions__codename__exact=perm_name), Q(userrole__user=user) | Q(userrole__user=None)).distinct() |
|
121 |
|
122 # local role OVERRIDES global role: |
|
123 texts_with_local_role = Text.objects.filter(userrole__in=UserRole.objects.filter(user=user).filter(~Q(role=None))) |
|
124 #Text.objects.filter(Q(userrole__user=user) & ~Q(userrole__role=None)) |
|
125 texts_without_local_role = Text.objects.exclude(id__in=texts_with_local_role) |
|
126 |
|
127 texts_with_local_role_with_perm = Text.objects.filter(id__in=texts_with_local_role).filter(Q(userrole__role__permissions__codename__exact=perm_name), Q(userrole__user=user) | Q(userrole__user=None)).distinct() |
|
128 |
|
129 # global perm? |
|
130 if UserRole.objects.filter(text=None).filter(role__permissions__codename__exact=perm_name).filter(Q(user=user) | Q(user=None) ).count() != 0: |
|
131 texts_without_local_role_with_perm = Text.objects.filter(id__in=texts_without_local_role) |
|
132 else: |
|
133 texts_without_local_role_with_perm = [] |
|
134 |
|
135 ids = set([t.id for t in texts_with_local_role_with_perm]).union(set([t.id for t in texts_without_local_role_with_perm])) |
|
136 return Text.objects.filter(id__in=ids) |
|
137 |
|
138 def get_viewable_comments(request, comments, text, order_by=('created',)): |
|
139 """ |
|
140 Get comments visibles by user |
|
141 comments: queryset |
|
142 """ |
|
143 user = get_request_user(request) |
|
144 |
|
145 if user and has_perm(request, 'can_view_unapproved_comment', text=text): |
|
146 return list(comments.order_by(*order_by)) |
|
147 else: |
|
148 if has_perm(request, 'can_view_approved_comment', text=text): |
|
149 visible_comments = comments.filter(state = 'approved').order_by(*order_by) |
|
150 # filter comments with a non visible (i.e. moderated) comment in the above thread |
|
151 comments_thread_viewable = [c for c in visible_comments if c.is_thread_full_visible()] |
|
152 return comments_thread_viewable |
|
153 elif user and has_perm(request, 'can_view_comment_own', text=text): |
|
154 visible_comments = comments.filter(user=user).order_by(*order_by) |
|
155 # filter comments with a non visible (i.e. moderated) comment in the above thread |
|
156 comments_thread_viewable = [c for c in visible_comments if c.is_thread_full_visible()] |
|
157 return comments_thread_viewable |
|
158 else: |
|
159 return [] |
|
160 |
|
161 def get_viewable_activities(request=None, act_types={}, text=None): |
|
162 """ |
|
163 Get activities user in request is allowed to see |
|
164 """ |
|
165 from cm.security import has_perm, get_texts_with_perm, get_viewable_comments |
|
166 |
|
167 selected_activities = reduce(list.__add__,[Activity.VIEWABLE_ACTIVITIES[k] for k in act_types.keys() if act_types[k]], []) |
|
168 |
|
169 activities = Activity.objects.filter(type__in=selected_activities) |
|
170 if text: |
|
171 activities = activities.filter(text=text) |
|
172 |
|
173 if not has_perm(request, 'can_manage_workspace'): |
|
174 texts = get_texts_with_perm(request, 'can_view_text') |
|
175 activities = activities.filter(Q(text__in=texts)) |
|
176 |
|
177 comments = [] |
|
178 [comments.extend(get_viewable_comments(request, t.last_text_version.comment_set.all(), t)) for t in texts] |
|
179 |
|
180 activities = activities.filter(Q(comment__in=comments) | Q(comment=None)) |
|
181 return activities.order_by('-created') |
|
182 |
|
183 |
|
184 # won't need to be overridden, should it be moved to another file ? |
|
185 def list_viewable_comments(request, comments_list, text): |
|
186 ret = [] |
|
187 for comment in comments_list : |
|
188 ret += [comment] + list_viewable_comments(request, get_viewable_comments(request, comment.comment_set, text), text) |
|
189 return ret |
|
190 |
|
191 |
|
192 # decorators (simple wrappers around above functions) |
|
193 def has_global_perm(perm_name, must_be_logged_in=False, redirect_field_name=REDIRECT_FIELD_NAME): |
|
194 def _dec(view_func): |
|
195 def _check_global_perm(request, *args, **kwargs): |
|
196 if must_be_logged_in and not is_authenticated(request): |
|
197 login_url = reverse('login') |
|
198 return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, urlquote(request.get_full_path()))) |
|
199 |
|
200 if has_perm(request, perm_name, text=None): |
|
201 return view_func(request, *args, **kwargs) |
|
202 |
|
203 raise UnauthorizedException('No global perm %s' % perm_name) |
|
204 _check_global_perm.__doc__ = view_func.__doc__ |
|
205 _check_global_perm.__dict__ = view_func.__dict__ |
|
206 |
|
207 return _check_global_perm |
|
208 return _dec |
|
209 |
|
210 def has_perm_on_text(perm_name, must_be_logged_in=False, redirect_field_name=REDIRECT_FIELD_NAME): |
|
211 """ |
|
212 decorator protection checking for perm for logged in user |
|
213 force logged in (i.e. redirect to connection screen if not if must_be_logged_in |
|
214 """ |
|
215 def _dec(view_func): |
|
216 def _check_local_perm(request, *args, **kwargs): |
|
217 if cm_settings.NO_SECURITY: |
|
218 return view_func(request, *args, **kwargs) |
|
219 |
|
220 if must_be_logged_in and not is_authenticated(request): |
|
221 login_url = reverse('login') |
|
222 return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, urlquote(request.get_full_path()))) |
|
223 |
|
224 if 'key' in kwargs: |
|
225 text = get_object_or_404(Text, key=kwargs['key']) |
|
226 else: |
|
227 raise Exception('no security check possible') |
|
228 |
|
229 if has_perm(request, perm_name, text=text): |
|
230 return view_func(request, *args, **kwargs) |
|
231 #else: |
|
232 # TODO: (? useful ?) if some user have the perm and not logged-in : redirect to login |
|
233 #if not request.user.is_authenticated() and number_has_perm_on_text(permission, text_id) > 0: |
|
234 # return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, urlquote(request.get_full_path()))) |
|
235 # else : unauthorized |
|
236 |
|
237 raise UnauthorizedException('No perm %s' % perm_name) |
|
238 _check_local_perm.__doc__ = view_func.__doc__ |
|
239 _check_local_perm.__dict__ = view_func.__dict__ |
|
240 |
|
241 return _check_local_perm |
|
242 return _dec |
|
243 |
|
244 def has_perm_on_comment(perm_name): |
|
245 """ |
|
246 decorator protection checking for perm for logged in user on to comment |
|
247 perm_name: 'virtual' permission name |
|
248 """ |
|
249 def _dec(view_func): |
|
250 def _check_local_perm(request, *args, **kwargs): |
|
251 if cm_settings.NO_SECURITY: |
|
252 return view_func(request, *args, **kwargs) |
|
253 |
|
254 if 'key' in kwargs: |
|
255 text = get_object_or_404(Text, key=kwargs['key']) |
|
256 # first try permission on text |
|
257 if has_perm(request, perm_name, text=text) : |
|
258 return view_func(request, *args, **kwargs) |
|
259 if 'comment_key' in kwargs: |
|
260 comment = get_object_or_404(Comment, key=kwargs['comment_key']) |
|
261 if has_own_perm(request, perm_name + "_own", text, comment) : |
|
262 return view_func(request, *args, **kwargs) |
|
263 else: |
|
264 raise Exception('no security check possible: no comment key') |
|
265 else: |
|
266 raise Exception('no security check possible: no text key') |
|
267 |
|
268 raise UnauthorizedException('No perm %s on comment' % perm_name) |
|
269 _check_local_perm.__doc__ = view_func.__doc__ |
|
270 _check_local_perm.__dict__ = view_func.__dict__ |
|
271 |
|
272 return _check_local_perm |
|
273 return _dec |
|
274 |
|
275 |
|
276 |