porky_lib 0.1.5 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9ea558fc6faee130daa48e2d3c5bec7d0803773c
4
- data.tar.gz: 0de735f7b91d041d9522cb37ff19f35df1cdbb3a
3
+ metadata.gz: 46e66e6a979326e1996e6e72466d805b81db819f
4
+ data.tar.gz: 357cec74b604085e0452a2b6ae9ecd94f07b288e
5
5
  SHA512:
6
- metadata.gz: 61a58989c93c0ae920e22dd4c05f1ed8385d51c6702ae862450ae37da5f3c5d62f28ba5b133b078b44283810633e5c6f018e7f8fcc9a532fe1dfa078f1787a7d
7
- data.tar.gz: 41e1b3b123a21340c2005e7183a487f47fe31a3b031095b18682cd342cefae2e71d8c9db07dde98f2da37c5f1f3509a0e2270447732c4ff7b88ee20689489292
6
+ metadata.gz: 3dd9de7ff39b6bba06957262223e244f221c0241ceb816cce9718a0a5d20cb4f5771a69564f563fb60ce5fa4a5cd06162f02f76803d80e8776e1792adad350a9
7
+ data.tar.gz: 3212f9cbd316bc2e4ff635ef357ebe77cca6c6a91989d2d1fc5dd196cb40b2c968a5499a1756084a697431f72ae203da1f8a06057e44e0fffd0ee9862bb7db74
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- porky_lib (0.1.5)
4
+ porky_lib (0.2.0)
5
5
  aws-sdk-kms
6
+ aws-sdk-s3
6
7
  msgpack
7
8
  rbnacl-libsodium
8
9
 
@@ -11,14 +12,18 @@ GEM
11
12
  specs:
12
13
  ast (2.4.0)
13
14
  aws-eventstream (1.0.1)
14
- aws-partitions (1.102.0)
15
- aws-sdk-core (3.25.0)
15
+ aws-partitions (1.104.0)
16
+ aws-sdk-core (3.27.0)
16
17
  aws-eventstream (~> 1.0)
17
18
  aws-partitions (~> 1.0)
18
19
  aws-sigv4 (~> 1.0)
19
20
  jmespath (~> 1.0)
20
- aws-sdk-kms (1.7.0)
21
- aws-sdk-core (~> 3)
21
+ aws-sdk-kms (1.9.0)
22
+ aws-sdk-core (~> 3, >= 3.26.0)
23
+ aws-sigv4 (~> 1.0)
24
+ aws-sdk-s3 (1.19.0)
25
+ aws-sdk-core (~> 3, >= 3.26.0)
26
+ aws-sdk-kms (~> 1)
22
27
  aws-sigv4 (~> 1.0)
23
28
  aws-sigv4 (1.0.3)
24
29
  bundler-audit (0.6.0)
@@ -37,7 +42,7 @@ GEM
37
42
  json (2.1.0)
38
43
  msgpack (1.2.4)
39
44
  parallel (1.12.1)
40
- parser (2.5.1.0)
45
+ parser (2.5.1.2)
41
46
  ast (~> 2.4.0)
42
47
  powerpack (0.1.2)
43
48
  rainbow (3.0.0)
@@ -46,35 +51,35 @@ GEM
46
51
  ffi
47
52
  rbnacl-libsodium (1.0.16)
48
53
  rbnacl (>= 3.0.1)
49
- rspec (3.7.0)
50
- rspec-core (~> 3.7.0)
51
- rspec-expectations (~> 3.7.0)
52
- rspec-mocks (~> 3.7.0)
54
+ rspec (3.8.0)
55
+ rspec-core (~> 3.8.0)
56
+ rspec-expectations (~> 3.8.0)
57
+ rspec-mocks (~> 3.8.0)
53
58
  rspec-collection_matchers (1.1.3)
54
59
  rspec-expectations (>= 2.99.0.beta1)
55
- rspec-core (3.7.1)
56
- rspec-support (~> 3.7.0)
57
- rspec-expectations (3.7.0)
60
+ rspec-core (3.8.0)
61
+ rspec-support (~> 3.8.0)
62
+ rspec-expectations (3.8.1)
58
63
  diff-lcs (>= 1.2.0, < 2.0)
59
- rspec-support (~> 3.7.0)
60
- rspec-mocks (3.7.0)
64
+ rspec-support (~> 3.8.0)
65
+ rspec-mocks (3.8.0)
61
66
  diff-lcs (>= 1.2.0, < 2.0)
62
- rspec-support (~> 3.7.0)
63
- rspec-support (3.7.1)
67
+ rspec-support (~> 3.8.0)
68
+ rspec-support (3.8.0)
64
69
  rspec_junit_formatter (0.4.1)
65
70
  rspec-core (>= 2, < 4, != 2.12.0)
66
- rubocop (0.57.2)
71
+ rubocop (0.59.1)
67
72
  jaro_winkler (~> 1.5.1)
68
73
  parallel (~> 1.10)
69
- parser (>= 2.5)
74
+ parser (>= 2.5, != 2.5.1.1)
70
75
  powerpack (~> 0.1)
71
76
  rainbow (>= 2.2.2, < 4.0)
72
77
  ruby-progressbar (~> 1.7)
73
78
  unicode-display_width (~> 1.0, >= 1.0.1)
74
- rubocop-rspec (1.27.0)
75
- rubocop (>= 0.56.0)
79
+ rubocop-rspec (1.29.1)
80
+ rubocop (>= 0.58.0)
76
81
  rubocop_runner (2.1.0)
77
- ruby-progressbar (1.9.0)
82
+ ruby-progressbar (1.10.0)
78
83
  simplecov (0.16.1)
79
84
  docile (~> 1.1)
80
85
  json (>= 1.8, < 3)
data/README.md CHANGED
@@ -116,6 +116,22 @@ To verify whether an alias exists or not:
116
116
  alias_exists = PorkyLib::Symmetric.instance.cmk_alias_exists?(key_alias)
117
117
  ```
118
118
 
119
+ ### To Read From AWS S3
120
+ ```ruby
121
+ # Where bucket_name is the name of the S3 bucket to read from
122
+ # file_key is file identifier of the file/data that was written to S3.
123
+ file_data = PorkyLib::FileService.read(bucket_name, file_key)
124
+ ```
125
+
126
+ ### To Write To AWS S3
127
+ ```ruby
128
+ # Where file is the data to encrypt and upload to S3 (can be raw data or path to a file on disk)
129
+ # bucket_name is the name of the S3 bucket to write to
130
+ # key_id is the ID of the CMK to use to generate a data encryption key to encrypt the file data
131
+ # options is an optional parameter for specifying optional metadata about the file
132
+ file_key = PorkyLib::FileService.write(file, bucket_name, key_id, options)
133
+ ```
134
+
119
135
  ## Development
120
136
 
121
137
  Development on this project should occur on separate feature branches and pull requests should be submitted. When submitting a
data/lib/porky_lib.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  module PorkyLib
4
4
  require 'porky_lib/config'
5
+ require 'porky_lib/file_service'
5
6
  require 'porky_lib/symmetric'
6
7
  require 'porky_lib/version'
8
+ require 'porky_lib/aws/kms/client'
7
9
  end
@@ -5,12 +5,14 @@ class PorkyLib::Config
5
5
  @aws_key_id = ''
6
6
  @aws_key_secret = ''
7
7
  @aws_client_mock = false
8
+ @max_file_size = 0
8
9
 
9
10
  @config = {
10
11
  aws_region: @aws_region,
11
12
  aws_key_id: @aws_key_id,
12
13
  aws_key_secret: @aws_key_secret,
13
- aws_client_mock: @aws_client_mock
14
+ aws_client_mock: @aws_client_mock,
15
+ max_file_size: @max_file_size
14
16
  }
15
17
 
16
18
  @allowed_config_keys = @config.keys
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sdk-s3'
4
+ require 'singleton'
5
+
6
+ class PorkyLib::FileService
7
+ include Singleton
8
+
9
+ class FileServiceError < StandardError; end
10
+ class FileSizeTooLargeError < StandardError; end
11
+
12
+ def read(bucket_name, file_key, options = {})
13
+ tempfile = Tempfile.new
14
+
15
+ begin
16
+ object = s3.bucket(bucket_name).object(file_key)
17
+ raise FileSizeTooLargeError, "File size is larger than maximum allowed size of #{max_file_size}" if object.content_length > max_size
18
+
19
+ object.download_file(tempfile.path, options)
20
+ rescue Aws::Errors::ServiceError => e
21
+ raise FileServiceError, "Attempt to download a file from S3 failed.\n#{e.message}"
22
+ end
23
+
24
+ decrypt_file_contents(tempfile)
25
+ end
26
+
27
+ def write(file, bucket_name, key_id, options = {})
28
+ raise FileServiceError, 'Invalid input. One or more input values is nil' if input_invalid?(file, bucket_name, key_id)
29
+ raise FileSizeTooLargeError, "File size is larger than maximum allowed size of #{max_file_size}" if file_size_invalid?(file)
30
+
31
+ data = file_data(file)
32
+ file_key = SecureRandom.uuid
33
+ tempfile = encrypt_file_contents(data, key_id, file_key)
34
+
35
+ begin
36
+ perform_upload(bucket_name, file_key, tempfile, options)
37
+ rescue Aws::Errors::ServiceError => e
38
+ raise FileServiceError, "Attempt to upload a file to S3 failed.\n#{e.message}"
39
+ end
40
+
41
+ # Remove tempfile from disk
42
+ tempfile.unlink
43
+ file_key
44
+ end
45
+
46
+ private
47
+
48
+ def input_invalid?(file, bucket_name, key_id)
49
+ file.nil? || bucket_name.nil? || key_id.nil?
50
+ end
51
+
52
+ def file_size_invalid?(file)
53
+ file.bytesize > max_size || (File.file?(file) && File.size(file) > max_size)
54
+ end
55
+
56
+ def file_data(file)
57
+ File.file?(file) ? File.read(file) : file
58
+ end
59
+
60
+ def max_size
61
+ PorkyLib::Config.config[:max_file_size]
62
+ end
63
+
64
+ def max_file_size
65
+ {
66
+ B: 1024,
67
+ KB: 1024 * 1024,
68
+ MB: 1024 * 1024 * 1024,
69
+ GB: 1024 * 1024 * 1024 * 1024
70
+ }.each_pair { |symbol, bytes| return "#{(max_size.to_f / (bytes / 1024)).round(2)}#{symbol}" if max_size < bytes }
71
+ end
72
+
73
+ def perform_upload(bucket_name, file_key, tempfile, options)
74
+ obj = s3.bucket(bucket_name).object(file_key)
75
+ if options.key?(:metadata)
76
+ obj.upload_file(tempfile.path, options[:metadata])
77
+ else
78
+ obj.upload_file(tempfile.path)
79
+ end
80
+ end
81
+
82
+ def decrypt_file_contents(tempfile)
83
+ file_contents = tempfile.read
84
+
85
+ # Remove tempfile from disk
86
+ tempfile.unlink
87
+
88
+ ciphertext_data = JSON.parse(file_contents, symbolize_names: true)
89
+ ciphertext_key = Base64.urlsafe_decode64(ciphertext_data[:key])
90
+ ciphertext = Base64.urlsafe_decode64(ciphertext_data[:data])
91
+ nonce = Base64.urlsafe_decode64(ciphertext_data[:nonce])
92
+
93
+ PorkyLib::Symmetric.instance.decrypt(ciphertext_key, ciphertext, nonce)
94
+ end
95
+
96
+ def encrypt_file_contents(file, key_id, file_key)
97
+ ciphertext_key, ciphertext, nonce = PorkyLib::Symmetric.instance.encrypt(file, key_id)
98
+
99
+ file_contents = {
100
+ key: Base64.urlsafe_encode64(ciphertext_key),
101
+ data: Base64.urlsafe_encode64(ciphertext),
102
+ nonce: Base64.urlsafe_encode64(nonce)
103
+ }.to_json
104
+
105
+ write_tempfile(file_contents, file_key)
106
+ end
107
+
108
+ def write_tempfile(file_contents, file_key)
109
+ tempfile = Tempfile.new(file_key)
110
+ tempfile << file_contents
111
+ tempfile.close
112
+
113
+ tempfile
114
+ end
115
+
116
+ def s3
117
+ @s3 ||= Aws::S3::Resource.new
118
+ end
119
+ end
@@ -56,6 +56,7 @@ class PorkyLib::Symmetric
56
56
 
57
57
  def decrypt_data_encryption_key(ciphertext_key, encryption_context = nil)
58
58
  return client.decrypt(ciphertext_blob: ciphertext_key, encryption_context: encryption_context).to_h[:plaintext] if encryption_context
59
+
59
60
  client.decrypt(ciphertext_blob: ciphertext_key).to_h[:plaintext]
60
61
  end
61
62
 
@@ -94,8 +95,7 @@ class PorkyLib::Symmetric
94
95
  # Securely delete the plaintext value from memory
95
96
  plaintext_key.replace(secure_delete_plaintext_key(plaintext_key.bytesize))
96
97
 
97
- result = secret_box.decrypt(nonce, ciphertext)
98
- result
98
+ secret_box.decrypt(nonce, ciphertext)
99
99
  end
100
100
 
101
101
  def secure_delete_plaintext_key(length)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PorkyLib
4
- VERSION = "0.1.5"
4
+ VERSION = "0.2.0"
5
5
  end
data/porky_lib.gemspec CHANGED
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency 'timecop'
37
37
 
38
38
  spec.add_dependency 'aws-sdk-kms'
39
+ spec.add_dependency 'aws-sdk-s3'
39
40
  spec.add_dependency 'msgpack'
40
41
  spec.add_dependency 'rbnacl-libsodium'
41
42
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: porky_lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Fletcher
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-30 00:00:00.000000000 Z
11
+ date: 2018-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -220,6 +220,20 @@ dependencies:
220
220
  - - ">="
221
221
  - !ruby/object:Gem::Version
222
222
  version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: aws-sdk-s3
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :runtime
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
223
237
  - !ruby/object:Gem::Dependency
224
238
  name: msgpack
225
239
  requirement: !ruby/object:Gem::Requirement
@@ -269,6 +283,7 @@ files:
269
283
  - lib/porky_lib.rb
270
284
  - lib/porky_lib/aws/kms/client.rb
271
285
  - lib/porky_lib/config.rb
286
+ - lib/porky_lib/file_service.rb
272
287
  - lib/porky_lib/symmetric.rb
273
288
  - lib/porky_lib/version.rb
274
289
  - porky_lib.gemspec