Commit e26b86de by Brian Wilson

Make sure temporary course directory uses safe characters.

parent 2ebed452
...@@ -6,6 +6,7 @@ If <filename> is '-', it pipes the file to stdout ...@@ -6,6 +6,7 @@ If <filename> is '-', it pipes the file to stdout
""" """
import os import os
import re
import shutil import shutil
import tarfile import tarfile
from tempfile import mktemp, mkdtemp from tempfile import mktemp, mkdtemp
...@@ -18,7 +19,7 @@ from django.core.management.base import BaseCommand, CommandError ...@@ -18,7 +19,7 @@ from django.core.management.base import BaseCommand, CommandError
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore.xml_exporter import export_to_xml from xmodule.modulestore.xml_exporter import export_to_xml
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.keys import CourseKey
class Command(BaseCommand): class Command(BaseCommand):
...@@ -30,9 +31,9 @@ class Command(BaseCommand): ...@@ -30,9 +31,9 @@ class Command(BaseCommand):
help = dedent(__doc__).strip() help = dedent(__doc__).strip()
def handle(self, *args, **options): def handle(self, *args, **options):
course_id, filename, pipe_results = self._parse_arguments(args) course_key, filename, pipe_results = self._parse_arguments(args)
export_course_to_tarfile(course_id, filename) export_course_to_tarfile(course_key, filename)
results = self._get_results(filename) if pipe_results else None results = self._get_results(filename) if pipe_results else None
...@@ -41,7 +42,7 @@ class Command(BaseCommand): ...@@ -41,7 +42,7 @@ class Command(BaseCommand):
def _parse_arguments(self, args): def _parse_arguments(self, args):
"""Parse command line arguments""" """Parse command line arguments"""
try: try:
course_id = SlashSeparatedCourseKey.from_deprecated_string(args[0]) course_key = CourseKey.from_string(args[0])
filename = args[1] filename = args[1]
except InvalidKeyError: except InvalidKeyError:
raise CommandError("Unparsable course_id") raise CommandError("Unparsable course_id")
...@@ -54,7 +55,7 @@ class Command(BaseCommand): ...@@ -54,7 +55,7 @@ class Command(BaseCommand):
filename = mktemp() filename = mktemp()
pipe_results = True pipe_results = True
return course_id, filename, pipe_results return course_key, filename, pipe_results
def _get_results(self, filename): def _get_results(self, filename):
"""Load results from file""" """Load results from file"""
...@@ -64,32 +65,37 @@ class Command(BaseCommand): ...@@ -64,32 +65,37 @@ class Command(BaseCommand):
return results return results
def export_course_to_tarfile(course_id, filename): def export_course_to_tarfile(course_key, filename):
"""Exports a course into a tar.gz file""" """Exports a course into a tar.gz file"""
tmp_dir = mkdtemp() tmp_dir = mkdtemp()
try: try:
course_dir = export_course_to_directory(course_id, tmp_dir) course_dir = export_course_to_directory(course_key, tmp_dir)
compress_directory(course_dir, filename) compress_directory(course_dir, filename)
finally: finally:
shutil.rmtree(tmp_dir) shutil.rmtree(tmp_dir)
def export_course_to_directory(course_id, root_dir): def export_course_to_directory(course_key, root_dir):
"""Export course into a directory""" """Export course into a directory"""
store = modulestore() store = modulestore()
course = store.get_course(course_id) course = store.get_course(course_key)
if course is None: if course is None:
raise CommandError("Invalid course_id") raise CommandError("Invalid course_id")
course_name = course.id.to_deprecated_string().replace('/', '-') # The safest characters are A-Z, a-z, 0-9, <underscore>, <period> and <hyphen>.
export_to_xml(store, None, course.id, root_dir, course_name) # We represent the first four with \w, but generalize to all unicode alphanumerics.
replacement_char = u'-'
course_dir = replacement_char.join([course.id.org, course.id.course, course.id.run])
course_dir = re.sub(r'[^\w\.\-]', replacement_char, course_dir, flags=re.UNICODE)
course_dir = path(root_dir) / course_name export_to_xml(store, None, course.id, root_dir, course_dir)
return course_dir
export_dir = path(root_dir) / course_dir
return export_dir
def compress_directory(directory, filename): def compress_directory(directory, filename):
"""Compress a directrory into a tar.gz file""" """Compress a directory into a tar.gz file"""
mode = 'w:gz' mode = 'w:gz'
name = path(directory).name name = path(directory).name
with tarfile.open(filename, mode) as tar_file: with tarfile.open(filename, mode) as tar_file:
......
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