google-cloud-storage 1.26.0 → 1.29.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.
@@ -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
@@ -63,7 +63,6 @@ module Google
63
63
  expires ||= 60*60*24
64
64
  p["expiration"] = (now + expires).strftime "%Y-%m-%dT%H:%M:%SZ"
65
65
 
66
-
67
66
  policy_str = escape_characters p.to_json
68
67
 
69
68
  policy = Base64.strict_encode64(policy_str).force_encoding "utf-8"
@@ -83,12 +82,13 @@ module Google
83
82
  client_email: nil,
84
83
  signing_key: nil,
85
84
  private_key: nil,
85
+ signer: nil,
86
86
  query: nil,
87
87
  scheme: "https",
88
88
  virtual_hosted_style: nil,
89
89
  bucket_bound_hostname: nil
90
90
  raise ArgumentError, "method is required" unless method
91
- 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
92
92
  datetime_now = Time.now.utc
93
93
  goog_date = datetime_now.strftime "%Y%m%dT%H%M%SZ"
94
94
  datestamp = datetime_now.strftime "%Y%m%d"
@@ -174,6 +174,8 @@ module Google
174
174
  def policy_conditions base_fields, user_conditions, user_fields
175
175
  # Convert each pair in base_fields hash to a single-entry hash in an array.
176
176
  conditions = base_fields.to_a.map { |f| Hash[*f] }
177
+ # Add the bucket to the head of the base_fields. This is not returned in the PostObject fields.
178
+ conditions.unshift "bucket" => @bucket_name
177
179
  # Add user-provided conditions to the head of the conditions array.
178
180
  conditions.unshift user_conditions if user_conditions && !user_conditions.empty?
179
181
  if user_fields
@@ -191,28 +193,40 @@ module Google
191
193
  def determine_issuer issuer, client_email
192
194
  # Parse the Service Account and get client id and private key
193
195
  issuer = issuer || client_email || @service.credentials.issuer
194
- raise SignedUrlUnavailable, "issuer (client_email) missing" unless issuer
196
+ raise SignedUrlUnavailable, error_msg("issuer (client_email)") unless issuer
195
197
  issuer
196
198
  end
197
199
 
198
- def determine_signing_key signing_key, private_key
199
- signing_key = signing_key || private_key || @service.credentials.signing_key
200
- 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
201
203
  signing_key
202
204
  end
203
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
+
204
211
  def service_account_signer signer
205
- signer = OpenSSL::PKey::RSA.new signer unless signer.respond_to? :sign
206
- # Sign string to sign
207
- lambda do |string_to_sign|
208
- sig = signer.sign OpenSSL::Digest::SHA256.new, string_to_sign
209
- 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
210
224
  end
211
225
  end
212
226
 
213
- def issuer_and_signer issuer, client_email, signing_key, private_key
227
+ def issuer_and_signer issuer, client_email, signing_key, private_key, signer
214
228
  issuer = determine_issuer issuer, client_email
215
- signing_key = determine_signing_key signing_key, private_key
229
+ signing_key = determine_signing_key signing_key, private_key, signer
216
230
  signer = service_account_signer signing_key
217
231
  [issuer, signer]
218
232
  end
@@ -259,7 +273,7 @@ module Google
259
273
  # Only the characters in the regex set [A-Za-z0-9.~_-] must be left un-escaped; all others must be
260
274
  # percent-encoded using %XX UTF-8 style.
261
275
  def escape_query_param str
262
- CGI.escape(str.to_s).gsub("%7E", "~")
276
+ CGI.escape(str.to_s).gsub("%7E", "~").gsub("+", "%20")
263
277
  end
264
278
 
265
279
  def host_name virtual_hosted_style, bucket_bound_hostname
@@ -336,11 +350,16 @@ module Google
336
350
  end
337
351
 
338
352
  def generate_signature signing_key, data
339
- unless signing_key.respond_to? :sign
340
- 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
341
361
  end
342
- signature = signing_key.sign OpenSSL::Digest::SHA256.new, data
343
- signature.unpack("H*").first.force_encoding "utf-8"
362
+ packed_signature.unpack("H*").first.force_encoding "utf-8"
344
363
  end
345
364
  end
346
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.0".freeze
19
+ VERSION = "1.29.0".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.0
4
+ version: 1.29.0
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-04-06 00:00:00.000000000 Z
12
+ date: 2020-09-22 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