@@ -233,18 +233,53 @@ def master_api_key_has_organisation_permission(
233233def _is_user_object_admin (
234234 user : "FFAdminUser" , object_ : Union [Project , Environment ]
235235) -> bool :
236- ModelClass = type (object_ )
237- base_filter = get_base_permission_filter (user , ModelClass ) # type: ignore[arg-type]
238- filter_ = base_filter & Q (id = object_ .id )
239-
240- if ModelClass is Project :
241- filter_ = filter_ & Q (organisation__users = user )
242- elif ModelClass is Environment :
243- filter_ = filter_ & Q (project__organisation__users = user )
236+ """
237+ Check if user has admin permission on a project or environment.
238+
239+ Runs separate queries with early returns:
240+ 1. Organisation membership - check to prevent orphaned permission
241+ records from granting access.
242+ 2. Direct user permission - checks UserProjectPermission/UserEnvironmentPermission.
243+ 3. Group permission - checks via user's group memberships.
244+ 4. Role permission - RBAC check, only if enabled.
245+
246+ Returns as soon as permission is found, avoiding unnecessary subsequent queries.
247+
248+ """
249+ model_class = type (object_ )
250+ object_id = object_ .id
251+
252+ # Check: verify user belongs to the organisation that owns this object
253+ if model_class is Project :
254+ if not Project .objects .filter (id = object_id , organisation__users = user ).exists ():
255+ return False
256+ elif model_class is Environment :
257+ if not Environment .objects .filter (
258+ id = object_id , project__organisation__users = user
259+ ).exists ():
260+ return False
244261 else : # pragma: no cover
245- raise ValueError (f"Unexpected object type { type (object_ )} " )
262+ raise ValueError (f"Unexpected object type { model_class } " )
263+
264+ # Check direct permission
265+ user_filter = get_user_permission_filter (user , allow_admin = True )
266+ if model_class .objects .filter (user_filter & Q (id = object_id )).exists ():
267+ return True
246268
247- return ModelClass .objects .filter (filter_ ).exists ()
269+ # Check group permission
270+ group_filter = get_group_permission_filter (user , allow_admin = True )
271+ if model_class .objects .filter (group_filter & Q (id = object_id )).exists ():
272+ return True
273+
274+ # Check role permission (only if RBAC installed)
275+ if settings .IS_RBAC_INSTALLED : # pragma: no cover
276+ role_filter = get_role_permission_filter (
277+ user , model_class , permission_key = None , allow_admin = True , tag_ids = None
278+ )
279+ if model_class .objects .filter (role_filter & Q (id = object_id )).exists ():
280+ return True
281+
282+ return False
248283
249284
250285def get_base_permission_filter ( # type: ignore[no-untyped-def]
0 commit comments