diff --git a/lms/djangoapps/dashboard/sysadmin.py b/lms/djangoapps/dashboard/sysadmin.py index 5475ab8d41a3..ae9e7025d286 100644 --- a/lms/djangoapps/dashboard/sysadmin.py +++ b/lms/djangoapps/dashboard/sysadmin.py @@ -31,12 +31,14 @@ from django.http import HttpResponse from django.utils.html import escape from django.contrib.admin.views.decorators import staff_member_required +from django.contrib.auth.decorators import login_required from django.http import Http404 from mitxmako.shortcuts import render_to_response from xmodule.modulestore.django import modulestore from xmodule.contentstore.django import contentstore from xmodule.modulestore.store_utilities import delete_course +from courseware.access import has_access import mongoengine import track.views @@ -526,7 +528,7 @@ def sysadmin_dashboard(request): # ----------------------------------------------------------------------------- -@staff_member_required +@login_required def view_git_logs(request, course_id=None): """Shows logs of imports that happened as a result of a git import""" # pylint: disable-msg=W0613 @@ -559,8 +561,18 @@ def view_git_logs(request, course_id=None): 'MONGODB_LOG settings. error: {0}').format(str(ex))) if course_id is None: + + # Require staff if not going to specific course + if not request.user.is_staff: + raise Http404 cilset = CourseImportLog.objects.all().order_by('-created') else: + course = get_course_by_id(course_id) + # Allow only course team, instructors, and staff + if not (request.user.is_staff or + has_access(request.user, course, 'instructor') or + has_access(request.user, course, 'staff')): + raise Http404 log.debug('course_id={0}'.format(course_id)) cilset = CourseImportLog.objects.filter( course_id=course_id).order_by('-created') diff --git a/lms/djangoapps/dashboard/tests/test_sysadmin.py b/lms/djangoapps/dashboard/tests/test_sysadmin.py index 8f8e1fd1dda0..c345b3b3c7f3 100644 --- a/lms/djangoapps/dashboard/tests/test_sysadmin.py +++ b/lms/djangoapps/dashboard/tests/test_sysadmin.py @@ -17,10 +17,12 @@ from dashboard.sysadmin import create_user from external_auth.models import ExternalAuthMap from django.contrib.auth.hashers import check_password +from django.contrib.auth.models import Group from xmodule.modulestore.django import modulestore from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from django.utils.html import escape +from courseware.access import get_access_group_name, has_access from dashboard.sysadmin import CourseImportLog import mongoengine @@ -86,9 +88,6 @@ def test_staff_access(self): response = self.client.get(reverse('sysadmin')) self.assertEqual('/sysadmin', response.context['next']) - response = self.client.get(reverse('gitlogs')) - self.assertEqual('/gitlogs', response.context['next']) - logged_in = self.client.login(username=self.user.username, password='foo') self.assertTrue(logged_in) @@ -97,7 +96,7 @@ def test_staff_access(self): self.assertEqual('/sysadmin', response.context['next']) response = self.client.get(reverse('gitlogs')) - self.assertEqual('/gitlogs', response.context['next']) + self.assertEqual(response.status_code, 404) self.user.is_staff = True self.user.save() @@ -344,8 +343,7 @@ def test_gitlogs(self): self._add_edx4edx() response = self.client.get(reverse('gitlogs')) - print(response.content) - print(CourseImportLog.objects.all()) + # Check that our earlier import has a log with a link to details self.assertIn('/gitlogs/MITx/edx4edx/edx4edx', response.content) @@ -356,6 +354,45 @@ def test_gitlogs(self): self._rm_edx4edx() + def test_gitlog_courseteam_access(self): + """Ensure course team users are allowed to access only their own course""" + + try: + os.mkdir(getattr(settings, 'GIT_REPO_DIR')) + except OSError: + pass + + self._setstaff_login() + self._add_edx4edx() + self.user.is_staff = False + self.user.save() + logged_in = self.client.login(username=self.user.username, + password='foo') + response = self.client.get(reverse('gitlogs')) + + # Make sure our non privileged user doesn't have access to all logs + self.assertEqual(response.status_code, 404) + + # Add user as staff in course team + def_ms = modulestore() + course = def_ms.get_course('MITx/edx4edx/edx4edx') + + staff_groupname = get_access_group_name(course, 'staff') + group, _ = Group.objects.get_or_create(name=staff_groupname) + self.user.groups.add(group) + + self.assertTrue(has_access(self.user, course, 'staff')) + logged_in = self.client.login(username=self.user.username, + password='foo') + self.assertTrue(logged_in) + + response = self.client.get( + reverse('gitlogs_detail', kwargs={'course_id': 'MITx/edx4edx/edx4edx'})) + print(response.content) + self.assertIn('======> IMPORTING course to location', response.content) + + self._rm_edx4edx() + @override_settings(GIT_ADD_COURSE_SCRIPT='') def test_no_script_set(self): """ Test if settings are right on mongo store import""" diff --git a/scripts/git_add_course.py b/scripts/git_add_course.py index 469e2233f11a..245913436421 100755 --- a/scripts/git_add_course.py +++ b/scripts/git_add_course.py @@ -119,9 +119,18 @@ def add_repo(repo, rdir_in): # Get XML logging logger and capture debug to parse results output = StringIO.StringIO() import_logger = logging.getLogger('xmodule.modulestore.xml_importer') + git_logger = logging.getLogger('git_add_script') + xml_logger = logging.getLogger('xmodule.modulestore.xml') + xml_seq_logger = logging.getLogger('xmodule.seq_module') + import_log_handler = logging.StreamHandler(output) import_log_handler.setLevel(logging.DEBUG) - import_logger.addHandler(import_log_handler) + + for logger in [import_logger, git_logger, xml_logger, xml_seq_logger, ]: + logger.old_level = logger.level + logger.setLevel(logging.DEBUG) + logger.addHandler(import_log_handler) + try: management.call_command('import', GIT_REPO_DIR, rdir, nostatic=not GIT_IMPORT_STATIC) @@ -132,6 +141,11 @@ def add_repo(repo, rdir_in): ret_import = output.getvalue() + # Remove handler hijacks + for logger in [import_logger, git_logger, xml_logger, xml_seq_logger, ]: + logger.setLevel(logger.old_level) + logger.removeHandler(import_log_handler) + course_id = 'unknown' location = 'unknown'