cloudinary 1.19.0 → 1.20.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 635b7d9336fcde0655fd8b348bda1cdf443a60e123991a1d6197a5a55e9d159b
4
- data.tar.gz: 8cef3ea547da816bf1125243e04ed72834414f8529a27579e2ba3916571d79bc
3
+ metadata.gz: 9b641514503d45c3a6c4e8ba5ed87f0f5b8e521260eda789212543be5ccf77a1
4
+ data.tar.gz: 0f65e99aa08e446f01d783af5284a1265aaa4f28a1e95c284addd30beb36ec8c
5
5
  SHA512:
6
- metadata.gz: a6852e75360ff11e51b3d6be2e8e15f48b06d39ac17b189b6fedd54f5a3041725514db2a799c8d08db3af6f355c9ae21559b3d69015ad8eea54e701554ec3d40
7
- data.tar.gz: 32997f5c779e28bb3711fea013d0131b922c92cc4e7314ec37d6c6ca45e8e2e765cfaf2979a3f04f7e4b57a5ddd9dc5ddb0b4c2b0afea9c3ff65e428a6005f41
6
+ metadata.gz: 191f627013b5f97fca44e748fd1c4631f9af58a69680b02fdc6b4604e2ddf31216542fcb88e2d1daa825c0ff22a138f2c7d0b41d4895a69cc1b7eaa42a1ab16a
7
+ data.tar.gz: 8a5abe09f562e188e1e6d43d05f462939c62c4070ac50e93c3acaf876998e651aa2fc50775a10c5fb324d388fac9d599c5fe0e60480cdbbebeb7eedf699057e8
data/CHANGELOG.md CHANGED
@@ -1,13 +1,32 @@
1
+ 1.20.0 / 2021-03-26
2
+ ==================
3
+
4
+ New functionality and features
5
+ ------------------------------
6
+
7
+ * Add support for `download_backedup_asset` helper method
8
+ * Add support for `filename_override` upload parameter
9
+ * Add support for `SHA-256` algorithm in auth signatures
10
+
11
+ Other Changes
12
+ -------------
13
+
14
+ * Fix `type` parameter support in ActiveStorage service
15
+ * Fix expression normalization in advanced cases
16
+ * Add test for context metadata as user variables
17
+ * Improve validation of auth token generation
18
+
19
+
1
20
  1.19.0 / 2021-03-05
2
21
  ==================
3
22
 
4
23
  New functionality and features
5
24
  ------------------------------
6
25
 
7
- * Add Account Provisioning API
8
- * Add support for `api_proxy` parameter
9
- * Add support for `date` parameter in `usage` Admin API
10
-
26
+ * Add Account Provisioning API
27
+ * Add support for `api_proxy` parameter
28
+ * Add support for `date` parameter in `usage` Admin API
29
+
11
30
  Other Changes
12
31
  -------------
13
32
 
@@ -17,7 +36,6 @@ Other Changes
17
36
  * Bump vulnerable version of rubyzip
18
37
  * Fix `cloudinary.gemspec` glob issue
19
38
 
20
-
21
39
  1.18.1 / 2020-09-30
22
40
  ===================
23
41
 
@@ -95,7 +95,12 @@ module ActiveStorage
95
95
 
96
96
  def delete(key)
97
97
  instrument :delete, key: key do
98
- Cloudinary::Uploader.destroy public_id(key), resource_type: resource_type(nil, key)
98
+ options = {
99
+ resource_type: resource_type(nil, key),
100
+ type: @options[:type]
101
+ }.compact
102
+
103
+ Cloudinary::Uploader.destroy public_id(key), **options
99
104
  end
100
105
  end
101
106
 
@@ -107,7 +112,12 @@ module ActiveStorage
107
112
  def exist?(key)
108
113
  instrument :exist, key: key do |payload|
109
114
  begin
110
- Cloudinary::Api.resource public_id(key), resource_type: resource_type(nil, key)
115
+ options = {
116
+ resource_type: resource_type(nil, key),
117
+ type: @options[:type]
118
+ }.compact
119
+
120
+ Cloudinary::Api.resource public_id(key), **options
111
121
  true
112
122
  rescue Cloudinary::Api::NotFound => e
113
123
  false
@@ -82,7 +82,8 @@ class Cloudinary::Api
82
82
  :phash,
83
83
  :quality_analysis,
84
84
  :derived_next_cursor,
85
- :accessibility_analysis
85
+ :accessibility_analysis,
86
+ :versions
86
87
  ), options)
87
88
  end
88
89
 
@@ -90,7 +91,7 @@ class Cloudinary::Api
90
91
  resource_type = options[:resource_type] || "image"
91
92
  type = options[:type] || "upload"
92
93
  uri = "resources/#{resource_type}/#{type}/restore"
93
- call_api(:post, uri, { :public_ids => public_ids }, options)
94
+ call_api(:post, uri, { :public_ids => public_ids, :versions => options[:versions] }, options)
94
95
  end
95
96
 
96
97
  def self.update(public_id, options={})
@@ -33,6 +33,10 @@ module Cloudinary
33
33
  end
34
34
  end
35
35
 
36
+ if url.blank? && acl.blank?
37
+ raise 'AuthToken must contain either an acl or a url property'
38
+ end
39
+
36
40
  token = []
37
41
  token << "ip=#{ip}" if ip
38
42
  token << "st=#{start}" if start
@@ -42,6 +42,7 @@ class Cloudinary::Uploader
42
42
  :faces => Cloudinary::Utils.as_safe_bool(options[:faces]),
43
43
  :folder => options[:folder],
44
44
  :format => options[:format],
45
+ :filename_override => options[:filename_override],
45
46
  :headers => build_custom_headers(options[:headers]),
46
47
  :image_metadata => Cloudinary::Utils.as_safe_bool(options[:image_metadata]),
47
48
  :invalidate => Cloudinary::Utils.as_safe_bool(options[:invalidate]),
@@ -341,10 +342,11 @@ class Cloudinary::Uploader
341
342
  non_signable ||= []
342
343
 
343
344
  unless options[:unsigned]
344
- api_key = options[:api_key] || Cloudinary.config.api_key || raise(CloudinaryException, "Must supply api_key")
345
- api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
346
- params[:signature] = Cloudinary::Utils.api_sign_request(params.reject { |k, v| non_signable.include?(k) }, api_secret)
347
- params[:api_key] = api_key
345
+ api_key = options[:api_key] || Cloudinary.config.api_key || raise(CloudinaryException, "Must supply api_key")
346
+ api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
347
+ signature_algorithm = options[:signature_algorithm]
348
+ params[:signature] = Cloudinary::Utils.api_sign_request(params.reject { |k, v| non_signable.include?(k) }, api_secret, signature_algorithm)
349
+ params[:api_key] = api_key
348
350
  end
349
351
  proxy = options[:api_proxy] || Cloudinary.config.api_proxy
350
352
  timeout = options.fetch(:timeout) { Cloudinary.config.to_h.fetch(:timeout, 60) }
@@ -32,19 +32,34 @@ class Cloudinary::Utils
32
32
 
33
33
  PREDEFINED_VARS = {
34
34
  "aspect_ratio" => "ar",
35
+ "aspectRatio" => "ar",
35
36
  "current_page" => "cp",
37
+ "currentPage" => "cp",
36
38
  "face_count" => "fc",
39
+ "faceCount" => "fc",
37
40
  "height" => "h",
38
41
  "initial_aspect_ratio" => "iar",
42
+ "initialAspectRatio" => "iar",
43
+ "trimmed_aspect_ratio" => "tar",
44
+ "trimmedAspectRatio" => "tar",
39
45
  "initial_height" => "ih",
46
+ "initialHeight" => "ih",
40
47
  "initial_width" => "iw",
48
+ "initialWidth" => "iw",
41
49
  "page_count" => "pc",
50
+ "pageCount" => "pc",
42
51
  "page_x" => "px",
52
+ "pageX" => "px",
43
53
  "page_y" => "py",
54
+ "pageY" => "py",
44
55
  "tags" => "tags",
45
56
  "initial_duration" => "idu",
57
+ "initialDuration" => "idu",
46
58
  "duration" => "du",
47
- "width" => "w"
59
+ "width" => "w",
60
+ "illustration_score" => "ils",
61
+ "illustrationScore" => "ils",
62
+ "context" => "ctx"
48
63
  }
49
64
 
50
65
  SIMPLE_TRANSFORMATION_PARAMS = {
@@ -144,6 +159,16 @@ class Cloudinary::Utils
144
159
  LONG_URL_SIGNATURE_LENGTH = 32
145
160
  SHORT_URL_SIGNATURE_LENGTH = 8
146
161
 
162
+ UPLOAD_PREFIX = 'https://api.cloudinary.com'
163
+
164
+ ALGO_SHA1 = :sha1
165
+ ALGO_SHA256 = :sha256
166
+
167
+ ALGORITHM_SIGNATURE = {
168
+ ALGO_SHA1 => Digest::SHA1,
169
+ ALGO_SHA256 => Digest::SHA256,
170
+ }
171
+
147
172
  def self.extract_config_params(options)
148
173
  options.select{|k,v| URL_KEYS.include?(k)}
149
174
  end
@@ -309,16 +334,16 @@ class Cloudinary::Utils
309
334
  "if_" + normalize_expression(if_value) unless if_value.to_s.empty?
310
335
  end
311
336
 
312
- EXP_REGEXP = Regexp.new('(?<!\$)('+PREDEFINED_VARS.keys.join("|")+')'+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
337
+ EXP_REGEXP = Regexp.new('(\$_*[^_ ]+)|(?<!\$)('+PREDEFINED_VARS.keys.join("|")+')'+'|('+CONDITIONAL_OPERATORS.keys.reverse.map { |k| Regexp.escape(k) }.join('|')+')(?=[ _])')
313
338
  EXP_REPLACEMENT = PREDEFINED_VARS.merge(CONDITIONAL_OPERATORS)
314
339
 
315
340
  def self.normalize_expression(expression)
316
341
  if expression.nil?
317
- ''
342
+ nil
318
343
  elsif expression.is_a?( String) && expression =~ /^!.+!$/ # quoted string
319
344
  expression
320
345
  else
321
- expression.to_s.gsub(EXP_REGEXP,EXP_REPLACEMENT).gsub(/[ _]+/, "_")
346
+ expression.to_s.gsub(EXP_REGEXP) { |match| EXP_REPLACEMENT[match] || match }.gsub(/[ _]+/, "_")
322
347
  end
323
348
  end
324
349
 
@@ -433,9 +458,9 @@ class Cloudinary::Utils
433
458
  params_to_sign.map{|k,v| [k.to_s, v.is_a?(Array) ? v.join(",") : v]}.reject{|k,v| v.nil? || v == ""}.sort_by(&:first).map{|k,v| "#{k}=#{v}"}.join("&")
434
459
  end
435
460
 
436
- def self.api_sign_request(params_to_sign, api_secret)
461
+ def self.api_sign_request(params_to_sign, api_secret, signature_algorithm = nil)
437
462
  to_sign = api_string_to_sign(params_to_sign)
438
- Digest::SHA1.hexdigest("#{to_sign}#{api_secret}")
463
+ hash("#{to_sign}#{api_secret}", signature_algorithm, :hexdigest)
439
464
  end
440
465
 
441
466
  # Returns a JSON array as String.
@@ -501,6 +526,7 @@ class Cloudinary::Utils
501
526
  use_root_path = config_option_consume(options, :use_root_path)
502
527
  auth_token = config_option_consume(options, :auth_token)
503
528
  long_url_signature = config_option_consume(options, :long_url_signature)
529
+ signature_algorithm = config_option_consume(options, :signature_algorithm)
504
530
  unless auth_token == false
505
531
  auth_token = Cloudinary::AuthToken.merge_auth_token(Cloudinary.config.auth_token, auth_token)
506
532
  end
@@ -545,7 +571,10 @@ class Cloudinary::Utils
545
571
  raise(CloudinaryException, "Must supply api_secret") if (secret.nil? || secret.empty?)
546
572
  to_sign = [transformation, sign_version && version, source_to_sign].reject(&:blank?).join("/")
547
573
  to_sign = fully_unescape(to_sign)
548
- signature = compute_signature(to_sign, secret, long_url_signature)
574
+ signature_algorithm = long_url_signature ? ALGO_SHA256 : signature_algorithm
575
+ hash = hash("#{to_sign}#{secret}", signature_algorithm)
576
+ signature = Base64.urlsafe_encode64(hash)
577
+ signature = "s--#{signature[0, long_url_signature ? LONG_URL_SIGNATURE_LENGTH : SHORT_URL_SIGNATURE_LENGTH ]}--"
549
578
  end
550
579
 
551
580
  prefix = unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
@@ -661,18 +690,31 @@ class Cloudinary::Utils
661
690
  prefix
662
691
  end
663
692
 
693
+ # Creates a base URL for the cloudinary api
694
+ #
695
+ # @param [Object] path Resource name
696
+ # @param [Hash] options Additional options
697
+ #
698
+ # @return [String]
699
+ def self.base_api_url(path, options = {})
700
+ cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || UPLOAD_PREFIX
701
+ cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise(CloudinaryException, 'Must supply cloud_name')
702
+
703
+ [cloudinary, 'v1_1', cloud_name, path].join('/')
704
+ end
705
+
664
706
  def self.cloudinary_api_url(action = 'upload', options = {})
665
- cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || "https://api.cloudinary.com"
666
- cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise(CloudinaryException, "Must supply cloud_name")
667
- resource_type = options[:resource_type] || "image"
668
- return [cloudinary, "v1_1", cloud_name, resource_type, action].join("/")
707
+ resource_type = options[:resource_type] || 'image'
708
+
709
+ base_api_url([resource_type, action], options)
669
710
  end
670
711
 
671
712
  def self.sign_request(params, options={})
672
713
  api_key = options[:api_key] || Cloudinary.config.api_key || raise(CloudinaryException, "Must supply api_key")
673
714
  api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
715
+ signature_algorithm = options[:signature_algorithm]
674
716
  params = params.reject{|k, v| self.safe_blank?(v)}
675
- params[:signature] = Cloudinary::Utils.api_sign_request(params, api_secret)
717
+ params[:signature] = api_sign_request(params, api_secret, signature_algorithm)
676
718
  params[:api_key] = api_key
677
719
  params
678
720
  end
@@ -1149,6 +1191,25 @@ class Cloudinary::Utils
1149
1191
  REMOTE_URL_REGEX === url
1150
1192
  end
1151
1193
 
1194
+ # The returned url should allow downloading the backedup asset based on the version and asset id
1195
+ #
1196
+ # asset and version id are returned with resource(<PUBLIC_ID1>, { versions: true })
1197
+ #
1198
+ # @param [String] asset_id Asset identifier
1199
+ # @param [String] version_id Specific version of asset to download
1200
+ # @param [Hash] options Additional options
1201
+ #
1202
+ # @return [String] An url for downloading a file
1203
+ def self.download_backedup_asset(asset_id, version_id, options = {})
1204
+ params = Cloudinary::Utils.sign_request({
1205
+ :timestamp => (options[:timestamp] || Time.now.to_i),
1206
+ :asset_id => asset_id,
1207
+ :version_id => version_id
1208
+ }, options)
1209
+
1210
+ "#{Cloudinary::Utils.base_api_url("download_backup", options)}?#{Cloudinary::Utils.hash_query_params((params))}"
1211
+ end
1212
+
1152
1213
  # Format date in a format accepted by the usage API (e.g., 31-12-2020) if
1153
1214
  # passed value is of type Date, otherwise return the string representation of
1154
1215
  # the input.
@@ -1163,23 +1224,18 @@ class Cloudinary::Utils
1163
1224
  end
1164
1225
  end
1165
1226
 
1166
- # Computes a short or long signature based on a message and secret
1167
- # @param [String] message The string to sign
1168
- # @param [String] secret A secret that will be added to the message when signing
1169
- # @param [Boolean] long_signature Whether to create a short or long signature
1170
- # @return [String] Properly formatted signature
1171
- def self.compute_signature(message, secret, long_url_signature)
1172
- combined_message_secret = message + secret
1173
-
1174
- algo, signature_length =
1175
- if long_url_signature
1176
- [Digest::SHA256, LONG_URL_SIGNATURE_LENGTH]
1177
- else
1178
- [Digest::SHA1, SHORT_URL_SIGNATURE_LENGTH]
1179
- end
1180
-
1181
- "s--#{Base64.urlsafe_encode64(algo.digest(combined_message_secret))[0, signature_length]}--"
1227
+ # Computes hash from input string using specified algorithm.
1228
+ #
1229
+ # @param [String] input String which to compute hash from
1230
+ # @param [String|nil] signature_algorithm Algorithm to use for computing hash
1231
+ # @param [Symbol] hash_method Hash method applied to a signature algorithm (:digest or :hexdigest)
1232
+ #
1233
+ # @return [String] Computed hash value
1234
+ def self.hash(input, signature_algorithm = nil, hash_method = :digest)
1235
+ signature_algorithm ||= Cloudinary.config.signature_algorithm || ALGO_SHA1
1236
+ algorithm = ALGORITHM_SIGNATURE[signature_algorithm] || raise("Unsupported algorithm '#{signature_algorithm}'")
1237
+ algorithm.public_send(hash_method, input)
1182
1238
  end
1183
1239
 
1184
- private_class_method :compute_signature
1240
+ private_class_method :hash
1185
1241
  end
@@ -1,4 +1,4 @@
1
1
  # Copyright Cloudinary
2
2
  module Cloudinary
3
- VERSION = "1.19.0"
3
+ VERSION = "1.20.0"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudinary
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.19.0
4
+ version: 1.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nadav Soferman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-03-05 00:00:00.000000000 Z
13
+ date: 2021-03-26 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws_cf_signer