Commit ad3140a6 by Don Mitchell

Cleaned up modules to reflect centralizing auth

parent 3ebac807
...@@ -24,6 +24,8 @@ Studio: Newly-created courses default to being published on Jan 1, 2030 ...@@ -24,6 +24,8 @@ Studio: Newly-created courses default to being published on Jan 1, 2030
Studio: Added pagination to the Files & Uploads page. Studio: Added pagination to the Files & Uploads page.
Common: Centralized authorization mechanisms and removed the app-specific ones.
Blades: Video player improvements: Blades: Video player improvements:
- Disable edX controls on iPhone/iPod (native controls are used). - Disable edX controls on iPhone/iPod (native controls are used).
- Disable unsupported controls (volume, playback rate) on iPad/Android. - Disable unsupported controls (volume, playback rate) on iPad/Android.
......
...@@ -10,7 +10,7 @@ from student.roles import CourseInstructorRole, CourseStaffRole ...@@ -10,7 +10,7 @@ from student.roles import CourseInstructorRole, CourseStaffRole
# #
# To run from command line: rake cms:clone SOURCE_LOC=edX/111/Foo1 DEST_LOC=edX/135/Foo3 # To run from command line: ./manage.py cms clone_course --settings=dev master/300/cough edx/111/foo
# #
class Command(BaseCommand): class Command(BaseCommand):
"""Clone a MongoDB-backed course to another location""" """Clone a MongoDB-backed course to another location"""
......
...@@ -3,14 +3,14 @@ Script for granting existing course instructors course creator privileges. ...@@ -3,14 +3,14 @@ Script for granting existing course instructors course creator privileges.
This script is only intended to be run once on a given environment. This script is only intended to be run once on a given environment.
""" """
from auth.authz import get_users_with_instructor_role, get_users_with_staff_role
from course_creators.views import add_user_with_status_granted, add_user_with_status_unrequested from course_creators.views import add_user_with_status_granted, add_user_with_status_unrequested
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from student.roles import CourseInstructorRole, CourseStaffRole
#------------ to run: ./manage.py cms populate_creators --settings=dev
class Command(BaseCommand): class Command(BaseCommand):
""" """
Script for granting existing course instructors course creator privileges. Script for granting existing course instructors course creator privileges.
...@@ -32,13 +32,13 @@ class Command(BaseCommand): ...@@ -32,13 +32,13 @@ class Command(BaseCommand):
# the admin user will already exist. # the admin user will already exist.
admin = User.objects.get(username=username, email=email) admin = User.objects.get(username=username, email=email)
for user in get_users_with_instructor_role(): for user in get_users_with_role(CourseInstructorRole.ROLE):
add_user_with_status_granted(admin, user) add_user_with_status_granted(admin, user)
# Some users will be both staff and instructors. Those folks have been # Some users will be both staff and instructors. Those folks have been
# added with status granted above, and add_user_with_status_unrequested # added with status granted above, and add_user_with_status_unrequested
# will not try to add them again if they already exist in the course creator database. # will not try to add them again if they already exist in the course creator database.
for user in get_users_with_staff_role(): for user in get_users_with_role(CourseStaffRole.ROLE):
add_user_with_status_unrequested(user) add_user_with_status_unrequested(user)
# There could be users who are not in either staff or instructor (they've # There could be users who are not in either staff or instructor (they've
...@@ -46,3 +46,12 @@ class Command(BaseCommand): ...@@ -46,3 +46,12 @@ class Command(BaseCommand):
# when they first go to their dashboard. # when they first go to their dashboard.
admin.delete() admin.delete()
#=============================================================================================================
# Because these are expensive and far-reaching, I moved them here
def get_users_with_role(role_prefix):
"""
An expensive operation which finds all users in the db with the given role prefix
"""
return User.objects.filter(groups__name__startswith=role_prefix)
...@@ -133,17 +133,10 @@ def _course_team_user(request, locator, email): ...@@ -133,17 +133,10 @@ def _course_team_user(request, locator, email):
return JsonResponse(msg, 400) return JsonResponse(msg, 400)
if request.method == "DELETE": if request.method == "DELETE":
# remove all roles in this course from this user: but fail if the user try:
# is the last instructor in the course team try_remove_instructor(request, locator, user)
instructors = CourseInstructorRole(locator) except CannotOrphanCourse as oops:
if instructors.has_user(user): return JsonResponse(oops.msg, 400)
if instructors.users_with_role().count() == 1:
msg = {
"error": _("You may not remove the last instructor from a course")
}
return JsonResponse(msg, 400)
else:
instructors.remove_users(request.user, user)
auth.remove_users(request.user, CourseStaffRole(locator), user) auth.remove_users(request.user, CourseStaffRole(locator), user)
return JsonResponse() return JsonResponse()
...@@ -167,19 +160,33 @@ def _course_team_user(request, locator, email): ...@@ -167,19 +160,33 @@ def _course_team_user(request, locator, email):
# add to staff regardless (can't do after removing from instructors as will no longer # add to staff regardless (can't do after removing from instructors as will no longer
# be allowed) # be allowed)
auth.add_users(request.user, CourseStaffRole(locator), user) auth.add_users(request.user, CourseStaffRole(locator), user)
# if we're trying to downgrade a user from "instructor" to "staff", try:
# make sure we have at least one other instructor in the course team. try_remove_instructor(request, locator, user)
instructors = CourseInstructorRole(locator) except CannotOrphanCourse as oops:
if instructors.has_user(user): return JsonResponse(oops.msg, 400)
if instructors.users_with_role().count() == 1:
msg = {
"error": _("You may not remove the last instructor from a course")
}
return JsonResponse(msg, 400)
else:
instructors.remove_users(request.user, user)
# auto-enroll the course creator in the course so that "View Live" will work. # auto-enroll the course creator in the course so that "View Live" will work.
CourseEnrollment.enroll(user, old_location.course_id) CourseEnrollment.enroll(user, old_location.course_id)
return JsonResponse() return JsonResponse()
class CannotOrphanCourse(Exception):
"""
Exception raised if an attempt is made to remove all responsible instructors from course.
"""
def __init__(self, msg):
self.msg = msg
Exception.__init__(self)
def try_remove_instructor(request, locator, user):
# remove all roles in this course from this user: but fail if the user
# is the last instructor in the course team
instructors = CourseInstructorRole(locator)
if instructors.has_user(user):
if instructors.users_with_role().count() == 1:
msg = {"error":_("You may not remove the last instructor from a course")}
raise CannotOrphanCourse(msg)
else:
auth.remove_users(request.user, instructors, user)
...@@ -409,7 +409,6 @@ INSTALLED_APPS = ( ...@@ -409,7 +409,6 @@ INSTALLED_APPS = (
# For CMS # For CMS
'contentstore', 'contentstore',
'auth',
'course_creators', 'course_creators',
'student', # misleading name due to sharing with lms 'student', # misleading name due to sharing with lms
'course_groups', # not used in cms (yet), but tests run 'course_groups', # not used in cms (yet), but tests run
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
<%! from django.utils.translation import ugettext as _ %> <%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %> <%! from django.core.urlresolvers import reverse %>
<%! from student.roles import CourseInstructorRole %> <%! from student.roles import CourseInstructorRole %>
<%! from student.auth import has_access %>
<%! from xmodule.modulestore.django import loc_mapper %> <%! from xmodule.modulestore.django import loc_mapper %>
<%inherit file="base.html" /> <%inherit file="base.html" />
<%block name="title">${_("Course Team Settings")}</%block> <%block name="title">${_("Course Team Settings")}</%block>
...@@ -66,7 +65,7 @@ ...@@ -66,7 +65,7 @@
<li class="user-item" data-email="${user.email}" data-url="${new_location.url_reverse('course_team/', user.email) }"> <li class="user-item" data-email="${user.email}" data-url="${new_location.url_reverse('course_team/', user.email) }">
<% is_instuctor = has_access(user, CourseInstructorRole(context_course.location)) %> <% is_instuctor = CourseInstructorRole(context_course.location).has_user(user) %>
% if is_instuctor: % if is_instuctor:
<span class="wrapper-ui-badge"> <span class="wrapper-ui-badge">
<span class="flag flag-role flag-role-admin is-hanging"> <span class="flag flag-role flag-role-admin is-hanging">
...@@ -121,7 +120,7 @@ ...@@ -121,7 +120,7 @@
% endfor % endfor
</ol> </ol>
<% user_is_instuctor = has_access(request.user, CourseInstructorRole(context_course.location)) %> <% user_is_instuctor = CourseInstructorRole(context_course.location).has_user(request.user) %>
% if user_is_instuctor and len(staff) == 1: % if user_is_instuctor and len(staff) == 1:
<div class="notice notice-incontext notice-create has-actions"> <div class="notice notice-incontext notice-create has-actions">
<div class="msg"> <div class="msg">
......
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