Skip to content

Commit 42c5c4e

Browse files
committed
feat: Implemented team member assignments endpoint
1 parent ce614a5 commit 42c5c4e

2 files changed

Lines changed: 149 additions & 0 deletions

File tree

openedx_authz/api/users.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
User = get_user_model()
4545

4646

47+
User = get_user_model()
48+
49+
4750
__all__ = [
4851
"assign_role_to_user_in_scope",
4952
"batch_assign_role_to_users_in_scope",

openedx_authz/rest_api/v1/views.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,3 +861,149 @@ def get(self, request: HttpRequest, username: str) -> Response:
861861
paginator = self.pagination_class()
862862
paginated_response_data = paginator.paginate_queryset(assignments, request)
863863
return paginator.get_paginated_response(paginated_response_data)
864+
865+
866+
@view_auth_classes()
867+
class AssignmentsAPIView(APIView):
868+
"""
869+
API view for listing all user role assignments
870+
This API is used on the main team members view on the Admin Console.
871+
872+
**Endpoints**
873+
874+
- GET: Retrieve all user role assignments
875+
876+
**Query Parameters**
877+
878+
- orgs (Optional): Comma-separated list of orgs to filter assignments by
879+
- roles (Optional): Comma-separated list of roles to filter assignments by
880+
- scopes (Optional): Comma-separated list of scopes to filter assignments by
881+
- search (Optional): Search term to filter assignments by full_name, username, or email
882+
- sort_by (Optional): Field to sort by. Options: role, org, scope, full_name, username, email. Defaults to full_name
883+
- order (Optional): Sort order, 'asc' or 'desc'. Defaults to asc
884+
- page (Optional): Page number for pagination
885+
- page_size (Optional): Number of items per page
886+
887+
**Response Format**
888+
889+
Returns a paginated list of user assignment objects, each containing:
890+
891+
- is_superadmin: whether this entry denotes a superadmin
892+
- role: The role
893+
- org: The org over which this role is applied
894+
- scope: The scope over which this role is applied
895+
- permission_count: The number of permissions that apply to this role
896+
- full_name: The full name of the user in this assignment
897+
- username: The username of the user in this assignment
898+
- email: The email of the user in this assignment
899+
900+
**Authentication and Permissions**
901+
902+
- Requires authenticated user.
903+
- Results are filtered according to calling user's "view scope team members" permissions.
904+
905+
**Example Request**
906+
907+
GET /api/authz/v1/assignments/?order=desc&sort_by=role&page=1&page_size=2&search=cont
908+
909+
**Example Response**::
910+
911+
{
912+
"count": 2,
913+
"next": null,
914+
"previous": null,
915+
"results": [
916+
{
917+
"is_superadmin": false,
918+
"role": "course_staff",
919+
"org": "OpenedX",
920+
"scope": "course-v1:OpenedX+DemoX+DemoCourse",
921+
"permission_count": 27,
922+
"full_name": "",
923+
"username": "contributor",
924+
"email": "[email protected]"
925+
},
926+
{
927+
"is_superadmin": true,
928+
"role": "django.superuser",
929+
"org": "*",
930+
"scope": "*",
931+
"permission_count": null,
932+
"full_name": "",
933+
"username": "admin",
934+
"email": "[email protected]"
935+
},
936+
]
937+
}
938+
"""
939+
940+
pagination_class = AuthZAPIViewPagination
941+
filter_backends = [UserAssignmentsSearchFilter, UserAssignmentsOrderingFilter]
942+
permission_classes = [AnyScopePermission]
943+
944+
@apidocs.schema(
945+
parameters=[
946+
apidocs.query_parameter("orgs", str, description="The orgs to query assignments for"),
947+
apidocs.query_parameter("roles", str, description="The roles to query assignments for"),
948+
apidocs.query_parameter("scopes", str, description="The scopes to query assignments for"),
949+
apidocs.query_parameter(
950+
"search", str, description="The search query to filter assignments by full_name, username, or email"
951+
),
952+
apidocs.query_parameter(
953+
"sort_by",
954+
str,
955+
description="The field to sort by. Options: role, org, scope, full_name, username, email",
956+
),
957+
apidocs.query_parameter("order", str, description="The order to sort by"),
958+
apidocs.query_parameter("page", int, description="Page number for pagination"),
959+
apidocs.query_parameter("page_size", int, description="Number of items per page"),
960+
],
961+
responses={
962+
status.HTTP_200_OK: TeamMemberUserAssignmentSerializer(many=True),
963+
status.HTTP_400_BAD_REQUEST: "The request parameters are invalid",
964+
status.HTTP_401_UNAUTHORIZED: "The user is not authenticated or does not have the required permissions",
965+
status.HTTP_403_FORBIDDEN: "The user does not have the required permisisons",
966+
},
967+
)
968+
@authz_permissions(
969+
[
970+
permissions.VIEW_LIBRARY_TEAM.identifier,
971+
permissions.COURSES_VIEW_COURSE_TEAM.identifier,
972+
]
973+
)
974+
def get(self, request: HttpRequest) -> Response:
975+
"""Retrieve all user role assignments."""
976+
serializer = ListAllTeamMembersAssignmentsSerializer(data=request.query_params)
977+
serializer.is_valid(raise_exception=True)
978+
query_params = serializer.validated_data
979+
980+
user_role_assignments: list[UserAssignmentData | SuperAdminAssignmentData] = []
981+
982+
# Retrieve superadmin assignments (django staff or superuser users), as they always have access to everything
983+
user_role_assignments += get_superadmin_assignments()
984+
985+
users_with_assignments = api.get_visible_role_assignments_for_user(
986+
orgs=query_params.get("orgs"),
987+
scopes=query_params.get("scopes"),
988+
roles=query_params.get("roles"),
989+
allowed_for_user_external_key=request.user.username,
990+
)
991+
992+
# Unpack list of UserAssignments to a list of UserAssignmentData
993+
for uwa in users_with_assignments:
994+
user_role_assignments += [
995+
UserAssignmentData(
996+
user=uwa.user, subject=assignment.subject, roles=assignment.roles, scope=assignment.scope
997+
)
998+
for assignment in uwa.assignments
999+
]
1000+
1001+
assignments = TeamMemberUserAssignmentSerializer(user_role_assignments, many=True).data
1002+
for backend in self.filter_backends:
1003+
assignments = backend().filter_queryset(request, assignments, self)
1004+
1005+
# Paginate
1006+
paginator = self.pagination_class()
1007+
paginated_response_data = paginator.paginate_queryset(assignments, request)
1008+
return paginator.get_paginated_response(paginated_response_data)
1009+

0 commit comments

Comments
 (0)