Merge pull request #5731 from Stanford-Online/ataki/upstream
Limit Upload File Sizes to GridFS
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user