Merge pull request #19436 from edx/jkantor/list-user-roles2
Add API call to list roles of the logged-on user
This commit is contained in:
@@ -466,6 +466,15 @@ def unenroll_user_from_all_courses(user_id):
|
||||
return _data_api().unenroll_user_from_all_courses(user_id)
|
||||
|
||||
|
||||
def get_user_roles(user_id):
|
||||
"""
|
||||
Returns a list of all roles that this user has.
|
||||
:param user_id: The id of the selected user.
|
||||
:return: All roles for all courses that this user has.
|
||||
"""
|
||||
return _data_api().get_user_roles(user_id)
|
||||
|
||||
|
||||
def _data_api():
|
||||
"""Returns a Data API.
|
||||
This relies on Django settings to find the appropriate data API.
|
||||
|
||||
@@ -27,6 +27,7 @@ from student.models import (
|
||||
EnrollmentClosedError,
|
||||
NonExistentCourseError
|
||||
)
|
||||
from student.roles import RoleCache
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -337,3 +338,16 @@ def get_course_enrollment_info(course_id, include_expired=False):
|
||||
raise CourseNotFoundError(msg)
|
||||
else:
|
||||
return CourseSerializer(course, include_expired=include_expired).data
|
||||
|
||||
|
||||
def get_user_roles(user_id):
|
||||
"""
|
||||
Returns a list of all roles that this user has.
|
||||
:param user_id: The id of the selected user.
|
||||
:return: All roles for all courses that this user has.
|
||||
"""
|
||||
user = _get_user(user_id)
|
||||
if not hasattr(user, '_roles'):
|
||||
user._roles = RoleCache(user)
|
||||
role_cache = user._roles
|
||||
return role_cache._roles
|
||||
|
||||
@@ -23,7 +23,7 @@ from enrollment.errors import (
|
||||
from enrollment.serializers import CourseEnrollmentSerializer
|
||||
from openedx.core.lib.exceptions import CourseNotFoundError
|
||||
from student.models import AlreadyEnrolledError, CourseEnrollment, CourseFullError, EnrollmentClosedError
|
||||
from student.tests.factories import UserFactory
|
||||
from student.tests.factories import UserFactory, CourseAccessRoleFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
@@ -378,3 +378,19 @@ class EnrollmentDataTest(ModuleStoreTestCase):
|
||||
|
||||
if not include_expired:
|
||||
self.assertNotIn('verified', result_slugs)
|
||||
|
||||
def test_get_roles(self):
|
||||
"""Create a role for a user, then get it"""
|
||||
expected_role = CourseAccessRoleFactory.create(course_id=self.course.id, user=self.user, role="SuperCoolTestRole")
|
||||
roles = data.get_user_roles(self.user.username)
|
||||
self.assertEqual(roles, {expected_role})
|
||||
|
||||
def test_get_roles_no_roles(self):
|
||||
"""Get roles for a user who has no roles"""
|
||||
roles = data.get_user_roles(self.user.username)
|
||||
self.assertEqual(roles, set())
|
||||
|
||||
def test_get_roles_invalid_user(self):
|
||||
"""Get roles for a user that doesn't exist"""
|
||||
with pytest.raises(UserNotFoundError):
|
||||
data.get_user_roles("i_dont_exist_and_should_raise_an_error")
|
||||
|
||||
@@ -45,6 +45,7 @@ from student.roles import CourseStaffRole
|
||||
from student.tests.factories import AdminFactory, UserFactory, SuperuserFactory
|
||||
from util.models import RateLimitConfiguration
|
||||
from util.testing import UrlResetMixin
|
||||
from six import text_type
|
||||
|
||||
|
||||
class EnrollmentTestMixin(object):
|
||||
@@ -1413,3 +1414,72 @@ class UnenrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase):
|
||||
url = reverse('unenrollment')
|
||||
headers = self.build_jwt_headers(submitting_user)
|
||||
return self.client.post(url, json.dumps(data), content_type='application/json', **headers)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
class UserRoleTest(ModuleStoreTestCase):
|
||||
"""
|
||||
Tests the API call to list user roles.
|
||||
"""
|
||||
USERNAME = "Bob"
|
||||
EMAIL = "bob@example.com"
|
||||
PASSWORD = "edx"
|
||||
|
||||
ENABLED_CACHES = ['default']
|
||||
|
||||
def setUp(self):
|
||||
""" Create a course and user, then log in. """
|
||||
super(UserRoleTest, self).setUp()
|
||||
self.course1 = CourseFactory.create(emit_signals=True, org="org1", course="course1", run="run1")
|
||||
self.course2 = CourseFactory.create(emit_signals=True, org="org2", course="course2", run="run2")
|
||||
self.user = UserFactory.create(
|
||||
username=self.USERNAME,
|
||||
email=self.EMAIL,
|
||||
password=self.PASSWORD,
|
||||
)
|
||||
self.client.login(username=self.USERNAME, password=self.PASSWORD)
|
||||
|
||||
def _create_expected_role_dict(self, course, role):
|
||||
return {
|
||||
'course_id': text_type(course.id),
|
||||
'org': course.org,
|
||||
'role': role.ROLE,
|
||||
}
|
||||
|
||||
def _assert_roles(self, expected_response):
|
||||
response = self.client.get(reverse('roles'))
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = json.loads(response.content)
|
||||
self.assertEqual(response_data, expected_response)
|
||||
|
||||
def test_not_logged_in(self):
|
||||
self.client.logout()
|
||||
response = self.client.get(reverse('roles'))
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
def test_roles_no_roles(self):
|
||||
self._assert_roles([])
|
||||
|
||||
def test_roles(self):
|
||||
role1 = CourseStaffRole(self.course1.id)
|
||||
role1.add_users(self.user)
|
||||
expected_role1 = self._create_expected_role_dict(self.course1, role1)
|
||||
self._assert_roles([expected_role1])
|
||||
role2 = CourseStaffRole(self.course2.id)
|
||||
role2.add_users(self.user)
|
||||
expected_role2 = self._create_expected_role_dict(self.course2, role2)
|
||||
self._assert_roles([expected_role2, expected_role1])
|
||||
|
||||
def test_roles_exception(self):
|
||||
with patch('enrollment.api.get_user_roles') as mock_get_user_roles:
|
||||
mock_get_user_roles.side_effect = Exception()
|
||||
response = self.client.get(reverse('roles'))
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
expected_response = {
|
||||
"message": (
|
||||
u"An error occurred while retrieving roles for user '{username}"
|
||||
).format(username=self.user.username)
|
||||
}
|
||||
response_data = json.loads(response.content)
|
||||
self.assertEqual(response_data, expected_response)
|
||||
|
||||
@@ -5,7 +5,7 @@ URLs for the Enrollment API
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import EnrollmentCourseDetailView, EnrollmentListView, EnrollmentView, UnenrollmentView
|
||||
from .views import EnrollmentCourseDetailView, EnrollmentListView, EnrollmentView, UnenrollmentView, EnrollmentUserRolesView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^enrollment/{username},{course_key}$'.format(
|
||||
@@ -18,4 +18,5 @@ urlpatterns = [
|
||||
url(r'^course/{course_key}$'.format(course_key=settings.COURSE_ID_PATTERN),
|
||||
EnrollmentCourseDetailView.as_view(), name='courseenrollmentdetails'),
|
||||
url(r'^unenroll/$', UnenrollmentView.as_view(), name='unenrollment'),
|
||||
url(r'^roles/$', EnrollmentUserRolesView.as_view(), name='roles'),
|
||||
]
|
||||
|
||||
@@ -205,6 +205,55 @@ class EnrollmentView(APIView, ApiKeyPermissionMixIn):
|
||||
)
|
||||
|
||||
|
||||
class EnrollmentUserRolesView(APIView):
|
||||
"""
|
||||
**Use Case**
|
||||
|
||||
Get the roles for the current logged-in user.
|
||||
|
||||
**Example Requests**
|
||||
|
||||
GET /api/enrollment/v1/roles/
|
||||
|
||||
**Response Values**
|
||||
|
||||
If the request is successful, an HTTP 200 "OK" response is
|
||||
returned along with a collection of user roles for the
|
||||
logged-in user
|
||||
|
||||
"""
|
||||
authentication_classes = (JwtAuthentication,
|
||||
OAuth2AuthenticationAllowInactiveUser,
|
||||
EnrollmentCrossDomainSessionAuth)
|
||||
permission_classes = ApiKeyHeaderPermissionIsAuthenticated,
|
||||
throttle_classes = EnrollmentUserThrottle,
|
||||
|
||||
@method_decorator(ensure_csrf_cookie_cross_domain)
|
||||
def get(self, request):
|
||||
"""
|
||||
Gets a list of all roles for the currently logged-in user
|
||||
"""
|
||||
try:
|
||||
roles_data = api.get_user_roles(request.user.username)
|
||||
except Exception:
|
||||
return Response(
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
data={
|
||||
"message": (
|
||||
u"An error occurred while retrieving roles for user '{username}"
|
||||
).format(username=request.user.username)
|
||||
}
|
||||
)
|
||||
return Response([
|
||||
{
|
||||
"org": role.org,
|
||||
"course_id": text_type(role.course_id),
|
||||
"role": role.role
|
||||
}
|
||||
for role in roles_data
|
||||
])
|
||||
|
||||
|
||||
@can_disable_rate_limit
|
||||
class EnrollmentCourseDetailView(APIView):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user