aws-sdk-resources 2.11.549

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-resources.rb +91 -0
  3. data/lib/aws-sdk-resources/batch.rb +143 -0
  4. data/lib/aws-sdk-resources/builder.rb +85 -0
  5. data/lib/aws-sdk-resources/builder_sources.rb +105 -0
  6. data/lib/aws-sdk-resources/collection.rb +107 -0
  7. data/lib/aws-sdk-resources/definition.rb +331 -0
  8. data/lib/aws-sdk-resources/documenter.rb +70 -0
  9. data/lib/aws-sdk-resources/documenter/base_operation_documenter.rb +279 -0
  10. data/lib/aws-sdk-resources/documenter/data_operation_documenter.rb +25 -0
  11. data/lib/aws-sdk-resources/documenter/has_many_operation_documenter.rb +69 -0
  12. data/lib/aws-sdk-resources/documenter/has_operation_documenter.rb +66 -0
  13. data/lib/aws-sdk-resources/documenter/operation_documenter.rb +20 -0
  14. data/lib/aws-sdk-resources/documenter/resource_operation_documenter.rb +53 -0
  15. data/lib/aws-sdk-resources/documenter/waiter_operation_documenter.rb +77 -0
  16. data/lib/aws-sdk-resources/errors.rb +15 -0
  17. data/lib/aws-sdk-resources/operation_methods.rb +83 -0
  18. data/lib/aws-sdk-resources/operations.rb +280 -0
  19. data/lib/aws-sdk-resources/options.rb +17 -0
  20. data/lib/aws-sdk-resources/request.rb +39 -0
  21. data/lib/aws-sdk-resources/request_params.rb +140 -0
  22. data/lib/aws-sdk-resources/resource.rb +243 -0
  23. data/lib/aws-sdk-resources/services/ec2.rb +21 -0
  24. data/lib/aws-sdk-resources/services/ec2/instance.rb +29 -0
  25. data/lib/aws-sdk-resources/services/iam.rb +19 -0
  26. data/lib/aws-sdk-resources/services/s3.rb +20 -0
  27. data/lib/aws-sdk-resources/services/s3/bucket.rb +131 -0
  28. data/lib/aws-sdk-resources/services/s3/encryption.rb +21 -0
  29. data/lib/aws-sdk-resources/services/s3/encryption/client.rb +369 -0
  30. data/lib/aws-sdk-resources/services/s3/encryption/decrypt_handler.rb +174 -0
  31. data/lib/aws-sdk-resources/services/s3/encryption/default_cipher_provider.rb +63 -0
  32. data/lib/aws-sdk-resources/services/s3/encryption/default_key_provider.rb +38 -0
  33. data/lib/aws-sdk-resources/services/s3/encryption/encrypt_handler.rb +50 -0
  34. data/lib/aws-sdk-resources/services/s3/encryption/errors.rb +13 -0
  35. data/lib/aws-sdk-resources/services/s3/encryption/io_auth_decrypter.rb +56 -0
  36. data/lib/aws-sdk-resources/services/s3/encryption/io_decrypter.rb +29 -0
  37. data/lib/aws-sdk-resources/services/s3/encryption/io_encrypter.rb +69 -0
  38. data/lib/aws-sdk-resources/services/s3/encryption/key_provider.rb +29 -0
  39. data/lib/aws-sdk-resources/services/s3/encryption/kms_cipher_provider.rb +71 -0
  40. data/lib/aws-sdk-resources/services/s3/encryption/materials.rb +58 -0
  41. data/lib/aws-sdk-resources/services/s3/encryption/utils.rb +79 -0
  42. data/lib/aws-sdk-resources/services/s3/file_downloader.rb +169 -0
  43. data/lib/aws-sdk-resources/services/s3/file_part.rb +75 -0
  44. data/lib/aws-sdk-resources/services/s3/file_uploader.rb +58 -0
  45. data/lib/aws-sdk-resources/services/s3/multipart_file_uploader.rb +187 -0
  46. data/lib/aws-sdk-resources/services/s3/multipart_upload.rb +42 -0
  47. data/lib/aws-sdk-resources/services/s3/multipart_upload_error.rb +16 -0
  48. data/lib/aws-sdk-resources/services/s3/object.rb +290 -0
  49. data/lib/aws-sdk-resources/services/s3/object_copier.rb +99 -0
  50. data/lib/aws-sdk-resources/services/s3/object_multipart_copier.rb +180 -0
  51. data/lib/aws-sdk-resources/services/s3/object_summary.rb +73 -0
  52. data/lib/aws-sdk-resources/services/s3/presigned_post.rb +651 -0
  53. data/lib/aws-sdk-resources/services/sns.rb +7 -0
  54. data/lib/aws-sdk-resources/services/sns/message_verifier.rb +171 -0
  55. data/lib/aws-sdk-resources/services/sqs.rb +7 -0
  56. data/lib/aws-sdk-resources/services/sqs/queue_poller.rb +521 -0
  57. data/lib/aws-sdk-resources/source.rb +39 -0
  58. metadata +118 -0
@@ -0,0 +1,29 @@
1
+ module Aws
2
+ module S3
3
+ module Encryption
4
+
5
+ # This module defines the interface required for a {Client#key_provider}.
6
+ # A key provider is any object that:
7
+ #
8
+ # * Responds to {#encryption_materials} with an {Materials} object.
9
+ #
10
+ # * Responds to {#key_for}, receiving a JSON document String,
11
+ # returning an encryption key. The returned encryption key
12
+ # must be one of:
13
+ #
14
+ # * `OpenSSL::PKey::RSA` - for asymmetric encryption
15
+ # * `String` - 32, 24, or 16 bytes long, for symmetric encryption
16
+ #
17
+ module KeyProvider
18
+
19
+ # @return [Materials]
20
+ def encryption_materials; end
21
+
22
+ # @param [String<JSON>] materials_description
23
+ # @return [OpenSSL::PKey::RSA, String] encryption_key
24
+ def key_for(materials_description); end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,71 @@
1
+ require 'base64'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ # @api private
7
+ class KmsCipherProvider
8
+
9
+ def initialize(options = {})
10
+ @kms_key_id = options[:kms_key_id]
11
+ @kms_client = options[:kms_client]
12
+ end
13
+
14
+ # @return [Array<Hash,Cipher>] Creates an returns a new encryption
15
+ # envelope and encryption cipher.
16
+ def encryption_cipher
17
+ encryption_context = { "kms_cmk_id" => @kms_key_id }
18
+ key_data = @kms_client.generate_data_key(
19
+ key_id: @kms_key_id,
20
+ encryption_context: encryption_context,
21
+ key_spec: 'AES_256',
22
+ )
23
+ cipher = Utils.aes_encryption_cipher(:CBC)
24
+ cipher.key = key_data.plaintext
25
+ envelope = {
26
+ 'x-amz-key-v2' => encode64(key_data.ciphertext_blob),
27
+ 'x-amz-iv' => encode64(cipher.iv = cipher.random_iv),
28
+ 'x-amz-cek-alg' => 'AES/CBC/PKCS5Padding',
29
+ 'x-amz-wrap-alg' => 'kms',
30
+ 'x-amz-matdesc' => Json.dump(encryption_context)
31
+ }
32
+ [envelope, cipher]
33
+ end
34
+
35
+ # @return [Cipher] Given an encryption envelope, returns a
36
+ # decryption cipher.
37
+ def decryption_cipher(envelope)
38
+ encryption_context = Json.load(envelope['x-amz-matdesc'])
39
+ key = @kms_client.decrypt(
40
+ ciphertext_blob: decode64(envelope['x-amz-key-v2']),
41
+ encryption_context: encryption_context,
42
+ ).plaintext
43
+ iv = decode64(envelope['x-amz-iv'])
44
+ block_mode =
45
+ case envelope['x-amz-cek-alg']
46
+ when 'AES/CBC/PKCS5Padding'
47
+ :CBC
48
+ when 'AES/GCM/NoPadding'
49
+ :GCM
50
+ else
51
+ type = envelope['x-amz-cek-alg'].inspect
52
+ msg = "unsupported content encrypting key (cek) format: #{type}"
53
+ raise Errors::DecryptionError, msg
54
+ end
55
+ Utils.aes_decryption_cipher(block_mode, key, iv)
56
+ end
57
+
58
+ private
59
+
60
+ def encode64(str)
61
+ Base64.encode64(str).split("\n") * ""
62
+ end
63
+
64
+ def decode64(str)
65
+ Base64.decode64(str)
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,58 @@
1
+ require 'base64'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ class Materials
7
+
8
+ # @option options [required, OpenSSL::PKey::RSA, String] :key
9
+ # The master key to use for encrypting/decrypting all objects.
10
+ #
11
+ # @option options [String<JSON>] :description ('{}')
12
+ # The encryption materials description. This is must be
13
+ # a JSON document string.
14
+ #
15
+ def initialize(options = {})
16
+ @key = validate_key(options[:key])
17
+ @description = validate_desc(options[:description])
18
+ end
19
+
20
+ # @return [OpenSSL::PKey::RSA, String]
21
+ attr_reader :key
22
+
23
+ # @return [String<JSON>]
24
+ attr_reader :description
25
+
26
+ private
27
+
28
+ def validate_key(key)
29
+ case key
30
+ when OpenSSL::PKey::RSA then key
31
+ when String
32
+ if [32, 24, 16].include?(key.bytesize)
33
+ key
34
+ else
35
+ msg = "invalid key, symmetric key required to be 16, 24, or "
36
+ msg << "32 bytes in length, saw length " + key.bytesize.to_s
37
+ raise ArgumentError, msg
38
+ end
39
+ else
40
+ msg = "invalid encryption key, expected an OpenSSL::PKey::RSA key "
41
+ msg << "(for asymmetric encryption) or a String (for symmetric "
42
+ msg << "encryption)."
43
+ raise ArgumentError, msg
44
+ end
45
+ end
46
+
47
+ def validate_desc(description)
48
+ Json.load(description)
49
+ description
50
+ rescue Json::ParseError, EncodingError
51
+ msg = "expected description to be a valid JSON document string"
52
+ raise ArgumentError, msg
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,79 @@
1
+ require 'openssl'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ # @api private
7
+ module Utils
8
+
9
+ UNSAFE_MSG = "unsafe encryption, data is longer than key length"
10
+
11
+ class << self
12
+
13
+ def encrypt(key, data)
14
+ case key
15
+ when OpenSSL::PKey::RSA # asymmetric encryption
16
+ warn(UNSAFE_MSG) if key.public_key.n.num_bits < cipher_size(data)
17
+ key.public_encrypt(data)
18
+ when String # symmetric encryption
19
+ warn(UNSAFE_MSG) if cipher_size(key) < cipher_size(data)
20
+ cipher = aes_encryption_cipher(:ECB, key)
21
+ cipher.update(data) + cipher.final
22
+ end
23
+ end
24
+
25
+ def decrypt(key, data)
26
+ begin
27
+ case key
28
+ when OpenSSL::PKey::RSA # asymmetric decryption
29
+ key.private_decrypt(data)
30
+ when String # symmetric Decryption
31
+ cipher = aes_cipher(:decrypt, :ECB, key, nil)
32
+ cipher.update(data) + cipher.final
33
+ end
34
+ rescue OpenSSL::Cipher::CipherError
35
+ msg = 'decryption failed, possible incorrect key'
36
+ raise Errors::DecryptionError, msg
37
+ end
38
+ end
39
+
40
+ # @param [String] block_mode "CBC" or "ECB"
41
+ # @param [OpenSSL::PKey::RSA, String, nil] key
42
+ # @param [String, nil] iv The initialization vector
43
+ def aes_encryption_cipher(block_mode, key = nil, iv = nil)
44
+ aes_cipher(:encrypt, block_mode, key, iv)
45
+ end
46
+
47
+ # @param [String] block_mode "CBC" or "ECB"
48
+ # @param [OpenSSL::PKey::RSA, String, nil] key
49
+ # @param [String, nil] iv The initialization vector
50
+ def aes_decryption_cipher(block_mode, key = nil, iv = nil)
51
+ aes_cipher(:decrypt, block_mode, key, iv)
52
+ end
53
+
54
+ # @param [String] mode "encrypt" or "decrypt"
55
+ # @param [String] block_mode "CBC" or "ECB"
56
+ # @param [OpenSSL::PKey::RSA, String, nil] key
57
+ # @param [String, nil] iv The initialization vector
58
+ def aes_cipher(mode, block_mode, key, iv)
59
+ cipher = key ?
60
+ OpenSSL::Cipher.new("aes-#{cipher_size(key)}-#{block_mode.downcase}") :
61
+ OpenSSL::Cipher.new("aes-256-#{block_mode.downcase}")
62
+ cipher.send(mode) # encrypt or decrypt
63
+ cipher.key = key if key
64
+ cipher.iv = iv if iv
65
+ cipher
66
+ end
67
+
68
+ # @param [String] key
69
+ # @return [Integer]
70
+ # @raise ArgumentError
71
+ def cipher_size(key)
72
+ key.bytesize * 8
73
+ end
74
+
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,169 @@
1
+ require 'pathname'
2
+ require 'thread'
3
+ require 'set'
4
+ require 'tmpdir'
5
+
6
+ module Aws
7
+ module S3
8
+ # @api private
9
+ class FileDownloader
10
+
11
+ MIN_CHUNK_SIZE = 5 * 1024 * 1024
12
+ MAX_PARTS = 10_000
13
+ THREAD_COUNT = 10
14
+
15
+ def initialize(options = {})
16
+ @client = options[:client] || Client.new
17
+ end
18
+
19
+ # @return [Client]
20
+ attr_reader :client
21
+
22
+ def download(destination, options = {})
23
+ @path = destination
24
+ @mode = options[:mode] || "auto"
25
+ @thread_count = options[:thread_count] || THREAD_COUNT
26
+ @chunk_size = options[:chunk_size]
27
+ @bucket = options[:bucket]
28
+ @key = options[:key]
29
+
30
+ case @mode
31
+ when "auto" then multipart_download
32
+ when "single_request" then single_request
33
+ when "get_range"
34
+ if @chunk_size
35
+ resp = @client.head_object(bucket: @bucket, key: @key)
36
+ multithreaded_get_by_ranges(construct_chunks(resp.content_length))
37
+ else
38
+ msg = "In :get_range mode, :chunk_size must be provided"
39
+ raise ArgumentError, msg
40
+ end
41
+ else
42
+ msg = "Invalid mode #{@mode} provided, "\
43
+ "mode should be :single_request, :get_range or :auto"
44
+ raise ArgumentError, msg
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def multipart_download
51
+ resp = @client.head_object(bucket: @bucket, key: @key, part_number: 1)
52
+ count = resp.parts_count
53
+ if count.nil? || count <= 1
54
+ resp.content_length < MIN_CHUNK_SIZE ?
55
+ single_request :
56
+ multithreaded_get_by_ranges(construct_chunks(resp.content_length))
57
+ else
58
+ # partNumber is an option
59
+ resp = @client.head_object(bucket: @bucket, key: @key)
60
+ resp.content_length < MIN_CHUNK_SIZE ?
61
+ single_request :
62
+ compute_mode(resp.content_length, count)
63
+ end
64
+ end
65
+
66
+ def compute_mode(file_size, count)
67
+ chunk_size = compute_chunk(file_size)
68
+ part_size = (file_size.to_f / count.to_f).ceil
69
+ if chunk_size < part_size
70
+ multithreaded_get_by_ranges(construct_chunks(file_size))
71
+ else
72
+ multithreaded_get_by_parts(count)
73
+ end
74
+ end
75
+
76
+ def construct_chunks(file_size)
77
+ offset = 0
78
+ default_chunk_size = compute_chunk(file_size)
79
+ chunks = []
80
+ while offset <= file_size
81
+ progress = offset + default_chunk_size
82
+ chunks << "bytes=#{offset}-#{progress < file_size ? progress : file_size}"
83
+ offset = progress + 1
84
+ end
85
+ chunks
86
+ end
87
+
88
+ def compute_chunk(file_size)
89
+ if @chunk_size && @chunk_size > file_size
90
+ raise ArgumentError, ":chunk_size shouldn't exceed total file size."
91
+ else
92
+ default_chunk_size = @chunk_size || [(file_size.to_f / MAX_PARTS).ceil, MIN_CHUNK_SIZE].max.to_i
93
+ end
94
+ end
95
+
96
+ def sort_files(files)
97
+ # sort file by start range count or part number
98
+ files.sort do |a, b|
99
+ a[/([^\=]+)$/].split('-')[0].to_i <=> b[/([^\=]+)$/].split('-')[0].to_i
100
+ end
101
+ end
102
+
103
+ def concatenate_parts(fileparts)
104
+ File.open(@path, 'wb')do |output_path|
105
+ sort_files(fileparts).each {|part| IO.copy_stream(part, output_path)}
106
+ end
107
+ end
108
+
109
+ def file_batches(chunks, dir, mode)
110
+ batches = []
111
+ chunks = (1..chunks) if mode.eql? 'part_number'
112
+ chunks.each_slice(@thread_count) do |slice|
113
+ batches << map_files(slice, dir, mode)
114
+ end
115
+ batches
116
+ end
117
+
118
+ def map_files(slice, dir, mode)
119
+ case mode
120
+ when 'range'
121
+ slice.inject({}) {|h, chunk| h[chunk] = File.join(dir, chunk); h}
122
+ when 'part_number'
123
+ slice.inject({}) {|h, part| h[part] = File.join(dir, "part_number=#{part}"); h}
124
+ end
125
+ end
126
+
127
+ def multithreaded_get_by_ranges(chunks)
128
+ thread_batches(chunks, 'range')
129
+ end
130
+
131
+ def multithreaded_get_by_parts(parts)
132
+ thread_batches(parts, 'part_number')
133
+ end
134
+
135
+ def thread_batches(chunks, param)
136
+ # create a tmp dir under destination dir for batches
137
+ dir = Dir.mktmpdir(nil, File.dirname(@path))
138
+ batches = file_batches(chunks, dir, param)
139
+ parts = batches.flat_map(&:values)
140
+ begin
141
+ batches.each do |batch|
142
+ threads = []
143
+ batch.each do |chunk, file|
144
+ threads << Thread.new do
145
+ resp = @client.get_object(
146
+ :bucket => @bucket,
147
+ :key => @key,
148
+ param.to_sym => chunk,
149
+ :response_target => file
150
+ )
151
+ end
152
+ end
153
+ threads.each(&:join)
154
+ end
155
+ concatenate_parts(parts)
156
+ ensure
157
+ # clean up tmp dir
158
+ FileUtils.remove_entry(dir)
159
+ end
160
+ end
161
+
162
+ def single_request
163
+ @client.get_object(
164
+ bucket: @bucket, key: @key, response_target: @path
165
+ )
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,75 @@
1
+ module Aws
2
+ module S3
3
+
4
+ # A utility class that provides an IO-like interface to a portion of
5
+ # a file on disk.
6
+ # @api private
7
+ class FilePart
8
+
9
+ # @option options [required,String,Pathname,File,Tempfile] :source
10
+ # @option options [required,Integer] :offset The file part will read
11
+ # starting at this byte offset.
12
+ # @option options [required,Integer] :size The maximum number of bytes to
13
+ # read from the `:offset`.
14
+ def initialize(options = {})
15
+ @source = options[:source]
16
+ @first_byte = options[:offset]
17
+ @last_byte = @first_byte + options[:size]
18
+ @size = options[:size]
19
+ @file = nil
20
+ end
21
+
22
+ # @return [String,Pathname,File,Tempfile]
23
+ attr_reader :source
24
+
25
+ # @return [Integer]
26
+ attr_reader :first_byte
27
+
28
+ # @return [Integer]
29
+ attr_reader :last_byte
30
+
31
+ # @return [Integer]
32
+ attr_reader :size
33
+
34
+ def read(bytes = nil, output_buffer = nil)
35
+ open_file unless @file
36
+ read_from_file(bytes, output_buffer)
37
+ end
38
+
39
+ def rewind
40
+ if @file
41
+ @file.seek(@first_byte)
42
+ @position = @first_byte
43
+ end
44
+ 0
45
+ end
46
+
47
+ def close
48
+ @file.close if @file
49
+ end
50
+
51
+ private
52
+
53
+ def open_file
54
+ @file = File.open(@source, 'rb')
55
+ rewind
56
+ end
57
+
58
+ def read_from_file(bytes, output_buffer)
59
+ if bytes
60
+ data = @file.read([remaining_bytes, bytes].min)
61
+ data = nil if data == ''
62
+ else
63
+ data = @file.read(remaining_bytes)
64
+ end
65
+ @position += data ? data.bytesize : 0
66
+ output_buffer ? output_buffer.replace(data || '') : data
67
+ end
68
+
69
+ def remaining_bytes
70
+ @last_byte - @position
71
+ end
72
+
73
+ end
74
+ end
75
+ end