Make course ids and usage ids opaque to LMS and Studio [partial commit]

This commit updates common/djangoapps.

These keys are now objects with a limited interface, and the particular
internal representation is managed by the data storage layer (the
modulestore).

For the LMS, there should be no outward-facing changes to the system.
The keys are, for now, a change to internal representation only. For
Studio, the new serialized form of the keys is used in urls, to allow
for further migration in the future.

Co-Author: Andy Armstrong <andya@edx.org>
Co-Author: Christina Roberts <christina@edx.org>
Co-Author: David Baumgold <db@edx.org>
Co-Author: Diana Huang <dkh@edx.org>
Co-Author: Don Mitchell <dmitchell@edx.org>
Co-Author: Julia Hansbrough <julia@edx.org>
Co-Author: Nimisha Asthagiri <nasthagiri@edx.org>
Co-Author: Sarina Canelake <sarina@edx.org>

[LMS-2370]
This commit is contained in:
Calen Pennington
2014-04-30 10:17:43 -04:00
parent 7852906ce0
commit e2bfcf2a36
42 changed files with 603 additions and 330 deletions

View File

@@ -5,12 +5,12 @@ import mock
from django.test import TestCase
from django.contrib.auth.models import User, AnonymousUser
from xmodule.modulestore import Location
from django.core.exceptions import PermissionDenied
from student.roles import CourseInstructorRole, CourseStaffRole, CourseCreatorRole
from student.tests.factories import AdminFactory
from student.auth import has_access, add_users, remove_users
from xmodule.modulestore.locations import SlashSeparatedCourseKey
class CreatorGroupTest(TestCase):
@@ -137,54 +137,54 @@ class CourseGroupTest(TestCase):
self.global_admin = AdminFactory()
self.creator = User.objects.create_user('testcreator', 'testcreator+courses@edx.org', 'foo')
self.staff = User.objects.create_user('teststaff', 'teststaff+courses@edx.org', 'foo')
self.location = Location('i4x', 'mitX', '101', 'course', 'test')
self.course_key = SlashSeparatedCourseKey('mitX', '101', 'test')
def test_add_user_to_course_group(self):
"""
Tests adding user to course group (happy path).
"""
# Create groups for a new course (and assign instructor role to the creator).
self.assertFalse(has_access(self.creator, CourseInstructorRole(self.location)))
add_users(self.global_admin, CourseInstructorRole(self.location), self.creator)
add_users(self.global_admin, CourseStaffRole(self.location), self.creator)
self.assertTrue(has_access(self.creator, CourseInstructorRole(self.location)))
self.assertFalse(has_access(self.creator, CourseInstructorRole(self.course_key)))
add_users(self.global_admin, CourseInstructorRole(self.course_key), self.creator)
add_users(self.global_admin, CourseStaffRole(self.course_key), self.creator)
self.assertTrue(has_access(self.creator, CourseInstructorRole(self.course_key)))
# Add another user to the staff role.
self.assertFalse(has_access(self.staff, CourseStaffRole(self.location)))
add_users(self.creator, CourseStaffRole(self.location), self.staff)
self.assertTrue(has_access(self.staff, CourseStaffRole(self.location)))
self.assertFalse(has_access(self.staff, CourseStaffRole(self.course_key)))
add_users(self.creator, CourseStaffRole(self.course_key), self.staff)
self.assertTrue(has_access(self.staff, CourseStaffRole(self.course_key)))
def test_add_user_to_course_group_permission_denied(self):
"""
Verifies PermissionDenied if caller of add_user_to_course_group is not instructor role.
"""
add_users(self.global_admin, CourseInstructorRole(self.location), self.creator)
add_users(self.global_admin, CourseStaffRole(self.location), self.creator)
add_users(self.global_admin, CourseInstructorRole(self.course_key), self.creator)
add_users(self.global_admin, CourseStaffRole(self.course_key), self.creator)
with self.assertRaises(PermissionDenied):
add_users(self.staff, CourseStaffRole(self.location), self.staff)
add_users(self.staff, CourseStaffRole(self.course_key), self.staff)
def test_remove_user_from_course_group(self):
"""
Tests removing user from course group (happy path).
"""
add_users(self.global_admin, CourseInstructorRole(self.location), self.creator)
add_users(self.global_admin, CourseStaffRole(self.location), self.creator)
add_users(self.global_admin, CourseInstructorRole(self.course_key), self.creator)
add_users(self.global_admin, CourseStaffRole(self.course_key), self.creator)
add_users(self.creator, CourseStaffRole(self.location), self.staff)
self.assertTrue(has_access(self.staff, CourseStaffRole(self.location)))
add_users(self.creator, CourseStaffRole(self.course_key), self.staff)
self.assertTrue(has_access(self.staff, CourseStaffRole(self.course_key)))
remove_users(self.creator, CourseStaffRole(self.location), self.staff)
self.assertFalse(has_access(self.staff, CourseStaffRole(self.location)))
remove_users(self.creator, CourseStaffRole(self.course_key), self.staff)
self.assertFalse(has_access(self.staff, CourseStaffRole(self.course_key)))
remove_users(self.creator, CourseInstructorRole(self.location), self.creator)
self.assertFalse(has_access(self.creator, CourseInstructorRole(self.location)))
remove_users(self.creator, CourseInstructorRole(self.course_key), self.creator)
self.assertFalse(has_access(self.creator, CourseInstructorRole(self.course_key)))
def test_remove_user_from_course_group_permission_denied(self):
"""
Verifies PermissionDenied if caller of remove_user_from_course_group is not instructor role.
"""
add_users(self.global_admin, CourseInstructorRole(self.location), self.creator)
add_users(self.global_admin, CourseInstructorRole(self.course_key), self.creator)
another_staff = User.objects.create_user('another', 'teststaff+anothercourses@edx.org', 'foo')
add_users(self.global_admin, CourseStaffRole(self.location), self.creator, self.staff, another_staff)
add_users(self.global_admin, CourseStaffRole(self.course_key), self.creator, self.staff, another_staff)
with self.assertRaises(PermissionDenied):
remove_users(self.staff, CourseStaffRole(self.location), another_staff)
remove_users(self.staff, CourseStaffRole(self.course_key), another_staff)

View File

@@ -6,6 +6,7 @@ from django_comment_common.models import (
from django_comment_common.utils import seed_permissions_roles
from student.models import CourseEnrollment, UserProfile
from util.testing import UrlResetMixin
from xmodule.modulestore.locations import SlashSeparatedCourseKey
from mock import patch
@@ -23,6 +24,8 @@ class AutoAuthEnabledTestCase(UrlResetMixin, TestCase):
super(AutoAuthEnabledTestCase, self).setUp()
self.url = '/auto_auth'
self.client = Client()
self.course_id = 'edX/Test101/2014_Spring'
self.course_key = SlashSeparatedCourseKey.from_deprecated_string(self.course_id)
def test_create_user(self):
"""
@@ -83,43 +86,39 @@ class AutoAuthEnabledTestCase(UrlResetMixin, TestCase):
def test_course_enrollment(self):
# Create a user and enroll in a course
course_id = "edX/Test101/2014_Spring"
self._auto_auth(username='test', course_id=course_id)
self._auto_auth(username='test', course_id=self.course_id)
# Check that a course enrollment was created for the user
self.assertEqual(CourseEnrollment.objects.count(), 1)
enrollment = CourseEnrollment.objects.get(course_id=course_id)
enrollment = CourseEnrollment.objects.get(course_id=self.course_key)
self.assertEqual(enrollment.user.username, "test")
def test_double_enrollment(self):
# Create a user and enroll in a course
course_id = "edX/Test101/2014_Spring"
self._auto_auth(username='test', course_id=course_id)
self._auto_auth(username='test', course_id=self.course_id)
# Make the same call again, re-enrolling the student in the same course
self._auto_auth(username='test', course_id=course_id)
self._auto_auth(username='test', course_id=self.course_id)
# Check that only one course enrollment was created for the user
self.assertEqual(CourseEnrollment.objects.count(), 1)
enrollment = CourseEnrollment.objects.get(course_id=course_id)
enrollment = CourseEnrollment.objects.get(course_id=self.course_key)
self.assertEqual(enrollment.user.username, "test")
def test_set_roles(self):
course_id = "edX/Test101/2014_Spring"
seed_permissions_roles(course_id)
course_roles = dict((r.name, r) for r in Role.objects.filter(course_id=course_id))
seed_permissions_roles(self.course_key)
course_roles = dict((r.name, r) for r in Role.objects.filter(course_id=self.course_key))
self.assertEqual(len(course_roles), 4) # sanity check
# Student role is assigned by default on course enrollment.
self._auto_auth(username='a_student', course_id=course_id)
self._auto_auth(username='a_student', course_id=self.course_id)
user = User.objects.get(username='a_student')
user_roles = user.roles.all()
self.assertEqual(len(user_roles), 1)
self.assertEqual(user_roles[0], course_roles[FORUM_ROLE_STUDENT])
self._auto_auth(username='a_moderator', course_id=course_id, roles='Moderator')
self._auto_auth(username='a_moderator', course_id=self.course_id, roles='Moderator')
user = User.objects.get(username='a_moderator')
user_roles = user.roles.all()
self.assertEqual(
@@ -128,7 +127,7 @@ class AutoAuthEnabledTestCase(UrlResetMixin, TestCase):
course_roles[FORUM_ROLE_MODERATOR]]))
# check multiple roles work.
self._auto_auth(username='an_admin', course_id=course_id,
self._auto_auth(username='an_admin', course_id=self.course_id,
roles='{},{}'.format(FORUM_ROLE_MODERATOR, FORUM_ROLE_ADMINISTRATOR))
user = User.objects.get(username='an_admin')
user_roles = user.roles.all()

View File

@@ -15,6 +15,7 @@ from student.tests.factories import UserFactory, CourseEnrollmentFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from xmodule.modulestore.locations import SlashSeparatedCourseKey
from bulk_email.models import CourseAuthorization
@@ -100,7 +101,10 @@ class TestStudentDashboardEmailViewXMLBacked(ModuleStoreTestCase):
# Create student account
student = UserFactory.create()
CourseEnrollmentFactory.create(user=student, course_id=self.course_name)
CourseEnrollmentFactory.create(
user=student,
course_id=SlashSeparatedCourseKey.from_deprecated_string(self.course_name)
)
self.client.login(username=student.username, password="test")
try:

View File

@@ -20,6 +20,7 @@ from xmodule.modulestore.inheritance import own_metadata
from xmodule.modulestore.django import editable_modulestore
from external_auth.models import ExternalAuthMap
from xmodule.modulestore.locations import SlashSeparatedCourseKey
TEST_DATA_MIXED_MODULESTORE = mixed_store_config(settings.COMMON_TEST_DATA_ROOT, {})
@@ -275,7 +276,10 @@ class UtilFnTest(TestCase):
COURSE_ID = u'org/num/run' # pylint: disable=C0103
COURSE_URL = u'/courses/{}/otherstuff'.format(COURSE_ID) # pylint: disable=C0103
NON_COURSE_URL = u'/blahblah' # pylint: disable=C0103
self.assertEqual(_parse_course_id_from_string(COURSE_URL), COURSE_ID)
self.assertEqual(
_parse_course_id_from_string(COURSE_URL),
SlashSeparatedCourseKey.from_deprecated_string(COURSE_ID)
)
self.assertIsNone(_parse_course_id_from_string(NON_COURSE_URL))
@@ -320,7 +324,7 @@ class ExternalAuthShibTest(ModuleStoreTestCase):
"""
Tests the _get_course_enrollment_domain utility function
"""
self.assertIsNone(_get_course_enrollment_domain("I/DONT/EXIST"))
self.assertIsNone(_get_course_enrollment_domain(SlashSeparatedCourseKey("I", "DONT", "EXIST")))
self.assertIsNone(_get_course_enrollment_domain(self.course.id))
self.assertEqual(self.shib_course.enrollment_domain, _get_course_enrollment_domain(self.shib_course.id))
@@ -340,7 +344,7 @@ class ExternalAuthShibTest(ModuleStoreTestCase):
Tests the redirects when visiting course-specific URL with @login_required.
Should vary by course depending on its enrollment_domain
"""
TARGET_URL = reverse('courseware', args=[self.course.id]) # pylint: disable=C0103
TARGET_URL = reverse('courseware', args=[self.course.id.to_deprecated_string()]) # pylint: disable=C0103
noshib_response = self.client.get(TARGET_URL, follow=True)
self.assertEqual(noshib_response.redirect_chain[-1],
('http://testserver/accounts/login?next={url}'.format(url=TARGET_URL), 302))
@@ -348,7 +352,7 @@ class ExternalAuthShibTest(ModuleStoreTestCase):
.format(platform_name=settings.PLATFORM_NAME)))
self.assertEqual(noshib_response.status_code, 200)
TARGET_URL_SHIB = reverse('courseware', args=[self.shib_course.id]) # pylint: disable=C0103
TARGET_URL_SHIB = reverse('courseware', args=[self.shib_course.id.to_deprecated_string()]) # pylint: disable=C0103
shib_response = self.client.get(**{'path': TARGET_URL_SHIB,
'follow': True,
'REMOTE_USER': self.extauth.external_id,

View File

@@ -53,7 +53,7 @@ class UserStandingTest(TestCase):
try:
self.some_url = reverse('dashboard')
except NoReverseMatch:
self.some_url = '/course'
self.some_url = '/course/'
# since it's only possible to disable accounts from lms, we're going
# to skip tests for cms