carrierwave 2.2.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +180 -62
- data/lib/carrierwave/compatibility/paperclip.rb +4 -2
- data/lib/carrierwave/downloader/base.rb +28 -14
- data/lib/carrierwave/downloader/remote_file.rb +13 -10
- data/lib/carrierwave/locale/en.yml +5 -3
- data/lib/carrierwave/mount.rb +36 -50
- data/lib/carrierwave/mounter.rb +118 -50
- data/lib/carrierwave/orm/activerecord.rb +21 -62
- data/lib/carrierwave/processing/mini_magick.rb +45 -14
- data/lib/carrierwave/processing/rmagick.rb +47 -20
- data/lib/carrierwave/processing/vips.rb +43 -12
- data/lib/carrierwave/sanitized_file.rb +58 -77
- data/lib/carrierwave/storage/abstract.rb +5 -5
- data/lib/carrierwave/storage/file.rb +6 -5
- data/lib/carrierwave/storage/fog.rb +86 -65
- data/lib/carrierwave/test/matchers.rb +11 -7
- data/lib/carrierwave/uploader/cache.rb +19 -11
- data/lib/carrierwave/uploader/callbacks.rb +1 -1
- data/lib/carrierwave/uploader/configuration.rb +18 -8
- data/lib/carrierwave/uploader/{content_type_whitelist.rb → content_type_allowlist.rb} +18 -16
- data/lib/carrierwave/uploader/{content_type_blacklist.rb → content_type_denylist.rb} +20 -15
- data/lib/carrierwave/uploader/dimension.rb +66 -0
- data/lib/carrierwave/uploader/{extension_whitelist.rb → extension_allowlist.rb} +17 -15
- data/lib/carrierwave/uploader/{extension_blacklist.rb → extension_denylist.rb} +19 -14
- data/lib/carrierwave/uploader/file_size.rb +2 -2
- data/lib/carrierwave/uploader/processing.rb +45 -7
- data/lib/carrierwave/uploader/proxy.rb +16 -3
- data/lib/carrierwave/uploader/store.rb +70 -6
- data/lib/carrierwave/uploader/url.rb +1 -1
- data/lib/carrierwave/uploader/versions.rb +158 -138
- data/lib/carrierwave/uploader.rb +10 -8
- data/lib/carrierwave/utilities/file_name.rb +47 -0
- data/lib/carrierwave/utilities/uri.rb +14 -11
- data/lib/carrierwave/utilities.rb +1 -0
- data/lib/carrierwave/validations/active_model.rb +4 -6
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +18 -17
- data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +1 -1
- data/lib/generators/uploader_generator.rb +3 -3
- metadata +37 -63
@@ -18,6 +18,8 @@ module CarrierWave
|
|
18
18
|
# [:fog_use_ssl_for_aws] (optional) #public_url will use https for the AWS generated URL]
|
19
19
|
# [:fog_aws_accelerate] (optional) #public_url will use s3-accelerate subdomain
|
20
20
|
# instead of s3, defaults to false
|
21
|
+
# [:fog_aws_fips] (optional) #public_url will use s3-fips subdomain
|
22
|
+
# instead of s3, defaults to false
|
21
23
|
#
|
22
24
|
#
|
23
25
|
# AWS credentials contain the following keys:
|
@@ -146,7 +148,7 @@ module CarrierWave
|
|
146
148
|
:key => uploader.fog_directory,
|
147
149
|
:public => uploader.fog_public
|
148
150
|
).files.all(:prefix => uploader.cache_dir).each do |file|
|
149
|
-
# generate_cache_id returns key
|
151
|
+
# generate_cache_id returns key formatted TIMEINT-PID(-COUNTER)-RND
|
150
152
|
matched = file.key.match(/(\d+)-\d+-\d+(?:-\d+)?/)
|
151
153
|
next unless matched
|
152
154
|
time = Time.at(matched[1].to_i)
|
@@ -162,9 +164,10 @@ module CarrierWave
|
|
162
164
|
end
|
163
165
|
|
164
166
|
class File
|
165
|
-
DEFAULT_S3_REGION = 'us-east-1'
|
167
|
+
DEFAULT_S3_REGION = 'us-east-1'.freeze
|
166
168
|
|
167
169
|
include CarrierWave::Utilities::Uri
|
170
|
+
include CarrierWave::Utilities::FileName
|
168
171
|
|
169
172
|
##
|
170
173
|
# Current local path to file
|
@@ -197,27 +200,27 @@ module CarrierWave
|
|
197
200
|
# [NilClass] no authenticated url available
|
198
201
|
#
|
199
202
|
def authenticated_url(options = {})
|
200
|
-
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(
|
203
|
+
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(fog_provider)
|
201
204
|
# avoid a get by using local references
|
202
205
|
local_directory = connection.directories.new(:key => @uploader.fog_directory)
|
203
206
|
local_file = local_directory.files.new(:key => path)
|
204
207
|
expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
|
205
|
-
case
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
else
|
211
|
-
warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
|
212
|
-
local_file.url(expire_at)
|
213
|
-
end
|
214
|
-
when 'Rackspace', 'OpenStack'
|
215
|
-
connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
|
216
|
-
when 'Aliyun'
|
217
|
-
expire_at = expire_at - Time.now
|
218
|
-
local_file.url(expire_at)
|
208
|
+
case fog_provider
|
209
|
+
when 'AWS', 'Google'
|
210
|
+
# Older versions of fog-google do not support options as a parameter
|
211
|
+
if url_options_supported?(local_file)
|
212
|
+
local_file.url(expire_at, options)
|
219
213
|
else
|
214
|
+
warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
|
220
215
|
local_file.url(expire_at)
|
216
|
+
end
|
217
|
+
when 'Rackspace', 'OpenStack'
|
218
|
+
connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
|
219
|
+
when 'Aliyun'
|
220
|
+
expire_at -= Time.now
|
221
|
+
local_file.url(expire_at)
|
222
|
+
else
|
223
|
+
local_file.url(expire_at)
|
221
224
|
end
|
222
225
|
end
|
223
226
|
end
|
@@ -258,18 +261,6 @@ module CarrierWave
|
|
258
261
|
end
|
259
262
|
end
|
260
263
|
|
261
|
-
##
|
262
|
-
# Return extension of file
|
263
|
-
#
|
264
|
-
# === Returns
|
265
|
-
#
|
266
|
-
# [String] extension of file or nil if the file has no extension
|
267
|
-
#
|
268
|
-
def extension
|
269
|
-
path_elements = path.split('.')
|
270
|
-
path_elements.last if path_elements.size > 1
|
271
|
-
end
|
272
|
-
|
273
264
|
##
|
274
265
|
# deprecated: All attributes from file (includes headers)
|
275
266
|
#
|
@@ -296,16 +287,16 @@ module CarrierWave
|
|
296
287
|
#
|
297
288
|
# [String] contents of file
|
298
289
|
def read
|
299
|
-
file_body = file
|
290
|
+
file_body = file&.body
|
300
291
|
|
301
292
|
return if file_body.nil?
|
302
293
|
return file_body unless file_body.is_a?(::File)
|
303
294
|
|
304
|
-
# Fog::Storage::XXX::File#body could return the source file which was
|
305
|
-
read_source_file
|
295
|
+
# Fog::Storage::XXX::File#body could return the source file which was uploaded to the remote server.
|
296
|
+
return read_source_file if ::File.exist?(file_body.path)
|
306
297
|
|
307
298
|
# If the source file doesn't exist, the remote content is read
|
308
|
-
@file = nil
|
299
|
+
@file = nil
|
309
300
|
file.body
|
310
301
|
end
|
311
302
|
|
@@ -320,6 +311,15 @@ module CarrierWave
|
|
320
311
|
file.nil? ? 0 : file.content_length
|
321
312
|
end
|
322
313
|
|
314
|
+
##
|
315
|
+
# === Returns
|
316
|
+
#
|
317
|
+
# [Boolean] whether the file is non-existent or empty
|
318
|
+
#
|
319
|
+
def empty?
|
320
|
+
!exists? || size.zero?
|
321
|
+
end
|
322
|
+
|
323
323
|
##
|
324
324
|
# Check if the file exists on the remote service
|
325
325
|
#
|
@@ -343,7 +343,7 @@ module CarrierWave
|
|
343
343
|
fog_file = new_file.to_file
|
344
344
|
@content_type ||= new_file.content_type
|
345
345
|
@file = directory.files.create({
|
346
|
-
:body => fog_file
|
346
|
+
:body => fog_file || new_file.read,
|
347
347
|
:content_type => @content_type,
|
348
348
|
:key => path,
|
349
349
|
:public => @uploader.fog_public
|
@@ -364,7 +364,7 @@ module CarrierWave
|
|
364
364
|
#
|
365
365
|
def public_url
|
366
366
|
encoded_path = encode_path(path)
|
367
|
-
if host = @uploader.asset_host
|
367
|
+
if (host = @uploader.asset_host)
|
368
368
|
if host.respond_to? :call
|
369
369
|
"#{host.call(self)}/#{encoded_path}"
|
370
370
|
else
|
@@ -376,26 +376,29 @@ module CarrierWave
|
|
376
376
|
when 'AWS'
|
377
377
|
# check if some endpoint is set in fog_credentials
|
378
378
|
if @uploader.fog_credentials.has_key?(:endpoint)
|
379
|
+
raise 'fog_aws_fips = true is incompatible with :endpoint, as FIPS endpoints do not support path-style URLs.' if @uploader.fog_aws_fips
|
379
380
|
"#{@uploader.fog_credentials[:endpoint]}/#{@uploader.fog_directory}/#{encoded_path}"
|
380
381
|
else
|
381
382
|
protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
|
382
383
|
|
383
384
|
subdomain_regex = /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
|
384
|
-
|
385
|
+
# To use the virtual-hosted style, the bucket name needs to be representable as a subdomain
|
386
|
+
use_virtual_hosted_style = @uploader.fog_directory.to_s =~ subdomain_regex && !(protocol == 'https' && @uploader.fog_directory =~ /\./)
|
387
|
+
|
388
|
+
region = @uploader.fog_credentials[:region].to_s
|
389
|
+
regional_host = 's3.amazonaws.com' # used for DEFAULT_S3_REGION or no region set
|
390
|
+
if @uploader.fog_aws_fips
|
391
|
+
regional_host = "s3-fips.#{region}.amazonaws.com" # https://aws.amazon.com/compliance/fips/
|
392
|
+
elsif ![DEFAULT_S3_REGION, ''].include?(region)
|
393
|
+
regional_host = "s3.#{region}.amazonaws.com"
|
394
|
+
end
|
385
395
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
"#{protocol}://#{@uploader.fog_directory}.#{s3_subdomain}.amazonaws.com/#{encoded_path}"
|
396
|
+
if use_virtual_hosted_style
|
397
|
+
regional_host = 's3-accelerate.amazonaws.com' if @uploader.fog_aws_accelerate
|
398
|
+
"#{protocol}://#{@uploader.fog_directory}.#{regional_host}/#{encoded_path}"
|
390
399
|
else # directory is not a valid subdomain, so use path style for access
|
391
|
-
|
392
|
-
|
393
|
-
when DEFAULT_S3_REGION, ''
|
394
|
-
's3.amazonaws.com'
|
395
|
-
else
|
396
|
-
"s3.#{region}.amazonaws.com"
|
397
|
-
end
|
398
|
-
"#{protocol}://#{host}/#{@uploader.fog_directory}/#{encoded_path}"
|
400
|
+
raise 'FIPS Endpoints can only be used with Virtual Hosted-Style addressing.' if @uploader.fog_aws_fips
|
401
|
+
"#{protocol}://#{regional_host}/#{@uploader.fog_directory}/#{encoded_path}"
|
399
402
|
end
|
400
403
|
end
|
401
404
|
when 'Google'
|
@@ -409,7 +412,7 @@ module CarrierWave
|
|
409
412
|
end
|
410
413
|
|
411
414
|
##
|
412
|
-
# Return url to file, if
|
415
|
+
# Return url to file, if available
|
413
416
|
#
|
414
417
|
# === Returns
|
415
418
|
#
|
@@ -435,7 +438,7 @@ module CarrierWave
|
|
435
438
|
# [NilClass] no file name available
|
436
439
|
#
|
437
440
|
def filename(options = {})
|
438
|
-
return unless file_url = url(options)
|
441
|
+
return unless (file_url = url(options))
|
439
442
|
CGI.unescape(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
|
440
443
|
end
|
441
444
|
|
@@ -451,10 +454,29 @@ module CarrierWave
|
|
451
454
|
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
|
452
455
|
#
|
453
456
|
def copy_to(new_path)
|
454
|
-
|
457
|
+
file.copy(@uploader.fog_directory, new_path, copy_options)
|
455
458
|
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
|
456
459
|
end
|
457
460
|
|
461
|
+
##
|
462
|
+
# Return the local file
|
463
|
+
#
|
464
|
+
# === Returns
|
465
|
+
#
|
466
|
+
# [File] The local file as Ruby's File class
|
467
|
+
# or
|
468
|
+
# [NilClass] When there's no file, or the file is remotely stored
|
469
|
+
#
|
470
|
+
def to_file
|
471
|
+
return nil unless file.body.is_a? ::File
|
472
|
+
|
473
|
+
if file.body.closed?
|
474
|
+
::File.open(file.body.path) # Reopen if it's already closed
|
475
|
+
else
|
476
|
+
file.body
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
458
480
|
private
|
459
481
|
|
460
482
|
##
|
@@ -476,12 +498,10 @@ module CarrierWave
|
|
476
498
|
# [Fog::#{provider}::Directory] containing directory
|
477
499
|
#
|
478
500
|
def directory
|
479
|
-
@directory ||=
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
)
|
484
|
-
end
|
501
|
+
@directory ||= connection.directories.new(
|
502
|
+
:key => @uploader.fog_directory,
|
503
|
+
:public => @uploader.fog_public
|
504
|
+
)
|
485
505
|
end
|
486
506
|
|
487
507
|
##
|
@@ -498,14 +518,15 @@ module CarrierWave
|
|
498
518
|
def copy_options
|
499
519
|
options = {}
|
500
520
|
options.merge!(acl_header) if acl_header.present?
|
501
|
-
options['Content-Type'] ||= content_type if content_type
|
521
|
+
options[fog_provider == "Google" ? :content_type : 'Content-Type'] ||= content_type if content_type
|
502
522
|
options.merge(@uploader.fog_attributes)
|
503
523
|
end
|
504
524
|
|
505
525
|
def acl_header
|
506
|
-
|
526
|
+
case fog_provider
|
527
|
+
when 'AWS'
|
507
528
|
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
|
508
|
-
|
529
|
+
when "Google"
|
509
530
|
@uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
|
510
531
|
else
|
511
532
|
{}
|
@@ -516,14 +537,14 @@ module CarrierWave
|
|
516
537
|
@uploader.fog_credentials[:provider].to_s
|
517
538
|
end
|
518
539
|
|
519
|
-
def read_source_file
|
520
|
-
|
540
|
+
def read_source_file
|
541
|
+
source_file = to_file
|
542
|
+
return unless source_file
|
521
543
|
|
522
544
|
begin
|
523
|
-
|
524
|
-
file_body.read
|
545
|
+
source_file.read
|
525
546
|
ensure
|
526
|
-
|
547
|
+
source_file.close
|
527
548
|
end
|
528
549
|
end
|
529
550
|
|
@@ -45,11 +45,11 @@ module CarrierWave
|
|
45
45
|
def matches?(actual)
|
46
46
|
@actual = actual
|
47
47
|
# Satisfy expectation here. Return false or raise an error if it's not met.
|
48
|
-
(File.stat(@actual.path).mode &
|
48
|
+
(File.stat(@actual.path).mode & 0o777) == @expected
|
49
49
|
end
|
50
50
|
|
51
51
|
def failure_message
|
52
|
-
"expected #{@actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode &
|
52
|
+
"expected #{@actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0o777).to_s(8)}"
|
53
53
|
end
|
54
54
|
|
55
55
|
def failure_message_when_negated
|
@@ -76,11 +76,11 @@ module CarrierWave
|
|
76
76
|
def matches?(actual)
|
77
77
|
@actual = actual
|
78
78
|
# Satisfy expectation here. Return false or raise an error if it's not met.
|
79
|
-
(File.stat(File.dirname
|
79
|
+
(File.stat(File.dirname(@actual.path)).mode & 0o777) == @expected
|
80
80
|
end
|
81
81
|
|
82
82
|
def failure_message
|
83
|
-
"expected #{File.dirname @actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode &
|
83
|
+
"expected #{File.dirname @actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0o777).to_s(8)}"
|
84
84
|
end
|
85
85
|
|
86
86
|
def failure_message_when_negated
|
@@ -341,9 +341,11 @@ module CarrierWave
|
|
341
341
|
begin
|
342
342
|
require 'rmagick'
|
343
343
|
rescue LoadError
|
344
|
-
|
345
|
-
|
346
|
-
|
344
|
+
begin
|
345
|
+
require 'RMagick'
|
346
|
+
rescue LoadError
|
347
|
+
puts "WARNING: Failed to require rmagick, image processing may fail!"
|
348
|
+
end
|
347
349
|
end
|
348
350
|
end
|
349
351
|
MagickWrapper.new(filename)
|
@@ -353,6 +355,7 @@ module CarrierWave
|
|
353
355
|
|
354
356
|
class MagickWrapper # :nodoc:
|
355
357
|
attr_reader :image
|
358
|
+
|
356
359
|
def width
|
357
360
|
image.columns
|
358
361
|
end
|
@@ -372,6 +375,7 @@ module CarrierWave
|
|
372
375
|
|
373
376
|
class MiniMagickWrapper # :nodoc:
|
374
377
|
attr_reader :image
|
378
|
+
|
375
379
|
def width
|
376
380
|
image[:width]
|
377
381
|
end
|
@@ -24,7 +24,8 @@ module CarrierWave
|
|
24
24
|
# [String] a cache id in the format TIMEINT-PID-COUNTER-RND
|
25
25
|
#
|
26
26
|
def self.generate_cache_id
|
27
|
-
[
|
27
|
+
[
|
28
|
+
Time.now.utc.to_i,
|
28
29
|
SecureRandom.random_number(1_000_000_000_000_000),
|
29
30
|
'%04d' % (CarrierWave::CacheCounter.increment % 10_000),
|
30
31
|
'%04d' % SecureRandom.random_number(10_000)
|
@@ -64,7 +65,7 @@ module CarrierWave
|
|
64
65
|
# It's recommended that you keep cache files in one place only.
|
65
66
|
#
|
66
67
|
def clean_cached_files!(seconds=60*60*24)
|
67
|
-
(cache_storage || storage).new(
|
68
|
+
(cache_storage || storage).new(new).clean_cache!(seconds)
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
@@ -90,9 +91,9 @@ module CarrierWave
|
|
90
91
|
end
|
91
92
|
|
92
93
|
def sanitized_file
|
93
|
-
ActiveSupport::Deprecation.warn('#sanitized_file is deprecated, use #file instead.')
|
94
94
|
file
|
95
95
|
end
|
96
|
+
CarrierWave.deprecator.deprecate_methods(self, sanitized_file: :file)
|
96
97
|
|
97
98
|
##
|
98
99
|
# Returns a String which uniquely identifies the currently cached file for later retrieval
|
@@ -102,7 +103,7 @@ module CarrierWave
|
|
102
103
|
# [String] a cache name, in the format TIMEINT-PID-COUNTER-RND/filename.txt
|
103
104
|
#
|
104
105
|
def cache_name
|
105
|
-
File.join(cache_id,
|
106
|
+
File.join(cache_id, original_filename) if cache_id && original_filename
|
106
107
|
end
|
107
108
|
|
108
109
|
##
|
@@ -110,7 +111,7 @@ module CarrierWave
|
|
110
111
|
#
|
111
112
|
# By default, cache!() uses copy_to(), which operates by copying the file
|
112
113
|
# to the cache, then deleting the original file. If move_to_cache() is
|
113
|
-
#
|
114
|
+
# overridden to return true, then cache!() uses move_to(), which simply
|
114
115
|
# moves the file to the cache. Useful for large files.
|
115
116
|
#
|
116
117
|
# === Parameters
|
@@ -129,6 +130,7 @@ module CarrierWave
|
|
129
130
|
|
130
131
|
self.cache_id = CarrierWave.generate_cache_id unless cache_id
|
131
132
|
|
133
|
+
@identifier = nil
|
132
134
|
@staged = true
|
133
135
|
@filename = new_file.filename
|
134
136
|
self.original_filename = new_file.filename
|
@@ -165,7 +167,7 @@ module CarrierWave
|
|
165
167
|
self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
|
166
168
|
@staged = true
|
167
169
|
@filename = original_filename
|
168
|
-
@file = cache_storage.retrieve_from_cache!(
|
170
|
+
@file = cache_storage.retrieve_from_cache!(full_original_filename)
|
169
171
|
end
|
170
172
|
end
|
171
173
|
|
@@ -180,20 +182,21 @@ module CarrierWave
|
|
180
182
|
#
|
181
183
|
# [String] the cache path
|
182
184
|
#
|
183
|
-
def cache_path(for_file=
|
185
|
+
def cache_path(for_file=full_original_filename)
|
184
186
|
File.join(*[cache_dir, @cache_id, for_file].compact)
|
185
187
|
end
|
186
188
|
|
189
|
+
protected
|
190
|
+
|
191
|
+
attr_reader :cache_id
|
192
|
+
|
187
193
|
private
|
188
194
|
|
189
195
|
def workfile_path(for_file=original_filename)
|
190
196
|
File.join(CarrierWave.tmp_path, @cache_id, version_name.to_s, for_file)
|
191
197
|
end
|
192
198
|
|
193
|
-
attr_reader :
|
194
|
-
|
195
|
-
# We can override the full_original_filename method in other modules
|
196
|
-
alias_method :full_original_filename, :original_filename
|
199
|
+
attr_reader :original_filename
|
197
200
|
|
198
201
|
def cache_id=(cache_id)
|
199
202
|
# Earlier version used 3 part cache_id. Thus we should allow for
|
@@ -210,6 +213,11 @@ module CarrierWave
|
|
210
213
|
def cache_storage
|
211
214
|
@cache_storage ||= (self.class.cache_storage || self.class.storage).new(self)
|
212
215
|
end
|
216
|
+
|
217
|
+
# We can override the full_original_filename method in other modules
|
218
|
+
def full_original_filename
|
219
|
+
forcing_extension(original_filename)
|
220
|
+
end
|
213
221
|
end # Cache
|
214
222
|
end # Uploader
|
215
223
|
end # CarrierWave
|
@@ -24,6 +24,7 @@ module CarrierWave
|
|
24
24
|
add_config :move_to_store
|
25
25
|
add_config :remove_previously_stored_files_after_update
|
26
26
|
add_config :downloader
|
27
|
+
add_config :force_extension
|
27
28
|
|
28
29
|
# fog
|
29
30
|
add_deprecated_config :fog_provider
|
@@ -34,6 +35,7 @@ module CarrierWave
|
|
34
35
|
add_config :fog_authenticated_url_expiration
|
35
36
|
add_config :fog_use_ssl_for_aws
|
36
37
|
add_config :fog_aws_accelerate
|
38
|
+
add_config :fog_aws_fips
|
37
39
|
|
38
40
|
# Mounting
|
39
41
|
add_config :ignore_integrity_errors
|
@@ -44,6 +46,9 @@ module CarrierWave
|
|
44
46
|
add_config :validate_download
|
45
47
|
add_config :mount_on
|
46
48
|
add_config :cache_only
|
49
|
+
add_config :download_retry_count
|
50
|
+
add_config :download_retry_wait_time
|
51
|
+
add_config :skip_ssrf_protection
|
47
52
|
|
48
53
|
# set default values
|
49
54
|
reset_config
|
@@ -77,7 +82,7 @@ module CarrierWave
|
|
77
82
|
def storage(storage = nil)
|
78
83
|
case storage
|
79
84
|
when Symbol
|
80
|
-
if storage_engine = storage_engines[storage]
|
85
|
+
if (storage_engine = storage_engines[storage])
|
81
86
|
self._storage = eval storage_engine
|
82
87
|
else
|
83
88
|
raise CarrierWave::UnknownStorageError, "Unknown storage: #{storage}"
|
@@ -123,7 +128,7 @@ module CarrierWave
|
|
123
128
|
@#{name} = nil
|
124
129
|
|
125
130
|
def self.#{name}(value=nil)
|
126
|
-
@#{name} = value
|
131
|
+
@#{name} = value unless value.nil?
|
127
132
|
return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
|
128
133
|
name = superclass.#{name}
|
129
134
|
return nil if name.nil? && !instance_variable_defined?(:@#{name})
|
@@ -153,19 +158,19 @@ module CarrierWave
|
|
153
158
|
def add_deprecated_config(name)
|
154
159
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
155
160
|
def self.#{name}(value=nil)
|
156
|
-
|
161
|
+
CarrierWave.deprecator.warn "##{name} is deprecated and has no effect"
|
157
162
|
end
|
158
163
|
|
159
164
|
def self.#{name}=(value)
|
160
|
-
|
165
|
+
CarrierWave.deprecator.warn "##{name} is deprecated and has no effect"
|
161
166
|
end
|
162
167
|
|
163
168
|
def #{name}=(value)
|
164
|
-
|
169
|
+
CarrierWave.deprecator.warn "##{name} is deprecated and has no effect"
|
165
170
|
end
|
166
171
|
|
167
172
|
def #{name}
|
168
|
-
|
173
|
+
CarrierWave.deprecator.warn "##{name} is deprecated and has no effect"
|
169
174
|
end
|
170
175
|
RUBY
|
171
176
|
end
|
@@ -179,8 +184,8 @@ module CarrierWave
|
|
179
184
|
#
|
180
185
|
def reset_config
|
181
186
|
configure do |config|
|
182
|
-
config.permissions =
|
183
|
-
config.directory_permissions =
|
187
|
+
config.permissions = 0o644
|
188
|
+
config.directory_permissions = 0o755
|
184
189
|
config.storage_engines = {
|
185
190
|
:file => "CarrierWave::Storage::File",
|
186
191
|
:fog => "CarrierWave::Storage::Fog"
|
@@ -193,6 +198,7 @@ module CarrierWave
|
|
193
198
|
config.fog_authenticated_url_expiration = 600
|
194
199
|
config.fog_use_ssl_for_aws = true
|
195
200
|
config.fog_aws_accelerate = false
|
201
|
+
config.fog_aws_fips = false
|
196
202
|
config.store_dir = 'uploads'
|
197
203
|
config.cache_dir = 'uploads/tmp'
|
198
204
|
config.delete_tmp_file_after_storage = true
|
@@ -200,6 +206,7 @@ module CarrierWave
|
|
200
206
|
config.move_to_store = false
|
201
207
|
config.remove_previously_stored_files_after_update = true
|
202
208
|
config.downloader = CarrierWave::Downloader::Base
|
209
|
+
config.force_extension = false
|
203
210
|
config.ignore_integrity_errors = true
|
204
211
|
config.ignore_processing_errors = true
|
205
212
|
config.ignore_download_errors = true
|
@@ -210,6 +217,9 @@ module CarrierWave
|
|
210
217
|
config.base_path = CarrierWave.base_path
|
211
218
|
config.enable_processing = true
|
212
219
|
config.ensure_multipart_form = true
|
220
|
+
config.download_retry_count = 0
|
221
|
+
config.download_retry_wait_time = 5
|
222
|
+
config.skip_ssrf_protection = false
|
213
223
|
end
|
214
224
|
end
|
215
225
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module CarrierWave
|
2
2
|
module Uploader
|
3
|
-
module
|
3
|
+
module ContentTypeAllowlist
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
included do
|
7
|
-
before :cache, :
|
7
|
+
before :cache, :check_content_type_allowlist!
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
@@ -29,32 +29,34 @@ module CarrierWave
|
|
29
29
|
# end
|
30
30
|
#
|
31
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
32
|
end
|
38
33
|
|
39
34
|
private
|
40
35
|
|
41
|
-
def
|
42
|
-
|
36
|
+
def check_content_type_allowlist!(new_file)
|
37
|
+
allowlist = content_type_allowlist
|
38
|
+
if !allowlist && respond_to?(:content_type_whitelist) && content_type_whitelist
|
39
|
+
CarrierWave.deprecator.warn "#content_type_whitelist is deprecated, use #content_type_allowlist instead." unless instance_variable_defined?(:@content_type_whitelist_warned)
|
40
|
+
@content_type_whitelist_warned = true
|
41
|
+
allowlist = content_type_whitelist
|
42
|
+
end
|
43
|
+
|
44
|
+
return unless allowlist
|
43
45
|
|
44
46
|
content_type = new_file.content_type
|
45
|
-
if !
|
46
|
-
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.
|
47
|
-
allowed_types: Array(
|
47
|
+
if !allowlisted_content_type?(allowlist, content_type)
|
48
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_allowlist_error", content_type: content_type,
|
49
|
+
allowed_types: Array(allowlist).join(", "), default: :"errors.messages.content_type_whitelist_error")
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
51
|
-
def
|
52
|
-
Array(
|
53
|
+
def allowlisted_content_type?(allowlist, content_type)
|
54
|
+
Array(allowlist).any? do |item|
|
53
55
|
item = Regexp.quote(item) if item.class != Regexp
|
54
|
-
content_type =~
|
56
|
+
content_type =~ /\A#{item}/
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
|
-
end #
|
60
|
+
end # ContentTypeAllowlist
|
59
61
|
end # Uploader
|
60
62
|
end # CarrierWave
|