@@ -548,6 +548,45 @@ def descendent_roles(self):
548548 descendents .update (set (target_team .has_roles .all ()))
549549 return descendents
550550
551+ def get_child_filters (self , permission , role_content_type , types_prefetch ):
552+ "Given a permission that a certain object role grants, returns child object filter instructions"
553+ filter_path = None
554+ child_model = None
555+ role_model = role_content_type .model_class ()
556+ permission_content_type = types_prefetch .get_content_type (permission .content_type_id )
557+ permission_model = permission_content_type .model_class ()
558+ if is_add_perm (permission .codename ):
559+ # Add evaluations for add permission to children which are parents of the permission model
560+ # this only matters when for multi-layer inheritance (grandchildren)
561+ if issubclass (permission_model , RemoteObject ):
562+ eval_ct = permission_content_type .parent_content_type
563+ if eval_ct == role_content_type :
564+ return (None , None , None ) # this permission is for a direct child model
565+ else :
566+ for path , model in permission_registry .get_child_models (role_model ):
567+ if '__' in path and model ._meta .model_name == permission_content_type .model :
568+ path_to_parent , filter_path = path .split ('__' , 1 )
569+ child_model = permission_model ._meta .get_field (path_to_parent ).related_model
570+ eval_ct = DABContentType .objects .get_for_model (child_model ).id
571+ break
572+ if not child_model :
573+ return (None , None , None ) # this permission is for a direct child model
574+ elif issubclass (permission_model , RemoteObject ):
575+ eval_ct = permission .content_type_id
576+ else :
577+ for path , model in permission_registry .get_child_models (role_model ):
578+ if model ._meta .model_name == permission_content_type .model :
579+ filter_path = path
580+ child_model = model
581+ eval_ct = permission .content_type_id
582+ break
583+ else :
584+ logger .warning (f'{ self .role_definition } listed { permission .codename } but model is not a child, ignoring' )
585+ return (None , None , None )
586+
587+ return (eval_ct , child_model , filter_path )
588+
589+
551590 def expected_direct_permissions (self , types_prefetch = None ) -> set [tuple [str , int , Union [int , UUID ]]]:
552591 """The expected permissions that holding this ObjectRole confers to the holder
553592
@@ -569,8 +608,6 @@ def expected_direct_permissions(self, types_prefetch=None) -> set[tuple[str, int
569608 # ObjectRole.object_id is stored as text, we convert it to the model pk native type
570609 object_id = role_model ._meta .pk .to_python (self .object_id )
571610 for permission in types_prefetch .permissions_for_object_role (self ):
572- permission_content_type = types_prefetch .get_content_type (permission .content_type_id )
573- permission_model = permission_content_type .model_class ()
574611
575612 # direct object permission
576613 if permission .content_type_id == self .content_type_id :
@@ -582,43 +619,18 @@ def expected_direct_permissions(self, types_prefetch=None) -> set[tuple[str, int
582619 expected_evaluations .add ((permission .codename , self .content_type_id , object_id ))
583620
584621 # Add evaluations for child objects, where this role gives permission to child objects
585- filter_path = None
586- child_model = None
587- if is_add_perm (permission .codename ):
588- # Add evaluations for add permission to children which are parents of the permission model
589- # this only matters when for multi-layer inheritance (grandchildren)
590- if issubclass (permission_model , RemoteObject ):
591- eval_ct = permission_content_type .parent_content_type
592- if eval_ct == role_content_type :
593- continue # this permission is for a direct child model
594- else :
595- for path , model in permission_registry .get_child_models (role_model ):
596- if '__' in path and model ._meta .model_name == permission_content_type .model :
597- path_to_parent , filter_path = path .split ('__' , 1 )
598- child_model = permission_model ._meta .get_field (path_to_parent ).related_model
599- eval_ct = DABContentType .objects .get_for_model (child_model ).id
600- break
601- if not child_model :
602- continue # this permission is for a direct child model
603- elif issubclass (permission_model , RemoteObject ):
604- eval_ct = permission .content_type_id
605- else :
606- for path , model in permission_registry .get_child_models (role_model ):
607- if model ._meta .model_name == permission_content_type .model :
608- filter_path = path
609- child_model = model
610- eval_ct = permission .content_type_id
611- break
612- else :
613- logger .warning (f'{ self .role_definition } listed { permission .codename } but model is not a child, ignoring' )
614- continue
622+ eval_ct , child_model , filter_path = self .get_child_filters (permission , role_content_type , types_prefetch )
623+ if not eval_ct :
624+ continue
615625
616626 # fetching child objects of an organization is very performance sensitive
617627 # for multiple permissions of same type, make sure to only do query once
618628 id_list = []
619629 if eval_ct in cached_id_lists :
620630 id_list = cached_id_lists [eval_ct ]
621631 else :
632+ permission_content_type = types_prefetch .get_content_type (permission .content_type_id )
633+ permission_model = permission_content_type .model_class ()
622634 if issubclass (permission_model , RemoteObject ):
623635 # Build id_list from ObjectRole objects if it is remote object
624636 id_list = (
0 commit comments