diff --git a/static/js/avatar.js b/static/js/avatar.js index 624cb9e2cf..cf4fbc047d 100644 --- a/static/js/avatar.js +++ b/static/js/avatar.js @@ -72,7 +72,8 @@ exports.build_user_avatar_widget = function (upload_function) { get_file_input, $("#user_avatar_file_input_error").expectOne(), $("#user_avatar_upload_button").expectOne(), - upload_function + upload_function, + page_params.max_avatar_file_size ); }; diff --git a/static/js/realm_icon.js b/static/js/realm_icon.js index f38008dfd2..4537562e24 100644 --- a/static/js/realm_icon.js +++ b/static/js/realm_icon.js @@ -24,7 +24,8 @@ var realm_icon = (function () { get_file_input, $("#realm_icon_file_input_error").expectOne(), $("#realm_icon_upload_button").expectOne(), - upload_function + upload_function, + page_params.max_icon_file_size ); }; diff --git a/static/js/upload_widget.js b/static/js/upload_widget.js index bdd99e77cd..9639c124a7 100644 --- a/static/js/upload_widget.js +++ b/static/js/upload_widget.js @@ -2,6 +2,8 @@ var upload_widget = (function () { var exports = {}; + var default_max_file_size = 5; + function is_image_format(file) { var type = file.type; if (!type) { @@ -22,8 +24,11 @@ var upload_widget = (function () { file_name_field, // jQuery object to show file name input_error, // jQuery object for error text clear_button, // jQuery button to clear last upload choice - upload_button // jQuery button to open file dialog + upload_button, // jQuery button to open file dialog + max_file_upload_size ) { + // default value of max upladed file size + max_file_upload_size = max_file_upload_size || default_max_file_size; function accept(file) { file_name_field.text(file.name); @@ -61,8 +66,10 @@ var upload_widget = (function () { input_error.hide(); } else if (e.target.files.length === 1) { var file = e.target.files[0]; - if (file.size > 5 * 1024 * 1024) { - input_error.text(i18n.t('File size must be < 5Mb.')); + if (file.size > max_file_upload_size * 1024 * 1024) { + input_error.text(i18n.t('File size must be < __max_file_size__Mb.', { + max_file_size: max_file_upload_size, + })); input_error.show(); clear(); } else if (!is_image_format(file)) { @@ -103,7 +110,11 @@ var upload_widget = (function () { get_file_input, // function returns a jQuery file input object input_error, // jQuery object for error text upload_button, // jQuery button to open file dialog - upload_function) { + upload_function, + max_file_upload_size + ) { + // default value of max upladed file size + max_file_upload_size = max_file_upload_size || default_max_file_size; function accept() { input_error.hide(); @@ -131,8 +142,10 @@ var upload_widget = (function () { input_error.hide(); } else if (e.target.files.length === 1) { var file = e.target.files[0]; - if (file.size > 5 * 1024 * 1024) { - input_error.text(i18n.t('File size must be < 5Mb.')); + if (file.size > max_file_upload_size * 1024 * 1024) { + input_error.text(i18n.t('File size must be < __max_file_size__Mb.', { + max_file_size: max_file_upload_size, + })); input_error.show(); clear(); } else if (!is_image_format(file)) { diff --git a/zerver/lib/events.py b/zerver/lib/events.py index ee585b96b5..974123a824 100644 --- a/zerver/lib/events.py +++ b/zerver/lib/events.py @@ -106,6 +106,7 @@ def fetch_initial_state_data(user_profile, event_types, queue_id, state['realm_icon_url'] = realm_icon_url(user_profile.realm) state['realm_icon_source'] = user_profile.realm.icon_source state['realm_email_changes_disabled'] = user_profile.realm.email_changes_disabled + state['max_icon_file_size'] = settings.MAX_ICON_FILE_SIZE if want('realm_domain'): state['realm_domain'] = user_profile.realm.domain diff --git a/zerver/tests/test_upload.py b/zerver/tests/test_upload.py index 6886bb4a79..8bfc074a25 100644 --- a/zerver/tests/test_upload.py +++ b/zerver/tests/test_upload.py @@ -521,6 +521,14 @@ class AvatarTest(UploadSerializeMixin, ZulipTestCase): self.assertEqual(user_profile.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR) self.assertEqual(user_profile.avatar_version, 2) + def test_avatar_upload_file_size_error(self): + # type: () -> None + self.login("hamlet@zulip.com") + with get_test_image_file(self.correct_files[0][0]) as fp: + with self.settings(MAX_AVATAR_FILE_SIZE=0): + result = self.client_put_multipart("/json/users/me/avatar", {'file': fp}) + self.assert_json_error(result, "Uploaded file is larger than the allowed limit of 0 MB") + def tearDown(self): # type: () -> None destroy_uploads() @@ -650,7 +658,6 @@ class RealmIconTest(UploadSerializeMixin, ZulipTestCase): def test_realm_icon_version(self): # type: () -> None - self.login("iago@zulip.com") realm = get_realm('zulip') icon_version = realm.icon_version @@ -660,6 +667,14 @@ class RealmIconTest(UploadSerializeMixin, ZulipTestCase): realm = get_realm('zulip') self.assertEqual(realm.icon_version, icon_version + 1) + def test_realm_icon_upload_file_size_error(self): + # type: () -> None + self.login("iago@zulip.com") + with get_test_image_file(self.correct_files[0][0]) as fp: + with self.settings(MAX_ICON_FILE_SIZE=0): + result = self.client_put_multipart("/json/realm/icon", {'file': fp}) + self.assert_json_error(result, "Uploaded file is larger than the allowed limit of 0 MB") + def tearDown(self): # type: () -> None destroy_uploads() diff --git a/zerver/tests/tests.py b/zerver/tests/tests.py index 973237b150..54e6f7e311 100644 --- a/zerver/tests/tests.py +++ b/zerver/tests/tests.py @@ -1972,6 +1972,8 @@ class HomeTest(ZulipTestCase): "left_side_userlist", "login_page", "mandatory_topics", + "max_avatar_file_size", + "max_icon_file_size", "max_message_id", "maxfilesize", "muted_topics", diff --git a/zerver/views/home.py b/zerver/views/home.py index a893145ae7..f739cc4b1d 100644 --- a/zerver/views/home.py +++ b/zerver/views/home.py @@ -204,6 +204,7 @@ def home_real(request): login_page = settings.HOME_NOT_LOGGED_IN, server_uri = settings.SERVER_URI, maxfilesize = settings.MAX_FILE_UPLOAD_SIZE, + max_avatar_file_size = settings.MAX_AVATAR_FILE_SIZE, server_generation = settings.SERVER_GENERATION, use_websockets = settings.USE_WEBSOCKETS, save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES, @@ -284,6 +285,7 @@ def home_real(request): 'emoji_alt_code', 'last_event_id', 'left_side_userlist', + 'max_icon_file_size', 'max_message_id', 'muted_topics', 'realm_add_emoji_by_admins_only', diff --git a/zerver/views/realm_icon.py b/zerver/views/realm_icon.py index 4abb75e61d..a12f4c9c64 100644 --- a/zerver/views/realm_icon.py +++ b/zerver/views/realm_icon.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.shortcuts import redirect from django.utils.translation import ugettext as _ from django.http import HttpResponse, HttpRequest @@ -18,6 +19,9 @@ def upload_icon(request, user_profile): return json_error(_("You must upload exactly one icon.")) icon_file = list(request.FILES.values())[0] + if ((settings.MAX_ICON_FILE_SIZE * 1024 * 1024) < icon_file.size): + return json_error(_("Uploaded file is larger than the allowed limit of %s MB") % ( + settings.MAX_ICON_FILE_SIZE)) upload_icon_image(icon_file, user_profile) do_change_icon_source(user_profile.realm, user_profile.realm.ICON_UPLOADED) icon_url = realm_icon_url(user_profile.realm) diff --git a/zerver/views/user_settings.py b/zerver/views/user_settings.py index 9a4ca276ed..f3936f74ab 100644 --- a/zerver/views/user_settings.py +++ b/zerver/views/user_settings.py @@ -268,6 +268,9 @@ def set_avatar_backend(request, user_profile): return json_error(_("You must upload exactly one avatar.")) user_file = list(request.FILES.values())[0] + if ((settings.MAX_AVATAR_FILE_SIZE * 1024 * 1024) < user_file.size): + return json_error(_("Uploaded file is larger than the allowed limit of %s MB") % ( + settings.MAX_AVATAR_FILE_SIZE)) upload_avatar_image(user_file, user_profile, user_profile) do_change_avatar_fields(user_profile, UserProfile.AVATAR_FROM_USER) user_avatar_url = avatar_url(user_profile) diff --git a/zproject/settings.py b/zproject/settings.py index 58fbd80531..0ac86c5532 100644 --- a/zproject/settings.py +++ b/zproject/settings.py @@ -116,6 +116,8 @@ DEFAULT_SETTINGS = {'TWITTER_CONSUMER_KEY': '', 'S3_AVATAR_BUCKET': '', 'LOCAL_UPLOADS_DIR': None, 'MAX_FILE_UPLOAD_SIZE': 25, + 'MAX_AVATAR_FILE_SIZE': 5, + 'MAX_ICON_FILE_SIZE': 5, 'ERROR_REPORTING': True, 'BROWSER_ERROR_REPORTING': False, 'STAGING_ERROR_NOTIFICATIONS': False,