mpw 4.1.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/mpw/mpw.rb CHANGED
@@ -25,7 +25,11 @@ require 'mpw/item'
25
25
 
26
26
  module MPW
27
27
  class MPW
28
- # Constructor
28
+ # @param key [String] gpg key name
29
+ # @param wallet_file [String] path of the wallet file
30
+ # @param gpg_pass [String] password of the gpg key
31
+ # @param gpg_exe [String] path of the gpg executable
32
+ # @param pinmode [Boolean] enable the gpg pinmode
29
33
  def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil, pinmode = false)
30
34
  @key = key
31
35
  @gpg_pass = gpg_pass
@@ -98,7 +102,7 @@ module MPW
98
102
  raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
99
103
  end
100
104
 
101
- # Encrypt a file
105
+ # Encrypt all data in tarball
102
106
  def write_data
103
107
  data = {}
104
108
  tmp_file = "#{@wallet_file}.tmp"
@@ -154,7 +158,7 @@ module MPW
154
158
  end
155
159
 
156
160
  # Get a password
157
- # args: id -> the item id
161
+ # @param id [String] the item id
158
162
  def get_password(id)
159
163
  password = decrypt(@passwords[id])
160
164
 
@@ -165,9 +169,9 @@ module MPW
165
169
  end
166
170
  end
167
171
 
168
- # Set a password
169
- # args: id -> the item id
170
- # password -> the new password
172
+ # Set a new password for an item
173
+ # @param id [String] the item id
174
+ # @param password [String] the new password
171
175
  def set_password(id, password)
172
176
  salt = MPW.password(length: Random.rand(4..9))
173
177
  password = "$#{salt}::#{password}"
@@ -176,13 +180,13 @@ module MPW
176
180
  end
177
181
 
178
182
  # Return the list of all gpg keys
179
- # rtrn: an array with the gpg keys name
183
+ # @return [Array] the gpg keys name
180
184
  def list_keys
181
185
  @keys.keys
182
186
  end
183
187
 
184
188
  # Add a public key
185
- # args: key -> new public key file or name
189
+ # @param key [String] new public key file or name
186
190
  def add_key(key)
187
191
  if File.exist?(key)
188
192
  data = File.open(key).read
@@ -200,7 +204,7 @@ module MPW
200
204
  end
201
205
 
202
206
  # Delete a public key
203
- # args: key -> public key to delete
207
+ # @param key [String] public key to delete
204
208
  def delete_key(key)
205
209
  @keys.delete(key)
206
210
  @passwords.each_key { |id| set_password(id, get_password(id)) }
@@ -208,7 +212,7 @@ module MPW
208
212
  end
209
213
 
210
214
  # Add a new item
211
- # @args: item -> Object MPW::Item
215
+ # @param item [Item]
212
216
  def add(item)
213
217
  raise I18n.t('error.bad_class') unless item.instance_of?(Item)
214
218
  raise I18n.t('error.empty') if item.empty?
@@ -217,8 +221,8 @@ module MPW
217
221
  end
218
222
 
219
223
  # Search in some csv data
220
- # @args: options -> a hash with paramaters
221
- # @rtrn: a list with the resultat of the search
224
+ # @param options [Hash]
225
+ # @return [Array] a list with the resultat of the search
222
226
  def list(**options)
223
227
  result = []
224
228
 
@@ -240,9 +244,9 @@ module MPW
240
244
  result
241
245
  end
242
246
 
243
- # Search in some csv data
244
- # @args: id -> the id item
245
- # @rtrn: a row with the result of the search
247
+ # Search an item with an id
248
+ # @param id [String]the id item
249
+ # @return [Item] an item or nil
246
250
  def search_by_id(id)
247
251
  @data.each do |item|
248
252
  return item if item.id == id
@@ -251,36 +255,35 @@ module MPW
251
255
  nil
252
256
  end
253
257
 
254
- # Set an opt key
255
- # args: id -> the item id
256
- # key -> the new key
258
+ # Set a new opt key
259
+ # @param id [String] the item id
260
+ # @param key [String] the new key
257
261
  def set_otp_key(id, key)
258
262
  @otp_keys[id] = encrypt(key.to_s) unless key.to_s.empty?
259
263
  end
260
264
 
261
265
  # Get an opt key
262
- # args: id -> the item id
263
- # key -> the new key
266
+ # @param id [String] the item id
264
267
  def get_otp_key(id)
265
268
  @otp_keys.key?(id) ? decrypt(@otp_keys[id]) : nil
266
269
  end
267
270
 
268
271
  # Get an otp code
269
- # @args: id -> the item id
270
- # @rtrn: an otp code
272
+ # @param id [String] the item id
273
+ # @return [String] an otp code
271
274
  def get_otp_code(id)
272
275
  @otp_keys.key?(id) ? 0 : ROTP::TOTP.new(decrypt(@otp_keys[id])).now
273
276
  end
274
277
 
275
278
  # Get remaining time before expire otp code
276
- # @rtrn: return time in seconde
279
+ # @return [Integer] time in seconde
277
280
  def get_otp_remaining_time
278
281
  (Time.now.utc.to_i / 30 + 1) * 30 - Time.now.utc.to_i
279
282
  end
280
283
 
281
284
  # Generate a random password
282
- # @args: options -> :length, :special, :alpha, :numeric
283
- # @rtrn: a random string
285
+ # @param options [Hash] :length, :special, :alpha, :numeric
286
+ # @return [String] a random string
284
287
  def self.password(**options)
285
288
  length =
286
289
  if !options.include?(:length) || options[:length].to_i <= 0
@@ -298,11 +301,9 @@ module MPW
298
301
  chars = [*('A'..'Z'), *('a'..'z'), *('0'..'9')] if chars.empty?
299
302
 
300
303
  result = ''
301
- while length > 62
302
- result << chars.sample(62).join
303
- length -= 62
304
+ length.times do
305
+ result << chars.sample
304
306
  end
305
- result << chars.sample(length).join
306
307
 
307
308
  result
308
309
  end
@@ -310,7 +311,8 @@ module MPW
310
311
  private
311
312
 
312
313
  # Decrypt a gpg file
313
- # @args: data -> string to decrypt
314
+ # @param data [String] data to decrypt
315
+ # @return [String] data decrypted
314
316
  def decrypt(data)
315
317
  return nil if data.to_s.empty?
316
318
 
@@ -331,7 +333,8 @@ module MPW
331
333
  end
332
334
 
333
335
  # Encrypt a file
334
- # args: data -> string to encrypt
336
+ # @param data [String] data to encrypt
337
+ # @return [String] data encrypted
335
338
  def encrypt(data)
336
339
  recipients = []
337
340
  crypto = GPGME::Crypto.new(armor: true, always_trust: true)
@@ -0,0 +1,19 @@
1
+ ---
2
+ 1:
3
+ host: fric.com
4
+ user: 230403
5
+ group: Bank
6
+ password: 5XdiTQOubRDw9B0aJoMlcEyL
7
+ protocol: https
8
+ port:
9
+ otp_key: 330223432
10
+ comment: I love my bank
11
+ 2:
12
+ host: assurance.com
13
+ user: user_2132
14
+ group: Assurance
15
+ password: DMyK6B3v4bWO52VzU7aTHIem
16
+ protocol: https
17
+ port: 443
18
+ otp_key:
19
+ comment:
@@ -1,28 +1,28 @@
1
- add_new:
2
- group: 'test_group'
3
- host: 'test_host'
4
- protocol: 'test_protocol'
5
- user: 'test_user'
6
- password: 'test_password'
7
- port: '42'
8
- comment: 'test_comment'
1
+ add:
2
+ group: 'Bank'
3
+ host: 'example.com'
4
+ protocol: 'https'
5
+ user: 'admin'
6
+ password: 'VmfnCN6pPIqgRIbc'
7
+ port: '8080'
8
+ comment: 'the website'
9
9
 
10
- add_existing:
10
+ import:
11
11
  id: 'TEST-ID-XXXXX'
12
- group: 'test_group_existing'
13
- host: 'test_host_existing'
14
- protocol: 'test_protocol_existing'
15
- user: 'test_user_existing'
16
- password: 'test_password_existing'
17
- port: '44'
18
- comment: 'test_comment_existing'
12
+ group: 'Cloud'
13
+ host: 'gogole.com'
14
+ protocol: 'https'
15
+ user: 'gg-2304'
16
+ password: 'TITl0kV9CDDa9sVK'
17
+ port: '8081'
18
+ comment: 'My little servers'
19
19
  created: 1386752948
20
20
 
21
21
  update:
22
- group: 'test_group_update'
23
- host: 'test_host_update'
24
- protocol: 'test_protocol_update'
25
- user: 'test_user_update'
26
- password: 'test_password_update'
27
- port: '43'
28
- comment: 'test_comment_update'
22
+ group: 'Assurance'
23
+ host: 'example2.com'
24
+ protocol: 'ssh'
25
+ user: 'root'
26
+ password: 'kbSrbv4WlMaVxaZ7'
27
+ port: '2222'
28
+ comment: 'i love ssh'
data/test/init.rb CHANGED
@@ -1,13 +1,17 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
+ require 'fileutils'
3
4
  require 'gpgme'
4
5
 
6
+ FileUtils.rm_rf("#{Dir.home}/.config/mpw")
7
+ FileUtils.rm_rf("#{Dir.home}/.gnupg")
8
+
5
9
  param = ''
6
10
  param << '<GnupgKeyParms format="internal">' + "\n"
7
11
  param << "Key-Type: RSA\n"
8
- param << "Key-Length: 2048\n"
12
+ param << "Key-Length: 512\n"
9
13
  param << "Subkey-Type: ELG-E\n"
10
- param << "Subkey-Length: 2048\n"
14
+ param << "Subkey-Length: 512\n"
11
15
  param << "Name-Real: test\n"
12
16
  param << "Name-Comment: test\n"
13
17
  param << "Name-Email: test2@example.com\n"
data/test/test_cli.rb ADDED
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'i18n'
4
+ require 'test/unit'
5
+
6
+ class TestConfig < Test::Unit::TestCase
7
+ def setup
8
+ if defined?(I18n.enforce_available_locales)
9
+ I18n.enforce_available_locales = true
10
+ end
11
+
12
+ I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
13
+ I18n.load_path = ["#{File.expand_path('../../i18n', __FILE__)}/en.yml"]
14
+ I18n.locale = :en
15
+
16
+ @password = 'password'
17
+ @fixtures = YAML.load_file('./test/files/fixtures.yml')
18
+ @gpg_key = 'test@example.com'
19
+ end
20
+
21
+ def test_00_init_config
22
+ output = %x(echo "#{@password}\n#{@password}" | mpw config --init #{@gpg_key})
23
+ assert_match(I18n.t('form.setup_config.valid'), output)
24
+ assert_match(I18n.t('form.setup_gpg_key.valid'), output)
25
+ end
26
+
27
+ def test_01_add_item
28
+ data = @fixtures['add']
29
+
30
+ output = %x(
31
+ echo #{@password} | mpw add \
32
+ --host #{data['host']} \
33
+ --port #{data['port']} \
34
+ --protocol #{data['protocol']} \
35
+ --user #{data['user']} \
36
+ --comment '#{data['comment']}' \
37
+ --group #{data['group']} \
38
+ --random
39
+ )
40
+ puts output
41
+ assert_match(I18n.t('form.add_item.valid'), output)
42
+
43
+ output = %x(echo #{@password} | mpw list)
44
+ puts output
45
+ assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
46
+ assert_match(data['user'], output)
47
+ assert_match(data['comment'], output)
48
+ assert_match(data['group'], output)
49
+ end
50
+
51
+ def test_02_search
52
+ data = @fixtures['add']
53
+
54
+ output = %x(echo #{@password} | mpw list --group #{data['group']})
55
+ assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
56
+
57
+ output = %x(echo #{@password} | mpw list --pattern #{data['host']})
58
+ assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
59
+
60
+ output = %x(echo #{@password} | mpw list --pattern #{data['comment']})
61
+ assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
62
+
63
+ output = %x(echo #{@password} | mpw list --group R1Pmfbp626TFpjlr)
64
+ assert_match(I18n.t('display.nothing'), output)
65
+
66
+ output = %x(echo #{@password} | mpw list --pattern h1IfnKqamaGM9oEX)
67
+ assert_match(I18n.t('display.nothing'), output)
68
+ end
69
+
70
+ def test_03_update_item
71
+ data = @fixtures['update']
72
+
73
+ output = %x(
74
+ echo #{@password} | mpw update \
75
+ -p #{@fixtures['add']['host']} \
76
+ --host #{data['host']} \
77
+ --port #{data['port']} \
78
+ --protocol #{data['protocol']} \
79
+ --user #{data['user']} \
80
+ --comment '#{data['comment']}' \
81
+ --new-group #{data['group']}
82
+ )
83
+ puts output
84
+ assert_match(I18n.t('form.update_item.valid'), output)
85
+
86
+ output = %x(echo #{@password} | mpw list)
87
+ puts output
88
+ assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
89
+ assert_match(data['user'], output)
90
+ assert_match(data['comment'], output)
91
+ assert_match(data['group'], output)
92
+ end
93
+
94
+ def test_04_delete_item
95
+ output = %x(echo "#{@password}\ny" | mpw delete -p #{@fixtures['update']['host']})
96
+ puts output
97
+ assert_match(I18n.t('form.delete_item.valid'), output)
98
+
99
+ output = %x(echo #{@password} | mpw list)
100
+ puts output
101
+ assert_match(I18n.t('display.nothing'), output)
102
+ end
103
+
104
+ def test_05_import_export
105
+ file_import = './test/files/fixtures-import.yml'
106
+ file_export = '/tmp/test-mpw.yml'
107
+
108
+ output = %x(echo #{@password} | mpw import --file #{file_import})
109
+ assert_match(I18n.t('form.import.valid', file: file_import), output)
110
+
111
+ output = %x(echo #{@password} | mpw export --file #{file_export})
112
+ assert_match(I18n.t('form.export.valid', file: file_export), output)
113
+ assert(File.exist?(file_export))
114
+ assert_equal(YAML.load_file(file_export).length, 2)
115
+
116
+ YAML.load_file(file_import).each_value do |import|
117
+ error = true
118
+
119
+ YAML.load_file(file_export).each_value do |export|
120
+ next if import['host'] != export['host']
121
+
122
+ %w[user group password protocol port otp_key comment].each do |key|
123
+ assert_equal(import[key].to_s, export[key].to_s)
124
+ end
125
+
126
+ error = false
127
+ end
128
+ assert(!error)
129
+ end
130
+ end
131
+
132
+ def test_06_copy
133
+ data = YAML.load_file('./test/files/fixtures-import.yml')[1]
134
+
135
+ output = %x(
136
+ echo "#{@password}\np\nq" | mpw copy \
137
+ --disable-clipboard \
138
+ -p #{data['host']}
139
+ )
140
+ puts output
141
+ assert_match(data['password'], output)
142
+ end
143
+
144
+ def test_07_setup_wallet
145
+ path = '/tmp/'
146
+ gpg_key = 'test2@example.com'
147
+
148
+ output = %x(echo #{@password} | mpw wallet --add-gpg-key #{gpg_key})
149
+ puts output
150
+ assert_match(I18n.t('form.add_key.valid'), output)
151
+
152
+ output = %x(echo #{@password} | mpw wallet --list-keys)
153
+ puts output
154
+ assert_match("| #{@gpg_key}", output)
155
+ assert_match("| #{gpg_key}", output)
156
+
157
+ output = %x(echo #{@password} | mpw wallet --delete-gpg-key #{gpg_key})
158
+ puts output
159
+ assert_match(I18n.t('form.delete_key.valid'), output)
160
+
161
+ output = %x(echo #{@password} | mpw wallet --list-keys)
162
+ puts output
163
+ assert_match("| #{@gpg_key}", output)
164
+ assert_no_match(/\| #{gpg_key}/, output)
165
+
166
+ output = %x(mpw wallet)
167
+ puts output
168
+ assert_match('| default', output)
169
+
170
+ output = %x(mpw wallet --path #{path})
171
+ puts output
172
+ assert_match(I18n.t('form.set_wallet_path.valid'), output)
173
+
174
+ output = %x(mpw config)
175
+ puts output
176
+ assert_match(%r{path_wallet_default.+\| #{path}/default.mpw}, output)
177
+ assert(File.exist?("#{path}/default.mpw"))
178
+
179
+ output = %x(mpw wallet --default-path)
180
+ puts output
181
+ assert_match(I18n.t('form.set_wallet_path.valid'), output)
182
+
183
+ output = %x(mpw config)
184
+ puts output
185
+ assert_no_match(/path_wallet_default/, output)
186
+ end
187
+
188
+ def test_08_setup_config
189
+ gpg_key = 'user@example2.com'
190
+ gpg_exe = '/usr/bin/gpg2'
191
+ wallet_dir = '/tmp/mpw'
192
+ length = 24
193
+ wallet = 'work'
194
+
195
+ output = %x(
196
+ mpw config \
197
+ --gpg-exe #{gpg_exe} \
198
+ --enable-pinmode \
199
+ --disable-alpha \
200
+ --disable-special-chars \
201
+ --disable-numeric \
202
+ --length #{length} \
203
+ --wallet-dir #{wallet_dir} \
204
+ --default-wallet #{wallet}
205
+ )
206
+ puts output
207
+ assert_match(I18n.t('form.set_config.valid'), output)
208
+
209
+ output = %x(mpw config)
210
+ puts output
211
+ assert_match(/gpg_key.+\| #{@gpg_key}/, output)
212
+ assert_match(/gpg_exe.+\| #{gpg_exe}/, output)
213
+ assert_match(/pinmode.+\| true/, output)
214
+ assert_match(/default_wallet.+\| #{wallet}/, output)
215
+ assert_match(/wallet_dir.+\| #{wallet_dir}/, output)
216
+ assert_match(/password_length.+\| #{length}/, output)
217
+ %w[numeric alpha special].each do |k|
218
+ assert_match(/password_#{k}.+\| false/, output)
219
+ end
220
+
221
+ output = %x(
222
+ mpw config \
223
+ --key #{gpg_key} \
224
+ --alpha \
225
+ --special-chars \
226
+ --numeric \
227
+ --disable-pinmode
228
+ )
229
+ puts output
230
+ assert_match(I18n.t('form.set_config.valid'), output)
231
+
232
+ output = %x(mpw config)
233
+ puts output
234
+ assert_match(/gpg_key.+\| #{gpg_key}/, output)
235
+ assert_match(/pinmode.+\| false/, output)
236
+ %w[numeric alpha special].each do |k|
237
+ assert_match(/password_#{k}.+\| true/, output)
238
+ end
239
+ end
240
+
241
+ def test_09_generate_password
242
+ length = 24
243
+
244
+ output = %x(
245
+ mpw genpwd \
246
+ --length #{length} \
247
+ --alpha
248
+ )
249
+ assert_match(/[a-zA-Z]{#{length}}/, output)
250
+
251
+ output = %x(
252
+ mpw genpwd \
253
+ --length #{length} \
254
+ --numeric
255
+ )
256
+ assert_match(/[0-9]{#{length}}/, output)
257
+
258
+ output = %x(
259
+ mpw genpwd \
260
+ --length #{length} \
261
+ --special-chars
262
+ )
263
+ assert_no_match(/[a-zA-Z0-9]/, output)
264
+ end
265
+ end