2121 from users .models import FFAdminUser
2222
2323
24+ def get_active_membership_filter (user : "FFAdminUser" , prefix : str = "" ) -> Q :
25+ """
26+ Build a filter matching objects related to an organisation that `user` is an
27+ active member of.
28+
29+ Deactivated memberships (`UserOrganisation.is_active=False`) are excluded, so
30+ this must be used in place of traversing the `Organisation.users` M2M.
31+
32+ `prefix` is the query path to the organisation, e.g. `"project__organisation__"`.
33+ """
34+ return Q (
35+ ** {
36+ f"{ prefix } userorganisation__user" : user ,
37+ f"{ prefix } userorganisation__is_active" : True ,
38+ }
39+ )
40+
41+
2442def is_user_organisation_admin (
2543 user : "FFAdminUser" , organisation : Union [Organisation , int ]
2644) -> bool :
2745 user_organisation = user .get_user_organisation (organisation )
28- if user_organisation is not None :
46+ if user_organisation is not None and user_organisation . is_active :
2947 set_span_attribute ("organisation.id" , user_organisation .organisation_id )
3048 return user_organisation .role == OrganisationRole .ADMIN .name
3149 return False
@@ -93,6 +111,7 @@ def get_permitted_projects_for_user(
93111 admin_organisations_filter = Q (
94112 organisation__userorganisation__user = user ,
95113 organisation__userorganisation__role = OrganisationRole .ADMIN .name ,
114+ organisation__userorganisation__is_active = True ,
96115 )
97116 project_ids_from_admin_organisations = Project .objects .filter (
98117 admin_organisations_filter
@@ -104,7 +123,7 @@ def get_permitted_projects_for_user(
104123 queryset = Project .objects .filter (id__in = project_ids )
105124
106125 # Final check to ensure that the user is a member of the organisation
107- queryset = queryset .filter (organisation__users = user )
126+ queryset = queryset .filter (get_active_membership_filter ( user , "organisation__" ) )
108127
109128 return queryset
110129
@@ -166,7 +185,9 @@ def get_permitted_environments_for_user(
166185 queryset = queryset .prefetch_related ("metadata" )
167186
168187 # Final check to ensure the user is a member of the organisation
169- queryset = queryset .filter (project__organisation__users = user )
188+ queryset = queryset .filter (
189+ get_active_membership_filter (user , "project__organisation__" )
190+ )
170191
171192 # Description is defered due to Oracle support where a
172193 # query can't have a where clause if description is in
@@ -214,7 +235,9 @@ def user_has_organisation_permission(
214235 return True
215236
216237 # Check: verify user belongs to the organisation
217- if not Organisation .objects .filter (id = organisation .id , users = user ).exists ():
238+ if not Organisation .objects .filter (
239+ get_active_membership_filter (user ) & Q (id = organisation .id )
240+ ).exists ():
218241 return False
219242
220243 # NOTE: since we store organisation admin slightly differently
@@ -274,11 +297,14 @@ def _is_user_object_admin(
274297
275298 # Check: verify user belongs to the organisation that owns this object
276299 if model_class is Project :
277- if not Project .objects .filter (id = object_id , organisation__users = user ).exists ():
300+ if not Project .objects .filter (
301+ get_active_membership_filter (user , "organisation__" ) & Q (id = object_id )
302+ ).exists ():
278303 return False
279304 elif model_class is Environment :
280305 if not Environment .objects .filter (
281- id = object_id , project__organisation__users = user
306+ get_active_membership_filter (user , "project__organisation__" )
307+ & Q (id = object_id )
282308 ).exists ():
283309 return False
284310 else : # pragma: no cover
0 commit comments