Skip to content

Commit fe5cd43

Browse files
committed
docs: add apidocs
1 parent c50d9b0 commit fe5cd43

3 files changed

Lines changed: 120 additions & 27 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"""Custom permissions for the Open edX AuthZ REST API."""
1+
"""Permissions for the Open edX AuthZ REST API."""

openedx_authz/rest_api/v1/serializers.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ class PermissionValidationSerializer(ActionMixin, ScopeMixin): # pylint: disabl
2525
"""Serializer for permission validation request."""
2626

2727

28+
class PermissionValidationResponseSerializer(PermissionValidationSerializer): # pylint: disable=abstract-method
29+
"""Serializer for permission validation response."""
30+
31+
allowed = serializers.BooleanField()
32+
33+
2834
class AddUserToRoleWithScopeSerializer(RoleMixin, ScopeMixin): # pylint: disable=abstract-method
2935
"""Serializer for adding a user to a role with a scope."""
3036

@@ -43,3 +49,19 @@ class ListUsersInRoleWithScopeSerializer(RoleMixin, ScopeMixin): # pylint: disa
4349

4450
class ListRolesWithScopeSerializer(ScopeMixin): # pylint: disable=abstract-method
4551
"""Serializer for listing roles with a scope."""
52+
53+
54+
class ListUsersInRoleWithScopeResponseSerializer(serializers.Serializer): # pylint: disable=abstract-method
55+
"""Serializer for listing users in a role with a scope response."""
56+
57+
username = serializers.CharField(max_length=255)
58+
full_name = serializers.CharField(max_length=255)
59+
email = serializers.EmailField()
60+
61+
62+
class ListRolesWithScopeResponseSerializer(serializers.Serializer): # pylint: disable=abstract-method
63+
"""Serializer for listing roles with a scope response."""
64+
65+
role = serializers.CharField(max_length=255)
66+
permissions = serializers.ListField(child=serializers.CharField(max_length=255))
67+
user_count = serializers.IntegerField()

openedx_authz/rest_api/v1/views.py

Lines changed: 97 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
"""Views for the Open edX AuthZ REST API."""
1+
"""
2+
REST API views for Open edX Authorization (AuthZ) system.
3+
4+
This module provides Django REST Framework views for managing authorization
5+
permissions, roles, and user assignments within Open edX platform.
6+
"""
27

38
import logging
49

10+
import edx_api_doc_tools as apidocs
511
from common.djangoapps.student.models.user import get_user_by_username_or_email
612
from django.contrib.auth import get_user_model
13+
from django.http import HttpRequest
714
from rest_framework import status
815
from rest_framework.permissions import IsAuthenticated
916
from rest_framework.response import Response
@@ -19,8 +26,11 @@
1926
)
2027
from openedx_authz.rest_api.v1.serializers import (
2128
AddUserToRoleWithScopeSerializer,
29+
ListRolesWithScopeResponseSerializer,
2230
ListRolesWithScopeSerializer,
31+
ListUsersInRoleWithScopeResponseSerializer,
2332
ListUsersInRoleWithScopeSerializer,
33+
PermissionValidationResponseSerializer,
2434
PermissionValidationSerializer,
2535
RemoveUserFromRoleWithScopeSerializer,
2636
)
@@ -32,43 +42,71 @@
3242

3343
class PermissionValidationView(APIView):
3444
"""
35-
Validate permissions for the authenticated user.
45+
API view for validating user permissions against authorization policies.
46+
47+
This view allows authenticated users to check whether they have
48+
specific permissions for given actions and scopes within the system.
49+
Supports batch permission validation through POST request.
3650
"""
3751

3852
permission_classes = [IsAuthenticated]
3953

40-
def post(self, request):
54+
@apidocs.schema(
55+
body=PermissionValidationSerializer(help_text="The permissions to validate", many=True),
56+
responses={
57+
status.HTTP_200_OK: PermissionValidationResponseSerializer,
58+
status.HTTP_400_BAD_REQUEST: "The request data is invalid",
59+
status.HTTP_401_UNAUTHORIZED: "The user is not authenticated",
60+
},
61+
)
62+
def post(self, request: HttpRequest) -> Response:
4163
"""Validate permissions for the authenticated user."""
4264
serializer = PermissionValidationSerializer(data=request.data, many=True)
4365
serializer.is_valid(raise_exception=True)
4466

4567
username = request.user.username
4668
subject = UserData(username=username)
4769

70+
response_data = []
4871
for perm in serializer.validated_data:
4972
try:
5073
action = ActionData(name=perm["action"])
5174
scope = ScopeData(name=perm["scope"])
5275
allowed = has_permission(subject, action, scope)
53-
perm["allowed"] = allowed
76+
response_data.append(
77+
{
78+
"action": perm["action"],
79+
"scope": perm["scope"],
80+
"allowed": allowed,
81+
}
82+
)
5483
except Exception as e: # pylint: disable=broad-exception-caught
5584
logger.error(f"Error validating permission for user {username}: {e}")
5685

57-
return Response(serializer.validated_data, status=status.HTTP_200_OK)
86+
serializer = PermissionValidationResponseSerializer(response_data, many=True)
87+
return Response(serializer.data, status=status.HTTP_200_OK)
5888

5989

6090
class RoleUserAPIView(APIView):
6191
"""
62-
APIView for managing users and their roles.
63-
Handles GET (list), PUT (add), and DELETE (remove) operations.
92+
API view for managing user-role assignments within specific scope.
6493
"""
6594

6695
permission_classes = [IsAuthenticated]
6796

68-
def get(self, request):
69-
"""
70-
Get list of users in the role.
71-
"""
97+
@apidocs.schema(
98+
parameters=[
99+
apidocs.query_parameter("role", str, description="The name of the role to query"),
100+
apidocs.query_parameter("scope", str, description="The authorization scope for the role"),
101+
],
102+
responses={
103+
status.HTTP_200_OK: ListUsersInRoleWithScopeResponseSerializer(many=True),
104+
status.HTTP_400_BAD_REQUEST: "The request parameters are invalid",
105+
status.HTTP_401_UNAUTHORIZED: "The user is not authenticated",
106+
},
107+
)
108+
def get(self, request: HttpRequest) -> Response:
109+
"""Retrieve all users assigned to a specific role within a scope."""
72110
serializer = ListUsersInRoleWithScopeSerializer(data=request.query_params)
73111
serializer.is_valid(raise_exception=True)
74112

@@ -88,12 +126,19 @@ def get(self, request):
88126
}
89127
)
90128

91-
return Response(response_data, status=status.HTTP_200_OK)
92-
93-
def put(self, request):
94-
"""
95-
Add users to the role.
96-
"""
129+
serializer = ListUsersInRoleWithScopeResponseSerializer(response_data, many=True)
130+
return Response(serializer.data, status=status.HTTP_200_OK)
131+
132+
@apidocs.schema(
133+
body=AddUserToRoleWithScopeSerializer,
134+
responses={
135+
status.HTTP_207_MULTI_STATUS: "The users were added to the role",
136+
status.HTTP_400_BAD_REQUEST: "The request data is invalid",
137+
status.HTTP_401_UNAUTHORIZED: "The user is not authenticated",
138+
},
139+
)
140+
def put(self, request: HttpRequest) -> Response:
141+
"""Assign multiple users to a specific role within a scope."""
97142
serializer = AddUserToRoleWithScopeSerializer(data=request.data)
98143
serializer.is_valid(raise_exception=True)
99144

@@ -115,10 +160,22 @@ def put(self, request):
115160
response_data = {"completed": completed, "errors": errors}
116161
return Response(response_data, status=status.HTTP_207_MULTI_STATUS)
117162

118-
def delete(self, request):
119-
"""
120-
Remove user role from the role.
121-
"""
163+
@apidocs.schema(
164+
parameters=[
165+
apidocs.query_parameter("user", str, description="The user to remove from the role"),
166+
apidocs.query_parameter("role", str, description="The role to remove the user from"),
167+
apidocs.query_parameter("scope", str, description="The scope to remove the user from"),
168+
],
169+
responses={
170+
status.HTTP_200_OK: "The user was removed from the role",
171+
status.HTTP_400_BAD_REQUEST: "The request data is invalid",
172+
status.HTTP_401_UNAUTHORIZED: "The user is not authenticated",
173+
status.HTTP_404_NOT_FOUND: "The user was not found",
174+
status.HTTP_500_INTERNAL_SERVER_ERROR: "The user was not removed from the role",
175+
},
176+
)
177+
def delete(self, request: HttpRequest) -> Response:
178+
"""Remove a user from a specific role within a scope."""
122179
serializer = RemoveUserFromRoleWithScopeSerializer(data=request.query_params)
123180
serializer.is_valid(raise_exception=True)
124181

@@ -139,13 +196,26 @@ def delete(self, request):
139196

140197
class RoleListView(APIView):
141198
"""
142-
Get list of roles with their permissions for a scope.
199+
API view for retrieving role definitions and their associated permissions.
200+
201+
Provides read-only access to role information within specific
202+
authorization scopes, including permission mappings and user counts.
143203
"""
144204

145205
permission_classes = [IsAuthenticated]
146206

147-
def get(self, request):
148-
"""Get list of roles with their permissions for a library."""
207+
@apidocs.schema(
208+
parameters=[
209+
apidocs.query_parameter("scope", str, description="The scope to query roles for"),
210+
],
211+
responses={
212+
status.HTTP_200_OK: ListRolesWithScopeResponseSerializer(many=True),
213+
status.HTTP_400_BAD_REQUEST: "The request parameters are invalid",
214+
status.HTTP_401_UNAUTHORIZED: "The user is not authenticated",
215+
},
216+
)
217+
def get(self, request: HttpRequest) -> Response:
218+
"""Retrieve all roles and their permissions for a specific scope."""
149219
serializer = ListRolesWithScopeSerializer(data=request.query_params)
150220
serializer.is_valid(raise_exception=True)
151221

@@ -159,9 +229,10 @@ def get(self, request):
159229
{
160230
"role": role.name,
161231
"permissions": [perm.action.name for perm in role.permissions] if role.permissions else [],
162-
# TODO: Get user count using a api function
232+
# TODO: Get user count using an API function
163233
"user_count": 0,
164234
}
165235
)
166236

167-
return Response(response_data, status=status.HTTP_200_OK)
237+
serializer = ListRolesWithScopeResponseSerializer(response_data, many=True)
238+
return Response(serializer.data, status=status.HTTP_200_OK)

0 commit comments

Comments
 (0)