azure-storage-common 1.1.0 → 2.0.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.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/lib/azure/core.rb +47 -0
  3. data/lib/azure/core/auth/authorizer.rb +36 -0
  4. data/lib/azure/core/auth/shared_key.rb +125 -0
  5. data/lib/azure/core/auth/shared_key_lite.rb +48 -0
  6. data/lib/azure/core/auth/signer.rb +51 -0
  7. data/lib/azure/core/default.rb +23 -0
  8. data/lib/azure/core/error.rb +21 -0
  9. data/lib/azure/core/filtered_service.rb +45 -0
  10. data/lib/azure/core/http/debug_filter.rb +36 -0
  11. data/lib/azure/core/http/http_error.rb +135 -0
  12. data/lib/azure/core/http/http_filter.rb +53 -0
  13. data/lib/azure/core/http/http_request.rb +194 -0
  14. data/lib/azure/core/http/http_response.rb +102 -0
  15. data/lib/azure/core/http/retry_policy.rb +84 -0
  16. data/lib/azure/core/http/signer_filter.rb +33 -0
  17. data/lib/azure/core/service.rb +46 -0
  18. data/lib/azure/core/signed_service.rb +45 -0
  19. data/lib/azure/core/utility.rb +244 -0
  20. data/lib/azure/core/version.rb +33 -0
  21. data/lib/azure/http_response_helper.rb +38 -0
  22. data/lib/azure/storage/common.rb +26 -26
  23. data/lib/azure/storage/common/autoload.rb +62 -61
  24. data/lib/azure/storage/common/client.rb +162 -162
  25. data/lib/azure/storage/common/client_options.rb +363 -363
  26. data/lib/azure/storage/common/client_options_error.rb +41 -41
  27. data/lib/azure/storage/common/configurable.rb +212 -212
  28. data/lib/azure/storage/common/core.rb +35 -35
  29. data/lib/azure/storage/common/core/auth/anonymous_signer.rb +43 -43
  30. data/lib/azure/storage/common/core/auth/shared_access_signature.rb +30 -30
  31. data/lib/azure/storage/common/core/auth/shared_access_signature_generator.rb +399 -352
  32. data/lib/azure/storage/common/core/auth/shared_access_signature_signer.rb +57 -57
  33. data/lib/azure/storage/common/core/auth/shared_key.rb +60 -60
  34. data/lib/azure/storage/common/core/auth/token_signer.rb +43 -43
  35. data/lib/azure/storage/common/core/autoload.rb +53 -53
  36. data/lib/azure/storage/common/core/error.rb +43 -43
  37. data/lib/azure/storage/common/core/filter/exponential_retry_filter.rb +64 -64
  38. data/lib/azure/storage/common/core/filter/linear_retry_filter.rb +55 -55
  39. data/lib/azure/storage/common/core/filter/retry_filter.rb +300 -300
  40. data/lib/azure/storage/common/core/http_client.rb +79 -69
  41. data/lib/azure/storage/common/core/sr.rb +85 -85
  42. data/lib/azure/storage/common/core/token_credential.rb +64 -64
  43. data/lib/azure/storage/common/core/utility.rb +255 -255
  44. data/lib/azure/storage/common/default.rb +868 -868
  45. data/lib/azure/storage/common/service/access_policy.rb +37 -37
  46. data/lib/azure/storage/common/service/cors.rb +38 -38
  47. data/lib/azure/storage/common/service/cors_rule.rb +48 -48
  48. data/lib/azure/storage/common/service/enumeration_results.rb +32 -32
  49. data/lib/azure/storage/common/service/geo_replication.rb +40 -40
  50. data/lib/azure/storage/common/service/logging.rb +47 -47
  51. data/lib/azure/storage/common/service/metrics.rb +45 -45
  52. data/lib/azure/storage/common/service/retention_policy.rb +37 -37
  53. data/lib/azure/storage/common/service/serialization.rb +335 -335
  54. data/lib/azure/storage/common/service/signed_identifier.rb +40 -40
  55. data/lib/azure/storage/common/service/storage_service.rb +322 -322
  56. data/lib/azure/storage/common/service/storage_service_properties.rb +48 -48
  57. data/lib/azure/storage/common/service/storage_service_stats.rb +39 -39
  58. data/lib/azure/storage/common/service/user_delegation_key.rb +50 -0
  59. data/lib/azure/storage/common/version.rb +49 -49
  60. metadata +60 -18
@@ -1,363 +1,363 @@
1
- # frozen_string_literal: true
2
-
3
- #-------------------------------------------------------------------------
4
- # # Copyright (c) Microsoft and contributors. All rights reserved.
5
- #
6
- # The MIT License(MIT)
7
-
8
- # Permission is hereby granted, free of charge, to any person obtaining a copy
9
- # of this software and associated documentation files(the "Software"), to deal
10
- # in the Software without restriction, including without limitation the rights
11
- # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12
- # copies of the Software, and to permit persons to whom the Software is
13
- # furnished to do so, subject to the following conditions :
14
-
15
- # The above copyright notice and this permission notice shall be included in
16
- # all copies or substantial portions of the Software.
17
-
18
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
- # THE SOFTWARE.
25
- #--------------------------------------------------------------------------
26
-
27
- require "uri"
28
- require "azure/storage/common/client_options_error"
29
- require "azure/storage/common/core/auth/anonymous_signer"
30
-
31
- module Azure::Storage::Common
32
- module ClientOptions
33
- attr_accessor :ca_file, :ssl_version, :ssl_min_version, :ssl_max_version
34
-
35
- # Public: Reset options for [Azure::Storage::Common::Client]
36
- #
37
- # ==== Attributes
38
- #
39
- # * +options+ - Hash | String. Optional parameters or storage connection string.
40
- #
41
- # ==== Options
42
- #
43
- # Accepted key/value pairs in options parameter are:
44
- #
45
- # * +:use_development_storage+ - TrueClass|FalseClass. Whether to use storage emulator.
46
- # * +:development_storage_proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
47
- # * +:storage_connection_string+ - String. The storage connection string.
48
- # * +:storage_account_name+ - String. The name of the storage account.
49
- # * +:storage_access_key+ - Base64 String. The access key of the storage account.
50
- # * +:storage_sas_token+ - String. The signed access signature for the storage account or one of its service.
51
- # * +:storage_blob_host+ - String. Specified Blob serivce endpoint or hostname
52
- # * +:storage_table_host+ - String. Specified Table serivce endpoint or hostname
53
- # * +:storage_queue_host+ - String. Specified Queue serivce endpoint or hostname
54
- # * +:storage_dns_suffix+ - String. The suffix of a regional Storage Serivce, to
55
- # * +:default_endpoints_protocol+ - String. http or https
56
- # * +:use_path_style_uri+ - String. Whether use path style URI for specified endpoints
57
- # * +:ca_file+ - String. File path of the CA file if having issue with SSL
58
- # * +:ssl_version+ - Symbol. The ssl version to be used, sample: :TLSv1_1, :TLSv1_2, for the details, see https://github.com/ruby/openssl/blob/master/lib/openssl/ssl.rb
59
- # * +:ssl_min_version+ - Symbol. The min ssl version supported, only supported in Ruby 2.5+
60
- # * +:ssl_max_version+ - Symbol. The max ssl version supported, only supported in Ruby 2.5+
61
- #
62
- # The valid set of options include:
63
- # * Storage Emulator: +:use_development_storage+ required, +:development_storage_proxy_uri+ optionally
64
- # * Storage account name and key: +:storage_account_name+ and +:storage_access_key+ required, set +:storage_dns_suffix+ necessarily
65
- # * Storage account name and SAS token: +:storage_account_name+ and +:storage_sas_token+ required, set +:storage_dns_suffix+ necessarily
66
- # * Specified hosts and SAS token: At least one of the service host and SAS token. It's up to user to ensure the SAS token is suitable for the serivce
67
- # * Anonymous Blob: only +:storage_blob_host+, if it is to only access blobs within a container
68
- #
69
- # Additional notes:
70
- # * Specified hosts can be set when use account name with access key or sas token
71
- # * +:default_endpoints_protocol+ can be set if the scheme is not specified in hosts
72
- # * Storage emulator always use path style URI
73
- #
74
- # When empty options are given, it will try to read settings from Environment Variables. Refer to [Azure::Storage::Common:ClientOptions.env_vars_mapping] for the mapping relationship
75
- #
76
- # @return [Azure::Storage::Common::Client]
77
- def reset!(options = {})
78
- if options.is_a? String
79
- options = parse_connection_string(options)
80
- elsif options.is_a? Hash
81
- # When the options are provided via singleton setup: Azure::Storage.setup()
82
- options = setup_options if options.length == 0
83
-
84
- options = parse_connection_string(options[:storage_connection_string]) if options[:storage_connection_string]
85
- end
86
-
87
- # Load from environment when no valid input
88
- options = load_env if options.length == 0
89
-
90
- @ca_file = options.delete(:ca_file)
91
- @ssl_version = options.delete(:ssl_version)
92
- @ssl_min_version = options.delete(:ssl_min_version)
93
- @ssl_max_version = options.delete(:ssl_max_version)
94
- @options = filter(options)
95
- self.send(:reset_config!, @options) if self.respond_to?(:reset_config!)
96
- self
97
- end
98
-
99
- # Check if this client is configured with the same options
100
- def same_options?(opts)
101
- opts.length == 0 || opts.hash == options.hash
102
- end
103
-
104
- # The options after validated and normalized
105
- #
106
- # @return [Hash]
107
- def options
108
- @options ||= {}
109
- end
110
-
111
- # The valid options for the storage client
112
- #
113
- # @return [Array]
114
- def self.valid_options
115
- @valid_options ||= [
116
- :use_development_storage,
117
- :development_storage_proxy_uri,
118
- :storage_account_name,
119
- :storage_access_key,
120
- :storage_connection_string,
121
- :storage_sas_token,
122
- :storage_blob_host,
123
- :storage_table_host,
124
- :storage_queue_host,
125
- :storage_file_host,
126
- :storage_dns_suffix,
127
- :default_endpoints_protocol,
128
- :use_path_style_uri
129
- ]
130
- end
131
-
132
- # The mapping between Storage Environment Variables and the options name
133
- #
134
- # @return [Hash]
135
- def self.env_vars_mapping
136
- @env_vars_mapping ||= {
137
- "EMULATED" => :use_development_storage,
138
- "AZURE_STORAGE_ACCOUNT" => :storage_account_name,
139
- "AZURE_STORAGE_ACCESS_KEY" => :storage_access_key,
140
- "AZURE_STORAGE_CONNECTION_STRING" => :storage_connection_string,
141
- "AZURE_STORAGE_BLOB_HOST" => :storage_blob_host,
142
- "AZURE_STORAGE_TABLE_HOST" => :storage_table_host,
143
- "AZURE_STORAGE_QUEUE_HOST" => :storage_queue_host,
144
- "AZURE_STORAGE_FILE_HOST" => :storage_file_host,
145
- "AZURE_STORAGE_SAS_TOKEN" => :storage_sas_token,
146
- "AZURE_STORAGE_DNS_SUFFIX" => :storage_dns_suffix
147
- }
148
- end
149
-
150
- # The mapping between Storage Connection String items and the options name
151
- #
152
- # @return [Hash]
153
- def self.connection_string_mapping
154
- @connection_string_mapping ||= {
155
- "UseDevelopmentStorage" => :use_development_storage,
156
- "DevelopmentStorageProxyUri" => :development_storage_proxy_uri,
157
- "DefaultEndpointsProtocol" => :default_endpoints_protocol,
158
- "AccountName" => :storage_account_name,
159
- "AccountKey" => :storage_access_key,
160
- "BlobEndpoint" => :storage_blob_host,
161
- "TableEndpoint" => :storage_table_host,
162
- "QueueEndpoint" => :storage_queue_host,
163
- "FileEndpoint" => :storage_file_host,
164
- "SharedAccessSignature" => :storage_sas_token,
165
- "EndpointSuffix" => :storage_dns_suffix
166
- }
167
- end
168
-
169
- private
170
-
171
- def method_missing(method_name, *args, &block)
172
- return super unless options.key? method_name
173
- options[method_name]
174
- end
175
-
176
- def filter(opts = {})
177
- results = {}
178
-
179
- # P1 - develpoment storage
180
- begin
181
- results = validated_options(opts,
182
- required: [:use_development_storage],
183
- optional: [:development_storage_proxy_uri])
184
- results[:use_development_storage] = true
185
- proxy_uri = results[:development_storage_proxy_uri] ||= StorageServiceClientConstants::DEV_STORE_URI
186
- results.merge!(storage_account_name: StorageServiceClientConstants::DEVSTORE_STORAGE_ACCOUNT,
187
- storage_access_key: StorageServiceClientConstants::DEVSTORE_STORAGE_ACCESS_KEY,
188
- storage_blob_host: "#{proxy_uri}:#{StorageServiceClientConstants::DEVSTORE_BLOB_HOST_PORT}",
189
- storage_table_host: "#{proxy_uri}:#{StorageServiceClientConstants::DEVSTORE_TABLE_HOST_PORT}",
190
- storage_queue_host: "#{proxy_uri}:#{StorageServiceClientConstants::DEVSTORE_QUEUE_HOST_PORT}",
191
- storage_file_host: "#{proxy_uri}:#{StorageServiceClientConstants::DEVSTORE_FILE_HOST_PORT}",
192
- use_path_style_uri: true)
193
- return results
194
- rescue InvalidOptionsError => e
195
- end
196
-
197
- # P2 - explicit hosts with account connection string
198
- begin
199
- results = validated_options(opts,
200
- required: [:storage_connection_string],
201
- optional: [:use_path_style_uri])
202
- results[:use_path_style_uri] = results.key?(:use_path_style_uri)
203
- normalize_hosts(results)
204
- return results
205
- rescue InvalidOptionsError => e
206
- end
207
-
208
- # P3 - account name and key or sas with default hosts or an end suffix
209
- begin
210
- results = validated_options(opts,
211
- required: [:storage_account_name],
212
- only_one: [:storage_access_key, :storage_sas_token, :signer],
213
- optional: [:default_endpoints_protocol, :storage_dns_suffix])
214
- protocol = results[:default_endpoints_protocol] ||= StorageServiceClientConstants::DEFAULT_PROTOCOL
215
- suffix = results[:storage_dns_suffix] ||= StorageServiceClientConstants::DEFAULT_ENDPOINT_SUFFIX
216
- account = results[:storage_account_name]
217
- results.merge!(storage_blob_host: "#{protocol}://#{account}.#{ServiceType::BLOB}.#{suffix}",
218
- storage_table_host: "#{protocol}://#{account}.#{ServiceType::TABLE}.#{suffix}",
219
- storage_queue_host: "#{protocol}://#{account}.#{ServiceType::QUEUE}.#{suffix}",
220
- storage_file_host: "#{protocol}://#{account}.#{ServiceType::FILE}.#{suffix}",
221
- use_path_style_uri: false)
222
- return results
223
- rescue InvalidOptionsError => e
224
- end
225
-
226
- # P4 - explicit hosts with account name and key
227
- begin
228
- results = validated_options(opts,
229
- required: [:storage_account_name, :storage_access_key],
230
- at_least_one: [:storage_blob_host, :storage_table_host, :storage_file_host, :storage_queue_host],
231
- optional: [:use_path_style_uri, :default_endpoints_protocol])
232
- results[:use_path_style_uri] = results.key?(:use_path_style_uri)
233
- normalize_hosts(results)
234
- return results
235
- rescue InvalidOptionsError => e
236
- end
237
-
238
- # P5 - anonymous or sas only for one or more particular services, options with account name/key + hosts should be already validated in P4
239
- begin
240
- results = validated_options(opts,
241
- at_least_one: [:storage_blob_host, :storage_table_host, :storage_file_host, :storage_queue_host],
242
- optional: [:use_path_style_uri, :default_endpoints_protocol, :storage_sas_token])
243
- results[:use_path_style_uri] = results.key?(:use_path_style_uri)
244
- normalize_hosts(results)
245
- # Adds anonymous signer if no sas token
246
- results[:signer] = Azure::Storage::Common::Core::Auth::AnonymousSigner.new unless results.key?(:storage_sas_token)
247
- return results
248
- rescue InvalidOptionsError => e
249
- end
250
-
251
- # P6 - account name and key or sas with explicit hosts
252
- begin
253
- results = validated_options(opts,
254
- required: [:storage_account_name],
255
- only_one: [:storage_access_key, :storage_sas_token],
256
- at_least_one: [:storage_blob_host, :storage_table_host, :storage_file_host, :storage_queue_host])
257
- results[:use_path_style_uri] = results.key?(:use_path_style_uri)
258
- normalize_hosts(results)
259
- return results
260
- rescue InvalidOptionsError => e
261
- end
262
-
263
- raise InvalidOptionsError, "options provided are not valid set: #{opts}" # wrong opts if move to this line
264
- end
265
-
266
- def normalize_hosts(options)
267
- if options[:default_endpoints_protocol]
268
- [:storage_blob_host, :storage_table_host, :storage_file_host, :storage_queue_host].each do |k|
269
- if options[k]
270
- raise InvalidOptionsError, "Explict host cannot contain scheme if default_endpoints_protocol is set." if options[k] =~ /^https?/
271
- options[k] = "#{options[:default_endpoints_protocol]}://#{options[k]}"
272
- end
273
- end
274
- end
275
- end
276
-
277
- def is_base64_encoded
278
- Proc.new do |i|
279
- i.is_a?(String) && i =~ /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$/
280
- end
281
- end
282
-
283
- def is_url
284
- Proc.new do |i|
285
- i = "http://" + i unless i =~ /\Ahttps?:\/\//
286
- i =~ URI.regexp(["http", "https"])
287
- end
288
- end
289
-
290
- def is_true
291
- Proc.new { |i| i == true || (i.is_a?(String) && i.downcase == "true") }
292
- end
293
-
294
- def is_non_empty_string
295
- Proc.new { |i| i && i.is_a?(String) && i.strip.length }
296
- end
297
-
298
- def validated_options(opts, requirements = {})
299
- raise InvalidOptionsError, 'nil is not allowed for option\'s value' if opts.values.any? { |v| v == nil }
300
- required = requirements[:required] || []
301
- at_least_one = requirements[:at_least_one] || []
302
- only_one = requirements[:only_one] || []
303
- optional = requirements[:optional] || []
304
-
305
- raise InvalidOptionsError, "Not all required keys are provided: #{required}" if required.any? { |k| !opts.key? k }
306
- raise InvalidOptionsError, "Only one of #{only_one} is required" unless only_one.length == 0 || only_one.count { |k| opts.key? k } == 1
307
- raise InvalidOptionsError, "At least one of #{at_least_one} is required" unless at_least_one.length == 0 || at_least_one.any? { |k| opts.key? k }
308
-
309
- @@option_validators ||= {
310
- use_development_storage: is_true,
311
- development_storage_proxy_uri: is_url,
312
- storage_account_name: lambda { |i| i.is_a?(String) },
313
- storage_access_key: is_base64_encoded,
314
- storage_sas_token: lambda { |i| i.is_a?(String) },
315
- storage_blob_host: is_url,
316
- storage_table_host: is_url,
317
- storage_queue_host: is_url,
318
- storage_file_host: is_url,
319
- storage_dns_suffix: is_url,
320
- default_endpoints_protocol: lambda { |i| ["http", "https"].include? i.downcase },
321
- use_path_style_uri: is_true,
322
- signer: lambda { |i| i.is_a? Azure::Core::Auth::Signer}
323
- }
324
-
325
- valid_options = required + at_least_one + only_one + optional
326
- results = {}
327
-
328
- opts.each do |k, v|
329
- raise InvalidOptionsError, "#{k} is not included in valid options" unless valid_options.length == 0 || valid_options.include?(k)
330
- unless @@option_validators.key?(k) && @@option_validators[k].call(v)
331
- raise InvalidOptionsError, "#{k} is invalid"
332
- end
333
- results[k] = v
334
- end
335
- results
336
- end
337
-
338
- def load_env
339
- cs = ENV["AZURE_STORAGE_CONNECTION_STRING"]
340
- return parse_connection_string(cs) if cs
341
-
342
- opts = {}
343
- ClientOptions.env_vars_mapping.each { |k, v| opts[v] = ENV[k] if ENV[k] }
344
- opts
345
- end
346
-
347
- def parse_connection_string(connection_string)
348
- opts = {}
349
- connection_string.split(";").each do |i|
350
- e = i.index("=")
351
- raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING if e < 0 || e == i.length - 1
352
- key, value = i[0..e - 1], i[e + 1..i.length - 1]
353
- raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING_BAD_KEY % key unless ClientOptions.connection_string_mapping.key? key
354
- raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING_EMPTY_KEY % key if value.length == 0
355
- raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING_DUPLICATE_KEY % key if opts.key? key
356
- opts[ClientOptions.connection_string_mapping[key]] = value
357
- end
358
- raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING if opts.length == 0
359
-
360
- opts
361
- end
362
- end
363
- end
1
+ # frozen_string_literal: true
2
+
3
+ #-------------------------------------------------------------------------
4
+ # # Copyright (c) Microsoft and contributors. All rights reserved.
5
+ #
6
+ # The MIT License(MIT)
7
+
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files(the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions :
14
+
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ # THE SOFTWARE.
25
+ #--------------------------------------------------------------------------
26
+
27
+ require "uri"
28
+ require "azure/storage/common/client_options_error"
29
+ require "azure/storage/common/core/auth/anonymous_signer"
30
+
31
+ module Azure::Storage::Common
32
+ module ClientOptions
33
+ attr_accessor :ca_file, :ssl_version, :ssl_min_version, :ssl_max_version
34
+
35
+ # Public: Reset options for [Azure::Storage::Common::Client]
36
+ #
37
+ # ==== Attributes
38
+ #
39
+ # * +options+ - Hash | String. Optional parameters or storage connection string.
40
+ #
41
+ # ==== Options
42
+ #
43
+ # Accepted key/value pairs in options parameter are:
44
+ #
45
+ # * +:use_development_storage+ - TrueClass|FalseClass. Whether to use storage emulator.
46
+ # * +:development_storage_proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
47
+ # * +:storage_connection_string+ - String. The storage connection string.
48
+ # * +:storage_account_name+ - String. The name of the storage account.
49
+ # * +:storage_access_key+ - Base64 String. The access key of the storage account.
50
+ # * +:storage_sas_token+ - String. The signed access signature for the storage account or one of its service.
51
+ # * +:storage_blob_host+ - String. Specified Blob serivce endpoint or hostname
52
+ # * +:storage_table_host+ - String. Specified Table serivce endpoint or hostname
53
+ # * +:storage_queue_host+ - String. Specified Queue serivce endpoint or hostname
54
+ # * +:storage_dns_suffix+ - String. The suffix of a regional Storage Serivce, to
55
+ # * +:default_endpoints_protocol+ - String. http or https
56
+ # * +:use_path_style_uri+ - String. Whether use path style URI for specified endpoints
57
+ # * +:ca_file+ - String. File path of the CA file if having issue with SSL
58
+ # * +:ssl_version+ - Symbol. The ssl version to be used, sample: :TLSv1_1, :TLSv1_2, for the details, see https://github.com/ruby/openssl/blob/master/lib/openssl/ssl.rb
59
+ # * +:ssl_min_version+ - Symbol. The min ssl version supported, only supported in Ruby 2.5+
60
+ # * +:ssl_max_version+ - Symbol. The max ssl version supported, only supported in Ruby 2.5+
61
+ #
62
+ # The valid set of options include:
63
+ # * Storage Emulator: +:use_development_storage+ required, +:development_storage_proxy_uri+ optionally
64
+ # * Storage account name and key: +:storage_account_name+ and +:storage_access_key+ required, set +:storage_dns_suffix+ necessarily
65
+ # * Storage account name and SAS token: +:storage_account_name+ and +:storage_sas_token+ required, set +:storage_dns_suffix+ necessarily
66
+ # * Specified hosts and SAS token: At least one of the service host and SAS token. It's up to user to ensure the SAS token is suitable for the serivce
67
+ # * Anonymous Blob: only +:storage_blob_host+, if it is to only access blobs within a container
68
+ #
69
+ # Additional notes:
70
+ # * Specified hosts can be set when use account name with access key or sas token
71
+ # * +:default_endpoints_protocol+ can be set if the scheme is not specified in hosts
72
+ # * Storage emulator always use path style URI
73
+ #
74
+ # When empty options are given, it will try to read settings from Environment Variables. Refer to [Azure::Storage::Common:ClientOptions.env_vars_mapping] for the mapping relationship
75
+ #
76
+ # @return [Azure::Storage::Common::Client]
77
+ def reset!(options = {})
78
+ if options.is_a? String
79
+ options = parse_connection_string(options)
80
+ elsif options.is_a? Hash
81
+ # When the options are provided via singleton setup: Azure::Storage.setup()
82
+ options = setup_options if options.length == 0
83
+
84
+ options = parse_connection_string(options[:storage_connection_string]) if options[:storage_connection_string]
85
+ end
86
+
87
+ # Load from environment when no valid input
88
+ options = load_env if options.length == 0
89
+
90
+ @ca_file = options.delete(:ca_file)
91
+ @ssl_version = options.delete(:ssl_version)
92
+ @ssl_min_version = options.delete(:ssl_min_version)
93
+ @ssl_max_version = options.delete(:ssl_max_version)
94
+ @options = filter(options)
95
+ self.send(:reset_config!, @options) if self.respond_to?(:reset_config!)
96
+ self
97
+ end
98
+
99
+ # Check if this client is configured with the same options
100
+ def same_options?(opts)
101
+ opts.length == 0 || opts.hash == options.hash
102
+ end
103
+
104
+ # The options after validated and normalized
105
+ #
106
+ # @return [Hash]
107
+ def options
108
+ @options ||= {}
109
+ end
110
+
111
+ # The valid options for the storage client
112
+ #
113
+ # @return [Array]
114
+ def self.valid_options
115
+ @valid_options ||= [
116
+ :use_development_storage,
117
+ :development_storage_proxy_uri,
118
+ :storage_account_name,
119
+ :storage_access_key,
120
+ :storage_connection_string,
121
+ :storage_sas_token,
122
+ :storage_blob_host,
123
+ :storage_table_host,
124
+ :storage_queue_host,
125
+ :storage_file_host,
126
+ :storage_dns_suffix,
127
+ :default_endpoints_protocol,
128
+ :use_path_style_uri
129
+ ]
130
+ end
131
+
132
+ # The mapping between Storage Environment Variables and the options name
133
+ #
134
+ # @return [Hash]
135
+ def self.env_vars_mapping
136
+ @env_vars_mapping ||= {
137
+ "EMULATED" => :use_development_storage,
138
+ "AZURE_STORAGE_ACCOUNT" => :storage_account_name,
139
+ "AZURE_STORAGE_ACCESS_KEY" => :storage_access_key,
140
+ "AZURE_STORAGE_CONNECTION_STRING" => :storage_connection_string,
141
+ "AZURE_STORAGE_BLOB_HOST" => :storage_blob_host,
142
+ "AZURE_STORAGE_TABLE_HOST" => :storage_table_host,
143
+ "AZURE_STORAGE_QUEUE_HOST" => :storage_queue_host,
144
+ "AZURE_STORAGE_FILE_HOST" => :storage_file_host,
145
+ "AZURE_STORAGE_SAS_TOKEN" => :storage_sas_token,
146
+ "AZURE_STORAGE_DNS_SUFFIX" => :storage_dns_suffix
147
+ }
148
+ end
149
+
150
+ # The mapping between Storage Connection String items and the options name
151
+ #
152
+ # @return [Hash]
153
+ def self.connection_string_mapping
154
+ @connection_string_mapping ||= {
155
+ "UseDevelopmentStorage" => :use_development_storage,
156
+ "DevelopmentStorageProxyUri" => :development_storage_proxy_uri,
157
+ "DefaultEndpointsProtocol" => :default_endpoints_protocol,
158
+ "AccountName" => :storage_account_name,
159
+ "AccountKey" => :storage_access_key,
160
+ "BlobEndpoint" => :storage_blob_host,
161
+ "TableEndpoint" => :storage_table_host,
162
+ "QueueEndpoint" => :storage_queue_host,
163
+ "FileEndpoint" => :storage_file_host,
164
+ "SharedAccessSignature" => :storage_sas_token,
165
+ "EndpointSuffix" => :storage_dns_suffix
166
+ }
167
+ end
168
+
169
+ private
170
+
171
+ def method_missing(method_name, *args, &block)
172
+ return super unless options.key? method_name
173
+ options[method_name]
174
+ end
175
+
176
+ def filter(opts = {})
177
+ results = {}
178
+
179
+ # P1 - develpoment storage
180
+ begin
181
+ results = validated_options(opts,
182
+ required: [:use_development_storage],
183
+ optional: [:development_storage_proxy_uri])
184
+ results[:use_development_storage] = true
185
+ proxy_uri = results[:development_storage_proxy_uri] ||= StorageServiceClientConstants::DEV_STORE_URI
186
+ results.merge!(storage_account_name: StorageServiceClientConstants::DEVSTORE_STORAGE_ACCOUNT,
187
+ storage_access_key: StorageServiceClientConstants::DEVSTORE_STORAGE_ACCESS_KEY,
188
+ storage_blob_host: "#{proxy_uri}:#{StorageServiceClientConstants::DEVSTORE_BLOB_HOST_PORT}",
189
+ storage_table_host: "#{proxy_uri}:#{StorageServiceClientConstants::DEVSTORE_TABLE_HOST_PORT}",
190
+ storage_queue_host: "#{proxy_uri}:#{StorageServiceClientConstants::DEVSTORE_QUEUE_HOST_PORT}",
191
+ storage_file_host: "#{proxy_uri}:#{StorageServiceClientConstants::DEVSTORE_FILE_HOST_PORT}",
192
+ use_path_style_uri: true)
193
+ return results
194
+ rescue InvalidOptionsError => e
195
+ end
196
+
197
+ # P2 - explicit hosts with account connection string
198
+ begin
199
+ results = validated_options(opts,
200
+ required: [:storage_connection_string],
201
+ optional: [:use_path_style_uri])
202
+ results[:use_path_style_uri] = results.key?(:use_path_style_uri)
203
+ normalize_hosts(results)
204
+ return results
205
+ rescue InvalidOptionsError => e
206
+ end
207
+
208
+ # P3 - account name and key or sas with default hosts or an end suffix
209
+ begin
210
+ results = validated_options(opts,
211
+ required: [:storage_account_name],
212
+ only_one: [:storage_access_key, :storage_sas_token, :signer],
213
+ optional: [:default_endpoints_protocol, :storage_dns_suffix])
214
+ protocol = results[:default_endpoints_protocol] ||= StorageServiceClientConstants::DEFAULT_PROTOCOL
215
+ suffix = results[:storage_dns_suffix] ||= StorageServiceClientConstants::DEFAULT_ENDPOINT_SUFFIX
216
+ account = results[:storage_account_name]
217
+ results.merge!(storage_blob_host: "#{protocol}://#{account}.#{ServiceType::BLOB}.#{suffix}",
218
+ storage_table_host: "#{protocol}://#{account}.#{ServiceType::TABLE}.#{suffix}",
219
+ storage_queue_host: "#{protocol}://#{account}.#{ServiceType::QUEUE}.#{suffix}",
220
+ storage_file_host: "#{protocol}://#{account}.#{ServiceType::FILE}.#{suffix}",
221
+ use_path_style_uri: false)
222
+ return results
223
+ rescue InvalidOptionsError => e
224
+ end
225
+
226
+ # P4 - explicit hosts with account name and key
227
+ begin
228
+ results = validated_options(opts,
229
+ required: [:storage_account_name, :storage_access_key],
230
+ at_least_one: [:storage_blob_host, :storage_table_host, :storage_file_host, :storage_queue_host],
231
+ optional: [:use_path_style_uri, :default_endpoints_protocol])
232
+ results[:use_path_style_uri] = results.key?(:use_path_style_uri)
233
+ normalize_hosts(results)
234
+ return results
235
+ rescue InvalidOptionsError => e
236
+ end
237
+
238
+ # P5 - anonymous or sas only for one or more particular services, options with account name/key + hosts should be already validated in P4
239
+ begin
240
+ results = validated_options(opts,
241
+ at_least_one: [:storage_blob_host, :storage_table_host, :storage_file_host, :storage_queue_host],
242
+ optional: [:use_path_style_uri, :default_endpoints_protocol, :storage_sas_token])
243
+ results[:use_path_style_uri] = results.key?(:use_path_style_uri)
244
+ normalize_hosts(results)
245
+ # Adds anonymous signer if no sas token
246
+ results[:signer] = Azure::Storage::Common::Core::Auth::AnonymousSigner.new unless results.key?(:storage_sas_token)
247
+ return results
248
+ rescue InvalidOptionsError => e
249
+ end
250
+
251
+ # P6 - account name and key or sas with explicit hosts
252
+ begin
253
+ results = validated_options(opts,
254
+ required: [:storage_account_name],
255
+ only_one: [:storage_access_key, :storage_sas_token],
256
+ at_least_one: [:storage_blob_host, :storage_table_host, :storage_file_host, :storage_queue_host])
257
+ results[:use_path_style_uri] = results.key?(:use_path_style_uri)
258
+ normalize_hosts(results)
259
+ return results
260
+ rescue InvalidOptionsError => e
261
+ end
262
+
263
+ raise InvalidOptionsError, "options provided are not valid set: #{opts}" # wrong opts if move to this line
264
+ end
265
+
266
+ def normalize_hosts(options)
267
+ if options[:default_endpoints_protocol]
268
+ [:storage_blob_host, :storage_table_host, :storage_file_host, :storage_queue_host].each do |k|
269
+ if options[k]
270
+ raise InvalidOptionsError, "Explict host cannot contain scheme if default_endpoints_protocol is set." if options[k] =~ /^https?/
271
+ options[k] = "#{options[:default_endpoints_protocol]}://#{options[k]}"
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ def is_base64_encoded
278
+ Proc.new do |i|
279
+ i.is_a?(String) && i =~ /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$/
280
+ end
281
+ end
282
+
283
+ def is_url
284
+ Proc.new do |i|
285
+ i = "http://" + i unless i =~ /\Ahttps?:\/\//
286
+ i =~ URI.regexp(["http", "https"])
287
+ end
288
+ end
289
+
290
+ def is_true
291
+ Proc.new { |i| i == true || (i.is_a?(String) && i.downcase == "true") }
292
+ end
293
+
294
+ def is_non_empty_string
295
+ Proc.new { |i| i && i.is_a?(String) && i.strip.length }
296
+ end
297
+
298
+ def validated_options(opts, requirements = {})
299
+ raise InvalidOptionsError, 'nil is not allowed for option\'s value' if opts.values.any? { |v| v == nil }
300
+ required = requirements[:required] || []
301
+ at_least_one = requirements[:at_least_one] || []
302
+ only_one = requirements[:only_one] || []
303
+ optional = requirements[:optional] || []
304
+
305
+ raise InvalidOptionsError, "Not all required keys are provided: #{required}" if required.any? { |k| !opts.key? k }
306
+ raise InvalidOptionsError, "Only one of #{only_one} is required" unless only_one.length == 0 || only_one.count { |k| opts.key? k } == 1
307
+ raise InvalidOptionsError, "At least one of #{at_least_one} is required" unless at_least_one.length == 0 || at_least_one.any? { |k| opts.key? k }
308
+
309
+ @@option_validators ||= {
310
+ use_development_storage: is_true,
311
+ development_storage_proxy_uri: is_url,
312
+ storage_account_name: lambda { |i| i.is_a?(String) },
313
+ storage_access_key: is_base64_encoded,
314
+ storage_sas_token: lambda { |i| i.is_a?(String) },
315
+ storage_blob_host: is_url,
316
+ storage_table_host: is_url,
317
+ storage_queue_host: is_url,
318
+ storage_file_host: is_url,
319
+ storage_dns_suffix: is_url,
320
+ default_endpoints_protocol: lambda { |i| ["http", "https"].include? i.downcase },
321
+ use_path_style_uri: is_true,
322
+ signer: lambda { |i| i.is_a? Azure::Core::Auth::Signer}
323
+ }
324
+
325
+ valid_options = required + at_least_one + only_one + optional
326
+ results = {}
327
+
328
+ opts.each do |k, v|
329
+ raise InvalidOptionsError, "#{k} is not included in valid options" unless valid_options.length == 0 || valid_options.include?(k)
330
+ unless @@option_validators.key?(k) && @@option_validators[k].call(v)
331
+ raise InvalidOptionsError, "#{k} is invalid"
332
+ end
333
+ results[k] = v
334
+ end
335
+ results
336
+ end
337
+
338
+ def load_env
339
+ cs = ENV["AZURE_STORAGE_CONNECTION_STRING"]
340
+ return parse_connection_string(cs) if cs
341
+
342
+ opts = {}
343
+ ClientOptions.env_vars_mapping.each { |k, v| opts[v] = ENV[k] if ENV[k] }
344
+ opts
345
+ end
346
+
347
+ def parse_connection_string(connection_string)
348
+ opts = {}
349
+ connection_string.split(";").each do |i|
350
+ e = i.index("=")
351
+ raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING if e < 0 || e == i.length - 1
352
+ key, value = i[0..e - 1], i[e + 1..i.length - 1]
353
+ raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING_BAD_KEY % key unless ClientOptions.connection_string_mapping.key? key
354
+ raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING_EMPTY_KEY % key if value.length == 0
355
+ raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING_DUPLICATE_KEY % key if opts.key? key
356
+ opts[ClientOptions.connection_string_mapping[key]] = value
357
+ end
358
+ raise InvalidConnectionStringError, Azure::Storage::Common::Core::SR::INVALID_CONNECTION_STRING if opts.length == 0
359
+
360
+ opts
361
+ end
362
+ end
363
+ end