11"""Projects authorization adapter."""
22
33import asyncio
4- from collections .abc import AsyncIterable , Awaitable , Callable
4+ from collections .abc import AsyncGenerator , AsyncIterable , Awaitable , Callable
55from dataclasses import dataclass , field
66from enum import StrEnum
77from functools import wraps
@@ -385,6 +385,15 @@ async def users_with_permission(
385385 ids .append (response .subject .subject_object_id )
386386 return ids
387387
388+ async def get_all_members (
389+ self , resource_type : ResourceType , * , zed_token : ZedToken | None = None
390+ ) -> AsyncGenerator [Member , None ]:
391+ """Get all users that are members of a specific resource."""
392+ members = self ._get_members_helper (resource_type , resource_id = None , zed_token = zed_token )
393+ async for member in members :
394+ if member .user_id and member .user_id != "*" :
395+ yield member
396+
388397 @_is_allowed (Scope .READ )
389398 async def members (
390399 self ,
@@ -395,41 +404,63 @@ async def members(
395404 * ,
396405 zed_token : ZedToken | None = None ,
397406 ) -> list [Member ]:
407+ """Get all users that are members of a specific resource type, if role is None then all roles are retrieved."""
408+ members = self ._get_members_helper (resource_type , str (resource_id ), role , zed_token = zed_token )
409+ return [m async for m in members ]
410+
411+ async def _get_members_helper (
412+ self ,
413+ resource_type : ResourceType ,
414+ resource_id : str | None ,
415+ role : Role | None = None ,
416+ * ,
417+ zed_token : ZedToken | None = None ,
418+ ) -> AsyncGenerator [Member , None ]:
398419 """Get all users that are members of a resource, if role is None then all roles are retrieved."""
399- resource_id_str = str (resource_id )
400420 consistency = Consistency (at_least_as_fresh = zed_token ) if zed_token else Consistency (fully_consistent = True )
401421 sub_filter = SubjectFilter (subject_type = ResourceType .user .value )
402- rel_filter = RelationshipFilter (
403- resource_type = resource_type ,
404- optional_resource_id = resource_id_str ,
405- optional_subject_filter = sub_filter ,
406- )
407- if role :
408- relation = _Relation .from_role (role )
422+ if resource_id is None :
423+ rel_filter = RelationshipFilter (resource_type = resource_type , optional_subject_filter = sub_filter )
424+ else :
409425 rel_filter = RelationshipFilter (
410426 resource_type = resource_type ,
411- optional_resource_id = resource_id_str ,
412- optional_relation = relation ,
427+ optional_resource_id = resource_id ,
413428 optional_subject_filter = sub_filter ,
414429 )
430+ if role :
431+ relation = _Relation .from_role (role )
432+ if resource_id is None :
433+ rel_filter = RelationshipFilter (
434+ resource_type = resource_type ,
435+ optional_relation = relation ,
436+ optional_subject_filter = sub_filter ,
437+ )
438+ else :
439+ rel_filter = RelationshipFilter (
440+ resource_type = resource_type ,
441+ optional_resource_id = resource_id ,
442+ optional_relation = relation ,
443+ optional_subject_filter = sub_filter ,
444+ )
415445 responses : AsyncIterable [ReadRelationshipsResponse ] = self .client .ReadRelationships (
416446 ReadRelationshipsRequest (
417447 consistency = consistency ,
418448 relationship_filter = rel_filter ,
419449 )
420450 )
421- members : list [ Member ] = []
451+
422452 async for response in responses :
423453 # Skip "public_viewer" relationships
424454 if response .relationship .relation == _Relation .public_viewer .value :
425455 continue
426456 member_role = _Relation (response .relationship .relation ).to_role ()
427- members . append (
428- Member (
429- user_id = response . relationship . subject . object . object_id , role = member_role , resource_id = resource_id
430- )
457+ member = Member (
458+ user_id = response . relationship . subject . object . object_id ,
459+ role = member_role ,
460+ resource_id = response . relationship . resource . object_id ,
431461 )
432- return members
462+
463+ yield member
433464
434465 @staticmethod
435466 def authz_change (
0 commit comments