mpw 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef1f27beb1e85561895eeaec053e65339d404a18
4
- data.tar.gz: b69ea026f373f5b3039a71776e4acc9ec4d44e3c
3
+ metadata.gz: 38a2af169d14523a873d9ca733b19ce2f7856830
4
+ data.tar.gz: 7d3365270ad43f53316ce9519e7b2feb01ee73ca
5
5
  SHA512:
6
- metadata.gz: 46c34128083ac6a70fcbb1ef3b9e0fb29b9bb58419a51d0b3623d0dbc34dd8b6371c8bed9c13b4dc61ffd7c9d0a2de06f37a61477e296b78934878edcf62959c
7
- data.tar.gz: 0ff807e8c938d31abec7bce1f4a18b4f795eacf87065c8166430ffc9f86e3a9533f9098032db6ca94455345761099ea89c006072017868cac29aff6e1a381443
6
+ metadata.gz: 3d8931b9d43a244cae00039e66cc9575d21cb6ab4b7448104eccbaa70bc95bf0e3ebbec3c2297504fd5d59b2f6610764777e94e59750e2d7020b71bc9d2449c3
7
+ data.tar.gz: eea9b9f36d27dc035e1c18a04a0ef6c3cdda0b71fb1709f72dc30739373c088c06b40b0464fa42d196fda2020d1f58ff38489ed0f24caf22479dcfce0df8e9fe
data/Gemfile CHANGED
@@ -7,3 +7,4 @@ gem 'colorize'
7
7
  gem 'net-ssh'
8
8
  gem 'net-scp'
9
9
  gem 'clipboard'
10
+ gem 'rotp'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.2.0
data/bin/mpw CHANGED
@@ -16,11 +16,15 @@
16
16
  # along with this program; if not, write to the Free Software
17
17
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
18
 
19
+ $: << File.expand_path('../../lib', __FILE__)
20
+
19
21
  require 'optparse'
20
- require 'pathname'
21
22
  require 'locale'
22
23
  require 'set'
23
24
  require 'i18n'
25
+ require 'mpw/mpw'
26
+ require 'mpw/config'
27
+ require 'mpw/cli'
24
28
 
25
29
  # --------------------------------------------------------- #
26
30
  # Set local
@@ -32,15 +36,8 @@ if defined?(I18n.enforce_available_locales)
32
36
  I18n.enforce_available_locales = true
33
37
  end
34
38
 
35
- APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
36
-
37
- # TODO
38
- require "#{APP_ROOT}/../lib/mpw/mpw.rb"
39
- require "#{APP_ROOT}/../lib/mpw/config.rb"
40
- require "#{APP_ROOT}/../lib/mpw/ui/cli.rb"
41
-
42
39
  I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
43
- I18n.load_path = Dir["#{APP_ROOT}/../i18n/*.yml"]
40
+ I18n.load_path = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
44
41
  I18n.default_locale = :en
45
42
  I18n.locale = lang.to_sym
46
43
 
@@ -48,8 +45,10 @@ I18n.locale = lang.to_sym
48
45
  # Options
49
46
  # --------------------------------------------------------- #
50
47
 
48
+ options_password = {}
51
49
  options = {}
52
50
  options[:force] = false
51
+ options[:otp] = false
53
52
  options[:sync] = true
54
53
  options[:clipboard] = true
55
54
  options[:group] = nil
@@ -97,8 +96,7 @@ OptionParser.new do |opts|
97
96
  end
98
97
 
99
98
  opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
100
- puts MPW::MPW::password(length)
101
- exit 0
99
+ options_password[:length] = length
102
100
  end
103
101
 
104
102
  opts.on('-h', '--help', I18n.t('option.help')) do
@@ -118,10 +116,18 @@ OptionParser.new do |opts|
118
116
  options[:key] = key
119
117
  end
120
118
 
119
+ opts.on('-n', '--numeric', I18n.t('option.numeric')) do
120
+ options_password[:numeric] = true
121
+ end
122
+
121
123
  opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
122
124
  options[:sync] = false
123
125
  end
124
126
 
127
+ opts.on('-O', '--otp', I18n.t('option.otp')) do
128
+ options[:otp] = true
129
+ end
130
+
125
131
  opts.on('-s', '--show [SEARCH]', I18n.t('option.show')) do |search|
126
132
  search.nil? ? (options[:show] = '') : (options[:show] = search)
127
133
  end
@@ -141,15 +147,29 @@ OptionParser.new do |opts|
141
147
  opts.on('-W', '--setup-wallet', I18n.t('option.setup_wallet')) do
142
148
  options[:setup_wallet] = true
143
149
  end
150
+
151
+ opts.on('-x', '--special-chars', I18n.t('option.special_chars')) do
152
+ options_password[:special] = true
153
+ end
154
+
155
+ opts.on('-y', '--alpha', I18n.t('option.alpha')) do
156
+ options_password[:alpha] = true
157
+ end
144
158
  end.parse!
145
159
 
146
160
  # --------------------------------------------------------- #
147
161
  # Main
148
162
  # --------------------------------------------------------- #
149
163
 
164
+ # Generate password
165
+ if not options_password.empty?
166
+ puts MPW::MPW::password(options_password)
167
+ exit 0
168
+ end
169
+
150
170
  begin
151
171
  config = MPW::Config.new(options[:config])
152
- cli = MPW::Cli.new(config, options[:clipboard], options[:sync])
172
+ cli = MPW::Cli.new(config, options[:clipboard], options[:sync], options[:otp])
153
173
 
154
174
  # Setup a new config
155
175
  if not options[:setup].nil?
data/i18n/en.yml CHANGED
@@ -37,6 +37,7 @@ en:
37
37
 
38
38
  option:
39
39
  add: "Add an item or key"
40
+ alpha: "Use letter to generate a password"
40
41
  config: "Specify the configuration file to use"
41
42
  clipboard: "Disable the clipboard feature"
42
43
  export: "Export a wallet in an yaml file"
@@ -49,8 +50,10 @@ en:
49
50
  import: "Import item since a yaml file"
50
51
  key: "Specify the key name, to use with the options [--add | --delete | --update]"
51
52
  no_sync: "Disable synchronization with the server"
53
+ numeric: "Use number to generate a password"
52
54
  setup: "Create a new configuration file"
53
55
  setup_wallet: "Create a new configuration file for a wallet"
56
+ special_chars: "Use special char to generate a password"
54
57
  show: "Search and show the items"
55
58
  show_all: "List all items"
56
59
  remove: "Delete an item"
@@ -72,11 +75,19 @@ en:
72
75
  password: "Enter the the password: "
73
76
  port: "Enter the connection port (optional): "
74
77
  comment: "Enter a comment (optional): "
78
+ otp_key: "Enter the otp secret (base32 secret key): "
75
79
  valid: "Item has been added!"
76
80
  clipboard:
81
+ choice: "What do you want to copy ? [q = quit, p = password, l = login]: "
77
82
  clean: "The clipboard has been cleaned."
78
- login: "The login has been copied in clipboard, press any key to continue."
83
+ login: "The login has been copied in clipboard."
79
84
  password: "The password has been copied in clipboard for 30s!"
85
+ otp: "The OTP code has been copied for %{time}s!"
86
+ help:
87
+ name: "Help"
88
+ login: "Press <l> to copy the login"
89
+ password: "Press <p> to copy the password"
90
+ otp_code: "Press <o> to copy the otp code"
80
91
  delete_key:
81
92
  valid: "Key has been deleted!"
82
93
  delete_item:
@@ -125,6 +136,7 @@ en:
125
136
  password: "Enter the the password: "
126
137
  port: "Enter the connection port [%{port}]: "
127
138
  comment: "Enter a comment [%{comment}]: "
139
+ otp_key: "Enter the otp secret (base32 secret key): "
128
140
  valid: "Item has been updated!"
129
141
  export:
130
142
  valid: "The export in %{file} is succesfull!"
@@ -136,7 +148,9 @@ en:
136
148
  group: "Group"
137
149
  login: "Login"
138
150
  name: "Name"
151
+ no_group: "Without group"
139
152
  nothing: "No matches!"
153
+ otp_code: "OTP code"
140
154
  password: "Password"
141
155
  port: "Port"
142
156
  protocol: "Protocol"
data/i18n/fr.yml CHANGED
@@ -37,6 +37,7 @@ fr:
37
37
 
38
38
  option:
39
39
  add: "Ajoute un élément ou une clé"
40
+ alpha: "Utilise des lettres dans la génération d'un mot de passe"
40
41
  config: "Spécifie le fichier de configuration à utiliser"
41
42
  clipboard: "Désactive la fonction presse papier"
42
43
  export: "Exporte un portefeuille dans un fichier yaml"
@@ -49,8 +50,10 @@ fr:
49
50
  import: "Importe des éléments depuis un fichier yaml"
50
51
  key: "Spécifie le nom d'une clé, à utiliser avec les options [--add | --delete | --update]"
51
52
  no_sync: "Désactive la synchronisation avec le serveur"
53
+ numeric: "Utilise des chiffre dans la génération d'un mot de passe"
52
54
  setup: "Création d'un nouveau fichier de configuration"
53
55
  setup_wallet: "Création d'un nouveau fichier de configuration pour un portefeuille"
56
+ special_chars: "Utilise des charactères speciaux dans la génération d'un mot de passe"
54
57
  show: "Recherche et affiche les éléments"
55
58
  show_all: "Liste tous les éléments"
56
59
  remove: "Supprime un élément"
@@ -72,11 +75,19 @@ fr:
72
75
  password: "Entrez le mot de passe: "
73
76
  port: "Entrez le port de connexion (optionnel): "
74
77
  comment: "Entrez un commentaire (optionnel): "
78
+ otp_key: "Entrez le secret OTP: "
75
79
  valid: "L'élément a bien été ajouté!"
76
80
  clipboard:
81
+ choice: "Que voulez-vous copier ? : "
77
82
  clean: "Le presse papier a été nettoyé."
78
- login: "L'identifiant a été copié dans le presse papier, pressez n'importe quelle touche pour continuer."
83
+ login: "L'identifiant a été copié dans le presse papier"
79
84
  password: "Le mot de passe a été copié dans le presse papier pour 30s!"
85
+ otp: "Le code OTP a été copié dans le presse papier il est valable %{time}s!"
86
+ help:
87
+ name: "Aide"
88
+ login: "Pressez <l> pour copier l'identifiant"
89
+ password: "Pressez <p> pour copier le mot de passe"
90
+ otp_code: "Pressez <o> pour copier le code OTP"
80
91
  delete_key:
81
92
  valid: "La clé a bien été supprimée!"
82
93
  delete_item:
@@ -125,6 +136,7 @@ fr:
125
136
  password: "Entrez le mot de passe: "
126
137
  port: "Entrez un port de connexion [%{port}]: "
127
138
  comment: "Entrez un commentaire [%{comment}]: "
139
+ otp_key: "Entrez le secret OTP: "
128
140
  valid: "L'élément a bien été mis à jour!"
129
141
  export:
130
142
  valid: "L'export dans %{file} est un succès!"
@@ -136,7 +148,9 @@ fr:
136
148
  group: "Groupe"
137
149
  login: "Identifiant"
138
150
  name: "Nom"
151
+ no_group: "Sans groupe"
139
152
  nothing: "Aucun résultat!"
153
+ otp_code: "Code OTP"
140
154
  password: "Mot de passe"
141
155
  port: "Port"
142
156
  protocol: "Protocol"
@@ -21,10 +21,8 @@ require 'i18n'
21
21
  require 'colorize'
22
22
  require 'highline/import'
23
23
  require 'clipboard'
24
-
25
- #TODO
26
- require "#{APP_ROOT}/../lib/mpw/item.rb"
27
- require "#{APP_ROOT}/../lib/mpw/mpw.rb"
24
+ require 'mpw/item'
25
+ require 'mpw/mpw'
28
26
 
29
27
  module MPW
30
28
  class Cli
@@ -32,10 +30,13 @@ class Cli
32
30
  # Constructor
33
31
  # @args: config -> the config
34
32
  # sync -> boolean for sync or not
35
- def initialize(config, clipboard=true, sync=true)
33
+ # clipboard -> enable clopboard
34
+ # otp -> enable otp
35
+ def initialize(config, clipboard=true, sync=true, otp=false)
36
36
  @config = config
37
37
  @clipboard = clipboard
38
38
  @sync = sync
39
+ @otp = otp
39
40
  end
40
41
 
41
42
  # Create a new config file
@@ -160,7 +161,12 @@ class Cli
160
161
  result.each do |item|
161
162
  if group != item.group
162
163
  group = item.group
163
- puts "#{I18n.t('display.group')}: #{group}".yellow
164
+
165
+ if group.empty?
166
+ puts I18n.t('display.no_group').yellow
167
+ else
168
+ puts "\n#{group}".yellow
169
+ end
164
170
  end
165
171
 
166
172
  print " |_ ".yellow
@@ -171,6 +177,8 @@ class Cli
171
177
 
172
178
  i += 1
173
179
  end
180
+
181
+ print "\n"
174
182
  choice = ask(I18n.t('form.select')).to_i
175
183
 
176
184
  if choice >= 1 and choice < i
@@ -197,12 +205,20 @@ class Cli
197
205
  puts item.protocol
198
206
  print "#{I18n.t('display.login')}: ".cyan
199
207
  puts item.user
200
- print "#{I18n.t('display.password')}: ".cyan
208
+
201
209
  if @clipboard
210
+ print "#{I18n.t('display.password')}: ".cyan
202
211
  puts '***********'
203
212
  else
213
+ print "#{I18n.t('display.password')}: ".cyan
204
214
  puts @mpw.get_password(item.id)
215
+
216
+ if @mpw.get_otp_code(item.id) > 0
217
+ print "#{I18n.t('display.otp_code')}: ".cyan
218
+ puts "#{@mpw.get_otp_code(item.id)} (#{@mpw.get_otp_remaining_time}s)"
219
+ end
205
220
  end
221
+
206
222
  print "#{I18n.t('display.port')}: ".cyan
207
223
  puts item.port
208
224
  print "#{I18n.t('display.comment')}: ".cyan
@@ -212,18 +228,51 @@ class Cli
212
228
  end
213
229
 
214
230
  # Copy in clipboard the login and password
231
+ # @args: item -> the item
215
232
  def clipboard(item)
216
- Clipboard.copy(item.user)
217
- print "\n#{I18n.t('form.clipboard.login')}".green
218
- gets
233
+ pid = nil
234
+
235
+ # Security: force quit after 90s
236
+ Thread.new do
237
+ sleep 90
238
+ exit
239
+ end
240
+
241
+ while true
242
+ choice = ask(I18n.t('form.clipboard.choice')).to_s
243
+
244
+ case choice
245
+ when 'q', 'quit'
246
+ break
247
+
248
+ when 'l', 'login'
249
+ Clipboard.copy(item.user)
250
+ puts I18n.t('form.clipboard.login').green
219
251
 
220
- Clipboard.copy(@mpw.get_password(item.id))
221
- puts I18n.t('form.clipboard.password').yellow
252
+ when 'p', 'password'
253
+ Clipboard.copy(@mpw.get_password(item.id))
254
+ puts I18n.t('form.clipboard.password').yellow
222
255
 
223
- sleep(30)
256
+ Thread.new do
257
+ sleep 30
258
+
259
+ Clipboard.clear
260
+ end
261
+
262
+ when 'o', 'otp'
263
+ Clipboard.copy(@mpw.get_otp_code(item.id))
264
+ puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
265
+
266
+ else
267
+ puts "----- #{I18n.t('form.clipboard.help.name')} -----".cyan
268
+ puts I18n.t('form.clipboard.help.login')
269
+ puts I18n.t('form.clipboard.help.password')
270
+ puts I18n.t('form.clipboard.help.otp_code')
271
+ next
272
+ end
273
+ end
224
274
 
225
275
  Clipboard.clear
226
- puts I18n.t('form.clipboard.clean').green
227
276
  rescue SystemExit, Interrupt
228
277
  Clipboard.clear
229
278
  end
@@ -254,6 +303,7 @@ class Cli
254
303
  @wallet_file = wallets[choice-1]
255
304
  else
256
305
  puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
306
+ exit 2
257
307
  end
258
308
  end
259
309
  else
@@ -301,10 +351,15 @@ class Cli
301
351
  options[:port] = ask(I18n.t('form.add_item.port')).to_s
302
352
  options[:comment] = ask(I18n.t('form.add_item.comment')).to_s
303
353
 
354
+ if @otp
355
+ otp_key = ask(I18n.t('form.add_item.otp_key')).to_s
356
+ end
357
+
304
358
  item = Item.new(options)
305
359
 
306
360
  @mpw.add(item)
307
361
  @mpw.set_password(item.id, password)
362
+ @mpw.set_otp_key(item.id, otp_key)
308
363
  @mpw.write_data
309
364
  @mpw.sync(true) if @sync
310
365
 
@@ -330,10 +385,15 @@ class Cli
330
385
  options[:port] = ask(I18n.t('form.update_item.port' , port: item.port)).to_s
331
386
  options[:comment] = ask(I18n.t('form.update_item.comment' , comment: item.comment)).to_s
332
387
 
388
+ if @otp
389
+ otp_key = ask(I18n.t('form.update_item.otp_key')).to_s
390
+ end
391
+
333
392
  options.delete_if { |k,v| v.empty? }
334
-
393
+
335
394
  item.update(options)
336
395
  @mpw.set_password(item.id, password) if not password.empty?
396
+ @mpw.set_otp_key(item.id, otp_key) if not otp_key.to_s.empty?
337
397
  @mpw.write_data
338
398
  @mpw.sync(true) if @sync
339
399
 
data/lib/mpw/item.rb CHANGED
@@ -21,8 +21,6 @@ require 'i18n'
21
21
  module MPW
22
22
  class Item
23
23
 
24
- attr_accessor :error_msg
25
-
26
24
  attr_accessor :id
27
25
  attr_accessor :name
28
26
  attr_accessor :group
@@ -41,8 +39,7 @@ class Item
41
39
  # raise an error if the hash hasn't the key name
42
40
  def initialize(options={})
43
41
  if not options.has_key?(:name) or options[:name].to_s.empty?
44
- @error_msg = I18n.t('error.update.name_empty')
45
- raise @error_msg
42
+ raise I18n.t('error.update.name_empty')
46
43
  end
47
44
 
48
45
  if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?
@@ -60,11 +57,9 @@ class Item
60
57
 
61
58
  # Update the item
62
59
  # @args: options -> a hash of parameter
63
- # @rtrn: true if the item is update
64
60
  def update(options={})
65
61
  if options.has_key?(:name) and options[:name].to_s.empty?
66
- @error_msg = I18n.t('error.update.name_empty')
67
- return false
62
+ raise I18n.t('error.update.name_empty')
68
63
  end
69
64
 
70
65
  @name = options[:name] if options.has_key?(:name)
@@ -75,8 +70,6 @@ class Item
75
70
  @port = options[:port].to_i if options.has_key?(:port) and not options[:port].to_s.empty?
76
71
  @comment = options[:comment] if options.has_key?(:comment)
77
72
  @last_edit = Time.now.to_i if not options.has_key?(:no_update_last_edit)
78
-
79
- return true
80
73
  end
81
74
 
82
75
  # Update last_sync
@@ -85,7 +78,6 @@ class Item
85
78
  end
86
79
 
87
80
  # Delete all data
88
- # @rtrn: true
89
81
  def delete
90
82
  @id = nil
91
83
  @name = nil
@@ -98,8 +90,6 @@ class Item
98
90
  @created = nil
99
91
  @last_edit = nil
100
92
  @last_sync = nil
101
-
102
- return true
103
93
  end
104
94
 
105
95
  def empty?
data/lib/mpw/mpw.rb CHANGED
@@ -20,9 +20,8 @@ require 'rubygems/package'
20
20
  require 'gpgme'
21
21
  require 'i18n'
22
22
  require 'yaml'
23
-
24
- #TODO
25
- require "#{APP_ROOT}/../lib/mpw/item.rb"
23
+ require 'rotp'
24
+ require 'mpw/item'
26
25
 
27
26
  module MPW
28
27
  class MPW
@@ -45,6 +44,7 @@ class MPW
45
44
  @data = []
46
45
  @keys = {}
47
46
  @passwords = {}
47
+ @otp_keys = {}
48
48
 
49
49
  data = nil
50
50
 
@@ -70,6 +70,10 @@ class MPW
70
70
 
71
71
  when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
72
72
  @passwords[Regexp.last_match('id')] = f.read
73
+
74
+ when /^wallet\/otp_keys\/(?<id>[a-zA-Z0-9]+)\.gpg$/
75
+ @otp_keys[Regexp.last_match('id')] = f.read
76
+
73
77
  else
74
78
  next
75
79
  end
@@ -120,6 +124,8 @@ class MPW
120
124
  )
121
125
  end
122
126
 
127
+ @config['last_update'] = Time.now.to_i
128
+
123
129
  Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
124
130
  data_encrypt = encrypt(data.to_yaml)
125
131
  tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
@@ -137,6 +143,12 @@ class MPW
137
143
  end
138
144
  end
139
145
 
146
+ @otp_keys.each do |id, key|
147
+ tar.add_file_simple("wallet/otp_keys/#{id}.gpg", 0400, key.length) do |io|
148
+ io.write(key)
149
+ end
150
+ end
151
+
140
152
  @keys.each do |id, key|
141
153
  tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
142
154
  io.write(key)
@@ -167,7 +179,7 @@ class MPW
167
179
  # args: id -> the item id
168
180
  # password -> the new password
169
181
  def set_password(id, password)
170
- salt = MPW::password(Random.rand(4..9))
182
+ salt = MPW::password(length: Random.rand(4..9))
171
183
  password = "$#{salt}::#{password}"
172
184
 
173
185
  @passwords[id] = encrypt(password)
@@ -319,13 +331,13 @@ class MPW
319
331
  # @args: force -> force the sync
320
332
  def sync(force=false)
321
333
  return if @config.empty? or @config['sync']['type'].to_s.empty?
322
- return if get_last_sync < Time.now.to_i + 300 and not force
323
-
334
+ return if get_last_sync + 300 > Time.now.to_i and not force
335
+
324
336
  tmp_file = "#{@wallet_file}.sync"
325
337
 
326
338
  case @config['sync']['type']
327
339
  when 'sftp', 'scp', 'ssh'
328
- require "#{APP_ROOT}/../lib/mpw/sync/ssh.rb"
340
+ require "mpw/sync/ssh"
329
341
  sync = SyncSSH.new(@config['sync'])
330
342
  when 'ftp'
331
343
  require 'mpw/sync/ftp'
@@ -342,7 +354,7 @@ class MPW
342
354
 
343
355
  File.unlink(tmp_file) if File.exist?(tmp_file)
344
356
 
345
- return if remote.get_last_sync == get_last_sync
357
+ return if remote.get_last_sync == @config['last_update']
346
358
 
347
359
  if not remote.to_s.empty?
348
360
  @data.each do |item|
@@ -413,22 +425,54 @@ class MPW
413
425
  raise "#{I18n.t('error.sync.general')}\n#{e}"
414
426
  end
415
427
 
428
+ # Set an opt key
429
+ # args: id -> the item id
430
+ # key -> the new key
431
+ def set_otp_key(id, key)
432
+ @otp_keys[id] = encrypt(key)
433
+ end
434
+
435
+ # Get an otp code
436
+ # @args: id -> the item id
437
+ # @rtrn: an otp code
438
+ def get_otp_code(id)
439
+ if not @otp_keys.has_key?(id)
440
+ return 0
441
+ else
442
+ return ROTP::TOTP.new(decrypt(@otp_keys[id])).now
443
+ end
444
+ end
445
+
446
+ # Get remaining time before expire otp code
447
+ # @rtrn: return time in seconde
448
+ def get_otp_remaining_time
449
+ return (Time.now.utc.to_i / 30 + 1) * 30 - Time.now.utc.to_i
450
+ end
451
+
416
452
  # Generate a random password
417
- # @args: length -> the length password
453
+ # @args: options -> :length, :special, :alpha, :numeric
418
454
  # @rtrn: a random string
419
- def self.password(length=8)
420
- if length.to_i <= 0
455
+ def self.password(options={})
456
+ if not options.include?(:length) or options[:length].to_i <= 0
421
457
  length = 8
458
+ elsif options[:length].to_i >= 32768
459
+ length = 32768
422
460
  else
423
- length = length.to_i
461
+ length = options[:length].to_i
424
462
  end
425
463
 
464
+ chars = []
465
+ chars += [*('!'..'?')] - [*('0'..'9')] if options.include?(:special)
466
+ chars += [*('A'..'Z'),*('a'..'z')] if options.include?(:alpha)
467
+ chars += [*('0'..'9')] if options.include?(:numeric)
468
+ chars = [*('A'..'Z'),*('a'..'z'),*('0'..'9')] if chars.empty?
469
+
426
470
  result = ''
427
471
  while length > 62 do
428
- result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(62).join
472
+ result << chars.sample(62).join
429
473
  length -= 62
430
474
  end
431
- result << ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(length).join
475
+ result << chars.sample(length).join
432
476
 
433
477
  return result
434
478
  end
data/mpw.gemspec CHANGED
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_dependency "net-ssh"
26
26
  spec.add_dependency "net-scp"
27
27
  spec.add_dependency "clipboard"
28
+ spec.add_dependency "rotp"
28
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mpw
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrien Waksberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-09 00:00:00.000000000 Z
11
+ date: 2016-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rotp
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  description: Manage your passwords in all security with MPW, we use GPG to encrypt
126
140
  your passwords
127
141
  email:
@@ -140,12 +154,12 @@ files:
140
154
  - bin/mpw
141
155
  - i18n/en.yml
142
156
  - i18n/fr.yml
157
+ - lib/mpw/cli.rb
143
158
  - lib/mpw/config.rb
144
159
  - lib/mpw/item.rb
145
160
  - lib/mpw/mpw.rb
146
161
  - lib/mpw/sync/ftp.rb
147
162
  - lib/mpw/sync/ssh.rb
148
- - lib/mpw/ui/cli.rb
149
163
  - mpw.gemspec
150
164
  - test/files/fixtures.yml
151
165
  - test/files/test_import.csv