Commit 061a46be by Calen Pennington

Formalize various access roles as objects

This centralizes the logic for group membership, and means that we
don't need to make queries to find out whether the legacy groups names exist.
parent 68c182c0
...@@ -4,9 +4,17 @@ from student.models import (User, UserProfile, Registration, ...@@ -4,9 +4,17 @@ from student.models import (User, UserProfile, Registration,
PendingEmailChange, UserStanding, PendingEmailChange, UserStanding,
) )
from course_modes.models import CourseMode from course_modes.models import CourseMode
from django.contrib.auth.models import Group from django.contrib.auth.models import Group, AnonymousUser
from datetime import datetime from datetime import datetime
from factory import DjangoModelFactory, SubFactory, PostGenerationMethodCall, post_generation, Sequence from factory import (
DjangoModelFactory,
Factory,
LazyAttribute,
post_generation,
PostGenerationMethodCall,
Sequence,
SubFactory,
)
from uuid import uuid4 from uuid import uuid4
from pytz import UTC from pytz import UTC
...@@ -16,8 +24,10 @@ from pytz import UTC ...@@ -16,8 +24,10 @@ from pytz import UTC
class GroupFactory(DjangoModelFactory): class GroupFactory(DjangoModelFactory):
FACTORY_FOR = Group FACTORY_FOR = Group
FACTORY_DJANGO_GET_OR_CREATE = ('name', )
name = Sequence(u'group{0}'.format)
name = u'staff_MITx/999/Robot_Super_Course'
class UserStandingFactory(DjangoModelFactory): class UserStandingFactory(DjangoModelFactory):
...@@ -30,9 +40,10 @@ class UserStandingFactory(DjangoModelFactory): ...@@ -30,9 +40,10 @@ class UserStandingFactory(DjangoModelFactory):
class UserProfileFactory(DjangoModelFactory): class UserProfileFactory(DjangoModelFactory):
FACTORY_FOR = UserProfile FACTORY_FOR = UserProfile
FACTORY_DJANGO_GET_OR_CREATE = ('user', )
user = None user = None
name = u'Robot Test' name = LazyAttribute(u'{0.user.first_name} {0.user.last_name}'.format)
level_of_education = None level_of_education = None
gender = u'm' gender = u'm'
mailing_address = None mailing_address = None
...@@ -59,6 +70,7 @@ class RegistrationFactory(DjangoModelFactory): ...@@ -59,6 +70,7 @@ class RegistrationFactory(DjangoModelFactory):
class UserFactory(DjangoModelFactory): class UserFactory(DjangoModelFactory):
FACTORY_FOR = User FACTORY_FOR = User
FACTORY_DJANGO_GET_OR_CREATE = ('email', 'username')
username = Sequence(u'robot{0}'.format) username = Sequence(u'robot{0}'.format)
email = Sequence(u'robot+test+{0}@edx.org'.format) email = Sequence(u'robot+test+{0}@edx.org'.format)
...@@ -82,6 +94,21 @@ class UserFactory(DjangoModelFactory): ...@@ -82,6 +94,21 @@ class UserFactory(DjangoModelFactory):
else: else:
return None return None
@post_generation
def groups(self, create, extracted, **kwargs):
if extracted is None:
return
if isinstance(extracted, basestring):
extracted = [extracted]
for group_name in extracted:
self.groups.add(GroupFactory.simple_generate(create, name=group_name))
class AnonymousUserFactory(Factory):
FACTORY_FOR = AnonymousUser
class AdminFactory(UserFactory): class AdminFactory(UserFactory):
is_staff = True is_staff = True
......
...@@ -28,7 +28,7 @@ from celery.states import SUCCESS, FAILURE, RETRY ...@@ -28,7 +28,7 @@ from celery.states import SUCCESS, FAILURE, RETRY
from celery.exceptions import RetryTaskError from celery.exceptions import RetryTaskError
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User
from django.core.mail import EmailMultiAlternatives, get_connection from django.core.mail import EmailMultiAlternatives, get_connection
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
...@@ -36,8 +36,8 @@ from bulk_email.models import ( ...@@ -36,8 +36,8 @@ from bulk_email.models import (
CourseEmail, Optout, CourseEmailTemplate, CourseEmail, Optout, CourseEmailTemplate,
SEND_TO_MYSELF, SEND_TO_ALL, TO_OPTIONS, SEND_TO_MYSELF, SEND_TO_ALL, TO_OPTIONS,
) )
from courseware.access import _course_staff_group_name, _course_instructor_group_name
from courseware.courses import get_course, course_image_url from courseware.courses import get_course, course_image_url
from courseware.roles import CourseStaffRole, CourseInstructorRole
from instructor_task.models import InstructorTask from instructor_task.models import InstructorTask
from instructor_task.subtasks import ( from instructor_task.subtasks import (
SubtaskStatus, SubtaskStatus,
...@@ -106,12 +106,8 @@ def _get_recipient_queryset(user_id, to_option, course_id, course_location): ...@@ -106,12 +106,8 @@ def _get_recipient_queryset(user_id, to_option, course_id, course_location):
if to_option == SEND_TO_MYSELF: if to_option == SEND_TO_MYSELF:
recipient_qset = User.objects.filter(id=user_id) recipient_qset = User.objects.filter(id=user_id)
else: else:
staff_grpname = _course_staff_group_name(course_location) staff_qset = CourseStaffRole(course_location).users_with_role()
staff_group, _ = Group.objects.get_or_create(name=staff_grpname) instructor_qset = CourseInstructorRole(course_location).users_with_role()
staff_qset = staff_group.user_set.all()
instructor_grpname = _course_instructor_group_name(course_location)
instructor_group, _ = Group.objects.get_or_create(name=instructor_grpname)
instructor_qset = instructor_group.user_set.all()
recipient_qset = staff_qset | instructor_qset recipient_qset = staff_qset | instructor_qset
if to_option == SEND_TO_ALL: if to_option == SEND_TO_ALL:
# We also require students to have activated their accounts to # We also require students to have activated their accounts to
......
...@@ -50,10 +50,10 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase): ...@@ -50,10 +50,10 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase):
def setUp(self): def setUp(self):
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.instructor = InstructorFactory(self.course) self.instructor = InstructorFactory(course=self.course.location)
# Create staff # Create staff
self.staff = [StaffFactory(self.course) for _ in xrange(STAFF_COUNT)] self.staff = [StaffFactory(course=self.course.location) for _ in xrange(STAFF_COUNT)]
# Create students # Create students
self.students = [UserFactory() for _ in xrange(STUDENT_COUNT)] self.students = [UserFactory() for _ in xrange(STUDENT_COUNT)]
......
...@@ -25,7 +25,7 @@ class TestWikiAccessBase(ModuleStoreTestCase): ...@@ -25,7 +25,7 @@ class TestWikiAccessBase(ModuleStoreTestCase):
self.wiki = get_or_create_root() self.wiki = get_or_create_root()
self.course_math101 = CourseFactory.create(org='org', number='math101', display_name='Course') self.course_math101 = CourseFactory.create(org='org', number='math101', display_name='Course')
self.course_math101_staff = [InstructorFactory(self.course_math101), StaffFactory(self.course_math101)] self.course_math101_staff = [InstructorFactory(course=self.course_math101.location), StaffFactory(course=self.course_math101.location)]
wiki_math101 = self.create_urlpath(self.wiki, course_wiki_slug(self.course_math101)) wiki_math101 = self.create_urlpath(self.wiki, course_wiki_slug(self.course_math101))
wiki_math101_page = self.create_urlpath(wiki_math101, 'Child') wiki_math101_page = self.create_urlpath(wiki_math101, 'Child')
...@@ -44,9 +44,9 @@ class TestWikiAccess(TestWikiAccessBase): ...@@ -44,9 +44,9 @@ class TestWikiAccess(TestWikiAccessBase):
super(TestWikiAccess, self).setUp() super(TestWikiAccess, self).setUp()
self.course_310b = CourseFactory.create(org='org', number='310b', display_name='Course') self.course_310b = CourseFactory.create(org='org', number='310b', display_name='Course')
self.course_310b_staff = [InstructorFactory(self.course_310b), StaffFactory(self.course_310b)] self.course_310b_staff = [InstructorFactory(course=self.course_310b.location), StaffFactory(course=self.course_310b.location)]
self.course_310b_ = CourseFactory.create(org='org', number='310b_', display_name='Course') self.course_310b_ = CourseFactory.create(org='org', number='310b_', display_name='Course')
self.course_310b__staff = [InstructorFactory(self.course_310b_), StaffFactory(self.course_310b_)] self.course_310b__staff = [InstructorFactory(course=self.course_310b_.location), StaffFactory(course=self.course_310b_.location)]
self.wiki_310b = self.create_urlpath(self.wiki, course_wiki_slug(self.course_310b)) self.wiki_310b = self.create_urlpath(self.wiki, course_wiki_slug(self.course_310b))
self.wiki_310b_ = self.create_urlpath(self.wiki, course_wiki_slug(self.course_310b_)) self.wiki_310b_ = self.create_urlpath(self.wiki, course_wiki_slug(self.course_310b_))
...@@ -105,7 +105,7 @@ class TestWikiAccessForNumericalCourseNumber(TestWikiAccessBase): ...@@ -105,7 +105,7 @@ class TestWikiAccessForNumericalCourseNumber(TestWikiAccessBase):
super(TestWikiAccessForNumericalCourseNumber, self).setUp() super(TestWikiAccessForNumericalCourseNumber, self).setUp()
self.course_200 = CourseFactory.create(org='org', number='200', display_name='Course') self.course_200 = CourseFactory.create(org='org', number='200', display_name='Course')
self.course_200_staff = [InstructorFactory(self.course_200), StaffFactory(self.course_200)] self.course_200_staff = [InstructorFactory(course=self.course_200.location), StaffFactory(course=self.course_200.location)]
wiki_200 = self.create_urlpath(self.wiki, course_wiki_slug(self.course_200)) wiki_200 = self.create_urlpath(self.wiki, course_wiki_slug(self.course_200))
wiki_200_page = self.create_urlpath(wiki_200, 'Child') wiki_200_page = self.create_urlpath(wiki_200, 'Child')
...@@ -126,7 +126,7 @@ class TestWikiAccessForOldFormatCourseStaffGroups(TestWikiAccessBase): ...@@ -126,7 +126,7 @@ class TestWikiAccessForOldFormatCourseStaffGroups(TestWikiAccessBase):
self.course_math101c = CourseFactory.create(org='org', number='math101c', display_name='Course') self.course_math101c = CourseFactory.create(org='org', number='math101c', display_name='Course')
Group.objects.get_or_create(name='instructor_math101c') Group.objects.get_or_create(name='instructor_math101c')
self.course_math101c_staff = [InstructorFactory(self.course_math101c), StaffFactory(self.course_math101c)] self.course_math101c_staff = [InstructorFactory(course=self.course_math101c.location), StaffFactory(course=self.course_math101c.location)]
wiki_math101c = self.create_urlpath(self.wiki, course_wiki_slug(self.course_math101c)) wiki_math101c = self.create_urlpath(self.wiki, course_wiki_slug(self.course_math101c))
wiki_math101c_page = self.create_urlpath(wiki_math101c, 'Child') wiki_math101c_page = self.create_urlpath(wiki_math101c, 'Child')
......
"""
Classes used to model the roles used in the courseware. Each role is responsible for checking membership,
adding users, removing users, and listing members
"""
from abc import ABCMeta, abstractmethod
from functools import partial
from django.contrib.auth.models import User, Group
from xmodule.modulestore import Location
class CourseContextRequired(Exception):
"""
Raised when a course_context is required to determine permissions
"""
pass
class AccessRole(object):
"""
Object representing a role with particular access to a resource
"""
__metaclass__ = ABCMeta
@abstractmethod
def has_user(self, user): # pylint: disable=unused-argument
"""
Return whether the supplied django user has access to this role.
"""
return False
@abstractmethod
def add_users(self, *users):
"""
Add the role to the supplied django users.
"""
pass
@abstractmethod
def remove_users(self, *users):
"""
Remove the role from the supplied django users.
"""
pass
@abstractmethod
def users_with_role(self):
"""
Return a django QuerySet for all of the users with this role
"""
return User.objects.none()
class GlobalStaff(AccessRole):
"""
The global staff role
"""
def has_user(self, user):
return user.is_staff
def add_users(self, *users):
for user in users:
user.is_staff = True
user.save()
def remove_users(self, *users):
for user in users:
user.is_staff = False
user.save()
def users_with_role(self):
raise Exception("This operation is un-indexed, and shouldn't be used")
class GroupBasedRole(AccessRole):
"""
A role based on membership to any of a set of groups.
"""
def __init__(self, group_names):
"""
Create a GroupBasedRole from a list of group names
The first element of `group_names` will be the preferred group
to use when adding a user to this Role.
If a user is a member of any of the groups in the list, then
they will be consider a member of the Role
"""
self._group_names = [name.lower() for name in group_names]
def has_user(self, user):
"""
Return whether the supplied django user has access to this role.
"""
# pylint: disable=protected-access
if not user.is_authenticated():
return False
if not hasattr(user, '_groups'):
user._groups = set(name.lower() for name in user.groups.values_list('name', flat=True))
return len(user._groups.intersection(self._group_names)) > 0
def add_users(self, *users):
"""
Add the supplied django users to this role.
"""
group, _ = Group.objects.get_or_create(name=self._group_names[0])
group.user_set.add(*users)
for user in users:
if hasattr(user, '_groups'):
del user._groups
def remove_users(self, *users):
"""
Remove the supplied django users from this role.
"""
group, _ = Group.objects.get_or_create(name=self._group_names[0])
group.user_set.remove(*users)
for user in users:
if hasattr(user, '_groups'):
del user._groups
def users_with_role(self):
"""
Return a django QuerySet for all of the users with this role
"""
return User.objects.filter(groups__name__in=self._group_names)
class CourseRole(GroupBasedRole):
"""
A named role in a particular course
"""
def __init__(self, role, location, course_context=None):
# pylint: disable=no-member
loc = Location(location)
legacy_group_name = '{0}_{1}'.format(role, loc.course)
if loc.category.lower() == 'course':
course_id = loc.course_id
else:
if course_context is None:
raise CourseContextRequired()
course_id = course_context
group_name = '{0}_{1}'.format(role, course_id)
super(CourseRole, self).__init__([group_name, legacy_group_name])
class OrgRole(GroupBasedRole):
"""
A named role in a particular org
"""
def __init__(self, role, location):
# pylint: disable=no-member
location = Location(location)
super(OrgRole, self).__init__(['{}_{}'.format(role, location.org)])
class CourseStaffRole(CourseRole):
"""A Staff member of a course"""
def __init__(self, *args, **kwargs):
super(CourseStaffRole, self).__init__('staff', *args, **kwargs)
class CourseInstructorRole(CourseRole):
"""A course Instructor"""
def __init__(self, *args, **kwargs):
super(CourseInstructorRole, self).__init__('instructor', *args, **kwargs)
class CourseBetaTesterRole(CourseRole):
"""A course Beta Tester"""
def __init__(self, *args, **kwargs):
super(CourseBetaTesterRole, self).__init__('beta_testers', *args, **kwargs)
class OrgStaffRole(OrgRole):
"""An organization staff member"""
def __init__(self, *args, **kwargs):
super(OrgStaffRole, self).__init__('staff', *args, **kwargs)
class OrgInstructorRole(OrgRole):
"""An organization staff member"""
def __init__(self, *args, **kwargs):
super(OrgInstructorRole, self).__init__('staff', *args, **kwargs)
from datetime import datetime
import json import json
from functools import partial from functools import partial
from factory import DjangoModelFactory, SubFactory from factory import DjangoModelFactory, SubFactory, post_generation
from student.tests.factories import UserFactory as StudentUserFactory
from student.tests.factories import GroupFactory as StudentGroupFactory # Imported to re-export
# pylint: disable=unused-import
from student.tests.factories import UserFactory # Imported to re-export
from student.tests.factories import GroupFactory # Imported to re-export
from student.tests.factories import CourseEnrollmentAllowedFactory # Imported to re-export
from student.tests.factories import RegistrationFactory # Imported to re-export
# pylint: enable=unused-import
from student.tests.factories import UserProfileFactory as StudentUserProfileFactory from student.tests.factories import UserProfileFactory as StudentUserProfileFactory
from student.tests.factories import CourseEnrollmentAllowedFactory as StudentCourseEnrollmentAllowedFactory
from student.tests.factories import RegistrationFactory as StudentRegistrationFactory
from courseware.models import StudentModule, XModuleUserStateSummaryField from courseware.models import StudentModule, XModuleUserStateSummaryField
from courseware.models import XModuleStudentInfoField, XModuleStudentPrefsField from courseware.models import XModuleStudentInfoField, XModuleStudentPrefsField
from instructor.access import allow_access from courseware.roles import CourseInstructorRole, CourseStaffRole
from xmodule.modulestore import Location from xmodule.modulestore import Location
from pytz import UTC
location = partial(Location, 'i4x', 'edX', 'test_course', 'problem') location = partial(Location, 'i4x', 'edX', 'test_course', 'problem')
class UserProfileFactory(StudentUserProfileFactory): class UserProfileFactory(StudentUserProfileFactory):
name = 'Robot Studio'
courseware = 'course.xml' courseware = 'course.xml'
class RegistrationFactory(StudentRegistrationFactory): class InstructorFactory(UserFactory):
pass
class UserFactory(StudentUserFactory):
email = 'robot@edx.org'
last_name = 'Tester'
last_login = datetime.now(UTC)
date_joined = datetime.now(UTC)
def InstructorFactory(course): # pylint: disable=invalid-name
""" """
Given a course object, returns a User object with instructor Given a course Location, returns a User object with instructor
permissions for `course`. permissions for `course`.
""" """
user = StudentUserFactory.create(last_name="Instructor") last_name = "Instructor"
allow_access(course, user, "instructor")
return user @post_generation
def course(self, create, extracted, **kwargs):
if extracted is None:
raise ValueError("Must specify a course location for a course instructor user")
CourseInstructorRole(extracted).add_users(self)
def StaffFactory(course): # pylint: disable=invalid-name class StaffFactory(UserFactory):
""" """
Given a course object, returns a User object with staff Given a course Location, returns a User object with staff
permissions for `course`. permissions for `course`.
""" """
user = StudentUserFactory.create(last_name="Staff") last_name = "Staff"
allow_access(course, user, "staff")
return user
class GroupFactory(StudentGroupFactory):
name = 'test_group'
class CourseEnrollmentAllowedFactory(StudentCourseEnrollmentAllowedFactory): @post_generation
pass def course(self, create, extracted, **kwargs):
if extracted is None:
raise ValueError("Must specify a course location for a course staff user")
CourseStaffRole(extracted).add_users(self)
class StudentModuleFactory(DjangoModelFactory): class StudentModuleFactory(DjangoModelFactory):
......
import courseware.access as access
import datetime
from mock import Mock from mock import Mock
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from courseware.tests.factories import UserFactory, CourseEnrollmentAllowedFactory, StaffFactory, InstructorFactory
from student.tests.factories import AnonymousUserFactory
from xmodule.modulestore import Location from xmodule.modulestore import Location
import courseware.access as access
from courseware.tests.tests import TEST_DATA_MIXED_MODULESTORE from courseware.tests.tests import TEST_DATA_MIXED_MODULESTORE
from .factories import CourseEnrollmentAllowedFactory
import datetime
import pytz import pytz
# pylint: disable=protected-access
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
class AccessTestCase(TestCase): class AccessTestCase(TestCase):
""" """
Tests for the various access controls on the student dashboard Tests for the various access controls on the student dashboard
""" """
def test__has_global_staff_access(self):
u = Mock(is_staff=False)
self.assertFalse(access._has_global_staff_access(u))
u = Mock(is_staff=True) def setUp(self):
self.assertTrue(access._has_global_staff_access(u)) self.course = Location('i4x://edX/toy/course/2012_Fall')
self.anonymous_user = AnonymousUserFactory()
self.student = UserFactory()
self.global_staff = UserFactory(is_staff=True)
self.course_staff = StaffFactory(course=self.course)
self.course_instructor = InstructorFactory(course=self.course)
def test__has_access_to_location(self): def test__has_access_to_location(self):
location = Location('i4x://edX/toy/course/2012_Fall') self.assertFalse(access._has_access_to_location(None, self.course, 'staff', None))
self.assertFalse(access._has_access_to_location(self.anonymous_user, self.course, 'staff', None))
self.assertFalse(access._has_access_to_location(self.anonymous_user, self.course, 'instructor', None))
self.assertTrue(access._has_access_to_location(self.global_staff, self.course, 'staff', None))
self.assertTrue(access._has_access_to_location(self.global_staff, self.course, 'instructor', None))
self.assertFalse(access._has_access_to_location(None, location,
'staff', None))
u = Mock()
u.is_authenticated.return_value = False
self.assertFalse(access._has_access_to_location(u, location,
'staff', None))
u = Mock(is_staff=True)
self.assertTrue(access._has_access_to_location(u, location,
'instructor', None))
# A user has staff access if they are in the staff group # A user has staff access if they are in the staff group
u = Mock(is_staff=False) self.assertTrue(access._has_access_to_location(self.course_staff, self.course, 'staff', None))
g = Mock() self.assertFalse(access._has_access_to_location(self.course_staff, self.course, 'instructor', None))
g.name = 'staff_edX/toy/2012_Fall'
u.groups.all.return_value = [g] # A user has staff and instructor access if they are in the instructor group
self.assertTrue(access._has_access_to_location(u, location, self.assertTrue(access._has_access_to_location(self.course_instructor, self.course, 'staff', None))
'staff', None)) self.assertTrue(access._has_access_to_location(self.course_instructor, self.course, 'instructor', None))
# A user has staff access if they are in the instructor group
g.name = 'instructor_edX/toy/2012_Fall' # A user does not have staff or instructor access if they are
self.assertTrue(access._has_access_to_location(u, location,
'staff', None))
# A user has instructor access if they are in the instructor group
g.name = 'instructor_edX/toy/2012_Fall'
self.assertTrue(access._has_access_to_location(u, location,
'instructor', None))
# A user does not have staff access if they are
# not in either the staff or the the instructor group # not in either the staff or the the instructor group
g.name = 'student_only' self.assertFalse(access._has_access_to_location(self.student, self.course, 'staff', None))
self.assertFalse(access._has_access_to_location(u, location, self.assertFalse(access._has_access_to_location(self.student, self.course, 'instructor', None))
'staff', None))
# A user does not have instructor access if they are
# not in the instructor group
g.name = 'student_only'
self.assertFalse(access._has_access_to_location(u, location,
'instructor', None))
def test__has_access_string(self): def test__has_access_string(self):
u = Mock(is_staff=True) u = Mock(is_staff=True)
......
...@@ -12,11 +12,11 @@ import json ...@@ -12,11 +12,11 @@ import json
from django.test.utils import override_settings from django.test.utils import override_settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.contrib.auth.models import Group, User from django.contrib.auth.models import User
from courseware.access import _course_staff_group_name
from courseware.tests.helpers import LoginEnrollmentTestCase from courseware.tests.helpers import LoginEnrollmentTestCase
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from courseware.roles import CourseStaffRole
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.django import modulestore, clear_existing_modulestores from xmodule.modulestore.django import modulestore, clear_existing_modulestores
from lms.lib.xblock.runtime import quote_slashes from lms.lib.xblock.runtime import quote_slashes
...@@ -42,9 +42,7 @@ class TestStaffMasqueradeAsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase) ...@@ -42,9 +42,7 @@ class TestStaffMasqueradeAsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase)
self.activate_user(self.instructor) self.activate_user(self.instructor)
def make_instructor(course): def make_instructor(course):
group_name = _course_staff_group_name(course.location) CourseStaffRole(course.location).add_users(User.objects.get(email=self.instructor))
g = Group.objects.create(name=group_name)
g.user_set.add(User.objects.get(email=self.instructor))
make_instructor(self.graded_course) make_instructor(self.graded_course)
......
"""
Tests of courseware.roles
"""
from django.test import TestCase
from xmodule.modulestore import Location
from courseware.tests.factories import UserFactory, StaffFactory, InstructorFactory
from student.tests.factories import AnonymousUserFactory
from courseware.roles import GlobalStaff, CourseRole
class RolesTestCase(TestCase):
"""
Tests of courseware.roles
"""
def setUp(self):
self.course = Location('i4x://edX/toy/course/2012_Fall')
self.anonymous_user = AnonymousUserFactory()
self.student = UserFactory()
self.global_staff = UserFactory(is_staff=True)
self.course_staff = StaffFactory(course=self.course)
self.course_instructor = InstructorFactory(course=self.course)
def test_global_staff(self):
self.assertFalse(GlobalStaff().has_user(self.student))
self.assertFalse(GlobalStaff().has_user(self.course_staff))
self.assertFalse(GlobalStaff().has_user(self.course_instructor))
self.assertTrue(GlobalStaff().has_user(self.global_staff))
def test_group_name_case_insensitive(self):
uppercase_loc = "i4x://ORG/COURSE/course/NAME"
lowercase_loc = uppercase_loc.lower()
lowercase_group = "role_org/course/name"
uppercase_group = lowercase_group.upper()
lowercase_user = UserFactory(groups=lowercase_group)
uppercase_user = UserFactory(groups=uppercase_group)
self.assertTrue(CourseRole("role", lowercase_loc).has_user(lowercase_user))
self.assertTrue(CourseRole("role", uppercase_loc).has_user(lowercase_user))
self.assertTrue(CourseRole("role", lowercase_loc).has_user(uppercase_user))
self.assertTrue(CourseRole("role", uppercase_loc).has_user(uppercase_user))
...@@ -3,19 +3,19 @@ import pytz ...@@ -3,19 +3,19 @@ import pytz
from mock import patch from mock import patch
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test.utils import override_settings from django.test.utils import override_settings
# Need access to internal func to put users in the right group # Need access to internal func to put users in the right group
from courseware.access import (has_access, _course_staff_group_name, from courseware.access import has_access
course_beta_test_group_name, settings as access_settings) from courseware.roles import CourseBetaTesterRole, CourseInstructorRole, CourseStaffRole, GlobalStaff
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from helpers import LoginEnrollmentTestCase, check_for_get_code from courseware.tests.helpers import LoginEnrollmentTestCase, check_for_get_code
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
...@@ -173,9 +173,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -173,9 +173,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
email, password = self.ACCOUNT_INFO[1] email, password = self.ACCOUNT_INFO[1]
# Make the instructor staff in self.course # Make the instructor staff in self.course
group_name = _course_staff_group_name(self.course.location) CourseInstructorRole(self.course.location).add_users(User.objects.get(email=email))
group = Group.objects.create(name=group_name)
group.user_set.add(User.objects.get(email=email))
self.login(email, password) self.login(email, password)
...@@ -206,7 +204,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -206,7 +204,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
for url in urls: for url in urls:
check_for_get_code(self, 200, url) check_for_get_code(self, 200, url)
@patch.dict(access_settings.MITX_FEATURES, {'DISABLE_START_DATES': False}) @patch.dict('courseware.access.settings.MITX_FEATURES', {'DISABLE_START_DATES': False})
def test_dark_launch_enrolled_student(self): def test_dark_launch_enrolled_student(self):
""" """
Make sure that before course start, students can't access course Make sure that before course start, students can't access course
...@@ -237,7 +235,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -237,7 +235,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
self._check_non_staff_light(self.test_course) self._check_non_staff_light(self.test_course)
self._check_non_staff_dark(self.test_course) self._check_non_staff_dark(self.test_course)
@patch.dict(access_settings.MITX_FEATURES, {'DISABLE_START_DATES': False}) @patch.dict('courseware.access.settings.MITX_FEATURES', {'DISABLE_START_DATES': False})
def test_dark_launch_instructor(self): def test_dark_launch_instructor(self):
""" """
Make sure that before course start instructors can access the Make sure that before course start instructors can access the
...@@ -253,9 +251,8 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -253,9 +251,8 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.test_course = self.update_course(self.test_course, test_course_data) self.test_course = self.update_course(self.test_course, test_course_data)
# Make the instructor staff in self.course # Make the instructor staff in self.course
group_name = _course_staff_group_name(self.course.location) CourseStaffRole(self.course.location).add_users(User.objects.get(email=instructor_email))
group = Group.objects.create(name=group_name)
group.user_set.add(User.objects.get(email=instructor_email))
self.logout() self.logout()
self.login(instructor_email, instructor_password) self.login(instructor_email, instructor_password)
# Enroll in the classes---can't see courseware otherwise. # Enroll in the classes---can't see courseware otherwise.
...@@ -267,7 +264,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -267,7 +264,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
self._check_non_staff_dark(self.test_course) self._check_non_staff_dark(self.test_course)
self._check_staff(self.course) self._check_staff(self.course)
@patch.dict(access_settings.MITX_FEATURES, {'DISABLE_START_DATES': False}) @patch.dict('courseware.access.settings.MITX_FEATURES', {'DISABLE_START_DATES': False})
def test_dark_launch_staff(self): def test_dark_launch_staff(self):
""" """
Make sure that before course start staff can access Make sure that before course start staff can access
...@@ -295,7 +292,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -295,7 +292,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
self._check_staff(self.course) self._check_staff(self.course)
self._check_staff(self.test_course) self._check_staff(self.test_course)
@patch.dict(access_settings.MITX_FEATURES, {'DISABLE_START_DATES': False}) @patch.dict('courseware.access.settings.MITX_FEATURES', {'DISABLE_START_DATES': False})
def test_enrollment_period(self): def test_enrollment_period(self):
""" """
Check that enrollment periods work. Check that enrollment periods work.
...@@ -323,25 +320,22 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -323,25 +320,22 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertTrue(self.enroll(self.test_course)) self.assertTrue(self.enroll(self.test_course))
# Make the instructor staff in the self.course # Make the instructor staff in the self.course
group_name = _course_staff_group_name(self.course.location) instructor_role = CourseInstructorRole(self.course.location)
group = Group.objects.create(name=group_name) instructor_role.add_users(User.objects.get(email=instructor_email))
group.user_set.add(User.objects.get(email=instructor_email))
self.logout() self.logout()
self.login(instructor_email, instructor_password) self.login(instructor_email, instructor_password)
self.assertTrue(self.enroll(self.course)) self.assertTrue(self.enroll(self.course))
# now make the instructor global staff, but not in the instructor group # now make the instructor global staff, but not in the instructor group
group.user_set.remove(User.objects.get(email=instructor_email)) instructor_role.remove_users(User.objects.get(email=instructor_email))
instructor = User.objects.get(email=instructor_email) GlobalStaff().add_users(User.objects.get(email=instructor_email))
instructor.is_staff = True
instructor.save()
# unenroll and try again # unenroll and try again
self.unenroll(self.course) self.unenroll(self.course)
self.assertTrue(self.enroll(self.course)) self.assertTrue(self.enroll(self.course))
@patch.dict(access_settings.MITX_FEATURES, {'DISABLE_START_DATES': False}) @patch.dict('courseware.access.settings.MITX_FEATURES', {'DISABLE_START_DATES': False})
def test_beta_period(self): def test_beta_period(self):
""" """
Check that beta-test access works. Check that beta-test access works.
...@@ -366,9 +360,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -366,9 +360,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertFalse(has_access(student_user, self.course, 'load')) self.assertFalse(has_access(student_user, self.course, 'load'))
# now add the student to the beta test group # now add the student to the beta test group
group_name = course_beta_test_group_name(self.course.location) CourseBetaTesterRole(self.course.location).add_users(student_user)
group = Group.objects.create(name=group_name)
group.user_set.add(student_user)
# now the student should see it # now the student should see it
self.assertTrue(has_access(student_user, self.course, 'load')) self.assertTrue(has_access(student_user, self.course, 'load'))
...@@ -10,13 +10,18 @@ TO DO sync instructor and staff flags ...@@ -10,13 +10,18 @@ TO DO sync instructor and staff flags
""" """
import logging import logging
from django.contrib.auth.models import Group
from courseware.access import (get_access_group_name,
course_beta_test_group_name)
from django_comment_common.models import Role from django_comment_common.models import Role
from courseware.roles import CourseBetaTesterRole, CourseInstructorRole, CourseStaffRole
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
ROLES = {
'beta': CourseBetaTesterRole,
'instructor': CourseInstructorRole,
'staff': CourseStaffRole,
}
def list_with_level(course, level): def list_with_level(course, level):
""" """
...@@ -26,16 +31,7 @@ def list_with_level(course, level): ...@@ -26,16 +31,7 @@ def list_with_level(course, level):
There could be other levels specific to the course. There could be other levels specific to the course.
If there is no Group for that course-level, returns an empty list If there is no Group for that course-level, returns an empty list
""" """
if level == 'beta': return ROLES[level](course.location).users_with_role()
grpname = course_beta_test_group_name(course.location)
else:
grpname = get_access_group_name(course, level)
try:
return Group.objects.get(name=grpname).user_set.all()
except Group.DoesNotExist:
log.info("list_with_level called with non-existant group named {}".format(grpname))
return []
def allow_access(course, user, level): def allow_access(course, user, level):
...@@ -66,18 +62,15 @@ def _change_access(course, user, level, action): ...@@ -66,18 +62,15 @@ def _change_access(course, user, level, action):
NOTE: will create a group if it does not yet exist. NOTE: will create a group if it does not yet exist.
""" """
if level == 'beta': try:
grpname = course_beta_test_group_name(course.location) role = ROLES[level](course.location)
elif level in ['instructor', 'staff']: except KeyError:
grpname = get_access_group_name(course, level)
else:
raise ValueError("unrecognized level '{}'".format(level)) raise ValueError("unrecognized level '{}'".format(level))
group, _ = Group.objects.get_or_create(name=grpname)
if action == 'allow': if action == 'allow':
user.groups.add(group) role.add_users(user)
elif action == 'revoke': elif action == 'revoke':
user.groups.remove(group) role.remove_users(user)
else: else:
raise ValueError("unrecognized action '{}'".format(action)) raise ValueError("unrecognized action '{}'".format(action))
......
...@@ -3,15 +3,14 @@ Test instructor.access ...@@ -3,15 +3,14 @@ Test instructor.access
""" """
from nose.tools import raises from nose.tools import raises
from django.contrib.auth.models import Group
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from courseware.roles import CourseBetaTesterRole, CourseStaffRole
from courseware.access import get_access_group_name
from django_comment_common.models import (Role, from django_comment_common.models import (Role,
FORUM_ROLE_MODERATOR) FORUM_ROLE_MODERATOR)
from instructor.access import (allow_access, from instructor.access import (allow_access,
...@@ -51,39 +50,29 @@ class TestInstructorAccessAllow(ModuleStoreTestCase): ...@@ -51,39 +50,29 @@ class TestInstructorAccessAllow(ModuleStoreTestCase):
def test_allow(self): def test_allow(self):
user = UserFactory() user = UserFactory()
allow_access(self.course, user, 'staff') allow_access(self.course, user, 'staff')
group = Group.objects.get( self.assertTrue(CourseStaffRole(self.course.location).has_user(user))
name=get_access_group_name(self.course, 'staff')
)
self.assertIn(user, group.user_set.all())
def test_allow_twice(self): def test_allow_twice(self):
user = UserFactory() user = UserFactory()
allow_access(self.course, user, 'staff') allow_access(self.course, user, 'staff')
allow_access(self.course, user, 'staff') allow_access(self.course, user, 'staff')
group = Group.objects.get( self.assertTrue(CourseStaffRole(self.course.location).has_user(user))
name=get_access_group_name(self.course, 'staff')
)
self.assertIn(user, group.user_set.all())
def test_allow_beta(self): def test_allow_beta(self):
""" Test allow beta against list beta. """ """ Test allow beta against list beta. """
user = UserFactory() user = UserFactory()
allow_access(self.course, user, 'beta') allow_access(self.course, user, 'beta')
self.assertIn(user, list_with_level(self.course, 'beta')) self.assertTrue(CourseBetaTesterRole(self.course.location).has_user(user))
@raises(ValueError) @raises(ValueError)
def test_allow_badlevel(self): def test_allow_badlevel(self):
user = UserFactory() user = UserFactory()
allow_access(self.course, user, 'robot-not-a-level') allow_access(self.course, user, 'robot-not-a-level')
group = Group.objects.get(name=get_access_group_name(self.course, 'robot-not-a-level'))
self.assertIn(user, group.user_set.all())
@raises(Exception) @raises(Exception)
def test_allow_noneuser(self): def test_allow_noneuser(self):
user = None user = None
allow_access(self.course, user, 'staff') allow_access(self.course, user, 'staff')
group = Group.objects.get(name=get_access_group_name(self.course, 'staff'))
self.assertIn(user, group.user_set.all())
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
...@@ -102,32 +91,22 @@ class TestInstructorAccessRevoke(ModuleStoreTestCase): ...@@ -102,32 +91,22 @@ class TestInstructorAccessRevoke(ModuleStoreTestCase):
def test_revoke(self): def test_revoke(self):
user = self.staff[0] user = self.staff[0]
revoke_access(self.course, user, 'staff') revoke_access(self.course, user, 'staff')
group = Group.objects.get( self.assertFalse(CourseStaffRole(self.course.location).has_user(user))
name=get_access_group_name(self.course, 'staff')
)
self.assertNotIn(user, group.user_set.all())
def test_revoke_twice(self): def test_revoke_twice(self):
user = self.staff[0] user = self.staff[0]
revoke_access(self.course, user, 'staff') revoke_access(self.course, user, 'staff')
group = Group.objects.get( self.assertFalse(CourseStaffRole(self.course.location).has_user(user))
name=get_access_group_name(self.course, 'staff')
)
self.assertNotIn(user, group.user_set.all())
def test_revoke_beta(self): def test_revoke_beta(self):
user = self.beta_testers[0] user = self.beta_testers[0]
revoke_access(self.course, user, 'beta') revoke_access(self.course, user, 'beta')
self.assertNotIn(user, list_with_level(self.course, 'beta')) self.assertFalse(CourseBetaTesterRole(self.course.location).has_user(user))
@raises(ValueError) @raises(ValueError)
def test_revoke_badrolename(self): def test_revoke_badrolename(self):
user = UserFactory() user = UserFactory()
revoke_access(self.course, user, 'robot-not-a-level') revoke_access(self.course, user, 'robot-not-a-level')
group = Group.objects.get(
name=get_access_group_name(self.course, 'robot-not-a-level')
)
self.assertNotIn(user, group.user_set.all())
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
......
...@@ -183,7 +183,7 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -183,7 +183,7 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase):
""" """
Ensure that a staff member can't access instructor endpoints. Ensure that a staff member can't access instructor endpoints.
""" """
staff_member = StaffFactory(self.course) staff_member = StaffFactory(course=self.course.location)
CourseEnrollment.enroll(staff_member, self.course.id) CourseEnrollment.enroll(staff_member, self.course.id)
self.client.login(username=staff_member.username, password='test') self.client.login(username=staff_member.username, password='test')
# Try to promote to forums admin - not working # Try to promote to forums admin - not working
...@@ -212,7 +212,7 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -212,7 +212,7 @@ class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase):
""" """
Ensure that an instructor member can access all endpoints. Ensure that an instructor member can access all endpoints.
""" """
inst = InstructorFactory(self.course) inst = InstructorFactory(course=self.course.location)
CourseEnrollment.enroll(inst, self.course.id) CourseEnrollment.enroll(inst, self.course.id)
self.client.login(username=inst.username, password='test') self.client.login(username=inst.username, password='test')
...@@ -249,7 +249,7 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -249,7 +249,7 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase):
""" """
def setUp(self): def setUp(self):
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.instructor = InstructorFactory(self.course) self.instructor = InstructorFactory(course=self.course.location)
self.client.login(username=self.instructor.username, password='test') self.client.login(username=self.instructor.username, password='test')
self.enrolled_student = UserFactory() self.enrolled_student = UserFactory()
...@@ -376,7 +376,7 @@ class TestInstructorAPILevelsAccess(ModuleStoreTestCase, LoginEnrollmentTestCase ...@@ -376,7 +376,7 @@ class TestInstructorAPILevelsAccess(ModuleStoreTestCase, LoginEnrollmentTestCase
""" """
def setUp(self): def setUp(self):
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.instructor = InstructorFactory(self.course) self.instructor = InstructorFactory(course=self.course.location)
self.client.login(username=self.instructor.username, password='test') self.client.login(username=self.instructor.username, password='test')
self.other_instructor = UserFactory() self.other_instructor = UserFactory()
...@@ -513,7 +513,7 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa ...@@ -513,7 +513,7 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
""" """
def setUp(self): def setUp(self):
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.instructor = InstructorFactory(self.course) self.instructor = InstructorFactory(course=self.course.location)
self.client.login(username=self.instructor.username, password='test') self.client.login(username=self.instructor.username, password='test')
self.students = [UserFactory() for _ in xrange(6)] self.students = [UserFactory() for _ in xrange(6)]
...@@ -650,7 +650,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase) ...@@ -650,7 +650,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
""" """
def setUp(self): def setUp(self):
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.instructor = InstructorFactory(self.course) self.instructor = InstructorFactory(course=self.course.location)
self.client.login(username=self.instructor.username, password='test') self.client.login(username=self.instructor.username, password='test')
self.student = UserFactory() self.student = UserFactory()
...@@ -794,7 +794,7 @@ class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -794,7 +794,7 @@ class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
""" """
def setUp(self): def setUp(self):
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.instructor = InstructorFactory(self.course) self.instructor = InstructorFactory(course=self.course.location)
self.client.login(username=self.instructor.username, password='test') self.client.login(username=self.instructor.username, password='test')
test_subject = u'\u1234 test subject' test_subject = u'\u1234 test subject'
test_message = u'\u6824 test message' test_message = u'\u6824 test message'
...@@ -916,7 +916,7 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -916,7 +916,7 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
def setUp(self): def setUp(self):
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.instructor = InstructorFactory(self.course) self.instructor = InstructorFactory(course=self.course.location)
self.client.login(username=self.instructor.username, password='test') self.client.login(username=self.instructor.username, password='test')
self.student = UserFactory() self.student = UserFactory()
...@@ -1047,7 +1047,7 @@ class TestInstructorAPIAnalyticsProxy(ModuleStoreTestCase, LoginEnrollmentTestCa ...@@ -1047,7 +1047,7 @@ class TestInstructorAPIAnalyticsProxy(ModuleStoreTestCase, LoginEnrollmentTestCa
def setUp(self): def setUp(self):
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.instructor = InstructorFactory(self.course) self.instructor = InstructorFactory(course=self.course.location)
self.client.login(username=self.instructor.username, password='test') self.client.login(username=self.instructor.username, password='test')
@patch.object(instructor.views.api.requests, 'get') @patch.object(instructor.views.api.requests, 'get')
......
...@@ -11,16 +11,15 @@ Notes for running by hand: ...@@ -11,16 +11,15 @@ Notes for running by hand:
from django.test.utils import override_settings from django.test.utils import override_settings
# Need access to internal func to put users in the right group # Need access to internal func to put users in the right group
from django.contrib.auth.models import Group, User from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from courseware.access import _course_staff_group_name
from courseware.tests.helpers import LoginEnrollmentTestCase from courseware.tests.helpers import LoginEnrollmentTestCase
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from courseware.roles import CourseStaffRole
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.django import modulestore, clear_existing_modulestores from xmodule.modulestore.django import modulestore, clear_existing_modulestores
import xmodule.modulestore.django
from mock import patch from mock import patch
...@@ -46,10 +45,8 @@ class TestInstructorDashboardAnonCSV(ModuleStoreTestCase, LoginEnrollmentTestCas ...@@ -46,10 +45,8 @@ class TestInstructorDashboardAnonCSV(ModuleStoreTestCase, LoginEnrollmentTestCas
self.activate_user(self.instructor) self.activate_user(self.instructor)
def make_instructor(course): def make_instructor(course):
""" Create an instructor for the course. """ """ Create an instructor for the course."""
group_name = _course_staff_group_name(course.location) CourseStaffRole(course.location).add_users(User.objects.get(email=self.instructor))
group = Group.objects.create(name=group_name)
group.user_set.add(User.objects.get(email=self.instructor))
make_instructor(self.toy) make_instructor(self.toy)
...@@ -68,4 +65,3 @@ class TestInstructorDashboardAnonCSV(ModuleStoreTestCase, LoginEnrollmentTestCas ...@@ -68,4 +65,3 @@ class TestInstructorDashboardAnonCSV(ModuleStoreTestCase, LoginEnrollmentTestCas
self.assertEqual(response['Content-Type'], 'text/csv') self.assertEqual(response['Content-Type'], 'text/csv')
body = response.content.replace('\r', '') body = response.content.replace('\r', '')
self.assertEqual(body, '"User ID","Anonymized user ID"\n"2","42"\n') self.assertEqual(body, '"User ID","Anonymized user ID"\n"2","42"\n')
...@@ -11,16 +11,15 @@ Notes for running by hand: ...@@ -11,16 +11,15 @@ Notes for running by hand:
from django.test.utils import override_settings from django.test.utils import override_settings
# Need access to internal func to put users in the right group # Need access to internal func to put users in the right group
from django.contrib.auth.models import Group, User from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from courseware.access import _course_staff_group_name
from courseware.tests.helpers import LoginEnrollmentTestCase from courseware.tests.helpers import LoginEnrollmentTestCase
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from courseware.roles import CourseStaffRole
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.django import modulestore, clear_existing_modulestores from xmodule.modulestore.django import modulestore, clear_existing_modulestores
import xmodule.modulestore.django
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
...@@ -44,9 +43,7 @@ class TestInstructorDashboardGradeDownloadCSV(ModuleStoreTestCase, LoginEnrollme ...@@ -44,9 +43,7 @@ class TestInstructorDashboardGradeDownloadCSV(ModuleStoreTestCase, LoginEnrollme
def make_instructor(course): def make_instructor(course):
""" Create an instructor for the course. """ """ Create an instructor for the course. """
group_name = _course_staff_group_name(course.location) CourseStaffRole(course.location).add_users(User.objects.get(email=self.instructor))
group = Group.objects.create(name=group_name)
group.user_set.add(User.objects.get(email=self.instructor))
make_instructor(self.toy) make_instructor(self.toy)
......
...@@ -195,7 +195,7 @@ class TestInstructorEnrollsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase) ...@@ -195,7 +195,7 @@ class TestInstructorEnrollsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase)
course = self.course course = self.course
# Create activated, but not enrolled, user # Create activated, but not enrolled, user
UserFactory.create(username="student3_0", email="student3_0@test.com") UserFactory.create(username="student3_0", email="student3_0@test.com", first_name='Autoenrolled')
url = reverse('instructor_dashboard', kwargs={'course_id': course.id}) url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
response = self.client.post(url, {'action': 'Enroll multiple students', 'multiple_students': 'student3_0@test.com, student3_1@test.com, student3_2@test.com', 'auto_enroll': 'on', 'email_students': 'on'}) response = self.client.post(url, {'action': 'Enroll multiple students', 'multiple_students': 'student3_0@test.com, student3_1@test.com, student3_2@test.com', 'auto_enroll': 'on', 'email_students': 'on'})
...@@ -215,12 +215,12 @@ class TestInstructorEnrollsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase) ...@@ -215,12 +215,12 @@ class TestInstructorEnrollsStudent(ModuleStoreTestCase, LoginEnrollmentTestCase)
) )
self.assertEqual( self.assertEqual(
mail.outbox[0].body, mail.outbox[0].body,
"Dear Robot Test\n\nYou have been enrolled in Robot Super Course " "Dear Autoenrolled Test\n\nYou have been enrolled in Robot Super Course "
"at edx.org by a member of the course staff. " "at edx.org by a member of the course staff. "
"The course should now appear on your edx.org dashboard.\n\n" "The course should now appear on your edx.org dashboard.\n\n"
"To start accessing course materials, please visit " "To start accessing course materials, please visit "
"https://edx.org/courses/MITx/999/Robot_Super_Course\n\n" "https://edx.org/courses/MITx/999/Robot_Super_Course\n\n"
"----\nThis email was automatically sent from edx.org to Robot Test" "----\nThis email was automatically sent from edx.org to Autoenrolled Test"
) )
self.assertEqual( self.assertEqual(
......
...@@ -6,16 +6,16 @@ Unit tests for instructor dashboard forum administration ...@@ -6,16 +6,16 @@ Unit tests for instructor dashboard forum administration
from django.test.utils import override_settings from django.test.utils import override_settings
# Need access to internal func to put users in the right group # Need access to internal func to put users in the right group
from django.contrib.auth.models import Group, User from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django_comment_common.models import Role, FORUM_ROLE_ADMINISTRATOR, \ from django_comment_common.models import Role, FORUM_ROLE_ADMINISTRATOR, \
FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA, FORUM_ROLE_STUDENT FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA, FORUM_ROLE_STUDENT
from django_comment_client.utils import has_forum_access from django_comment_client.utils import has_forum_access
from courseware.access import _course_staff_group_name
from courseware.tests.helpers import LoginEnrollmentTestCase from courseware.tests.helpers import LoginEnrollmentTestCase
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from courseware.roles import CourseStaffRole
from xmodule.modulestore.django import modulestore, clear_existing_modulestores from xmodule.modulestore.django import modulestore, clear_existing_modulestores
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
...@@ -54,9 +54,7 @@ class TestInstructorDashboardForumAdmin(ModuleStoreTestCase, LoginEnrollmentTest ...@@ -54,9 +54,7 @@ class TestInstructorDashboardForumAdmin(ModuleStoreTestCase, LoginEnrollmentTest
self.activate_user(self.student) self.activate_user(self.student)
self.activate_user(self.instructor) self.activate_user(self.instructor)
group_name = _course_staff_group_name(self.toy.location) CourseStaffRole(self.toy.location).add_users(User.objects.get(email=self.instructor))
g = Group.objects.create(name=group_name)
g.user_set.add(User.objects.get(email=self.instructor))
self.logout() self.logout()
self.login(self.instructor, self.password) self.login(self.instructor, self.password)
......
...@@ -8,7 +8,7 @@ import json ...@@ -8,7 +8,7 @@ import json
import logging import logging
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Group, User from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test.utils import override_settings from django.test.utils import override_settings
from mock import MagicMock, patch, Mock from mock import MagicMock, patch, Mock
...@@ -22,11 +22,11 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase ...@@ -22,11 +22,11 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.open_ended_grading_classes import peer_grading_service, controller_query_service from xmodule.open_ended_grading_classes import peer_grading_service, controller_query_service
from xmodule.tests import test_util_open_ended from xmodule.tests import test_util_open_ended
from courseware.access import _course_staff_group_name
from courseware.tests import factories from courseware.tests import factories
from courseware.tests.helpers import LoginEnrollmentTestCase, check_for_get_code, check_for_post_code from courseware.tests.helpers import LoginEnrollmentTestCase, check_for_get_code, check_for_post_code
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from lms.lib.xblock.runtime import LmsModuleSystem from lms.lib.xblock.runtime import LmsModuleSystem
from courseware.roles import CourseStaffRole
from mitxmako.shortcuts import render_to_string from mitxmako.shortcuts import render_to_string
from student.models import unique_id_for_user from student.models import unique_id_for_user
...@@ -52,9 +52,7 @@ def make_instructor(course, user_email): ...@@ -52,9 +52,7 @@ def make_instructor(course, user_email):
""" """
Makes a given user an instructor in a course. Makes a given user an instructor in a course.
""" """
group_name = _course_staff_group_name(course.location) CourseStaffRole(course.location).add_users(User.objects.get(email=user_email))
group = Group.objects.create(name=group_name)
group.user_set.add(User.objects.get(email=user_email))
class StudentProblemListMockQuery(object): class StudentProblemListMockQuery(object):
......
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