-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: course_roles mfe-course authoring helper function (#33599)
* feat: Roles 15 - permission checks back end changes part 1 (#33347) * test: add test cases for permission list check functions * test: update tests * feat: add helper functions to check lists of permissions * style: improve code style * feat: add course roles checks in the contentstore app * feat: add course roles checks in the student app * feat: add course roles checks in the lms discussion app * feat: add course roles checks in the lms instructor app * feat: add course roles checks in the Learning Sequences package * style: fix code style * fix: course_permission_check calls * feat: add validation for AnonymousUser in course permission check helper functions * fix: disable some pylint warnings * test: update number of querys asserted in has_course_author_access * feat: add helper functions to check course or organization permissions * test: update course_roles tests * feat: replace course or organization helper functions in auth * docs: update course_roles docstrings * feat: ROLES-23 Create permissions in db table (#33394) * feat: add migration to load permissions in the database * feat: add Permission enum * feat: change Permission enum name to CourseRolesPermission * feat: replace permission constants with the CourseRolesPermission enum * feat: add unique decorator to permissions enum * feat: add course_roles_permissions dict with names and descriptions (with i18n) * docs: add CourseRolesPermission docstring * style: fix pylint errors * style: fix pylint errors * docs: add permissions module docstring * feat: add helper function to get user permissions for a course * test: add test for get_all_user_permissions_for_a_course * feat: add views to coures roles api * test: add test for course roles views * feat: add urls for course roles api * feat: add course roles api urls to lms and cms * docs: add comment to indicate which urls are from course roles api * feat: add translation to ValueError exception message * feat: add translation to ValueError exception message * feat: raise exeption if course does not exist * feat: add instance permissions in get_all_user_permissions_for_a_course helper * feat: improve validations in get_all_user_permissions_for_a_course * feat: improve validations in UserPermissionsView * test: update get user permissions tests * docs: update UserPermissionsView docstring * feat: change message errors * docs: update docstrings in test_views * fix: add missing super method call in a class * fix: add password to test user * fix: chain re-raising exceptions
- Loading branch information
1 parent
affa8f9
commit 4ac95de
Showing
10 changed files
with
315 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
""" | ||
Tests for the course_roles views. | ||
""" | ||
import ddt | ||
from django.urls import reverse | ||
from django.utils.http import urlencode | ||
from rest_framework import status | ||
from rest_framework.test import APIClient | ||
from organizations.tests.factories import OrganizationFactory | ||
|
||
from common.djangoapps.student.tests.factories import UserFactory | ||
from openedx.core.djangoapps.course_roles.models import ( | ||
CourseRolesPermission, | ||
CourseRolesRole, | ||
CourseRolesService, | ||
CourseRolesUserRole, | ||
) | ||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase | ||
from xmodule.modulestore.tests.factories import CourseFactory | ||
|
||
|
||
@ddt.ddt | ||
class UserPermissionsViewTestCase(SharedModuleStoreTestCase): | ||
""" | ||
Tests for the UserPermissionsView. | ||
""" | ||
def setUp(self): | ||
super().setUp() | ||
self.client = APIClient() | ||
self.user_1 = UserFactory(username="test_user_1", password="test") | ||
self.organization_1 = OrganizationFactory(name="test_organization_1") | ||
self.course_1 = CourseFactory.create( | ||
display_name="test course 1", run="Testing_course_1", org=self.organization_1.name | ||
) | ||
self.role_1 = CourseRolesRole.objects.create(name="test_role_1") | ||
self.service = CourseRolesService.objects.create(name="test_service") | ||
self.role_1.services.add(self.service) | ||
self.permission_1 = CourseRolesPermission.objects.create(name="test_permission_1") | ||
self.permission_2 = CourseRolesPermission.objects.create(name="test_permission_2") | ||
self.role_1.permissions.add(self.permission_1) | ||
self.role_1.permissions.add(self.permission_2) | ||
CourseRolesUserRole.objects.create( | ||
user=self.user_1, role=self.role_1, course_id=self.course_1.id, org=self.organization_1 | ||
) | ||
|
||
def test_get_user_permissions_without_login(self): | ||
# Test get user permissions without login. | ||
querykwargs = {'course_id': self.course_1.id, 'user_id': self.user_1.id} | ||
url = f'{reverse("course_roles_api:user_permissions")}?{urlencode(querykwargs)}' | ||
response = self.client.get(url) | ||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) | ||
|
||
def test_get_user_permissions_view(self): | ||
# Test get user permissions view with valid queryargs. | ||
querykwargs = {'course_id': self.course_1.id, 'user_id': self.user_1.id} | ||
url = f'{reverse("course_roles_api:user_permissions")}?{urlencode(querykwargs)}' | ||
expected_api_response = {'permissions': {self.permission_1.name, self.permission_2.name}} | ||
# Ensure the view returns a 200 OK status code | ||
self.client.login(username=self.user_1, password='test') | ||
response = self.client.get(url) | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
# Ensure the view returns the correct permissions for the user | ||
self.assertEqual(response.data['permissions'], expected_api_response['permissions']) | ||
|
||
@ddt.data( | ||
(None, None), | ||
(None, "course_id"), | ||
(1, None) | ||
) | ||
@ddt.unpack | ||
def test_get_user_permission_view_without_queryargs(self, user_id, course_id): | ||
# Test get user permissions without queryargs. | ||
querykwargs = {'course_id': course_id, 'user_id': user_id} | ||
querykwargs = {k: v for k, v in querykwargs.items() if v is not None} | ||
url = f'{reverse("course_roles_api:user_permissions")}?{urlencode(querykwargs)}' | ||
self.client.login(username=self.user_1, password='test') | ||
response = self.client.get(url) | ||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
|
||
def test_get_user_permission_view_with_invalid_queryargs(self): | ||
# Test get user permissions with invalid queryargs. | ||
self.client.login(username=self.user_1, password='test') | ||
org = 'org1' | ||
number = 'course1' | ||
run = 'run1' | ||
course_id = self.store.make_course_key(org, number, run) | ||
querykwargs = {'course_id': course_id, 'user_id': self.user_1.id} | ||
url = f'{reverse("course_roles_api:user_permissions")}?{urlencode(querykwargs)}' | ||
response = self.client.get(url) | ||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) | ||
querykwargs = {'course_id': self.course_1.id, 'user_id': 999} | ||
url = f'{reverse("course_roles_api:user_permissions")}?{urlencode(querykwargs)}' | ||
response = self.client.get(url) | ||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
""" | ||
URL configuration for course roles api. | ||
""" | ||
from django.urls import path | ||
|
||
from .views import UserPermissionsView | ||
|
||
app_name = 'course_roles_api' | ||
|
||
urlpatterns = [ | ||
path('v1/user_permissions/', UserPermissionsView.as_view(), name='user_permissions'), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
""" | ||
Views for the course roles API. | ||
""" | ||
from rest_framework.exceptions import ParseError, NotFound | ||
from rest_framework.views import APIView | ||
from rest_framework.response import Response | ||
from opaque_keys import InvalidKeyError | ||
from opaque_keys.edx.keys import CourseKey | ||
|
||
from openedx.core.lib.api.view_utils import view_auth_classes | ||
from openedx.core.djangoapps.course_roles.helpers import get_all_user_permissions_for_a_course | ||
|
||
|
||
@view_auth_classes() | ||
class UserPermissionsView(APIView): | ||
""" | ||
View for getting all permissions for a user in a course. | ||
""" | ||
def get(self, request): | ||
""" | ||
Get all permissions for a user in a course. | ||
**Permissions**: User must be authenticated. | ||
**Response Format**: | ||
```json | ||
{ | ||
"permissions": [(str) permission_name, ...] | ||
} | ||
``` | ||
**Response Error Codes**: | ||
- 400: If the user_id or course_id parameters are missing or are invalid. | ||
- 404: If the user or course does not exist. | ||
""" | ||
user_id = self.request.query_params.get('user_id', None) | ||
if user_id is None: | ||
raise ParseError('Required user_id parameter is missing') | ||
course_id = self.request.query_params.get('course_id', None) | ||
if course_id is None: | ||
raise ParseError('Required course_id parameter is missing') | ||
try: | ||
course_key = CourseKey.from_string(course_id) | ||
except InvalidKeyError as exc: | ||
raise ParseError('Invalid course_id parameter') from exc | ||
try: | ||
permissions = { | ||
'permissions': get_all_user_permissions_for_a_course(user_id, course_key), | ||
} | ||
except ValueError as exc: | ||
raise NotFound(str(exc)) from exc | ||
return Response(permissions) |