diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 1956db8184d..de80ba88212 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -25,7 +25,6 @@ class UploadsController < ApplicationController def create # capture current user for block later on me = current_user - RateLimiter.new( current_user, "uploads-per-minute", @@ -228,7 +227,7 @@ class UploadsController < ApplicationController "upload.attachments.too_large_humanized", max_size: ActiveSupport::NumberHelper.number_to_human_size( - SiteSetting.max_attachment_size_kb.kilobytes, + UploadsController.max_attachment_size_for_user(current_user).kilobytes, ), ), ) @@ -277,7 +276,7 @@ class UploadsController < ApplicationController if url.present? && is_api maximum_upload_size = [ SiteSetting.max_image_size_kb, - SiteSetting.max_attachment_size_kb, + UploadsController.max_attachment_size_for_user(current_user), ].max.kilobytes tempfile = begin @@ -319,12 +318,20 @@ class UploadsController < ApplicationController private + def self.max_attachment_size_for_user(user) + if user.id == Discourse::SYSTEM_USER_ID && !SiteSetting.system_user_max_attachment_size_kb.zero? + SiteSetting.system_user_max_attachment_size_kb + else + SiteSetting.max_attachment_size_kb + end + end + # We can preemptively check size for attachments, but not for (most) images # as they may be further reduced in size by UploadCreator (at this point # they may have already been reduced in size by preprocessors) def attachment_too_big?(file_name, file_size) !FileHelper.is_supported_image?(file_name) && - file_size >= SiteSetting.max_attachment_size_kb.kilobytes + file_size >= UploadsController.max_attachment_size_for_user(current_user).kilobytes end # Gifs are not resized on the client and not reduced in size by UploadCreator diff --git a/config/site_settings.yml b/config/site_settings.yml index 2399d5344ce..8d7c7747afa 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -1490,6 +1490,11 @@ files: default: 4096 max: 1024000 type: file_size_restriction + system_user_max_attachment_size_kb: + default: 0 + max: 4096000 + type: file_size_restriction + hidden: true max_image_megapixels: default: 40 min: 5 diff --git a/lib/validators/upload_validator.rb b/lib/validators/upload_validator.rb index 3ff057ce0ae..d85068a1d18 100644 --- a/lib/validators/upload_validator.rb +++ b/lib/validators/upload_validator.rb @@ -146,7 +146,14 @@ class UploadValidator < ActiveModel::Validator if upload.for_export SiteSetting.max_export_file_size_kb else - SiteSetting.get("max_#{type}_size_kb") + if upload.user&.id == Discourse::SYSTEM_USER_ID && type == "attachment" + [ + SiteSetting.get("system_user_max_attachment_size_kb"), + SiteSetting.get("max_attachment_size_kb"), + ].max + else + SiteSetting.get("max_#{type}_size_kb") + end end max_size_bytes = max_size_kb.kilobytes diff --git a/spec/requests/uploads_controller_spec.rb b/spec/requests/uploads_controller_spec.rb index 1bca77e3db7..13025ebab28 100644 --- a/spec/requests/uploads_controller_spec.rb +++ b/spec/requests/uploads_controller_spec.rb @@ -2,6 +2,7 @@ RSpec.describe UploadsController do fab!(:user) { Fabricate(:user, refresh_auto_groups: true) } + fab!(:system_user) { Discourse.system_user } describe "#create" do it "requires you to be logged in" do @@ -238,6 +239,75 @@ RSpec.describe UploadsController do expect(message).to contain_exactly(I18n.t("upload.images.size_not_found")) end end + + context "when system user is logged in" do + before { sign_in(system_user) } + + let(:text_file) { Rack::Test::UploadedFile.new(File.new("#{Rails.root}/LICENSE.txt")) } + + it "properly returns errors if system_user_max_attachment_size_kb is not set" do + SiteSetting.authorized_extensions = "*" + SiteSetting.max_attachment_size_kb = 1 + + post "/uploads.json", params: { file: text_file, type: "composer" } + + expect(response.status).to eq(422) + errors = response.parsed_body["errors"] + expect(errors.first).to eq( + I18n.t("upload.attachments.too_large_humanized", max_size: "1 KB"), + ) + end + + it "should accept large files if system user" do + SiteSetting.authorized_extensions = "*" + SiteSetting.system_user_max_attachment_size_kb = 421_730 + + post "/uploads.json", params: { file: text_file, type: "composer" } + expect(response.status).to eq(200) + end + + it "should fail to accept large files if system user system_user_max_attachment_size_kb setting is low" do + SiteSetting.authorized_extensions = "*" + SiteSetting.max_attachment_size_kb = 1 + SiteSetting.system_user_max_attachment_size_kb = 1 + + post "/uploads.json", params: { file: text_file, type: "composer" } + + expect(response.status).to eq(422) + errors = response.parsed_body["errors"] + expect(errors.first).to eq( + I18n.t("upload.attachments.too_large_humanized", max_size: "1 KB"), + ) + end + + it "should fail to accept large files if system user system_user_max_attachment_size_kb setting is low and general setting is low" do + SiteSetting.authorized_extensions = "*" + SiteSetting.max_attachment_size_kb = 10 + SiteSetting.system_user_max_attachment_size_kb = 5 + + post "/uploads.json", params: { file: text_file, type: "composer" } + + expect(response.status).to eq(422) + errors = response.parsed_body["errors"] + expect(errors.first).to eq( + I18n.t("upload.attachments.too_large_humanized", max_size: "10 KB"), + ) + end + + it "should fail to accept large files if attachment_size settings are low" do + SiteSetting.authorized_extensions = "*" + SiteSetting.max_attachment_size_kb = 1 + SiteSetting.system_user_max_attachment_size_kb = 10 + + post "/uploads.json", params: { file: text_file, type: "composer" } + + expect(response.status).to eq(422) + errors = response.parsed_body["errors"] + expect(errors.first).to eq( + I18n.t("upload.attachments.too_large_humanized", max_size: "10 KB"), + ) + end + end end def upload_file(file, folder = "images")