mpw 2.0.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 +7 -0
- data/.gitignore +1 -0
- data/CHANGELOG +13 -0
- data/Gemfile +10 -0
- data/LICENSE +339 -0
- data/README.md +14 -0
- data/VERSION +1 -0
- data/bin/mpw +165 -0
- data/bin/mpw-server +78 -0
- data/bin/mpw-ssh +89 -0
- data/i18n/cli/en.yml +149 -0
- data/i18n/cli/fr.yml +149 -0
- data/i18n/server/en.yml +26 -0
- data/i18n/server/fr.yml +26 -0
- data/lib/mpw/config.rb +231 -0
- data/lib/mpw/item.rb +109 -0
- data/lib/mpw/mpw.rb +332 -0
- data/lib/mpw/server.rb +343 -0
- data/lib/mpw/sync/ftp.rb +81 -0
- data/lib/mpw/sync/mpw.rb +124 -0
- data/lib/mpw/sync/ssh.rb +80 -0
- data/lib/mpw/sync.rb +138 -0
- data/lib/mpw/ui/cli.rb +324 -0
- data/lib/mpw/ui/clissh.rb +42 -0
- data/mpw.gemspec +19 -0
- data/test/files/fixtures.yml +32 -0
- data/test/files/test_import.csv +3 -0
- data/test/files/test_import.yml +23 -0
- data/test/test_item.rb +218 -0
- data/test/test_mpw.rb +191 -0
- data/test/tests.rb +4 -0
- metadata +82 -0
data/lib/mpw/sync.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# author: nishiki
|
3
|
+
# mail: nishiki@yaegashi.fr
|
4
|
+
# info: a simple script who manage your passwords
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'i18n'
|
8
|
+
require 'yaml'
|
9
|
+
require 'tempfile'
|
10
|
+
require 'mpw/mpw'
|
11
|
+
require 'mpw/item'
|
12
|
+
|
13
|
+
module MPW
|
14
|
+
class Sync
|
15
|
+
|
16
|
+
attr_accessor :error_msg
|
17
|
+
|
18
|
+
# Constructor
|
19
|
+
# raise an exception if there is a bad parameter
|
20
|
+
def initialize(config, local, password=nil)
|
21
|
+
@error_msg = nil
|
22
|
+
@config = config
|
23
|
+
@local = local
|
24
|
+
@password = password
|
25
|
+
|
26
|
+
raise I18n.t('error.class') if not @local.instance_of?(MPW)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get the data on remote host
|
30
|
+
# @rtrn: true if get the date, else false
|
31
|
+
def get_remote
|
32
|
+
case @config.sync_type
|
33
|
+
when 'mpw'
|
34
|
+
require 'mpw/sync/mpw'
|
35
|
+
@sync = SyncMPW.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
|
36
|
+
when 'sftp', 'scp', 'ssh'
|
37
|
+
require 'mpw/sync/ssh'
|
38
|
+
@sync = SyncSSH.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
|
39
|
+
when 'ftp'
|
40
|
+
require 'mpw/sync/ftp'
|
41
|
+
@sync = SyncFTP.new(@config.sync_host, @config.sync_user, @config.sync_pwd, @config.sync_path, @config.sync_port)
|
42
|
+
else
|
43
|
+
@error_msg = I18n.t('error.unknown_type')
|
44
|
+
return false
|
45
|
+
end
|
46
|
+
|
47
|
+
if not @sync.connect
|
48
|
+
@error_msg = @sync.error_msg
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
file_tmp = Tempfile.new('mpw-')
|
54
|
+
raise @sync.error_msg if not @sync.get(file_tmp.path)
|
55
|
+
|
56
|
+
@remote = MPW.new(file_tmp.path, @config.key)
|
57
|
+
raise @remote.error_msg if not @remote.decrypt(@password)
|
58
|
+
|
59
|
+
file_tmp.close(true)
|
60
|
+
return true
|
61
|
+
rescue Exception => e
|
62
|
+
@error_msg = "#{I18n.t('error.sync.download')} #{e}"
|
63
|
+
file_tmp.close(true)
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
|
67
|
+
# Sync remote data and local data
|
68
|
+
# raise an exception if there is a problem
|
69
|
+
def sync
|
70
|
+
|
71
|
+
if not @remote.to_s.empty?
|
72
|
+
@local.list.each do |item|
|
73
|
+
update = false
|
74
|
+
@remote.list.each do |r|
|
75
|
+
|
76
|
+
# Update item
|
77
|
+
if item.id == r.id
|
78
|
+
if item.last_edit < r.last_edit
|
79
|
+
raise item.error_msg if not item.update(name: r.name,
|
80
|
+
group: r.group,
|
81
|
+
host: r.host,
|
82
|
+
protocol: r.protocol,
|
83
|
+
user: r.user,
|
84
|
+
password: r.password,
|
85
|
+
port: r.port,
|
86
|
+
comment: r.comment
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
r.delete
|
91
|
+
update = true
|
92
|
+
|
93
|
+
break
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Remove an old item
|
98
|
+
if not update and item.last_sync.to_i < @config.last_sync and item.last_edit < @config.last_sync
|
99
|
+
item.delete
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Add item
|
105
|
+
@remote.list.each do |r|
|
106
|
+
if r.last_edit > @config.last_sync
|
107
|
+
item = Item.new(id: r.id,
|
108
|
+
name: r.name,
|
109
|
+
group: r.group,
|
110
|
+
host: r.host,
|
111
|
+
protocol: r.protocol,
|
112
|
+
user: r.user,
|
113
|
+
password: r.password,
|
114
|
+
port: r.port,
|
115
|
+
comment: r.comment,
|
116
|
+
created: r.created,
|
117
|
+
last_edit: r.last_edit
|
118
|
+
)
|
119
|
+
raise @local.error_msg if not @local.add(item)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
@local.list.each do |item|
|
124
|
+
item.set_last_sync
|
125
|
+
end
|
126
|
+
|
127
|
+
raise @mpw.error_msg if not @local.encrypt
|
128
|
+
raise @sync.error_msg if not @sync.update(@config.file_gpg)
|
129
|
+
|
130
|
+
@config.set_last_sync
|
131
|
+
|
132
|
+
return true
|
133
|
+
rescue Exception => e
|
134
|
+
@error_msg = "#{I18n.t('error.sync.unknown')} #{e}"
|
135
|
+
return false
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/lib/mpw/ui/cli.rb
ADDED
@@ -0,0 +1,324 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# author: nishiki
|
3
|
+
# mail: nishiki@yaegashi.fr
|
4
|
+
# info: a simple script who m your passwords
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'highline/import'
|
8
|
+
require 'pathname'
|
9
|
+
require 'readline'
|
10
|
+
require 'i18n'
|
11
|
+
require 'colorize'
|
12
|
+
require 'mpw/sync'
|
13
|
+
require 'mpw/mpw'
|
14
|
+
require 'mpw/item'
|
15
|
+
|
16
|
+
class Cli
|
17
|
+
|
18
|
+
# Constructor
|
19
|
+
# @args: lang -> the operating system language
|
20
|
+
# config_file -> a specify config file
|
21
|
+
def initialize(config)
|
22
|
+
@config = config
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sync the data with the server
|
26
|
+
# @rtnr: true if the synchro is finish
|
27
|
+
def sync
|
28
|
+
@sync = MPW::Sync.new(@config, @mpw, @password)
|
29
|
+
|
30
|
+
raise(@sync.error_msg) if not @sync.get_remote
|
31
|
+
raise(@sync.error_msg) if not @sync.sync
|
32
|
+
|
33
|
+
return true
|
34
|
+
rescue Exception => e
|
35
|
+
puts "#{I18n.t('display.error')} #7: #{e}".red
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create a new config file
|
40
|
+
# @args: lang -> the software language
|
41
|
+
def setup(lang)
|
42
|
+
puts I18n.t('form.setup.title')
|
43
|
+
puts '--------------------'
|
44
|
+
language = ask(I18n.t('form.setup.lang', lang: lang)).to_s
|
45
|
+
key = ask(I18n.t('form.setup.gpg_key')).to_s
|
46
|
+
share_keys = ask(I18n.t('form.setup.share_gpg_keys')).to_s
|
47
|
+
file_gpg = ask(I18n.t('form.setup.gpg_file', home: @conf.dir_config)).to_s
|
48
|
+
sync_type = ask(I18n.t('form.setup.sync_type')).to_s
|
49
|
+
|
50
|
+
if ['ssh', 'ftp', 'mpw'].include?(sync_type)
|
51
|
+
sync_host = ask(I18n.t('form.setup.sync_host')).to_s
|
52
|
+
sync_port = ask(I18n.t('form.setup.sync_port')).to_s
|
53
|
+
sync_user = ask(I18n.t('form.setup.sync_user')).to_s
|
54
|
+
sync_pwd = ask(I18n.t('form.setup.sync_pwd')).to_s
|
55
|
+
sync_path = ask(I18n.t('form.setup.sync_path')).to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
if language.nil? or language.empty?
|
59
|
+
language = lang
|
60
|
+
end
|
61
|
+
I18n.locale = language.to_sym
|
62
|
+
|
63
|
+
sync_type = sync_type.nil? or sync_type.empty? ? nil : sync_type
|
64
|
+
sync_host = sync_host.nil? or sync_host.empty? ? nil : sync_host
|
65
|
+
sync_port = sync_port.nil? or sync_port.empty? ? nil : sync_port.to_i
|
66
|
+
sync_user = sync_user.nil? or sync_user.empty? ? nil : sync_user
|
67
|
+
sync_pwd = sync_pwd.nil? or sync_pwd.empty? ? nil : sync_pwd
|
68
|
+
sync_path = sync_path.nil? or sync_path.empty? ? nil : sync_path
|
69
|
+
|
70
|
+
if @config.setup(key, share_keys, language, file_gpg, sync_type, sync_host, sync_port, sync_user, sync_pwd, sync_path)
|
71
|
+
puts "#{I18n.t('form.setup.valid')}".green
|
72
|
+
else
|
73
|
+
puts "#{I18n.t('display.error')} #8: #{@config.error_msg}".red
|
74
|
+
exit 2
|
75
|
+
end
|
76
|
+
|
77
|
+
if not @config.checkconfig
|
78
|
+
puts "#{I18n.t('display.error')} #9: #{@config.error_msg}".red
|
79
|
+
exit 2
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Setup a new GPG key
|
84
|
+
def setup_gpg_key
|
85
|
+
puts I18n.t('form.setup_gpg_key.title')
|
86
|
+
puts '--------------------'
|
87
|
+
ask = ask(I18n.t('form.setup_gpg_key.ask')).to_s
|
88
|
+
|
89
|
+
if not ['Y', 'y', 'O', 'o'].include?(ask)
|
90
|
+
puts I18n.t('form.setup_gpg_key.no_create')
|
91
|
+
exit 2
|
92
|
+
end
|
93
|
+
|
94
|
+
name = ask(I18n.t('form.setup_gpg_key.name')).to_s
|
95
|
+
password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
|
96
|
+
confirm = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
|
97
|
+
|
98
|
+
if password != confirm
|
99
|
+
puts I18n.t('form.setup_gpg_key.error_password')
|
100
|
+
exit 2
|
101
|
+
end
|
102
|
+
|
103
|
+
length = ask(I18n.t('form.setup_gpg_key.length')).to_s
|
104
|
+
expire = ask(I18n.t('form.setup_gpg_key.expire')).to_s
|
105
|
+
password = password.to_s
|
106
|
+
|
107
|
+
length = length.nil? or length.empty? ? 2048 : length.to_i
|
108
|
+
expire = expire.nil? or expire.empty? ? 0 : expire.to_i
|
109
|
+
|
110
|
+
puts I18n.t('form.setup_gpg_key.wait')
|
111
|
+
|
112
|
+
if @config.setup_gpg_key(password, name, length, expire)
|
113
|
+
puts "#{I18n.t('form.setup_gpg_key.valid')}".green
|
114
|
+
else
|
115
|
+
puts "#{I18n.t('display.error')} #10: #{@config.error_msg}".red
|
116
|
+
exit 2
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Request the GPG password and decrypt the file
|
121
|
+
def decrypt
|
122
|
+
if not defined?(@mpw)
|
123
|
+
@mpw = MPW::MPW.new(@config.file_gpg, @config.key, @config.share_keys)
|
124
|
+
end
|
125
|
+
|
126
|
+
@password = ask(I18n.t('display.gpg_password')) {|q| q.echo = false}
|
127
|
+
if not @mpw.decrypt(@password)
|
128
|
+
puts "#{I18n.t('display.error')} #11: #{@mpw.error_msg}".red
|
129
|
+
exit 2
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Display the query's result
|
134
|
+
# @args: search -> the string to search
|
135
|
+
# protocol -> search from a particular protocol
|
136
|
+
def display(options={})
|
137
|
+
result = @mpw.list(options)
|
138
|
+
|
139
|
+
case result.length
|
140
|
+
when 0
|
141
|
+
puts I18n.t('display.nothing')
|
142
|
+
when 1
|
143
|
+
display_item(result.first)
|
144
|
+
else
|
145
|
+
i = 1
|
146
|
+
result.each do |item|
|
147
|
+
print "#{i}: ".cyan
|
148
|
+
print item.name
|
149
|
+
print " -> #{item.comment}".magenta if not item.comment.to_s.empty?
|
150
|
+
print "\n"
|
151
|
+
|
152
|
+
i += 1
|
153
|
+
end
|
154
|
+
choice = ask(I18n.t('form.select')).to_i
|
155
|
+
|
156
|
+
if choice >= 1 and choice < i
|
157
|
+
display_item(result[choice-1])
|
158
|
+
else
|
159
|
+
puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Display an item in the default format
|
165
|
+
# @args: item -> an array with the item information
|
166
|
+
def display_item(item)
|
167
|
+
puts '--------------------'.cyan
|
168
|
+
print 'Id: '.cyan
|
169
|
+
puts item.id
|
170
|
+
print "#{I18n.t('display.name')}: ".cyan
|
171
|
+
puts item.name
|
172
|
+
print "#{I18n.t('display.group')}: ".cyan
|
173
|
+
puts item.group
|
174
|
+
print "#{I18n.t('display.server')}: ".cyan
|
175
|
+
puts item.host
|
176
|
+
print "#{I18n.t('display.protocol')}: ".cyan
|
177
|
+
puts item.protocol
|
178
|
+
print "#{I18n.t('display.login')}: ".cyan
|
179
|
+
puts item.user
|
180
|
+
print "#{I18n.t('display.password')}: ".cyan
|
181
|
+
puts item.password
|
182
|
+
print "#{I18n.t('display.port')}: ".cyan
|
183
|
+
puts item.port
|
184
|
+
print "#{I18n.t('display.comment')}: ".cyan
|
185
|
+
puts item.comment
|
186
|
+
end
|
187
|
+
|
188
|
+
# Form to add a new item
|
189
|
+
def add
|
190
|
+
options = {}
|
191
|
+
|
192
|
+
puts I18n.t('form.add.title')
|
193
|
+
puts '--------------------'
|
194
|
+
options[:name] = ask(I18n.t('form.add.name')).to_s
|
195
|
+
options[:group] = ask(I18n.t('form.add.group')).to_s
|
196
|
+
options[:host] = ask(I18n.t('form.add.server')).to_s
|
197
|
+
options[:protocol] = ask(I18n.t('form.add.protocol')).to_s
|
198
|
+
options[:user] = ask(I18n.t('form.add.login')).to_s
|
199
|
+
options[:password] = ask(I18n.t('form.add.password')).to_s
|
200
|
+
options[:port] = ask(I18n.t('form.add.port')).to_s
|
201
|
+
options[:comment] = ask(I18n.t('form.add.comment')).to_s
|
202
|
+
|
203
|
+
item = MPW::Item.new(options)
|
204
|
+
if @mpw.add(item)
|
205
|
+
if @mpw.encrypt
|
206
|
+
sync
|
207
|
+
puts "#{I18n.t('form.add.valid')}".green
|
208
|
+
else
|
209
|
+
puts "#{I18n.t('display.error')} #12: #{@mpw.error_msg}".red
|
210
|
+
end
|
211
|
+
else
|
212
|
+
puts "#{I18n.t('display.error')} #13: #{item.error_msg}".red
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Update an item
|
217
|
+
# @args: id -> the item's id
|
218
|
+
def update(id)
|
219
|
+
item = @mpw.search_by_id(id)
|
220
|
+
|
221
|
+
if not item.nil?
|
222
|
+
options = {}
|
223
|
+
|
224
|
+
puts I18n.t('form.update.title')
|
225
|
+
puts '--------------------'
|
226
|
+
options[:name] = ask(I18n.t('form.update.name' , name: item.name)).to_s
|
227
|
+
options[:group] = ask(I18n.t('form.update.group' , group: item.group)).to_s
|
228
|
+
options[:host] = ask(I18n.t('form.update.server' , server: item.host)).to_s
|
229
|
+
options[:protocol] = ask(I18n.t('form.update.protocol', protocol: item.protocol)).to_s
|
230
|
+
options[:user] = ask(I18n.t('form.update.login' , login: item.user)).to_s
|
231
|
+
options[:password] = ask(I18n.t('form.update.password')).to_s
|
232
|
+
options[:port] = ask(I18n.t('form.update.port' , port: item.port)).to_s
|
233
|
+
options[:comment] = ask(I18n.t('form.update.comment' , comment: item.comment)).to_s
|
234
|
+
|
235
|
+
options.delete_if { |k,v| v.empty? }
|
236
|
+
|
237
|
+
if item.update(options)
|
238
|
+
if @mpw.encrypt
|
239
|
+
sync
|
240
|
+
puts "#{I18n.t('form.update.valid')}".green
|
241
|
+
else
|
242
|
+
puts "#{I18n.t('display.error')} #14: #{@mpw.error_msg}".red
|
243
|
+
end
|
244
|
+
else
|
245
|
+
puts "#{I18n.t('display.error')} #15: #{item.error_msg}".red
|
246
|
+
end
|
247
|
+
else
|
248
|
+
puts I18n.t('display.nothing')
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Remove an item
|
253
|
+
# @args: id -> the item's id
|
254
|
+
# force -> no resquest a validation
|
255
|
+
def delete(id, force=false)
|
256
|
+
if not force
|
257
|
+
item = @mpw.search_by_id(id)
|
258
|
+
|
259
|
+
if not item.nil?
|
260
|
+
display_item(item)
|
261
|
+
|
262
|
+
confirm = ask("#{I18n.t('form.delete.ask', id: id)} (y/N) ").to_s
|
263
|
+
if confirm =~ /^(y|yes|YES|Yes|Y)$/
|
264
|
+
force = true
|
265
|
+
end
|
266
|
+
else
|
267
|
+
puts I18n.t('display.nothing')
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
if force
|
272
|
+
item.delete
|
273
|
+
|
274
|
+
if @mpw.encrypt
|
275
|
+
sync
|
276
|
+
puts "#{I18n.t('form.delete.valid', id: id)}".green
|
277
|
+
else
|
278
|
+
puts "#{I18n.t('display.error')} #16: #{@mpw.error_msg}".red
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# Export the items in a CSV file
|
284
|
+
# @args: file -> the destination file
|
285
|
+
def export(file, type=:yaml)
|
286
|
+
if @mpw.export(file, type)
|
287
|
+
puts "#{I18n.t('export.valid', file)}".green
|
288
|
+
else
|
289
|
+
puts "#{I18n.t('display.error')} #17: #{@mpw.error_msg}".red
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# Import items from a CSV file
|
294
|
+
# @args: file -> the import file
|
295
|
+
# force -> no resquest a validation
|
296
|
+
def import(file, type=:yaml, force=false)
|
297
|
+
|
298
|
+
if not force
|
299
|
+
result = @mpw.import_preview(file, type)
|
300
|
+
if result.is_a?(Array) and not result.empty?
|
301
|
+
result.each do |r|
|
302
|
+
display_item(r)
|
303
|
+
end
|
304
|
+
|
305
|
+
confirm = ask("#{I18n.t('form.import.ask', file: file)} (y/N) ").to_s
|
306
|
+
if confirm =~ /^(y|yes|YES|Yes|Y)$/
|
307
|
+
force = true
|
308
|
+
end
|
309
|
+
else
|
310
|
+
puts I18n.t('form.import.not_valid')
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
if force
|
315
|
+
if @mpw.import(file, type) and @mpw.encrypt
|
316
|
+
sync
|
317
|
+
puts "#{I18n.t('form.import.valid')}".green
|
318
|
+
else
|
319
|
+
puts "#{I18n.t('display.error')} #18: #{@mpw.error_msg}".red
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# author: nishiki
|
3
|
+
# mail: nishiki@yaegashi.fr
|
4
|
+
# info: a simple script who manage your passwords
|
5
|
+
|
6
|
+
require 'mpw/ui/cli'
|
7
|
+
|
8
|
+
class CliSSH < Cli
|
9
|
+
|
10
|
+
attr_accessor :server, :port, :login
|
11
|
+
|
12
|
+
# Connect to SSH
|
13
|
+
# args: search -> string to search
|
14
|
+
def ssh(search)
|
15
|
+
result = @mpw.search(search, nil, 'ssh')
|
16
|
+
|
17
|
+
if result.length > 0
|
18
|
+
result.each do |r|
|
19
|
+
server = @server.nil? ? r[:host] : @server
|
20
|
+
port = @port.nil? ? r[:port] : @port
|
21
|
+
login = @login.nil? ? r[:login] : @login
|
22
|
+
|
23
|
+
passwd = r[:password]
|
24
|
+
|
25
|
+
if port.nil? and port.empty?
|
26
|
+
port = 22
|
27
|
+
end
|
28
|
+
|
29
|
+
puts "#{I18n.t('ssh.display.connect')} ssh #{login}@#{server} -p #{port}"
|
30
|
+
if passwd.empty?
|
31
|
+
system("ssh #{login}@#{server} -p #{port}")
|
32
|
+
else
|
33
|
+
system("sshpass -p '#{passwd}' ssh #{login}@#{server} -p #{port}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
else
|
38
|
+
puts I18n.t('ssh.display.nothing')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
data/mpw.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'mpw'
|
7
|
+
spec.version = File.open('VERSION').read
|
8
|
+
spec.authors = ['nishiki']
|
9
|
+
spec.email = ['gems@yae.im']
|
10
|
+
spec.summary = 'Manage your password'
|
11
|
+
spec.description = 'Save and read your password with gpg'
|
12
|
+
spec.homepage = 'https://github.com/nishiki/manage-password'
|
13
|
+
spec.license = 'GPL'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = ['mpw', 'mpw-server']
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
add_new:
|
2
|
+
name: 'test_name'
|
3
|
+
group: 'test_group'
|
4
|
+
host: 'test_host'
|
5
|
+
protocol: 'test_protocol'
|
6
|
+
user: 'test_user'
|
7
|
+
password: 'test_password'
|
8
|
+
port: '42'
|
9
|
+
comment: 'test_comment'
|
10
|
+
|
11
|
+
add_existing:
|
12
|
+
id: 'TEST-ID-XXXXX'
|
13
|
+
name: 'test_name_existing'
|
14
|
+
group: 'test_group_existing'
|
15
|
+
host: 'test_host_existing'
|
16
|
+
protocol: 'test_protocol_existing'
|
17
|
+
user: 'test_user_existing'
|
18
|
+
password: 'test_password_existing'
|
19
|
+
port: '44'
|
20
|
+
comment: 'test_comment_existing'
|
21
|
+
created: 1386752948
|
22
|
+
|
23
|
+
update:
|
24
|
+
name: 'test_name_update'
|
25
|
+
group: 'test_group_update'
|
26
|
+
host: 'test_host_update'
|
27
|
+
protocol: 'test_protocol_update'
|
28
|
+
user: 'test_user_update'
|
29
|
+
password: 'test_password_update'
|
30
|
+
port: '43'
|
31
|
+
comment: 'test_comment_update'
|
32
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
---
|
2
|
+
XWas7vpy0HerhOYd:
|
3
|
+
id: XWas7vpy0HerhOYd
|
4
|
+
name: test_name
|
5
|
+
group: test_group
|
6
|
+
host: test_host
|
7
|
+
protocol: test_protocol
|
8
|
+
user: test_user
|
9
|
+
password: test_password
|
10
|
+
port: 42
|
11
|
+
comment: test_comment
|
12
|
+
date: 1419858983
|
13
|
+
D7URyJENLa91jt0b:
|
14
|
+
id: D7URyJENLa91jt0b
|
15
|
+
name: test_name_update
|
16
|
+
group: test_group_update
|
17
|
+
host: test_host_update
|
18
|
+
protocol: test_protocol_update
|
19
|
+
user: test_user_update
|
20
|
+
password: test_password_update
|
21
|
+
port: 43
|
22
|
+
comment: test_comment_update
|
23
|
+
date: 1419858983
|