google-cloud-storage 1.26.2 → 1.29.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -77,13 +77,21 @@ module Google
77
77
  end
78
78
 
79
79
  def determine_signing_key options = {}
80
- options[:signing_key] || options[:private_key] ||
81
- @service.credentials.signing_key
80
+ signing_key = options[:signing_key] || options[:private_key] ||
81
+ options[:signer] || @service.credentials.signing_key
82
+ raise SignedUrlUnavailable, error_msg("signing_key (private_key, signer)") unless signing_key
83
+ signing_key
82
84
  end
83
85
 
84
86
  def determine_issuer options = {}
85
- options[:issuer] || options[:client_email] ||
86
- @service.credentials.issuer
87
+ issuer = options[:issuer] || options[:client_email] || @service.credentials.issuer
88
+ raise SignedUrlUnavailable, error_msg("issuer (client_email)") unless issuer
89
+ issuer
90
+ end
91
+
92
+ def error_msg attr_name
93
+ "Service account credentials '#{attr_name}' is missing. To generate service account credentials " \
94
+ "see https://cloud.google.com/iam/docs/service-accounts"
87
95
  end
88
96
 
89
97
  def post_object options
@@ -99,8 +107,6 @@ module Google
99
107
  i = determine_issuer options
100
108
  s = determine_signing_key options
101
109
 
102
- raise SignedUrlUnavailable unless i && s
103
-
104
110
  policy_str = p.to_json
105
111
  policy = Base64.strict_encode64(policy_str).delete "\n"
106
112
 
@@ -119,18 +125,21 @@ module Google
119
125
  i = determine_issuer options
120
126
  s = determine_signing_key options
121
127
 
122
- raise SignedUrlUnavailable unless i && s
123
-
124
128
  sig = generate_signature s, signature_str(options)
125
129
  generate_signed_url i, sig, options[:expires], options[:query]
126
130
  end
127
131
 
128
132
  def generate_signature signing_key, secret
129
- unless signing_key.respond_to? :sign
130
- signing_key = OpenSSL::PKey::RSA.new signing_key
133
+ unencoded_signature = ""
134
+ if signing_key.is_a? Proc
135
+ unencoded_signature = signing_key.call secret
136
+ else
137
+ unless signing_key.respond_to? :sign
138
+ signing_key = OpenSSL::PKey::RSA.new signing_key
139
+ end
140
+ unencoded_signature = signing_key.sign OpenSSL::Digest::SHA256.new, secret
131
141
  end
132
- signature = signing_key.sign OpenSSL::Digest::SHA256.new, secret
133
- Base64.strict_encode64(signature).delete "\n"
142
+ Base64.strict_encode64(unencoded_signature).delete "\n"
134
143
  end
135
144
 
136
145
  def generate_signed_url issuer, signed_string, expires, query
@@ -43,6 +43,7 @@ module Google
43
43
  client_email: nil,
44
44
  signing_key: nil,
45
45
  private_key: nil,
46
+ signer: nil,
46
47
  expires: nil,
47
48
  fields: nil,
48
49
  conditions: nil,
@@ -50,8 +51,7 @@ module Google
50
51
  virtual_hosted_style: nil,
51
52
  bucket_bound_hostname: nil
52
53
  i = determine_issuer issuer, client_email
53
- s = determine_signing_key signing_key, private_key
54
- raise SignedUrlUnavailable unless i && s
54
+ s = determine_signing_key signing_key, private_key, signer
55
55
 
56
56
  now = Time.now.utc
57
57
  base_fields = required_fields i, now
@@ -82,12 +82,13 @@ module Google
82
82
  client_email: nil,
83
83
  signing_key: nil,
84
84
  private_key: nil,
85
+ signer: nil,
85
86
  query: nil,
86
87
  scheme: "https",
87
88
  virtual_hosted_style: nil,
88
89
  bucket_bound_hostname: nil
89
90
  raise ArgumentError, "method is required" unless method
90
- issuer, signer = issuer_and_signer issuer, client_email, signing_key, private_key
91
+ issuer, signer = issuer_and_signer issuer, client_email, signing_key, private_key, signer
91
92
  datetime_now = Time.now.utc
92
93
  goog_date = datetime_now.strftime "%Y%m%dT%H%M%SZ"
93
94
  datestamp = datetime_now.strftime "%Y%m%d"
@@ -176,7 +177,7 @@ module Google
176
177
  # Add the bucket to the head of the base_fields. This is not returned in the PostObject fields.
177
178
  conditions.unshift "bucket" => @bucket_name
178
179
  # Add user-provided conditions to the head of the conditions array.
179
- conditions.unshift user_conditions if user_conditions && !user_conditions.empty?
180
+ conditions = user_conditions + conditions if user_conditions
180
181
  if user_fields
181
182
  # Convert each pair in fields hash to a single-entry hash and add it to the head of the conditions array.
182
183
  user_fields.to_a.reverse.each { |f| conditions.unshift Hash[*f] }
@@ -192,28 +193,40 @@ module Google
192
193
  def determine_issuer issuer, client_email
193
194
  # Parse the Service Account and get client id and private key
194
195
  issuer = issuer || client_email || @service.credentials.issuer
195
- raise SignedUrlUnavailable, "issuer (client_email) missing" unless issuer
196
+ raise SignedUrlUnavailable, error_msg("issuer (client_email)") unless issuer
196
197
  issuer
197
198
  end
198
199
 
199
- def determine_signing_key signing_key, private_key
200
- signing_key = signing_key || private_key || @service.credentials.signing_key
201
- raise SignedUrlUnavailable, "signing_key (private_key) missing" unless signing_key
200
+ def determine_signing_key signing_key, private_key, signer
201
+ signing_key = signing_key || private_key || signer || @service.credentials.signing_key
202
+ raise SignedUrlUnavailable, error_msg("signing_key (private_key, signer)") unless signing_key
202
203
  signing_key
203
204
  end
204
205
 
206
+ def error_msg attr_name
207
+ "Service account credentials '#{attr_name}' is missing. To generate service account credentials " \
208
+ "see https://cloud.google.com/iam/docs/service-accounts"
209
+ end
210
+
205
211
  def service_account_signer signer
206
- signer = OpenSSL::PKey::RSA.new signer unless signer.respond_to? :sign
207
- # Sign string to sign
208
- lambda do |string_to_sign|
209
- sig = signer.sign OpenSSL::Digest::SHA256.new, string_to_sign
210
- sig.unpack("H*").first
212
+ if signer.is_a? Proc
213
+ lambda do |string_to_sign|
214
+ sig = signer.call string_to_sign
215
+ sig.unpack("H*").first
216
+ end
217
+ else
218
+ signer = OpenSSL::PKey::RSA.new signer unless signer.respond_to? :sign
219
+ # Sign string to sign
220
+ lambda do |string_to_sign|
221
+ sig = signer.sign OpenSSL::Digest::SHA256.new, string_to_sign
222
+ sig.unpack("H*").first
223
+ end
211
224
  end
212
225
  end
213
226
 
214
- def issuer_and_signer issuer, client_email, signing_key, private_key
227
+ def issuer_and_signer issuer, client_email, signing_key, private_key, signer
215
228
  issuer = determine_issuer issuer, client_email
216
- signing_key = determine_signing_key signing_key, private_key
229
+ signing_key = determine_signing_key signing_key, private_key, signer
217
230
  signer = service_account_signer signing_key
218
231
  [issuer, signer]
219
232
  end
@@ -260,7 +273,7 @@ module Google
260
273
  # Only the characters in the regex set [A-Za-z0-9.~_-] must be left un-escaped; all others must be
261
274
  # percent-encoded using %XX UTF-8 style.
262
275
  def escape_query_param str
263
- CGI.escape(str.to_s).gsub("%7E", "~")
276
+ CGI.escape(str.to_s).gsub("%7E", "~").gsub "+", "%20"
264
277
  end
265
278
 
266
279
  def host_name virtual_hosted_style, bucket_bound_hostname
@@ -274,7 +287,7 @@ module Google
274
287
  path = []
275
288
  path << "/#{@bucket_name}" if path_style
276
289
  path << "/#{String(@file_name)}" if @file_name && !@file_name.empty?
277
- CGI.escape(path.join).gsub "%2F", "/"
290
+ CGI.escape(path.join).gsub("%2F", "/").gsub "+", "%20"
278
291
  end
279
292
 
280
293
  ##
@@ -337,11 +350,16 @@ module Google
337
350
  end
338
351
 
339
352
  def generate_signature signing_key, data
340
- unless signing_key.respond_to? :sign
341
- signing_key = OpenSSL::PKey::RSA.new signing_key
353
+ packed_signature = nil
354
+ if signing_key.is_a? Proc
355
+ packed_signature = signing_key.call data
356
+ else
357
+ unless signing_key.respond_to? :sign
358
+ signing_key = OpenSSL::PKey::RSA.new signing_key
359
+ end
360
+ packed_signature = signing_key.sign OpenSSL::Digest::SHA256.new, data
342
361
  end
343
- signature = signing_key.sign OpenSSL::Digest::SHA256.new, data
344
- signature.unpack("H*").first.force_encoding "utf-8"
362
+ packed_signature.unpack("H*").first.force_encoding "utf-8"
345
363
  end
346
364
  end
347
365
  end
@@ -483,7 +483,7 @@ module Google
483
483
  # A {SignedUrlUnavailable} is raised if the service account credentials
484
484
  # are missing. Service account credentials are acquired by following the
485
485
  # steps in [Service Account Authentication](
486
- # https://cloud.google.com/storage/docs/authentication#service_accounts).
486
+ # https://cloud.google.com/iam/docs/service-accounts).
487
487
  #
488
488
  # @see https://cloud.google.com/storage/docs/access-control/signed-urls
489
489
  # Signed URLs guide
@@ -511,10 +511,22 @@ module Google
511
511
  # use the signed URL.
512
512
  # @param [String] issuer Service Account's Client Email.
513
513
  # @param [String] client_email Service Account's Client Email.
514
- # @param [OpenSSL::PKey::RSA, String] signing_key Service Account's
515
- # Private Key.
516
- # @param [OpenSSL::PKey::RSA, String] private_key Service Account's
517
- # Private Key.
514
+ # @param [OpenSSL::PKey::RSA, String, Proc] signing_key Service Account's
515
+ # Private Key or a Proc that accepts a single String parameter and returns a
516
+ # RSA SHA256 signature using a valid Google Service Account Private Key.
517
+ # @param [OpenSSL::PKey::RSA, String, Proc] private_key Service Account's
518
+ # Private Key or a Proc that accepts a single String parameter and returns a
519
+ # RSA SHA256 signature using a valid Google Service Account Private Key.
520
+ # @param [OpenSSL::PKey::RSA, String, Proc] signer Service Account's
521
+ # Private Key or a Proc that accepts a single String parameter and returns a
522
+ # RSA SHA256 signature using a valid Google Service Account Private Key.
523
+ #
524
+ # When using this method in environments such as GAE Flexible Environment,
525
+ # GKE, or Cloud Functions where the private key is unavailable, it may be
526
+ # necessary to provide a Proc (or lambda) via the signer parameter. This
527
+ # Proc should return a signature created using a RPC call to the
528
+ # [Service Account Credentials signBlob](https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob)
529
+ # method as shown in the example below.
518
530
  # @param [Hash] query Query string parameters to include in the signed
519
531
  # URL. The given parameters are not verified by the signature.
520
532
  #
@@ -540,7 +552,12 @@ module Google
540
552
  # to create. Must be one of `:v2` or `:v4`. The default value is
541
553
  # `:v2`.
542
554
  #
543
- # @return [String]
555
+ # @return [String] The signed URL.
556
+ #
557
+ # @raise [SignedUrlUnavailable] If the service account credentials
558
+ # are missing. Service account credentials are acquired by following the
559
+ # steps in [Service Account Authentication](
560
+ # https://cloud.google.com/iam/docs/service-accounts).
544
561
  #
545
562
  # @example
546
563
  # require "google/cloud/storage"
@@ -575,6 +592,41 @@ module Google
575
592
  # issuer: issuer_email,
576
593
  # signing_key: key
577
594
  #
595
+ # @example Using Cloud IAMCredentials signBlob to create the signature:
596
+ # require "google/cloud/storage"
597
+ # require "google/apis/iamcredentials_v1"
598
+ # require "googleauth"
599
+ #
600
+ # # Issuer is the service account email that the Signed URL will be signed with
601
+ # # and any permission granted in the Signed URL must be granted to the
602
+ # # Google Service Account.
603
+ # issuer = "service-account@project-id.iam.gserviceaccount.com"
604
+ #
605
+ # # Create a lambda that accepts the string_to_sign
606
+ # signer = lambda do |string_to_sign|
607
+ # IAMCredentials = Google::Apis::IamcredentialsV1
608
+ # iam_client = IAMCredentials::IAMCredentialsService.new
609
+ #
610
+ # # Get the environment configured authorization
611
+ # scopes = ["https://www.googleapis.com/auth/iam"]
612
+ # iam_client.authorization = Google::Auth.get_application_default scopes
613
+ #
614
+ # request = {
615
+ # "payload": string_to_sign,
616
+ # }
617
+ # resource = "projects/-/serviceAccounts/#{issuer}"
618
+ # response = iam_client.sign_service_account_blob resource, request, {}
619
+ # response.signed_blob
620
+ # end
621
+ #
622
+ # storage = Google::Cloud::Storage.new
623
+ #
624
+ # bucket_name = "my-todo-app"
625
+ # file_path = "avatars/heidi/400x400.png"
626
+ # url = storage.signed_url bucket_name, file_path,
627
+ # method: "GET", issuer: issuer,
628
+ # signer: signer
629
+ #
578
630
  # @example Using the `headers` option:
579
631
  # require "google/cloud/storage"
580
632
  #
@@ -616,6 +668,7 @@ module Google
616
668
  client_email: nil,
617
669
  signing_key: nil,
618
670
  private_key: nil,
671
+ signer: nil,
619
672
  query: nil,
620
673
  scheme: "HTTPS",
621
674
  virtual_hosted_style: nil,
@@ -624,31 +677,32 @@ module Google
624
677
  version ||= :v2
625
678
  case version.to_sym
626
679
  when :v2
627
- signer = File::SignerV2.new bucket, path, service
628
-
629
- signer.signed_url method: method,
630
- expires: expires,
631
- headers: headers,
632
- content_type: content_type,
633
- content_md5: content_md5,
634
- issuer: issuer,
635
- client_email: client_email,
636
- signing_key: signing_key,
637
- private_key: private_key,
638
- query: query
680
+ sign = File::SignerV2.new bucket, path, service
681
+ sign.signed_url method: method,
682
+ expires: expires,
683
+ headers: headers,
684
+ content_type: content_type,
685
+ content_md5: content_md5,
686
+ issuer: issuer,
687
+ client_email: client_email,
688
+ signing_key: signing_key,
689
+ private_key: private_key,
690
+ signer: signer,
691
+ query: query
639
692
  when :v4
640
- signer = File::SignerV4.new bucket, path, service
641
- signer.signed_url method: method,
642
- expires: expires,
643
- headers: headers,
644
- issuer: issuer,
645
- client_email: client_email,
646
- signing_key: signing_key,
647
- private_key: private_key,
648
- query: query,
649
- scheme: scheme,
650
- virtual_hosted_style: virtual_hosted_style,
651
- bucket_bound_hostname: bucket_bound_hostname
693
+ sign = File::SignerV4.new bucket, path, service
694
+ sign.signed_url method: method,
695
+ expires: expires,
696
+ headers: headers,
697
+ issuer: issuer,
698
+ client_email: client_email,
699
+ signing_key: signing_key,
700
+ private_key: private_key,
701
+ signer: signer,
702
+ query: query,
703
+ scheme: scheme,
704
+ virtual_hosted_style: virtual_hosted_style,
705
+ bucket_bound_hostname: bucket_bound_hostname
652
706
  else
653
707
  raise ArgumentError, "version '#{version}' not supported"
654
708
  end
@@ -39,7 +39,7 @@ module Google
39
39
  ##
40
40
  # Creates a new Service instance.
41
41
  def initialize project, credentials,
42
- retries: nil, timeout: nil, host: nil
42
+ retries: nil, timeout: nil, host: nil, quota_project: nil
43
43
  @project = project
44
44
  @credentials = credentials
45
45
  @service = API::StorageService.new
@@ -55,6 +55,7 @@ module Google
55
55
  @service.request_options.header["x-goog-api-client"] = \
56
56
  "gl-ruby/#{RUBY_VERSION} gccl/#{Google::Cloud::Storage::VERSION}"
57
57
  @service.request_options.header["Accept-Encoding"] = "gzip"
58
+ @service.request_options.quota_project = quota_project if quota_project
58
59
  @service.authorization = @credentials.client if @credentials
59
60
  @service.root_url = host if host
60
61
  end
@@ -292,12 +293,12 @@ module Google
292
293
  def insert_file bucket_name, source, path = nil, acl: nil,
293
294
  cache_control: nil, content_disposition: nil,
294
295
  content_encoding: nil, content_language: nil,
295
- content_type: nil, crc32c: nil, md5: nil, metadata: nil,
296
+ content_type: nil, custom_time: nil, crc32c: nil, md5: nil, metadata: nil,
296
297
  storage_class: nil, key: nil, kms_key: nil,
297
298
  temporary_hold: nil, event_based_hold: nil,
298
299
  user_project: nil
299
300
  params =
300
- { cache_control: cache_control, content_type: content_type,
301
+ { cache_control: cache_control, content_type: content_type, custom_time: custom_time,
301
302
  content_disposition: content_disposition, md5_hash: md5,
302
303
  content_encoding: content_encoding, crc32c: crc32c,
303
304
  content_language: content_language, metadata: metadata,
@@ -16,7 +16,7 @@
16
16
  module Google
17
17
  module Cloud
18
18
  module Storage
19
- VERSION = "1.26.2".freeze
19
+ VERSION = "1.29.2".freeze
20
20
  end
21
21
  end
22
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google-cloud-storage
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.26.2
4
+ version: 1.29.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Moore
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-05-28 00:00:00.000000000 Z
12
+ date: 2020-12-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: google-cloud-core
@@ -298,7 +298,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
298
298
  - !ruby/object:Gem::Version
299
299
  version: '0'
300
300
  requirements: []
301
- rubygems_version: 3.0.6
301
+ rubygems_version: 3.1.4
302
302
  signing_key:
303
303
  specification_version: 4
304
304
  summary: API Client library for Google Cloud Storage