mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 18:03:43 +08:00
FEATURE: new 'allow_staff_to_upload_any_file_in_pm' site setting
This commit is contained in:
parent
16475bae89
commit
54e8fb0d89
|
@ -239,8 +239,9 @@ export default Ember.Component.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
$element.on('fileuploadsubmit', (e, data) => {
|
$element.on('fileuploadsubmit', (e, data) => {
|
||||||
const isUploading = validateUploadedFiles(data.files);
|
const isPrivateMessage = this.get("composer.privateMessage");
|
||||||
data.formData = { type: "composer" };
|
const isUploading = validateUploadedFiles(data.files, { isPrivateMessage });
|
||||||
|
data.formData = { type: "composer", for_private_message: isPrivateMessage };
|
||||||
this.setProperties({ uploadProgress: 0, isUploading });
|
this.setProperties({ uploadProgress: 0, isUploading });
|
||||||
return isUploading;
|
return isUploading;
|
||||||
});
|
});
|
||||||
|
|
|
@ -185,6 +185,12 @@ export function validateUploadedFile(file, opts) {
|
||||||
if (!name) { return false; }
|
if (!name) { return false; }
|
||||||
|
|
||||||
// check that the uploaded file is authorized
|
// 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 (opts["imagesOnly"]) {
|
||||||
if (!isAnImage(name) && !isAuthorizedImage(name)) {
|
if (!isAnImage(name) && !isAuthorizedImage(name)) {
|
||||||
bootbox.alert(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: authorizedImagesExtensions() }));
|
bootbox.alert(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: authorizedImagesExtensions() }));
|
||||||
|
|
|
@ -14,14 +14,15 @@ class UploadsController < ApplicationController
|
||||||
|
|
||||||
url = params[:url]
|
url = params[:url]
|
||||||
file = params[:file] || params[:files]&.first
|
file = params[:file] || params[:files]&.first
|
||||||
|
for_private_message = params[:for_private_message]
|
||||||
|
|
||||||
if params[:synchronous] && (current_user.staff? || is_api?)
|
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
|
render json: data.as_json
|
||||||
else
|
else
|
||||||
Scheduler::Defer.later("Create Upload") do
|
Scheduler::Defer.later("Create Upload") do
|
||||||
begin
|
begin
|
||||||
data = create_upload(file, url, type)
|
data = create_upload(file, url, type, for_private_message)
|
||||||
ensure
|
ensure
|
||||||
MessageBus.publish("/uploads/#{type}", (data || {}).as_json, client_ids: [params[:client_id]])
|
MessageBus.publish("/uploads/#{type}", (data || {}).as_json, client_ids: [params[:client_id]])
|
||||||
end
|
end
|
||||||
|
@ -58,7 +59,7 @@ class UploadsController < ApplicationController
|
||||||
raise Discourse::NotFound
|
raise Discourse::NotFound
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_upload(file, url, type)
|
def create_upload(file, url, type, for_private_message = false)
|
||||||
if file.nil?
|
if file.nil?
|
||||||
if url.present? && is_api?
|
if url.present? && is_api?
|
||||||
maximum_upload_size = [SiteSetting.max_image_size_kb, SiteSetting.max_attachment_size_kb].max.kilobytes
|
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?
|
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?
|
if upload.errors.empty? && current_user.admin?
|
||||||
retain_hours = params[:retain_hours].to_i
|
retain_hours = params[:retain_hours].to_i
|
||||||
|
|
|
@ -13,8 +13,9 @@ class Upload < ActiveRecord::Base
|
||||||
|
|
||||||
has_many :optimized_images, dependent: :destroy
|
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_theme
|
||||||
|
attr_accessor :for_private_message
|
||||||
|
|
||||||
validates_presence_of :filesize
|
validates_presence_of :filesize
|
||||||
validates_presence_of :original_filename
|
validates_presence_of :original_filename
|
||||||
|
|
|
@ -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)."
|
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."
|
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."
|
default_invitee_trust_level: "Default trust level (0-4) for invited users."
|
||||||
|
|
|
@ -798,6 +798,9 @@ files:
|
||||||
default: 95
|
default: 95
|
||||||
min: 1
|
min: 1
|
||||||
max: 100
|
max: 100
|
||||||
|
allow_staff_to_upload_any_file_in_pm:
|
||||||
|
default: true
|
||||||
|
client: true
|
||||||
|
|
||||||
trust:
|
trust:
|
||||||
default_trust_level:
|
default_trust_level:
|
||||||
|
|
|
@ -577,7 +577,7 @@ module Email
|
||||||
# read attachment
|
# read attachment
|
||||||
File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded }
|
File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded }
|
||||||
# create the upload for the user
|
# 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)
|
upload = UploadCreator.new(tmp, attachment.filename, opts).create_for(options[:user].id)
|
||||||
if upload && upload.errors.empty?
|
if upload && upload.errors.empty?
|
||||||
# try to inline images
|
# try to inline images
|
||||||
|
|
|
@ -16,8 +16,9 @@ class UploadCreator
|
||||||
# - type (string)
|
# - type (string)
|
||||||
# - content_type (string)
|
# - content_type (string)
|
||||||
# - origin (string)
|
# - origin (string)
|
||||||
# - is_attachment_for_group_message (boolean)
|
# - for_group_message (boolean)
|
||||||
# - for_theme (boolean)
|
# - for_theme (boolean)
|
||||||
|
# - for_private_message (boolean)
|
||||||
def initialize(file, filename, opts = {})
|
def initialize(file, filename, opts = {})
|
||||||
@upload = Upload.new
|
@upload = Upload.new
|
||||||
@file = file
|
@file = file
|
||||||
|
@ -78,13 +79,9 @@ class UploadCreator
|
||||||
@upload.width, @upload.height = ImageSizer.resize(*@image_info.size)
|
@upload.width, @upload.height = ImageSizer.resize(*@image_info.size)
|
||||||
end
|
end
|
||||||
|
|
||||||
if @opts[:is_attachment_for_group_message]
|
@upload.for_private_message = true if @opts[:for_private_message]
|
||||||
@upload.is_attachment_for_group_message = true
|
@upload.for_group_message = true if @opts[:for_group_message]
|
||||||
end
|
@upload.for_theme = true if @opts[:for_theme]
|
||||||
|
|
||||||
if @opts[:for_theme]
|
|
||||||
@upload.for_theme = true
|
|
||||||
end
|
|
||||||
|
|
||||||
return @upload unless @upload.save
|
return @upload unless @upload.save
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,13 @@ module Validators; end
|
||||||
class Validators::UploadValidator < ActiveModel::Validator
|
class Validators::UploadValidator < ActiveModel::Validator
|
||||||
|
|
||||||
def validate(upload)
|
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
|
# 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
|
return upload.original_filename =~ SiteSetting.attachment_filename_blacklist_regex
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ describe UploadsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'parameterize the type' do
|
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
|
xhr :post, :create, file: logo, type: "super \# long \//\\ type with \\. $%^&*( chars" * 5
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,11 +52,11 @@ describe UploadsController do
|
||||||
expect(response.status).to eq 200
|
expect(response.status).to eq 200
|
||||||
|
|
||||||
expect(message.channel).to eq("/uploads/avatar")
|
expect(message.channel).to eq("/uploads/avatar")
|
||||||
expect(message.data).to be
|
expect(message.data["id"]).to be
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is successful with an attachment' do
|
it 'is successful with an attachment' do
|
||||||
SiteSetting.stubs(:authorized_extensions).returns("*")
|
SiteSetting.authorized_extensions = "*"
|
||||||
|
|
||||||
Jobs.expects(:enqueue).never
|
Jobs.expects(:enqueue).never
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ describe UploadsController do
|
||||||
|
|
||||||
expect(response.status).to eq 200
|
expect(response.status).to eq 200
|
||||||
expect(message.channel).to eq("/uploads/composer")
|
expect(message.channel).to eq("/uploads/composer")
|
||||||
expect(message.data).to be
|
expect(message.data["id"]).to be
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is successful with synchronous api' do
|
it 'is successful with synchronous api' do
|
||||||
|
@ -110,7 +110,7 @@ describe UploadsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'properly returns errors' do
|
it 'properly returns errors' do
|
||||||
SiteSetting.stubs(:max_attachment_size_kb).returns(1)
|
SiteSetting.max_attachment_size_kb = 1
|
||||||
|
|
||||||
Jobs.expects(:enqueue).never
|
Jobs.expects(:enqueue).never
|
||||||
|
|
||||||
|
@ -123,17 +123,30 @@ describe UploadsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ensures allow_uploaded_avatars is enabled when uploading an avatar' do
|
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"
|
xhr :post, :create, file: logo, type: "avatar"
|
||||||
expect(response).to_not be_success
|
expect(response).to_not be_success
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ensures sso_overrides_avatar is not enabled when uploading an avatar' do
|
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"
|
xhr :post, :create, file: logo, type: "avatar"
|
||||||
expect(response).to_not be_success
|
expect(response).to_not be_success
|
||||||
end
|
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
|
it 'returns an error when it could not determine the dimensions of an image' do
|
||||||
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything).never
|
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything).never
|
||||||
|
|
||||||
|
@ -194,7 +207,7 @@ describe UploadsController do
|
||||||
|
|
||||||
context "prevent anons from downloading files" 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
|
it "returns 404 when an anonymous user tries to download a file" do
|
||||||
Upload.expects(:find_by).never
|
Upload.expects(:find_by).never
|
||||||
|
|
|
@ -65,11 +65,22 @@ test("new user cannot upload attachments", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("ensures an authorized upload", 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");
|
sandbox.stub(bootbox, "alert");
|
||||||
|
|
||||||
not(validUpload([html]));
|
not(validUpload(files));
|
||||||
ok(bootbox.alert.calledWith(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: authorizedExtensions() })));
|
ok(validUpload(files, { isPrivateMessage: true }));
|
||||||
});
|
});
|
||||||
|
|
||||||
var imageSize = 10 * 1024;
|
var imageSize = 10 * 1024;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user