porky_lib 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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