From bd5386356a9836ad377a933dbb79a8b2ecaf8a5f Mon Sep 17 00:00:00 2001 From: KyryloKireiev Date: Tue, 31 Oct 2023 16:02:36 +0200 Subject: [PATCH 1/2] feat: [FC-0031] Add optional field 'is_enrolled' to course detail view --- lms/djangoapps/course_api/serializers.py | 11 +++++ .../course_api/tests/test_serializers.py | 43 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/lms/djangoapps/course_api/serializers.py b/lms/djangoapps/course_api/serializers.py index dbf4736d4e8..e4855e05cb1 100644 --- a/lms/djangoapps/course_api/serializers.py +++ b/lms/djangoapps/course_api/serializers.py @@ -5,6 +5,8 @@ import urllib +from common.djangoapps.student.models import CourseEnrollment +from django.contrib.auth import get_user_model from django.urls import reverse from edx_django_utils import monitoring as monitoring_utils from rest_framework import serializers @@ -164,10 +166,19 @@ def to_representation(self, instance): """ Get the `certificate_available_date` in response if the `certificates.auto_certificate_generation` waffle switch is enabled + + Get the 'is_enrolled' in response + if user is authenticated and 'username' is in query params. """ response = super().to_representation(instance) if can_show_certificate_available_date_field(instance): response['certificate_available_date'] = instance.certificate_available_date + + requested_user = self.context['request'].query_params.get('username', None) + if self.context['request'].user.is_authenticated and requested_user: + User = get_user_model() + requested_user = User.objects.get(username=requested_user) + response['is_enrolled'] = CourseEnrollment.is_enrolled(requested_user, instance.id) return response diff --git a/lms/djangoapps/course_api/tests/test_serializers.py b/lms/djangoapps/course_api/tests/test_serializers.py index ec11fc05de6..b6069896aef 100644 --- a/lms/djangoapps/course_api/tests/test_serializers.py +++ b/lms/djangoapps/course_api/tests/test_serializers.py @@ -188,6 +188,49 @@ def test_basic(self): ) self.assertDictEqual(result, self.expected_data) + @mock.patch('lms.djangoapps.course_api.serializers.CourseEnrollment.is_enrolled', return_value=True) + def test_is_enrolled_field_true(self, mock_is_enrolled): + course = self.create_course() + result = self._get_result_with_query_param(course) + assert result['is_enrolled'] is True + mock_is_enrolled.assert_called_once() + + @mock.patch('lms.djangoapps.course_api.serializers.CourseEnrollment.is_enrolled', return_value=False) + def test_is_enrolled_field_false(self, mock_is_enrolled): + course = self.create_course() + result = self._get_result_with_query_param(course) + assert result['is_enrolled'] is False + mock_is_enrolled.assert_called_once() + + def test_is_enrolled_field_anonymous_user(self): + course = self.create_course() + result = self._get_anonymous_result(course) + self.assertNotIn('is_enrolled', result) + + def _get_anonymous_request(self): + return Request(self.request_factory.get('/')) + + def _get_anonymous_result(self, course): + course_overview = CourseOverview.get_from_id(course.id) + return self.serializer_class(course_overview, context={'request': self._get_anonymous_request()}).data + + def _get_result_with_query_param(self, course): + """ + Return the CourseSerializer for the specified course with 'username' in query params. + """ + course_overview = CourseOverview.get_from_id(course.id) + return self.serializer_class(course_overview, context={'request': self._get_request_with_query_param()}).data + + def _get_request_with_query_param(self, user=None): + """ + Build a Request object for the specified user with 'username' in query params. + """ + if user is None: + user = self.honor_user + request = Request(self.request_factory.get('/', {'username': user.username})) + request.user = user + return request + class TestCourseKeySerializer(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring From d344a7eef623725221358c23d20af9326f7cc59c Mon Sep 17 00:00:00 2001 From: Glib Glugovskiy Date: Fri, 15 Dec 2023 20:28:31 +0200 Subject: [PATCH 2/2] fix: [FC-0031] Restrict access to is_enrolled field --- lms/djangoapps/course_api/serializers.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lms/djangoapps/course_api/serializers.py b/lms/djangoapps/course_api/serializers.py index e4855e05cb1..5fc71137678 100644 --- a/lms/djangoapps/course_api/serializers.py +++ b/lms/djangoapps/course_api/serializers.py @@ -167,18 +167,22 @@ def to_representation(self, instance): Get the `certificate_available_date` in response if the `certificates.auto_certificate_generation` waffle switch is enabled - Get the 'is_enrolled' in response - if user is authenticated and 'username' is in query params. + Get the 'is_enrolled' in response if 'username' is in query params, + user is staff, superuser, or user is authenticated and + the has the same 'username' as the 'username' in the query params. """ response = super().to_representation(instance) if can_show_certificate_available_date_field(instance): response['certificate_available_date'] = instance.certificate_available_date - requested_user = self.context['request'].query_params.get('username', None) - if self.context['request'].user.is_authenticated and requested_user: - User = get_user_model() - requested_user = User.objects.get(username=requested_user) - response['is_enrolled'] = CourseEnrollment.is_enrolled(requested_user, instance.id) + requested_username = self.context['request'].query_params.get('username', None) + if requested_username: + user = self.context['request'].user + if ((user.is_authenticated and user.username == requested_username) + or user.is_staff or user.is_superuser): + User = get_user_model() + requested_user = User.objects.get(username=requested_username) + response['is_enrolled'] = CourseEnrollment.is_enrolled(requested_user, instance.id) return response