mpw 4.0.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +122 -0
- data/.travis.yml +2 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +7 -9
- data/README.md +1 -7
- data/VERSION +1 -1
- data/bin/mpw +25 -25
- data/bin/mpw-add +54 -25
- data/bin/mpw-config +74 -62
- data/bin/mpw-copy +25 -30
- data/bin/mpw-delete +24 -29
- data/bin/mpw-export +27 -32
- data/bin/mpw-genpwd +22 -22
- data/bin/mpw-import +20 -25
- data/bin/mpw-list +24 -29
- data/bin/mpw-update +61 -32
- data/bin/mpw-wallet +48 -73
- data/i18n/en.yml +15 -26
- data/i18n/fr.yml +15 -26
- data/lib/mpw/cli.rb +554 -546
- data/lib/mpw/config.rb +168 -139
- data/lib/mpw/item.rb +75 -82
- data/lib/mpw/mpw.rb +328 -465
- data/mpw.gemspec +11 -10
- data/templates/add_form.erb +8 -8
- data/templates/update_form.erb +7 -7
- data/test/test_config.rb +70 -55
- data/test/test_item.rb +167 -167
- data/test/test_mpw.rb +132 -132
- data/test/test_translate.rb +23 -23
- data/test/tests.rb +1 -1
- metadata +4 -45
- data/lib/mpw/sync/ftp.rb +0 -68
- data/lib/mpw/sync/ssh.rb +0 -67
data/lib/mpw/config.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
# MPW is a software to crypt and manage your passwords
|
3
|
-
# Copyright (C)
|
4
|
-
#
|
3
|
+
# Copyright (C) 2017 Adrien Waksberg <mpw@yae.im>
|
4
|
+
#
|
5
5
|
# This program is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU General Public License
|
7
7
|
# as published by the Free Software Foundation; either version 2
|
8
8
|
# of the License, or (at your option) any later version.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# This program is distributed in the hope that it will be useful,
|
11
11
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
12
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
13
|
# GNU General Public License for more details.
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# You should have received a copy of the GNU General Public License
|
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.
|
@@ -20,140 +20,169 @@ require 'gpgme'
|
|
20
20
|
require 'yaml'
|
21
21
|
require 'i18n'
|
22
22
|
require 'fileutils'
|
23
|
-
|
23
|
+
|
24
24
|
module MPW
|
25
|
-
class Config
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
end
|
25
|
+
class Config
|
26
|
+
attr_accessor :error_msg
|
27
|
+
|
28
|
+
attr_accessor :gpg_key
|
29
|
+
attr_accessor :lang
|
30
|
+
attr_accessor :config_dir
|
31
|
+
attr_accessor :default_wallet
|
32
|
+
attr_accessor :wallet_dir
|
33
|
+
attr_accessor :wallet_paths
|
34
|
+
attr_accessor :gpg_exe
|
35
|
+
attr_accessor :password
|
36
|
+
attr_accessor :pinmode
|
37
|
+
|
38
|
+
# Constructor
|
39
|
+
# @args: config_file -> the specify config file
|
40
|
+
def initialize(config_file = nil)
|
41
|
+
@config_file = config_file
|
42
|
+
@config_dir =
|
43
|
+
if /darwin/ =~ RUBY_PLATFORM
|
44
|
+
"#{Dir.home}/Library/Preferences/mpw"
|
45
|
+
elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
|
46
|
+
"#{Dir.home}/AppData/Local/mpw"
|
47
|
+
else
|
48
|
+
"#{Dir.home}/.config/mpw"
|
49
|
+
end
|
50
|
+
|
51
|
+
@config_file = "#{@config_dir}/mpw.cfg" if @config_file.to_s.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Create a new config file
|
55
|
+
# @args: options -> hash with values
|
56
|
+
# @rtrn: true if le config file is create
|
57
|
+
def setup(**options)
|
58
|
+
gpg_key = options[:gpg_key] || @gpg_key
|
59
|
+
lang = options[:lang] || @lang
|
60
|
+
wallet_dir = options[:wallet_dir] || @wallet_dir
|
61
|
+
default_wallet = options[:default_wallet] || @default_wallet
|
62
|
+
gpg_exe = options[:gpg_exe] || @gpg_exe
|
63
|
+
pinmode = options[:pinmode] || @pinmode
|
64
|
+
password = { numeric: true,
|
65
|
+
alpha: true,
|
66
|
+
special: false,
|
67
|
+
length: 16 }
|
68
|
+
|
69
|
+
%w[numeric special alpha length].each do |k|
|
70
|
+
if options.key?("pwd_#{k}".to_sym)
|
71
|
+
password[k.to_sym] = options["pwd_#{k}".to_sym]
|
72
|
+
elsif !@password.nil? && @password.key?(k.to_sym)
|
73
|
+
password[k.to_sym] = @password[k.to_sym]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
unless gpg_key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
|
78
|
+
raise I18n.t('error.config.key_bad_format')
|
79
|
+
end
|
80
|
+
|
81
|
+
wallet_dir = "#{@config_dir}/wallets" if wallet_dir.to_s.empty?
|
82
|
+
config = { 'gpg_key' => gpg_key,
|
83
|
+
'lang' => lang,
|
84
|
+
'wallet_dir' => wallet_dir,
|
85
|
+
'default_wallet' => default_wallet,
|
86
|
+
'gpg_exe' => gpg_exe,
|
87
|
+
'password' => password,
|
88
|
+
'pinmode' => pinmode,
|
89
|
+
'wallet_paths' => @wallet_paths }
|
90
|
+
|
91
|
+
FileUtils.mkdir_p(@config_dir, mode: 0700)
|
92
|
+
FileUtils.mkdir_p(wallet_dir, mode: 0700)
|
93
|
+
|
94
|
+
File.open(@config_file, 'w') do |file|
|
95
|
+
file << config.to_yaml
|
96
|
+
end
|
97
|
+
rescue => e
|
98
|
+
raise "#{I18n.t('error.config.write')}\n#{e}"
|
99
|
+
end
|
100
|
+
|
101
|
+
# Setup a new gpg key
|
102
|
+
# @args: password -> the GPG key password
|
103
|
+
# name -> the name of user
|
104
|
+
# length -> length of the GPG key
|
105
|
+
# expire -> the time of expire to GPG key
|
106
|
+
# @rtrn: true if the GPG key is create, else false
|
107
|
+
def setup_gpg_key(password, name, length = 4096, expire = 0)
|
108
|
+
raise I18n.t('error.config.genkey_gpg.name') if name.to_s.empty?
|
109
|
+
raise I18n.t('error.config.genkey_gpg.password') if password.to_s.empty?
|
110
|
+
|
111
|
+
param = ''
|
112
|
+
param << '<GnupgKeyParms format="internal">' + "\n"
|
113
|
+
param << "Key-Type: RSA\n"
|
114
|
+
param << "Key-Length: #{length}\n"
|
115
|
+
param << "Subkey-Type: ELG-E\n"
|
116
|
+
param << "Subkey-Length: #{length}\n"
|
117
|
+
param << "Name-Real: #{name}\n"
|
118
|
+
param << "Name-Comment: #{name}\n"
|
119
|
+
param << "Name-Email: #{@gpg_key}\n"
|
120
|
+
param << "Expire-Date: #{expire}\n"
|
121
|
+
param << "Passphrase: #{password}\n"
|
122
|
+
param << "</GnupgKeyParms>\n"
|
123
|
+
|
124
|
+
ctx = GPGME::Ctx.new
|
125
|
+
ctx.genkey(param, nil, nil)
|
126
|
+
rescue => e
|
127
|
+
raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
|
128
|
+
end
|
129
|
+
|
130
|
+
# Load the config file
|
131
|
+
def load_config
|
132
|
+
config = YAML.load_file(@config_file)
|
133
|
+
@gpg_key = config['gpg_key']
|
134
|
+
@lang = config['lang']
|
135
|
+
@wallet_dir = config['wallet_dir']
|
136
|
+
@wallet_paths = config['wallet_paths'] || {}
|
137
|
+
@default_wallet = config['default_wallet']
|
138
|
+
@gpg_exe = config['gpg_exe']
|
139
|
+
@password = config['password'] || {}
|
140
|
+
@pinmode = config['pinmode']
|
141
|
+
|
142
|
+
raise if @gpg_key.empty? || @wallet_dir.empty?
|
143
|
+
|
144
|
+
I18n.locale = @lang.to_sym
|
145
|
+
rescue => e
|
146
|
+
raise "#{I18n.t('error.config.load')}\n#{e}"
|
147
|
+
end
|
148
|
+
|
149
|
+
# Check if private key exist
|
150
|
+
# @rtrn: true if the key exist, else false
|
151
|
+
def check_gpg_key?
|
152
|
+
ctx = GPGME::Ctx.new
|
153
|
+
ctx.each_key(@gpg_key, true) do
|
154
|
+
return true
|
155
|
+
end
|
156
|
+
|
157
|
+
false
|
158
|
+
end
|
159
|
+
|
160
|
+
# Change the path of one wallet
|
161
|
+
# @args: path -> the new directory path
|
162
|
+
# wallet -> the wallet name
|
163
|
+
def set_wallet_path(path, wallet)
|
164
|
+
path = @wallet_dir if path == 'default'
|
165
|
+
|
166
|
+
return if path == @wallet_dir && File.exist?("#{@wallet_dir}/#{wallet}.mpw")
|
167
|
+
return if path == @wallet_paths[wallet]
|
168
|
+
|
169
|
+
old_wallet_file =
|
170
|
+
if @wallet_paths.key?(wallet)
|
171
|
+
"#{@wallet_paths[wallet]}/#{wallet}.mpw"
|
172
|
+
else
|
173
|
+
"#{@wallet_dir}/#{wallet}.mpw"
|
174
|
+
end
|
175
|
+
|
176
|
+
FileUtils.mkdir_p(path) unless Dir.exist?(path)
|
177
|
+
FileUtils.mv(old_wallet_file, "#{path}/#{wallet}.mpw") if File.exist?(old_wallet_file)
|
178
|
+
|
179
|
+
if path == @wallet_dir
|
180
|
+
@wallet_paths.delete(wallet)
|
181
|
+
else
|
182
|
+
@wallet_paths[wallet] = path
|
183
|
+
end
|
184
|
+
|
185
|
+
setup
|
186
|
+
end
|
187
|
+
end
|
159
188
|
end
|
data/lib/mpw/item.rb
CHANGED
@@ -1,109 +1,102 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
# MPW is a software to crypt and manage your passwords
|
3
|
-
# Copyright (C)
|
4
|
-
#
|
3
|
+
# Copyright (C) 2017 Adrien Waksberg <mpw@yae.im>
|
4
|
+
#
|
5
5
|
# This program is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU General Public License
|
7
7
|
# as published by the Free Software Foundation; either version 2
|
8
8
|
# of the License, or (at your option) any later version.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# This program is distributed in the hope that it will be useful,
|
11
11
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
12
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
13
|
# GNU General Public License for more details.
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# You should have received a copy of the GNU General Public License
|
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
19
|
require 'i18n'
|
20
|
-
|
21
|
-
module MPW
|
22
|
-
class Item
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
21
|
+
module MPW
|
22
|
+
class Item
|
23
|
+
attr_accessor :id
|
24
|
+
attr_accessor :group
|
25
|
+
attr_accessor :host
|
26
|
+
attr_accessor :protocol
|
27
|
+
attr_accessor :user
|
28
|
+
attr_accessor :port
|
29
|
+
attr_accessor :otp
|
30
|
+
attr_accessor :comment
|
31
|
+
attr_accessor :last_edit
|
32
|
+
attr_accessor :created
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
34
|
+
# Constructor
|
35
|
+
# Create a new item
|
36
|
+
# @args: options -> a hash of parameter
|
37
|
+
# raise an error if the hash hasn't the key name
|
38
|
+
def initialize(**options)
|
39
|
+
if !options.key?(:host) || options[:host].to_s.empty?
|
40
|
+
raise I18n.t('error.update.host_empty')
|
41
|
+
end
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
43
|
+
if !options.key?(:id) || options[:id].to_s.empty? || !options.key?(:created) || options[:created].to_s.empty?
|
44
|
+
@id = generate_id
|
45
|
+
@created = Time.now.to_i
|
46
|
+
else
|
47
|
+
@id = options[:id]
|
48
|
+
@created = options[:created]
|
49
|
+
@last_edit = options[:last_edit]
|
50
|
+
options[:no_update_last_edit] = true
|
51
|
+
end
|
54
52
|
|
55
|
-
|
56
|
-
|
53
|
+
update(options)
|
54
|
+
end
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
56
|
+
# Update the item
|
57
|
+
# @args: options -> a hash of parameter
|
58
|
+
def update(**options)
|
59
|
+
if options.key?(:host) && options[:host].to_s.empty?
|
60
|
+
raise I18n.t('error.update.host_empty')
|
61
|
+
end
|
64
62
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
63
|
+
@group = options[:group] if options.key?(:group)
|
64
|
+
@host = options[:host] if options.key?(:host)
|
65
|
+
@protocol = options[:protocol] if options.key?(:protocol)
|
66
|
+
@user = options[:user] if options.key?(:user)
|
67
|
+
@port = options[:port].to_i if options.key?(:port) && !options[:port].to_s.empty?
|
68
|
+
@otp = options[:otp] if options.key?(:otp)
|
69
|
+
@comment = options[:comment] if options.key?(:comment)
|
70
|
+
@last_edit = Time.now.to_i unless options.key?(:no_update_last_edit)
|
71
|
+
end
|
74
72
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
73
|
+
# Delete all data
|
74
|
+
def delete
|
75
|
+
@id = nil
|
76
|
+
@group = nil
|
77
|
+
@host = nil
|
78
|
+
@protocol = nil
|
79
|
+
@user = nil
|
80
|
+
@port = nil
|
81
|
+
@otp = nil
|
82
|
+
@comment = nil
|
83
|
+
@created = nil
|
84
|
+
@last_edit = nil
|
85
|
+
end
|
79
86
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
@group = nil
|
84
|
-
@host = nil
|
85
|
-
@protocol = nil
|
86
|
-
@user = nil
|
87
|
-
@port = nil
|
88
|
-
@otp = nil
|
89
|
-
@comment = nil
|
90
|
-
@created = nil
|
91
|
-
@last_edit = nil
|
92
|
-
@last_sync = nil
|
93
|
-
end
|
87
|
+
def empty?
|
88
|
+
@id.to_s.empty?
|
89
|
+
end
|
94
90
|
|
95
|
-
|
96
|
-
|
97
|
-
|
91
|
+
def nil?
|
92
|
+
false
|
93
|
+
end
|
98
94
|
|
99
|
-
|
100
|
-
return false
|
101
|
-
end
|
95
|
+
private
|
102
96
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
97
|
+
# Generate an random id
|
98
|
+
def generate_id
|
99
|
+
[*('A'..'Z'), *('a'..'z'), *('0'..'9')].sample(16).join
|
100
|
+
end
|
101
|
+
end
|
109
102
|
end
|