Commit 3f3e9724 by Julia Hansbrough Committed by Sarina Canelake

Add fallback for org based roles

parent 29bb179a
...@@ -6,14 +6,15 @@ import random ...@@ -6,14 +6,15 @@ import random
from chrono import Timer from chrono import Timer
from mock import patch, Mock from mock import patch, Mock
import ddt
from django.test import RequestFactory from django.test import RequestFactory
from contentstore.views.course import _accessible_courses_list, _accessible_courses_list_from_groups from contentstore.views.course import _accessible_courses_list, _accessible_courses_list_from_groups, AccessListFallback
from contentstore.utils import delete_course_and_groups, reverse_course_url from contentstore.utils import delete_course_and_groups, reverse_course_url
from contentstore.tests.utils import AjaxEnabledTestClient from contentstore.tests.utils import AjaxEnabledTestClient
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from student.roles import CourseInstructorRole, CourseStaffRole, GlobalStaff from student.roles import CourseInstructorRole, CourseStaffRole, GlobalStaff, OrgStaffRole, OrgInstructorRole
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.locations import SlashSeparatedCourseKey from xmodule.modulestore.locations import SlashSeparatedCourseKey
...@@ -24,6 +25,7 @@ TOTAL_COURSES_COUNT = 500 ...@@ -24,6 +25,7 @@ TOTAL_COURSES_COUNT = 500
USER_COURSES_COUNT = 50 USER_COURSES_COUNT = 50
@ddt.ddt
class TestCourseListing(ModuleStoreTestCase): class TestCourseListing(ModuleStoreTestCase):
""" """
Unit tests for getting the list of courses for a logged in user Unit tests for getting the list of courses for a logged in user
...@@ -271,3 +273,31 @@ class TestCourseListing(ModuleStoreTestCase): ...@@ -271,3 +273,31 @@ class TestCourseListing(ModuleStoreTestCase):
courses_list = _accessible_courses_list_from_groups(self.request) courses_list = _accessible_courses_list_from_groups(self.request)
self.assertEqual(len(courses_list), 1, courses_list) self.assertEqual(len(courses_list), 1, courses_list)
@ddt.data(OrgStaffRole('AwesomeOrg'), OrgInstructorRole('AwesomeOrg'))
def test_course_listing_org_permissions(self, role):
"""
Create multiple courses within the same org. Verify that someone with org-wide permissions can access
all of them.
"""
org_course_one = SlashSeparatedCourseKey('AwesomeOrg', 'Course1', 'RunBabyRun')
CourseFactory.create(
org=org_course_one.org,
number=org_course_one.course,
run=org_course_one.run
)
org_course_two = SlashSeparatedCourseKey('AwesomeOrg', 'Course2', 'RunRunRun')
CourseFactory.create(
org=org_course_two.org,
number=org_course_two.course,
run=org_course_two.run
)
# Two types of org-wide roles have edit permissions: staff and instructor. We test both
role.add_users(self.user)
with self.assertRaises(AccessListFallback):
_accessible_courses_list_from_groups(self.request)
courses_list = _accessible_courses_list(self.request)
self.assertEqual(len(courses_list), 2)
from student.roles import CourseStaffRole, GlobalStaff, CourseInstructorRole """ Helper methods for determining user access permissions in Studio """
from student.roles import CourseStaffRole, GlobalStaff, CourseInstructorRole, OrgStaffRole, OrgInstructorRole
from student import auth from student import auth
...@@ -14,6 +16,10 @@ def has_course_access(user, course_key, role=CourseStaffRole): ...@@ -14,6 +16,10 @@ def has_course_access(user, course_key, role=CourseStaffRole):
""" """
if GlobalStaff().has_user(user): if GlobalStaff().has_user(user):
return True return True
if OrgInstructorRole(org=course_key.org).has_user(user):
return True
if OrgStaffRole(org=course_key.org).has_user(user):
return True
return auth.has_access(user, role(course_key)) return auth.has_access(user, role(course_key))
......
...@@ -68,6 +68,14 @@ __all__ = ['course_info_handler', 'course_handler', 'course_info_update_handler' ...@@ -68,6 +68,14 @@ __all__ = ['course_info_handler', 'course_handler', 'course_info_update_handler'
'textbooks_list_handler', 'textbooks_detail_handler'] 'textbooks_list_handler', 'textbooks_detail_handler']
class AccessListFallback(Exception):
"""
An exception that is raised whenever we need to `fall back` to fetching *all* courses
available to a user, rather than using a shorter method (i.e. fetching by group)
"""
pass
def _get_course_module(course_key, user, depth=0): def _get_course_module(course_key, user, depth=0):
""" """
Internal method used to calculate and return the locator and course module Internal method used to calculate and return the locator and course module
...@@ -190,11 +198,16 @@ def _accessible_courses_list_from_groups(request): ...@@ -190,11 +198,16 @@ def _accessible_courses_list_from_groups(request):
for course_access in all_courses: for course_access in all_courses:
course_key = course_access.course_id course_key = course_access.course_id
if course_key not in courses_list: if course_key is None:
# If the course_access does not have a course_id, it's an org-based role, so we fall back
raise AccessListFallback
try:
course = modulestore('direct').get_course(course_key) course = modulestore('direct').get_course(course_key)
if course is not None and not isinstance(course, ErrorDescriptor): except ItemNotFoundError:
# ignore deleted or errored courses raise ItemNotFoundError
courses_list[course_key] = course if course is not None and not isinstance(course, ErrorDescriptor):
# ignore deleted or errored courses
courses_list[course_key] = course
return courses_list.values() return courses_list.values()
...@@ -213,7 +226,7 @@ def course_listing(request): ...@@ -213,7 +226,7 @@ def course_listing(request):
else: else:
try: try:
courses = _accessible_courses_list_from_groups(request) courses = _accessible_courses_list_from_groups(request)
except ItemNotFoundError: except AccessListFallback:
# user have some old groups or there was some error getting courses from django groups # user have some old groups or there was some error getting courses from django groups
# so fallback to iterating through all courses # so fallback to iterating through all courses
courses = _accessible_courses_list(request) courses = _accessible_courses_list(request)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment