carrierwave 1.3.4 → 2.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +117 -43
- data/lib/carrierwave/downloader/base.rb +93 -0
- data/lib/carrierwave/downloader/remote_file.rb +65 -0
- data/lib/carrierwave/locale/en.yml +5 -4
- data/lib/carrierwave/mount.rb +25 -19
- data/lib/carrierwave/mounter.rb +70 -47
- data/lib/carrierwave/orm/activerecord.rb +14 -8
- data/lib/carrierwave/processing/mini_magick.rb +100 -117
- data/lib/carrierwave/processing/rmagick.rb +1 -1
- data/lib/carrierwave/processing/vips.rb +284 -0
- data/lib/carrierwave/processing.rb +1 -0
- data/lib/carrierwave/sanitized_file.rb +38 -16
- data/lib/carrierwave/storage/file.rb +2 -2
- data/lib/carrierwave/storage/fog.rb +44 -13
- data/lib/carrierwave/storage.rb +1 -0
- data/lib/carrierwave/uploader/cache.rb +24 -16
- data/lib/carrierwave/uploader/configuration.rb +28 -15
- data/lib/carrierwave/uploader/content_type_blacklist.rb +17 -8
- data/lib/carrierwave/uploader/content_type_whitelist.rb +20 -8
- data/lib/carrierwave/uploader/download.rb +2 -123
- data/lib/carrierwave/uploader/extension_blacklist.rb +18 -10
- data/lib/carrierwave/uploader/extension_whitelist.rb +19 -10
- data/lib/carrierwave/uploader/mountable.rb +6 -0
- data/lib/carrierwave/uploader/processing.rb +11 -1
- data/lib/carrierwave/uploader/proxy.rb +2 -2
- data/lib/carrierwave/uploader/serialization.rb +1 -1
- data/lib/carrierwave/uploader/store.rb +5 -3
- data/lib/carrierwave/uploader/url.rb +6 -3
- data/lib/carrierwave/uploader/versions.rb +43 -13
- data/lib/carrierwave/uploader.rb +0 -9
- data/lib/carrierwave/validations/active_model.rb +3 -3
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +4 -0
- data/lib/generators/templates/uploader.rb +2 -2
- metadata +100 -27
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'carrierwave/downloader/base'
|
2
|
+
|
1
3
|
module CarrierWave
|
2
4
|
|
3
5
|
module Uploader
|
@@ -21,9 +23,10 @@ module CarrierWave
|
|
21
23
|
add_config :move_to_cache
|
22
24
|
add_config :move_to_store
|
23
25
|
add_config :remove_previously_stored_files_after_update
|
26
|
+
add_config :downloader
|
24
27
|
|
25
28
|
# fog
|
26
|
-
|
29
|
+
add_deprecated_config :fog_provider
|
27
30
|
add_config :fog_attributes
|
28
31
|
add_config :fog_credentials
|
29
32
|
add_config :fog_directory
|
@@ -107,8 +110,8 @@ module CarrierWave
|
|
107
110
|
# cache_storage CarrierWave::Storage::File
|
108
111
|
# cache_storage MyCustomStorageEngine
|
109
112
|
#
|
110
|
-
def cache_storage(storage =
|
111
|
-
|
113
|
+
def cache_storage(storage = false)
|
114
|
+
unless storage == false
|
112
115
|
self._cache_storage = storage.is_a?(Symbol) ? eval(storage_engines[storage]) : storage
|
113
116
|
end
|
114
117
|
_cache_storage
|
@@ -119,16 +122,8 @@ module CarrierWave
|
|
119
122
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
120
123
|
@#{name} = nil
|
121
124
|
|
122
|
-
def self.eager_load_fog(fog_credentials)
|
123
|
-
# see #1198. This will hopefully no longer be necessary after fog 2.0
|
124
|
-
require self.fog_provider
|
125
|
-
require 'carrierwave/storage/fog'
|
126
|
-
Fog::Storage.new(fog_credentials) if fog_credentials.present?
|
127
|
-
end unless defined? eager_load_fog
|
128
|
-
|
129
125
|
def self.#{name}(value=nil)
|
130
126
|
@#{name} = value if value
|
131
|
-
eager_load_fog(value) if value && '#{name}' == 'fog_credentials'
|
132
127
|
return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
|
133
128
|
name = superclass.#{name}
|
134
129
|
return nil if name.nil? && !instance_variable_defined?(:@#{name})
|
@@ -136,12 +131,10 @@ module CarrierWave
|
|
136
131
|
end
|
137
132
|
|
138
133
|
def self.#{name}=(value)
|
139
|
-
eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
|
140
134
|
@#{name} = value
|
141
135
|
end
|
142
136
|
|
143
137
|
def #{name}=(value)
|
144
|
-
self.class.eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
|
145
138
|
@#{name} = value
|
146
139
|
end
|
147
140
|
|
@@ -157,6 +150,26 @@ module CarrierWave
|
|
157
150
|
RUBY
|
158
151
|
end
|
159
152
|
|
153
|
+
def add_deprecated_config(name)
|
154
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
155
|
+
def self.#{name}(value=nil)
|
156
|
+
ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.#{name}=(value)
|
160
|
+
ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
|
161
|
+
end
|
162
|
+
|
163
|
+
def #{name}=(value)
|
164
|
+
ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
|
165
|
+
end
|
166
|
+
|
167
|
+
def #{name}
|
168
|
+
ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
|
169
|
+
end
|
170
|
+
RUBY
|
171
|
+
end
|
172
|
+
|
160
173
|
def configure
|
161
174
|
yield self
|
162
175
|
end
|
@@ -173,8 +186,7 @@ module CarrierWave
|
|
173
186
|
:fog => "CarrierWave::Storage::Fog"
|
174
187
|
}
|
175
188
|
config.storage = :file
|
176
|
-
config.cache_storage =
|
177
|
-
config.fog_provider = 'fog'
|
189
|
+
config.cache_storage = nil
|
178
190
|
config.fog_attributes = {}
|
179
191
|
config.fog_credentials = {}
|
180
192
|
config.fog_public = true
|
@@ -187,6 +199,7 @@ module CarrierWave
|
|
187
199
|
config.move_to_cache = false
|
188
200
|
config.move_to_store = false
|
189
201
|
config.remove_previously_stored_files_after_update = true
|
202
|
+
config.downloader = CarrierWave::Downloader::Base
|
190
203
|
config.ignore_integrity_errors = true
|
191
204
|
config.ignore_processing_errors = true
|
192
205
|
config.ignore_download_errors = true
|
@@ -8,39 +8,48 @@ module CarrierWave
|
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
11
|
-
# Override this method in your uploader to provide a
|
11
|
+
# Override this method in your uploader to provide a denylist of files content types
|
12
12
|
# which are not allowed to be uploaded.
|
13
13
|
# Not only strings but Regexp are allowed as well.
|
14
14
|
#
|
15
15
|
# === Returns
|
16
16
|
#
|
17
|
-
# [NilClass, String, Regexp, Array[String, Regexp]] a
|
17
|
+
# [NilClass, String, Regexp, Array[String, Regexp]] a denylist of content types which are not allowed to be uploaded
|
18
18
|
#
|
19
19
|
# === Examples
|
20
20
|
#
|
21
|
-
# def
|
21
|
+
# def content_type_denylist
|
22
22
|
# %w(text/json application/json)
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
# Basically the same, but using a Regexp:
|
26
26
|
#
|
27
|
-
# def
|
27
|
+
# def content_type_denylist
|
28
28
|
# [/(text|application)\/json/]
|
29
29
|
# end
|
30
30
|
#
|
31
|
-
def
|
31
|
+
def content_type_denylist
|
32
|
+
if respond_to?(:content_type_blacklist)
|
33
|
+
ActiveSupport::Deprecation.warn "#content_type_blacklist is deprecated, use #content_type_denylist instead." unless instance_variable_defined?(:@content_type_blacklist_warned)
|
34
|
+
@content_type_blacklist_warned = true
|
35
|
+
content_type_blacklist
|
36
|
+
end
|
37
|
+
end
|
32
38
|
|
33
39
|
private
|
34
40
|
|
35
41
|
def check_content_type_blacklist!(new_file)
|
42
|
+
return unless content_type_denylist
|
43
|
+
|
36
44
|
content_type = new_file.content_type
|
37
|
-
if
|
38
|
-
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_blacklist_error",
|
45
|
+
if blacklisted_content_type?(content_type)
|
46
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_blacklist_error",
|
47
|
+
content_type: content_type, default: :"errors.messages.content_type_denylist_error")
|
39
48
|
end
|
40
49
|
end
|
41
50
|
|
42
51
|
def blacklisted_content_type?(content_type)
|
43
|
-
Array(
|
52
|
+
Array(content_type_denylist).any? { |item| content_type =~ /#{item}/ }
|
44
53
|
end
|
45
54
|
|
46
55
|
end # ContentTypeBlacklist
|
@@ -8,39 +8,51 @@ module CarrierWave
|
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
11
|
-
# Override this method in your uploader to provide
|
11
|
+
# Override this method in your uploader to provide an allowlist of files content types
|
12
12
|
# which are allowed to be uploaded.
|
13
13
|
# Not only strings but Regexp are allowed as well.
|
14
14
|
#
|
15
15
|
# === Returns
|
16
16
|
#
|
17
|
-
# [NilClass, String, Regexp, Array[String, Regexp]]
|
17
|
+
# [NilClass, String, Regexp, Array[String, Regexp]] an allowlist of content types which are allowed to be uploaded
|
18
18
|
#
|
19
19
|
# === Examples
|
20
20
|
#
|
21
|
-
# def
|
21
|
+
# def content_type_allowlist
|
22
22
|
# %w(text/json application/json)
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
# Basically the same, but using a Regexp:
|
26
26
|
#
|
27
|
-
# def
|
27
|
+
# def content_type_allowlist
|
28
28
|
# [/(text|application)\/json/]
|
29
29
|
# end
|
30
30
|
#
|
31
|
-
def
|
31
|
+
def content_type_allowlist
|
32
|
+
if respond_to?(:content_type_whitelist)
|
33
|
+
ActiveSupport::Deprecation.warn "#content_type_whitelist is deprecated, use #content_type_allowlist instead." unless instance_variable_defined?(:@content_type_whitelist_warned)
|
34
|
+
@content_type_whitelist_warned = true
|
35
|
+
content_type_whitelist
|
36
|
+
end
|
37
|
+
end
|
32
38
|
|
33
39
|
private
|
34
40
|
|
35
41
|
def check_content_type_whitelist!(new_file)
|
42
|
+
return unless content_type_allowlist
|
43
|
+
|
36
44
|
content_type = new_file.content_type
|
37
|
-
if
|
38
|
-
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_whitelist_error", content_type: content_type,
|
45
|
+
if !whitelisted_content_type?(content_type)
|
46
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_whitelist_error", content_type: content_type,
|
47
|
+
allowed_types: Array(content_type_allowlist).join(", "), default: :"errors.messages.content_type_allowlist_error")
|
39
48
|
end
|
40
49
|
end
|
41
50
|
|
42
51
|
def whitelisted_content_type?(content_type)
|
43
|
-
Array(
|
52
|
+
Array(content_type_allowlist).any? do |item|
|
53
|
+
item = Regexp.quote(item) if item.class != Regexp
|
54
|
+
content_type =~ /\A#{item}/
|
55
|
+
end
|
44
56
|
end
|
45
57
|
|
46
58
|
end # ContentTypeWhitelist
|
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'open-uri'
|
2
|
-
require 'ssrf_filter'
|
3
|
-
|
4
1
|
module CarrierWave
|
5
2
|
module Uploader
|
6
3
|
module Download
|
@@ -10,87 +7,8 @@ module CarrierWave
|
|
10
7
|
include CarrierWave::Uploader::Configuration
|
11
8
|
include CarrierWave::Uploader::Cache
|
12
9
|
|
13
|
-
class RemoteFile
|
14
|
-
attr_reader :uri
|
15
|
-
|
16
|
-
def initialize(uri, remote_headers = {}, skip_ssrf_protection: false)
|
17
|
-
@uri = uri
|
18
|
-
@remote_headers = remote_headers.reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
|
19
|
-
@file, @content_type, @headers = nil
|
20
|
-
@skip_ssrf_protection = skip_ssrf_protection
|
21
|
-
end
|
22
|
-
|
23
|
-
def original_filename
|
24
|
-
filename = filename_from_header || filename_from_uri
|
25
|
-
mime_type = MIME::Types[content_type].first
|
26
|
-
unless File.extname(filename).present? || mime_type.blank?
|
27
|
-
filename = "#{filename}.#{mime_type.extensions.first}"
|
28
|
-
end
|
29
|
-
filename
|
30
|
-
end
|
31
|
-
|
32
|
-
def respond_to?(*args)
|
33
|
-
super or file.respond_to?(*args)
|
34
|
-
end
|
35
|
-
|
36
|
-
def http?
|
37
|
-
@uri.scheme =~ /^https?$/
|
38
|
-
end
|
39
|
-
|
40
|
-
def content_type
|
41
|
-
@content_type || 'application/octet-stream'
|
42
|
-
end
|
43
|
-
|
44
|
-
def headers
|
45
|
-
@headers || {}
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def file
|
51
|
-
if @file.blank?
|
52
|
-
if @skip_ssrf_protection
|
53
|
-
@file = (URI.respond_to?(:open) ? URI : Kernel).open(@uri.to_s, @remote_headers)
|
54
|
-
@file = @file.is_a?(String) ? StringIO.new(@file) : @file
|
55
|
-
@content_type = @file.content_type
|
56
|
-
@headers = @file.meta
|
57
|
-
@uri = @file.base_uri
|
58
|
-
else
|
59
|
-
request = nil
|
60
|
-
response = SsrfFilter.get(@uri, headers: @remote_headers) do |req|
|
61
|
-
request = req
|
62
|
-
end
|
63
|
-
response.value
|
64
|
-
@file = StringIO.new(response.body)
|
65
|
-
@content_type = response.content_type
|
66
|
-
@headers = response
|
67
|
-
@uri = request.uri
|
68
|
-
end
|
69
|
-
end
|
70
|
-
@file
|
71
|
-
|
72
|
-
rescue StandardError => e
|
73
|
-
raise CarrierWave::DownloadError, "could not download file: #{e.message}"
|
74
|
-
end
|
75
|
-
|
76
|
-
def filename_from_header
|
77
|
-
if headers['content-disposition']
|
78
|
-
match = headers['content-disposition'].match(/filename="?([^"]+)/)
|
79
|
-
return match[1] unless match.nil? || match[1].empty?
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def filename_from_uri
|
84
|
-
URI::DEFAULT_PARSER.unescape(File.basename(@uri.path))
|
85
|
-
end
|
86
|
-
|
87
|
-
def method_missing(*args, &block)
|
88
|
-
file.send(*args, &block)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
10
|
##
|
93
|
-
# Caches the file by downloading it from the given URL.
|
11
|
+
# Caches the file by downloading it from the given URL, using downloader.
|
94
12
|
#
|
95
13
|
# === Parameters
|
96
14
|
#
|
@@ -98,48 +16,9 @@ module CarrierWave
|
|
98
16
|
# [remote_headers (Hash)] Request headers
|
99
17
|
#
|
100
18
|
def download!(uri, remote_headers = {})
|
101
|
-
|
102
|
-
file = RemoteFile.new(processed_uri, remote_headers, skip_ssrf_protection: skip_ssrf_protection?(processed_uri))
|
103
|
-
raise CarrierWave::DownloadError, "trying to download a file which is not served over HTTP" unless file.http?
|
19
|
+
file = downloader.new(self).download(uri, remote_headers)
|
104
20
|
cache!(file)
|
105
21
|
end
|
106
|
-
|
107
|
-
##
|
108
|
-
# Processes the given URL by parsing and escaping it. Public to allow overriding.
|
109
|
-
#
|
110
|
-
# === Parameters
|
111
|
-
#
|
112
|
-
# [url (String)] The URL where the remote file is stored
|
113
|
-
#
|
114
|
-
def process_uri(uri)
|
115
|
-
URI.parse(uri)
|
116
|
-
rescue URI::InvalidURIError
|
117
|
-
uri_parts = uri.split('?')
|
118
|
-
# regexp from Ruby's URI::Parser#regexp[:UNSAFE], with [] specifically removed
|
119
|
-
encoded_uri = URI::DEFAULT_PARSER.escape(uri_parts.shift, /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/)
|
120
|
-
encoded_uri << '?' << URI::DEFAULT_PARSER.escape(uri_parts.join('?')) if uri_parts.any?
|
121
|
-
URI.parse(encoded_uri) rescue raise CarrierWave::DownloadError, "couldn't parse URL"
|
122
|
-
end
|
123
|
-
|
124
|
-
##
|
125
|
-
# If this returns true, SSRF protection will be bypassed.
|
126
|
-
# You can override this if you want to allow accessing specific local URIs that are not SSRF exploitable.
|
127
|
-
#
|
128
|
-
# === Parameters
|
129
|
-
#
|
130
|
-
# [uri (URI)] The URI where the remote file is stored
|
131
|
-
#
|
132
|
-
# === Examples
|
133
|
-
#
|
134
|
-
# class MyUploader < CarrierWave::Uploader::Base
|
135
|
-
# def skip_ssrf_protection?(uri)
|
136
|
-
# uri.hostname == 'localhost' && uri.port == 80
|
137
|
-
# end
|
138
|
-
# end
|
139
|
-
#
|
140
|
-
def skip_ssrf_protection?(uri)
|
141
|
-
false
|
142
|
-
end
|
143
22
|
end # Download
|
144
23
|
end # Uploader
|
145
24
|
end # CarrierWave
|
@@ -8,43 +8,51 @@ module CarrierWave
|
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
11
|
-
# Override this method in your uploader to provide a
|
11
|
+
# Override this method in your uploader to provide a denylist of extensions which
|
12
12
|
# are prohibited to be uploaded. Compares the file's extension case insensitive.
|
13
13
|
# Furthermore, not only strings but Regexp are allowed as well.
|
14
14
|
#
|
15
|
-
# When using a Regexp in the
|
15
|
+
# When using a Regexp in the denylist, `\A` and `\z` are automatically added to
|
16
16
|
# the Regexp expression, also case insensitive.
|
17
17
|
#
|
18
18
|
# === Returns
|
19
19
|
|
20
|
-
# [NilClass, String, Regexp, Array[String, Regexp]] a
|
20
|
+
# [NilClass, String, Regexp, Array[String, Regexp]] a deny list of extensions which are prohibited to be uploaded
|
21
21
|
#
|
22
22
|
# === Examples
|
23
23
|
#
|
24
|
-
# def
|
24
|
+
# def extension_denylist
|
25
25
|
# %w(swf tiff)
|
26
26
|
# end
|
27
27
|
#
|
28
28
|
# Basically the same, but using a Regexp:
|
29
29
|
#
|
30
|
-
# def
|
30
|
+
# def extension_denylist
|
31
31
|
# [/swf/, 'tiff']
|
32
32
|
# end
|
33
33
|
#
|
34
|
-
|
35
|
-
|
34
|
+
def extension_denylist
|
35
|
+
if respond_to?(:extension_blacklist)
|
36
|
+
ActiveSupport::Deprecation.warn "#extension_blacklist is deprecated, use #extension_denylist instead." unless instance_variable_defined?(:@extension_blacklist_warned)
|
37
|
+
@extension_blacklist_warned = true
|
38
|
+
extension_blacklist
|
39
|
+
end
|
40
|
+
end
|
36
41
|
|
37
42
|
private
|
38
43
|
|
39
44
|
def check_extension_blacklist!(new_file)
|
45
|
+
return unless extension_denylist
|
46
|
+
|
40
47
|
extension = new_file.extension.to_s
|
41
|
-
if
|
42
|
-
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_blacklist_error", extension: new_file.extension.inspect,
|
48
|
+
if blacklisted_extension?(extension)
|
49
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_blacklist_error", extension: new_file.extension.inspect,
|
50
|
+
prohibited_types: Array(extension_denylist).join(", "), default: :"errors.messages.extension_denylist_error")
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
46
54
|
def blacklisted_extension?(extension)
|
47
|
-
Array(
|
55
|
+
Array(extension_denylist).any? { |item| extension =~ /\A#{item}\z/i }
|
48
56
|
end
|
49
57
|
end
|
50
58
|
end
|
@@ -8,45 +8,54 @@ module CarrierWave
|
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
11
|
-
# Override this method in your uploader to provide
|
11
|
+
# Override this method in your uploader to provide an allowlist of extensions which
|
12
12
|
# are allowed to be uploaded. Compares the file's extension case insensitive.
|
13
13
|
# Furthermore, not only strings but Regexp are allowed as well.
|
14
14
|
#
|
15
|
-
# When using a Regexp in the
|
15
|
+
# When using a Regexp in the allowlist, `\A` and `\z` are automatically added to
|
16
16
|
# the Regexp expression, also case insensitive.
|
17
17
|
#
|
18
18
|
# === Returns
|
19
19
|
#
|
20
|
-
# [NilClass, String, Regexp, Array[String, Regexp]]
|
20
|
+
# [NilClass, String, Regexp, Array[String, Regexp]] an allowlist of extensions which are allowed to be uploaded
|
21
21
|
#
|
22
22
|
# === Examples
|
23
23
|
#
|
24
|
-
# def
|
24
|
+
# def extension_allowlist
|
25
25
|
# %w(jpg jpeg gif png)
|
26
26
|
# end
|
27
27
|
#
|
28
28
|
# Basically the same, but using a Regexp:
|
29
29
|
#
|
30
|
-
# def
|
30
|
+
# def extension_allowlist
|
31
31
|
# [/jpe?g/, 'gif', 'png']
|
32
32
|
# end
|
33
33
|
#
|
34
|
-
def
|
34
|
+
def extension_allowlist
|
35
|
+
if respond_to?(:extension_whitelist)
|
36
|
+
ActiveSupport::Deprecation.warn "#extension_whitelist is deprecated, use #extension_allowlist instead." unless instance_variable_defined?(:@extension_whitelist_warned)
|
37
|
+
@extension_whitelist_warned = true
|
38
|
+
extension_whitelist
|
39
|
+
end
|
40
|
+
end
|
35
41
|
|
36
42
|
private
|
37
43
|
|
38
44
|
def check_extension_whitelist!(new_file)
|
45
|
+
return unless extension_allowlist
|
46
|
+
|
39
47
|
extension = new_file.extension.to_s
|
40
|
-
if
|
41
|
-
|
48
|
+
if !whitelisted_extension?(extension)
|
49
|
+
# Look for whitelist first, then fallback to allowlist
|
50
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_whitelist_error", extension: new_file.extension.inspect,
|
51
|
+
allowed_types: Array(extension_allowlist).join(", "), default: :"errors.messages.extension_allowlist_error")
|
42
52
|
end
|
43
53
|
end
|
44
54
|
|
45
55
|
def whitelisted_extension?(extension)
|
46
56
|
downcase_extension = extension.downcase
|
47
|
-
Array(
|
57
|
+
Array(extension_allowlist).any? { |item| downcase_extension =~ /\A#{item}\z/i }
|
48
58
|
end
|
49
|
-
|
50
59
|
end # ExtensionWhitelist
|
51
60
|
end # Uploader
|
52
61
|
end # CarrierWave
|
@@ -33,6 +33,12 @@ module CarrierWave
|
|
33
33
|
@mounted_as = mounted_as
|
34
34
|
end
|
35
35
|
|
36
|
+
##
|
37
|
+
# Returns array index of given uploader within currently mounted uploaders
|
38
|
+
#
|
39
|
+
def index
|
40
|
+
model.__send__(:_mounter, mounted_as).uploaders.index(self)
|
41
|
+
end
|
36
42
|
end # Mountable
|
37
43
|
end # Uploader
|
38
44
|
end # CarrierWave
|
@@ -80,7 +80,17 @@ module CarrierWave
|
|
80
80
|
next unless self.send(condition, new_file)
|
81
81
|
end
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
|
+
if args.is_a? Array
|
85
|
+
kwargs, args = args.partition { |arg| arg.is_a? Hash }
|
86
|
+
end
|
87
|
+
|
88
|
+
if kwargs.present?
|
89
|
+
kwargs = kwargs.reduce(:merge)
|
90
|
+
self.send(method, *args, **kwargs)
|
91
|
+
else
|
92
|
+
self.send(method, *args)
|
93
|
+
end
|
84
94
|
end
|
85
95
|
end
|
86
96
|
end
|
@@ -23,14 +23,14 @@ module CarrierWave
|
|
23
23
|
alias_method :path, :current_path
|
24
24
|
|
25
25
|
##
|
26
|
-
# Returns a string that uniquely identifies the last stored file
|
26
|
+
# Returns a string that uniquely identifies the retrieved or last stored file
|
27
27
|
#
|
28
28
|
# === Returns
|
29
29
|
#
|
30
30
|
# [String] uniquely identifies a file
|
31
31
|
#
|
32
32
|
def identifier
|
33
|
-
storage.try(:identifier)
|
33
|
+
@identifier || storage.try(:identifier)
|
34
34
|
end
|
35
35
|
|
36
36
|
##
|
@@ -7,7 +7,7 @@ module CarrierWave
|
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
def serializable_hash(options = nil)
|
10
|
-
{"url" => url}.merge Hash[versions.map { |name, version| [name, { "url" => version.url }] }]
|
10
|
+
{"url" => url}.merge Hash[versions.map { |name, version| [name.to_s, { "url" => version.url }] }]
|
11
11
|
end
|
12
12
|
|
13
13
|
def as_json(options=nil)
|
@@ -11,7 +11,7 @@ module CarrierWave
|
|
11
11
|
prepend Module.new {
|
12
12
|
def initialize(*)
|
13
13
|
super
|
14
|
-
@file, @filename, @cache_id = nil
|
14
|
+
@file, @filename, @cache_id, @identifier = nil
|
15
15
|
end
|
16
16
|
}
|
17
17
|
end
|
@@ -61,7 +61,7 @@ module CarrierWave
|
|
61
61
|
#
|
62
62
|
def store!(new_file=nil)
|
63
63
|
cache!(new_file) if new_file && ((@cache_id != parent_cache_id) || @cache_id.nil?)
|
64
|
-
if !cache_only
|
64
|
+
if !cache_only && @file && @cache_id
|
65
65
|
with_callbacks(:store, new_file) do
|
66
66
|
new_file = storage.store!(@file)
|
67
67
|
if delete_tmp_file_after_storage
|
@@ -69,7 +69,8 @@ module CarrierWave
|
|
69
69
|
cache_storage.delete_dir!(cache_path(nil))
|
70
70
|
end
|
71
71
|
@file = new_file
|
72
|
-
@cache_id = nil
|
72
|
+
@cache_id = @identifier = nil
|
73
|
+
@staged = false
|
73
74
|
end
|
74
75
|
end
|
75
76
|
end
|
@@ -84,6 +85,7 @@ module CarrierWave
|
|
84
85
|
def retrieve_from_store!(identifier)
|
85
86
|
with_callbacks(:retrieve_from_store, identifier) do
|
86
87
|
@file = storage.retrieve!(identifier)
|
88
|
+
@identifier = identifier
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
@@ -15,9 +15,12 @@ module CarrierWave
|
|
15
15
|
# [String] the location where this file is accessible via a url
|
16
16
|
#
|
17
17
|
def url(options = {})
|
18
|
-
if file.respond_to?(:url)
|
19
|
-
file.method(:url).arity
|
20
|
-
|
18
|
+
if file.respond_to?(:url)
|
19
|
+
tmp_url = file.method(:url).arity.zero? ? file.url : file.url(options)
|
20
|
+
return tmp_url if tmp_url.present?
|
21
|
+
end
|
22
|
+
|
23
|
+
if file.respond_to?(:path)
|
21
24
|
path = encode_path(file.path.sub(File.expand_path(root), ''))
|
22
25
|
|
23
26
|
if host = asset_host
|