mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 13:33:24 +00:00
Add size limit for uploading user avatars and realm icons.
- Add settings parameter for max realm icon size. - Add settings parameter for max user avatar size. - Add checking file size to avatar and icon uploading views. - Transfer file size limit parameter to frontend. - Add tests.
This commit is contained in:
@@ -72,7 +72,8 @@ exports.build_user_avatar_widget = function (upload_function) {
|
|||||||
get_file_input,
|
get_file_input,
|
||||||
$("#user_avatar_file_input_error").expectOne(),
|
$("#user_avatar_file_input_error").expectOne(),
|
||||||
$("#user_avatar_upload_button").expectOne(),
|
$("#user_avatar_upload_button").expectOne(),
|
||||||
upload_function
|
upload_function,
|
||||||
|
page_params.max_avatar_file_size
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ var realm_icon = (function () {
|
|||||||
get_file_input,
|
get_file_input,
|
||||||
$("#realm_icon_file_input_error").expectOne(),
|
$("#realm_icon_file_input_error").expectOne(),
|
||||||
$("#realm_icon_upload_button").expectOne(),
|
$("#realm_icon_upload_button").expectOne(),
|
||||||
upload_function
|
upload_function,
|
||||||
|
page_params.max_icon_file_size
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ var upload_widget = (function () {
|
|||||||
|
|
||||||
var exports = {};
|
var exports = {};
|
||||||
|
|
||||||
|
var default_max_file_size = 5;
|
||||||
|
|
||||||
function is_image_format(file) {
|
function is_image_format(file) {
|
||||||
var type = file.type;
|
var type = file.type;
|
||||||
if (!type) {
|
if (!type) {
|
||||||
@@ -22,8 +24,11 @@ var upload_widget = (function () {
|
|||||||
file_name_field, // jQuery object to show file name
|
file_name_field, // jQuery object to show file name
|
||||||
input_error, // jQuery object for error text
|
input_error, // jQuery object for error text
|
||||||
clear_button, // jQuery button to clear last upload choice
|
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) {
|
function accept(file) {
|
||||||
file_name_field.text(file.name);
|
file_name_field.text(file.name);
|
||||||
@@ -61,8 +66,10 @@ var upload_widget = (function () {
|
|||||||
input_error.hide();
|
input_error.hide();
|
||||||
} else if (e.target.files.length === 1) {
|
} else if (e.target.files.length === 1) {
|
||||||
var file = e.target.files[0];
|
var file = e.target.files[0];
|
||||||
if (file.size > 5 * 1024 * 1024) {
|
if (file.size > max_file_upload_size * 1024 * 1024) {
|
||||||
input_error.text(i18n.t('File size must be < 5Mb.'));
|
input_error.text(i18n.t('File size must be < __max_file_size__Mb.', {
|
||||||
|
max_file_size: max_file_upload_size,
|
||||||
|
}));
|
||||||
input_error.show();
|
input_error.show();
|
||||||
clear();
|
clear();
|
||||||
} else if (!is_image_format(file)) {
|
} else if (!is_image_format(file)) {
|
||||||
@@ -103,7 +110,11 @@ var upload_widget = (function () {
|
|||||||
get_file_input, // function returns a jQuery file input object
|
get_file_input, // function returns a jQuery file input object
|
||||||
input_error, // jQuery object for error text
|
input_error, // jQuery object for error text
|
||||||
upload_button, // jQuery button to open file dialog
|
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() {
|
function accept() {
|
||||||
input_error.hide();
|
input_error.hide();
|
||||||
@@ -131,8 +142,10 @@ var upload_widget = (function () {
|
|||||||
input_error.hide();
|
input_error.hide();
|
||||||
} else if (e.target.files.length === 1) {
|
} else if (e.target.files.length === 1) {
|
||||||
var file = e.target.files[0];
|
var file = e.target.files[0];
|
||||||
if (file.size > 5 * 1024 * 1024) {
|
if (file.size > max_file_upload_size * 1024 * 1024) {
|
||||||
input_error.text(i18n.t('File size must be < 5Mb.'));
|
input_error.text(i18n.t('File size must be < __max_file_size__Mb.', {
|
||||||
|
max_file_size: max_file_upload_size,
|
||||||
|
}));
|
||||||
input_error.show();
|
input_error.show();
|
||||||
clear();
|
clear();
|
||||||
} else if (!is_image_format(file)) {
|
} else if (!is_image_format(file)) {
|
||||||
|
|||||||
@@ -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_url'] = realm_icon_url(user_profile.realm)
|
||||||
state['realm_icon_source'] = user_profile.realm.icon_source
|
state['realm_icon_source'] = user_profile.realm.icon_source
|
||||||
state['realm_email_changes_disabled'] = user_profile.realm.email_changes_disabled
|
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'):
|
if want('realm_domain'):
|
||||||
state['realm_domain'] = user_profile.realm.domain
|
state['realm_domain'] = user_profile.realm.domain
|
||||||
|
|||||||
@@ -521,6 +521,14 @@ class AvatarTest(UploadSerializeMixin, ZulipTestCase):
|
|||||||
self.assertEqual(user_profile.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR)
|
self.assertEqual(user_profile.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR)
|
||||||
self.assertEqual(user_profile.avatar_version, 2)
|
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):
|
def tearDown(self):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
destroy_uploads()
|
destroy_uploads()
|
||||||
@@ -650,7 +658,6 @@ class RealmIconTest(UploadSerializeMixin, ZulipTestCase):
|
|||||||
|
|
||||||
def test_realm_icon_version(self):
|
def test_realm_icon_version(self):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
|
|
||||||
self.login("iago@zulip.com")
|
self.login("iago@zulip.com")
|
||||||
realm = get_realm('zulip')
|
realm = get_realm('zulip')
|
||||||
icon_version = realm.icon_version
|
icon_version = realm.icon_version
|
||||||
@@ -660,6 +667,14 @@ class RealmIconTest(UploadSerializeMixin, ZulipTestCase):
|
|||||||
realm = get_realm('zulip')
|
realm = get_realm('zulip')
|
||||||
self.assertEqual(realm.icon_version, icon_version + 1)
|
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):
|
def tearDown(self):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
destroy_uploads()
|
destroy_uploads()
|
||||||
|
|||||||
@@ -1972,6 +1972,8 @@ class HomeTest(ZulipTestCase):
|
|||||||
"left_side_userlist",
|
"left_side_userlist",
|
||||||
"login_page",
|
"login_page",
|
||||||
"mandatory_topics",
|
"mandatory_topics",
|
||||||
|
"max_avatar_file_size",
|
||||||
|
"max_icon_file_size",
|
||||||
"max_message_id",
|
"max_message_id",
|
||||||
"maxfilesize",
|
"maxfilesize",
|
||||||
"muted_topics",
|
"muted_topics",
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ def home_real(request):
|
|||||||
login_page = settings.HOME_NOT_LOGGED_IN,
|
login_page = settings.HOME_NOT_LOGGED_IN,
|
||||||
server_uri = settings.SERVER_URI,
|
server_uri = settings.SERVER_URI,
|
||||||
maxfilesize = settings.MAX_FILE_UPLOAD_SIZE,
|
maxfilesize = settings.MAX_FILE_UPLOAD_SIZE,
|
||||||
|
max_avatar_file_size = settings.MAX_AVATAR_FILE_SIZE,
|
||||||
server_generation = settings.SERVER_GENERATION,
|
server_generation = settings.SERVER_GENERATION,
|
||||||
use_websockets = settings.USE_WEBSOCKETS,
|
use_websockets = settings.USE_WEBSOCKETS,
|
||||||
save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES,
|
save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES,
|
||||||
@@ -284,6 +285,7 @@ def home_real(request):
|
|||||||
'emoji_alt_code',
|
'emoji_alt_code',
|
||||||
'last_event_id',
|
'last_event_id',
|
||||||
'left_side_userlist',
|
'left_side_userlist',
|
||||||
|
'max_icon_file_size',
|
||||||
'max_message_id',
|
'max_message_id',
|
||||||
'muted_topics',
|
'muted_topics',
|
||||||
'realm_add_emoji_by_admins_only',
|
'realm_add_emoji_by_admins_only',
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from django.conf import settings
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.http import HttpResponse, HttpRequest
|
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."))
|
return json_error(_("You must upload exactly one icon."))
|
||||||
|
|
||||||
icon_file = list(request.FILES.values())[0]
|
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)
|
upload_icon_image(icon_file, user_profile)
|
||||||
do_change_icon_source(user_profile.realm, user_profile.realm.ICON_UPLOADED)
|
do_change_icon_source(user_profile.realm, user_profile.realm.ICON_UPLOADED)
|
||||||
icon_url = realm_icon_url(user_profile.realm)
|
icon_url = realm_icon_url(user_profile.realm)
|
||||||
|
|||||||
@@ -268,6 +268,9 @@ def set_avatar_backend(request, user_profile):
|
|||||||
return json_error(_("You must upload exactly one avatar."))
|
return json_error(_("You must upload exactly one avatar."))
|
||||||
|
|
||||||
user_file = list(request.FILES.values())[0]
|
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)
|
upload_avatar_image(user_file, user_profile, user_profile)
|
||||||
do_change_avatar_fields(user_profile, UserProfile.AVATAR_FROM_USER)
|
do_change_avatar_fields(user_profile, UserProfile.AVATAR_FROM_USER)
|
||||||
user_avatar_url = avatar_url(user_profile)
|
user_avatar_url = avatar_url(user_profile)
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ DEFAULT_SETTINGS = {'TWITTER_CONSUMER_KEY': '',
|
|||||||
'S3_AVATAR_BUCKET': '',
|
'S3_AVATAR_BUCKET': '',
|
||||||
'LOCAL_UPLOADS_DIR': None,
|
'LOCAL_UPLOADS_DIR': None,
|
||||||
'MAX_FILE_UPLOAD_SIZE': 25,
|
'MAX_FILE_UPLOAD_SIZE': 25,
|
||||||
|
'MAX_AVATAR_FILE_SIZE': 5,
|
||||||
|
'MAX_ICON_FILE_SIZE': 5,
|
||||||
'ERROR_REPORTING': True,
|
'ERROR_REPORTING': True,
|
||||||
'BROWSER_ERROR_REPORTING': False,
|
'BROWSER_ERROR_REPORTING': False,
|
||||||
'STAGING_ERROR_NOTIFICATIONS': False,
|
'STAGING_ERROR_NOTIFICATIONS': False,
|
||||||
|
|||||||
Reference in New Issue
Block a user