1616from rest_framework .views import APIView
1717
1818from openedx_authz .api .data import ContentLibraryData
19- from openedx_authz .api .roles import (
20- get_all_roles_and_subjects_in_scope ,
21- get_role_definitions_in_scope ,
22- get_all_users_by_role ,
23- )
19+ from openedx_authz .api .roles import get_all_users_by_role , get_role_definitions_in_scope
2420from openedx_authz .api .users import (
2521 assign_role_to_user_in_scope ,
26- get_user_role_assignments_for_role_in_scope ,
22+ get_all_user_role_assignments_in_scope_v2 ,
2723 unassign_role_from_user ,
2824 user_has_permission ,
29- get_all_user_role_assignments_in_scope ,
3025)
26+ from openedx_authz .rest_api .v1 .paginators import RoleUserAPIViewPagination
3127from openedx_authz .rest_api .v1 .serializers import (
3228 AddUserToRoleWithScopeSerializer ,
3329 ListRolesWithScopeResponseSerializer ,
3430 ListRolesWithScopeSerializer ,
35- ListUsersInRoleWithScopeResponseSerializer ,
3631 ListUsersInRoleWithScopeSerializer ,
3732 PermissionValidationResponseSerializer ,
3833 PermissionValidationSerializer ,
3934 RemoveUserFromRoleWithScopeSerializer ,
35+ RoleAssignmentSerializer ,
4036)
4137
4238try :
43- from common .djangoapps .student .models .user import get_user_by_username_or_email
39+ from common .djangoapps .student .models .user import get_user_by_username_or_email # type: ignore
4440except ImportError :
4541 get_user_by_username_or_email = None
4642
@@ -101,65 +97,33 @@ class RoleUserAPIView(APIView):
10197 """
10298
10399 permission_classes = [IsAuthenticated ]
100+ pagination_class = RoleUserAPIViewPagination
104101
105102 @apidocs .schema (
106103 parameters = [
107104 apidocs .query_parameter ("role" , str , description = "The name of the role to query" ),
108105 apidocs .query_parameter ("scope" , str , description = "The authorization scope for the role" ),
106+ apidocs .query_parameter ("page" , int , description = "Page number for pagination" ),
107+ apidocs .query_parameter ("page_size" , int , description = "Number of items per page" ),
109108 ],
110109 responses = {
111- status .HTTP_200_OK : ListUsersInRoleWithScopeResponseSerializer ( many = True ) ,
110+ status .HTTP_200_OK : "The users were retrieved successfully" ,
112111 status .HTTP_400_BAD_REQUEST : "The request parameters are invalid" ,
113112 status .HTTP_401_UNAUTHORIZED : "The user is not authenticated" ,
114113 },
115114 )
116115 def get (self , request : HttpRequest ) -> Response :
117- """Retrieve all users assigned to a specific role within a scope."""
116+ """Retrieve all users with role assignments within a specific scope."""
117+ # TODO: Filter by role
118118 serializer = ListUsersInRoleWithScopeSerializer (data = request .query_params )
119119 serializer .is_valid (raise_exception = True )
120+ user_role_assignments = get_all_user_role_assignments_in_scope_v2 (serializer .validated_data ["scope" ])
120121
121- role_name = serializer .validated_data .get ("role" )
122- scope = serializer .validated_data ["scope" ]
123-
124- response_data = []
125-
126- scope_data = ContentLibraryData (external_key = scope )
127-
128- # TODO: Should this be another endpoint?
129- if not role_name :
130- roles = get_all_roles_and_subjects_in_scope (scope_data )
131- final_roles = []
132- for role in roles :
133- role_data = {
134- "role" : role ["role" ],
135- "users" : [],
136- }
137- for user_identifier in role ["users" ]:
138- user = get_user_by_username_or_email (user_identifier )
139- role_data ["users" ].append (
140- {
141- "username" : user .username ,
142- "full_name" : user .profile .name ,
143- "email" : user .email ,
144- }
145- )
146- final_roles .append (role_data )
147- return Response (final_roles , status = status .HTTP_200_OK )
148-
149- role_assignments = get_user_role_assignments_for_role_in_scope (role_name , scope )
150- for assignment in role_assignments :
151- # TODO: Should we get all users at once instead of one by one?
152- user = get_user_by_username_or_email (assignment .subject .username )
153- response_data .append (
154- {
155- "username" : assignment .subject .username ,
156- "full_name" : user .profile .name ,
157- "email" : user .email ,
158- }
159- )
122+ paginator = self .pagination_class ()
123+ paginated_assignments = paginator .paginate_queryset (user_role_assignments , request )
160124
161- serializer = ListUsersInRoleWithScopeResponseSerializer ( response_data , many = True )
162- return Response (serializer .data , status = status . HTTP_200_OK )
125+ serializer = RoleAssignmentSerializer ( paginated_assignments , many = True )
126+ return paginator . get_paginated_response (serializer .data )
163127
164128 @apidocs .schema (
165129 body = AddUserToRoleWithScopeSerializer ,
@@ -174,57 +138,62 @@ def put(self, request: HttpRequest) -> Response:
174138 serializer = AddUserToRoleWithScopeSerializer (data = request .data )
175139 serializer .is_valid (raise_exception = True )
176140
177- completed , errors = [], []
141+ # TODO: Should we validate that the role or scope exists?
178142 role_name = serializer .validated_data ["role" ]
179143 scope = serializer .validated_data ["scope" ]
180144
145+ completed , errors = [], []
181146 for user_identifier in serializer .validated_data ["users" ]:
182147 try :
183148 user = get_user_by_username_or_email (user_identifier )
184149 assign_role_to_user_in_scope (user .username , role_name , scope )
185- completed .append ({"user " : user_identifier , "status" : "role_added" })
150+ completed .append ({"user_identifier " : user_identifier , "status" : "role_added" })
186151 except User .DoesNotExist :
187- errors .append ({"user " : user_identifier , "error" : "user_not_found" })
152+ errors .append ({"user_identifier " : user_identifier , "error" : "user_not_found" })
188153 except Exception as e : # pylint: disable=broad-exception-caught
189154 logger .error (f"Error assigning role to user { user_identifier } : { e } " )
190- errors .append ({"user " : user_identifier , "error" : "assignment_failed" })
155+ errors .append ({"user_identifier " : user_identifier , "error" : "assignment_failed" })
191156
192157 response_data = {"completed" : completed , "errors" : errors }
193158 return Response (response_data , status = status .HTTP_207_MULTI_STATUS )
194159
195160 @apidocs .schema (
196161 parameters = [
197- apidocs .query_parameter ("user" , str , description = "The user to remove from the role" ),
198- apidocs .query_parameter ("role" , str , description = "The role to remove the user from" ),
199- apidocs .query_parameter ("scope" , str , description = "The scope to remove the user from" ),
162+ apidocs .query_parameter (
163+ "users" , str , description = "List of user identifiers (username or email) separated by a comma"
164+ ),
165+ apidocs .query_parameter ("role" , str , description = "The role to remove the users from" ),
166+ apidocs .query_parameter ("scope" , str , description = "The scope to remove the users from" ),
200167 ],
201168 responses = {
202- status .HTTP_200_OK : "The user was removed from the role" ,
203- status .HTTP_400_BAD_REQUEST : "The request data is invalid" ,
169+ status .HTTP_207_MULTI_STATUS : "The users were removed from the role" ,
170+ status .HTTP_400_BAD_REQUEST : "The request parameters are invalid" ,
204171 status .HTTP_401_UNAUTHORIZED : "The user is not authenticated" ,
205- status .HTTP_404_NOT_FOUND : "The user was not found" ,
206- status .HTTP_500_INTERNAL_SERVER_ERROR : "The user was not removed from the role" ,
207172 },
208173 )
209174 def delete (self , request : HttpRequest ) -> Response :
210- """Remove a user from a specific role within a scope."""
175+ """Remove multiple users from a specific role within a scope."""
211176 serializer = RemoveUserFromRoleWithScopeSerializer (data = request .query_params )
212177 serializer .is_valid (raise_exception = True )
213178
214- # Should we allow a list of users separated by a comma?
215- user_identifier = serializer .validated_data ["user" ]
179+ user_identifiers = serializer .validated_data ["users" ]
216180 role_name = serializer .validated_data ["role" ]
217181 scope = serializer .validated_data ["scope" ]
218182
219- try :
220- user = get_user_by_username_or_email (user_identifier )
221- unassign_role_from_user (user .username , role_name , scope )
222- return Response ({"message" : "Role successfully removed from user" }, status = status .HTTP_200_OK )
223- except User .DoesNotExist :
224- return Response ({"error" : "User not found" }, status = status .HTTP_404_NOT_FOUND )
225- except Exception as e : # pylint: disable=broad-exception-caught
226- logger .error (f"Error removing role from user { user_identifier } : { e } " )
227- return Response ({"error" : "Internal server error" }, status = status .HTTP_500_INTERNAL_SERVER_ERROR )
183+ completed , errors = [], []
184+ for user_identifier in user_identifiers :
185+ try :
186+ user = get_user_by_username_or_email (user_identifier )
187+ unassign_role_from_user (user .username , role_name , scope )
188+ completed .append ({"user_identifier" : user_identifier , "status" : "role_removed" })
189+ except User .DoesNotExist :
190+ errors .append ({"user_identifier" : user_identifier , "error" : "user_not_found" })
191+ except Exception as e : # pylint: disable=broad-exception-caught
192+ logger .error (f"Error removing role from user { user_identifier } : { e } " )
193+ errors .append ({"user_identifier" : user_identifier , "error" : "removal_failed" })
194+
195+ response_data = {"completed" : completed , "errors" : errors }
196+ return Response (response_data , status = status .HTTP_207_MULTI_STATUS )
228197
229198
230199class RoleListView (APIView ):
0 commit comments