* chore: update API endpoints to support default JWT auth The default DRF Auth classes were recently updated to allow for both JWT and Session auth by default. Any endpoint that overrides the AUTHENTICATION_CLASSES but has just session, just JWT or just both of those should be updated to remove the override. Details in https://github.com/openedx/edx-platform/issues/33662
162 lines
5.4 KiB
Python
162 lines
5.4 KiB
Python
"""
|
|
Views served by the Agreements app
|
|
"""
|
|
|
|
from django.conf import settings
|
|
from rest_framework import status
|
|
from rest_framework.views import APIView
|
|
from rest_framework.response import Response
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from opaque_keys.edx.keys import CourseKey
|
|
|
|
from common.djangoapps.student import auth
|
|
from common.djangoapps.student.roles import CourseStaffRole
|
|
from openedx.core.djangoapps.agreements.api import (
|
|
create_integrity_signature,
|
|
create_lti_pii_signature,
|
|
get_integrity_signature,
|
|
)
|
|
from openedx.core.djangoapps.agreements.serializers import IntegritySignatureSerializer, LTIPIISignatureSerializer
|
|
|
|
|
|
def is_user_course_or_global_staff(user, course_id):
|
|
"""
|
|
Return whether a user is course staff for a given course, described by the course_id,
|
|
or is global staff.
|
|
"""
|
|
|
|
return user.is_staff or auth.user_has_role(user, CourseStaffRole(CourseKey.from_string(course_id)))
|
|
|
|
|
|
class AuthenticatedAPIView(APIView):
|
|
"""
|
|
Authenticated API View.
|
|
"""
|
|
permission_classes = (IsAuthenticated,)
|
|
|
|
|
|
class IntegritySignatureView(AuthenticatedAPIView):
|
|
"""
|
|
Endpoint for an Integrity Signature
|
|
/integrity_signature/{course_id}
|
|
|
|
Supports:
|
|
HTTP GET: Returns an existing signed integrity agreement (by course id and user)
|
|
|
|
HTTP GET
|
|
** Scenarios **
|
|
?username=xyz
|
|
returns an existing signed integrity agreement for the given user and course
|
|
|
|
HTTP POST
|
|
* If an integrity signature does not exist for the user + course, creates one and
|
|
returns it. If one does exist, returns the existing signature.
|
|
"""
|
|
|
|
def get(self, request, course_id):
|
|
"""
|
|
In order to check whether the user has signed the integrity agreement for a given course.
|
|
|
|
Should return the following:
|
|
username (str)
|
|
course_id (str)
|
|
created_at (str)
|
|
|
|
If a username is not given, it should default to the requesting user (or masqueraded user).
|
|
Only staff should be able to access this endpoint for other users.
|
|
"""
|
|
if not settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE'):
|
|
return Response(
|
|
status=status.HTTP_404_NOT_FOUND,
|
|
)
|
|
|
|
# check that user can make request
|
|
user = request.user.username
|
|
requested_user = request.GET.get('username')
|
|
is_staff = is_user_course_or_global_staff(request.user, course_id)
|
|
|
|
if not is_staff and requested_user and (user != requested_user):
|
|
return Response(
|
|
status=status.HTTP_403_FORBIDDEN,
|
|
data={
|
|
"message": "User does not have permission to view integrity agreement."
|
|
}
|
|
)
|
|
|
|
username = requested_user if requested_user else user
|
|
signature = get_integrity_signature(username, course_id)
|
|
|
|
if signature is None:
|
|
return Response(
|
|
status=status.HTTP_404_NOT_FOUND,
|
|
)
|
|
|
|
serializer = IntegritySignatureSerializer(signature)
|
|
return Response(serializer.data)
|
|
|
|
def post(self, request, course_id):
|
|
"""
|
|
Create an integrity signature for the requesting user and course. If a signature
|
|
already exists, returns the existing signature instead of creating a new one.
|
|
|
|
/api/agreements/v1/integrity_signature/{course_id}
|
|
|
|
Example response:
|
|
{
|
|
username: "janedoe",
|
|
course_id: "org.2/course_2/Run_2",
|
|
created_at: "2021-04-23T18:25:43.511Z"
|
|
}
|
|
"""
|
|
if not settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE'):
|
|
return Response(
|
|
status=status.HTTP_404_NOT_FOUND,
|
|
)
|
|
|
|
username = request.user.username
|
|
signature = create_integrity_signature(username, course_id)
|
|
serializer = IntegritySignatureSerializer(signature)
|
|
return Response(serializer.data)
|
|
|
|
|
|
class LTIPIISignatureView(AuthenticatedAPIView):
|
|
"""
|
|
Endpoint for a LTI PII Signature
|
|
/lti_pii_signature/{course_id}
|
|
|
|
HTTP POST
|
|
* If an LTI PII signature does not exist for the user + course, creates one and
|
|
returns it. If one does exist, returns the existing signature.
|
|
"""
|
|
|
|
def post(self, request, course_id):
|
|
"""
|
|
Create an LTI PII signature for the requesting user and course. If a signature
|
|
already exists, returns the existing signature instead of creating a new one.
|
|
|
|
/api/agreements/v1/lti_pii_signature/{course_id}
|
|
|
|
Example response:
|
|
{
|
|
username: "janedoe",
|
|
course_id: "org.2/course_2/Run_2",
|
|
created_at: "2021-04-23T18:25:43.511Z"
|
|
}
|
|
"""
|
|
if not settings.FEATURES.get('ENABLE_LTI_PII_ACKNOWLEDGEMENT'):
|
|
return Response(
|
|
status=status.HTTP_404_NOT_FOUND,
|
|
)
|
|
|
|
serializer = LTIPIISignatureSerializer(data=request.data)
|
|
statusStr = ""
|
|
if serializer.is_valid():
|
|
username = request.user.username
|
|
lti_tools = request.data.get("lti_tools")
|
|
signature = create_lti_pii_signature(username, course_id, lti_tools)
|
|
serializer = LTIPIISignatureSerializer(signature)
|
|
statusStr = status.HTTP_200_OK
|
|
else:
|
|
statusStr = status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
return Response(data=serializer.data, status=statusStr)
|