1 '''
2 Created on Jun 1, 2010
3
4 @author: jnaous
5 '''
6 from django.contrib.contenttypes.models import ContentType
7 from django.http import Http404
8 from expedient.common.permissions.shortcuts import must_have_permission,\
9 give_permission_to
10 from expedient.common.middleware import threadlocals
11 from expedient.common.permissions.models import ObjectPermission
12
14 '''
15 Get the user from the request. This function is helpful when
16 using the require_*_permission_for_view decorators.
17
18 For example::
19
20 @require_objs_permissions_for_view(
21 ["can_view_obj_detail"],
22 get_user_from_req,
23 get_objects_from_filter_func(Obj, 1),
24 ["GET"],
25 )
26 def view_obj_detail(request, obj_id):
27 ...
28
29 @param request: the request object
30 @type request: C{HttpRequest}
31 '''
32 return request.user
33
35 """
36 Returns a function that can be used for the require_*_permission_for_view
37 decorators to get a queryset from some argument.
38
39 The returned function has a signature (*args, **kwargs) and mainly does
40 the following::
41
42 klass.objects.filter(**{filter: arg})
43
44 where C{arg} is obtained from the arguments. If C{index} is an
45 C{int}, C{arg} is assumed to be positional. Otherwise, it is assumed to be
46 a keyword.
47
48 For example::
49
50 @require_obj_permission_for_view(
51 ["can_view_obj_detail"],
52 get_user_from_req,
53 get_queryset(Obj, 1),
54 ["GET"],
55 )
56 def view_obj_detail(request, obj_id):
57 ...
58
59 @param klass: The class of the object to be returned.
60 @type klass: class
61 @param index: location of the id in the arguments when the arguments are
62 given as (*args, **kwargs).
63 @type index: C{int} for positional, hashable for keyword.
64 @keyword filter: a filter to be used for obtaining the object.
65 @type filter: C{str}
66
67 @return: A callable that returns an object from (*args, **kwargs)
68 """
69
70 def wrapper(*args, **kwargs):
71 if type(index) == int:
72 arg = args[index]
73 else:
74 arg = kwargs[index]
75 return klass.objects.filter(**{filter: arg})
76
77 return wrapper
78
80 """
81 Same as L{get_queryset} but also calls the C{as_leaf_class} function
82 on the first element in the queryset and returns a queryset with the
83 returned object's class.
84 """
85 def wrapper(*args, **kwargs):
86 if type(index) == int:
87 arg = args[index]
88 else:
89 arg = kwargs[index]
90
91 parent_qs = parent_klass.objects.filter(**{filter: arg})
92 parents = list(parent_qs)
93 if parents:
94 ids = [p.id for p in parents]
95 return parents[0].as_leaf_class().\
96 __class__.objects.filter(id__in=ids)
97 return parent_qs
98 return wrapper
99
101 """
102 Returns a function usable as the C{target_func} of the
103 L{require_objs_permissions_for_view} decorator. The returned function
104 returns the C{ContentType} queryset for a class. This can be used to
105 enforce class level permissions on views.
106
107 @param klass: the model class for which we want the queryset.
108 """
109 def target_func(*args, **kwargs):
110 ct = ContentType.objects.get_for_model(klass)
111 return ContentType.objects.filter(pk=ct.pk)
112 return target_func
113
115 """
116 Returns a function usable as a C{target_func} parameter. The returned
117 function returns a C{QuerySet} containing one object with the given C{id}.
118
119 @param klass: the class of the queryset's model.
120 @param id: the object's id.
121 """
122 def target_func(*args, **kwargs):
123 return klass.objects.filter(id=id)
124 return target_func
125
127 """
128 Get an object from the ContentType id and from the object's id.
129
130 @param ct_id: ContentType's id for the object class.
131 @param id: object's id.
132 """
133 try:
134 ct = ContentType.objects.get_for_id(ct_id)
135 except ContentType.DoesNotExist:
136 raise Http404()
137 try:
138 return ct.get_object_for_this_type(pk=id)
139 except ct.model_class().DoesNotExist:
140 raise Http404()
141
143 """Get a save function that can be used to enforce create, edit, and
144 delete permissions.
145
146 For example::
147
148 class ModelX(models.Model):
149 ...
150 save = permissions_save_override(
151 "user", lambda: ModelX, "can_create", "can_edit", "can_delete")
152
153 @param permittee_kw: the keyword used to store the permittee in
154 threadlocals
155 @type permittee_kw: C{str}.
156 @param model_func: A callable that returns the the class.
157 @type model_func: C{Model} subclass
158 @param create_perm: The name of the creation permission for the class.
159 @type create_perm: C{str}
160 @param edit_perm: The name of the edit permission for the instance.
161 @type edit_perm: C{str}
162 @param delete_perm: the name of the delete permission for the instance.
163 @type delete_perm: C{str}
164 @return: a save function that can be used to enforce permissions.
165 @rtype: a callable.
166 """
167 def save(self, *args, **kwargs):
168 """
169 Override the default save method to enforce permissions.
170 """
171 pk = getattr(self, "pk", None)
172 if not pk:
173
174 must_have_permission(permittee_kw, model_func(), create_perm)
175 else:
176 must_have_permission(permittee_kw, self, edit_perm)
177
178 super(model_func(), self).save(*args, **kwargs)
179
180 if not pk:
181
182 d = threadlocals.get_thread_locals()
183 give_permission_to(
184 edit_perm, self, d[permittee_kw], can_delegate=True)
185 give_permission_to(
186 delete_perm, self, d[permittee_kw], can_delegate=True)
187 return save
188
190 """Get a delete function that can be used to enforce
191 delete permissions.
192
193 For example::
194
195 class ModelX(models.Model):
196 ...
197 delete = permissions_delete_override(
198 "user", lambda: ModelX, "can_delete")
199
200 @param permittee_kw: the keyword used to store the permittee in
201 threadlocals
202 @type permittee_kw: C{str}.
203 @param model_func: A callable that returns the class.
204 @type model_func: C{Model} subclass
205 @param delete_perm: the name of the delete permission for the instance.
206 @type delete_perm: C{str}
207 """
208 def delete(self, *args, **kwargs):
209 """
210 Override the default delete method to enforce permissions.
211 """
212 must_have_permission(permittee_kw, self, delete_perm)
213
214 ObjectPermission.objects.filter_from_instance(self).delete()
215 super(model_func(), self).delete(*args, **kwargs)
216 return delete
217