99from guardian .shortcuts import (get_perms , remove_perm , assign_perm ,
1010 get_objects_for_user )
1111from guardian .utils import get_user_obj_perms_model
12+ from guardian .models import Permission , UserObjectPermission
1213
1314from cookies .models import *
1415from collections import defaultdict
@@ -54,7 +55,11 @@ def is_owner(user, obj):
5455 -------
5556 bool
5657 """
57- return getattr (obj , 'created_by' , None ) == user or user .is_superuser
58+ return (isinstance (obj , Collection ) and getattr (obj , 'created_by' , None ) == user ) or user .is_superuser
59+
60+
61+ def is_public (obj ):
62+ return getattr (obj , 'public' , False )
5863
5964
6065def check_authorization (auth , user , obj ):
@@ -71,10 +76,39 @@ def check_authorization(auth, user, obj):
7176 -------
7277 bool
7378 """
79+
7480 if auth == 'is_owner' :
7581 return is_owner (user , obj )
76- auth = auth_label (auth , obj )
77- return user .is_superuser or is_owner (user , obj ) or user .has_perm (auth , obj )
82+
83+ if isinstance (obj , Resource ):
84+ if getattr (obj , 'belongs_to' , False ):
85+ auth = auth_label (auth , obj .belongs_to )
86+ _authorized = check_authorization (auth , user , obj .belongs_to )
87+ else :
88+ # If the Resource has no Collection, only the owner or admin can
89+ # access it.
90+ _authorized = False
91+ elif isinstance (obj , ConceptEntity ):
92+ resource_type = ContentType .objects .get_for_model (Resource )
93+ resource = obj .relations_to .filter (source_type = resource_type ).first ().source
94+ _authorized = check_authorization (auth , user , resource )
95+ elif isinstance (obj , Relation ):
96+ _authorized = check_authorization (auth , user , obj .source )
97+ elif isinstance (obj , Value ):
98+ _check = lambda o : check_authorization (auth , user , o )
99+ _sources = [relation .source for relation in obj .relations_to .all () if not isinstance (relation .source , Value )]
100+ _targets = [relation .target for relation in obj .relations_from .all () if not isinstance (relation .target , Value )]
101+ _authorized = all (map (_check , _sources )) and all (map (_check , _targets )) and (_sources or _targets )
102+ elif obj is None :
103+ _authorized = False
104+ else :
105+ auth = auth_label (auth , obj )
106+ _authorized = user .has_perm (auth , obj )
107+ return user .is_superuser or is_owner (user , obj ) or _authorized or (is_public (obj ) and 'view' in auth )
108+
109+
110+ def label_authorizations (auths , obj ):
111+ return [auth_label (auth .split ('_' )[0 ], obj ) for auth in auths ]
78112
79113
80114def auth_label (auth , obj ):
@@ -98,167 +132,20 @@ def auth_label(auth, obj):
98132 _auth_map = {
99133 'view_conceptentity' : 'view_entity' ,
100134 }
101- if auth in SHARED_AUTHORIZATIONS or '_' in auth : # Already labeled.
135+ if auth in SHARED_AUTHORIZATIONS : # Already labeled.
102136 return _auth_map .get (auth , auth )
103- if isinstance (obj , ConceptEntity ) and auth == 'view' :
137+ auth = auth .split ('_' )[0 ]
138+ if (isinstance (obj , ConceptEntity ) or getattr (obj , 'model' , None ) is ConceptEntity ) and auth == 'view' :
104139 return 'view_entity'
105- elif isinstance (obj , Collection ) and auth == 'view' :
140+ elif ( isinstance (obj , Collection ) or getattr ( obj , 'model' , None ) is Collection ) and auth == 'view' :
106141 return 'view_resource'
107- model_label = type (obj ).__name__ .lower ()
142+ if isinstance (obj , QuerySet ):
143+ model_label = obj .model .__name__ .lower ()
144+ else :
145+ model_label = type (obj ).__name__ .lower ()
108146 return '%s_%s' % (auth , model_label )
109147
110148
111- def _propagate_to_resources (auths , user , obj , ** kwargs ):
112- """
113- Propagate authorizations from :class:`.Collection` instances to its
114- related :class:`.Resource` instances.
115-
116- Parameters
117- ----------
118- auths : list
119- A list of authorizations (str). Any authorizations not in this list
120- will be removed from ``obj`` for ``user``.
121- user : :class:`.User`
122- obj : :class:`.Collection`
123- by_user : :class:`.User`
124- If provided, the ``change_authorizations`` auth will be enforced for
125- this user.
126- propagate : bool
127- If ``True`` (default), authorizations will propagate to "children"
128- of ``obj``. i.e. Collection -> Resource -> Relation -> ConceptEntity.
129-
130- Returns
131- -------
132- None
133- """
134- logger .debug ('_propagate_to_resources: %s' % ', ' .join (auths ))
135- by_user = kwargs .get ('by_user' , None )
136- child_auths = map (lambda a : a .replace ('collection' , 'resource' ), auths )
137- children = obj .resources .all ()
138- if by_user :
139- children = apply_filter (by_user , 'change_authorizations' , children )
140- logger .debug ('child auths %s' % ', ' .join (child_auths ))
141- update_authorizations (child_auths , user , children , ** kwargs )
142-
143-
144- def _propagate_to_relations (auths , user , obj , ** kwargs ):
145- """
146- Propagate authorizations from :class:`.Resource` instances to its
147- related :class:`.Relation` instances.
148-
149- Parameters
150- ----------
151- auths : list
152- A list of authorizations (str). Any authorizations not in this list
153- will be removed from ``obj`` for ``user``.
154- user : :class:`.User`
155- obj : :class:`.Resource`
156- by_user : :class:`.User`
157- If provided, the ``change_authorizations`` auth will be enforced for
158- this user.
159- propagate : bool
160- If ``True`` (default), authorizations will propagate to "children"
161- of ``obj``. i.e. Collection -> Resource -> Relation -> ConceptEntity.
162-
163- Returns
164- -------
165- None
166- """
167- by_user = kwargs .get ('by_user' , None )
168- child_auths = map (lambda a : a .replace ('resource' , 'relation' ), auths )
169- children_from = obj .relations_from .all ()
170- children_to = obj .relations_to .all ()
171- if by_user :
172- children_from = apply_filter (by_user , 'change_authorizations' , children_from )
173- children_to = apply_filter (by_user , 'change_authorizations' , children_to )
174-
175- update_authorizations (child_auths , user , children_from , ** kwargs )
176- update_authorizations (child_auths , user , children_to , ** kwargs )
177-
178-
179- def _propagate_to_content (auths , user , obj , ** kwargs ):
180- by_user = kwargs .get ('by_user' , None )
181- logger .debug (repr (kwargs ))
182- logger .debug (repr (auths ))
183- for relation in obj .content .all ():
184- update_authorizations (auths , user , relation .content_resource , ** kwargs )
185-
186-
187-
188- def _propagate_to_entities (auths , user , obj , ** kwargs ):
189- """
190- Propagate authorizations from :class:`.Relation` instances to ``source``
191- and/or ``target`` :class:`.ConceptEntity` instances.
192-
193- Parameters
194- ----------
195- auths : list
196- A list of authorizations (str). Any authorizations not in this list
197- will be removed from ``obj`` for ``user``.
198- user : :class:`.User`
199- obj : :class:`.Relation`
200- by_user : :class:`.User`
201- If provided, the ``change_authorizations`` auth will be enforced for
202- this user.
203- propagate : bool
204- If ``True`` (default), authorizations will propagate to "children"
205- of ``obj``. i.e. Collection -> Resource -> Relation -> ConceptEntity.
206-
207- Returns
208- -------
209- None
210- """
211- by_user = kwargs .get ('by_user' , None )
212- child_auths = map (lambda a : a .replace ('relation' , 'conceptentity' ), auths )
213- for field in ['source' , 'target' ]:
214- child = getattr (obj , field )
215- if isinstance (child , ConceptEntity ):
216- if by_user :
217- if check_authorization ('change_authorizations' , by_user , child ):
218- continue
219- update_authorizations (child_auths , user , child , ** kwargs )
220-
221-
222- def _propagate_to_children (auths , user , obj , ** kwargs ):
223- """
224- Propagate authorizations to child objects.
225-
226- Parameters
227- ----------
228- auths : list
229- A list of authorizations (str). Any authorizations not in this list
230- will be removed from ``obj`` for ``user``.
231- user : :class:`.User`
232- obj : Model instance or :class:`.QuerySet`
233- by_user : :class:`.User`
234- If provided, the ``change_authorizations`` auth will be enforced for
235- this user.
236- propagate : bool
237- If ``True`` (default), authorizations will propagate to "children"
238- of ``obj``. i.e. Collection -> Resource -> Relation -> ConceptEntity.
239-
240- Returns
241- -------
242- None
243- """
244- if isinstance (obj , Collection ):
245- logger .debug ('Collection -> Resources' )
246- _propagate_to_resources (auths , user , obj , ** kwargs )
247- elif isinstance (obj , Resource ):
248- logger .debug ('Resource -> Relation' )
249- _propagate_to_relations (auths , user , obj , ** kwargs )
250-
251- logger .debug ('Resource -> Content' )
252- _propagate_to_content (auths , user , obj , ** kwargs )
253- elif isinstance (obj , Relation ):
254- logger .debug ('Relation -> ConceptEntity' )
255- _propagate_to_entities (auths , user , obj , ** kwargs )
256- elif isinstance (obj , ConceptEntity ):
257- logger .debug ('ConceptEntity -> Relation' )
258- kwargs .pop ('propagate' , None )
259- _propagate_to_relations (auths , user , obj , ** kwargs )
260-
261-
262149def update_authorizations (auths , user , obj , ** kwargs ):
263150 """
264151 Replace the current authorizations for ``user`` on ``obj`` with ``auths``.
@@ -273,21 +160,17 @@ def update_authorizations(auths, user, obj, **kwargs):
273160 by_user : :class:`.User`
274161 If provided, the ``change_authorizations`` auth will be enforced for
275162 this user.
276- propagate : bool
277- If ``True`` (default), authorizations will propagate to "children"
278- of ``obj``. i.e. Collection -> Resource -> Relation -> ConceptEntity.
279163
280164 Returns
281165 -------
282166 None
283167 """
284-
168+
285169 logger .debug ('update authorizations for %s with %s for %s' % \
286170 (repr (obj ), ' ' .join (auths ), repr (user )))
287171
288172 # ``auths`` may or may not have model-specific auth labels.
289- labeled_auths = [auth_label (auth , obj ) for auth in auths ]
290-
173+ labeled_auths = label_authorizations (auths , obj )
291174 by_user = kwargs .get ('by_user' , None )
292175 if by_user and isinstance (obj , QuerySet ):
293176 obj = apply_filter (by_user , 'change_authorizations' , obj )
@@ -311,10 +194,6 @@ def update_authorizations(auths, user, obj, **kwargs):
311194 msg = '"%s" not a valid auth for %s' % (auth , repr (obj ))
312195 raise ValueError (msg )
313196
314- if kwargs .get ('propagate' , True ):
315- logger .debug ('propagate' )
316- _propagate_to_children (auths , user , obj , ** kwargs )
317-
318197
319198def list_authorizations (obj , user = None ):
320199 """
@@ -362,6 +241,9 @@ def apply_filter(user, auth, queryset):
362241 """
363242 Limit ``queryset`` to those objects for which ``user`` has ``permission``.
364243
244+ As of 0.4 this depends entirely on the :class:`.Collection` to which
245+ objects belong.
246+
365247 Parameters
366248 ----------
367249 user : :class:`django.contrib.auth.models.User`
@@ -373,18 +255,42 @@ def apply_filter(user, auth, queryset):
373255 :class:`django.db.models.QuerySet`
374256
375257 """
376- # TODO: implement a more general way to correct these legacy auth names.
377- if getattr (queryset , 'model' , None ) == Collection \
378- and auth == 'view_collection' :
379- auth = 'view_resource'
380-
258+ # Everything depends on the Collection now.
259+ auth = auth_label (auth , Collection .objects .first ())
381260 if user .is_superuser :
382261 return queryset
383262 if type (queryset ) is list :
384263 return [obj for obj in queryset if check_authorization (auth , user , obj )]
385264 if auth == 'is_owner' :
386265 return queryset .filter (created_by_id = user .id )
387- return get_objects_for_user (user , auth , queryset )
266+
267+ ctype = ContentType .objects .get_for_model (Collection )
268+ rtype = ContentType .objects .get_for_model (Resource )
269+ perm = Permission .objects .get (codename = auth , content_type_id = ctype )
270+ perms = UserObjectPermission .objects .filter (user_id = user .id ,
271+ permission_id = perm .id ,
272+ content_type_id = ctype .id )
273+
274+ # For some reason Guardian stores related primary keys as strings; without
275+ # mapping back to int this will cause Postgres to choke.
276+ collection_pks = map (int , perms .values_list ('object_pk' , flat = True ))
277+ if queryset .model is Collection :
278+ return queryset .filter (pk__in = collection_pks )
279+ elif queryset .model is Resource :
280+ q = Q (belongs_to__id__in = collection_pks )
281+ else : # Traverse back up to the Collection via its Resources.
282+ resources = Resource .objects .filter (belongs_to__id__in = collection_pks )\
283+ .values_list ('id' , flat = True )
284+
285+ if queryset .model is ConceptEntity :
286+ q = Q (relations_to__source_instance_id__in = resources , relations_to__source_type = rtype ) \
287+ | Q (relations_from__target_instance_id__in = resources , relations_from__target_type = rtype )
288+ elif queryset .model is Relation :
289+ q = Q (source_instance_id__in = resources ) \
290+ | Q (target_instance_id__in = resources )
291+ elif queryset .model is Value :
292+ q = Q (relations_to__source_instance_id__in = resources )
293+ return queryset .filter (q ).distinct ()
388294
389295
390296def make_nonpublic (obj ):
0 commit comments