iostreams 0.11.0 → 0.12.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: 79c7b3bec464587bc14ad4cb1c951f6b08f8ffd2
4
- data.tar.gz: 106e0fc38ffc1396ee5714ef19f9a14ea38113ae
3
+ metadata.gz: d4dcddf28ba98342cec548ede58e83e08f6d4948
4
+ data.tar.gz: 0fd6ff53b81dc4fdd8847f5c646d875aa03f7c4e
5
5
  SHA512:
6
- metadata.gz: 3f61f7b6a5a07f0419ac4f322fa2b2e986adf0f104d8b65c7f20539e8adbdb3c629f24af3ab8376202d1fb4f18f8376cd6720a83ba654be6b8d784b36c22c1ad
7
- data.tar.gz: bf2604ec77e4fd5923aaed1a478f66464e7f9a73c695574b3420b078723752be9b1eaed2b0f930da714b2958d649435711619c1528802ff4d60ce1e23d779d61
6
+ metadata.gz: '09453fc367c6d133311676971c31e9135549559995f2ffbcb9960bfd6054121344c6c6b5d7eb85bfb993b1531a8d70a7299659bceb0cffb12805b9ce823ac035'
7
+ data.tar.gz: afb3e83e545230f98a6e2acf936e937d20fdc3a753607aae039788daae426cd0bbd03d5018272e0c179512b7e4b55e6a9fcc8af2ad4b83b43aa05996630de446
@@ -62,8 +62,8 @@ module IOStreams
62
62
  # Select highest level: 5
63
63
  #
64
64
  # Delete test keys:
65
- # IOStreams::Pgp.delete_keys(email: 'sender@example.org', secret: true)
66
- # IOStreams::Pgp.delete_keys(email: 'receiver@example.org', secret: true)
65
+ # IOStreams::Pgp.delete_keys(email: 'sender@example.org', private: true)
66
+ # IOStreams::Pgp.delete_keys(email: 'receiver@example.org', private: true)
67
67
  #
68
68
  # Limitations
69
69
  # - Designed for processing larger files since a process is spawned for each file processed.
@@ -84,9 +84,13 @@ module IOStreams
84
84
  class Failure < StandardError
85
85
  end
86
86
 
87
- # Generate a new ultimate trusted local public and private key
88
- # Returns [String] the key id for the generated key
89
- # Raises an exception if it fails to generate the key
87
+ class UnsupportedVersion < Failure
88
+ end
89
+
90
+ # Generate a new ultimate trusted local public and private key.
91
+ #
92
+ # Returns [String] the key id for the generated key.
93
+ # Raises an exception if it fails to generate the key.
90
94
  #
91
95
  # name: [String]
92
96
  # Name of who owns the key, such as organization
@@ -105,126 +109,197 @@ module IOStreams
105
109
  #
106
110
  # See `man gpg` for the remaining options
107
111
  def self.generate_key(name:, email:, comment: nil, passphrase: nil, key_type: 'RSA', key_length: 4096, subkey_type: 'RSA', subkey_length: key_length, expire_date: nil)
108
- Open3.popen2e('gpg --batch --gen-key') do |stdin, out, waith_thr|
109
- stdin.puts "Key-Type: #{key_type}" if key_type
110
- stdin.puts "Key-Length: #{key_length}" if key_length
111
- stdin.puts "Subkey-Type: #{subkey_type}" if subkey_type
112
- stdin.puts "Subkey-Length: #{subkey_length}" if subkey_length
113
- stdin.puts "Name-Real: #{name}" if name
114
- stdin.puts "Name-Comment: #{comment}" if comment
115
- stdin.puts "Name-Email: #{email}" if email
116
- stdin.puts "Expire-Date: #{expire_date}" if expire_date
117
- stdin.puts "Passphrase: #{passphrase}" if passphrase
118
- stdin.puts '%commit'
119
- stdin.close
120
- if waith_thr.value.success?
121
- key_id = nil
122
- out.each_line do |line|
123
- if (line = line.chomp) =~ /^gpg: key ([0-9A-F]+) marked as ultimately trusted/
124
- key_id = $1.to_i(16)
125
- end
126
- end
127
- key_id
128
- else
129
- raise(Pgp::Failure, "GPG Failed to generate key: #{out.read.chomp}")
112
+ version_check
113
+ params = ''
114
+ params << "Key-Type: #{key_type}\n" if key_type
115
+ params << "Key-Length: #{key_length}\n" if key_length
116
+ params << "Subkey-Type: #{subkey_type}\n" if subkey_type
117
+ params << "Subkey-Length: #{subkey_length}\n" if subkey_length
118
+ params << "Name-Real: #{name}\n" if name
119
+ params << "Name-Comment: #{comment}\n" if comment
120
+ params << "Name-Email: #{email}\n" if email
121
+ params << "Expire-Date: #{expire_date}\n" if expire_date
122
+ params << "Passphrase: #{passphrase}\n" if passphrase
123
+ params << '%commit'
124
+ out, err, status = Open3.capture3('gpg --batch --gen-key', binmode: true, stdin_data: params)
125
+ logger.debug { "IOStreams::Pgp.generate_key output:\n#{out}#{err}" } if logger
126
+ if status.success?
127
+ if match = err.match(/gpg: key ([0-9A-F]+)\s+/)
128
+ return match[1]
130
129
  end
130
+ else
131
+ raise(Pgp::Failure, "GPG Failed to generate key: #{out}#{err}")
131
132
  end
132
133
  end
133
134
 
134
- # Delete a secret and public keys using its email
135
- # Returns false if no key was found
136
- # Raises an exception if it fails to delete the key
135
+ # Delete all private and public keys for a particular email.
137
136
  #
138
- # email: [String] Email address for the key
137
+ # Returns false if no key was found.
138
+ # Raises an exception if it fails to delete the key.
139
+ #
140
+ # email: [String] Email address for the key.
139
141
  #
140
142
  # public: [true|false]
141
143
  # Whether to delete the public key
142
144
  # Default: true
143
145
  #
144
- # secret: [true|false]
145
- # Whether to delete the secret key
146
+ # private: [true|false]
147
+ # Whether to delete the private key
146
148
  # Default: false
147
- def self.delete_keys(email:, public: true, secret: false)
149
+ def self.delete_keys(email:, public: true, private: false)
150
+ version_check
148
151
  cmd = "for i in `gpg --with-colons --fingerprint #{email} | grep \"^fpr\" | cut -d: -f10`; do\n"
149
- cmd << "gpg --batch --delete-secret-keys \"$i\" ;\n" if secret
152
+ cmd << "gpg --batch --delete-secret-keys \"$i\" ;\n" if private
150
153
  cmd << "gpg --batch --delete-keys \"$i\" ;\n" if public
151
154
  cmd << 'done'
152
- Open3.popen2e(cmd) do |stdin, out, waith_thr|
153
- output = out.read.chomp
154
- if waith_thr.value.success?
155
- return false if output =~ /(public key not found|No public key)/i
156
- raise(Pgp::Failure, "GPG Failed to delete keys for #{email}: #{output}") if output.include?('error')
157
- true
158
- else
159
- raise(Pgp::Failure, "GPG Failed calling gpg to delete secret keys for #{email}: #{output}")
160
- end
155
+
156
+ out, err, status = Open3.capture3(cmd, binmode: true)
157
+ logger.debug { "IOStreams::Pgp.delete_keys output:\n#{err}#{out}" } if logger
158
+
159
+ if status.success?
160
+ return false if err =~ /(not found|No public key)/i
161
+ raise(Pgp::Failure, "GPG Failed to delete keys for #{email}:#{err}#{out}") if out.include?('error')
162
+ true
163
+ else
164
+ raise(Pgp::Failure, "GPG Failed calling gpg to delete private keys for #{email}: #{err}#{out}")
161
165
  end
162
166
  end
163
167
 
164
- def self.has_key?(email:)
165
- Open3.popen2e("gpg --list-keys --with-colons #{email}") do |stdin, out, waith_thr|
166
- output = out.read.chomp
167
- if waith_thr.value.success?
168
- output.each_line do |line|
169
- return true if line.include?(email)
170
- end
171
- false
172
- else
173
- return false if output =~ /(public key not found|No public key)/i
174
- raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email}: #{output}")
175
- end
168
+ # Returns [true|false] whether their is a key for the supplied email or key_id
169
+ def self.has_key?(email: nil, key_id: nil, private: false)
170
+ raise(ArgumentError, 'Either :email, or :key_id must be supplied') if email.nil? && key_id.nil?
171
+
172
+ !list_keys(email: email, key_id: key_id, private: private).empty?
173
+ end
174
+
175
+ # Returns [Array<Hash>] the list of keys.
176
+ # Each Hash consists of:
177
+ # key_length: [Integer]
178
+ # key_type: [String]
179
+ # key_id: [String]
180
+ # date: [String]
181
+ # name: [String]
182
+ # email: [String]
183
+ # Returns [] if no keys were found.
184
+ def self.list_keys(email: nil, key_id: nil, private: false)
185
+ version_check
186
+ cmd = private ? '--list-secret-keys' : '--list-keys'
187
+ out, err, status = Open3.capture3("gpg #{cmd} #{email || key_id}", binmode: true)
188
+ logger.debug { "IOStreams::Pgp.list_keys output:\n#{err}#{out}" } if logger
189
+ if status.success? && out.length > 0
190
+ # Sample output
191
+ # pub 4096R/3A5456F5 2017-06-07
192
+ # uid [ unknown] Joe Bloggs <j@bloggs.net>
193
+ # sub 4096R/2C9B240B 2017-06-07
194
+ parse_list_output(out)
195
+ else
196
+ return [] if err =~ /(key not found|No (public|secret) key)/i
197
+ raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email || key_id}: #{err}#{out}")
176
198
  end
177
199
  end
178
200
 
179
- # Returns [String] the first fingerprint for the supplied email
180
- # Returns nil if no fingerprint was found
181
- def self.fingerprint(email:)
182
- Open3.popen2e("gpg --list-keys --fingerprint --with-colons #{email}") do |stdin, out, waith_thr|
183
- output = out.read.chomp
184
- if waith_thr.value.success?
185
- output.each_line do |line|
186
- if match = line.match(/\Afpr.*::([^\:]*):\Z/)
187
- return match[1]
188
- end
189
- end
190
- nil
191
- else
192
- return if output =~ /(public key not found|No public key)/i
193
- raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email}: #{output}")
194
- end
201
+ # Extract information from the supplied key.
202
+ #
203
+ # Useful for confirming encryption keys before importing them.
204
+ #
205
+ # Returns [Array<Hash>] the list of primary keys.
206
+ # Each Hash consists of:
207
+ # key_length: [Integer]
208
+ # key_type: [String]
209
+ # key_id: [String]
210
+ # date: [String]
211
+ # name: [String]
212
+ # email: [String]
213
+ def self.key_info(key:)
214
+ version_check
215
+ out, err, status = Open3.capture3('gpg', binmode: true, stdin_data: key)
216
+ logger.debug { "IOStreams::Pgp.key_info output:\n#{err}#{out}" } if logger
217
+ if status.success? && out.length > 0
218
+ # Sample Output:
219
+ #
220
+ # pub 4096R/3A5456F5 2017-06-07
221
+ # uid Joe Bloggs <j@bloggs.net>
222
+ # sub 4096R/2C9B240B 2017-06-07
223
+ parse_list_output(out)
224
+ else
225
+ raise(Pgp::Failure, "GPG Failed extracting key details: #{err} #{out}")
195
226
  end
196
227
  end
197
228
 
198
- # Returns [String] the key for the supplied email address
229
+ # Returns [String] containing all the keys for the supplied email address.
199
230
  #
200
- # email: [String] Email address for requested key
231
+ # email: [String] Email address for requested key.
201
232
  #
202
233
  # ascii: [true|false]
203
234
  # Whether to export as ASCII text instead of binary format
204
235
  # Default: true
205
236
  #
206
- # secret: [true|false]
237
+ # private: [true|false]
207
238
  # Whether to export the private key
208
239
  # Default: false
209
- def self.export(email:, ascii: true, secret: false)
240
+ def self.export(email:, ascii: true, private: false)
241
+ version_check
210
242
  armor = ascii ? ' --armor' : nil
211
- cmd = secret ? '--export-secret-keys' : '--export'
243
+ cmd = private ? '--export-secret-keys' : '--export'
212
244
  out, err, status = Open3.capture3("gpg#{armor} #{cmd} #{email}", binmode: true)
245
+ logger.debug { "IOStreams::Pgp.export output:\n#{err}" } if logger
213
246
  if status.success? && out.length > 0
214
247
  out
215
248
  else
216
- raise(Pgp::Failure, "GPG Failed reading key: #{email}: #{err} #{out}")
249
+ raise(Pgp::Failure, "GPG Failed reading key: #{email}: #{err}")
217
250
  end
218
251
  end
219
252
 
220
253
  # Imports the supplied public/private key
221
- # Returns [String] the output returned from the import command
222
- def self.import(key)
254
+ #
255
+ # Returns [Array<Hash>] keys that were successfully imported.
256
+ # Each Hash consists of:
257
+ # key_id: [String]
258
+ # type: [String]
259
+ # name: [String]
260
+ # email: [String]
261
+ # Returns [] if the same key was previously imported.
262
+ #
263
+ # Raises Pgp::Failure if there was an issue importing any of the keys.
264
+ #
265
+ # Notes:
266
+ # * Importing a new key for the same email address does not remove the prior key if any.
267
+ # * Invalidated keys must be removed manually.
268
+ def self.import(key:)
269
+ version_check
223
270
  out, err, status = Open3.capture3('gpg --import', binmode: true, stdin_data: key)
224
- if status.success? && out.length > 0
225
- out
271
+ logger.debug { "IOStreams::Pgp.import output:\n#{err}#{out}" } if logger
272
+ if status.success? && err.length > 0
273
+ # Sample output
274
+ #
275
+ # gpg: key C16500E3: secret key imported\n"
276
+ # gpg: key C16500E3: public key "Joe Bloggs <pgp_test@iostreams.net>" imported
277
+ # gpg: Total number processed: 1
278
+ # gpg: imported: 1 (RSA: 1)
279
+ # gpg: secret keys read: 1
280
+ # gpg: secret keys imported: 1
281
+ #
282
+ # Ignores unchanged:
283
+ # gpg: key 9615D46D: \"Joe Bloggs <j@bloggs.net>\" not changed\n
284
+ results = []
285
+ secret = false
286
+ err.each_line do |line|
287
+ if line =~ /secret key imported/
288
+ secret = true
289
+ elsif match = line.match(/key\s+(\w+):\s+(\w+).+\"(.*)<(.*)>\"/)
290
+ results << {
291
+ key_id: match[1].to_s.strip,
292
+ private: secret,
293
+ name: match[3].to_s.strip,
294
+ email: match[4].to_s.strip
295
+ }
296
+ secret = false
297
+ end
298
+ end
299
+ results
226
300
  else
227
- raise(Pgp::Failure, "GPG Failed importing key: #{err} #{out}")
301
+ return [] if err =~ /already in secret keyring/
302
+ raise(Pgp::Failure, "GPG Failed importing key: #{err}#{out}")
228
303
  end
229
304
  end
230
305
 
@@ -236,11 +311,13 @@ module IOStreams
236
311
  # After importing keys, they are not trusted and the relevant trust level must be set.
237
312
  # Default: 5 : Ultimate
238
313
  def self.set_trust(email:, level: 5)
314
+ version_check
239
315
  fingerprint = fingerprint(email: email)
240
316
  return unless fingerprint
241
317
 
242
318
  trust = "#{fingerprint}:#{level + 1}:\n"
243
319
  out, err, status = Open3.capture3('gpg --import-ownertrust', stdin_data: trust)
320
+ logger.debug { "IOStreams::Pgp.set_trust output:\n#{err}#{out}" } if logger
244
321
  if status.success?
245
322
  err
246
323
  else
@@ -248,5 +325,100 @@ module IOStreams
248
325
  end
249
326
  end
250
327
 
328
+ # DEPRECATED - Use key_ids instead of fingerprints
329
+ def self.fingerprint(email:)
330
+ version_check
331
+ Open3.popen2e("gpg --list-keys --fingerprint --with-colons #{email}") do |stdin, out, waith_thr|
332
+ output = out.read.chomp
333
+ if waith_thr.value.success?
334
+ output.each_line do |line|
335
+ if match = line.match(/\Afpr.*::([^\:]*):\Z/)
336
+ return match[1]
337
+ end
338
+ end
339
+ nil
340
+ else
341
+ return if output =~ /(public key not found|No public key)/i
342
+ raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email}: #{output}")
343
+ end
344
+ end
345
+ end
346
+
347
+ def self.logger=(logger)
348
+ @logger = logger
349
+ end
350
+
351
+ # Returns [String] the version of pgp currently installed
352
+ def self.pgp_version
353
+ @pgp_version ||= begin
354
+ out, err, status = Open3.capture3("gpg --version")
355
+ logger.debug { "IOStreams::Pgp.version output:\n#{err}#{out}" } if logger
356
+ if status.success?
357
+ # Sample output
358
+ # gpg (GnuPG) 2.0.30
359
+ # libgcrypt 1.7.6
360
+ # Copyright (C) 2015 Free Software Foundation, Inc.
361
+ # License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
362
+ # This is free software: you are free to change and redistribute it.
363
+ # There is NO WARRANTY, to the extent permitted by law.
364
+ #
365
+ # Home: ~/.gnupg
366
+ # Supported algorithms:
367
+ # Pubkey: RSA, RSA, RSA, ELG, DSA
368
+ # Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
369
+ # CAMELLIA128, CAMELLIA192, CAMELLIA256
370
+ # Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
371
+ # Compression: Uncompressed, ZIP, ZLIB, BZIP2
372
+ if match = out.lines.first.match(/(\d+\.\d+.\d+)/)
373
+ match[1]
374
+ end
375
+ else
376
+ return [] if err =~ /(key not found|No (public|secret) key)/i
377
+ raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email || key_id}: #{err}#{out}")
378
+ end
379
+ end
380
+ end
381
+
382
+ private
383
+
384
+ @logger = nil
385
+
386
+ def self.logger
387
+ @logger
388
+ end
389
+
390
+ def self.version_check
391
+ raise(Pgp::UnsupportedVersion, "Version #{pgp_version} of gpg is not yet supported. You are welcome to submit a Pull Request.") if pgp_version.to_f >= 2.1
392
+ end
393
+
394
+ def self.parse_list_output(out)
395
+ results = []
396
+ hash = {}
397
+ out.each_line do |line|
398
+ if match = line.match(/(pub|sec)\s+(\d+)(.*)\/(\w+)\s+(\S+)/)
399
+ hash = {
400
+ private: match[1] == 'sec',
401
+ key_length: match[2].to_s.to_i,
402
+ key_type: match[3],
403
+ key_id: match[4],
404
+ date: (Date.parse(match[5].to_s) rescue match[5])
405
+ }
406
+ elsif match = line.match(/uid\s+(.+)<(.+)>/)
407
+ name = match[1].strip
408
+ hash[:email] = match[2].strip
409
+ if match = name.match(/(\[(.+)\])?(.+)/)
410
+ trust = match[2].to_s.strip
411
+ hash[:trust] = trust unless trust.empty?
412
+ hash[:name] = match[3].to_s.strip
413
+ else
414
+ hash[:name] = name
415
+ end
416
+ results << hash
417
+ hash = {}
418
+ end
419
+ end
420
+ results
421
+ end
422
+
251
423
  end
252
424
  end
@@ -1,3 +1,3 @@
1
1
  module IOStreams #:nodoc
2
- VERSION = '0.11.0'
2
+ VERSION = '0.12.0'
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'io_streams/version'
2
+ #@formatter:off
2
3
  module IOStreams
3
4
  module CSV
4
5
  autoload :Reader, 'io_streams/csv/reader'
@@ -0,0 +1,238 @@
1
+ require_relative 'test_helper'
2
+
3
+ #IOStreams::Pgp.logger = SemanticLogger[IOStreams::Pgp]
4
+
5
+ module Streams
6
+ class PgpTest < Minitest::Test
7
+ describe IOStreams::Pgp::Reader do
8
+ let :user_name do
9
+ 'Joe Bloggs'
10
+ end
11
+
12
+ let :email do
13
+ 'pgp_test@iostreams.net'
14
+ end
15
+
16
+ let :generated_key_id do
17
+ IOStreams::Pgp.generate_key(name: user_name, email: email, key_length: 1024)
18
+ end
19
+
20
+ let :public_key do
21
+ generated_key_id
22
+ IOStreams::Pgp.export(email: email)
23
+ end
24
+
25
+ let :private_key do
26
+ generated_key_id
27
+ IOStreams::Pgp.export(email: email, private: true)
28
+ end
29
+
30
+ before do
31
+ IOStreams::Pgp.delete_keys(email: email, public: true, private: true)
32
+ end
33
+
34
+ describe '.pgp_version' do
35
+ it 'returns pgp version' do
36
+ assert IOStreams::Pgp.pgp_version
37
+ end
38
+ end
39
+
40
+ describe '.generate_key' do
41
+ it 'returns the key id' do
42
+ assert generated_key_id
43
+ end
44
+ end
45
+
46
+ describe '.has_key?' do
47
+ before do
48
+ generated_key_id
49
+ end
50
+
51
+ it 'confirms public key' do
52
+ assert IOStreams::Pgp.has_key?(key_id: generated_key_id)
53
+ end
54
+
55
+ it 'confirms private key' do
56
+ assert IOStreams::Pgp.has_key?(key_id: generated_key_id, private: true)
57
+ end
58
+ end
59
+
60
+ describe '.delete_keys' do
61
+ it 'handles no keys' do
62
+ refute IOStreams::Pgp.delete_keys(email: 'random@iostreams.net', public: true, private: true)
63
+ end
64
+
65
+ it 'deletes existing keys' do
66
+ generated_key_id
67
+ assert IOStreams::Pgp.delete_keys(email: email, public: true, private: true)
68
+ end
69
+
70
+ it 'deletes just the private key' do
71
+ generated_key_id
72
+ assert IOStreams::Pgp.delete_keys(email: email, public: false, private: true)
73
+ refute IOStreams::Pgp.has_key?(key_id: generated_key_id, private: true)
74
+ assert IOStreams::Pgp.has_key?(key_id: generated_key_id, private: false)
75
+ end
76
+ end
77
+
78
+ describe '.export' do
79
+ before do
80
+ generated_key_id
81
+ end
82
+
83
+ it 'exports public keys by email' do
84
+ assert ascii_keys = IOStreams::Pgp.export(email: email)
85
+ assert ascii_keys =~ /BEGIN PGP PUBLIC KEY BLOCK/, ascii_keys
86
+ end
87
+
88
+ it 'exports private keys by email' do
89
+ assert ascii_keys = IOStreams::Pgp.export(email: email, private: true)
90
+ assert ascii_keys =~ /BEGIN PGP PRIVATE KEY BLOCK/, ascii_keys
91
+ end
92
+
93
+ it 'exports public keys as binary' do
94
+ assert keys = IOStreams::Pgp.export(email: email, ascii: false)
95
+ refute keys =~ /BEGIN PGP (PUBLIC|PRIVATE) KEY BLOCK/, keys
96
+ end
97
+
98
+ it 'exports private keys as binary' do
99
+ assert keys = IOStreams::Pgp.export(email: email, ascii: false, private: true)
100
+ refute keys =~ /BEGIN PGP (PUBLIC|PRIVATE) KEY BLOCK/, keys
101
+ end
102
+ end
103
+
104
+ describe '.list_keys' do
105
+ before do
106
+ generated_key_id
107
+ end
108
+
109
+ it 'lists public keys' do
110
+ assert keys = IOStreams::Pgp.list_keys(email: email)
111
+ assert_equal 1, keys.size
112
+ assert key = keys.first
113
+
114
+ assert_equal key[:date], Date.today
115
+ assert_equal email, key[:email]
116
+ assert_equal generated_key_id, key[:key_id]
117
+ assert_equal 1024, key[:key_length]
118
+ assert_equal 'R', key[:key_type]
119
+ assert_equal user_name, key[:name]
120
+ refute key[:private], key
121
+ assert_equal 'ultimate', key[:trust]
122
+ end
123
+
124
+ it 'lists private keys' do
125
+ assert keys = IOStreams::Pgp.list_keys(email: email, private: true)
126
+ assert_equal 1, keys.size
127
+ assert key = keys.first
128
+
129
+ assert_equal key[:date], Date.today
130
+ assert_equal email, key[:email]
131
+ assert_equal generated_key_id, key[:key_id]
132
+ assert_equal 1024, key[:key_length]
133
+ assert_equal 'R', key[:key_type]
134
+ assert_equal user_name, key[:name]
135
+ assert key[:private], key
136
+ refute key.key?(:trust)
137
+ end
138
+ end
139
+
140
+ describe '.key_info' do
141
+ it 'extracts public key info' do
142
+ assert keys = IOStreams::Pgp.key_info(key: public_key)
143
+ assert_equal 1, keys.size
144
+ assert key = keys.first
145
+
146
+ assert_equal key[:date], Date.today
147
+ assert_equal email, key[:email]
148
+ assert_equal generated_key_id, key[:key_id]
149
+ assert_equal 1024, key[:key_length]
150
+ assert_equal 'R', key[:key_type]
151
+ assert_equal user_name, key[:name]
152
+ refute key[:private], key
153
+ refute key.key?(:trust)
154
+ end
155
+
156
+ it 'extracts private key info' do
157
+ assert keys = IOStreams::Pgp.key_info(key: private_key)
158
+ assert_equal 1, keys.size
159
+ assert key = keys.first
160
+
161
+ assert_equal key[:date], Date.today
162
+ assert_equal email, key[:email]
163
+ assert_equal generated_key_id, key[:key_id]
164
+ assert_equal 1024, key[:key_length]
165
+ assert_equal 'R', key[:key_type]
166
+ assert_equal user_name, key[:name]
167
+ assert key[:private], key
168
+ refute key.key?(:trust)
169
+ end
170
+ end
171
+
172
+ describe '.import' do
173
+ it 'handle duplicate public key' do
174
+ generated_key_id
175
+ assert_equal [], IOStreams::Pgp.import(key: public_key)
176
+ end
177
+
178
+ it 'handle duplicate private key' do
179
+ generated_key_id
180
+ assert_equal [], IOStreams::Pgp.import(key: private_key)
181
+ end
182
+
183
+ describe 'without keys' do
184
+ before do
185
+ @public_key = public_key
186
+ @private_key = private_key
187
+ IOStreams::Pgp.delete_keys(email: email, public: true, private: true)
188
+ end
189
+
190
+ it 'imports ascii public key' do
191
+ assert keys = IOStreams::Pgp.import(key: @public_key)
192
+ assert_equal 1, keys.size
193
+ assert key = keys.first
194
+
195
+ assert_equal email, key[:email]
196
+ assert_equal generated_key_id, key[:key_id]
197
+ assert_equal user_name, key[:name]
198
+ refute key[:private], key
199
+ end
200
+
201
+ it 'imports ascii private key' do
202
+ assert keys = IOStreams::Pgp.import(key: @private_key)
203
+ assert_equal 1, keys.size
204
+ assert key = keys.first
205
+
206
+ assert_equal email, key[:email]
207
+ assert_equal generated_key_id, key[:key_id]
208
+ assert_equal user_name, key[:name]
209
+ assert key[:private], key
210
+ end
211
+
212
+ it 'imports binary public key' do
213
+ assert keys = IOStreams::Pgp.import(key: @public_key)
214
+ assert_equal 1, keys.size
215
+ assert key = keys.first
216
+
217
+ assert_equal email, key[:email]
218
+ assert_equal generated_key_id, key[:key_id]
219
+ assert_equal user_name, key[:name]
220
+ refute key[:private], key
221
+ end
222
+
223
+ it 'imports binary private key' do
224
+ assert keys = IOStreams::Pgp.import(key: @private_key)
225
+ assert_equal 1, keys.size
226
+ assert key = keys.first
227
+
228
+ assert_equal email, key[:email]
229
+ assert_equal generated_key_id, key[:key_id]
230
+ assert_equal user_name, key[:name]
231
+ assert key[:private], key
232
+ end
233
+ end
234
+ end
235
+
236
+ end
237
+ end
238
+ end
@@ -32,7 +32,7 @@ module Streams
32
32
  io = StringIO.new(io_string.string)
33
33
  result = nil
34
34
  begin
35
- zin = ::Zip::InputStream.new(io)
35
+ zin = ::Zip::InputStream.new(io)
36
36
  zin.get_next_entry
37
37
  result = zin.read
38
38
  ensure
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iostreams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-01 00:00:00.000000000 Z
11
+ date: 2017-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -69,6 +69,7 @@ files:
69
69
  - test/gzip_writer_test.rb
70
70
  - test/io_streams_test.rb
71
71
  - test/pgp_reader_test.rb
72
+ - test/pgp_test.rb
72
73
  - test/pgp_writer_test.rb
73
74
  - test/test_helper.rb
74
75
  - test/xlsx_reader_test.rb
@@ -116,6 +117,7 @@ test_files:
116
117
  - test/gzip_writer_test.rb
117
118
  - test/io_streams_test.rb
118
119
  - test/pgp_reader_test.rb
120
+ - test/pgp_test.rb
119
121
  - test/pgp_writer_test.rb
120
122
  - test/test_helper.rb
121
123
  - test/xlsx_reader_test.rb