Merge pull request #5731 from Stanford-Online/ataki/upstream

Limit Upload File Sizes to GridFS
This commit is contained in:
Sarina Canelake
2014-12-11 16:36:30 -05:00
14 changed files with 295 additions and 61 deletions

View File

@@ -83,6 +83,9 @@ def _asset_index(request, course_key):
return render_to_response('asset_index.html', {
'context_course': course_module,
'max_file_size_in_mbs': settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB,
'chunk_size_in_mbs': settings.UPLOAD_CHUNK_SIZE_IN_MB,
'max_file_size_redirect_url': settings.MAX_ASSET_UPLOAD_FILE_SIZE_URL,
'asset_callback_url': reverse_course_url('assets_handler', course_key)
})
@@ -152,6 +155,14 @@ def _get_assets_for_page(request, course_key, current_page, page_size, sort):
)
def get_file_size(upload_file):
"""
Helper method for getting file size of an upload file.
Can be used for mocking test file sizes.
"""
return upload_file.size
@require_POST
@ensure_csrf_cookie
@login_required
@@ -176,6 +187,26 @@ def _upload_asset(request, course_key):
upload_file = request.FILES['file']
filename = upload_file.name
mime_type = upload_file.content_type
size = get_file_size(upload_file)
# If file is greater than a specified size, reject the upload
# request and send a message to the user. Note that since
# the front-end may batch large file uploads in smaller chunks,
# we validate the file-size on the front-end in addition to
# validating on the backend. (see cms/static/js/views/assets.js)
max_file_size_in_bytes = settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB * 1000 ** 2
if size > max_file_size_in_bytes:
return JsonResponse({
'error': _(
'File {filename} exceeds maximum size of '
'{size_mb} MB. Please follow the instructions here '
'to upload a file elsewhere and link to it instead: '
'{faq_url}').format(
filename=filename,
size_mb=settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB,
faq_url=settings.MAX_ASSET_UPLOAD_FILE_SIZE_URL,
)
}, status=413)
content_loc = StaticContent.compute_location(course_key, filename)

View File

@@ -5,6 +5,7 @@ from datetime import datetime
from io import BytesIO
from pytz import UTC
import json
from django.conf import settings
from contentstore.tests.utils import CourseTestCase
from contentstore.views import assets
from contentstore.utils import reverse_course_url
@@ -16,10 +17,14 @@ from xmodule.modulestore.django import modulestore
from xmodule.modulestore.xml_importer import import_from_xml
from django.test.utils import override_settings
from opaque_keys.edx.locations import SlashSeparatedCourseKey, AssetLocation
from django.conf import settings
import mock
from ddt import ddt
from ddt import data
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
MAX_FILE_SIZE = settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB * 1000 ** 2
class AssetsTestCase(CourseTestCase):
"""
@@ -33,9 +38,14 @@ class AssetsTestCase(CourseTestCase):
"""
Post to the asset upload url
"""
f = self.get_sample_asset(name)
return self.client.post(self.url, {"name": name, "file": f})
def get_sample_asset(self, name):
"""Returns an in-memory file with the given name for testing"""
f = BytesIO(name)
f.name = name + ".txt"
return self.client.post(self.url, {"name": name, "file": f})
return f
class BasicAssetsTestCase(AssetsTestCase):
@@ -132,6 +142,7 @@ class PaginationTestCase(AssetsTestCase):
self.assertGreaterEqual(name2, name3)
@ddt
class UploadTestCase(AssetsTestCase):
"""
Unit tests for uploading a file
@@ -148,6 +159,24 @@ class UploadTestCase(AssetsTestCase):
resp = self.client.post(self.url, {"name": "file.txt"}, "application/json")
self.assertEquals(resp.status_code, 400)
@data(
(int(MAX_FILE_SIZE / 2.0), "small.file.test", 200),
(MAX_FILE_SIZE, "justequals.file.test", 200),
(MAX_FILE_SIZE + 90, "large.file.test", 413),
)
@mock.patch('contentstore.views.assets.get_file_size')
def test_file_size(self, case, get_file_size):
max_file_size, name, status_code = case
get_file_size.return_value = max_file_size
f = self.get_sample_asset(name=name)
resp = self.client.post(self.url, {
"name": name,
"file": f
})
self.assertEquals(resp.status_code, status_code)
class DownloadTestCase(AssetsTestCase):
"""