cloudinary 2.2.0 โ†’ 2.3.1

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: 4f827756d17fc0b871d277a5bbdc6bc1ab3edeb30cf8765f29dd9fda17f6870d
4
- data.tar.gz: 91e533b452f021f65255e26caed2271d5a96387a5c5728f5268720c9a2306fb6
3
+ metadata.gz: c0ad3ba5b49a838e7d13426ba67a292f27831034fca177ce31e8858bd3a214f6
4
+ data.tar.gz: 91b672ddf6bec07bdddd0691e29b8e50d28d757627bbd22384c62afd0661a73a
5
5
  SHA512:
6
- metadata.gz: 837eb7160f55eed0b1df45d041c2a8deb18ccd0acd6ee405cf70f151ee7926bf5715bd9852f7da21ef393a28e8ad630d40770d47ed7b296c915a98dd5fb8f417
7
- data.tar.gz: a724d55a25e2b09a33d2c722021ad3c335a034e822961f86312916b6296902d1b65417ae0f630537fa10f603cf7ff28f98b291f648777d371d578524a0062ba9
6
+ metadata.gz: b2189e9c87739637af5a46b989d76ee14b7ccf024228a785fdc54bd875176ece44a3f72ad46659bf10ec1a86a7686faca19921b89faa4b7deaed47c616257457
7
+ data.tar.gz: 644dbad25b178cbc92928b9b1c2e7e51b4fcd0c23a2bd1d7a48f054db6c4fea640e8b998138fe914cbd7ea3ea8b06c28a449429c5b0d20a45e6d2662140e98b2
@@ -0,0 +1,42 @@
1
+ name: Ruby Test ๐Ÿ’Ž
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ test:
11
+ name: ๐Ÿ’Ž Test with Ruby ${{ matrix.ruby-version }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ ruby-version:
16
+ - '3.1.7'
17
+ - '3.2.8'
18
+ - '3.3.8'
19
+ - '3.4.4'
20
+
21
+ runs-on: ubuntu-22.04
22
+
23
+ steps:
24
+ - name: ๐Ÿ”„ Checkout Repository
25
+ uses: actions/checkout@v4
26
+
27
+ - name: ๐Ÿ› ๏ธ Setup Ruby ${{ matrix.ruby-version }}
28
+ uses: ruby/setup-ruby@v1
29
+ with:
30
+ ruby-version: ${{ matrix.ruby-version }}
31
+ bundler-cache: true
32
+
33
+ - name: โ˜๏ธ Set up test cloud
34
+ run: |
35
+ export CLOUDINARY_URL=$(bash tools/get_test_cloud.sh)
36
+ echo "CLOUDINARY_URL=$CLOUDINARY_URL" >> $GITHUB_ENV
37
+ echo "cloud_name: $(echo $CLOUDINARY_URL | cut -d'@' -f2)"
38
+
39
+ - name: ๐Ÿงช Run tests
40
+ run: bundle exec rspec --format documentation --color
41
+ env:
42
+ CLOUDINARY_URL: ${{ env.CLOUDINARY_URL }}
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ 2.3.1 / 2025-06-17
2
+ ==================
3
+
4
+ * Fix API parameters signature
5
+ * Allow passing tag attributes for `cloudinary_js_config` tag
6
+ * Fix `Cloudinary::Uploader.exists?` method
7
+
8
+ 2.3.0 / 2025-02-20
9
+ ==================
10
+
11
+ New functionality and features
12
+ ------------------------------
13
+
14
+ * Add support for `429 Too Many Requests` HTTP status code
15
+ * Add support for `allow_dynamic_list_values` parameter in `MetadataField`
16
+ * Add support for `config` Admin API
17
+
1
18
  2.2.0 / 2024-09-08
2
19
  ==================
3
20
 
@@ -14,6 +14,22 @@ class Cloudinary::Api
14
14
  call_api(:get, "ping", {}, options)
15
15
  end
16
16
 
17
+ # Retrieves account configuration details.
18
+ #
19
+ # @param [Hash] options The optional parameters.
20
+ #
21
+ # @return [Cloudinary::Api::Response]
22
+ #
23
+ # @raise [Cloudinary::Api::Error]
24
+ #
25
+ # @see https://cloudinary.com/documentation/admin_api#config
26
+ def self.config(options={})
27
+ uri = "config"
28
+ params = only(options, :settings)
29
+
30
+ call_api(:get, uri, params, options)
31
+ end
32
+
17
33
  # Gets cloud usage details.
18
34
  #
19
35
  # Returns a report detailing your current Cloudinary cloud usage details, including
@@ -1021,9 +1037,7 @@ class Cloudinary::Api
1021
1037
  #
1022
1038
  # @see https://cloudinary.com/documentation/admin_api#create_a_metadata_field
1023
1039
  def self.add_metadata_field(field, options = {})
1024
- params = only(field, :type, :external_id, :label, :mandatory, :default_value, :validation, :datasource)
1025
-
1026
- call_metadata_api(:post, [], params, options)
1040
+ call_metadata_api(:post, [], prepare_metadata_field_params(field), options)
1027
1041
  end
1028
1042
 
1029
1043
  # Updates a metadata field by external ID.
@@ -1040,10 +1054,19 @@ class Cloudinary::Api
1040
1054
  #
1041
1055
  # @see https://cloudinary.com/documentation/admin_api#update_a_metadata_field_by_external_id
1042
1056
  def self.update_metadata_field(field_external_id, field, options = {})
1043
- uri = [field_external_id]
1044
- params = only(field, :label, :mandatory, :default_value, :validation)
1057
+ uri = [field_external_id]
1045
1058
 
1046
- call_metadata_api(:put, uri, params, options)
1059
+ call_metadata_api(:put, uri, prepare_metadata_field_params(field), options)
1060
+ end
1061
+
1062
+ # Prepares optional parameters for add/update_metadata_field API calls.
1063
+ # @param [Hash] options Additional options
1064
+ # @return [Object] Optional parameters
1065
+ def self.prepare_metadata_field_params(field)
1066
+ only(field,
1067
+ :type, :external_id, :label, :mandatory, :restrictions, :default_value, :default_disabled,
1068
+ :validation, :datasource, :allow_dynamic_list_values
1069
+ )
1047
1070
  end
1048
1071
 
1049
1072
  # Deletes a metadata field definition by external ID.
@@ -57,7 +57,7 @@ module Cloudinary::BaseApi
57
57
  when 403 then NotAllowed
58
58
  when 404 then NotFound
59
59
  when 409 then AlreadyExists
60
- when 420 then RateLimited
60
+ when 420, 429 then RateLimited
61
61
  when 500 then GeneralError
62
62
  else raise GeneralError.new("Server returned unexpected status code - #{response.status} - #{response.body}")
63
63
  end
@@ -2,10 +2,12 @@ module Cloudinary::CloudinaryController
2
2
  protected
3
3
 
4
4
  def valid_cloudinary_response?
5
- received_signature = request.query_parameters[:signature]
6
- calculated_signature = Cloudinary::Utils.api_sign_request(
7
- request.query_parameters.select{|key, value| [:public_id, :version].include?(key.to_sym)},
8
- Cloudinary.config.api_secret)
9
- return received_signature == calculated_signature
5
+ params = request.query_parameters.select { |key, value| [:public_id, :version, :signature].include?(key.to_sym) }.transform_keys(&:to_sym)
6
+
7
+ Cloudinary::Utils.verify_api_response_signature(
8
+ params[:public_id],
9
+ params[:version],
10
+ params[:signature]
11
+ )
10
12
  end
11
13
  end
@@ -219,14 +219,19 @@ module CloudinaryHelper
219
219
  end
220
220
 
221
221
  CLOUDINARY_JS_CONFIG_PARAMS = [:api_key, :cloud_name, :private_cdn, :secure_distribution, :cdn_subdomain]
222
- def cloudinary_js_config
222
+ def cloudinary_js_config(**tag_options)
223
223
  params = {}
224
224
  CLOUDINARY_JS_CONFIG_PARAMS.each do
225
225
  |param|
226
226
  value = Cloudinary.config.send(param)
227
227
  params[param] = value if !value.nil?
228
228
  end
229
- content_tag("script", "$.cloudinary.config(#{params.to_json});".html_safe, :type=>"text/javascript")
229
+ content_tag(
230
+ "script",
231
+ "$.cloudinary.config(#{params.to_json});".html_safe,
232
+ :type=>"text/javascript",
233
+ **tag_options
234
+ )
230
235
  end
231
236
 
232
237
  def cl_client_hints_meta_tag
@@ -9,8 +9,7 @@ class Cloudinary::PreloadedFile
9
9
 
10
10
  def valid?
11
11
  public_id = @resource_type == "raw" ? self.filename : self.public_id
12
- expected_signature = Cloudinary::Utils.api_sign_request({:public_id=>public_id, :version=>version}, Cloudinary.config.api_secret)
13
- @signature == expected_signature
12
+ Cloudinary::Utils.verify_api_response_signature(public_id, version, @signature)
14
13
  end
15
14
 
16
15
  def identifier
@@ -150,12 +150,11 @@ class Cloudinary::Uploader
150
150
  def self.exists?(public_id, options={})
151
151
  cloudinary_url = Cloudinary::Utils.cloudinary_url(public_id, options)
152
152
  begin
153
- code = RestClient::Request.execute(:method => :head, :url => cloudinary_url, :timeout => 5).code
153
+ code = Faraday.new(request: { timeout: 5 }).head(cloudinary_url).status
154
154
  code >= 200 && code < 300
155
- rescue RestClient::ResourceNotFound
155
+ rescue Faraday::ResourceNotFound
156
156
  return false
157
157
  end
158
-
159
158
  end
160
159
 
161
160
  def self.explicit(public_id, options={})
@@ -361,7 +360,8 @@ class Cloudinary::Uploader
361
360
  api_key = options[:api_key] || Cloudinary.config.api_key || raise(CloudinaryException, "Must supply api_key")
362
361
  api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
363
362
  signature_algorithm = options[:signature_algorithm]
364
- params[:signature] = Cloudinary::Utils.api_sign_request(params.reject { |k, v| non_signable.include?(k) }, api_secret, signature_algorithm)
363
+ signature_version = options[:signature_version]
364
+ params[:signature] = Cloudinary::Utils.api_sign_request(params.reject { |k, v| non_signable.include?(k) }, api_secret, signature_algorithm, signature_version)
365
365
  params[:api_key] = api_key
366
366
  end
367
367
 
@@ -469,12 +469,50 @@ class Cloudinary::Utils
469
469
  end
470
470
  end
471
471
 
472
- def self.api_string_to_sign(params_to_sign)
473
- 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("&")
472
+ # Encodes a parameter for safe inclusion in URL query strings.
473
+ #
474
+ # Specifically replaces "&" characters with their percent-encoded equivalent "%26"
475
+ # to prevent them from being interpreted as parameter separators in URL query strings.
476
+ #
477
+ # @param [Object] value The parameter to encode
478
+ # @return [String] Encoded parameter
479
+ # @private
480
+ def self.encode_param(value)
481
+ value.to_s.gsub("&", "%26")
474
482
  end
475
483
 
476
- def self.api_sign_request(params_to_sign, api_secret, signature_algorithm = nil)
477
- to_sign = api_string_to_sign(params_to_sign)
484
+ # Generates a string to be signed for API requests
485
+ #
486
+ # @param [Hash] params_to_sign Parameters to include in the signature
487
+ # @param [Integer] signature_version Version of signature algorithm to use:
488
+ # - Version 1: Original behavior without parameter encoding
489
+ # - Version 2+ (default): Includes parameter encoding to prevent parameter smuggling
490
+ # @return [String] String to be signed
491
+ # @private
492
+ def self.api_string_to_sign(params_to_sign, signature_version = 2)
493
+ params_to_sign.map { |k, v| [k.to_s, v.is_a?(Array) ? v.join(",") : v] }
494
+ .reject { |k, v| v.nil? || v == "" }
495
+ .sort_by(&:first)
496
+ .map { |k, v|
497
+ param_string = "#{k}=#{v}"
498
+ signature_version >= 2 ? encode_param(param_string) : param_string
499
+ }
500
+ .join("&")
501
+ end
502
+
503
+ # Signs API request parameters
504
+ #
505
+ # @param [Hash] params_to_sign Parameters to include in the signature
506
+ # @param [String] api_secret API secret for signing
507
+ # @param [Symbol|nil] signature_algorithm Hash algorithm to use (:sha1 or :sha256)
508
+ # @param [Integer|nil] signature_version Version of signature algorithm to use:
509
+ # - Version 1: Original behavior without parameter encoding
510
+ # - Version 2+ (default): Includes parameter encoding to prevent parameter smuggling
511
+ # @return [String] Hexadecimal signature
512
+ # @private
513
+ def self.api_sign_request(params_to_sign, api_secret, signature_algorithm = nil, signature_version = nil)
514
+ signature_version ||= Cloudinary.config.signature_version || 2
515
+ to_sign = api_string_to_sign(params_to_sign, signature_version)
478
516
  hash("#{to_sign}#{api_secret}", signature_algorithm, :hexdigest)
479
517
  end
480
518
 
@@ -746,8 +784,9 @@ class Cloudinary::Utils
746
784
  api_key = options[:api_key] || Cloudinary.config.api_key || raise(CloudinaryException, "Must supply api_key")
747
785
  api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
748
786
  signature_algorithm = options[:signature_algorithm]
787
+ signature_version = options[:signature_version]
749
788
  params = params.reject{|k, v| self.safe_blank?(v)}
750
- params[:signature] = api_sign_request(params, api_secret, signature_algorithm)
789
+ params[:signature] = api_sign_request(params, api_secret, signature_algorithm, signature_version)
751
790
  params[:api_key] = api_key
752
791
  params
753
792
  end
@@ -1351,7 +1390,7 @@ class Cloudinary::Utils
1351
1390
  :version => version
1352
1391
  }
1353
1392
 
1354
- signature == api_sign_request(parameters_to_sign, api_secret, signature_algorithm)
1393
+ signature == api_sign_request(parameters_to_sign, api_secret, signature_algorithm, 1)
1355
1394
  end
1356
1395
 
1357
1396
  # Verifies the authenticity of a notification signature.
@@ -1,4 +1,4 @@
1
1
  # Copyright Cloudinary
2
2
  module Cloudinary
3
- VERSION = "2.2.0"
3
+ VERSION = "2.3.1"
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: 2.2.0
4
+ version: 2.3.1
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: 2024-09-08 00:00:00.000000000 Z
13
+ date: 2025-06-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: faraday
@@ -332,9 +332,9 @@ files:
332
332
  - ".github/ISSUE_TEMPLATE/bug_report.md"
333
333
  - ".github/ISSUE_TEMPLATE/feature_request.md"
334
334
  - ".github/pull_request_template.md"
335
+ - ".github/workflows/ci.yml"
335
336
  - ".gitignore"
336
337
  - ".rspec"
337
- - ".travis.yml"
338
338
  - CHANGELOG.md
339
339
  - CONTRIBUTING.md
340
340
  - Gemfile
data/.travis.yml DELETED
@@ -1,23 +0,0 @@
1
- dist: jammy
2
- language: ruby
3
- rvm:
4
- - 3.1.4
5
- - 3.2.2
6
- - 3.3.0
7
-
8
- matrix:
9
- include:
10
- # There is an OpenSSL issue on Jammy with Ruby 3.0
11
- - name: "Ruby: 3.0.6"
12
- dist: focal
13
- rvm: 3.0.6
14
-
15
- before_script: >
16
- export CLOUDINARY_URL=$(bash tools/get_test_cloud.sh);
17
- echo cloud_name: "$(echo $CLOUDINARY_URL | cut -d'@' -f2)"
18
- script: bundle exec rspec
19
-
20
- notifications:
21
- email:
22
- recipients:
23
- - sdk_developers@cloudinary.com