FEATURE: new 'allow_staff_to_upload_any_file_in_pm' site setting

This commit is contained in:
Régis Hanol 2017-06-12 22:41:29 +02:00
parent 16475bae89
commit 54e8fb0d89
11 changed files with 71 additions and 28 deletions

View File

@ -239,8 +239,9 @@ export default Ember.Component.extend({
});
$element.on('fileuploadsubmit', (e, data) => {
const isUploading = validateUploadedFiles(data.files);
data.formData = { type: "composer" };
const isPrivateMessage = this.get("composer.privateMessage");
const isUploading = validateUploadedFiles(data.files, { isPrivateMessage });
data.formData = { type: "composer", for_private_message: isPrivateMessage };
this.setProperties({ uploadProgress: 0, isUploading });
return isUploading;
});

View File

@ -185,6 +185,12 @@ export function validateUploadedFile(file, opts) {
if (!name) { return false; }
// check that the uploaded file is authorized
if (Discourse.SiteSettings.allow_staff_to_upload_any_file_in_pm) {
if (opts["isPrivateMessage"] && Discourse.User.current("staff")) {
return true
}
}
if (opts["imagesOnly"]) {
if (!isAnImage(name) && !isAuthorizedImage(name)) {
bootbox.alert(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: authorizedImagesExtensions() }));

View File

@ -14,14 +14,15 @@ class UploadsController < ApplicationController
url = params[:url]
file = params[:file] || params[:files]&.first
for_private_message = params[:for_private_message]
if params[:synchronous] && (current_user.staff? || is_api?)
data = create_upload(file, url, type)
data = create_upload(file, url, type, for_private_message)
render json: data.as_json
else
Scheduler::Defer.later("Create Upload") do
begin
data = create_upload(file, url, type)
data = create_upload(file, url, type, for_private_message)
ensure
MessageBus.publish("/uploads/#{type}", (data || {}).as_json, client_ids: [params[:client_id]])
end
@ -58,7 +59,7 @@ class UploadsController < ApplicationController
raise Discourse::NotFound
end
def create_upload(file, url, type)
def create_upload(file, url, type, for_private_message = false)
if file.nil?
if url.present? && is_api?
maximum_upload_size = [SiteSetting.max_image_size_kb, SiteSetting.max_attachment_size_kb].max.kilobytes
@ -77,7 +78,10 @@ class UploadsController < ApplicationController
return { errors: [I18n.t("upload.file_missing")] } if tempfile.nil?
upload = UploadCreator.new(tempfile, filename, type: type, content_type: content_type).create_for(current_user.id)
opts = { type: type, content_type: content_type }
opts[:for_private_message] = true if for_private_message
upload = UploadCreator.new(tempfile, filename, opts).create_for(current_user.id)
if upload.errors.empty? && current_user.admin?
retain_hours = params[:retain_hours].to_i

View File

@ -13,8 +13,9 @@ class Upload < ActiveRecord::Base
has_many :optimized_images, dependent: :destroy
attr_accessor :is_attachment_for_group_message
attr_accessor :for_group_message
attr_accessor :for_theme
attr_accessor :for_private_message
validates_presence_of :filesize
validates_presence_of :original_filename

View File

@ -1191,6 +1191,8 @@ en:
png_to_jpg_quality: "Quality of the converted JPG file (1 is lowest quality, 99 is best quality, 100 to disable)."
allow_staff_to_upload_any_file_in_pm: "Allow staff members to upload any files in PM."
enable_flash_video_onebox: "Enable embedding of swf and flv (Adobe Flash) links in oneboxes. WARNING: may introduce security risks."
default_invitee_trust_level: "Default trust level (0-4) for invited users."

View File

@ -798,6 +798,9 @@ files:
default: 95
min: 1
max: 100
allow_staff_to_upload_any_file_in_pm:
default: true
client: true
trust:
default_trust_level:

View File

@ -577,7 +577,7 @@ module Email
# read attachment
File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded }
# create the upload for the user
opts = { is_attachment_for_group_message: options[:is_group_message] }
opts = { for_group_message: options[:is_group_message] }
upload = UploadCreator.new(tmp, attachment.filename, opts).create_for(options[:user].id)
if upload && upload.errors.empty?
# try to inline images

View File

@ -16,8 +16,9 @@ class UploadCreator
# - type (string)
# - content_type (string)
# - origin (string)
# - is_attachment_for_group_message (boolean)
# - for_group_message (boolean)
# - for_theme (boolean)
# - for_private_message (boolean)
def initialize(file, filename, opts = {})
@upload = Upload.new
@file = file
@ -78,13 +79,9 @@ class UploadCreator
@upload.width, @upload.height = ImageSizer.resize(*@image_info.size)
end
if @opts[:is_attachment_for_group_message]
@upload.is_attachment_for_group_message = true
end
if @opts[:for_theme]
@upload.for_theme = true
end
@upload.for_private_message = true if @opts[:for_private_message]
@upload.for_group_message = true if @opts[:for_group_message]
@upload.for_theme = true if @opts[:for_theme]
return @upload unless @upload.save

View File

@ -5,8 +5,13 @@ module Validators; end
class Validators::UploadValidator < ActiveModel::Validator
def validate(upload)
# staff can upload any file in PM
if upload.for_private_message && SiteSetting.allow_staff_to_upload_any_file_in_pm
return true if upload.user&.staff?
end
# check the attachment blacklist
if upload.is_attachment_for_group_message && SiteSetting.allow_all_attachments_for_group_messages
if upload.for_group_message && SiteSetting.allow_all_attachments_for_group_messages
return upload.original_filename =~ SiteSetting.attachment_filename_blacklist_regex
end

View File

@ -38,7 +38,7 @@ describe UploadsController do
end
it 'parameterize the type' do
subject.expects(:create_upload).with(logo, nil, "super_long_type_with_charssuper_long_type_with_char")
subject.expects(:create_upload).with(logo, nil, "super_long_type_with_charssuper_long_type_with_char", nil)
xhr :post, :create, file: logo, type: "super \# long \//\\ type with \\. $%^&*( chars" * 5
end
@ -52,11 +52,11 @@ describe UploadsController do
expect(response.status).to eq 200
expect(message.channel).to eq("/uploads/avatar")
expect(message.data).to be
expect(message.data["id"]).to be
end
it 'is successful with an attachment' do
SiteSetting.stubs(:authorized_extensions).returns("*")
SiteSetting.authorized_extensions = "*"
Jobs.expects(:enqueue).never
@ -66,7 +66,7 @@ describe UploadsController do
expect(response.status).to eq 200
expect(message.channel).to eq("/uploads/composer")
expect(message.data).to be
expect(message.data["id"]).to be
end
it 'is successful with synchronous api' do
@ -110,7 +110,7 @@ describe UploadsController do
end
it 'properly returns errors' do
SiteSetting.stubs(:max_attachment_size_kb).returns(1)
SiteSetting.max_attachment_size_kb = 1
Jobs.expects(:enqueue).never
@ -123,17 +123,30 @@ describe UploadsController do
end
it 'ensures allow_uploaded_avatars is enabled when uploading an avatar' do
SiteSetting.stubs(:allow_uploaded_avatars).returns(false)
SiteSetting.allow_uploaded_avatars = false
xhr :post, :create, file: logo, type: "avatar"
expect(response).to_not be_success
end
it 'ensures sso_overrides_avatar is not enabled when uploading an avatar' do
SiteSetting.stubs(:sso_overrides_avatar).returns(true)
SiteSetting.sso_overrides_avatar = true
xhr :post, :create, file: logo, type: "avatar"
expect(response).to_not be_success
end
it 'allows staff to upload any file in PM' do
SiteSetting.authorized_extensions = "jpg"
SiteSetting.allow_staff_to_upload_any_file_in_pm = true
@user.update_columns(moderator: true)
message = MessageBus.track_publish do
xhr :post, :create, file: text_file, type: "composer", for_private_message: true
end.first
expect(response).to be_success
expect(message.data["id"]).to be
end
it 'returns an error when it could not determine the dimensions of an image' do
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything).never
@ -194,7 +207,7 @@ describe UploadsController do
context "prevent anons from downloading files" do
before { SiteSetting.stubs(:prevent_anons_from_downloading_files).returns(true) }
before { SiteSetting.prevent_anons_from_downloading_files = true }
it "returns 404 when an anonymous user tries to download a file" do
Upload.expects(:find_by).never

View File

@ -65,11 +65,22 @@ test("new user cannot upload attachments", function() {
});
test("ensures an authorized upload", function() {
const html = { name: "unauthorized.html" };
sandbox.stub(bootbox, "alert");
not(validUpload([{ name: "unauthorized.html" }]));
ok(bootbox.alert.calledWith(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: authorizedExtensions() })));
});
test("staff can upload anything in PM", function() {
const files = [{ name: "some.docx" }];
Discourse.SiteSettings.authorized_extensions = "jpeg";
Discourse.SiteSettings.allow_staff_to_upload_any_file_in_pm = true;
Discourse.User.resetCurrent(Discourse.User.create({ moderator: true }));
sandbox.stub(bootbox, "alert");
not(validUpload([html]));
ok(bootbox.alert.calledWith(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: authorizedExtensions() })));
not(validUpload(files));
ok(validUpload(files, { isPrivateMessage: true }));
});
var imageSize = 10 * 1024;