cloudinary 1.19.0 → 1.20.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 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