porky_lib 0.9.2 → 0.11.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
  SHA256:
3
- metadata.gz: fd1dfb255c5b91f06fe66af135ef84ef65fcb96bd02ad5150090619460bd7d86
4
- data.tar.gz: 5edbf7d85795d8aaff798d486c043464199c03542c924f297559835796b9d829
3
+ metadata.gz: 70a7962acaef46a1b1e4cd9e1decaccbe5bb35867de03bbc8c4d24329f0ef4be
4
+ data.tar.gz: 81cbd165551dcafd6fb595e24a74af691143858bb3d8c9f70c4d8d1cb3d0a580
5
5
  SHA512:
6
- metadata.gz: bc55b6280bce9d76a9d6553e239487f14712ba535fdf83c54006e6cd27c67e8babda30e58632fbf1d76f531bc8c822b96d34a1e0a5209cf4098f436567bd951d
7
- data.tar.gz: 3e428d8b741ba820b43ef1dc891dd0cd2b3ae81d9b2ba0d578705c84296ade1c4b510eb69e706c8d5869e4153884f1af2a5cd1a22fed09e226a9d694134b6035
6
+ metadata.gz: f5fbc041473d83b4f4ad03321d3f30ba40990c139f215dc98a388c99999cb747415287a2c822bedb6f3dc2f71d37f1ceb72017ada14b65aba88584555ff448fb
7
+ data.tar.gz: 292725e636df669d3022edb4cd7f566b1b472b60dca17c1e8db1351c107ad06799e4b4b805425a5b58282b9c50bab57519033cf37e39286a3d71098474a5d0f3
@@ -3,10 +3,8 @@ jobs:
3
3
  build:
4
4
  parallelism: 1
5
5
  working_directory: ~/porky_lib
6
- environment:
7
- - CODECOV_TOKEN: 4d03ca51-5054-4858-87c7-b37c376ffb6a
8
6
  docker:
9
- - image: circleci/ruby:2.6.5
7
+ - image: circleci/ruby:2.7.1
10
8
 
11
9
  steps:
12
10
  - checkout
@@ -9,7 +9,8 @@ AllCops:
9
9
  - node_modules/**/*
10
10
  - output/**/*
11
11
  - vendor/**/*
12
- TargetRubyVersion: 2.3.7
12
+ - spec/porky_lib/data/*
13
+ TargetRubyVersion: 2.7.1
13
14
  RSpec:
14
15
  Patterns:
15
16
  - _spec.rb
@@ -17,10 +18,22 @@ AllCops:
17
18
  SpecTypes:
18
19
  Feature: 'spec/features/**/*'
19
20
 
20
- Documentation:
21
+ Style/Documentation:
21
22
  Enabled: false
22
23
 
23
- Metrics/LineLength:
24
+ Security/MarshalLoad:
25
+ Enabled: true
26
+
27
+ Security/Eval:
28
+ Enabled: true
29
+
30
+ Security/YAMLLoad:
31
+ Enabled: true
32
+
33
+ Security/JSONLoad:
34
+ Enabled: true
35
+
36
+ Layout/LineLength:
24
37
  Max: 160
25
38
 
26
39
  Metrics/ClassLength:
@@ -46,3 +59,45 @@ RSpec/MultipleExpectations:
46
59
 
47
60
  RSpec/ExampleLength:
48
61
  Max: 10
62
+
63
+ Style/HashEachMethods:
64
+ Enabled: true
65
+
66
+ Style/HashTransformKeys:
67
+ Enabled: true
68
+
69
+ Style/HashTransformValues:
70
+ Enabled: true
71
+
72
+ Lint/RaiseException:
73
+ Enabled: true
74
+
75
+ Lint/StructNewOverride:
76
+ Enabled: true
77
+
78
+ Layout/SpaceAroundMethodCallOperator:
79
+ Enabled: true
80
+
81
+ Style/ExponentialNotation:
82
+ Enabled: true
83
+
84
+ Layout/EmptyLinesAroundAttributeAccessor:
85
+ Enabled: true
86
+
87
+ Lint/DeprecatedOpenSSLConstant:
88
+ Enabled: true
89
+
90
+ Style/SlicingWithRange:
91
+ Enabled: true
92
+
93
+ Lint/MixedRegexpCaptureTypes:
94
+ Enabled: true
95
+
96
+ Style/RedundantRegexpCharacterClass:
97
+ Enabled: true
98
+
99
+ Style/RedundantRegexpEscape:
100
+ Enabled: true
101
+
102
+ Style/RedundantFetchBlock:
103
+ Enabled: true
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- ruby '2.6.5'
3
+ ruby '2.7.1'
4
4
 
5
5
  source "https://rubygems.org"
6
6
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- porky_lib (0.9.2)
4
+ porky_lib (0.11.0)
5
5
  aws-sdk-kms
6
6
  aws-sdk-s3
7
7
  msgpack
@@ -11,85 +11,91 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- ast (2.4.0)
15
- aws-eventstream (1.0.3)
16
- aws-partitions (1.228.0)
17
- aws-sdk-core (3.72.0)
18
- aws-eventstream (~> 1.0, >= 1.0.2)
19
- aws-partitions (~> 1, >= 1.228.0)
14
+ ast (2.4.1)
15
+ aws-eventstream (1.1.0)
16
+ aws-partitions (1.338.0)
17
+ aws-sdk-core (3.103.0)
18
+ aws-eventstream (~> 1, >= 1.0.2)
19
+ aws-partitions (~> 1, >= 1.239.0)
20
20
  aws-sigv4 (~> 1.1)
21
21
  jmespath (~> 1.0)
22
- aws-sdk-kms (1.25.0)
23
- aws-sdk-core (~> 3, >= 3.71.0)
22
+ aws-sdk-kms (1.36.0)
23
+ aws-sdk-core (~> 3, >= 3.99.0)
24
24
  aws-sigv4 (~> 1.1)
25
- aws-sdk-s3 (1.51.0)
26
- aws-sdk-core (~> 3, >= 3.71.0)
25
+ aws-sdk-s3 (1.73.0)
26
+ aws-sdk-core (~> 3, >= 3.102.1)
27
27
  aws-sdk-kms (~> 1)
28
28
  aws-sigv4 (~> 1.1)
29
- aws-sigv4 (1.1.0)
30
- aws-eventstream (~> 1.0, >= 1.0.2)
31
- bundler-audit (0.6.1)
29
+ aws-sigv4 (1.2.1)
30
+ aws-eventstream (~> 1, >= 1.0.2)
31
+ bundler-audit (0.7.0.1)
32
32
  bundler (>= 1.2.0, < 3)
33
- thor (~> 0.18)
34
- byebug (11.0.1)
35
- codecov (0.1.15)
33
+ thor (>= 0.18, < 2)
34
+ byebug (11.1.3)
35
+ codacy-coverage (2.2.1)
36
+ simplecov
37
+ codecov (0.1.17)
36
38
  json
37
39
  simplecov
38
40
  url
39
41
  diff-lcs (1.3)
40
42
  docile (1.3.2)
41
- ffi (1.10.0)
42
- jaro_winkler (1.5.3)
43
+ ffi (1.12.2)
43
44
  jmespath (1.4.0)
44
- json (2.2.0)
45
- msgpack (1.3.1)
46
- parallel (1.18.0)
47
- parser (2.6.5.0)
48
- ast (~> 2.4.0)
45
+ json (2.3.0)
46
+ msgpack (1.3.3)
47
+ parallel (1.19.2)
48
+ parser (2.7.1.4)
49
+ ast (~> 2.4.1)
49
50
  rainbow (3.0.0)
50
- rake (13.0.0)
51
+ rake (13.0.1)
51
52
  rbnacl (5.0.0)
52
53
  ffi
53
54
  rbnacl-libsodium (1.0.16)
54
55
  rbnacl (>= 3.0.1)
56
+ regexp_parser (1.7.1)
57
+ rexml (3.2.4)
55
58
  rspec (3.9.0)
56
59
  rspec-core (~> 3.9.0)
57
60
  rspec-expectations (~> 3.9.0)
58
61
  rspec-mocks (~> 3.9.0)
59
62
  rspec-collection_matchers (1.2.0)
60
63
  rspec-expectations (>= 2.99.0.beta1)
61
- rspec-core (3.9.0)
62
- rspec-support (~> 3.9.0)
64
+ rspec-core (3.9.2)
65
+ rspec-support (~> 3.9.3)
63
66
  rspec-expectations (3.9.0)
64
67
  diff-lcs (>= 1.2.0, < 2.0)
65
68
  rspec-support (~> 3.9.0)
66
- rspec-mocks (3.9.0)
69
+ rspec-mocks (3.9.1)
67
70
  diff-lcs (>= 1.2.0, < 2.0)
68
71
  rspec-support (~> 3.9.0)
69
- rspec-support (3.9.0)
72
+ rspec-support (3.9.3)
70
73
  rspec_junit_formatter (0.4.1)
71
74
  rspec-core (>= 2, < 4, != 2.12.0)
72
- rubocop (0.75.1)
73
- jaro_winkler (~> 1.5.1)
75
+ rubocop (0.86.0)
74
76
  parallel (~> 1.10)
75
- parser (>= 2.6)
77
+ parser (>= 2.7.0.1)
76
78
  rainbow (>= 2.2.2, < 4.0)
79
+ regexp_parser (>= 1.7)
80
+ rexml
81
+ rubocop-ast (>= 0.0.3, < 1.0)
77
82
  ruby-progressbar (~> 1.7)
78
- unicode-display_width (>= 1.4.0, < 1.7)
79
- rubocop-performance (1.5.0)
83
+ unicode-display_width (>= 1.4.0, < 2.0)
84
+ rubocop-ast (0.0.3)
85
+ parser (>= 2.7.0.1)
86
+ rubocop-performance (1.6.1)
80
87
  rubocop (>= 0.71.0)
81
- rubocop-rspec (1.36.0)
88
+ rubocop-rspec (1.40.0)
82
89
  rubocop (>= 0.68.1)
83
90
  rubocop_runner (2.2.0)
84
91
  ruby-progressbar (1.10.1)
85
- simplecov (0.17.1)
92
+ simplecov (0.18.5)
86
93
  docile (~> 1.1)
87
- json (>= 1.8, < 3)
88
- simplecov-html (~> 0.10.0)
89
- simplecov-html (0.10.2)
90
- thor (0.20.3)
94
+ simplecov-html (~> 0.11)
95
+ simplecov-html (0.12.2)
96
+ thor (1.0.1)
91
97
  timecop (0.9.1)
92
- unicode-display_width (1.6.0)
98
+ unicode-display_width (1.7.0)
93
99
  url (0.3.2)
94
100
 
95
101
  PLATFORMS
@@ -101,6 +107,7 @@ DEPENDENCIES
101
107
  bundler
102
108
  bundler-audit
103
109
  byebug
110
+ codacy-coverage
104
111
  codecov
105
112
  msgpack
106
113
  porky_lib!
@@ -119,7 +126,7 @@ DEPENDENCIES
119
126
  timecop
120
127
 
121
128
  RUBY VERSION
122
- ruby 2.6.5p114
129
+ ruby 2.7.1p83
123
130
 
124
131
  BUNDLED WITH
125
- 2.0.2
132
+ 2.1.4
data/README.md CHANGED
@@ -20,7 +20,7 @@ And then execute:
20
20
  Or install it yourself as:
21
21
 
22
22
  $ gem install porky_lib
23
-
23
+
24
24
  Inside of your Ruby program do:
25
25
 
26
26
  ```ruby
@@ -86,7 +86,7 @@ To encrypt data with a known plaintext key:
86
86
  # plaintext_key is the encryption key to use
87
87
  # encryption_info is the structure returned that contains:
88
88
  # ciphertext: plaintext encrypted under plaintext_key
89
- # nonce: The generated nonce
89
+ # nonce: The generated nonce
90
90
  encryption_info = PorkyLib::Symmetric.instance.encrypt_with_key(plaintext, plaintext_key)
91
91
  ```
92
92
 
@@ -104,7 +104,7 @@ To decrypt data with a known plaintext key:
104
104
  ```ruby
105
105
  # Where ciphertext is the encrypted data to be decrypted
106
106
  # plaintext_key is the decryption key to use
107
- # nonce is the nonce to use
107
+ # nonce is the nonce to use
108
108
  # decryption_info is the structured returned that contains:
109
109
  # plaintext: ciphertext decrypted under plaintext_key
110
110
  decryption_info = PorkyLib::Symmetric.instance.decrypt_with_key(ciphertext, plaintext_key, nonce)
@@ -137,7 +137,7 @@ plaintext_key.replace(PorkyLib::Symmetric.instance.secure_delete_plaintext_key(p
137
137
  To verify whether an alias exists or not:
138
138
  ```ruby
139
139
  # Where key_alias is the alias name to verify
140
- alias_exists = PorkyLib::Symmetric.instance.cmk_alias_exists?(key_alias)
140
+ alias_exists = PorkyLib::Symmetric.instance.cmk_alias_exists?(key_alias)
141
141
  ```
142
142
 
143
143
  ### To Read From AWS S3
@@ -156,21 +156,57 @@ file_data = PorkyLib::Unencrypted::FileService.read(bucket_name, file_key)
156
156
 
157
157
  ### To Write To AWS S3
158
158
  ```ruby
159
- # Where file is the data to encrypt and upload to S3 (can be raw data or ruby file object)
159
+ # --- DEPRECATED --- Please use write_data or write_file instead of write
160
+ # Where file is the data to encrypt and upload to S3 (can be a path or raw data or ruby file object)
160
161
  # bucket_name is the name of the S3 bucket to write to
161
162
  # key_id is the ID of the CMK to use to generate a data encryption key to encrypt the file data
162
- # options is an optional parameter for specifying optional metadata about the file
163
+ # options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
163
164
  file_key = PorkyLib::FileService.write(file, bucket_name, key_id, options)
164
165
  ```
165
166
 
166
- ### To Write Unencrypted Files To AWS S3
167
+ ### To Write Files To AWS S3
167
168
  ```ruby
168
- # Where file is the data to upload to S3 (can be raw data or ruby file object)
169
+ # Where file is the data to encrypt and upload to S3 (can be a path or ruby file object)
169
170
  # bucket_name is the name of the S3 bucket to write to
170
- # options is an optional parameter for specifying optional metadata about the file
171
+ # key_id is the ID of the CMK to use to generate a data encryption key to encrypt the file data
172
+ # options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
173
+ file_key = PorkyLib::FileService.write_file(file, bucket_name, key_id, options)
174
+ ```
175
+
176
+ ### To Write Data To AWS S3
177
+ ```ruby
178
+ # Where data is the raw data to encrypt and upload to S3
179
+ # bucket_name is the name of the S3 bucket to write to
180
+ # key_id is the ID of the CMK to use to generate a data encryption key to encrypt the file data
181
+ # options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
182
+ file_key = PorkyLib::FileService.write_data(data, bucket_name, key_id, options)
183
+ ```
184
+
185
+ ### To Write Unencrypted To AWS S3
186
+ ```ruby
187
+ # --- DEPRECATED --- Please use write_data or write_file instead of write
188
+ # Where file is the data to upload to S3 (can be a path or raw data or ruby file object)
189
+ # bucket_name is the name of the S3 bucket to write to
190
+ # options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
171
191
  file_key = PorkyLib::Unencrypted::FileService.write(file, bucket_name, options)
172
192
  ```
173
193
 
194
+ ### To Write Unencrypted Files To AWS S3
195
+ ```ruby
196
+ # Where file is the data to encrypt and upload to S3 (can be a path or ruby file object)
197
+ # bucket_name is the name of the S3 bucket to write to
198
+ # options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
199
+ file_key = PorkyLib::Unencrypted::FileService.write_file(file, bucket_name, options)
200
+ ```
201
+
202
+ ### To Write Unencrypted Data To AWS S3
203
+ ```ruby
204
+ # Where data is the raw data to encrypt and upload to S3
205
+ # bucket_name is the name of the S3 bucket to write to
206
+ # options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
207
+ file_key = PorkyLib::Unencrypted::FileService.write_data(data, bucket_name, options)
208
+ ```
209
+
174
210
  ### Generate S3 Presigned POST URL
175
211
  To generate a new presigned POST url (used to upload files directly to AWS S3):
176
212
  ```ruby
@@ -188,12 +224,84 @@ To generate a new presigned GET url (used to download files directly from AWS S3
188
224
  url = PorkyLib::Symmetric.instance.presigned_get_url(bucket_name, file_key)
189
225
  ```
190
226
 
227
+ ## Rake task
228
+ If you want to write or read an encrypted file from the command line, there is a Rake write and read task.
229
+
230
+ > Note: the environment variables can be set globally or by prepending them to the rake task command
231
+
232
+ ### Write file
233
+
234
+ Rake task name: `file:write`
235
+
236
+ Environment variables:
237
+ * Required
238
+ * `FILE_PATH` - Absolute or relative file path
239
+ * `CMK_KEY_ID` - Alias of the CMK key
240
+ * `AWS_S3_BUCKET` - AWS S3 bucket name
241
+ * `AWS_REGION` - AWS region name
242
+ * `AWS_ACCESS_KEY_ID` - AWS access key ID (credentials)
243
+ * `AWS_ACCESS_KEY` - AWS secret access key (credentials)
244
+ * Optional
245
+ * `AWS_S3_MOCK_CLIENT` - PorkyLib's AWS KMS mock client (defaults to `true`)
246
+ * `AWS_S3_MAX_FILE_SIZE` - Max file size (defaults to `1MB`)
247
+ * `AWS_S3_STORAGE_CLASS` - One of STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE (defaults to `STANDARD`)
248
+ * `AWS_S3_KEEP_FILE_NAME` - Saves the file in AWS S3 with the original file name (defaults to `true`)
249
+
250
+ ### Read file
251
+
252
+ Rake task name: `file:read`
253
+
254
+ Environment variables:
255
+ * Required
256
+ * `FILE_KEY` - AWS S3 object file key
257
+ * `AWS_S3_BUCKET` - AWS S3 bucket name
258
+ * `AWS_REGION` - AWS region name
259
+ * `AWS_ACCESS_KEY_ID` - AWS access key ID (credentials)
260
+ * `AWS_ACCESS_KEY` - AWS secret access key (credentials)
261
+ * Optional
262
+ * `AWS_S3_MOCK_CLIENT` - PorkyLib's AWS KMS mock client (defaults to `true`)
263
+ * `AWS_S3_MAX_FILE_SIZE` - Max file size (defaults to `1MB`)
264
+ * `DESTINATION` - Location to save the file (defaults to `FILE_KEY`)
265
+
191
266
  ## Development
192
267
 
193
268
  Development on this project should occur on separate feature branches and pull requests should be submitted. When submitting a
194
269
  pull request, the pull request comment template should be filled out as much as possible to ensure a quick review and increase
195
270
  the likelihood of the pull request being accepted.
196
271
 
272
+ ### Ruby
273
+
274
+ This application requires:
275
+
276
+ * Ruby version: 2.7.1
277
+
278
+ Ruby 2.7.1 and greater requires OpenSSL 1.1+. To link to Homebrew's upgraded version of OpenSSL, add the following to your bash profile
279
+
280
+ ```shell script
281
+ export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl@1.1)"
282
+ ```
283
+
284
+ If you do not have Ruby installed, it is recommended you use ruby-install and chruby to manage Ruby versions.
285
+
286
+ ```bash
287
+ brew install ruby-install chruby
288
+ ruby-install ruby 2.7.1
289
+ ```
290
+
291
+ Add the following lines to ~/.bash_profile:
292
+
293
+ ```bash
294
+ source /usr/local/opt/chruby/share/chruby/chruby.sh
295
+ source /usr/local/opt/chruby/share/chruby/auto.sh
296
+ ```
297
+
298
+ Set Ruby version to 2.7.1:
299
+
300
+ ```bash
301
+ source ~/.bash_profile
302
+ chruby 2.7.1
303
+ ```
304
+
197
305
  ### Running Tests
198
306
 
199
307
  ```ruby
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
+ import 'lib/tasks/file.rake'
5
6
 
6
7
  RSpec::Core::RakeTask.new(:spec)
7
8
 
@@ -72,7 +72,7 @@ class Aws::KMS::Client
72
72
 
73
73
  def decrypt(ciphertext_blob:, encryption_context: nil)
74
74
  key_id, decoded_context, plaintext = MessagePack.unpack(ciphertext_blob.reverse)
75
- decoded_context = Hash[decoded_context.map { |k, v| [k.to_sym, v] }] if decoded_context
75
+ decoded_context = decoded_context.transform_keys(&:to_sym) if decoded_context
76
76
  raise Aws::KMS::Errors::InvalidCiphertextException.new(nil, nil) unless decoded_context == encryption_context
77
77
 
78
78
  Aws::KMS::Types::DecryptResponse.new(
@@ -3,6 +3,8 @@
3
3
  require 'singleton'
4
4
 
5
5
  class PorkyLib::FileService
6
+ extend Gem::Deprecate
7
+
6
8
  include Singleton
7
9
  include PorkyLib::FileServiceHelper
8
10
 
@@ -56,10 +58,30 @@ class PorkyLib::FileService
56
58
 
57
59
  def write(file, bucket_name, key_id, options = {})
58
60
  raise FileServiceError, 'Invalid input. One or more input values is nil' if input_invalid?(file, bucket_name, key_id)
59
- raise FileSizeTooLargeError, "File size is larger than maximum allowed size of #{max_file_size}" if file_size_invalid?(file)
60
61
 
61
- data = file_data(file)
62
- file_key = generate_file_key(options)
62
+ if file?(file)
63
+ write_file(file, bucket_name, key_id, options)
64
+ else
65
+ write_data(file, bucket_name, key_id, options)
66
+ end
67
+ end
68
+ deprecate :write, 'write_file or write_data', 2020, 1
69
+
70
+ def write_file(file, bucket_name, key_id, options = {})
71
+ raise FileServiceError, 'Invalid input. One or more input values is nil' if input_invalid?(file, bucket_name, key_id)
72
+
73
+ write_data(read_file(file), bucket_name, key_id, options)
74
+ end
75
+
76
+ def write_data(data, bucket_name, key_id, options = {})
77
+ raise FileServiceError, 'Invalid input. One or more input values is nil' if input_invalid?(data, bucket_name, key_id)
78
+ raise FileSizeTooLargeError, "Data size is larger than maximum allowed size of #{max_file_size}" if data_size_invalid?(data)
79
+
80
+ file_key = if options.key?(:file_name)
81
+ options[:file_name]
82
+ else
83
+ generate_file_key(options)
84
+ end
63
85
  tempfile = encrypt_file_contents(data, key_id, file_key, options)
64
86
 
65
87
  begin
@@ -73,12 +95,12 @@ class PorkyLib::FileService
73
95
  file_key
74
96
  end
75
97
 
76
- def overwrite_file(file, file_key, bucket_name, key_id, options = {})
77
- raise FileServiceError, 'Invalid input. One or more input values is nil' if input_invalid?(file, bucket_name, key_id)
98
+ def overwrite_file(data, file_key, bucket_name, key_id, options = {})
99
+ raise FileServiceError, 'Invalid input. One or more input values is nil' if overwrite_input_invalid?(data, file_key, bucket_name, key_id)
78
100
  raise FileServiceError, 'Invalid input. file_key cannot be nil if overwriting an existing file' if file_key.nil?
79
- raise FileSizeTooLargeError, "File size is larger than maximum allowed size of #{max_file_size}" if file_size_invalid?(file)
80
101
 
81
- data = file_data(file)
102
+ raise FileSizeTooLargeError, "File size is larger than maximum allowed size of #{max_file_size}" if data_size_invalid?(data)
103
+
82
104
  tempfile = encrypt_file_contents(data, key_id, file_key, options)
83
105
 
84
106
  begin
@@ -158,7 +180,11 @@ class PorkyLib::FileService
158
180
  @s3_client ||= Aws::S3::Client.new
159
181
  end
160
182
 
161
- def input_invalid?(file, bucket_name, key_id)
162
- file.nil? || bucket_name.nil? || key_id.nil?
183
+ def input_invalid?(file_or_data, bucket_name, key_id)
184
+ file_or_data.nil? || bucket_name.nil? || key_id.nil?
185
+ end
186
+
187
+ def overwrite_input_invalid?(file_or_data, file_key, bucket_name, key_id)
188
+ file_or_data.nil? || file_key.nil? || bucket_name.nil? || key_id.nil?
163
189
  end
164
190
  end
@@ -3,17 +3,18 @@
3
3
  require 'aws-sdk-s3'
4
4
 
5
5
  module PorkyLib::FileServiceHelper
6
- def file_size_invalid?(file_or_content)
7
- if a_file?(file_or_content)
8
- File.size(file_or_content) > max_size
9
- else
10
- file_or_content.bytesize > max_size
11
- end
6
+ extend Gem::Deprecate
7
+
8
+ class FileServiceError < StandardError; end
9
+
10
+ def data_size_invalid?(data)
11
+ data.bytesize > max_size
12
12
  end
13
13
 
14
- def file_data(file_or_content)
15
- a_file?(file_or_content) ? File.read(file_or_content) : file_or_content
14
+ def file?(file_or_content)
15
+ a_file?(file_or_content) || a_path?(file_or_content)
16
16
  end
17
+ deprecate :file?, :none, 2020, 1
17
18
 
18
19
  def write_tempfile(file_contents, file_key)
19
20
  tempfile = Tempfile.new(file_key)
@@ -23,13 +24,25 @@ module PorkyLib::FileServiceHelper
23
24
  tempfile
24
25
  end
25
26
 
27
+ def read_file(file)
28
+ raise FileServiceError, 'file cannot be nil' if file.nil?
29
+ return file if !a_file?(file) && contain_null_byte?(file)
30
+ raise FileServiceError, 'The specified file does not exist' unless File.file?(file)
31
+
32
+ File.read(file)
33
+ rescue Errno::EACCES
34
+ raise FileServiceError, 'The specified file cannot be read, no permissions'
35
+ end
36
+
26
37
  def perform_upload(bucket_name, file_key, tempfile, options)
27
38
  obj = s3.bucket(bucket_name).object(file_key)
28
- if options.key?(:metadata)
29
- obj.upload_file(tempfile.path, metadata: options[:metadata])
30
- else
31
- obj.upload_file(tempfile.path)
32
- end
39
+
40
+ upload_options = {
41
+ metadata: (options[:metadata] if options.key?(:metadata)),
42
+ storage_class: (options[:storage_class] if options.key?(:storage_class))
43
+ }.compact
44
+
45
+ obj.upload_file(tempfile.path, upload_options)
33
46
  end
34
47
 
35
48
  def s3
@@ -58,4 +71,17 @@ module PorkyLib::FileServiceHelper
58
71
  def a_file?(file_or_content)
59
72
  !file_or_content.is_a?(String)
60
73
  end
74
+
75
+ def a_path?(content_or_path)
76
+ return false if contain_null_byte?(content_or_path)
77
+
78
+ File.file?(content_or_path)
79
+ end
80
+
81
+ def contain_null_byte?(data)
82
+ null_byte = (+"\u0000").force_encoding("ASCII-8BIT")
83
+ data = (+data).force_encoding("ASCII-8BIT")
84
+
85
+ data.include?(null_byte)
86
+ end
61
87
  end
@@ -3,6 +3,8 @@
3
3
  require 'singleton'
4
4
 
5
5
  class PorkyLib::Unencrypted::FileService
6
+ extend Gem::Deprecate
7
+
6
8
  include Singleton
7
9
  include PorkyLib::FileServiceHelper
8
10
 
@@ -26,10 +28,31 @@ class PorkyLib::Unencrypted::FileService
26
28
 
27
29
  def write(file, bucket_name, options = {})
28
30
  raise FileServiceError, 'Invalid input. One or more input values is nil' if input_invalid?(file, bucket_name)
29
- raise FileSizeTooLargeError, "File size is larger than maximum allowed size of #{max_file_size}" if file_size_invalid?(file)
30
31
 
31
- file_key = generate_file_key(options)
32
- tempfile = write_tempfile(file_data(file), file_key)
32
+ if file?(file)
33
+ write_file(file, bucket_name, options)
34
+ else
35
+ write_data(file, bucket_name, options)
36
+ end
37
+ end
38
+ deprecate :write, 'write_file or write_data', 2020, 1
39
+
40
+ def write_file(file, bucket_name, options = {})
41
+ raise FileServiceError, 'Invalid input. One or more input values is nil' if input_invalid?(file, bucket_name)
42
+
43
+ write_data(read_file(file), bucket_name, options)
44
+ end
45
+
46
+ def write_data(data, bucket_name, options = {})
47
+ raise FileServiceError, 'Invalid input. One or more input values is nil' if input_invalid?(data, bucket_name)
48
+ raise FileSizeTooLargeError, "Data size is larger than maximum allowed size of #{max_file_size}" if data_size_invalid?(data)
49
+
50
+ file_key = if options.key?(:file_name)
51
+ options[:file_name]
52
+ else
53
+ generate_file_key(options)
54
+ end
55
+ tempfile = write_tempfile(data, file_key)
33
56
 
34
57
  begin
35
58
  perform_upload(bucket_name, file_key, tempfile, options)
@@ -42,7 +65,7 @@ class PorkyLib::Unencrypted::FileService
42
65
 
43
66
  private
44
67
 
45
- def input_invalid?(file, bucket_name)
46
- file.nil? || bucket_name.nil?
68
+ def input_invalid?(file_or_data, bucket_name)
69
+ file_or_data.nil? || bucket_name.nil?
47
70
  end
48
71
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PorkyLib
4
- VERSION = "0.9.2"
4
+ VERSION = "0.11.0"
5
5
  end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'porky_lib'
4
+
5
+ namespace :file do
6
+ desc "Read a file from AWS S3"
7
+ task :read do
8
+ # Optional arguments
9
+ use_mock_client = ENV.fetch('AWS_S3_MOCK_CLIENT', 'true') == 'true'
10
+ max_file_size = ENV.fetch('AWS_S3_MAX_FILE_SIZE', 1_048_576).to_i
11
+ destination = ENV.fetch('DESTINATION', ENV['FILE_KEY'])
12
+
13
+ # Required arguments
14
+ arguments = {
15
+ file_key: ENV['FILE_KEY'],
16
+ aws_s3_bucket: ENV['AWS_S3_BUCKET'],
17
+ aws_region: ENV['AWS_REGION'],
18
+ aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
19
+ aws_access_key: ENV['AWS_ACCESS_KEY']
20
+ }
21
+
22
+ # Checks presence of required arguments and configures porky_lib
23
+ check_arguments(arguments)
24
+ setup_porky_lib(arguments, use_mock_client, max_file_size)
25
+
26
+ # Reads and writes the file
27
+ message, = PorkyLib::FileService.instance.read(arguments[:aws_s3_bucket], arguments[:file_key])
28
+ file = File.open(destination, 'w')
29
+ file.puts(message)
30
+ file.close
31
+
32
+ puts "SUCCESS - Saved file: '#{destination}' with content of the bucket: '#{arguments[:aws_s3_bucket]}' with file_key: '#{arguments[:file_key]}'"
33
+ end
34
+
35
+ desc "Write a file to AWS S3"
36
+ task :write do
37
+ # Optional arguments
38
+ use_mock_client = ENV.fetch('AWS_S3_MOCK_CLIENT', 'true') == 'true'
39
+ max_file_size = ENV.fetch('AWS_S3_MAX_FILE_SIZE', 1_048_576).to_i
40
+ storage_class = ENV.fetch('AWS_S3_STORAGE_CLASS', 'STANDARD')
41
+ keep_file_name = ENV.fetch('AWS_S3_KEEP_FILE_NAME', 'true') == 'true'
42
+
43
+ # Required arguments
44
+ arguments = {
45
+ file_path: ENV['FILE_PATH'],
46
+ cmk_key_id: ENV['CMK_KEY_ID'],
47
+ aws_s3_bucket: ENV['AWS_S3_BUCKET'],
48
+ aws_region: ENV['AWS_REGION'],
49
+ aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
50
+ aws_access_key: ENV['AWS_ACCESS_KEY']
51
+ }
52
+
53
+ # Checks presence of required arguments and configures porky_lib
54
+ check_arguments(arguments)
55
+ setup_porky_lib(arguments, use_mock_client, max_file_size)
56
+
57
+ write_options = {
58
+ storage_class: storage_class,
59
+ file_name: (File.basename(arguments[:file_path]) if keep_file_name)
60
+ }.compact
61
+
62
+ # Creates CMK key with empty tags and stores file
63
+ PorkyLib::Symmetric.instance.create_key([{}], arguments[:cmk_key_id]) unless PorkyLib::Symmetric.instance.cmk_alias_exists?(arguments[:cmk_key_id])
64
+ file_key = PorkyLib::FileService.instance.write_file(arguments[:file_path], arguments[:aws_s3_bucket], arguments[:cmk_key_id], write_options)
65
+
66
+ puts "SUCCESS - Created file: '#{arguments[:file_path]}' bucket: '#{arguments[:aws_s3_bucket]}' file_key: '#{file_key}'"
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def check_arguments(arguments)
73
+ nil_arguments = []
74
+ arguments.map { |key, value| nil_arguments.push(key.to_s.upcase) if value.nil? && !key.nil? }
75
+ abort "ERROR - Need to provide as environment variables: #{nil_arguments.join(', ')}" unless nil_arguments.empty?
76
+ end
77
+
78
+ def setup_porky_lib(arguments, use_mock_client, max_file_size)
79
+ PorkyLib::Config.configure(aws_region: arguments[:aws_region],
80
+ aws_key_id: arguments[:aws_access_key_id],
81
+ aws_key_secret: arguments[:aws_access_key],
82
+ aws_client_mock: use_mock_client,
83
+ max_file_size: max_file_size)
84
+ PorkyLib::Config.initialize_aws
85
+ end
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'bundler'
26
26
  spec.add_development_dependency 'bundler-audit'
27
27
  spec.add_development_dependency 'byebug'
28
+ spec.add_development_dependency 'codacy-coverage'
28
29
  spec.add_development_dependency 'codecov'
29
30
  spec.add_development_dependency 'msgpack'
30
31
  spec.add_development_dependency 'rake'
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.9.2
4
+ version: 0.11.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: 2019-10-29 00:00:00.000000000 Z
11
+ date: 2020-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-kms
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: codacy-coverage
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: codecov
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -390,6 +404,7 @@ files:
390
404
  - lib/porky_lib/unencrypted.rb
391
405
  - lib/porky_lib/unencrypted/file_service.rb
392
406
  - lib/porky_lib/version.rb
407
+ - lib/tasks/file.rake
393
408
  - porky_lib.gemspec
394
409
  homepage: https://github.com/Zetatango/porky_lib
395
410
  licenses: []
@@ -409,7 +424,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
409
424
  - !ruby/object:Gem::Version
410
425
  version: '0'
411
426
  requirements: []
412
- rubygems_version: 3.0.3
427
+ rubygems_version: 3.1.2
413
428
  signing_key:
414
429
  specification_version: 4
415
430
  summary: A library for cryptographic services using AWS KMS and RbNaCl