|
4 | 4 |
|
5 | 5 |
|
6 | 6 | import json |
| 7 | + |
7 | 8 | from opaque_keys.edx.keys import UsageKey |
| 9 | +from openedx_authz.constants.roles import COURSE_AUDITOR, COURSE_STAFF |
8 | 10 |
|
9 | 11 | from cms.djangoapps.contentstore.tests.test_course_settings import CourseTestCase |
| 12 | +from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient |
10 | 13 | from cms.djangoapps.contentstore.utils import reverse_course_url, reverse_usage_url |
| 14 | +from common.djangoapps.student.tests.factories import UserFactory |
| 15 | +from openedx.core.djangoapps.authz.tests.mixins import CourseAuthzTestMixin |
11 | 16 | from openedx.core.lib.xblock_utils import get_course_update_items |
12 | 17 | from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order |
13 | 18 |
|
@@ -312,3 +317,125 @@ def test_course_update_id(self): |
312 | 317 | course_updates = modulestore().get_item(updates_location) |
313 | 318 | del course_updates.items[0]["status"] |
314 | 319 | self.assertEqual(course_updates.items, [{'date': update_date, 'content': update_content, 'id': 2}]) |
| 320 | + |
| 321 | + |
| 322 | +class CourseUpdateAuthzTest(CourseAuthzTestMixin, CourseTestCase): |
| 323 | + """ |
| 324 | + Tests course_info_update_handler authorization using openedx-authz. |
| 325 | + """ |
| 326 | + |
| 327 | + authz_roles_to_assign = [COURSE_STAFF.external_key] |
| 328 | + |
| 329 | + @property |
| 330 | + def course_key(self): |
| 331 | + return self.course.id |
| 332 | + |
| 333 | + def create_update_url(self, provided_id=None): |
| 334 | + kwargs = {'provided_id': str(provided_id)} if provided_id else None |
| 335 | + return reverse_course_url('course_info_update_handler', self.course.id, kwargs=kwargs) |
| 336 | + |
| 337 | + def setUp(self): |
| 338 | + super().setUp() |
| 339 | + self.auditor_user = UserFactory() |
| 340 | + self.add_user_to_role(self.auditor_user, COURSE_AUDITOR.external_key) |
| 341 | + |
| 342 | + self.staff_client = self._make_client_for_user(self.authorized_user) |
| 343 | + self.auditor_client = self._make_client_for_user(self.auditor_user) |
| 344 | + self.unauthorized_client = self._make_client_for_user(self.unauthorized_user) |
| 345 | + |
| 346 | + def _make_client_for_user(self, user): |
| 347 | + client = AjaxEnabledTestClient() |
| 348 | + client.login(username=user.username, password='Password1234') |
| 349 | + return client |
| 350 | + |
| 351 | + def _create_update(self, client): |
| 352 | + return client.ajax_post( |
| 353 | + self.create_update_url(), |
| 354 | + {'content': 'Test update', 'date': 'January 1, 2026'}, |
| 355 | + ) |
| 356 | + |
| 357 | + # -- Authorized user (course_staff): full access -- |
| 358 | + |
| 359 | + def test_authorized_user_can_get(self): |
| 360 | + resp = self.staff_client.get_json(self.create_update_url()) |
| 361 | + self.assertEqual(resp.status_code, 200) |
| 362 | + |
| 363 | + def test_authorized_user_can_post(self): |
| 364 | + resp = self._create_update(self.staff_client) |
| 365 | + self.assertEqual(resp.status_code, 200) |
| 366 | + |
| 367 | + def test_authorized_user_can_put(self): |
| 368 | + update_id = self._create_update(self.staff_client).json()['id'] |
| 369 | + resp = self.staff_client.ajax_post( |
| 370 | + self.create_update_url(update_id), |
| 371 | + {'content': 'Updated', 'date': 'January 2, 2026'}, |
| 372 | + HTTP_X_HTTP_METHOD_OVERRIDE="PUT", |
| 373 | + REQUEST_METHOD="POST", |
| 374 | + ) |
| 375 | + self.assertEqual(resp.status_code, 200) |
| 376 | + |
| 377 | + def test_authorized_user_can_delete(self): |
| 378 | + update_id = self._create_update(self.staff_client).json()['id'] |
| 379 | + resp = self.staff_client.delete(self.create_update_url(update_id)) |
| 380 | + self.assertEqual(resp.status_code, 200) |
| 381 | + |
| 382 | + # -- View-only user (course_auditor): GET only -- |
| 383 | + |
| 384 | + def test_auditor_can_get(self): |
| 385 | + resp = self.auditor_client.get_json(self.create_update_url()) |
| 386 | + self.assertEqual(resp.status_code, 200) |
| 387 | + |
| 388 | + def test_auditor_cannot_post(self): |
| 389 | + resp = self._create_update(self.auditor_client) |
| 390 | + self.assertEqual(resp.status_code, 403) |
| 391 | + |
| 392 | + def test_auditor_cannot_put(self): |
| 393 | + update_id = self._create_update(self.staff_client).json()['id'] |
| 394 | + resp = self.auditor_client.ajax_post( |
| 395 | + self.create_update_url(update_id), |
| 396 | + {'content': 'Test', 'date': 'January 2, 2026'}, |
| 397 | + HTTP_X_HTTP_METHOD_OVERRIDE="PUT", |
| 398 | + REQUEST_METHOD="POST", |
| 399 | + ) |
| 400 | + self.assertEqual(resp.status_code, 403) |
| 401 | + |
| 402 | + def test_auditor_cannot_delete(self): |
| 403 | + update_id = self._create_update(self.staff_client).json()['id'] |
| 404 | + resp = self.auditor_client.delete(self.create_update_url(update_id)) |
| 405 | + self.assertEqual(resp.status_code, 403) |
| 406 | + |
| 407 | + # -- Unauthorized user: no access -- |
| 408 | + |
| 409 | + def test_unauthorized_user_cannot_get(self): |
| 410 | + resp = self.unauthorized_client.get_json(self.create_update_url()) |
| 411 | + self.assertEqual(resp.status_code, 403) |
| 412 | + |
| 413 | + def test_unauthorized_user_cannot_post(self): |
| 414 | + resp = self._create_update(self.unauthorized_client) |
| 415 | + self.assertEqual(resp.status_code, 403) |
| 416 | + |
| 417 | + # -- Staff/superuser without authz role: access via enforcer admin check -- |
| 418 | + |
| 419 | + def test_django_staff_without_role_can_get(self): |
| 420 | + staff_user = UserFactory(is_staff=True) |
| 421 | + client = self._make_client_for_user(staff_user) |
| 422 | + resp = client.get_json(self.create_update_url()) |
| 423 | + self.assertEqual(resp.status_code, 200) |
| 424 | + |
| 425 | + def test_django_staff_without_role_can_post(self): |
| 426 | + staff_user = UserFactory(is_staff=True) |
| 427 | + client = self._make_client_for_user(staff_user) |
| 428 | + resp = self._create_update(client) |
| 429 | + self.assertEqual(resp.status_code, 200) |
| 430 | + |
| 431 | + def test_superuser_without_role_can_get(self): |
| 432 | + superuser = UserFactory(is_superuser=True) |
| 433 | + client = self._make_client_for_user(superuser) |
| 434 | + resp = client.get_json(self.create_update_url()) |
| 435 | + self.assertEqual(resp.status_code, 200) |
| 436 | + |
| 437 | + def test_superuser_without_role_can_post(self): |
| 438 | + superuser = UserFactory(is_superuser=True) |
| 439 | + client = self._make_client_for_user(superuser) |
| 440 | + resp = self._create_update(client) |
| 441 | + self.assertEqual(resp.status_code, 200) |
0 commit comments