active_storage_validations 1.3.0 → 1.3.2
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 +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
|
|