carrierwave 1.3.2 → 2.2.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of carrierwave might be problematic. Click here for more details.
- 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 +71 -48
- data/lib/carrierwave/orm/activerecord.rb +14 -8
- data/lib/carrierwave/processing/mini_magick.rb +100 -117
- data/lib/carrierwave/processing/rmagick.rb +2 -2
- 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 +96 -17
@@ -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
|