diff --git a/README.md b/README.md deleted file mode 100644 index e3ca84c..0000000 --- a/README.md +++ /dev/null @@ -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: -``` diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..a62c1e2 --- /dev/null +++ b/README.rst @@ -0,0 +1,53 @@ +MPW: Manage your passwords! +******************************************************* + +mpw is a little software which stores your passwords in `GnuPG ` 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 ` + +.. |License| image:: https://img.shields.io/badge/license-GPL--2.0-blue.svg diff --git a/VERSION b/VERSION index e4604e3..c0de572 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.1 +4.0.0-beta diff --git a/bin/mpw b/bin/mpw index f5e717b..2d32700 100755 --- a/bin/mpw +++ b/bin/mpw @@ -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 diff --git a/bin/mpw-add b/bin/mpw-add new file mode 100644 index 0000000..5322cc1 --- /dev/null +++ b/bin/mpw-add @@ -0,0 +1,61 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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]) diff --git a/bin/mpw-config b/bin/mpw-config new file mode 100644 index 0000000..120da5b --- /dev/null +++ b/bin/mpw-config @@ -0,0 +1,75 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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 diff --git a/bin/mpw-copy b/bin/mpw-copy new file mode 100644 index 0000000..0a14ed7 --- /dev/null +++ b/bin/mpw-copy @@ -0,0 +1,71 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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) diff --git a/bin/mpw-delete b/bin/mpw-delete new file mode 100644 index 0000000..2d79e6a --- /dev/null +++ b/bin/mpw-delete @@ -0,0 +1,66 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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) diff --git a/bin/mpw-export b/bin/mpw-export new file mode 100644 index 0000000..f891873 --- /dev/null +++ b/bin/mpw-export @@ -0,0 +1,70 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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) diff --git a/bin/mpw-genpwd b/bin/mpw-genpwd new file mode 100644 index 0000000..8af4290 --- /dev/null +++ b/bin/mpw-genpwd @@ -0,0 +1,50 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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 diff --git a/bin/mpw-import b/bin/mpw-import new file mode 100644 index 0000000..c740252 --- /dev/null +++ b/bin/mpw-import @@ -0,0 +1,61 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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]) diff --git a/bin/mpw-list b/bin/mpw-list new file mode 100644 index 0000000..9d18b0d --- /dev/null +++ b/bin/mpw-list @@ -0,0 +1,66 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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) diff --git a/bin/mpw-update b/bin/mpw-update new file mode 100644 index 0000000..301b876 --- /dev/null +++ b/bin/mpw-update @@ -0,0 +1,66 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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) diff --git a/bin/mpw-wallet b/bin/mpw-wallet new file mode 100644 index 0000000..b81e6b2 --- /dev/null +++ b/bin/mpw-wallet @@ -0,0 +1,91 @@ +#!/usr/bin/ruby +# MPW is a software to crypt and manage your passwords +# Copyright (C) 2016 Adrien Waksberg +# +# 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 diff --git a/i18n/en.yml b/i18n/en.yml index c704849..9a964d2 100644 --- a/i18n/en.yml +++ b/i18n/en.yml @@ -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 to copy the login" password: "Press

to copy the password" otp_code: "Press to copy the otp code" + quit: "Press 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!" diff --git a/i18n/fr.yml b/i18n/fr.yml index 41cac4d..d2cae5b 100644 --- a/i18n/fr.yml +++ b/i18n/fr.yml @@ -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 pour copier l'identifiant" password: "Pressez

pour copier le mot de passe" otp_code: "Pressez pour copier le code OTP" + quit: "Pressez 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!" diff --git a/lib/mpw/cli.rb b/lib/mpw/cli.rb index 392e7bd..722b16d 100644 --- a/lib/mpw/cli.rb +++ b/lib/mpw/cli.rb @@ -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 diff --git a/lib/mpw/config.rb b/lib/mpw/config.rb index 90df409..2c1c496 100644 --- a/lib/mpw/config.rb +++ b/lib/mpw/config.rb @@ -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 diff --git a/lib/mpw/item.rb b/lib/mpw/item.rb index ef1309e..4beb49f 100644 --- a/lib/mpw/item.rb +++ b/lib/mpw/item.rb @@ -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 diff --git a/lib/mpw/mpw.rb b/lib/mpw/mpw.rb index baa83f5..533077e 100644 --- a/lib/mpw/mpw.rb +++ b/lib/mpw/mpw.rb @@ -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}" diff --git a/mpw.gemspec b/mpw.gemspec index fa4afc7..09fe6d9 100644 --- a/mpw.gemspec +++ b/mpw.gemspec @@ -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 diff --git a/templates/add_form.erb b/templates/add_form.erb new file mode 100644 index 0000000..c2d6d73 --- /dev/null +++ b/templates/add_form.erb @@ -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') %> diff --git a/templates/setup_form.erb b/templates/setup_form.erb new file mode 100644 index 0000000..f7e6d7b --- /dev/null +++ b/templates/setup_form.erb @@ -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 %> diff --git a/templates/update_form.erb b/templates/update_form.erb new file mode 100644 index 0000000..f4a380b --- /dev/null +++ b/templates/update_form.erb @@ -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 %>