active_storage_validations 1.3.0 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -8
- data/config/locales/da.yml +0 -1
- data/config/locales/de.yml +0 -1
- data/config/locales/en.yml +0 -1
- data/config/locales/es.yml +0 -1
- data/config/locales/fr.yml +0 -1
- data/config/locales/it.yml +0 -1
- data/config/locales/ja.yml +0 -1
- data/config/locales/nl.yml +0 -1
- data/config/locales/pl.yml +0 -1
- data/config/locales/pt-BR.yml +0 -1
- data/config/locales/ru.yml +0 -1
- data/config/locales/sv.yml +0 -1
- data/config/locales/tr.yml +0 -1
- data/config/locales/uk.yml +0 -1
- data/config/locales/vi.yml +0 -1
- data/config/locales/zh-CN.yml +0 -1
- data/lib/active_storage_validations/aspect_ratio_validator.rb +58 -43
- data/lib/active_storage_validations/attached_validator.rb +3 -3
- data/lib/active_storage_validations/base_size_validator.rb +5 -4
- data/lib/active_storage_validations/content_type_spoof_detector.rb +12 -48
- data/lib/active_storage_validations/content_type_validator.rb +97 -49
- data/lib/active_storage_validations/dimension_validator.rb +8 -7
- data/lib/active_storage_validations/limit_validator.rb +6 -5
- data/lib/active_storage_validations/matchers/aspect_ratio_validator_matcher.rb +7 -7
- data/lib/active_storage_validations/matchers/attached_validator_matcher.rb +6 -6
- data/lib/active_storage_validations/matchers/base_size_validator_matcher.rb +7 -7
- data/lib/active_storage_validations/matchers/content_type_validator_matcher.rb +33 -33
- data/lib/active_storage_validations/matchers/dimension_validator_matcher.rb +7 -7
- data/lib/active_storage_validations/matchers/limit_validator_matcher.rb +7 -7
- data/lib/active_storage_validations/matchers/processable_image_validator_matcher.rb +7 -7
- data/lib/active_storage_validations/metadata.rb +17 -18
- data/lib/active_storage_validations/processable_image_validator.rb +5 -5
- data/lib/active_storage_validations/shared/attachable.rb +134 -0
- data/lib/active_storage_validations/{concerns → shared}/errorable.rb +2 -1
- data/lib/active_storage_validations/shared/optionable.rb +27 -0
- data/lib/active_storage_validations/size_validator.rb +2 -2
- data/lib/active_storage_validations/total_size_validator.rb +2 -2
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +0 -1
- metadata +65 -25
- data/lib/active_storage_validations/concerns/metadatable.rb +0 -31
- data/lib/active_storage_validations/option_proc_unfolding.rb +0 -16
- /data/lib/active_storage_validations/matchers/{concerns → shared}/active_storageable.rb +0 -0
- /data/lib/active_storage_validations/matchers/{concerns → shared}/allow_blankable.rb +0 -0
- /data/lib/active_storage_validations/matchers/{concerns → shared}/attachable.rb +0 -0
- /data/lib/active_storage_validations/matchers/{concerns → shared}/contextable.rb +0 -0
- /data/lib/active_storage_validations/matchers/{concerns → shared}/messageable.rb +0 -0
- /data/lib/active_storage_validations/matchers/{concerns → shared}/rspecable.rb +0 -0
- /data/lib/active_storage_validations/matchers/{concerns → shared}/validatable.rb +0 -0
- /data/lib/active_storage_validations/{concerns → shared}/active_storageable.rb +0 -0
- /data/lib/active_storage_validations/{concerns → shared}/loggable.rb +0 -0
- /data/lib/active_storage_validations/{concerns → shared}/symbolizable.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e1145fabe1ee64e7f7033a38c5b1de1114b364e5e2d9eb010628afde0a3e0b3
|
4
|
+
data.tar.gz: 497b550e5d98bf96e4398969de34022b8e8f828003a620261909a6d39e349aff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b671b5b33834ff904de1dea6dca51bfd741eaf2e10d931896a6fc4ff9ff83fc213f1581d59c562a806640cdc7a10537fa5ddb240051152ff38b5933ea7c7dc33
|
7
|
+
data.tar.gz: 381da6fbfb0fa544357bc492cfc9535992097ed7118846bae62f1b1953e5058f7b1bbc389ebd0dd8543fb035edaba55cee34a8c73a1b4fa84fc2ffc3e8bf9654
|
data/README.md
CHANGED
@@ -91,9 +91,10 @@ Marcel::MimeType.extend "application/ino", extensions: %w(ino), parents: "text/p
|
|
91
91
|
```
|
92
92
|
|
93
93
|
**Content type spoofing protection**
|
94
|
+
|
94
95
|
File content type spoofing happens when an ill-intentioned user uploads a file which hides its true content type by faking its extension and its declared content type value. For example, a user may try to upload a `.exe` file (application/x-msdownload content type) dissimulated as a `.jpg` file (image/jpeg content type).
|
95
96
|
|
96
|
-
By default, the gem does not prevent content type spoofing (prevent it by default is a breaking change that will be implemented in v2). The spoofing protection relies on both the linux `file` command and `Marcel` gem.
|
97
|
+
By default, the gem does not prevent content type spoofing (prevent it by default is a breaking change that will be implemented in v2). The spoofing protection relies on both the linux `file` command and `Marcel` gem. Be careful, since it needs to load the whole file io to perform the analysis, it will use a lot of RAM for very large files. Therefore it could be a wise decision not to enable it in this case.
|
97
98
|
|
98
99
|
Take note that the `file` analyzer will not find the exactly same content type as the ActiveStorage blob (its content type detection relies on a different logic using content+filename+extension). To handle this issue, we consider a close parent content type to be a match. For example, for an ActiveStorage blob which content type is `video/x-ms-wmv`, the `file` analyzer will probably detect a `video/x-ms-asf` content type, this will be considered as a valid match because these 2 content types are closely related. The correlation mapping is based on `Marcel::TYPE_PARENTS`.
|
99
100
|
|
@@ -202,7 +203,6 @@ en:
|
|
202
203
|
aspect_ratio_not_portrait: "must be a portrait image"
|
203
204
|
aspect_ratio_not_landscape: "must be a landscape image"
|
204
205
|
aspect_ratio_is_not: "must have an aspect ratio of %{aspect_ratio}"
|
205
|
-
aspect_ratio_unknown: "has an unknown aspect ratio"
|
206
206
|
image_not_processable: "is not a valid image"
|
207
207
|
```
|
208
208
|
|
@@ -221,7 +221,8 @@ aspect_ratio_is_not: "must be a %{aspect_ratio} image"
|
|
221
221
|
|
222
222
|
### Content type
|
223
223
|
The `content_type_invalid` key has three variables that you can use:
|
224
|
-
- `content_type` containing the content type of the sent file
|
224
|
+
- `content_type` containing the exact content type of the sent file
|
225
|
+
- `human_content_type` containing a more user-friendly version of the sent file content type (e.g. 'TXT' for 'text/plain')
|
225
226
|
- `authorized_types` containing the list of authorized content types
|
226
227
|
- `filename` containing the current file name
|
227
228
|
|
@@ -436,10 +437,11 @@ Then you can use the matchers with the syntax specified in the RSpec section, ju
|
|
436
437
|
|
437
438
|
To run tests in root folder of gem:
|
438
439
|
|
439
|
-
* `BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle exec rake test` to run for Rails
|
440
|
+
* `BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle exec rake test` to run for Rails 7.0
|
440
441
|
* `BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle exec rake test` to run for Rails 7.0
|
441
|
-
* `BUNDLE_GEMFILE=gemfiles/rails_7_1.gemfile bundle exec rake test` to run for Rails 7.
|
442
|
-
* `BUNDLE_GEMFILE=gemfiles/
|
442
|
+
* `BUNDLE_GEMFILE=gemfiles/rails_7_1.gemfile bundle exec rake test` to run for Rails 7.1
|
443
|
+
* `BUNDLE_GEMFILE=gemfiles/rails_7_2.gemfile bundle exec rake test` to run for Rails 7.2
|
444
|
+
* `BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle exec rake test` to run for Rails 8.0
|
443
445
|
|
444
446
|
Snippet to run in console:
|
445
447
|
|
@@ -447,11 +449,13 @@ Snippet to run in console:
|
|
447
449
|
BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle
|
448
450
|
BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle
|
449
451
|
BUNDLE_GEMFILE=gemfiles/rails_7_1.gemfile bundle
|
450
|
-
BUNDLE_GEMFILE=gemfiles/
|
452
|
+
BUNDLE_GEMFILE=gemfiles/rails_7_2.gemfile bundle
|
453
|
+
BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle
|
451
454
|
BUNDLE_GEMFILE=gemfiles/rails_6_1_4.gemfile bundle exec rake test
|
452
455
|
BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle exec rake test
|
453
456
|
BUNDLE_GEMFILE=gemfiles/rails_7_1.gemfile bundle exec rake test
|
454
|
-
BUNDLE_GEMFILE=gemfiles/
|
457
|
+
BUNDLE_GEMFILE=gemfiles/rails_7_2.gemfile bundle exec rake test
|
458
|
+
BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle exec rake test
|
455
459
|
```
|
456
460
|
|
457
461
|
Tips:
|
data/config/locales/da.yml
CHANGED
@@ -29,5 +29,4 @@ da:
|
|
29
29
|
aspect_ratio_not_portrait: "skal være et portrætbillede"
|
30
30
|
aspect_ratio_not_landscape: "skal være et landskabsbillede"
|
31
31
|
aspect_ratio_is_not: "skal have et størrelsesforhold på %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "har et ukendt størrelsesforhold"
|
33
32
|
image_not_processable: "er ikke et gyldigt billede"
|
data/config/locales/de.yml
CHANGED
@@ -29,5 +29,4 @@ de:
|
|
29
29
|
aspect_ratio_not_portrait: "muss Hochformat sein"
|
30
30
|
aspect_ratio_not_landscape: "muss Querformat sein"
|
31
31
|
aspect_ratio_is_not: "muss ein Bildseitenverhältnis von %{aspect_ratio} haben"
|
32
|
-
aspect_ratio_unknown: "hat ein unbekanntes Bildseitenverhältnis"
|
33
32
|
image_not_processable: "ist kein gültiges Bild"
|
data/config/locales/en.yml
CHANGED
@@ -29,5 +29,4 @@ en:
|
|
29
29
|
aspect_ratio_not_portrait: "must be a portrait image"
|
30
30
|
aspect_ratio_not_landscape: "must be a landscape image"
|
31
31
|
aspect_ratio_is_not: "must have an aspect ratio of %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "has an unknown aspect ratio"
|
33
32
|
image_not_processable: "is not a valid image"
|
data/config/locales/es.yml
CHANGED
@@ -29,5 +29,4 @@ es:
|
|
29
29
|
aspect_ratio_not_portrait: "debe ser una imagen vertical"
|
30
30
|
aspect_ratio_not_landscape: "debe ser una imagen apaisada"
|
31
31
|
aspect_ratio_is_not: "debe tener una relación de aspecto de %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "tiene una relación de aspecto desconocida"
|
33
32
|
image_not_processable: "no es una imagen válida"
|
data/config/locales/fr.yml
CHANGED
@@ -29,5 +29,4 @@ fr:
|
|
29
29
|
aspect_ratio_not_portrait: "doit être une image en format portrait"
|
30
30
|
aspect_ratio_not_landscape: "doit être une image en format paysage"
|
31
31
|
aspect_ratio_is_not: "doit avoir un rapport hauteur / largeur de %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "a un rapport d'aspect inconnu"
|
33
32
|
image_not_processable: "n'est pas une image valide"
|
data/config/locales/it.yml
CHANGED
@@ -29,5 +29,4 @@ it:
|
|
29
29
|
aspect_ratio_not_portrait: "l’orientamento dell’immagine deve essere verticale"
|
30
30
|
aspect_ratio_not_landscape: "l’orientamento dell’immagine deve essere orizzontale"
|
31
31
|
aspect_ratio_is_not: "deve avere un rapporto altezza / larghezza di %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "ha un rapporto altezza / larghezza sconosciuto"
|
33
32
|
image_not_processable: "non è un'immagine valida"
|
data/config/locales/ja.yml
CHANGED
data/config/locales/nl.yml
CHANGED
@@ -29,5 +29,4 @@ nl:
|
|
29
29
|
aspect_ratio_not_portrait: "moet een staande afbeelding zijn"
|
30
30
|
aspect_ratio_not_landscape: "moet een liggende afbeelding zijn"
|
31
31
|
aspect_ratio_is_not: "moet een beeldverhouding hebben van %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "heeft een onbekende beeldverhouding"
|
33
32
|
image_not_processable: "is geen geldige afbeelding"
|
data/config/locales/pl.yml
CHANGED
@@ -29,5 +29,4 @@ pl:
|
|
29
29
|
aspect_ratio_not_portrait: "musi mieć proporcje portretu"
|
30
30
|
aspect_ratio_not_landscape: "musi mieć proporcje pejzażu"
|
31
31
|
aspect_ratio_is_not: "musi mieć proporcje %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "ma nieokreślone proporcje"
|
33
32
|
image_not_processable: "nie jest prawidłowym obrazem"
|
data/config/locales/pt-BR.yml
CHANGED
@@ -29,5 +29,4 @@ pt-BR:
|
|
29
29
|
aspect_ratio_not_portrait: "não está no formato retrato"
|
30
30
|
aspect_ratio_not_landscape: "não está no formato paisagem"
|
31
31
|
aspect_ratio_is_not: "não contém uma proporção de %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "não tem uma proporção definida"
|
33
32
|
image_not_processable: "não é uma imagem válida"
|
data/config/locales/ru.yml
CHANGED
@@ -29,5 +29,4 @@ ru:
|
|
29
29
|
aspect_ratio_not_portrait: "должно быть портретное изображение"
|
30
30
|
aspect_ratio_not_landscape: "должно быть пейзажное изображение"
|
31
31
|
aspect_ratio_is_not: "должен иметь соотношение сторон %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "имеет неизвестное соотношение сторон"
|
33
32
|
image_not_processable: "не является допустимым изображением"
|
data/config/locales/sv.yml
CHANGED
@@ -29,5 +29,4 @@ sv:
|
|
29
29
|
aspect_ratio_not_portrait: "måste vara en porträttorienterad bild"
|
30
30
|
aspect_ratio_not_landscape: "måste vara en landskapsorienterad bild"
|
31
31
|
aspect_ratio_is_not: "måste ha en följande aspect ratio %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "har en okänd aspect ratio"
|
33
32
|
image_not_processable: "är inte en giltig bild"
|
data/config/locales/tr.yml
CHANGED
@@ -29,5 +29,4 @@ tr:
|
|
29
29
|
aspect_ratio_not_portrait: "dikey bir imaj olmalı"
|
30
30
|
aspect_ratio_not_landscape: "yatay bir imaj olmalı"
|
31
31
|
aspect_ratio_is_not: "%{aspect_ratio} en boy oranına sahip olmalı"
|
32
|
-
aspect_ratio_unknown: "bilinmeyen en boy oranı"
|
33
32
|
image_not_processable: "geçerli bir imaj değil"
|
data/config/locales/uk.yml
CHANGED
@@ -29,5 +29,4 @@ uk:
|
|
29
29
|
aspect_ratio_not_portrait: "мусить бути портретне зображення"
|
30
30
|
aspect_ratio_not_landscape: "мусить бути пейзажне зображення"
|
31
31
|
aspect_ratio_is_not: "мусить мати співвідношення сторін %{aspect_ratio}"
|
32
|
-
aspect_ratio_unknown: "має невідоме співвідношення сторін"
|
33
32
|
image_not_processable: "не є допустимим зображенням"
|
data/config/locales/vi.yml
CHANGED
data/config/locales/zh-CN.yml
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
6
|
-
require_relative '
|
3
|
+
require_relative 'shared/active_storageable'
|
4
|
+
require_relative 'shared/attachable'
|
5
|
+
require_relative 'shared/errorable'
|
6
|
+
require_relative 'shared/optionable'
|
7
|
+
require_relative 'shared/symbolizable'
|
7
8
|
|
8
9
|
module ActiveStorageValidations
|
9
10
|
class AspectRatioValidator < ActiveModel::EachValidator # :nodoc
|
10
11
|
include ActiveStorageable
|
12
|
+
include Attachable
|
11
13
|
include Errorable
|
12
|
-
include
|
13
|
-
include OptionProcUnfolding
|
14
|
+
include Optionable
|
14
15
|
include Symbolizable
|
15
16
|
|
16
17
|
AVAILABLE_CHECKS = %i[with].freeze
|
@@ -22,7 +23,6 @@ module ActiveStorageValidations
|
|
22
23
|
aspect_ratio_not_portrait
|
23
24
|
aspect_ratio_not_landscape
|
24
25
|
aspect_ratio_is_not
|
25
|
-
aspect_ratio_unknown
|
26
26
|
].freeze
|
27
27
|
PRECISION = 3.freeze
|
28
28
|
|
@@ -40,48 +40,63 @@ module ActiveStorageValidations
|
|
40
40
|
private
|
41
41
|
|
42
42
|
def is_valid?(record, attribute, attachable, metadata)
|
43
|
-
flat_options =
|
44
|
-
errors_options = initialize_error_options(options, attachable)
|
45
|
-
|
46
|
-
if metadata[:width].to_i <= 0 || metadata[:height].to_i <= 0
|
47
|
-
errors_options[:aspect_ratio] = flat_options[:with]
|
43
|
+
flat_options = set_flat_options(record)
|
48
44
|
|
49
|
-
|
50
|
-
return false
|
51
|
-
end
|
45
|
+
return if image_metadata_missing?(record, attribute, attachable, flat_options, metadata)
|
52
46
|
|
53
47
|
case flat_options[:with]
|
54
|
-
when :square
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
when :portrait
|
60
|
-
return true if metadata[:height] > metadata[:width]
|
61
|
-
errors_options[:aspect_ratio] = flat_options[:with]
|
62
|
-
add_error(record, attribute, :aspect_ratio_not_portrait, **errors_options)
|
63
|
-
|
64
|
-
when :landscape
|
65
|
-
return true if metadata[:width] > metadata[:height]
|
66
|
-
errors_options[:aspect_ratio] = flat_options[:with]
|
67
|
-
add_error(record, attribute, :aspect_ratio_not_landscape, **errors_options)
|
68
|
-
|
69
|
-
when ASPECT_RATIO_REGEX
|
70
|
-
flat_options[:with] =~ ASPECT_RATIO_REGEX
|
71
|
-
x = $1.to_i
|
72
|
-
y = $2.to_i
|
73
|
-
|
74
|
-
return true if x > 0 && y > 0 && (x.to_f / y).round(PRECISION) == (metadata[:width].to_f / metadata[:height]).round(PRECISION)
|
75
|
-
|
76
|
-
errors_options[:aspect_ratio] = "#{x}:#{y}"
|
77
|
-
add_error(record, attribute, :aspect_ratio_is_not, **errors_options)
|
78
|
-
else
|
79
|
-
errors_options[:aspect_ratio] = flat_options[:with]
|
80
|
-
add_error(record, attribute, :aspect_ratio_unknown, **errors_options)
|
81
|
-
return false
|
48
|
+
when :square then validate_square_aspect_ratio(record, attribute, attachable, flat_options, metadata)
|
49
|
+
when :portrait then validate_portrait_aspect_ratio(record, attribute, attachable, flat_options, metadata)
|
50
|
+
when :landscape then validate_landscape_aspect_ratio(record, attribute, attachable, flat_options, metadata)
|
51
|
+
when ASPECT_RATIO_REGEX then validate_regex_aspect_ratio(record, attribute, attachable, flat_options, metadata)
|
82
52
|
end
|
83
53
|
end
|
84
54
|
|
55
|
+
def image_metadata_missing?(record, attribute, attachable, flat_options, metadata)
|
56
|
+
return false if metadata[:width].to_i > 0 && metadata[:height].to_i > 0
|
57
|
+
|
58
|
+
errors_options = initialize_error_options(options, attachable)
|
59
|
+
errors_options[:aspect_ratio] = flat_options[:with]
|
60
|
+
add_error(record, attribute, :image_metadata_missing, **errors_options)
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_square_aspect_ratio(record, attribute, attachable, flat_options, metadata)
|
65
|
+
return if metadata[:width] == metadata[:height]
|
66
|
+
|
67
|
+
errors_options = initialize_error_options(options, attachable)
|
68
|
+
errors_options[:aspect_ratio] = flat_options[:with]
|
69
|
+
add_error(record, attribute, :aspect_ratio_not_square, **errors_options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate_portrait_aspect_ratio(record, attribute, attachable, flat_options, metadata)
|
73
|
+
return if metadata[:width] < metadata[:height]
|
74
|
+
|
75
|
+
errors_options = initialize_error_options(options, attachable)
|
76
|
+
errors_options[:aspect_ratio] = flat_options[:with]
|
77
|
+
add_error(record, attribute, :aspect_ratio_not_portrait, **errors_options)
|
78
|
+
end
|
79
|
+
|
80
|
+
def validate_landscape_aspect_ratio(record, attribute, attachable, flat_options, metadata)
|
81
|
+
return if metadata[:width] > metadata[:height]
|
82
|
+
|
83
|
+
errors_options = initialize_error_options(options, attachable)
|
84
|
+
errors_options[:aspect_ratio] = flat_options[:with]
|
85
|
+
add_error(record, attribute, :aspect_ratio_not_landscape, **errors_options)
|
86
|
+
end
|
87
|
+
|
88
|
+
def validate_regex_aspect_ratio(record, attribute, attachable, flat_options, metadata)
|
89
|
+
flat_options[:with] =~ ASPECT_RATIO_REGEX
|
90
|
+
x = $1.to_i
|
91
|
+
y = $2.to_i
|
92
|
+
|
93
|
+
return if x > 0 && y > 0 && (x.to_f / y).round(PRECISION) == (metadata[:width].to_f / metadata[:height]).round(PRECISION)
|
94
|
+
|
95
|
+
errors_options = initialize_error_options(options, attachable)
|
96
|
+
errors_options[:aspect_ratio] = "#{x}:#{y}"
|
97
|
+
add_error(record, attribute, :aspect_ratio_is_not, **errors_options)
|
98
|
+
end
|
99
|
+
|
85
100
|
def ensure_at_least_one_validator_option
|
86
101
|
unless AVAILABLE_CHECKS.any? { |argument| options.key?(argument) }
|
87
102
|
raise ArgumentError, 'You must pass :with to the validator'
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
3
|
+
require_relative 'shared/active_storageable'
|
4
|
+
require_relative 'shared/errorable'
|
5
|
+
require_relative 'shared/symbolizable'
|
6
6
|
|
7
7
|
module ActiveStorageValidations
|
8
8
|
class AttachedValidator < ActiveModel::EachValidator # :nodoc:
|
@@ -1,14 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
3
|
+
require_relative 'shared/active_storageable'
|
4
|
+
require_relative 'shared/errorable'
|
5
|
+
require_relative 'shared/optionable'
|
6
|
+
require_relative 'shared/symbolizable'
|
6
7
|
|
7
8
|
module ActiveStorageValidations
|
8
9
|
class BaseSizeValidator < ActiveModel::EachValidator # :nodoc:
|
9
10
|
include ActiveStorageable
|
10
11
|
include Errorable
|
11
|
-
include
|
12
|
+
include Optionable
|
12
13
|
include Symbolizable
|
13
14
|
|
14
15
|
delegate :number_to_human_size, to: ActiveSupport::NumberHelper
|
@@ -1,18 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'shared/attachable'
|
4
|
+
require_relative 'shared/loggable'
|
4
5
|
require 'open3'
|
5
6
|
|
6
7
|
module ActiveStorageValidations
|
7
8
|
class ContentTypeSpoofDetector
|
8
9
|
class FileCommandLineToolNotInstalledError < StandardError; end
|
9
10
|
|
11
|
+
include Attachable
|
10
12
|
include Loggable
|
11
13
|
|
12
|
-
def initialize(record, attribute,
|
14
|
+
def initialize(record, attribute, attachable)
|
13
15
|
@record = record
|
14
16
|
@attribute = attribute
|
15
|
-
@
|
17
|
+
@attachable = attachable
|
16
18
|
end
|
17
19
|
|
18
20
|
def spoofed?
|
@@ -27,60 +29,22 @@ module ActiveStorageValidations
|
|
27
29
|
private
|
28
30
|
|
29
31
|
def filename
|
30
|
-
@filename ||= @
|
32
|
+
@filename ||= attachable_filename(@attachable).to_s
|
31
33
|
end
|
32
34
|
|
33
35
|
def supplied_content_type
|
34
|
-
|
35
|
-
@supplied_content_type ||= @file.blob.present? && @file.blob.content_type.downcase.split(/[;,\s]/, 2).first
|
36
|
+
@supplied_content_type ||= attachable_content_type(@attachable)
|
36
37
|
end
|
37
38
|
|
38
39
|
def io
|
39
|
-
@io ||=
|
40
|
-
when ActiveStorage::Attached::One then get_io_from_one
|
41
|
-
when ActiveStorage::Attached::Many then get_io_from_many
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def get_io_from_one
|
46
|
-
attachable = @record.attachment_changes[@attribute.to_s].attachable
|
47
|
-
|
48
|
-
case attachable
|
49
|
-
when ActionDispatch::Http::UploadedFile
|
50
|
-
attachable.read
|
51
|
-
when String
|
52
|
-
blob = ActiveStorage::Blob.find_signed!(attachable)
|
53
|
-
blob.download
|
54
|
-
when ActiveStorage::Blob
|
55
|
-
attachable.download
|
56
|
-
when Hash
|
57
|
-
attachable[:io].read
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def get_io_from_many
|
62
|
-
attachables = @record.attachment_changes[@attribute.to_s].attachables
|
63
|
-
|
64
|
-
if attachables.all? { |attachable| attachable.is_a?(ActionDispatch::Http::UploadedFile) }
|
65
|
-
attachables.find do |uploaded_file|
|
66
|
-
checksum = ActiveStorage::Blob.new.send(:compute_checksum_in_chunks, uploaded_file)
|
67
|
-
checksum == @file.checksum
|
68
|
-
end.read
|
69
|
-
elsif attachables.all? { |attachable| attachable.is_a?(String) }
|
70
|
-
# It's only possible to pass one String as attachable (not an array of String)
|
71
|
-
blob = ActiveStorage::Blob.find_signed!(attachables.first)
|
72
|
-
blob.download
|
73
|
-
elsif attachables.all? { |attachable| attachable.is_a?(ActiveStorage::Blob) }
|
74
|
-
attachables.find { |blob| blob == @file.blob }.download
|
75
|
-
elsif attachables.all? { |attachable| attachable.is_a?(Hash) }
|
76
|
-
# It's only possible to pass one Hash as attachable (not an array of Hash)
|
77
|
-
attachables.first[:io].read
|
78
|
-
end
|
40
|
+
@io ||= attachable_io(@attachable)
|
79
41
|
end
|
80
42
|
|
43
|
+
# Return the content_type found by Open3 analysis.
|
44
|
+
#
|
45
|
+
# Using Open3 is a better alternative than Marcel (Marcel::MimeType.for(io))
|
46
|
+
# for analyzing content type solely based on the file io.
|
81
47
|
def content_type_from_analyzer
|
82
|
-
# Using Open3 is a better alternative than Marcel (Marcel::MimeType.for(io))
|
83
|
-
# for analyzing content type solely based on the file io
|
84
48
|
@content_type_from_analyzer ||= open3_mime_type_for_io
|
85
49
|
end
|
86
50
|
|