porky_lib 0.9.2 → 0.11.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
  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