FEATURE: support for enabling all upload file types

BUGFIX: authorized extensions is now case insensitive
This commit is contained in:
Régis Hanol 2014-04-29 19:12:35 +02:00
parent 359d59242e
commit 4371374ba6
6 changed files with 70 additions and 23 deletions

View File

@ -7,9 +7,6 @@
**/ **/
Discourse.Utilities = { Discourse.Utilities = {
IMAGE_EXTENSIONS: [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tif", ".tiff"],
IS_AN_IMAGE_REGEXP: /\.(png|jpg|jpeg|gif|bmp|tif|tiff)$/i,
translateSize: function(size) { translateSize: function(size) {
switch (size) { switch (size) {
case 'tiny': return 20; case 'tiny': return 20;
@ -180,9 +177,9 @@ Discourse.Utilities = {
@returns true whenever the upload is valid @returns true whenever the upload is valid
**/ **/
validateUploadedFile: function(file, type) { validateUploadedFile: function(file, type) {
// check that the uploaded file is authorized // check that the uploaded file is authorized
if (!Discourse.Utilities.isAuthorizedUpload(file)) { if (!Discourse.Utilities.authorizesAllExtensions() &&
!Discourse.Utilities.isAuthorizedUpload(file)) {
var extensions = Discourse.Utilities.authorizedExtensions(); var extensions = Discourse.Utilities.authorizedExtensions();
bootbox.alert(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: extensions })); bootbox.alert(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: extensions }));
return false; return false;
@ -206,6 +203,16 @@ Discourse.Utilities = {
return true; return true;
}, },
/**
Determine whether all file extensions are authorized.
@method authorizesAllExtensions
**/
authorizesAllExtensions: function() {
return Discourse.SiteSettings.authorized_extensions.indexOf("*") >= 0;
},
/** /**
Check the extension of the file against the list of authorized extensions Check the extension of the file against the list of authorized extensions
@ -213,9 +220,27 @@ Discourse.Utilities = {
@param {File} file The file we want to upload @param {File} file The file we want to upload
**/ **/
isAuthorizedUpload: function(file) { isAuthorizedUpload: function(file) {
var extensions = Discourse.SiteSettings.authorized_extensions; if (file && file.name) {
var regexp = new RegExp("(" + extensions + ")$", "i"); var extensions = _.chain(Discourse.SiteSettings.authorized_extensions.split("|"))
return file && file.name ? file.name.match(regexp) : false; .reject(function(extension) { return extension.indexOf("*") >= 0; })
.map(function(extension) { return (extension.indexOf(".") === 0 ? extension.substring(1) : extension).replace(".", "\\."); })
.value();
return new RegExp("\\.(" + extensions.join("|") + ")$", "i").test(file.name);
}
return false;
},
/**
List the authorized extension for display
@method authorizedExtensions
**/
authorizedExtensions: function() {
return _.chain(Discourse.SiteSettings.authorized_extensions.split("|"))
.reject(function(extension) { return extension.indexOf("*") >= 0; })
.map(function(extension) { return extension.toLowerCase(); })
.value()
.join(", ");
}, },
/** /**
@ -239,7 +264,7 @@ Discourse.Utilities = {
@param {String} path The path @param {String} path The path
**/ **/
isAnImage: function(path) { isAnImage: function(path) {
return Discourse.Utilities.IS_AN_IMAGE_REGEXP.test(path); return (/\.(png|jpg|jpeg|gif|bmp|tif|tiff)$/i).test(path);
}, },
/** /**
@ -248,11 +273,8 @@ Discourse.Utilities = {
@method allowsAttachments @method allowsAttachments
**/ **/
allowsAttachments: function() { allowsAttachments: function() {
return _.difference(Discourse.SiteSettings.authorized_extensions.split("|"), Discourse.Utilities.IMAGE_EXTENSIONS).length > 0; return Discourse.Utilities.authorizesAllExtensions() ||
}, (/(png|jpg|jpeg|gif|bmp|tif|tiff)/i).test(Discourse.SiteSettings.authorized_extensions);
authorizedExtensions: function() {
return Discourse.SiteSettings.authorized_extensions.replace(/\|/g, ", ");
}, },
displayErrorForUpload: function(data) { displayErrorForUpload: function(data) {

View File

@ -803,7 +803,7 @@ en:
max_image_size_kb: "The maximum size of images we allow users to upload in kB - configure the limit in nginx (client_max_body_size) / apache or proxy as well." max_image_size_kb: "The maximum size of images we allow users to upload in kB - configure the limit in nginx (client_max_body_size) / apache or proxy as well."
max_attachment_size_kb: "The maximum size of files we allow users to upload in kB - configure the limit in nginx (client_max_body_size) / apache or proxy as well." max_attachment_size_kb: "The maximum size of files we allow users to upload in kB - configure the limit in nginx (client_max_body_size) / apache or proxy as well."
authorized_extensions: "A pipe (|) separated list of file extensions allowed for upload" authorized_extensions: "A pipe (|) separated list of file extensions allowed for upload ('*' for enabling all file types)"
max_similar_results: "How many similar topics to show a user while they are composing a new topic" max_similar_results: "How many similar topics to show a user while they are composing a new topic"
title_prettify: "Prevent common title typos and errors, including all caps, lowercase first character, multiple ! and ?, extra . at end, etc." title_prettify: "Prevent common title typos and errors, including all caps, lowercase first character, multiple ! and ?, extra . at end, etc."

View File

@ -284,7 +284,7 @@ files:
default: 1024 default: 1024
authorized_extensions: authorized_extensions:
client: true client: true
default: '.jpg|.jpeg|.png|.gif' default: 'jpg|jpeg|png|gif'
refresh: true refresh: true
list: true list: true
crawl_images: crawl_images:

View File

@ -31,7 +31,7 @@ class FileHelper
end end
def self.images_regexp def self.images_regexp
@@images_regexp ||= /\.(#{images.to_a.join("|").gsub(".", "\.")})$/i @@images_regexp ||= /\.(#{images.to_a.join("|")})$/i
end end
end end

View File

@ -47,30 +47,39 @@ class Validators::UploadValidator < ActiveModel::Validator
.tr(" ", "") .tr(" ", "")
.split("|") .split("|")
.each do |extension| .each do |extension|
authorized_uploads << (extension.start_with?(".") ? extension[1..-1] : extension) next if extension.include?("*")
authorized_uploads << (extension.start_with?(".") ? extension[1..-1] : extension).downcase
end end
authorized_uploads authorized_uploads
end end
def authorized_images def authorized_images
@authorized_images ||= (authorized_uploads & FileHelper.images) authorized_uploads & FileHelper.images
end end
def authorized_attachments def authorized_attachments
@authorized_attachments ||= (authorized_uploads - FileHelper.images) authorized_uploads - FileHelper.images
end
def authorizes_all_extensions?
SiteSetting.authorized_extensions.include?("*")
end end
def authorized_extensions(upload, extension, extensions) def authorized_extensions(upload, extension, extensions)
unless authorized = extensions.include?(extension) return true if authorizes_all_extensions?
unless authorized = extensions.include?(extension.downcase)
message = I18n.t("upload.unauthorized", authorized_extensions: extensions.to_a.join(", ")) message = I18n.t("upload.unauthorized", authorized_extensions: extensions.to_a.join(", "))
upload.errors.add(:original_filename, message) upload.errors.add(:original_filename, message)
end end
authorized authorized
end end
def maximum_file_size(upload, type) def maximum_file_size(upload, type)
max_size_kb = SiteSetting.send("max_#{type}_size_kb").kilobytes max_size_kb = SiteSetting.send("max_#{type}_size_kb").kilobytes
if upload.filesize > max_size_kb if upload.filesize > max_size_kb
message = I18n.t("upload.#{type}s.too_large", max_size_kb: max_size_kb) message = I18n.t("upload.#{type}s.too_large", max_size_kb: max_size_kb)
upload.errors.add(:filesize, message) upload.errors.add(:filesize, message)

View File

@ -28,7 +28,7 @@ describe UploadsController do
let(:text_file) do let(:text_file) do
ActionDispatch::Http::UploadedFile.new({ ActionDispatch::Http::UploadedFile.new({
filename: 'LICENSE.txt', filename: 'LICENSE.TXT',
tempfile: File.new("#{Rails.root}/LICENSE.txt") tempfile: File.new("#{Rails.root}/LICENSE.txt")
}) })
end end
@ -39,7 +39,7 @@ describe UploadsController do
context 'when authorized' do context 'when authorized' do
before { SiteSetting.stubs(:authorized_extensions).returns(".png|.txt") } before { SiteSetting.stubs(:authorized_extensions).returns(".PNG|.txt") }
it 'is successful with an image' do it 'is successful with an image' do
xhr :post, :create, file: logo xhr :post, :create, file: logo
@ -75,6 +75,22 @@ describe UploadsController do
end end
context 'when everything is authorized' do
before { SiteSetting.stubs(:authorized_extensions).returns("*") }
it 'is successful with an image' do
xhr :post, :create, file: logo
response.status.should eq 200
end
it 'is successful with an attachment' do
xhr :post, :create, file: text_file
response.status.should eq 200
end
end
end end
context 'with some files' do context 'with some files' do