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,174 @@
1
+ require 'base64'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ # @api private
7
+ class DecryptHandler < Seahorse::Client::Handler
8
+
9
+ V1_ENVELOPE_KEYS = %w(
10
+ x-amz-key
11
+ x-amz-iv
12
+ x-amz-matdesc
13
+ )
14
+
15
+ V2_ENVELOPE_KEYS = %w(
16
+ x-amz-key-v2
17
+ x-amz-iv
18
+ x-amz-cek-alg
19
+ x-amz-wrap-alg
20
+ x-amz-matdesc
21
+ )
22
+
23
+ POSSIBLE_ENVELOPE_KEYS = (V1_ENVELOPE_KEYS + V2_ENVELOPE_KEYS).uniq
24
+
25
+ POSSIBLE_ENCRYPTION_FORMATS = %w(
26
+ AES/GCM/NoPadding
27
+ AES/CBC/PKCS5Padding
28
+ )
29
+
30
+ def call(context)
31
+ attach_http_event_listeners(context)
32
+ @handler.call(context)
33
+ end
34
+
35
+ private
36
+
37
+ def attach_http_event_listeners(context)
38
+
39
+ context.http_response.on_headers(200) do
40
+ cipher = decryption_cipher(context)
41
+ decrypter = body_contains_auth_tag?(context) ?
42
+ authenticated_decrypter(context, cipher) :
43
+ IODecrypter.new(cipher, context.http_response.body)
44
+ context.http_response.body = decrypter
45
+ end
46
+
47
+ context.http_response.on_success(200) do
48
+ decrypter = context.http_response.body
49
+ decrypter.finalize
50
+ decrypter.io.rewind if decrypter.io.respond_to?(:rewind)
51
+ context.http_response.body = decrypter.io
52
+ end
53
+
54
+ context.http_response.on_error do
55
+ if context.http_response.body.respond_to?(:io)
56
+ context.http_response.body = context.http_response.body.io
57
+ end
58
+ end
59
+ end
60
+
61
+ def decryption_cipher(context)
62
+ if envelope = get_encryption_envelope(context)
63
+ context[:encryption][:cipher_provider].decryption_cipher(envelope)
64
+ else
65
+ raise Errors::DecryptionError, "unable to locate encryption envelope"
66
+ end
67
+ end
68
+
69
+ def get_encryption_envelope(context)
70
+ if context[:encryption][:envelope_location] == :metadata
71
+ envelope_from_metadata(context) || envelope_from_instr_file(context)
72
+ else
73
+ envelope_from_instr_file(context) || envelope_from_metadata(context)
74
+ end
75
+ end
76
+
77
+ def envelope_from_metadata(context)
78
+ possible_envelope = {}
79
+ POSSIBLE_ENVELOPE_KEYS.each do |suffix|
80
+ if value = context.http_response.headers["x-amz-meta-#{suffix}"]
81
+ possible_envelope[suffix] = value
82
+ end
83
+ end
84
+ extract_envelope(possible_envelope)
85
+ end
86
+
87
+ def envelope_from_instr_file(context)
88
+ suffix = context[:encryption][:instruction_file_suffix]
89
+ possible_envelope = Json.load(context.client.get_object(
90
+ bucket: context.params[:bucket],
91
+ key: context.params[:key] + suffix
92
+ ).body.read)
93
+ extract_envelope(possible_envelope)
94
+ rescue S3::Errors::ServiceError, Json::ParseError
95
+ nil
96
+ end
97
+
98
+ def extract_envelope(hash)
99
+ return v1_envelope(hash) if hash.key?('x-amz-key')
100
+ return v2_envelope(hash) if hash.key?('x-amz-key-v2')
101
+ if hash.keys.any? { |key| key.match(/^x-amz-key-(.+)$/) }
102
+ msg = "unsupported envelope encryption version #{$1}"
103
+ raise Errors::DecryptionError, msg
104
+ else
105
+ nil # no envelope found
106
+ end
107
+ end
108
+
109
+ def v1_envelope(envelope)
110
+ envelope
111
+ end
112
+
113
+ def v2_envelope(envelope)
114
+ unless POSSIBLE_ENCRYPTION_FORMATS.include? envelope['x-amz-cek-alg']
115
+ alg = envelope['x-amz-cek-alg'].inspect
116
+ msg = "unsupported content encrypting key (cek) format: #{alg}"
117
+ raise Errors::DecryptionError, msg
118
+ end
119
+ unless envelope['x-amz-wrap-alg'] == 'kms'
120
+ # possible to support
121
+ # RSA/ECB/OAEPWithSHA-256AndMGF1Padding
122
+ alg = envelope['x-amz-wrap-alg'].inspect
123
+ msg = "unsupported key wrapping algorithm: #{alg}"
124
+ raise Errors::DecryptionError, msg
125
+ end
126
+ unless V2_ENVELOPE_KEYS.sort == envelope.keys.sort
127
+ msg = "incomplete v2 encryption envelope:\n"
128
+ msg += " expected: #{V2_ENVELOPE_KEYS.join(',')}\n"
129
+ msg += " got: #{envelope_keys.join(', ')}"
130
+ raise Errors::DecryptionError, msg
131
+ end
132
+ envelope
133
+ end
134
+
135
+ # When the x-amz-meta-x-amz-tag-len header is present, it indicates
136
+ # that the body of this object has a trailing auth tag. The header
137
+ # indicates the length of that tag.
138
+ #
139
+ # This method fetches the tag from the end of the object by
140
+ # making a GET Object w/range request. This auth tag is used
141
+ # to initialize the cipher, and the decrypter truncates the
142
+ # auth tag from the body when writing the final bytes.
143
+ def authenticated_decrypter(context, cipher)
144
+ http_resp = context.http_response
145
+ content_length = http_resp.headers['content-length'].to_i
146
+ auth_tag_length = http_resp.headers['x-amz-meta-x-amz-tag-len']
147
+ auth_tag_length = auth_tag_length.to_i / 8
148
+
149
+ auth_tag = context.client.get_object(
150
+ bucket: context.params[:bucket],
151
+ key: context.params[:key],
152
+ range: "bytes=-#{auth_tag_length}"
153
+ ).body.read
154
+
155
+ cipher.auth_tag = auth_tag
156
+ cipher.auth_data = ''
157
+
158
+ # The encrypted object contains both the cipher text
159
+ # plus a trailing auth tag. This decrypter will the body
160
+ # expect for the trailing auth tag.
161
+ decrypter = IOAuthDecrypter.new(
162
+ io: http_resp.body,
163
+ encrypted_content_length: content_length - auth_tag_length,
164
+ cipher: cipher)
165
+ end
166
+
167
+ def body_contains_auth_tag?(context)
168
+ context.http_response.headers['x-amz-meta-x-amz-tag-len']
169
+ end
170
+
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,63 @@
1
+ require 'base64'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ # @api private
7
+ class DefaultCipherProvider
8
+
9
+ def initialize(options = {})
10
+ @key_provider = options[:key_provider]
11
+ end
12
+
13
+ # @return [Array<Hash,Cipher>] Creates an returns a new encryption
14
+ # envelope and encryption cipher.
15
+ def encryption_cipher
16
+ cipher = Utils.aes_encryption_cipher(:CBC)
17
+ envelope = {
18
+ 'x-amz-key' => encode64(encrypt(envelope_key(cipher))),
19
+ 'x-amz-iv' => encode64(envelope_iv(cipher)),
20
+ 'x-amz-matdesc' => materials_description,
21
+ }
22
+ [envelope, cipher]
23
+ end
24
+
25
+ # @return [Cipher] Given an encryption envelope, returns a
26
+ # decryption cipher.
27
+ def decryption_cipher(envelope)
28
+ master_key = @key_provider.key_for(envelope['x-amz-matdesc'])
29
+ key = Utils.decrypt(master_key, decode64(envelope['x-amz-key']))
30
+ iv = decode64(envelope['x-amz-iv'])
31
+ Utils.aes_decryption_cipher(:CBC, key, iv)
32
+ end
33
+
34
+ private
35
+
36
+ def envelope_key(cipher)
37
+ cipher.key = cipher.random_key
38
+ end
39
+
40
+ def envelope_iv(cipher)
41
+ cipher.iv = cipher.random_iv
42
+ end
43
+
44
+ def encrypt(data)
45
+ Utils.encrypt(@key_provider.encryption_materials.key, data)
46
+ end
47
+
48
+ def materials_description
49
+ @key_provider.encryption_materials.description
50
+ end
51
+
52
+ def encode64(str)
53
+ Base64.encode64(str).split("\n") * ""
54
+ end
55
+
56
+ def decode64(str)
57
+ Base64.decode64(str)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,38 @@
1
+ module Aws
2
+ module S3
3
+ module Encryption
4
+
5
+ # The default key provider is constructed with a single key
6
+ # that is used for both encryption and decryption, ignoring
7
+ # the possible per-object envelope encryption materials description.
8
+ # @api private
9
+ class DefaultKeyProvider
10
+
11
+ include KeyProvider
12
+
13
+ # @option options [required, OpenSSL::PKey::RSA, String] :encryption_key
14
+ # The master key to use for encrypting objects.
15
+ # @option options [String<JSON>] :materials_description ('{}')
16
+ # A description of the encryption key.
17
+ def initialize(options = {})
18
+ @encryption_materials = Materials.new(
19
+ key: options[:encryption_key],
20
+ description: options[:materials_description] || '{}'
21
+ )
22
+ end
23
+
24
+ # @return [Materials]
25
+ def encryption_materials
26
+ @encryption_materials
27
+ end
28
+
29
+ # @param [String<JSON>] materials_description
30
+ # @return Returns the key given in the constructor.
31
+ def key_for(materials_description)
32
+ @encryption_materials.key
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,50 @@
1
+ require 'base64'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ # @api private
7
+ class EncryptHandler < Seahorse::Client::Handler
8
+
9
+ def call(context)
10
+ envelope, cipher = context[:encryption][:cipher_provider].encryption_cipher
11
+ apply_encryption_envelope(context, envelope, cipher)
12
+ apply_encryption_cipher(context, cipher)
13
+ @handler.call(context)
14
+ end
15
+
16
+ private
17
+
18
+ def apply_encryption_envelope(context, envelope, cipher)
19
+ context[:encryption][:cipher] = cipher
20
+ if context[:encryption][:envelope_location] == :metadata
21
+ context.params[:metadata] ||= {}
22
+ context.params[:metadata].update(envelope)
23
+ else # :instruction_file
24
+ suffix = context[:encryption][:instruction_file_suffix]
25
+ context.client.put_object(
26
+ bucket: context.params[:bucket],
27
+ key: context.params[:key] + suffix,
28
+ body: Json.dump(envelope)
29
+ )
30
+ end
31
+ end
32
+
33
+ def apply_encryption_cipher(context, cipher)
34
+ io = context.params[:body] || ''
35
+ io = StringIO.new(io) if String === io
36
+ context.params[:body] = IOEncrypter.new(cipher, io)
37
+ context.params[:metadata] ||= {}
38
+ context.params[:metadata]['x-amz-unencrypted-content-length'] = io.size
39
+ if md5 = context.params.delete(:content_md5)
40
+ context.params[:metadata]['x-amz-unencrypted-content-md5'] = md5
41
+ end
42
+ context.http_response.on_headers do
43
+ context.params[:body].close
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,13 @@
1
+ module Aws
2
+ module S3
3
+ module Encryption
4
+ module Errors
5
+
6
+ class DecryptionError < RuntimeError; end
7
+
8
+ class EncryptionError < RuntimeError; end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,56 @@
1
+ module Aws
2
+ module S3
3
+ module Encryption
4
+ # @api private
5
+ class IOAuthDecrypter
6
+
7
+ # @option options [required, IO#write] :io
8
+ # An IO-like object that responds to {#write}.
9
+ # @option options [required, Integer] :encrypted_content_length
10
+ # The number of bytes to decrypt from the `:io` object.
11
+ # This should be the total size of `:io` minus the length of
12
+ # the cipher auth tag.
13
+ # @option options [required, OpenSSL::Cipher] :cipher An initialized
14
+ # cipher that can be used to decrypt the bytes as they are
15
+ # written to the `:io` object. The cipher should already have
16
+ # its `#auth_tag` set.
17
+ def initialize(options = {})
18
+ @decrypter = IODecrypter.new(options[:cipher], options[:io])
19
+ @max_bytes = options[:encrypted_content_length]
20
+ @bytes_written = 0
21
+ end
22
+
23
+ def write(chunk)
24
+ chunk = truncate_chunk(chunk)
25
+ if chunk.bytesize > 0
26
+ @bytes_written += chunk.bytesize
27
+ @decrypter.write(chunk)
28
+ end
29
+ end
30
+
31
+ def finalize
32
+ @decrypter.finalize
33
+ end
34
+
35
+ def io
36
+ @decrypter.io
37
+ end
38
+
39
+ private
40
+
41
+ def truncate_chunk(chunk)
42
+ if chunk.bytesize + @bytes_written <= @max_bytes
43
+ chunk
44
+ elsif @bytes_written < @max_bytes
45
+ chunk[0..(@max_bytes - @bytes_written - 1)]
46
+ else
47
+ # If the tag was sent over after the full body has been read,
48
+ # we don't want to accidentally append it.
49
+ ""
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,29 @@
1
+ module Aws
2
+ module S3
3
+ module Encryption
4
+ # @api private
5
+ class IODecrypter
6
+
7
+ # @param [OpenSSL::Cipher] cipher
8
+ # @param [IO#write] io An IO-like object that responds to `#write`.
9
+ def initialize(cipher, io)
10
+ @cipher = cipher.clone
11
+ @io = io
12
+ end
13
+
14
+ # @return [#write]
15
+ attr_reader :io
16
+
17
+ def write(chunk)
18
+ # decrypt and write
19
+ @io.write(@cipher.update(chunk))
20
+ end
21
+
22
+ def finalize
23
+ @io.write(@cipher.final)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,69 @@
1
+ require 'stringio'
2
+ require 'tempfile'
3
+
4
+ module Aws
5
+ module S3
6
+ module Encryption
7
+
8
+ # Provides an IO wrapper encrpyting a stream of data.
9
+ # It is possible to use this same object for decrypting. You must
10
+ # initialize it with a decryptiion cipher in that case and the
11
+ # IO object must contain cipher text instead of plain text.
12
+ # @api private
13
+ class IOEncrypter
14
+
15
+ # @api private
16
+ ONE_MEGABYTE = 1024 * 1024
17
+
18
+ def initialize(cipher, io)
19
+ @encrypted = io.size <= ONE_MEGABYTE ?
20
+ encrypt_to_stringio(cipher, io.read) :
21
+ encrypt_to_tempfile(cipher, io)
22
+ @size = @encrypted.size
23
+ end
24
+
25
+ # @return [Integer]
26
+ attr_reader :size
27
+
28
+ def read(bytes = nil, output_buffer = nil)
29
+ if Tempfile === @encrypted && @encrypted.closed?
30
+ @encrypted.open
31
+ @encrypted.binmode
32
+ end
33
+ @encrypted.read(bytes, output_buffer)
34
+ end
35
+
36
+ def rewind
37
+ @encrypted.rewind
38
+ end
39
+
40
+ # @api private
41
+ def close
42
+ @encrypted.close if Tempfile === @encrypted
43
+ end
44
+
45
+ private
46
+
47
+ def encrypt_to_stringio(cipher, plain_text)
48
+ if plain_text.empty?
49
+ StringIO.new(cipher.final)
50
+ else
51
+ StringIO.new(cipher.update(plain_text) + cipher.final)
52
+ end
53
+ end
54
+
55
+ def encrypt_to_tempfile(cipher, io)
56
+ encrypted = Tempfile.new(self.object_id.to_s)
57
+ encrypted.binmode
58
+ while chunk = io.read(ONE_MEGABYTE)
59
+ encrypted.write(cipher.update(chunk))
60
+ end
61
+ encrypted.write(cipher.final)
62
+ encrypted.rewind
63
+ encrypted
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end