1
0
Fork 0
mirror of https://github.com/nishiki/manage-password.git synced 2025-02-22 10:50:10 +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__) $: << File.expand_path('../../lib', __FILE__)
require 'optparse'
require 'locale' require 'locale'
require 'set' require 'set'
require 'i18n' require 'i18n'
require 'mpw/mpw'
require 'mpw/config'
require 'mpw/cli'
# --------------------------------------------------------- # # --------------------------------------------------------- #
# Set local # Set local
@ -45,190 +41,9 @@ I18n.locale = lang.to_sym
# Options # Options
# --------------------------------------------------------- # # --------------------------------------------------------- #
options_password = {} bin_dir = File.dirname(__FILE__)
options = {} command = "#{bin_dir}/mpw-#{ARGV[0]}"
options[:force] = false
options[:otp] = false
options[:sync] = true
options[:clipboard] = true
options[:group] = nil
options[:config] = nil
options[:wallet] = nil
OptionParser.new do |opts| if Dir.glob("#{bin_dir}/mpw-*").include?("#{command}")
opts.banner = "#{I18n.t('option.usage')}: mpw [options]" Kernel.load(command)
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
end 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: error:
config: config:
write: "Can't write the config file!" write: "Can't write the config file!"
check: "Checkconfig failed!" load: "Checkconfig failed!"
key_bad_format: "The key string isn't in good format!" key_bad_format: "The key string isn't in good format!"
no_key_public: "You haven't the public key of %{key}!" no_key_public: "You haven't the public key of %{key}!"
genkey_gpg: genkey_gpg:
exception: "Can't create the GPG key!" exception: "Can't create the GPG key!"
name: "You must define a name for your GPG key!" name: "You must define a name for your GPG key!"
password: "You must define a password 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}!" export: "Can't export, unable to write in %{file}!"
gpg_file: gpg_file:
decrypt: "Can't decrypt file!" decrypt: "Can't decrypt file!"
@ -41,41 +39,51 @@ en:
config: "Specify the configuration file to use" config: "Specify the configuration file to use"
clipboard: "Disable the clipboard feature" clipboard: "Disable the clipboard feature"
export: "Export a wallet in an yaml file" 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" force: "No ask to confirm when you delete an item"
generate_password: "Generate a random password (default 8 characters)" 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" group: "Search the items with specified group"
help: "Show this help message" 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" 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" no_sync: "Disable synchronization with the server"
numeric: "Use number to generate a password" 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: "Create a new configuration file"
setup_wallet: "Create a new configuration file for a wallet" setup_wallet: "Create a new configuration file for a wallet"
special_chars: "Use special char to generate a password" special_chars: "Use special char to generate a password"
show: "Search and show the items" show: "Search and show the items"
show_all: "List all items" show_all: "List all items"
remove: "Delete an item"
update: "Update an item"
usage: "Usage" usage: "Usage"
user: "Specify the user for the connection"
wallet: "Specify a wallet to use" wallet: "Specify a wallet to use"
wallet_dir: "Set the wallets folder"
form: form:
select: "Select the item: " select: "Select the item: "
add_key: add_key:
valid: "Key has been added!" valid: "Key has been added!"
add_item: add_item:
title: "Add a new item" name: "The item's name (mandatory"
name: "Enter the name: " group: "The group's name"
group: "Enter the group (optional): " host: "The hostname or ip"
server: "Enter the hostname or ip: " protocol: "The protocol of the connection (ssh, http, ...)"
protocol: "Enter the protocol of the connection (ssh, http, other): " login: "The login of connection"
login: "Enter the login connection: " password: "The password"
password: "Enter the the password: " port: "The connection port"
port: "Enter the connection port (optional): " comment: "A comment"
comment: "Enter a comment (optional): " otp_key: "The OTP secret"
otp_key: "Enter the otp secret (base32 secret key): "
valid: "Item has been added!" valid: "Item has been added!"
clipboard: clipboard:
choice: "What do you want to copy ? [q = quit, p = password, l = login]: " 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" login: "Press <l> to copy the login"
password: "Press <p> to copy the password" password: "Press <p> to copy the password"
otp_code: "Press <o> to copy the otp code" otp_code: "Press <o> to copy the otp code"
quit: "Press <q> to quit"
delete_key: delete_key:
valid: "Key has been deleted!" valid: "Key has been deleted!"
delete_item: delete_item:
ask: "Are you sure you want to remove the item %{id} ?" ask: "Are you sure you want to remove the item ?"
valid: "The item %{id} has been removed!" valid: "The item has been removed!"
not_valid: "The item %{id} hasn't been removed, because it doesn't exist!"
import: import:
ask: "Are you sure you want to import this file %{file} ?" ask: "Are you sure you want to import this file %{file} ?"
valid: "The import is succesfull!" valid: "The import is succesfull!"
@ -127,16 +135,15 @@ en:
wait: "Please waiting during the GPG key generate, this process can take few minutes." wait: "Please waiting during the GPG key generate, this process can take few minutes."
valid: "Your GPG key has been created ;-)" valid: "Your GPG key has been created ;-)"
update_item: update_item:
title: "Update an item" name: "The item's name (mandatory"
name: "Enter the name [%{name}]: " group: "The group's name"
group: "Enter the group [%{group}]: " host: "The hostname or ip"
server: "Enter the hostname or ip [%{server}]: " protocol: "The protocol of the connection (ssh, http, ...)"
protocol: "Enter the protocol of the connection [%{protocol}]: " login: "The login of connection"
login: "Enter the login connection [%{login}]: " password: "The password (leave empty if you don't want change)"
password: "Enter the the password: " port: "The connection port"
port: "Enter the connection port [%{port}]: " comment: "A comment"
comment: "Enter a comment [%{comment}]: " otp_key: "The OTP secret (leave empty if you don't want change"
otp_key: "Enter the otp secret (base32 secret key): "
valid: "Item has been updated!" valid: "Item has been updated!"
export: export:
valid: "The export in %{file} is succesfull!" valid: "The export in %{file} is succesfull!"

View file

@ -3,15 +3,13 @@ fr:
error: error:
config: config:
write: "Impossible d'écrire le fichier de configuration!" 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!" key_bad_format: "La clé GPG est invalide!"
no_key_public: "Vous ne possédez pas la clé publique de %{key}!" no_key_public: "Vous ne possédez pas la clé publique de %{key}!"
genkey_gpg: genkey_gpg:
exception: "La création de la clé GPG n'a pas pu aboutir!" exception: "La création de la clé GPG n'a pas pu aboutir!"
name: "Vous devez définir un nom pour votre clé GPG!" name: "Vous devez définir un nom pour votre clé GPG!"
password: "Vous devez définir un mot de passe 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}!" export: "Impossible d'exporter les données dans le fichier %{file}!"
gpg_file: gpg_file:
decrypt: "Impossible de déchiffrer le fichier GPG!" decrypt: "Impossible de déchiffrer le fichier GPG!"
@ -41,41 +39,51 @@ fr:
config: "Spécifie le fichier de configuration à utiliser" config: "Spécifie le fichier de configuration à utiliser"
clipboard: "Désactive la fonction presse papier" clipboard: "Désactive la fonction presse papier"
export: "Exporte un portefeuille dans un fichier yaml" 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" 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)" 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é" group: "Recherche les éléments appartenant au groupe spécifié"
help: "Affiche ce message d'aide" 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" 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" no_sync: "Désactive la synchronisation avec le serveur"
numeric: "Utilise des chiffre dans la génération d'un mot de passe" 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: "Création d'un nouveau fichier de configuration"
setup_wallet: "Création d'un nouveau fichier de configuration pour un portefeuille" 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" 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: "Recherche et affiche les éléments"
show_all: "Liste tous les éléments" show_all: "Liste tous les éléments"
remove: "Supprime un élément"
update: "Met à jour un élément"
usage: "Utilisation" usage: "Utilisation"
user: "Spécifie l'identifiant de connection"
wallet: "Spécifie le portefeuille à utiliser" wallet: "Spécifie le portefeuille à utiliser"
wallet_dir: "Spécifie le répertoire des portefeuilles"
form: form:
select: "Sélectionner l'élément: " select: "Sélectionner l'élément: "
add_key: add_key:
valid: "La clé a bien été ajoutée!" valid: "La clé a bien été ajoutée!"
add_item: add_item:
title: "Ajout d'un nouvel élément" name: "Le nom de l'élément (obligatoire)"
name: "Entrez le nom: " group: "Le nom du groupe"
group: "Entrez le groupe (optionnel): " host: "Le nom de domaine ou l'ip"
server: "Entrez le nom de domaine ou l'ip: " protocol: "Le protocole de connexion (ssh, http, ...)"
protocol: "Entrez le protocole de connexion (ssh, http, other): " login: "L'identifiant de connexion"
login: "Entrez l'identifiant de connexion: " password: "Le mot de passe"
password: "Entrez le mot de passe: " port: "Le port de connexion"
port: "Entrez le port de connexion (optionnel): " comment: "Un commentaire"
comment: "Entrez un commentaire (optionnel): " otp_key: "Le secret OTP"
otp_key: "Entrez le secret OTP: "
valid: "L'élément a bien été ajouté!" valid: "L'élément a bien été ajouté!"
clipboard: clipboard:
choice: "Que voulez-vous copier ? : " choice: "Que voulez-vous copier ? : "
@ -88,12 +96,12 @@ fr:
login: "Pressez <l> pour copier l'identifiant" login: "Pressez <l> pour copier l'identifiant"
password: "Pressez <p> pour copier le mot de passe" password: "Pressez <p> pour copier le mot de passe"
otp_code: "Pressez <o> pour copier le code OTP" otp_code: "Pressez <o> pour copier le code OTP"
quit: "Pressez <q> pour quitter"
delete_key: delete_key:
valid: "La clé a bien été supprimée!" valid: "La clé a bien été supprimée!"
delete_item: delete_item:
ask: "Êtes vous sûre de vouloir supprimer l'élément %{id} ?" ask: "Êtes vous sûre de vouloir supprimer l'élément ?"
valid: "L'élément %{id} a bien été supprimé!" valid: "L'élément a bien été supprimé!"
not_valid: "L'élément %{id} n'a pu être supprimé, car il n'existe pas!"
import: import:
ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?" ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?"
valid: "L'import est un succès!" 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." 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 ;-)" valid: "Votre clé GPG a bien été créée ;-)"
update_item: update_item:
title: "Mis à jour d'un élément" name: "Le nom de l'élément (obligatoire)"
name: "Entrez le nom [%{name}]: " group: "Le nom du groupe"
group: "Entrez le groupe [%{group}]: " host: "Le nom de domaine ou l'ip"
server: "Entrez le nom de domaine ou l'ip du serveur [%{server}]: " protocol: "Le protocole de connexion (ssh, http, ...)"
protocol: "Entrez le protocole de connexion [%{protocol}]: " login: "L'identifiant de connexion"
login: "Entrez votre identifiant de connexion [%{login}]: " password: "Le mot de passe (laissez vide si vous ne voulez pas le changer)"
password: "Entrez le mot de passe: " port: "Le port de connexion"
port: "Entrez un port de connexion [%{port}]: " comment: "Un commentaire"
comment: "Entrez un commentaire [%{comment}]: " otp_key: "Le secret OTP (laissez vide si vous ne voulez pas le changer)"
otp_key: "Entrez le secret OTP: "
valid: "L'élément a bien été mis à jour!" valid: "L'élément a bien été mis à jour!"
export: export:
valid: "L'export dans %{file} est un succès!" 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. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'readline' require 'readline'
require 'locale'
require 'i18n' require 'i18n'
require 'colorize' require 'colorize'
require 'highline/import' require 'highline/import'
require 'clipboard' require 'clipboard'
require 'tmpdir'
require 'mpw/item' require 'mpw/item'
require 'mpw/mpw' require 'mpw/mpw'
@ -30,33 +32,35 @@ class Cli
# Constructor # Constructor
# @args: config -> the config # @args: config -> the config
# sync -> boolean for sync or not # sync -> boolean for sync or not
# clipboard -> enable clopboard def initialize(config, sync=true)
# otp -> enable otp
def initialize(config, clipboard=true, sync=true, otp=false)
@config = config @config = config
@clipboard = clipboard
@sync = sync @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 end
# Create a new config file # Create a new config file
# @args: lang -> the software language # @args: options -> set param
def setup(lang) def setup(options)
puts I18n.t('form.setup_config.title') lang = options[:lang] || Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
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
if language.nil? or language.empty? I18n.locale = lang.to_sym
language = lang
end
I18n.locale = language.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 puts "#{I18n.t('form.setup_config.valid')}".green
rescue Exception => e rescue Exception => e
@ -65,16 +69,10 @@ class Cli
end end
# Setup a new GPG key # Setup a new GPG key
def setup_gpg_key # @args: gpg_key -> the key name
puts I18n.t('form.setup_gpg_key.title') def setup_gpg_key(gpg_key)
puts '--------------------' return if @config.check_gpg_key?
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
name = ask(I18n.t('form.setup_gpg_key.name')).to_s
password = ask(I18n.t('form.setup_gpg_key.password')) {|q| q.echo = false} 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} 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') raise I18n.t('form.setup_gpg_key.error_password')
end end
length = ask(I18n.t('form.setup_gpg_key.length')).to_s @password = password.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
puts I18n.t('form.setup_gpg_key.wait') 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 puts "#{I18n.t('form.setup_gpg_key.valid')}".green
rescue Exception => e rescue Exception => e
@ -100,23 +93,17 @@ class Cli
end end
# Setup wallet config for sync # Setup wallet config for sync
def setup_wallet_config # @args: options -> value to change
config = {} def setup_wallet_config(options={})
config['sync'] = {} if not options[:password].nil?
options[:password] = ask(I18n.t('form.setup_wallet.password')) {|q| q.echo = false}
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
end 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 @mpw.write_data
puts "#{I18n.t('form.setup_wallet.valid')}".green puts "#{I18n.t('form.setup_wallet.valid')}".green
@ -125,6 +112,15 @@ class Cli
exit 2 exit 2
end 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 # Request the GPG password and decrypt the file
def decrypt def decrypt
if not defined?(@mpw) if not defined?(@mpw)
@ -139,97 +135,119 @@ class Cli
exit 2 exit 2
end end
# Display the query's result # Format items on a table
# @args: search -> the string to search def table(items=[])
# protocol -> search from a particular protocol group = '.'
def display(options={})
result = @mpw.list(options)
case result.length
when 0
puts I18n.t('display.nothing')
when 1
display_item(result.first)
else
group = nil
i = 1 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' },
}
result.sort! { |a,b| a.group.downcase <=> b.group.downcase } items.each do |item|
data.each do |k, v|
next if k == :id or k == :otp
result.each do |item| 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 }
items.each do |item|
if group != item.group if group != item.group
group = item.group group = item.group
if group.empty? if group.to_s.empty?
puts I18n.t('display.no_group').yellow puts "\n#{I18n.t('display.no_group')}".red
else else
puts "\n#{group}".yellow puts "\n#{group}".red
end
end end
print " |_ ".yellow print ' '
print "#{i}: ".cyan length_total.times { print '=' }
print item.name print "\n "
print " -> #{item.comment}".magenta if not item.comment.to_s.empty? 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"
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" print "\n"
i += 1 i += 1
end end
print "\n" 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
# Get an item when multiple choice
# @args: items -> array of items
# @rtrn: item
def get_item(items)
return items[0] if items.length == 1
items.sort! { |a,b| a.group.to_s.downcase <=> b.group.to_s.downcase }
choice = ask(I18n.t('form.select')).to_i choice = ask(I18n.t('form.select')).to_i
if choice >= 1 and choice < i if choice >= 1 and choice <= items.length
display_item(result[choice-1]) return items[choice-1]
else else
puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow return nil
end end
end 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
if @clipboard
print "#{I18n.t('display.password')}: ".cyan
puts '***********'
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
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 # Copy in clipboard the login and password
# @args: item -> the item # @args: item -> the item
def clipboard(item) # clipboard -> enable clipboard
def clipboard(item, clipboard=true)
pid = nil pid = nil
# Security: force quit after 90s # Security: force quit after 90s
@ -246,10 +264,15 @@ class Cli
break break
when 'l', 'login' when 'l', 'login'
if clipboard
Clipboard.copy(item.user) Clipboard.copy(item.user)
puts I18n.t('form.clipboard.login').green puts I18n.t('form.clipboard.login').green
else
puts item.user
end
when 'p', 'password' when 'p', 'password'
if clipboard
Clipboard.copy(@mpw.get_password(item.id)) Clipboard.copy(@mpw.get_password(item.id))
puts I18n.t('form.clipboard.password').yellow puts I18n.t('form.clipboard.password').yellow
@ -258,9 +281,16 @@ class Cli
Clipboard.clear Clipboard.clear
end end
else
puts @mpw.get_password(item.id)
end
when 'o', 'otp' when 'o', 'otp'
if clipboard
Clipboard.copy(@mpw.get_otp_code(item.id)) 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 puts I18n.t('form.clipboard.otp', time: @mpw.get_otp_remaining_time).yellow
else else
@ -268,6 +298,7 @@ class Cli
puts I18n.t('form.clipboard.help.login') puts I18n.t('form.clipboard.help.login')
puts I18n.t('form.clipboard.help.password') puts I18n.t('form.clipboard.help.password')
puts I18n.t('form.clipboard.help.otp_code') puts I18n.t('form.clipboard.help.otp_code')
puts I18n.t('form.clipboard.help.quit')
next next
end end
end end
@ -277,34 +308,25 @@ class Cli
Clipboard.clear Clipboard.clear
end 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 # Display the wallet
# @args: wallet -> the wallet name # @args: wallet -> the wallet name
def get_wallet(wallet=nil) def get_wallet(wallet=nil)
if wallet.to_s.empty? if wallet.to_s.empty?
wallets = Dir.glob("#{@config.wallet_dir}/*.mpw") wallets = Dir.glob("#{@config.wallet_dir}/*.mpw")
case wallets.length if wallets.length == 1
when 0
puts I18n.t('display.nothing')
when 1
@wallet_file = wallets[0] @wallet_file = wallets[0]
else else
i = 1 @wallet_file = "#{@config.wallet_dir}/default.mpw"
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
end end
else else
@wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw" @wallet_file = "#{@config.wallet_dir}/#{wallet}.mpw"
@ -336,111 +358,158 @@ class Cli
puts "#{I18n.t('display.error')} #15: #{e}".red puts "#{I18n.t('display.error')} #15: #{e}".red
end end
# Form to add a new item # Text editor interface
def add # @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 = {} 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') Dir.mktmpdir do |dir|
puts '--------------------' tmp_file = "#{dir}/#{template_name}.yml"
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
if @otp File.open(tmp_file, 'w') do |f|
otp_key = ask(I18n.t('form.add_item.otp_key')).to_s f << template.result(binding)
end end
system("#{editor} #{tmp_file}")
opts = YAML::load_file(tmp_file)
end
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) item = Item.new(options)
if password
options[:password] = MPW::password(length: 24)
end
@mpw.add(item) @mpw.add(item)
@mpw.set_password(item.id, password) @mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
@mpw.set_otp_key(item.id, otp_key) @mpw.set_otp_key(item.id, options[:otp_key]) if options.has_key?(:otp_key)
@mpw.write_data @mpw.write_data
@mpw.sync(true) if @sync @mpw.sync(true) if @sync
puts "#{I18n.t('form.add_item.valid')}".green puts "#{I18n.t('form.add_item.valid')}".green
rescue Exception => e
puts "#{I18n.t('display.error')} #13: #{e}".red
end end
# Update an item # Update an item
# @args: id -> the item's id # @args: options -> the option to search
def update(id) def update(options={})
item = @mpw.search_by_id(id) items = @mpw.list(options)
if not item.nil? if items.length == 0
options = {} puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
else
table(items) if items.length > 1
puts I18n.t('form.update_item.title') item = get_item(items)
puts '--------------------' options = text_editor('update_form', item)
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.update(options) item.update(options)
@mpw.set_password(item.id, password) if not password.empty? @mpw.set_password(item.id, options[:password]) if options.has_key?(:password)
@mpw.set_otp_key(item.id, otp_key) if not otp_key.to_s.empty? @mpw.set_otp_key(item.id, options[:otp_key]) if options.has_key?(:otp_key)
@mpw.write_data @mpw.write_data
@mpw.sync(true) if @sync @mpw.sync(true) if @sync
puts "#{I18n.t('form.update_item.valid')}".green puts "#{I18n.t('form.update_item.valid')}".green
else
puts I18n.t('display.nothing')
end end
rescue Exception => e rescue Exception => e
puts "#{I18n.t('display.error')} #14: #{e}".red puts "#{I18n.t('display.error')} #14: #{e}".red
end end
# Remove an item # Remove an item
# @args: id -> the item's id # @args: options -> the option to search
# force -> no resquest a validation def delete(options={})
def delete(id, force=false) items = @mpw.list(options)
@clipboard = false
item = @mpw.search_by_id(id)
if item.nil? if items.length == 0
puts I18n.t('form.delete_item.not_valid', id: id) puts "#{I18n.t('display.warning')}: #{I18n.t('warning.select')}".yellow
return else
end table(items)
if not force item = get_item(items)
display_item(item) confirm = ask("#{I18n.t('form.delete_item.ask')} (y/N) ").to_s
confirm = ask("#{I18n.t('form.delete_item.ask', id: id)} (y/N) ").to_s
if not confirm =~ /^(y|yes|YES|Yes|Y)$/ if not confirm =~ /^(y|yes|YES|Yes|Y)$/
return return false
end
end end
item.delete item.delete
@mpw.write_data @mpw.write_data
@mpw.sync(true) if @sync @mpw.sync(true) if @sync
puts "#{I18n.t('form.delete_item.valid', id: id)}".green puts "#{I18n.t('form.delete_item.valid')}".green
end
rescue Exception => e rescue Exception => e
puts "#{I18n.t('display.error')} #16: #{e}".red puts "#{I18n.t('display.error')} #16: #{e}".red
end 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 # Export the items in a CSV file
# @args: file -> the destination file # @args: file -> the destination file
def export(file) # options -> option to search
@mpw.export(file) 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 rescue Exception => e
puts "#{I18n.t('display.error')} #17: #{e}".red puts "#{I18n.t('display.error')} #17: #{e}".red
end end
@ -448,7 +517,26 @@ class Cli
# Import items from a YAML file # Import items from a YAML file
# @args: file -> the import file # @args: file -> the import file
def 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 @mpw.write_data
puts "#{I18n.t('form.import.valid')}".green puts "#{I18n.t('form.import.valid')}".green

View file

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

View file

@ -22,12 +22,12 @@ module MPW
class Item class Item
attr_accessor :id attr_accessor :id
attr_accessor :name
attr_accessor :group attr_accessor :group
attr_accessor :host attr_accessor :host
attr_accessor :protocol attr_accessor :protocol
attr_accessor :user attr_accessor :user
attr_accessor :port attr_accessor :port
attr_accessor :otp
attr_accessor :comment attr_accessor :comment
attr_accessor :last_edit attr_accessor :last_edit
attr_accessor :last_sync attr_accessor :last_sync
@ -38,7 +38,7 @@ class Item
# @args: options -> a hash of parameter # @args: options -> a hash of parameter
# raise an error if the hash hasn't the key name # raise an error if the hash hasn't the key name
def initialize(options={}) 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') raise I18n.t('error.update.name_empty')
end end
@ -58,16 +58,16 @@ class Item
# Update the item # Update the item
# @args: options -> a hash of parameter # @args: options -> a hash of parameter
def update(options={}) 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') raise I18n.t('error.update.name_empty')
end end
@name = options[:name] if options.has_key?(:name)
@group = options[:group] if options.has_key?(:group) @group = options[:group] if options.has_key?(:group)
@host = options[:host] if options.has_key?(:host) @host = options[:host] if options.has_key?(:host)
@protocol = options[:protocol] if options.has_key?(:protocol) @protocol = options[:protocol] if options.has_key?(:protocol)
@user = options[:user] if options.has_key?(:user) @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? @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) @comment = options[:comment] if options.has_key?(:comment)
@last_edit = Time.now.to_i if not options.has_key?(:no_update_last_edit) @last_edit = Time.now.to_i if not options.has_key?(:no_update_last_edit)
end end
@ -80,12 +80,12 @@ class Item
# Delete all data # Delete all data
def delete def delete
@id = nil @id = nil
@name = nil
@group = nil @group = nil
@host = nil @host = nil
@protocol = nil @protocol = nil
@user = nil @user = nil
@port = nil @port = nil
@otp = nil
@comment = nil @comment = nil
@created = nil @created = nil
@last_edit = nil @last_edit = nil
@ -93,7 +93,7 @@ class Item
end end
def empty? def empty?
return @name.to_s.empty? return @id.to_s.empty?
end end
def nil? def nil?

View file

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