|
3 | 3 | """ |
4 | 4 | from django.urls import reverse |
5 | 5 | from rest_framework import status |
| 6 | +from rest_framework.test import APIClient |
6 | 7 |
|
| 8 | +from cms.djangoapps.contentstore.api.tests.base import BaseCourseViewTest |
7 | 9 | from cms.djangoapps.contentstore.course_group_config import ( |
8 | 10 | CONTENT_GROUP_CONFIGURATION_NAME, |
9 | 11 | ) |
10 | 12 | from cms.djangoapps.contentstore.tests.utils import CourseTestCase |
| 13 | +from common.djangoapps.student.tests.factories import UserFactory |
| 14 | +from openedx_authz.constants.roles import COURSE_DATA_RESEARCHER, COURSE_STAFF |
| 15 | +from openedx.core.djangoapps.authz.tests.mixins import CourseAuthzTestMixin |
11 | 16 | from xmodule.partitions.partitions import ( |
12 | 17 | Group, |
13 | 18 | UserPartition, |
@@ -53,3 +58,61 @@ def test_success_response(self): |
53 | 58 | self.assertContains(response, "Group C") |
54 | 59 | self.assertContains(response, CONTENT_GROUP_CONFIGURATION_NAME) |
55 | 60 | self.assertEqual(response.status_code, status.HTTP_200_OK) |
| 61 | + |
| 62 | +class CourseGroupConfigurationsAuthzTest(CourseAuthzTestMixin, BaseCourseViewTest): |
| 63 | + """ |
| 64 | + Tests Course Group Configuration API authorization using openedx-authz. |
| 65 | + The endpoint uses COURSES_MANAGE_GROUP_CONFIGURATIONS permission. |
| 66 | + """ |
| 67 | + |
| 68 | + view_name = "cms.djangoapps.contentstore:v1:group_configurations" |
| 69 | + authz_roles_to_assign = [COURSE_STAFF.external_key] |
| 70 | + |
| 71 | + def test_authorized_user_can_access(self): |
| 72 | + """User with COURSE_STAFF role can access.""" |
| 73 | + resp = self.authorized_client.get(self.get_url(self.course_key)) |
| 74 | + self.assertEqual(resp.status_code, status.HTTP_200_OK) |
| 75 | + |
| 76 | + def test_unauthorized_user_cannot_access(self): |
| 77 | + """User without role cannot access.""" |
| 78 | + resp = self.unauthorized_client.get(self.get_url(self.course_key)) |
| 79 | + self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN) |
| 80 | + |
| 81 | + def test_role_scoped_to_course(self): |
| 82 | + """Authorization should only apply to the assigned course.""" |
| 83 | + other_course = self.store.create_course("OtherOrg", "OtherCourse", "Run", self.staff.id) |
| 84 | + |
| 85 | + resp = self.authorized_client.get(self.get_url(other_course.id)) |
| 86 | + self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN) |
| 87 | + |
| 88 | + def test_staff_user_allowed_via_legacy(self): |
| 89 | + """ |
| 90 | + Staff users should still pass through legacy fallback. |
| 91 | + """ |
| 92 | + self.client.login(username=self.staff.username, password=self.password) |
| 93 | + |
| 94 | + resp = self.client.get(self.get_url(self.course_key)) |
| 95 | + self.assertEqual(resp.status_code, status.HTTP_200_OK) |
| 96 | + |
| 97 | + def test_superuser_allowed(self): |
| 98 | + """Superusers should always be allowed.""" |
| 99 | + superuser = UserFactory(is_superuser=True) |
| 100 | + |
| 101 | + client = APIClient() |
| 102 | + client.force_authenticate(user=superuser) |
| 103 | + |
| 104 | + resp = client.get(self.get_url(self.course_key)) |
| 105 | + self.assertEqual(resp.status_code, status.HTTP_200_OK) |
| 106 | + |
| 107 | + def test_non_staff_user_cannot_access(self): |
| 108 | + """ |
| 109 | + User without required permissions should be denied. |
| 110 | + This case validates that a non-staff user doesn't get access. |
| 111 | + """ |
| 112 | + non_staff_user = UserFactory() |
| 113 | + non_staff_client = APIClient() |
| 114 | + self.add_user_to_role(non_staff_user, COURSE_DATA_RESEARCHER.external_key) |
| 115 | + non_staff_client.force_authenticate(user=non_staff_user) |
| 116 | + |
| 117 | + resp = non_staff_client.get(self.get_url(self.course_key)) |
| 118 | + self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN) |
0 commit comments