1
0
Fork 0
mirror of https://github.com/nishiki/manage-password.git synced 2025-02-20 01:50:04 +00:00

merge text_editor branch

This commit is contained in:
Adrien Waksberg 2016-11-05 10:19:38 +01:00
commit 50a75450fd
24 changed files with 1230 additions and 715 deletions

128
README.md
View file

@ -1,128 +0,0 @@
# Manage your passwords!
MPW is a little software which stores your passwords in an GPG encrypted file.
MPW can synchronize your password with SSH or FTP.
# Installation
This program work with ruby >= 2.0
* install ruby and rubygems on your computer
* install xclip
* gem install mpw
# How to use
* Show help
```
mpw --help
```
* Setup a new config file
```
mpw --setup
mpw --setup --config /path/conf/file.cfg
```
* Create and setup a new wallet
```
mpw --setup-wallet --wallet new_wallet_name
mpw --setup-wallet --wallet new_wallet_name --config /path/conf/file.cfg
```
* Add a GPG key in wallet
```
mpw --add --key root@localhost.local
mpw --add --key root@localhost.local --config /path/conf/file.cfg
mpw --add --key root@localhost.local --wallet wallet_name
mpw --add --key root@localhost.local --config /path/conf/file.cfg --wallet wallet_name
```
* Add a new GPG key in wallet
```
mpw --add --key root@localhost.local --file /path/gpg/file.pub
mpw --add --key root@localhost.local --file /path/gpg/file.pub --config /path/conf/file.cfg
mpw --add --key root@localhost.local --file /path/gpg/file.pub --wallet wallet_name
mpw --add --key root@localhost.local --file /path/gpg/file.pub --config /path/conf/file.cfg --wallet wallet_name
```
* Delete a GPG key in wallet
```
mpw --delete --key root@localhost.local
mpw --delete --key root@localhost.local --wallet wallet_name
mpw --delete --key root@localhost.local --wallet wallet_name --config /path/conf/file.cfg
```
* Add a new item in wallet
```
mpw --add
mpw --add --config /path/conf/file.cfg
mpw --add --wallet wallet_name
mpw --add --config /path/conf/file.cfg --wallet wallet_name
```
* Update an item
```
mpw --update --id uniq_id
mpw --update --id uniq_id --config /path/conf/file.cfg
mpw --update --id uniq_id --wallet wallet_name
mpw --update --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
```
* Delete an item
```
mpw --delete --id uniq_id
mpw --delete --id uniq_id --config /path/conf/file.cfg
mpw --delete --id uniq_id --wallet wallet_name
mpw --delete --id uniq_id --config /path/conf/file.cfg --wallet wallet_name
```
* Show an item
```
mpw --show 'string to search'
mpw --show 'string to search' --config /path/conf/file.cfg
mpw --show 'string to search' --wallet wallet_name
mpw --show 'string to search' --config /path/conf/file.cfg --wallet wallet_name
mpw --show 'string to search' --group group_name
mpw --show 'string to search' --group group_name --config /path/conf/file.cfg
mpw --show 'string to search' --group group_name --wallet wallet_name
mpw --show 'string to search' --group group_name --config /path/conf/file.cfg --wallet wallet_name
```
* Export data in YAML file
```
mpw --export --file /path/file/to/export.yml
mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg
mpw --export --file /path/file/to/export.yml --wallet wallet_name
mpw --export --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
```
* Import data from YAML file
```
mpw --import --file /path/file/to/export.yml
mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg
mpw --import --file /path/file/to/export.yml --wallet wallet_name
mpw --import --file /path/file/to/export.yml --config /path/conf/file.cfg --wallet wallet_name
```
Format file to import:
```
1:
name: Website perso
group: Perso
host: localhost.local
protocol: ftp
user: test
password: letoortue
port: 21
comment: Mysuper website
2:
name: Linuxfr
group: Pro
host: Linuxfr.org
protocol: https
user: test
password: coucou
port:
comment:
```

53
README.rst Normal file
View file

@ -0,0 +1,53 @@
MPW: Manage your passwords!
*******************************************************
mpw is a little software which stores your passwords in `GnuPG <http://www.gnupg.org/>` encrypted files.
Features
========
* generate OTP code
* synchronize your passwords with SSH or FTP.
* copy your login, password or otp in clipboard
Install
=======
On debian or ubuntu::
apt install ruby ruby-dev xclip
gem install mpw
How to use
==========
A simple mpw usage::
mpw config --init user@host.com
mpw add
mpw copy
mpw add
mpw list
Output::
Bank
==============================================================================
ID | Host | User | Protocol | Port | OTP | Comment
==============================================================================
1 | bank.com | 1234456 | https | | X |
Linux
==============================================================================
ID | Host | User | Protocol | Port | OTP | Comment
==============================================================================
2 | linuxfr.org | example | https | | | Da Linux French Site
Licence |License|
=================
Full license here: `LICENSE <https://github.com/nishiki/manage-password/blob/master/LICENSE>`
.. |License| image:: https://img.shields.io/badge/license-GPL--2.0-blue.svg

View file

@ -1 +1 @@
3.2.1
4.0.0-beta

193
bin/mpw
View file

@ -18,13 +18,9 @@
$: << File.expand_path('../../lib', __FILE__)
require 'optparse'
require 'locale'
require 'set'
require 'i18n'
require 'mpw/mpw'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Set local
@ -45,190 +41,9 @@ I18n.locale = lang.to_sym
# Options
# --------------------------------------------------------- #
options_password = {}
options = {}
options[:force] = false
options[:otp] = false
options[:sync] = true
options[:clipboard] = true
options[:group] = nil
options[:config] = nil
options[:wallet] = nil
bin_dir = File.dirname(__FILE__)
command = "#{bin_dir}/mpw-#{ARGV[0]}"
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw [options]"
opts.on('-a', '--add', I18n.t('option.add')) do
options[:add] = true
end
opts.on('-A', '--show-all', I18n.t('option.show_all')) do
options[:type] = nil
options[:show] = ''
end
opts.on('-c', '--config CONFIG', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-C', '--no-clipboard', I18n.t('option.clipboard')) do
options[:clipboard] = false
end
opts.on('-d', '--delete', I18n.t('option.remove')) do
options[:delete] = true
end
opts.on('-e', '--export', I18n.t('option.export')) do
options[:export] = true
end
opts.on('-f', '--file FILE', I18n.t('option.file')) do |file|
options[:file] = file
end
opts.on('-F', '--force', I18n.t('option.force')) do
options[:force] = true
end
opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
options[:group] = group
end
opts.on('-G', '--generate-password [LENGTH]', I18n.t('option.generate_password')) do |length|
options_password[:length] = length
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-i', '--id ID', I18n.t('option.id')) do |id|
options[:id] = id
end
opts.on('-I', '--import', I18n.t('option.import')) do
options[:import] = true
end
opts.on('-k', '--key KEY', I18n.t('option.key')) do |key|
options[:key] = key
end
opts.on('-n', '--numeric', I18n.t('option.numeric')) do
options_password[:numeric] = true
end
opts.on('-N', '--no-sync', I18n.t('option.no_sync')) do
options[:sync] = false
end
opts.on('-O', '--otp', I18n.t('option.otp')) do
options[:otp] = true
end
opts.on('-s', '--show [SEARCH]', I18n.t('option.show')) do |search|
search.nil? ? (options[:show] = '') : (options[:show] = search)
end
opts.on('-S', '--setup', I18n.t('option.setup')) do
options[:setup] = true
end
opts.on('-u', '--update', I18n.t('option.update')) do
options[:update] = true
end
opts.on('-w', '--wallet WALLET', I18n.t('option.wallet')) do |wallet|
options[:wallet] = wallet
end
opts.on('-W', '--setup-wallet', I18n.t('option.setup_wallet')) do
options[:setup_wallet] = true
end
opts.on('-x', '--special-chars', I18n.t('option.special_chars')) do
options_password[:special] = true
end
opts.on('-y', '--alpha', I18n.t('option.alpha')) do
options_password[:alpha] = true
end
end.parse!
# --------------------------------------------------------- #
# Main
# --------------------------------------------------------- #
# Generate password
if not options_password.empty?
puts MPW::MPW::password(options_password)
exit 0
end
begin
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, options[:clipboard], options[:sync], options[:otp])
# Setup a new config
if not options[:setup].nil?
cli.setup(lang)
exit 0
end
cli.setup(lang) if not config.is_valid?
cli.setup_gpg_key if not config.check_gpg_key?
cli.get_wallet(options[:wallet])
cli.decrypt
# Display the item's informations
if not options[:show].nil?
opts = {search: options[:show],
group: options[:group],
}
cli.display(opts)
# Remove an item
elsif not options[:delete].nil? and not options[:id].nil?
cli.delete(options[:id], options[:force])
# Update an item
elsif not options[:update].nil? and not options[:id].nil?
cli.update(options[:id])
# Add a new item
elsif not options[:add].nil? and options[:key].nil?
cli.add
# Add a new public key in wallet
elsif not options[:add].nil? and not options[:key].nil?
cli.add_key(options[:key], options[:file])
# Delete a public key in wallet
elsif not options[:delete].nil? and not options[:key].nil?
cli.delete_key(options[:key])
# Export
elsif not options[:export].nil? and not options[:file].nil?
cli.export(options[:file])
# Add a new item
elsif not options[:import].nil? and not options[:file].nil?
cli.import(options[:file])
# Setup wallet config
elsif not options[:setup_wallet].nil?
cli.setup_wallet_config
end
cli = nil
exit 0
rescue SystemExit, Interrupt
exit 3
if Dir.glob("#{bin_dir}/mpw-*").include?("#{command}")
Kernel.load(command)
end

61
bin/mpw-add Normal file
View file

@ -0,0 +1,61 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Options
# --------------------------------------------------------- #
options = {}
options[:sync] = true
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw add [options]"
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
options[:sync] = false
end
opts.on('-r', '--random', I18n.t('option.random_password')) do
options[:password] = true
end
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
options[:wallet] = wallet
end
end.parse!
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, options[:sync])
cli.load_config
cli.get_wallet(options[:wallet])
cli.decrypt
cli.add(options[:password])

75
bin/mpw-config Normal file
View file

@ -0,0 +1,75 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Options
# --------------------------------------------------------- #
options = {}
values = {}
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw config [options]"
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-g', '--gpg-exe PATH', I18n.t('option.gpg_exe')) do |gpg_exe|
values[:gpg_exe] = gpg_exe
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-i', '--init GPG_KEY', I18n.t('option.init')) do |gpg_key|
options[:init] = true
values[:gpg_key] = gpg_key
end
opts.on('-k', '--key GPG_KEY', I18n.t('option.gpg_key')) do |gpg_key|
values[:gpg_key] = gpg_key
end
opts.on('-l', '--lang LANG', I18n.t('option.lang')) do |lang|
values[:lang] = lang
end
opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
values[:wallet_dir] = wallet_dir
end
end.parse!
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, nil)
if not options[:init].nil?
cli.setup(values)
cli.load_config
cli.get_wallet
cli.setup_gpg_key(options[:init])
cli.setup_wallet_config
else
cli.set_config(values)
end

71
bin/mpw-copy Normal file
View file

@ -0,0 +1,71 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Options
# --------------------------------------------------------- #
options = {}
options[:sync] = true
options[:clipboard] = true
values = {}
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw copy [options]"
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-d', '--disable-clipboard', I18n.t('option.clipboard')) do
options[:clipboard] = false
end
opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
values[:group] = group
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
options[:sync] = false
end
opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
values[:pattern] = pattern
end
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
options[:wallet] = wallet
end
end.parse!
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, options[:sync])
cli.load_config
cli.get_wallet(options[:wallet])
cli.decrypt
cli.copy(options[:clipboard], values)

66
bin/mpw-delete Normal file
View file

@ -0,0 +1,66 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Options
# --------------------------------------------------------- #
options = {}
options[:sync] = true
values = {}
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw delete [options]"
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
values[:group] = group
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
options[:sync] = false
end
opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
values[:pattern] = pattern
end
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
options[:wallet] = wallet
end
end.parse!
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, options[:sync])
cli.load_config
cli.get_wallet(options[:wallet])
cli.decrypt
cli.delete(values)

70
bin/mpw-export Normal file
View file

@ -0,0 +1,70 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Options
# --------------------------------------------------------- #
options = {}
options[:sync] = true
values = {}
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-f', '--file PATH', I18n.t('option.file_export')) do |file|
options[:file] = file
end
opts.on('-g', '--group GROUP', I18n.t('option.group')) do |group|
values[:group] = group
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
options[:sync] = false
end
opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
values[:pattern] = pattern
end
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
options[:wallet] = wallet
end
end.parse!
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, options[:sync])
cli.load_config
cli.get_wallet(options[:wallet])
cli.decrypt
cli.export(options[:file], values)

50
bin/mpw-genpwd Normal file
View file

@ -0,0 +1,50 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/mpw'
options = {}
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw passwd [options]"
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-l', '--length NUMBER', I18n.t('option.length')) do |length|
options[:length] = length.to_i
end
opts.on('-n', '--numeric', I18n.t('option.numeric')) do
options[:numeric] = true
end
opts.on('-s', '--special-chars', I18n.t('option.special_chars')) do
options[:special] = true
end
opts.on('-a', '--alpha', I18n.t('option.alpha')) do
options[:alpha] = true
end
end.parse!
puts MPW::MPW::password(options)
exit 0

61
bin/mpw-import Normal file
View file

@ -0,0 +1,61 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Options
# --------------------------------------------------------- #
options = {}
options[:sync] = true
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw import [options]"
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-f', '--file PATH', I18n.t('option.file_import')) do |file|
options[:file] = file
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
options[:sync] = false
end
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
options[:wallet] = wallet
end
end.parse!
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, options[:sync])
cli.load_config
cli.get_wallet(options[:wallet])
cli.decrypt
cli.import(options[:file])

66
bin/mpw-list Normal file
View file

@ -0,0 +1,66 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Options
# --------------------------------------------------------- #
options = {}
options[:sync] = true
values = {}
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw list [options]"
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
values[:group] = group
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
values[:pattern] = pattern
end
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
options[:sync] = false
end
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
options[:wallet] = wallet
end
end.parse!
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, options[:sync])
cli.load_config
cli.get_wallet(options[:wallet])
cli.decrypt
cli.list(values)

66
bin/mpw-update Normal file
View file

@ -0,0 +1,66 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Options
# --------------------------------------------------------- #
options = {}
options[:sync] = true
values = {}
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw update [options]"
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
values[:group] = group
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
options[:sync] = false
end
opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
values[:pattern] = pattern
end
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
options[:wallet] = wallet
end
end.parse!
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, options[:sync])
cli.load_config
cli.get_wallet(options[:wallet])
cli.decrypt
cli.update(values)

91
bin/mpw-wallet Normal file
View file

@ -0,0 +1,91 @@
#!/usr/bin/ruby
# MPW is a software to crypt and manage your passwords
# Copyright (C) 2016 Adrien Waksberg <mpw@yae.im>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'optparse'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- #
# Options
# --------------------------------------------------------- #
options = {}
options[:sync] = {}
values = {}
OptionParser.new do |opts|
opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
options[:config] = config
end
opts.on('-h', '--help', I18n.t('option.help')) do
puts opts
exit 0
end
opts.on('--host NAME', I18n.t('option.host')) do |host|
values[:host] = host
end
opts.on('-l', '--list', I18n.t('option.list')) do |list|
options[:list] = true
end
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
options[:sync] = false
end
opts.on('--password', I18n.t('option.password')) do
values[:password] = true
end
opts.on('--path PATH', I18n.t('option.path')) do |path|
values[:path] = path
end
opts.on('--port NUMBER', I18n.t('option.port')) do |port|
values[:port] = port
end
opts.on('--protocol NAME', I18n.t('option.protocol')) do |protocol|
values[:protocol] = protocol
end
opts.on('--user NAME', I18n.t('option.user')) do |user|
values[:user] = user
end
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
options[:wallet] = wallet
end
end.parse!
config = MPW::Config.new(options[:config])
cli = MPW::Cli.new(config, options[:sync])
cli.load_config
if not options[:list].nil?
cli.list_wallet
else
cli.get_wallet(options[:wallet])
cli.decrypt
cli.setup_wallet_config(values)
end

View file

@ -3,15 +3,13 @@ en:
error:
config:
write: "Can't write the config file!"
check: "Checkconfig failed!"
load: "Checkconfig failed!"
key_bad_format: "The key string isn't in good format!"
no_key_public: "You haven't the public key of %{key}!"
genkey_gpg:
exception: "Can't create the GPG key!"
name: "You must define a name for your GPG key!"
password: "You must define a password for your GPG key!"
delete:
id_no_exist: "Can't delete the item %{id}, it doesn't exist!"
export: "Can't export, unable to write in %{file}!"
gpg_file:
decrypt: "Can't decrypt file!"
@ -41,41 +39,51 @@ en:
config: "Specify the configuration file to use"
clipboard: "Disable the clipboard feature"
export: "Export a wallet in an yaml file"
file: "Specify a file, to use with the options [--import | --export | --add]"
file_export: "Specify the file where export data"
file_import: "Specify the file to import"
force: "No ask to confirm when you delete an item"
generate_password: "Generate a random password (default 8 characters)"
gpg_exe: "Set the gpg binary path to use"
gpg_key: "Specify a GPG key (ex: user@example.com)"
group: "Search the items with specified group"
help: "Show this help message"
id: "Specify an id, to use with the options [--delete | --update]"
host: "Specify the server for the synchronization"
init: "Initialize mpw"
import: "Import item since a yaml file"
key: "Specify the key name, to use with the options [--add | --delete | --update]"
key: "Specify the key name"
lang: "Set the software language"
list: "List the wallets"
no_sync: "Disable synchronization with the server"
numeric: "Use number to generate a password"
password: "Change the password for the synchronization"
path: "Specify the remote path"
pattern: "Given search pattern"
port: "Specify the connection port"
protocol: "Specify the protocol for the connection"
setup: "Create a new configuration file"
setup_wallet: "Create a new configuration file for a wallet"
special_chars: "Use special char to generate a password"
show: "Search and show the items"
show_all: "List all items"
remove: "Delete an item"
update: "Update an item"
usage: "Usage"
user: "Specify the user for the connection"
wallet: "Specify a wallet to use"
wallet_dir: "Set the wallets folder"
form:
select: "Select the item: "
add_key:
valid: "Key has been added!"
add_item:
title: "Add a new item"
name: "Enter the name: "
group: "Enter the group (optional): "
server: "Enter the hostname or ip: "
protocol: "Enter the protocol of the connection (ssh, http, other): "
login: "Enter the login connection: "
password: "Enter the the password: "
port: "Enter the connection port (optional): "
comment: "Enter a comment (optional): "
otp_key: "Enter the otp secret (base32 secret key): "
name: "The item's name (mandatory"
group: "The group's name"
host: "The hostname or ip"
protocol: "The protocol of the connection (ssh, http, ...)"
login: "The login of connection"
password: "The password"
port: "The connection port"
comment: "A comment"
otp_key: "The OTP secret"
valid: "Item has been added!"
clipboard:
choice: "What do you want to copy ? [q = quit, p = password, l = login]: "
@ -88,12 +96,12 @@ en:
login: "Press <l> to copy the login"
password: "Press <p> to copy the password"
otp_code: "Press <o> to copy the otp code"
quit: "Press <q> to quit"
delete_key:
valid: "Key has been deleted!"
delete_item:
ask: "Are you sure you want to remove the item %{id} ?"
valid: "The item %{id} has been removed!"
not_valid: "The item %{id} hasn't been removed, because it doesn't exist!"
ask: "Are you sure you want to remove the item ?"
valid: "The item has been removed!"
import:
ask: "Are you sure you want to import this file %{file} ?"
valid: "The import is succesfull!"
@ -127,16 +135,15 @@ en:
wait: "Please waiting during the GPG key generate, this process can take few minutes."
valid: "Your GPG key has been created ;-)"
update_item:
title: "Update an item"
name: "Enter the name [%{name}]: "
group: "Enter the group [%{group}]: "
server: "Enter the hostname or ip [%{server}]: "
protocol: "Enter the protocol of the connection [%{protocol}]: "
login: "Enter the login connection [%{login}]: "
password: "Enter the the password: "
port: "Enter the connection port [%{port}]: "
comment: "Enter a comment [%{comment}]: "
otp_key: "Enter the otp secret (base32 secret key): "
name: "The item's name (mandatory"
group: "The group's name"
host: "The hostname or ip"
protocol: "The protocol of the connection (ssh, http, ...)"
login: "The login of connection"
password: "The password (leave empty if you don't want change)"
port: "The connection port"
comment: "A comment"
otp_key: "The OTP secret (leave empty if you don't want change"
valid: "Item has been updated!"
export:
valid: "The export in %{file} is succesfull!"

View file

@ -3,15 +3,13 @@ fr:
error:
config:
write: "Impossible d'écrire le fichier de configuration!"
check: "Le fichier de configuration est invalide!"
load: "Le fichier de configuration est invalide!"
key_bad_format: "La clé GPG est invalide!"
no_key_public: "Vous ne possédez pas la clé publique de %{key}!"
genkey_gpg:
exception: "La création de la clé GPG n'a pas pu aboutir!"
name: "Vous devez définir un nom pour votre clé GPG!"
password: "Vous devez définir un mot de passe pour votre clé GPG!"
delete:
id_no_exist: "Impossible de supprimer l'élément %{id}, car il n'existe pas!"
export: "Impossible d'exporter les données dans le fichier %{file}!"
gpg_file:
decrypt: "Impossible de déchiffrer le fichier GPG!"
@ -41,41 +39,51 @@ fr:
config: "Spécifie le fichier de configuration à utiliser"
clipboard: "Désactive la fonction presse papier"
export: "Exporte un portefeuille dans un fichier yaml"
file: "Spécifie un fichier, à utiliser avec les options [--import | --export | --add]"
file_export: "Spécifie le fichier où exporter les données"
file_import: "Spécifie le fichier à importer"
force: "Ne demande pas de confirmation pour la suppression d'un élément"
generate_password: "Génére un mot de passe aléatoire (défaut 8 caractères)"
gpg_exe: "Spécifie le chemin du binaire gpg à utiliser"
gpg_key: "Spécifie une clé GPG (ex: user@example.com)"
group: "Recherche les éléments appartenant au groupe spécifié"
help: "Affiche ce message d'aide"
id: "Spécifie un identifiant, à utiliser avec les options [--delete | --update]"
host: "Spécifie le serveur pour la synchronisation"
import: "Importe des éléments depuis un fichier yaml"
key: "Spécifie le nom d'une clé, à utiliser avec les options [--add | --delete | --update]"
init: "Initialise mpw"
key: "Spécifie le nom d'une clé"
lang: "Spécifie la langue du logiciel (ex: fr)"
list: "Liste les portefeuilles"
no_sync: "Désactive la synchronisation avec le serveur"
numeric: "Utilise des chiffre dans la génération d'un mot de passe"
password: "Changer le mot de passe de connexion"
path: "Spécifie le chemin distant"
pattern: "Motif de donnée à chercher"
port: "Spécifie le port de connexion"
protocol: "Spécifie le protocol utilisé pour la connexion"
setup: "Création d'un nouveau fichier de configuration"
setup_wallet: "Création d'un nouveau fichier de configuration pour un portefeuille"
special_chars: "Utilise des charactères speciaux dans la génération d'un mot de passe"
show: "Recherche et affiche les éléments"
show_all: "Liste tous les éléments"
remove: "Supprime un élément"
update: "Met à jour un élément"
usage: "Utilisation"
user: "Spécifie l'identifiant de connection"
wallet: "Spécifie le portefeuille à utiliser"
wallet_dir: "Spécifie le répertoire des portefeuilles"
form:
select: "Sélectionner l'élément: "
add_key:
valid: "La clé a bien été ajoutée!"
add_item:
title: "Ajout d'un nouvel élément"
name: "Entrez le nom: "
group: "Entrez le groupe (optionnel): "
server: "Entrez le nom de domaine ou l'ip: "
protocol: "Entrez le protocole de connexion (ssh, http, other): "
login: "Entrez l'identifiant de connexion: "
password: "Entrez le mot de passe: "
port: "Entrez le port de connexion (optionnel): "
comment: "Entrez un commentaire (optionnel): "
otp_key: "Entrez le secret OTP: "
name: "Le nom de l'élément (obligatoire)"
group: "Le nom du groupe"
host: "Le nom de domaine ou l'ip"
protocol: "Le protocole de connexion (ssh, http, ...)"
login: "L'identifiant de connexion"
password: "Le mot de passe"
port: "Le port de connexion"
comment: "Un commentaire"
otp_key: "Le secret OTP"
valid: "L'élément a bien été ajouté!"
clipboard:
choice: "Que voulez-vous copier ? : "
@ -88,12 +96,12 @@ fr:
login: "Pressez <l> pour copier l'identifiant"
password: "Pressez <p> pour copier le mot de passe"
otp_code: "Pressez <o> pour copier le code OTP"
quit: "Pressez <q> pour quitter"
delete_key:
valid: "La clé a bien été supprimée!"
delete_item:
ask: "Êtes vous sûre de vouloir supprimer l'élément %{id} ?"
valid: "L'élément %{id} a bien été supprimé!"
not_valid: "L'élément %{id} n'a pu être supprimé, car il n'existe pas!"
ask: "Êtes vous sûre de vouloir supprimer l'élément ?"
valid: "L'élément a bien été supprimé!"
import:
ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?"
valid: "L'import est un succès!"
@ -127,16 +135,15 @@ fr:
wait: "Veuillez patienter durant la génération de votre clé GPG, ce processus peut prendre quelques minutes."
valid: "Votre clé GPG a bien été créée ;-)"
update_item:
title: "Mis à jour d'un élément"
name: "Entrez le nom [%{name}]: "
group: "Entrez le groupe [%{group}]: "
server: "Entrez le nom de domaine ou l'ip du serveur [%{server}]: "
protocol: "Entrez le protocole de connexion [%{protocol}]: "
login: "Entrez votre identifiant de connexion [%{login}]: "
password: "Entrez le mot de passe: "
port: "Entrez un port de connexion [%{port}]: "
comment: "Entrez un commentaire [%{comment}]: "
otp_key: "Entrez le secret OTP: "
name: "Le nom de l'élément (obligatoire)"
group: "Le nom du groupe"
host: "Le nom de domaine ou l'ip"
protocol: "Le protocole de connexion (ssh, http, ...)"
login: "L'identifiant de connexion"
password: "Le mot de passe (laissez vide si vous ne voulez pas le changer)"
port: "Le port de connexion"
comment: "Un commentaire"
otp_key: "Le secret OTP (laissez vide si vous ne voulez pas le changer)"
valid: "L'élément a bien été mis à jour!"
export:
valid: "L'export dans %{file} est un succès!"

View file

@ -17,10 +17,12 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'readline'
require 'locale'
require 'i18n'
require 'colorize'
require 'highline/import'
require 'clipboard'
require 'tmpdir'
require 'mpw/item'
require 'mpw/mpw'
@ -30,33 +32,35 @@ class Cli
# Constructor
# @args: config -> the config
# sync -> boolean for sync or not
# clipboard -> enable clopboard
# otp -> enable otp
def initialize(config, clipboard=true, sync=true, otp=false)
def initialize(config, sync=true)
@config = config
@clipboard = clipboard
@sync = sync
@otp = otp
end
# Change a parameter int the config after init
# @args: options -> param to change
def set_config(options)
gpg_key = options[:gpg_key] || @config.key
lang = options[:lang] || @config.lang
wallet_dir = options[:wallet_dir] || @config.wallet_dir
gpg_exe = options[:gpg_exe] || @config.gpg_exe
@config.setup(gpg_key, lang, wallet_dir, gpg_exe)
rescue Exception => e
puts "#{I18n.t('display.error')} #15: #{e}".red
exit 2
end
# Create a new config file
# @args: lang -> the software language
def setup(lang)
puts I18n.t('form.setup_config.title')
puts '--------------------'
language = ask(I18n.t('form.setup_config.lang', lang: lang)).to_s
key = ask(I18n.t('form.setup_config.gpg_key')).to_s
wallet_dir = ask(I18n.t('form.setup_config.wallet_dir', home: "#{@config.config_dir}")).to_s
gpg_exe = ask(I18n.t('form.setup_config.gpg_exe')).to_s
# @args: options -> set param
def setup(options)
lang = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
if language.nil? or language.empty?
language = lang
end
I18n.locale = language.to_sym
I18n.locale = lang.to_sym
@config.setup(key, lang, wallet_dir, gpg_exe)
@config.setup(options[:gpg_key], lang, options[:wallet_dir], options[:gpg_exe])
raise I18n.t('error.config.check') if not @config.is_valid?
load_config
puts "#{I18n.t('form.setup_config.valid')}".green
rescue Exception => e
@ -65,16 +69,10 @@ class Cli
end
# Setup a new GPG key
def setup_gpg_key
puts I18n.t('form.setup_gpg_key.title')
puts '--------------------'
ask = ask(I18n.t('form.setup_gpg_key.ask')).to_s
if not ['Y', 'y', 'O', 'o'].include?(ask)
raise I18n.t('form.setup_gpg_key.no_create')
end
# @args: gpg_key -> the key name
def setup_gpg_key(gpg_key)
return if @config.check_gpg_key?
name = ask(I18n.t('form.setup_gpg_key.name')).to_s
password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false}
confirm = ask(I18n.t('form.setup_gpg_key.confirm_password')) {|q| q.echo = false}
@ -82,16 +80,11 @@ class Cli
raise I18n.t('form.setup_gpg_key.error_password')
end
length = ask(I18n.t('form.setup_gpg_key.length')).to_s
expire = ask(I18n.t('form.setup_gpg_key.expire')).to_s
password = password.to_s
length = length.nil? or length.empty? ? 2048 : length.to_i
expire = expire.nil? or expire.empty? ? 0 : expire.to_i
@password = password.to_s
puts I18n.t('form.setup_gpg_key.wait')
@config.setup_gpg_key(password, name, length, expire)
@config.setup_gpg_key(@password, gpg_key)
puts "#{I18n.t('form.setup_gpg_key.valid')}".green
rescue Exception => e
@ -100,23 +93,17 @@ class Cli
end
# Setup wallet config for sync
def setup_wallet_config
config = {}
config['sync'] = {}
puts I18n.t('form.setup_wallet.title')
puts '--------------------'
config['sync']['type'] = ask(I18n.t('form.setup_wallet.sync_type')).to_s
if ['ftp', 'ssh'].include?(config['sync']['type'].downcase)
config['sync']['host'] = ask(I18n.t('form.setup_wallet.sync_host')).to_s
config['sync']['port'] = ask(I18n.t('form.setup_wallet.sync_port')).to_s
config['sync']['user'] = ask(I18n.t('form.setup_wallet.sync_user')).to_s
config['sync']['password'] = ask(I18n.t('form.setup_wallet.sync_pwd')).to_s
config['sync']['path'] = ask(I18n.t('form.setup_wallet.sync_path')).to_s
# @args: options -> value to change
def setup_wallet_config(options={})
if not options[:password].nil?
options[:password] = ask(I18n.t('form.setup_wallet.password')) {|q| q.echo = false}
end
@mpw.set_config(config)
#wallet_file = wallet.nil? ? "#{@config.wallet_dir}/default.mpw" : "#{@config.wallet_dir}/#{wallet}.mpw"
@mpw = MPW.new(@config.key, @wallet_file, @password, @config.gpg_exe)
@mpw.read_data
@mpw.set_config(options)
@mpw.write_data
puts "#{I18n.t('form.setup_wallet.valid')}".green
@ -125,6 +112,15 @@ class Cli
exit 2
end
# Load config
def load_config
@config.load_config
rescue Exception => e
puts "#{I18n.t('display.error')} #10: #{e}".red
exit 2
end
# Request the GPG password and decrypt the file
def decrypt
if not defined?(@mpw)
@ -139,97 +135,119 @@ class Cli
exit 2
end
# Display the query's result
# @args: search -> the string to search
# protocol -> search from a particular protocol
def display(options={})
result = @mpw.list(options)
# Format items on a table
def table(items=[])
group = '.'
i = 1
length_total = 10
data = { id: { length: 3, color: 'cyan' },
host: { length: 9, color: 'yellow' },
user: { length: 7, color: 'green' },
protocol: { length: 9, color: 'white' },
port: { length: 5, color: 'white' },
otp: { length: 4, color: 'white' },
comment: { length: 14, color: 'magenta' },
}
case result.length
when 0
puts I18n.t('display.nothing')
items.each do |item|
data.each do |k, v|
next if k == :id or k == :otp
when 1
display_item(result.first)
v[:length] = item.send(k.to_s).length + 3 if item.send(k.to_s).to_s.length >= v[:length]
end
end
data[:id][:length] = items.length.to_s.length + 2 if items.length.to_s.length > data[:id][:length]
data.each_value { |v| length_total += v[:length] }
items.sort! { |a, b| a.group.to_s.downcase <=> b.group.to_s.downcase }
else
group = nil
i = 1
items.each do |item|
if group != item.group
group = item.group
result.sort! { |a,b| a.group.downcase <=> b.group.downcase }
result.each do |item|
if group != item.group
group = item.group
if group.empty?
puts I18n.t('display.no_group').yellow
else
puts "\n#{group}".yellow
end
if group.to_s.empty?
puts "\n#{I18n.t('display.no_group')}".red
else
puts "\n#{group}".red
end
print " |_ ".yellow
print "#{i}: ".cyan
print item.name
print " -> #{item.comment}".magenta if not item.comment.to_s.empty?
print ' '
length_total.times { print '=' }
print "\n "
data.each do |k, v|
case k
when :id
print ' ID'
when :otp
print '| OTP'
else
print "| #{k.to_s.capitalize}"
end
(v[:length] - k.to_s.length).times { print ' ' }
end
print "\n "
length_total.times { print '=' }
print "\n"
i += 1
end
print " #{i}".send(data[:id][:color])
(data[:id][:length] - i.to_s.length).times { print ' ' }
data.each do |k, v|
next if k == :id
if k == :otp
print '| '
if item.otp; print ' X ' else 4.times { print ' ' } end
next
end
print '| '
print "#{item.send(k.to_s)}".send(v[:color])
(v[:length] - item.send(k.to_s).to_s.length).times { print ' ' }
end
print "\n"
choice = ask(I18n.t('form.select')).to_i
if choice >= 1 and choice < i
display_item(result[choice-1])
else
puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
end
i += 1
end
print "\n"
end
# Display the query's result
# @args: options -> the option to search
def list(options={})
result = @mpw.list(options)
if result.length == 0
puts I18n.t('display.nothing')
else
table(result)
end
end
# Display an item in the default format
# @args: item -> an array with the item information
def display_item(item)
puts '--------------------'.cyan
print 'Id: '.cyan
puts item.id
print "#{I18n.t('display.name')}: ".cyan
puts item.name
print "#{I18n.t('display.group')}: ".cyan
puts item.group
print "#{I18n.t('display.server')}: ".cyan
puts item.host
print "#{I18n.t('display.protocol')}: ".cyan
puts item.protocol
print "#{I18n.t('display.login')}: ".cyan
puts item.user
# Get an item when multiple choice
# @args: items -> array of items
# @rtrn: item
def get_item(items)
return items[0] if items.length == 1
if @clipboard
print "#{I18n.t('display.password')}: ".cyan
puts '***********'
items.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
choice = ask(I18n.t('form.select')).to_i
if choice >= 1 and choice <= items.length
return items[choice-1]
else
print "#{I18n.t('display.password')}: ".cyan
puts @mpw.get_password(item.id)
if @mpw.get_otp_code(item.id) > 0
print "#{I18n.t('display.otp_code')}: ".cyan
puts "#{@mpw.get_otp_code(item.id)} (#{@mpw.get_otp_remaining_time}s)"
end
return nil
end
print "#{I18n.t('display.port')}: ".cyan
puts item.port
print "#{I18n.t('display.comment')}: ".cyan
puts item.comment
clipboard(item) if @clipboard
end
# Copy in clipboard the login and password
# @args: item -> the item
def clipboard(item)
# clipboard -> enable clipboard
def clipboard(item, clipboard=true)
pid = nil
# Security: force quit after 90s
@ -246,21 +264,33 @@ class Cli
break
when 'l', 'login'
Clipboard.copy(item.user)
puts I18n.t('form.clipboard.login').green
if clipboard
Clipboard.copy(item.user)
puts I18n.t('form.clipboard.login').green
else
puts item.user
end
when 'p', 'password'
Clipboard.copy(@mpw.get_password(item.id))
puts I18n.t('form.clipboard.password').yellow
if clipboard
Clipboard.copy(@mpw.get_password(item.id))
puts I18n.t('form.clipboard.password').yellow
Thread.new do
sleep 30
Thread.new do
sleep 30
Clipboard.clear
Clipboard.clear
end
else
puts @mpw.get_password(item.id)
end
when 'o', 'otp'
Clipboard.copy(@mpw.get_otp_code(item.id))
if clipboard
Clipboard.copy(@mpw.get_otp_code(item.id))
else
puts @mpw.get_otp_code(item.id)
end
puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
else
@ -268,6 +298,7 @@ class Cli
puts I18n.t('form.clipboard.help.login')
puts I18n.t('form.clipboard.help.password')
puts I18n.t('form.clipboard.help.otp_code')
puts I18n.t('form.clipboard.help.quit')
next
end
end
@ -277,34 +308,25 @@ class Cli
Clipboard.clear
end
# List all wallets
def list_wallet
wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
wallets.each do |wallet|
puts File.basename(wallet, '.mpw')
end
end
# Display the wallet
# @args: wallet -> the wallet name
def get_wallet(wallet=nil)
if wallet.to_s.empty?
wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
case wallets.length
when 0
puts I18n.t('display.nothing')
when 1
if wallets.length == 1
@wallet_file = wallets[0]
else
i = 1
wallets.each do |wallet|
print "#{i}: ".cyan
puts File.basename(wallet, '.mpw')
i += 1
end
choice = ask(I18n.t('form.select')).to_i
if choice >= 1 and choice < i
@wallet_file = wallets[choice-1]
else
puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
exit 2
end
@wallet_file = "#{@config.wallet_dir}/default.mpw"
end
else
@wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"
@ -336,111 +358,158 @@ class Cli
puts "#{I18n.t('display.error')} #15: #{e}".red
end
# Form to add a new item
def add
options = {}
# Text editor interface
# @args: template -> template name
# item -> the item to edit
# password -> disable field password
def text_editor(template_name, item=nil, password=false)
editor = ENV['EDITOR'] || 'nano'
options = {}
opts = {}
template_file = "#{File.expand_path('../../../templates', __FILE__)}/#{template_name}.erb"
template = ERB.new(IO.read(template_file))
puts I18n.t('form.add_item.title')
puts '--------------------'
options[:name] = ask(I18n.t('form.add_item.name')).to_s
options[:group] = ask(I18n.t('form.add_item.group')).to_s
options[:host] = ask(I18n.t('form.add_item.server')).to_s
options[:protocol] = ask(I18n.t('form.add_item.protocol')).to_s
options[:user] = ask(I18n.t('form.add_item.login')).to_s
password = ask(I18n.t('form.add_item.password')).to_s
options[:port] = ask(I18n.t('form.add_item.port')).to_s
options[:comment] = ask(I18n.t('form.add_item.comment')).to_s
Dir.mktmpdir do |dir|
tmp_file = "#{dir}/#{template_name}.yml"
if @otp
otp_key = ask(I18n.t('form.add_item.otp_key')).to_s
File.open(tmp_file, 'w') do |f|
f << template.result(binding)
end
system("#{editor} #{tmp_file}")
opts = YAML::load_file(tmp_file)
end
item = Item.new(options)
opts.delete_if { |k,v| v.to_s.empty? }
opts.each do |k,v|
options[k.to_sym] = v
end
return options
end
# Form to add a new item
# @args: password -> generate a random password
def add(password=false)
options = text_editor('add_form', nil, password)
item = Item.new(options)
if password
options[:password] = MPW::password(length: 24)
end
@mpw.add(item)
@mpw.set_password(item.id, password)
@mpw.set_otp_key(item.id, otp_key)
@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
@mpw.set_otp_key(item.id, options[:otp_key]) if options.has_key?(:otp_key)
@mpw.write_data
@mpw.sync(true) if @sync
puts "#{I18n.t('form.add_item.valid')}".green
rescue Exception => e
puts "#{I18n.t('display.error')} #13: #{e}".red
end
# Update an item
# @args: id -> the item's id
def update(id)
item = @mpw.search_by_id(id)
# @args: options -> the option to search
def update(options={})
items = @mpw.list(options)
if items.length == 0
puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
else
table(items) if items.length > 1
if not item.nil?
options = {}
puts I18n.t('form.update_item.title')
puts '--------------------'
options[:name] = ask(I18n.t('form.update_item.name' , name: item.name)).to_s
options[:group] = ask(I18n.t('form.update_item.group' , group: item.group)).to_s
options[:host] = ask(I18n.t('form.update_item.server' , server: item.host)).to_s
options[:protocol] = ask(I18n.t('form.update_item.protocol', protocol: item.protocol)).to_s
options[:user] = ask(I18n.t('form.update_item.login' , login: item.user)).to_s
password = ask(I18n.t('form.update_item.password')).to_s
options[:port] = ask(I18n.t('form.update_item.port' , port: item.port)).to_s
options[:comment] = ask(I18n.t('form.update_item.comment' , comment: item.comment)).to_s
if @otp
otp_key = ask(I18n.t('form.update_item.otp_key')).to_s
end
options.delete_if { |k,v| v.empty? }
item = get_item(items)
options = text_editor('update_form', item)
item.update(options)
@mpw.set_password(item.id, password) if not password.empty?
@mpw.set_otp_key(item.id, otp_key) if not otp_key.to_s.empty?
@mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
@mpw.set_otp_key(item.id, options[:otp_key]) if options.has_key?(:otp_key)
@mpw.write_data
@mpw.sync(true) if @sync
puts "#{I18n.t('form.update_item.valid')}".green
else
puts I18n.t('display.nothing')
end
rescue Exception => e
puts "#{I18n.t('display.error')} #14: #{e}".red
end
# Remove an item
# @args: id -> the item's id
# force -> no resquest a validation
def delete(id, force=false)
@clipboard = false
item = @mpw.search_by_id(id)
# @args: options -> the option to search
def delete(options={})
items = @mpw.list(options)
if items.length == 0
puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
else
table(items)
if item.nil?
puts I18n.t('form.delete_item.not_valid', id: id)
return
end
if not force
display_item(item)
confirm = ask("#{I18n.t('form.delete_item.ask', id: id)} (y/N) ").to_s
item = get_item(items)
confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
if not confirm =~ /^(y|yes|YES|Yes|Y)$/
return
return false
end
item.delete
@mpw.write_data
@mpw.sync(true) if @sync
puts "#{I18n.t('form.delete_item.valid')}".green
end
item.delete
@mpw.write_data
@mpw.sync(true) if @sync
puts "#{I18n.t('form.delete_item.valid', id: id)}".green
rescue Exception => e
puts "#{I18n.t('display.error')} #16: #{e}".red
end
# Copy a password, otp, login
# @args: clipboard -> enable clipboard
# options -> the option to search
def copy(clipboard=true, options={})
items = @mpw.list(options)
if items.length == 0
puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
else
table(items)
item = get_item(items)
clipboard(item, clipboard)
end
rescue Exception => e
puts "#{I18n.t('display.error')} #14: #{e}".red
end
# Export the items in a CSV file
# @args: file -> the destination file
def export(file)
@mpw.export(file)
# options -> option to search
def export(file, options)
file = 'export-mpw.yml' if file.to_s.empty?
items = @mpw.list(options)
data = {}
i = 1
puts "#{I18n.t('export.export.valid', file)}".green
items.each do |item|
data.merge!(i => { 'host' => item.host,
'user' => item.user,
'group' => item.group,
'password' => @mpw.get_password(item.id),
'protocol' => item.protocol,
'port' => item.port,
'otp_key' => @mpw.get_otp_key(item.id),
'comment' => item.comment,
'last_edit' => item.last_edit,
'created' => item.created,
}
)
i += 1
end
File.open(file, 'w') {|f| f << data.to_yaml}
puts "#{I18n.t('export.valid', file)}".green
rescue Exception => e
puts "#{I18n.t('display.error')} #17: #{e}".red
end
@ -448,7 +517,26 @@ class Cli
# Import items from a YAML file
# @args: file -> the import file
def import(file)
@mpw.import(file)
raise I18n.t('import.file_empty') if file.to_s.empty?
raise I18n.t('import.file_not_exist') if not File.exist?(file)
YAML::load_file(file).each_value do |row|
item = Item.new(group: row['group'],
host: row['host'],
protocol: row['protocol'],
user: row['user'],
port: row['port'],
comment: row['comment'],
)
next if item.empty?
@mpw.add(item)
@mpw.set_password(item.id, row['password']) if not row['password'].to_s.empty?
@mpw.set_otp_key(item.id, row['otp_key']) if not row['otp_key'].to_s.empty?
end
@mpw.write_data
puts "#{I18n.t('form.import.valid')}".green

View file

@ -57,20 +57,18 @@ class Config
# gpg_exe -> the path of gpg executable
# @rtrn: true if le config file is create
def setup(key, lang, wallet_dir, gpg_exe)
if not key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
raise I18n.t('error.config.key_bad_format')
end
if wallet_dir.empty?
if wallet_dir.to_s.empty?
wallet_dir = "#{@config_dir}/wallets"
end
config = {'config' => {'key' => key,
'lang' => lang,
'wallet_dir' => wallet_dir,
'gpg_exe' => gpg_exe,
}
config = { 'key' => key,
'lang' => lang,
'wallet_dir' => wallet_dir,
'gpg_exe' => gpg_exe,
}
FileUtils.mkdir_p(wallet_dir, mode: 0700)
@ -89,9 +87,9 @@ class Config
# expire -> the time of expire to GPG key
# @rtrn: true if the GPG key is create, else false
def setup_gpg_key(password, name, length = 4096, expire = 0)
if name.nil? or name.empty?
if name.to_s.empty?
raise "#{I18n.t('error.config.genkey_gpg.name')}"
elsif password.nil? or password.empty?
elsif password.to_s.empty?
raise "#{I18n.t('error.config.genkey_gpg.password')}"
end
@ -114,22 +112,20 @@ class Config
raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
end
# Check the config file
# @rtrn: true if the config file is correct
def is_valid?
# Load the config file
def load_config
config = YAML::load_file(@config_file)
@key = config['config']['key']
@lang = config['config']['lang']
@wallet_dir = config['config']['wallet_dir']
@gpg_exe = config['config']['gpg_exe']
@key = config['key']
@lang = config['lang']
@wallet_dir = config['wallet_dir']
@gpg_exe = config['gpg_exe']
raise if @key.empty? or @wallet_dir.empty?
I18n.locale = @lang.to_sym
return true
rescue
return false
rescue Exception => e
raise "#{I18n.t('error.config.load')}\n#{e}"
end
# Check if private key exist

View file

@ -22,12 +22,12 @@ module MPW
class Item
attr_accessor :id
attr_accessor :name
attr_accessor :group
attr_accessor :host
attr_accessor :protocol
attr_accessor :user
attr_accessor :port
attr_accessor :otp
attr_accessor :comment
attr_accessor :last_edit
attr_accessor :last_sync
@ -38,15 +38,15 @@ class Item
# @args: options -> a hash of parameter
# raise an error if the hash hasn't the key name
def initialize(options={})
if not options.has_key?(:name) or options[:name].to_s.empty?
if not options.has_key?(:host) or options[:host].to_s.empty?
raise I18n.t('error.update.name_empty')
end
if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?
@id = generate_id
if not options.has_key?(:id) or options[:id].to_s.empty? or not options.has_key?(:created) or options[:created].to_s.empty?
@id = generate_id
@created = Time.now.to_i
else
@id = options[:id]
@id = options[:id]
@created = options[:created]
@last_edit = options[:last_edit]
options[:no_update_last_edit] = true
@ -58,16 +58,16 @@ class Item
# Update the item
# @args: options -> a hash of parameter
def update(options={})
if options.has_key?(:name) and options[:name].to_s.empty?
if options.has_key?(:host) and options[:host].to_s.empty?
raise I18n.t('error.update.name_empty')
end
@name = options[:name] if options.has_key?(:name)
@group = options[:group] if options.has_key?(:group)
@host = options[:host] if options.has_key?(:host)
@protocol = options[:protocol] if options.has_key?(:protocol)
@user = options[:user] if options.has_key?(:user)
@port = options[:port].to_i if options.has_key?(:port) and not options[:port].to_s.empty?
@otp = options[:otp] if options.has_key?(:otp)
@comment = options[:comment] if options.has_key?(:comment)
@last_edit = Time.now.to_i if not options.has_key?(:no_update_last_edit)
end
@ -80,12 +80,12 @@ class Item
# Delete all data
def delete
@id = nil
@name = nil
@group = nil
@host = nil
@protocol = nil
@user = nil
@port = nil
@otp = nil
@comment = nil
@created = nil
@last_edit = nil
@ -93,7 +93,7 @@ class Item
end
def empty?
return @name.to_s.empty?
return @id.to_s.empty?
end
def nil?
@ -104,6 +104,6 @@ class Item
private
def generate_id
return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
end
end
end
end
end

View file

@ -33,7 +33,7 @@ class MPW
@gpg_exe = gpg_exe
@wallet_file = wallet_file
if @gpg_exe
if not @gpg_exe.to_s.empty?
GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg")
end
end
@ -83,12 +83,12 @@ class MPW
if not data.nil? and not data.empty?
YAML.load(data).each_value do |d|
@data.push(Item.new(id: d['id'],
name: d['name'],
group: d['group'],
host: d['host'],
protocol: d['protocol'],
user: d['user'],
port: d['port'],
otp: @otp_keys.has_key?(d['id']),
comment: d['comment'],
last_edit: d['last_edit'],
created: d['created'],
@ -110,16 +110,15 @@ class MPW
@data.each do |item|
next if item.empty?
data.merge!(item.id => {'id' => item.id,
'name' => item.name,
'group' => item.group,
'host' => item.host,
'protocol' => item.protocol,
'user' => item.user,
'port' => item.port,
'comment' => item.comment,
'last_edit' => item.last_edit,
'created' => item.created,
data.merge!(item.id => { 'id' => item.id,
'group' => item.group,
'host' => item.host,
'protocol' => item.protocol,
'user' => item.user,
'port' => item.port,
'comment' => item.comment,
'last_edit' => item.last_edit,
'created' => item.created,
}
)
end
@ -158,7 +157,7 @@ class MPW
File.rename(tmp_file, @wallet_file)
rescue Exception => e
File.unlink(tmp_file)
File.unlink(tmp_file) if File.exist?(tmp_file)
raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
end
@ -211,16 +210,16 @@ class MPW
# Set config
# args: config -> a hash with config options
def set_config(config)
@config['sync'] = {} if @config['sync'].nil?
def set_config(options={})
@config = {} if @config.nil?
@config['sync']['type'] = config['sync']['type']
@config['sync']['host'] = config['sync']['host']
@config['sync']['port'] = config['sync']['port']
@config['sync']['user'] = config['sync']['user']
@config['sync']['password'] = config['sync']['password']
@config['sync']['path'] = config['sync']['path']
@config['sync']['last_sync'] = @config['sync']['last_sync'].nil? ? 0 : @config['sync']['last_sync']
@config['protocol'] = options[:protocol] if options.has_key?(:protocol)
@config['host'] = options[:host] if options.has_key?(:host)
@config['port'] = options[:port] if options.has_key?(:port)
@config['user'] = options[:user] if options.has_key?(:user)
@config['password'] = options[:password] if options.has_key?(:password)
@config['path'] = options[:path] if options.has_key?(:path)
@config['last_sync'] = @config['last_sync'].nil? ? 0 : @config['last_sync']
end
# Add a new item
@ -241,18 +240,17 @@ class MPW
def list(options={})
result = []
search = options[:search].to_s.downcase
group = options[:group].to_s.downcase
search = options[:pattern].to_s.downcase
group = options[:group].to_s.downcase
@data.each do |item|
next if item.empty?
next if not group.empty? and not group.eql?(item.group.downcase)
next if not group.empty? and not group.eql?(item.group.to_s.downcase)
name = item.name.to_s.downcase
host = item.host.to_s.downcase
comment = item.comment.to_s.downcase
if not name =~ /^.*#{search}.*$/ and not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/
if not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/
next
end
@ -273,56 +271,9 @@ class MPW
return nil
end
# Export to yaml
# @args: file -> file where you export the data
def export(file)
data = {}
@data.each do |item|
data.merge!(item.id => {'id' => item.id,
'name' => item.name,
'group' => item.group,
'host' => item.host,
'protocol' => item.protocol,
'user' => item.user,
'password' => get_password(item.id),
'port' => item.port,
'comment' => item.comment,
'last_edit' => item.last_edit,
'created' => item.created,
}
)
end
File.open(file, 'w') {|f| f << data.to_yaml}
rescue Exception => e
raise "#{I18n.t('error.export', file: file)}\n#{e}"
end
# Import to yaml
# @args: file -> path to file import
def import(file)
YAML::load_file(file).each_value do |row|
item = Item.new(name: row['name'],
group: row['group'],
host: row['host'],
protocol: row['protocol'],
user: row['user'],
port: row['port'],
comment: row['comment'],
)
raise 'Item is empty' if item.empty?
@data.push(item)
set_password(item.id, row['password'])
end
rescue Exception => e
raise "#{I18n.t('error.import', file: file)}\n#{e}"
end
# Get last sync
def get_last_sync
return @config['sync']['last_sync'].to_i
return @config['last_sync'].to_i
rescue
return 0
end
@ -330,18 +281,18 @@ class MPW
# Sync data with remote file
# @args: force -> force the sync
def sync(force=false)
return if @config.empty? or @config['sync']['type'].to_s.empty?
return if @config.empty? or @config['protocol'].to_s.empty?
return if get_last_sync + 300 > Time.now.to_i and not force
tmp_file = "#{@wallet_file}.sync"
case @config['sync']['type']
case @config['protocol']
when 'sftp', 'scp', 'ssh'
require "mpw/sync/ssh"
sync = SyncSSH.new(@config['sync'])
sync = SyncSSH.new(@config)
when 'ftp'
require 'mpw/sync/ftp'
sync = SyncFTP.new(@config['sync'])
sync = SyncFTP.new(@config)
else
raise I18n.t('error.sync.unknown_type')
end
@ -365,8 +316,7 @@ class MPW
# Update item
if item.last_edit < r.last_edit
item.update(name: r.name,
group: r.group,
item.update(group: r.group,
host: r.host,
protocol: r.protocol,
user: r.user,
@ -394,7 +344,6 @@ class MPW
next if r.last_edit <= get_last_sync
item = Item.new(id: r.id,
name: r.name,
group: r.group,
host: r.host,
protocol: r.protocol,
@ -415,7 +364,7 @@ class MPW
item.set_last_sync
end
@config['sync']['last_sync'] = Time.now.to_i
@config['last_sync'] = Time.now.to_i
write_data
sync.update(@wallet_file)
@ -429,13 +378,27 @@ class MPW
# args: id -> the item id
# key -> the new key
def set_otp_key(id, key)
@otp_keys[id] = encrypt(key)
if not key.to_s.empty?
@otp_keys[id] = encrypt(key.to_s)
end
end
# Get an opt key
# args: id -> the item id
# key -> the new key
def get_otp_key(id)
if @otp_keys.has_key?(id)
return decrypt(@otp_keys[id])
else
return nil
end
end
# Get an otp code
# @args: id -> the item id
# @rtrn: an otp code
def get_otp_code(id)
def get_otp_code(id)
if not @otp_keys.has_key?(id)
return 0
else
@ -481,6 +444,8 @@ class MPW
# @args: data -> string to decrypt
private
def decrypt(data)
return nil if data.to_s.empty?
crypto = GPGME::Crypto.new(armor: true)
return crypto.decrypt(data, password: @gpg_pass).read.force_encoding('utf-8')
@ -495,12 +460,12 @@ class MPW
recipients = []
crypto = GPGME::Crypto.new(armor: true, always_trust: true)
recipients.push(@key)
@keys.each_key do |key|
next if key == @key
recipients.push(key)
end
recipients.push(@key) if not recipients.index(@key).nil?
return crypto.encrypt(data, recipients: recipients).read
rescue Exception => e
raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"

View file

@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
spec.add_dependency "locale"
spec.add_dependency "colorize"
spec.add_dependency "net-ssh"
spec.add_dependency "net-scp"
spec.add_dependency "net-sftp"
spec.add_dependency "clipboard"
spec.add_dependency "rotp"
end

9
templates/add_form.erb Normal file
View file

@ -0,0 +1,9 @@
---
host: # <%= I18n.t('form.add_item.host') %>
user: # <%= I18n.t('form.add_item.login') %>
group: # <%= I18n.t('form.add_item.group') %>
protocol: # <%= I18n.t('form.add_item.protocol') %><% if not password %>
password: # <%= I18n.t('form.add_item.password') %><% end %>
port: # <%= I18n.t('form.add_item.port') %>
comment: # <%= I18n.t('form.add_item.comment') %>
otp_key: # <%= I18n.t('form.add_item.otp_key') %>

9
templates/setup_form.erb Normal file
View file

@ -0,0 +1,9 @@
---
# <%= I18n.t('form.setup_config.lang') %>
language: <%= @config.lang %>
# <%= I18n.t('form.setup_config.gpg_key') %>
gpg_key: <%= @config.key %>
# <%= I18n.t('form.setup_config.wallet_dir') %>
wallet_dir: <%= @config.config_dir %>
# <%= I18n.t('form.setup_config.gpg_exe') %>
gpg_exe: <%= @config.gpg_exe %>

17
templates/update_form.erb Normal file
View file

@ -0,0 +1,17 @@
---
# <%= I18n.t('form.update_item.host') %>
host: <%= item.host %>
# <%= I18n.t('form.update_item.login') %>
user: <%= item.user %>
# <%= I18n.t('form.update_item.password') %>
password:
# <%= I18n.t('form.update_item.group') %>
group: <%= item.group %>
# <%= I18n.t('form.update_item.protocol') %>
protocol: <%= item.protocol %>
# <%= I18n.t('form.update_item.port') %>
port: <%= item.port %>
# <%= I18n.t('form.update_item.otp_key') %>
opt_key:
# <%= I18n.t('form.update_item.comment') %>
comment: <%= item.comment %>