mirror of
https://github.com/nishiki/manage-password.git
synced 2025-03-20 21:34:35 +00:00
Compare commits
147 commits
4.0.0-beta
...
master
Author | SHA1 | Date | |
---|---|---|---|
be526c297f | |||
fe7a03ba52 | |||
a11777cade | |||
6a2d5da9fb | |||
30ca80f6db | |||
ed09f55ce2 | |||
2de66a4eaf | |||
b0905f18f1 | |||
8fddbd01be | |||
348db7cfd0 | |||
afd6585acc | |||
2abad3695f | |||
ef0a908144 | |||
ce02022ac7 | |||
dd02776baa | |||
8b5c7ae581 | |||
cf5b7e0142 | |||
42629c61b3 | |||
2a647afb10 | |||
e2b4557981 | |||
b2a38ccd4e | |||
8aeb7f5224 | |||
0195771c76 | |||
65db261c00 | |||
948db2e905 | |||
d185509113 | |||
11de55ccb7 | |||
767343e368 | |||
a46e51a6f1 | |||
![]() |
6ea40e5420 | ||
a2c2711ddc | |||
b83b27832e | |||
1fcac965e9 | |||
dcb286ec09 | |||
ab459f954d | |||
5f29f7b6e8 | |||
90486d1e93 | |||
6541931b79 | |||
efff66b12e | |||
811c576aae | |||
01831c1f2b | |||
3b9ff8c15c | |||
7c36986141 | |||
8e4fb1c91b | |||
62318fe077 | |||
![]() |
5f7cb6e0cc | ||
![]() |
ca2e0d9e47 | ||
f41a9e68d3 | |||
![]() |
72c213b7b7 | ||
f150feec10 | |||
977266def8 | |||
![]() |
09e451bf3b | ||
192a14ea96 | |||
a13fcd147f | |||
646f096aac | |||
6a3f2ca007 | |||
f91531b856 | |||
a7a165bca9 | |||
96380d3d93 | |||
9ad8eba901 | |||
83fe41921c | |||
02996b5904 | |||
b2b9437431 | |||
0fdefdf433 | |||
77bca426bc | |||
3f9d7eff33 | |||
b65595d0b7 | |||
388dc439b6 | |||
18ba8967d9 | |||
25baa260e3 | |||
55c07b90af | |||
84afbc4b40 | |||
3b5bb48e6b | |||
621819203f | |||
45ead1e24e | |||
d8df357993 | |||
7ce4ba721b | |||
d1adfd24c1 | |||
3543b0cf05 | |||
![]() |
92cb89ad33 | ||
1e688d191c | |||
2cb8ad4dbe | |||
![]() |
7eb585726f | ||
![]() |
01745cac2d | ||
![]() |
0f7510b6e6 | ||
![]() |
755df6c342 | ||
![]() |
3c787371b3 | ||
![]() |
002ec2175b | ||
ac2732d116 | |||
b1fd013d92 | |||
782f3d8b5f | |||
e04d00d3f4 | |||
5db52db1a3 | |||
18ba72e91b | |||
8cd7b6e583 | |||
da248460da | |||
74d08a7d5b | |||
c29d2e92d2 | |||
b32846ea5e | |||
c1baf3e1d4 | |||
05c30200c3 | |||
043e378eab | |||
7a831555f4 | |||
6a6120ca14 | |||
e99b18519d | |||
db6bb2b0f6 | |||
351499aef6 | |||
31aba3b13c | |||
e7fff4bb87 | |||
8ed90c2ddc | |||
e71f8e2acf | |||
178d0e6d1d | |||
7c72b2d90f | |||
871b1dcbed | |||
2e6d111b69 | |||
5a1e0f6aa8 | |||
ffedd5b88b | |||
![]() |
58215d4e01 | ||
![]() |
44c1c2b476 | ||
![]() |
de4beba3db | ||
![]() |
8fb83bd391 | ||
55e46e1afa | |||
ba745ccc0c | |||
6b8dce2eff | |||
e4fb780016 | |||
![]() |
52577425a6 | ||
![]() |
da81f34e07 | ||
![]() |
89abfac512 | ||
![]() |
e03614fb72 | ||
![]() |
cced11f6c4 | ||
![]() |
21c3732ad3 | ||
![]() |
5d267a4865 | ||
![]() |
d6f7c78eb1 | ||
![]() |
e486be5ba8 | ||
![]() |
980adacc2c | ||
![]() |
a737ee94d1 | ||
![]() |
cedfe9cef7 | ||
![]() |
000bc3aaa5 | ||
31574751ff | |||
c841123ac0 | |||
50d88fc970 | |||
c63d91facc | |||
a117e8a618 | |||
4b6de3575a | |||
35a4cd47e4 | |||
afdbb05bc5 | |||
a3dd83e63c |
52 changed files with 3407 additions and 2778 deletions
32
.docker-test
Normal file
32
.docker-test
Normal file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/bash
|
||||
|
||||
ruby_version=${1:-2.4.2}
|
||||
|
||||
if ! rvm use ruby-${ruby_version} &>/dev/null ; then
|
||||
echo "The ruby version '${ruby_version}' doesn't exist!"
|
||||
echo "Available versions are:"
|
||||
rvm list rubies strings | cut -d '-' -f2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo '# ---------------------------------'
|
||||
echo "# Use ruby version: ${ruby_version}"
|
||||
echo '# ---------------------------------'
|
||||
|
||||
cp -r /mpw ~/mpw
|
||||
cd ~/mpw
|
||||
gem install bundler --no-ri --no-rdoc
|
||||
bundle install
|
||||
gem build mpw.gemspec
|
||||
gem install mpw-$(cat VERSION).gem
|
||||
cp -a /dev/urandom /dev/random
|
||||
|
||||
rubocop
|
||||
ruby ./test/init.rb
|
||||
ruby ./test/test_config.rb
|
||||
ruby ./test/test_item.rb
|
||||
ruby ./test/test_mpw.rb
|
||||
ruby ./test/test_translate.rb
|
||||
ruby ./test/init.rb
|
||||
ruby ./test/test_cli.rb
|
||||
ruby ./test/test_import.rb
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,4 @@
|
|||
Gemfile.lock
|
||||
*.gem
|
||||
.yardoc
|
||||
doc
|
||||
|
|
38
.rubocop.yml
Normal file
38
.rubocop.yml
Normal file
|
@ -0,0 +1,38 @@
|
|||
|
||||
AllCops:
|
||||
Exclude:
|
||||
- db/**/*
|
||||
- config/**/*
|
||||
- Vagrantfile
|
||||
TargetRubyVersion: 2.3
|
||||
|
||||
Naming/AccessorMethodName:
|
||||
Enabled: false
|
||||
|
||||
Lint/RescueWithoutErrorClass:
|
||||
Enabled: false
|
||||
|
||||
Metrics/LineLength:
|
||||
Max: 120
|
||||
Metrics/CyclomaticComplexity:
|
||||
Enabled: false
|
||||
Metrics/PerceivedComplexity:
|
||||
Enabled: false
|
||||
Metrics/MethodLength:
|
||||
Enabled: false
|
||||
Metrics/BlockLength:
|
||||
Enabled: false
|
||||
Metrics/ClassLength:
|
||||
Enabled: false
|
||||
Metrics/AbcSize:
|
||||
Enabled: false
|
||||
|
||||
Style/NumericLiteralPrefix:
|
||||
Enabled: false
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
Style/CommandLiteral:
|
||||
Enabled: true
|
||||
EnforcedStyle: percent_x
|
||||
Style/Documentation:
|
||||
Enabled: false
|
23
.travis.yml
23
.travis.yml
|
@ -1,14 +1,23 @@
|
|||
language: ruby
|
||||
dist: precise
|
||||
rvm:
|
||||
- 2.4.0
|
||||
- 2.3.3
|
||||
- 2.2.6
|
||||
- 2.4.2
|
||||
- 2.3.5
|
||||
- 2.2.8
|
||||
- 2.1.10
|
||||
install:
|
||||
- sudo cp -a /dev/urandom /dev/random
|
||||
- sudo apt-get purge -y gnupg-agent gnupg2
|
||||
- bundle install
|
||||
- gem install 'test-unit'
|
||||
- echo 9999 > VERSION
|
||||
- gem build mpw.gemspec
|
||||
- gem install mpw-9999.gem
|
||||
- gem install mpw-$(cat VERSION).gem
|
||||
script:
|
||||
- ruby ./test/tests.rb
|
||||
- rubocop
|
||||
- ruby ./test/init.rb
|
||||
- ruby ./test/test_config.rb
|
||||
- ruby ./test/test_item.rb
|
||||
- ruby ./test/test_mpw.rb
|
||||
- ruby ./test/test_translate.rb
|
||||
- ruby ./test/init.rb
|
||||
- ruby ./test/test_cli.rb
|
||||
- ruby ./test/test_import.rb
|
||||
|
|
49
CHANGELOG
49
CHANGELOG
|
@ -1,49 +0,0 @@
|
|||
= CHANGELOG =
|
||||
== v4.0.0-beta1 ==
|
||||
|
||||
* add manage share key with new interface
|
||||
|
||||
== v4.0.0-beta ==
|
||||
|
||||
* new interface with a table
|
||||
* new command line interface
|
||||
* use text editor for add or update an item
|
||||
* fix generate gpg key with RSA
|
||||
* several bugs fix
|
||||
* add unit tests
|
||||
|
||||
== v3.2.1 ==
|
||||
|
||||
* fix bug when add a new item
|
||||
|
||||
== v3.2.0 ==
|
||||
|
||||
* add support OTP
|
||||
* fix bug in synchronize
|
||||
* improve interface
|
||||
|
||||
== v3.1.0 ==
|
||||
|
||||
* add clipboard
|
||||
* can change gpg version
|
||||
* minor change in interface
|
||||
* several bugs fix
|
||||
|
||||
== v3.0.0 ==
|
||||
|
||||
* new storage format
|
||||
* new share system
|
||||
* remove MPW server
|
||||
|
||||
== v2.0.0 ==
|
||||
|
||||
* change format csv to yaml
|
||||
* easy install with gem
|
||||
* add sync with ftp and ssh
|
||||
* many improvement
|
||||
|
||||
== v1.1.0 ==
|
||||
|
||||
* Add sync with MPW Server
|
||||
* Add MPW Server
|
||||
* Fix minors bugs
|
98
CHANGELOG.md
Normal file
98
CHANGELOG.md
Normal file
|
@ -0,0 +1,98 @@
|
|||
# CHANGELOG
|
||||
## v4.2.2 (2017-08-15)
|
||||
|
||||
* minor improvements in the interface
|
||||
|
||||
## v4.2.1 (2017-07-30)
|
||||
|
||||
* fix bug in otp generator
|
||||
|
||||
## v4.2.0 (2017-06-06)
|
||||
|
||||
* feat: improve the interface
|
||||
* feat: add copy url
|
||||
* feat: add unit tests for cli
|
||||
* feat: comment the code with yarn syntax
|
||||
* fix several bugs
|
||||
* fix translations
|
||||
|
||||
## v4.1.1 (2017-05-03)
|
||||
|
||||
* fix bug in init
|
||||
|
||||
## v4.1.0 (2017-04-22)
|
||||
|
||||
* feat: add options to update or add an item in command line
|
||||
* feat: print config
|
||||
* feat: add a specific path for a wallet
|
||||
* feat: add rubocop to fix syntax
|
||||
* fix: pinentry mode with gpg >= 2.1
|
||||
* remove SSH and FTP synchronization
|
||||
|
||||
## v4.0.0 (2017-03-09)
|
||||
|
||||
* feature: set default wallet
|
||||
* add option for generate a random password when you update an item
|
||||
* fix encryption when you share an existing wallet
|
||||
* several bugs fix
|
||||
|
||||
## v4.0.0-beta1 (2017-02-16)
|
||||
|
||||
* add manage share key with new interface
|
||||
|
||||
## v4.0.0-beta (2016-11-11)
|
||||
|
||||
* new interface with a table
|
||||
* new command line interface
|
||||
* use text editor for add or update an item
|
||||
* fix generate gpg key with RSA
|
||||
* several bugs fix
|
||||
* add unit tests
|
||||
|
||||
## v3.2.1 (2016-08-06)
|
||||
|
||||
* fix bug when add a new item
|
||||
|
||||
## v3.2.0 (2016-08-03)
|
||||
|
||||
* add support OTP
|
||||
* fix bug in synchronize
|
||||
* improve interface
|
||||
|
||||
## v3.1.0 (2016-07-09)
|
||||
|
||||
* add clipboard
|
||||
* can change gpg version
|
||||
* minor change in interface
|
||||
* several bugs fix
|
||||
|
||||
## v3.0.0 (2016-07-05)
|
||||
|
||||
* new storage format
|
||||
* new share system
|
||||
* remove MPW server
|
||||
|
||||
## v2.0.3 (2015-09-27)
|
||||
|
||||
* add no-sync option
|
||||
|
||||
## v2.0.1 (2015-06-23)
|
||||
|
||||
* fix mpw-ssh
|
||||
|
||||
## v2.0.0 (2015-06-22)
|
||||
|
||||
* change format csv to yaml
|
||||
* easy install with gem
|
||||
* add sync with ftp and ssh
|
||||
* many improvement
|
||||
|
||||
## v1.1.0 (2014-01-28)
|
||||
|
||||
* Add sync with MPW Server
|
||||
* Add MPW Server
|
||||
* Fix minors bugs
|
||||
|
||||
## v1.0.0 (2014-01-15)
|
||||
|
||||
* first release
|
16
Dockerfile
Normal file
16
Dockerfile
Normal file
|
@ -0,0 +1,16 @@
|
|||
FROM debian:stretch
|
||||
MAINTAINER Adrien Waksberg "mpw@yae.im"
|
||||
|
||||
RUN apt update
|
||||
RUN apt dist-upgrade -y
|
||||
|
||||
RUN apt install -y procps gnupg1 curl git
|
||||
RUN ln -snvf /usr/bin/gpg1 /usr/bin/gpg
|
||||
RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
|
||||
RUN curl -sSL https://get.rvm.io | bash -s stable
|
||||
RUN echo 'source "/usr/local/rvm/scripts/rvm"' >> /etc/bash.bashrc
|
||||
|
||||
RUN /bin/bash -l -c "rvm install 2.4.2"
|
||||
RUN /bin/bash -l -c "rvm install 2.3.5"
|
||||
RUN /bin/bash -l -c "rvm install 2.2.8"
|
||||
RUN /bin/bash -l -c "rvm install 2.1.10"
|
22
Gemfile
22
Gemfile
|
@ -1,10 +1,14 @@
|
|||
source 'https://rubygems.org'
|
||||
gem "i18n", "~> 0.7", ">= 0.7.0"
|
||||
gem "gpgme", "~> 2.0", ">= 2.0.12"
|
||||
gem "highline", "~> 1.7", ">= 1.7.8"
|
||||
gem "locale", "~> 2.1", ">= 2.1.2"
|
||||
gem "colorize", "~> 0.8", ">= 0.8.1"
|
||||
gem "net-ssh", "~> 3.2", ">= 3.2.0"
|
||||
gem "net-sftp", "~> 2.1", ">= 2.1.2"
|
||||
gem "clipboard", "~> 1.1", ">= 1.1.1"
|
||||
gem "rotp", "~> 3.1", ">= 3.1.0"
|
||||
gem 'clipboard', '~> 1.1', '>= 1.1.1'
|
||||
gem 'colorize', '~> 0.8', '>= 0.8.1'
|
||||
gem 'gpgme', '~> 2.0', '>= 2.0.14'
|
||||
gem 'highline', '~> 1.7', '>= 1.7.8'
|
||||
gem 'i18n', '~> 0.9', '>= 0.9.1'
|
||||
gem 'locale', '~> 2.1', '>= 2.1.2'
|
||||
gem 'rotp', '~> 3.3', '>= 3.3.0'
|
||||
|
||||
group :development do
|
||||
gem 'rubocop', '0.50.0'
|
||||
gem 'test-unit'
|
||||
gem 'yard'
|
||||
end
|
||||
|
|
476
LICENSE
476
LICENSE
|
@ -1,339 +1,201 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
Preamble
|
||||
1. Definitions.
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
Copyright 2017 Adrien Waksberg
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
195
README.md
Normal file
195
README.md
Normal file
|
@ -0,0 +1,195 @@
|
|||
# MPW: Manage your passwords!
|
||||
[](https://github.com/nishiki/manage-password/releases)
|
||||
[](https://travis-ci.org/nishiki/manage-password)
|
||||
[](https://github.com/nishiki/manage-password/blob/master/LICENSE)
|
||||
|
||||
mpw is a little software which stores your passwords in [GnuPG](http://www.gnupg.org/) encrypted files.
|
||||
|
||||
## Features
|
||||
|
||||
* generate random password
|
||||
* generate OTP code
|
||||
* copy your login, password or otp in clipboard
|
||||
* manage many wallets
|
||||
* share a wallet with others GPG keys
|
||||
|
||||
## Install
|
||||
|
||||
On debian or ubuntu:
|
||||
```
|
||||
apt install ruby ruby-dev xclip
|
||||
gem install mpw
|
||||
```
|
||||
|
||||
## How to use
|
||||
### First steps
|
||||
|
||||
Initialize your first wallet:
|
||||
```
|
||||
mpw config --init user@host.com
|
||||
```
|
||||
|
||||
Add your first item:
|
||||
```
|
||||
mpw add --host assurance.com --port 443 --user user_2132 --protocol https --random
|
||||
mpw add --host fric.com --user 230403 --otp-code 23434113 --protocol https --comment 'I love my bank' --random
|
||||
```
|
||||
|
||||
And list your items:
|
||||
```
|
||||
mpw list
|
||||
```
|
||||
or search an item with
|
||||
```
|
||||
mpw list --pattern love
|
||||
mpw list --group bank
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Assurance
|
||||
==========================================================================
|
||||
ID | Host | User | OTP | Comment
|
||||
==========================================================================
|
||||
1 | https://assurance.com:443 | user_2132 | |
|
||||
|
||||
Bank
|
||||
==========================================================================
|
||||
ID | Host | User | OTP | Comment
|
||||
==========================================================================
|
||||
3 | https://fric.com | 230403 | X | I love my bank
|
||||
```
|
||||
|
||||
Copy a password, login or OTP code:
|
||||
```
|
||||
mpw copy -p assurance.com
|
||||
```
|
||||
|
||||
Update an item:
|
||||
```
|
||||
mpw update -p assurance.com
|
||||
```
|
||||
|
||||
Delete an item:
|
||||
```
|
||||
mpw delete -p assurance.com
|
||||
```
|
||||
|
||||
### Manage wallets
|
||||
|
||||
List all available wallets:
|
||||
```
|
||||
mpw wallet
|
||||
```
|
||||
|
||||
List all GPG keys in wallet:
|
||||
```
|
||||
mpw wallet --list-keys [--wallet NAME]
|
||||
```
|
||||
|
||||
Share with an other GPG key:
|
||||
```
|
||||
mpw wallet --add-gpg-key test42@localhost.com
|
||||
or
|
||||
mpw wallet --add-gpg-key /path/to/file
|
||||
```
|
||||
|
||||
Remove a GPG key:
|
||||
```
|
||||
mpw wallet --delete-gpg-key test42@localhost.com
|
||||
```
|
||||
|
||||
### Export and import data
|
||||
|
||||
You can export your data in yaml file with your passwords in clear text:
|
||||
```
|
||||
mpw export --file export.yml
|
||||
```
|
||||
|
||||
Import data from an yaml file:
|
||||
```
|
||||
mpw import --file import.yml
|
||||
```
|
||||
|
||||
Example yaml file for mpw:
|
||||
|
||||
```
|
||||
---
|
||||
1:
|
||||
host: fric.com
|
||||
user: 230403
|
||||
group: Bank
|
||||
password: 5XdiTQOubRDw9B0aJoMlcEyL
|
||||
protocol: https
|
||||
port:
|
||||
otp_key: 330223432
|
||||
comment: I love my bank
|
||||
2:
|
||||
host: assurance.com
|
||||
user: user_2132
|
||||
group: Assurance
|
||||
password: DMyK6B3v4bWO52VzU7aTHIem
|
||||
protocol: https
|
||||
port: 443
|
||||
otp_key:
|
||||
comment:
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
Print the current config
|
||||
```
|
||||
mpw config
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
Configuration
|
||||
==============================================
|
||||
lang | fr
|
||||
gpg_key | mpw@yae.im
|
||||
default_wallet |
|
||||
config_dir | /home/mpw/.config/mpw
|
||||
pinmode | true
|
||||
gpg_exe |
|
||||
path_wallet_test | /tmp/test.mpw
|
||||
password_numeric | true
|
||||
password_alpha | true
|
||||
password_special | false
|
||||
password_length | 16
|
||||
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
Don't run the tests on your local machine, you risk to lost your datas.
|
||||
|
||||
### Test on local machine with docker
|
||||
|
||||
* install [docker](https://docs.docker.com/engine/installation/)
|
||||
* run the tests
|
||||
|
||||
```
|
||||
docker run -v $(pwd):/mpw:ro -it nishiki/ruby:stretch /bin/bash -l /mpw/.docker-test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
```
|
||||
* Author:: Adrien Waksberg <mpw@yae.im>
|
||||
|
||||
Copyright (c) 2013-2017 Adrien Waksberg
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
54
README.rst
54
README.rst
|
@ -1,54 +0,0 @@
|
|||
MPW: Manage your passwords!
|
||||
*******************************************************
|
||||
|Version| |Build Status| |License|
|
||||
|
||||
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
|
||||
|
||||
|
||||
.. |Version| image:: https://img.shields.io/badge/latest_version-4.0.0--beta1-yellow.svg
|
||||
:target: https://github.com/nishiki/manage-password/releases
|
||||
.. |License| image:: https://img.shields.io/badge/license-GPL--2.0-blue.svg
|
||||
:target: https://github.com/nishiki/manage-password/blob/master/LICENSE
|
||||
.. |Build Status| image:: https://travis-ci.org/nishiki/manage-password.svg?branch=master
|
||||
:target: https://travis-ci.org/nishiki/manage-password
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
4.0.0-beta1
|
||||
4.2.2
|
||||
|
|
79
bin/mpw
79
bin/mpw
|
@ -1,22 +1,25 @@
|
|||
#!/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.
|
||||
#!/usr/bin/env ruby
|
||||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
$: << File.expand_path('../../lib', __FILE__)
|
||||
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
|
||||
|
||||
require 'locale'
|
||||
require 'set'
|
||||
|
@ -30,7 +33,7 @@ require 'colorize'
|
|||
lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
|
||||
|
||||
if defined?(I18n.enforce_available_locales)
|
||||
I18n.enforce_available_locales = true
|
||||
I18n.enforce_available_locales = true
|
||||
end
|
||||
|
||||
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
||||
|
@ -45,25 +48,25 @@ I18n.locale = lang.to_sym
|
|||
bin_dir = File.dirname(__FILE__)
|
||||
command = "#{bin_dir}/mpw-#{ARGV[0]}"
|
||||
|
||||
if Dir.glob("#{bin_dir}/mpw-*").include?("#{command}")
|
||||
begin
|
||||
Kernel.load(command)
|
||||
rescue Exception => e
|
||||
puts "#{I18n.t('display.error')}: #{e}".red
|
||||
end
|
||||
if Dir.glob("#{bin_dir}/mpw-*").include?(command.to_s)
|
||||
begin
|
||||
Kernel.load(command)
|
||||
rescue OptionParser::ParseError => e
|
||||
puts "#{I18n.t('display.error')}: #{e}".red
|
||||
end
|
||||
else
|
||||
puts "#{I18n.t('option.usage')}: mpw COMMAND [options]\n\n"
|
||||
puts 'Commands:'
|
||||
puts " add #{I18n.t('command.add')}"
|
||||
puts " config #{I18n.t('command.config')}"
|
||||
puts " copy #{I18n.t('command.copy')}"
|
||||
puts " delete #{I18n.t('command.delete')}"
|
||||
puts " export #{I18n.t('command.export')}"
|
||||
puts " genpwd #{I18n.t('command.genpwd')}"
|
||||
puts " import #{I18n.t('command.import')}"
|
||||
puts " list #{I18n.t('command.list')}"
|
||||
puts " update #{I18n.t('command.update')}"
|
||||
puts " wallet #{I18n.t('command.wallet')}"
|
||||
puts "#{I18n.t('option.usage')}: mpw COMMAND [options]\n\n"
|
||||
puts 'Commands:'
|
||||
puts " add #{I18n.t('command.add')}"
|
||||
puts " config #{I18n.t('command.config')}"
|
||||
puts " copy #{I18n.t('command.copy')}"
|
||||
puts " delete #{I18n.t('command.delete')}"
|
||||
puts " export #{I18n.t('command.export')}"
|
||||
puts " genpwd #{I18n.t('command.genpwd')}"
|
||||
puts " import #{I18n.t('command.import')}"
|
||||
puts " list #{I18n.t('command.list')}"
|
||||
puts " update #{I18n.t('command.update')}"
|
||||
puts " wallet #{I18n.t('command.wallet')}"
|
||||
|
||||
exit 3
|
||||
exit 3
|
||||
end
|
||||
|
|
99
bin/mpw-add
99
bin/mpw-add
|
@ -1,20 +1,22 @@
|
|||
#!/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.
|
||||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/config'
|
||||
|
@ -24,38 +26,59 @@ require 'mpw/cli'
|
|||
# Options
|
||||
# --------------------------------------------------------- #
|
||||
|
||||
options = {}
|
||||
options[:sync] = true
|
||||
values = {}
|
||||
options = {}
|
||||
options[:text_editor] = true
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw add [options]"
|
||||
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('-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('-C', '--comment COMMENT', I18n.t('option.comment')) do |comment|
|
||||
values[:comment] = comment
|
||||
end
|
||||
|
||||
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
|
||||
options[:sync] = false
|
||||
end
|
||||
opts.on('-G', '--group NAME', I18n.t('option.new_group')) do |group|
|
||||
values[:group] = group
|
||||
end
|
||||
|
||||
opts.on('-r', '--random', I18n.t('option.random_password')) do
|
||||
options[:password] = true
|
||||
end
|
||||
opts.on('-h', '--help', I18n.t('option.help')) do
|
||||
puts opts
|
||||
exit 0
|
||||
end
|
||||
|
||||
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
|
||||
options[:wallet] = wallet
|
||||
end
|
||||
opts.on('-o', '--otp-code CODE', I18n.t('option.otp_code')) do |otp|
|
||||
values[:otp_key] = otp
|
||||
end
|
||||
|
||||
opts.on('-r', '--random', I18n.t('option.random_password')) do
|
||||
options[:password] = true
|
||||
end
|
||||
|
||||
opts.on('-t', '--text-editor', I18n.t('option.text_editor')) do
|
||||
options[:text_editor] = true
|
||||
end
|
||||
|
||||
opts.on('-u', '--url URL', I18n.t('option.url')) do |url|
|
||||
values[:url] = url
|
||||
end
|
||||
|
||||
opts.on('-U', '--user USER', 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 = MPW::Cli.new(config)
|
||||
|
||||
cli.load_config
|
||||
cli.get_wallet(options[:wallet])
|
||||
cli.decrypt
|
||||
cli.add(options[:password])
|
||||
cli.add(options[:password], options[:text_editor], values)
|
||||
|
|
144
bin/mpw-config
144
bin/mpw-config
|
@ -1,20 +1,22 @@
|
|||
#!/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.
|
||||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/config'
|
||||
|
@ -28,48 +30,92 @@ options = {}
|
|||
values = {}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw config [options]"
|
||||
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('-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('-d', '--default-wallet NAME', I18n.t('option.default_wallet')) do |default_wallet|
|
||||
values[:default_wallet] = default_wallet
|
||||
end
|
||||
|
||||
opts.on('-h', '--help', I18n.t('option.help')) do
|
||||
puts opts
|
||||
exit 0
|
||||
end
|
||||
opts.on('-g', '--gpg-exe PATH', I18n.t('option.gpg_exe')) do |gpg_exe|
|
||||
values[:gpg_exe] = gpg_exe
|
||||
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('-h', '--help', I18n.t('option.help')) do
|
||||
puts opts
|
||||
exit 0
|
||||
end
|
||||
|
||||
opts.on('-k', '--key GPG_KEY', I18n.t('option.gpg_key')) do |gpg_key|
|
||||
values[:gpg_key] = gpg_key
|
||||
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('-l', '--lang LANG', I18n.t('option.lang')) do |lang|
|
||||
values[:lang] = lang
|
||||
end
|
||||
opts.on('-k', '--key GPG_KEY', I18n.t('option.gpg_key')) do |gpg_key|
|
||||
values[:gpg_key] = gpg_key
|
||||
end
|
||||
|
||||
opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
|
||||
values[:wallet_dir] = wallet_dir
|
||||
end
|
||||
opts.on('-L', '--lang LANG', I18n.t('option.lang')) do |lang|
|
||||
values[:lang] = lang
|
||||
end
|
||||
|
||||
opts.on('-P', '--enable-pinmode', I18n.t('option.pinmode')) do
|
||||
values[:pinmode] = true
|
||||
end
|
||||
|
||||
opts.on('-p', '--disable-pinmode', I18n.t('option.disable_pinmode')) do
|
||||
values[:pinmode] = false
|
||||
end
|
||||
|
||||
opts.on('-w', '--wallet-dir PATH', I18n.t('option.wallet_dir')) do |wallet_dir|
|
||||
values[:wallet_dir] = wallet_dir
|
||||
end
|
||||
|
||||
opts.on('-l', '--length NUMBER', I18n.t('option.length')) do |length|
|
||||
values[:pwd_length] = length.to_i
|
||||
end
|
||||
|
||||
opts.on('-n', '--numeric', I18n.t('option.numeric')) do
|
||||
values[:pwd_numeric] = true
|
||||
end
|
||||
|
||||
opts.on('-N', '--disable-numeric', I18n.t('option.disable_numeric')) do
|
||||
values[:pwd_numeric] = false
|
||||
end
|
||||
|
||||
opts.on('-s', '--special-chars', I18n.t('option.special_chars')) do
|
||||
values[:pwd_special] = true
|
||||
end
|
||||
|
||||
opts.on('-S', '--disable-special-chars', I18n.t('option.special_chars')) do
|
||||
values[:pwd_special] = false
|
||||
end
|
||||
|
||||
opts.on('-a', '--alpha', I18n.t('option.alpha')) do
|
||||
values[:pwd_alpha] = true
|
||||
end
|
||||
|
||||
opts.on('-A', '--disable-alpha', I18n.t('option.disable_alpha')) do
|
||||
values[:pwd_alpha] = false
|
||||
end
|
||||
end.parse!
|
||||
|
||||
config = MPW::Config.new(options[:config])
|
||||
cli = MPW::Cli.new(config, nil)
|
||||
cli = MPW::Cli.new(config)
|
||||
|
||||
if not options[:init].nil?
|
||||
cli.setup(values)
|
||||
cli.load_config
|
||||
cli.get_wallet
|
||||
cli.setup_gpg_key(values[:gpg_key])
|
||||
cli.setup_wallet_config
|
||||
if options.key?(:init)
|
||||
cli.setup(values)
|
||||
cli.load_config
|
||||
cli.get_wallet
|
||||
cli.setup_gpg_key(values[:gpg_key])
|
||||
else
|
||||
cli.set_config(values)
|
||||
cli.load_config
|
||||
if values.empty?
|
||||
cli.list_config
|
||||
else
|
||||
cli.set_config(values)
|
||||
end
|
||||
end
|
||||
|
|
83
bin/mpw-copy
83
bin/mpw-copy
|
@ -1,20 +1,22 @@
|
|||
#!/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.
|
||||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/config'
|
||||
|
@ -25,45 +27,40 @@ require 'mpw/cli'
|
|||
# --------------------------------------------------------- #
|
||||
|
||||
options = {}
|
||||
options[:sync] = true
|
||||
options[:clipboard] = true
|
||||
values = {}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw copy [options]"
|
||||
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('-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('-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('-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('-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('-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
|
||||
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 = MPW::Cli.new(config)
|
||||
|
||||
cli.load_config
|
||||
cli.get_wallet(options[:wallet])
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
#!/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.
|
||||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/config'
|
||||
|
@ -24,41 +26,36 @@ require 'mpw/cli'
|
|||
# Options
|
||||
# --------------------------------------------------------- #
|
||||
|
||||
options = {}
|
||||
options[:sync] = true
|
||||
values = {}
|
||||
options = {}
|
||||
values = {}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw delete [options]"
|
||||
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('-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('-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('-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('-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
|
||||
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 = MPW::Cli.new(config)
|
||||
|
||||
cli.load_config
|
||||
cli.get_wallet(options[:wallet])
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
#!/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.
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/config'
|
||||
|
@ -24,45 +23,40 @@ require 'mpw/cli'
|
|||
# Options
|
||||
# --------------------------------------------------------- #
|
||||
|
||||
options = {}
|
||||
options[:sync] = true
|
||||
values = {}
|
||||
options = {}
|
||||
values = {}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
|
||||
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('-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('-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('-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('-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('-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
|
||||
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 = MPW::Cli.new(config)
|
||||
|
||||
cli.load_config
|
||||
cli.get_wallet(options[:wallet])
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
#!/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.
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/mpw'
|
||||
|
@ -22,29 +21,29 @@ require 'mpw/mpw'
|
|||
options = {}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw passwd [options]"
|
||||
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('-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('-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('-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('-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
|
||||
opts.on('-a', '--alpha', I18n.t('option.alpha')) do
|
||||
options[:alpha] = true
|
||||
end
|
||||
end.parse!
|
||||
|
||||
puts MPW::MPW::password(options)
|
||||
puts MPW::MPW.password(options)
|
||||
exit 0
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
#!/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.
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/config'
|
||||
|
@ -24,38 +23,43 @@ require 'mpw/cli'
|
|||
# Options
|
||||
# --------------------------------------------------------- #
|
||||
|
||||
options = {}
|
||||
options[:sync] = true
|
||||
formats =
|
||||
Dir["#{File.expand_path('../../lib/mpw/import', __FILE__)}/*.rb"]
|
||||
.map { |v| File.basename(v, '.rb') }
|
||||
.join(', ')
|
||||
options = {
|
||||
format: 'mpw'
|
||||
}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw import [options]"
|
||||
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('-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('-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('-F', '--format STRING', I18n.t('option.file_format', formats: formats)) do |format|
|
||||
options[:format] = format
|
||||
end
|
||||
|
||||
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
|
||||
options[:sync] = false
|
||||
end
|
||||
opts.on('-h', '--help', I18n.t('option.help')) do
|
||||
puts opts
|
||||
exit 0
|
||||
end
|
||||
|
||||
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
|
||||
options[:wallet] = wallet
|
||||
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 = MPW::Cli.new(config)
|
||||
|
||||
cli.load_config
|
||||
cli.get_wallet(options[:wallet])
|
||||
cli.decrypt
|
||||
cli.import(options[:file])
|
||||
cli.import(options[:file], options[:format])
|
||||
|
|
78
bin/mpw-list
78
bin/mpw-list
|
@ -1,20 +1,19 @@
|
|||
#!/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.
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/config'
|
||||
|
@ -24,41 +23,36 @@ require 'mpw/cli'
|
|||
# Options
|
||||
# --------------------------------------------------------- #
|
||||
|
||||
options = {}
|
||||
options[:sync] = true
|
||||
values = {}
|
||||
options = {}
|
||||
values = {}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw list [options]"
|
||||
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('-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('-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('-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('-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
|
||||
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 = MPW::Cli.new(config)
|
||||
|
||||
cli.load_config
|
||||
cli.get_wallet(options[:wallet])
|
||||
|
|
110
bin/mpw-update
110
bin/mpw-update
|
@ -1,20 +1,19 @@
|
|||
#!/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.
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/config'
|
||||
|
@ -24,43 +23,70 @@ require 'mpw/cli'
|
|||
# Options
|
||||
# --------------------------------------------------------- #
|
||||
|
||||
options = {}
|
||||
options[:sync] = true
|
||||
values = {}
|
||||
values = {}
|
||||
search = {}
|
||||
options = {}
|
||||
options[:text_editor] = false
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw update [options]"
|
||||
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('-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('-C', '--comment COMMENT', I18n.t('option.comment')) do |comment|
|
||||
values[:comment] = comment
|
||||
end
|
||||
|
||||
opts.on('-h', '--help', I18n.t('option.help')) do
|
||||
puts opts
|
||||
exit 0
|
||||
end
|
||||
opts.on('-g', '--group NAME', I18n.t('option.group')) do |group|
|
||||
search[:group] = group
|
||||
end
|
||||
|
||||
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
|
||||
options[:sync] = false
|
||||
end
|
||||
opts.on('-G', '--new-group NAME', I18n.t('option.new_group')) do |group|
|
||||
values[:group] = group
|
||||
end
|
||||
|
||||
opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
|
||||
values[:pattern] = pattern
|
||||
end
|
||||
opts.on('-h', '--help', I18n.t('option.help')) do
|
||||
puts opts
|
||||
exit 0
|
||||
end
|
||||
|
||||
opts.on('-w', '--wallet NAME', I18n.t('option.wallet')) do |wallet|
|
||||
options[:wallet] = wallet
|
||||
end
|
||||
opts.on('-o', '--otp-code CODE', I18n.t('option.otp_code')) do |otp|
|
||||
values[:otp_key] = otp
|
||||
end
|
||||
|
||||
opts.on('-p', '--pattern PATTERN', I18n.t('option.pattern')) do |pattern|
|
||||
search[:pattern] = pattern
|
||||
end
|
||||
|
||||
opts.on('-r', '--random', I18n.t('option.random_password')) do
|
||||
options[:password] = true
|
||||
end
|
||||
|
||||
opts.on('-t', '--text-editor', I18n.t('option.text_editor')) do
|
||||
options[:text_editor] = true
|
||||
end
|
||||
|
||||
opts.on('-u', '--url URL', I18n.t('option.url')) do |url|
|
||||
values[:url] = url
|
||||
end
|
||||
|
||||
opts.on('-U', '--user USER', 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 = MPW::Cli.new(config)
|
||||
|
||||
options[:text_editor] = true if values.empty?
|
||||
|
||||
cli.load_config
|
||||
cli.get_wallet(options[:wallet])
|
||||
cli.decrypt
|
||||
cli.update(values)
|
||||
cli.update(options[:password], options[:text_editor], search, values)
|
||||
|
|
146
bin/mpw-wallet
146
bin/mpw-wallet
|
@ -1,20 +1,19 @@
|
|||
#!/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.
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'optparse'
|
||||
require 'mpw/config'
|
||||
|
@ -25,92 +24,67 @@ require 'mpw/cli'
|
|||
# --------------------------------------------------------- #
|
||||
|
||||
options = {}
|
||||
options[:sync] = {}
|
||||
options[:delete] = false
|
||||
values = {}
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
|
||||
opts.banner = "#{I18n.t('option.usage')}: mpw wallet [options]"
|
||||
|
||||
opts.on('-a', '--add-gpg-key NAME', I18n.t('option.add_gpg_key')) do |gpg_key|
|
||||
options[:gpg_key] = gpg_key
|
||||
end
|
||||
opts.on('-a', '--add-gpg-key NAME', I18n.t('option.add_gpg_key')) do |gpg_key|
|
||||
options[:gpg_key] = gpg_key
|
||||
end
|
||||
|
||||
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
|
||||
options[:config] = config
|
||||
end
|
||||
opts.on('-c', '--config PATH', I18n.t('option.config')) do |config|
|
||||
options[:config] = config
|
||||
end
|
||||
|
||||
opts.on('-d', '--delete-gpg-key NAME', I18n.t('option.delete_gpg_key')) do |gpg_key|
|
||||
options[:gpg_key] = gpg_key
|
||||
options[:delete] = true
|
||||
end
|
||||
opts.on('-d', '--delete-gpg-key NAME', I18n.t('option.delete_gpg_key')) do |gpg_key|
|
||||
options[:gpg_key] = gpg_key
|
||||
options[:delete] = true
|
||||
end
|
||||
|
||||
opts.on('-h', '--help', I18n.t('option.help')) do
|
||||
puts opts
|
||||
exit 0
|
||||
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
|
||||
options[:list] = true
|
||||
end
|
||||
|
||||
opts.on('-l', '--list', I18n.t('option.list')) do
|
||||
options[:list] = true
|
||||
end
|
||||
opts.on('-L', '--list-keys', I18n.t('option.list_keys')) do
|
||||
options[:list_keys] = true
|
||||
end
|
||||
|
||||
opts.on('-L', '--list-keys', I18n.t('option.list_keys')) do
|
||||
options[:list_keys] = true
|
||||
end
|
||||
opts.on('-p', '--path PATH', I18n.t('option.path')) do |path|
|
||||
options[:path] = path
|
||||
end
|
||||
|
||||
opts.on('-n', '--no-sync', I18n.t('option.no_sync')) do
|
||||
options[:sync] = false
|
||||
end
|
||||
opts.on('-P', '--default-path', I18n.t('option.default_path')) do
|
||||
options[:path] = 'default'
|
||||
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
|
||||
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 = MPW::Cli.new(config)
|
||||
|
||||
cli.load_config
|
||||
|
||||
if not options[:list].nil?
|
||||
cli.list_wallet
|
||||
else
|
||||
cli.get_wallet(options[:wallet])
|
||||
cli.decrypt
|
||||
if options.key?(:path)
|
||||
cli.get_wallet(options[:wallet])
|
||||
cli.set_wallet_path(options[:path])
|
||||
elsif options.key?(:list_keys) || options.key?(:gpg_key)
|
||||
cli.get_wallet(options[:wallet])
|
||||
cli.decrypt
|
||||
|
||||
if not options[:list_keys].nil?
|
||||
cli.list_keys
|
||||
elsif not options[:gpg_key].nil?
|
||||
if options[:delete]
|
||||
cli.delete_key(options[:gpg_key])
|
||||
else
|
||||
cli.add_key(options[:gpg_key])
|
||||
end
|
||||
else
|
||||
cli.setup_wallet_config(values)
|
||||
end
|
||||
if options.key?(:list_keys)
|
||||
cli.list_keys
|
||||
elsif options.key?(:gpg_key)
|
||||
options[:delete] ? cli.delete_key(options[:gpg_key]) : cli.add_key(options[:gpg_key])
|
||||
end
|
||||
else
|
||||
cli.list_wallet
|
||||
end
|
||||
|
|
140
i18n/en.yml
140
i18n/en.yml
|
@ -5,36 +5,27 @@ en:
|
|||
config:
|
||||
write: "Can't write the config file!"
|
||||
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}!"
|
||||
key_bad_format: "The key string isn't in the right format!"
|
||||
no_key_public: "You haven't entered 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!"
|
||||
empty: "The class is void"
|
||||
empty: "The class is empty"
|
||||
export: "Can't export, unable to write in %{file}!"
|
||||
export_key: "Can't export the GPG key"
|
||||
gpg_file:
|
||||
decrypt: "Can't decrypt file!"
|
||||
encrypt: "Can't encrypt the GPG file!"
|
||||
mpw_file:
|
||||
read_data: "Can't to read the MPW file!"
|
||||
write_data: "Can't to write the MPW file!"
|
||||
read_data: "Can't read the MPW file!"
|
||||
write_data: "Can't write the MPW file!"
|
||||
import: "Can't import, unable to read %{file}!"
|
||||
update:
|
||||
host_empty: "You must define a host!"
|
||||
sync:
|
||||
general: "An error has appeared during the sync"
|
||||
connection: "Connection fail!"
|
||||
communication: "A communication problem with the server is appeared!"
|
||||
download: "Can't download the file!"
|
||||
not_authorized: "You haven't the access to remote file!"
|
||||
upload: "Can't upload the file on the server!"
|
||||
unknown: "An unknown error is occured!"
|
||||
unknown_type: "The sync type is unknown"
|
||||
host_and_comment_empty: "You must define a host or a comment!"
|
||||
|
||||
warning:
|
||||
select: 'Your choice is not a valid element!'
|
||||
select: 'Your choice is not a valid item!'
|
||||
|
||||
command:
|
||||
add: "Add a new item"
|
||||
|
@ -50,58 +41,68 @@ en:
|
|||
|
||||
option:
|
||||
add: "Add an item or key"
|
||||
add_gpg_key: "Share the wallet with an other GPG key"
|
||||
alpha: "Use letter to generate a password"
|
||||
add_gpg_key: "Share the wallet with another GPG key"
|
||||
alpha: "Use letter to create a password"
|
||||
comment: "Specify a comment"
|
||||
config: "Specify the configuration file to use"
|
||||
clipboard: "Disable the clipboard feature"
|
||||
delete_gpg_key: "Delete the wallet's share with an other GPG key"
|
||||
default_path: "Move the wallet to the default directory"
|
||||
default_wallet: "Specify the default wallet to use"
|
||||
delete_gpg_key: "Delete wallet sharing with an other GPG key"
|
||||
disable_alpha: "Don't use letters to create a password"
|
||||
disable_numeric: "Don't use numbers to generate a password"
|
||||
disable_pinmode: "Disable the pinentry mode"
|
||||
disable_special_chars: "Don't use special char to create a password"
|
||||
export: "Export a wallet in an yaml file"
|
||||
file_export: "Specify the file where export data"
|
||||
file_export: "Specify the file to export data"
|
||||
file_format: "Format of import file (default: mpw; available: %{formats})"
|
||||
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)"
|
||||
force: "Do not ask confirmation when deleting an item"
|
||||
generate_password: "Create 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"
|
||||
host: "Specify the server for the synchronization"
|
||||
init: "Initialize mpw"
|
||||
import: "Import item since a yaml file"
|
||||
key: "Specify the key name"
|
||||
import: "Import item from an yaml file"
|
||||
key: "Define the key name"
|
||||
lang: "Set the software language"
|
||||
length: "Size of the password"
|
||||
list: "List the wallets"
|
||||
list_keys: "List the GPG keys in wallet"
|
||||
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"
|
||||
new_group: "Define a group for the item"
|
||||
numeric: "Use number to create a password"
|
||||
otp_code: "Set an otp key"
|
||||
path: "Move the wallet in new specify directory"
|
||||
pattern: "Given search pattern"
|
||||
port: "Specify the connection port"
|
||||
protocol: "Specify the protocol for the connection"
|
||||
pinmode: "Enable pinentry mode (available with gpg >= 2.1)"
|
||||
random_password: "Generate a random password"
|
||||
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"
|
||||
usage: "Usage"
|
||||
user: "Specify the user for the connection"
|
||||
special_chars: "Use special char to create a password"
|
||||
show: "Search and display the items"
|
||||
show_all: "Listing all items"
|
||||
text_editor: "Use text editor to edit the item"
|
||||
usage: "Use"
|
||||
url: "Set an url (ex: https://example.com/path)"
|
||||
user: "Set an user"
|
||||
wallet: "Specify a wallet to use"
|
||||
wallet_dir: "Set the wallets folder"
|
||||
|
||||
form:
|
||||
select: "Select the item: "
|
||||
select:
|
||||
choice: "Select the item: "
|
||||
error: "No item selected"
|
||||
add_key:
|
||||
valid: "Key has been added!"
|
||||
add_item:
|
||||
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"
|
||||
name: "Item name (mandatory)"
|
||||
group: "Group name"
|
||||
host: "Hostname or ip"
|
||||
protocol: "Connection protocol (ssh, http, ...)"
|
||||
login: "Connection ID"
|
||||
password: "Password"
|
||||
port: "Connection port"
|
||||
comment: "A comment"
|
||||
otp_key: "The OTP secret"
|
||||
valid: "Item has been added!"
|
||||
|
@ -110,9 +111,11 @@ en:
|
|||
clean: "The clipboard has been cleaned."
|
||||
login: "The login has been copied in clipboard."
|
||||
password: "The password has been copied in clipboard for 30s!"
|
||||
otp: "The OTP code has been copied for %{time}s!"
|
||||
otp: "The OTP code has been copied %{time}s!"
|
||||
url: "The URL has been copied in clipboard."
|
||||
help:
|
||||
name: "Help"
|
||||
url: "Press <u> to copy URL"
|
||||
login: "Press <l> to copy the login"
|
||||
password: "Press <p> to copy the password"
|
||||
otp_code: "Press <o> to copy the otp code"
|
||||
|
@ -120,14 +123,19 @@ en:
|
|||
delete_key:
|
||||
valid: "Key has been deleted!"
|
||||
delete_item:
|
||||
ask: "Are you sure you want to remove the item ?"
|
||||
ask: "Are you sure you want to remove this item ?"
|
||||
valid: "The item has been removed!"
|
||||
import:
|
||||
ask: "Are you sure you want to import this file %{file} ?"
|
||||
file_empty: "The import file is empty!"
|
||||
file_not_exist: "The import file doesn't exist!"
|
||||
valid: "The import is succesfull!"
|
||||
format_unknown: "The import format '%{file_format} is unknown!"
|
||||
valid: "The import is successful!"
|
||||
not_valid: "No data to import!"
|
||||
set_config:
|
||||
valid: "The config file has been edited!"
|
||||
set_wallet_path:
|
||||
valid: "The wallet has been moved!"
|
||||
setup_config:
|
||||
title: "Setup a new config file"
|
||||
lang: "Choose your language (en, fr, ...) [default=%{lang}]: "
|
||||
|
@ -135,47 +143,38 @@ en:
|
|||
gpg_exe: "Enter the executable GPG path (optional): "
|
||||
wallet_dir: "Enter the wallets's folder path [default=%{home}/wallets]: "
|
||||
valid: "The config file has been created!"
|
||||
setup_wallet:
|
||||
password: "Sync password: "
|
||||
title: "Wallet setup"
|
||||
sync_type: "Synchronization type (ssh, ftp): "
|
||||
sync_host: "Synchronization server: "
|
||||
sync_port: "Port of the synchronization server: "
|
||||
sync_user: "Username for the synchronization: "
|
||||
sync_pwd: "Password for the synchronization: "
|
||||
sync_path: "File path for the synchronization : "
|
||||
valid: "The wallet config file has been created!"
|
||||
setup_gpg_key:
|
||||
title: "Setup a GPG key"
|
||||
ask: "Do you want create your GPG key ? (Y/n)"
|
||||
no_create: "You must create manually your GPG key or relaunch the software."
|
||||
ask: "Do you want to create your GPG key ? (Y/n)"
|
||||
no_create: "You must to create manually your GPG key or relaunch the software."
|
||||
name: "Your name and lastname: "
|
||||
password: "A password for the GPG key: "
|
||||
confirm_password: "Confirm your password: "
|
||||
error_password: "Your passwords aren't identical!"
|
||||
length: "Size of the GPG key [default=2048]: "
|
||||
expire: "Expire time of the GPG key [default=0 (unlimited)]: "
|
||||
wait: "Please waiting during the GPG key generate, this process can take few minutes."
|
||||
wait: "Please wait until GPG key is created, this process can take a few minutes."
|
||||
valid: "Your GPG key has been created ;-)"
|
||||
update_item:
|
||||
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"
|
||||
name: "Item name (mandatory)"
|
||||
group: "Group name"
|
||||
host: "Hostname or ip"
|
||||
protocol: "Connection protocol (ssh, http, ...)"
|
||||
login: "Login id"
|
||||
password: "Password (leave empty if you don't want to update it)"
|
||||
port: "Connection port"
|
||||
comment: "A comment"
|
||||
otp_key: "The OTP secret (leave empty if you don't want change"
|
||||
otp_key: "Secret OTP (leave empty if you don't want to update it"
|
||||
valid: "Item has been updated!"
|
||||
export:
|
||||
valid: "The export in %{file} is succesfull!"
|
||||
valid: "The export in %{file} is successful!"
|
||||
|
||||
display:
|
||||
comment: "Comment"
|
||||
config: "Configuration"
|
||||
error: "ERROR"
|
||||
keys: "GPG keys"
|
||||
gpg_password: "GPG passphrase: "
|
||||
gpg_password: "GPG password: "
|
||||
group: "Group"
|
||||
login: "Login"
|
||||
name: "Name"
|
||||
|
@ -186,6 +185,7 @@ en:
|
|||
port: "Port"
|
||||
protocol: "Protocol"
|
||||
server: "Server"
|
||||
wallets: "Wallets"
|
||||
warning: "Warning"
|
||||
|
||||
formats:
|
||||
|
|
56
i18n/fr.yml
56
i18n/fr.yml
|
@ -22,16 +22,7 @@ fr:
|
|||
write_data: "Impossible d'écrire le fichier MPW!"
|
||||
import: "Impossible d'importer le fichier %{file}, car il n'est pas lisible!"
|
||||
update:
|
||||
host_empty: "Vous devez définir un host!"
|
||||
sync:
|
||||
general: "Une erreur est survenue durant la synchronisation"
|
||||
connection: "La connexion n'a pu être établie!"
|
||||
communication: "Un problème de communication avec le serveur est apparu!"
|
||||
download: "Impossible de télécharger le fichier!"
|
||||
not_authorized: "Vous n'avez pas les autorisations d'accès au fichier distant!"
|
||||
upload: "Impossible d'envoyer le fichier sur le serveur!"
|
||||
unknown: "Une erreur inconnue est survenue!"
|
||||
unknown_type: "Le type de synchronisation est inconnu"
|
||||
host_and_comment_empty: "Vous devez définir un host ou un commentaire!"
|
||||
|
||||
warning:
|
||||
select: "Votre choix n'est pas un élément valide!"
|
||||
|
@ -53,10 +44,18 @@ fr:
|
|||
add_gpg_key: "Partage le portefeuille avec une autre clé GPG"
|
||||
alpha: "Utilise des lettres dans la génération d'un mot de passe"
|
||||
config: "Spécifie le fichier de configuration à utiliser"
|
||||
comment: "Spécifie un commentaire"
|
||||
clipboard: "Désactive la fonction presse papier"
|
||||
default_path: "Déplace le portefeuille dans le dossier par défaut"
|
||||
default_wallet: "Spécifie le porte-feuille à utiliser par défaut"
|
||||
delete_gpg_key: "Supprime le partage le portefeuille avec une autre clé GPG"
|
||||
disable_alpha: "Désactive l'utilisation des lettres dans la génération d'un mot de passe"
|
||||
disable_numeric: "Désactive l'utilisation des chiffre dans la génération d'un mot de passe"
|
||||
disable_pinmode: "Désactive le mode pinentry"
|
||||
disable_special_chars: "Désactive l'utilisation des charactères speciaux dans la génération d'un mot de passe"
|
||||
export: "Exporte un portefeuille dans un fichier yaml"
|
||||
file_export: "Spécifie le fichier où exporter les données"
|
||||
file_format: "Format du fichier d'import (défault: mpw; disponible: %{formats})"
|
||||
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)"
|
||||
|
@ -64,7 +63,6 @@ fr:
|
|||
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"
|
||||
host: "Spécifie le serveur pour la synchronisation"
|
||||
import: "Importe des éléments depuis un fichier yaml"
|
||||
init: "Initialise mpw"
|
||||
key: "Spécifie le nom d'une clé"
|
||||
|
@ -72,26 +70,29 @@ fr:
|
|||
length: "Taille du mot de passe"
|
||||
list: "Liste les portefeuilles"
|
||||
list_keys: "Liste les clés GPG dans le portefeuille"
|
||||
no_sync: "Désactive la synchronisation avec le serveur"
|
||||
new_group: "Spécifie le groupe de l'item"
|
||||
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"
|
||||
otp_code: "Spécifie un code OTP"
|
||||
path: "Déplace le portefeuille dans un nouveau dossier"
|
||||
pattern: "Motif de donnée à chercher"
|
||||
port: "Spécifie le port de connexion"
|
||||
protocol: "Spécifie le protocol utilisé pour la connexion"
|
||||
pinmode: "Active le mode pinentry (valable avec gpg >= 2.1)"
|
||||
random_password: "Génére un mot de passe aléatoire"
|
||||
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"
|
||||
text_editor: "Active l'édition avec un éditeur de texte"
|
||||
usage: "Utilisation"
|
||||
user: "Spécifie l'identifiant de connection"
|
||||
url: "Spécifie l'url (ex: http://example.com/path)"
|
||||
user: "Spécifie un utilisateur"
|
||||
wallet: "Spécifie le portefeuille à utiliser"
|
||||
wallet_dir: "Spécifie le répertoire des portefeuilles"
|
||||
|
||||
form:
|
||||
select: "Sélectionner l'élément: "
|
||||
select:
|
||||
choice: "Sélectionner l'élément: "
|
||||
error: "Aucun élément sélectionné"
|
||||
add_key:
|
||||
valid: "La clé a bien été ajoutée!"
|
||||
add_item:
|
||||
|
@ -111,8 +112,10 @@ fr:
|
|||
login: "L'identifiant a été copié dans le presse papier"
|
||||
password: "Le mot de passe a été copié dans le presse papier pour 30s!"
|
||||
otp: "Le code OTP a été copié dans le presse papier il est valable %{time}s!"
|
||||
url: "L'URL a été copié dans le presse papier"
|
||||
help:
|
||||
name: "Aide"
|
||||
url: "Pressez <u> pour copier l'URL"
|
||||
login: "Pressez <l> pour copier l'identifiant"
|
||||
password: "Pressez <p> pour copier le mot de passe"
|
||||
otp_code: "Pressez <o> pour copier le code OTP"
|
||||
|
@ -126,8 +129,13 @@ fr:
|
|||
ask: "Êtes vous sûre de vouloir importer le fichier %{file} ?"
|
||||
file_empty: "Le fichier d'import est vide!"
|
||||
file_not_exist: "Le fichier d'import n'existe pas"
|
||||
format_unknown: "Le format d'import '%{file_format}' est inconnu!"
|
||||
valid: "L'import est un succès!"
|
||||
not_valid: "Aucune donnée à importer!"
|
||||
set_config:
|
||||
valid: "Le fichier de configuration a bien été modifié!"
|
||||
set_wallet_path:
|
||||
valid: "Le portefeuille a bien été déplacé!"
|
||||
setup_config:
|
||||
title: "Création d'un nouveau fichier de configuration"
|
||||
lang: "Choisissez votre langue (en, fr, ...) [défaut=%{lang}]: "
|
||||
|
@ -135,16 +143,6 @@ fr:
|
|||
gpg_exe: "Entrez le chemin de l'exécutable GPG (optionnel): "
|
||||
wallet_dir: "Entrez le chemin du répertoire qui contiendra les porte-feuilles de mot de passe [défaut=%{home}/wallets]: "
|
||||
valid: "Le fichier de configuration a bien été créé!"
|
||||
setup_wallet:
|
||||
password: "Mot de passe de synchronisation: "
|
||||
title: "Configuration du porte-feuille"
|
||||
sync_type: "Type de synchronisation (ssh, ftp): "
|
||||
sync_host: "Serveur: "
|
||||
sync_port: "Port: "
|
||||
sync_user: "Utilisateur: "
|
||||
sync_pwd: "Mot de passe: "
|
||||
sync_path: "Chemin du fichier: "
|
||||
valid: "Le fichier de configuration du porte-feuille a bien été créé!"
|
||||
setup_gpg_key:
|
||||
title: "Configuration d'une nouvelle clé GPG"
|
||||
ask: "Voulez vous créer votre clé GPG ? (O/n)"
|
||||
|
@ -173,6 +171,7 @@ fr:
|
|||
|
||||
display:
|
||||
comment: "Commentaire"
|
||||
config: "Configuration"
|
||||
error: "ERREUR"
|
||||
keys: "Clés GPG"
|
||||
gpg_password: "Mot de passe GPG: "
|
||||
|
@ -186,6 +185,7 @@ fr:
|
|||
port: "Port"
|
||||
protocol: "Protocol"
|
||||
server: "Serveur"
|
||||
wallets: "Porte-feuilles"
|
||||
warning: "Warning"
|
||||
|
||||
formats:
|
||||
|
|
1168
lib/mpw/cli.rb
1168
lib/mpw/cli.rb
File diff suppressed because it is too large
Load diff
|
@ -1,144 +1,190 @@
|
|||
#!/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.
|
||||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'gpgme'
|
||||
require 'yaml'
|
||||
require 'i18n'
|
||||
require 'fileutils'
|
||||
|
||||
|
||||
module MPW
|
||||
class Config
|
||||
|
||||
attr_accessor :error_msg
|
||||
class Config
|
||||
attr_accessor :error_msg
|
||||
|
||||
attr_accessor :key
|
||||
attr_accessor :lang
|
||||
attr_accessor :config_dir
|
||||
attr_accessor :wallet_dir
|
||||
attr_accessor :gpg_exe
|
||||
attr_accessor :gpg_key
|
||||
attr_accessor :lang
|
||||
attr_accessor :config_dir
|
||||
attr_accessor :default_wallet
|
||||
attr_accessor :wallet_dir
|
||||
attr_accessor :wallet_paths
|
||||
attr_accessor :gpg_exe
|
||||
attr_accessor :password
|
||||
attr_accessor :pinmode
|
||||
|
||||
# Constructor
|
||||
# @args: config_file -> the specify config file
|
||||
def initialize(config_file=nil)
|
||||
@config_file = config_file
|
||||
# @param config_file [String] path of config file
|
||||
def initialize(config_file = nil)
|
||||
@config_file = config_file
|
||||
@config_dir =
|
||||
if RUBY_PLATFORM =~ /darwin/
|
||||
"#{Dir.home}/Library/Preferences/mpw"
|
||||
elsif RUBY_PLATFORM =~ /cygwin|mswin|mingw|bccwin|wince|emx/
|
||||
"#{Dir.home}/AppData/Local/mpw"
|
||||
else
|
||||
"#{Dir.home}/.config/mpw"
|
||||
end
|
||||
|
||||
if /darwin/ =~ RUBY_PLATFORM
|
||||
@config_dir = "#{Dir.home}/Library/Preferences/mpw"
|
||||
elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
|
||||
@config_dir = "#{Dir.home}/AppData/Local/mpw"
|
||||
else
|
||||
@config_dir = "#{Dir.home}/.config/mpw"
|
||||
end
|
||||
|
||||
if @config_file.nil? or @config_file.empty?
|
||||
@config_file = "#{@config_dir}/mpw.cfg"
|
||||
end
|
||||
end
|
||||
@config_file = "#{@config_dir}/mpw.cfg" if @config_file.to_s.empty?
|
||||
end
|
||||
|
||||
# Create a new config file
|
||||
# @args: key -> the gpg key to encrypt
|
||||
# lang -> the software language
|
||||
# wallet_dir -> the directory where are the wallets password
|
||||
# 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
|
||||
# Create a new config file
|
||||
# @param options [Hash] the value to set the config file
|
||||
def setup(**options)
|
||||
gpg_key = options[:gpg_key] || @gpg_key
|
||||
lang = options[:lang] || @lang
|
||||
wallet_dir = options[:wallet_dir] || @wallet_dir
|
||||
default_wallet = options[:default_wallet] || @default_wallet
|
||||
gpg_exe = options[:gpg_exe] || @gpg_exe
|
||||
pinmode = options.key?(:pinmode) ? options[:pinmode] : @pinmode
|
||||
password = {
|
||||
numeric: true,
|
||||
alpha: true,
|
||||
special: false,
|
||||
length: 16
|
||||
}
|
||||
|
||||
if wallet_dir.to_s.empty?
|
||||
wallet_dir = "#{@config_dir}/wallets"
|
||||
end
|
||||
%w[numeric special alpha length].each do |k|
|
||||
if options.key?("pwd_#{k}".to_sym)
|
||||
password[k.to_sym] = options["pwd_#{k}".to_sym]
|
||||
elsif !@password.nil? && @password.key?(k.to_sym)
|
||||
password[k.to_sym] = @password[k.to_sym]
|
||||
end
|
||||
end
|
||||
|
||||
config = { 'key' => key,
|
||||
'lang' => lang,
|
||||
'wallet_dir' => wallet_dir,
|
||||
'gpg_exe' => gpg_exe,
|
||||
}
|
||||
unless gpg_key =~ /[a-zA-Z0-9.-_]+\@[a-zA-Z0-9]+\.[a-zA-Z]+/
|
||||
raise I18n.t('error.config.key_bad_format')
|
||||
end
|
||||
|
||||
FileUtils.mkdir_p(@config_dir, mode: 0700)
|
||||
FileUtils.mkdir_p(wallet_dir, mode: 0700)
|
||||
wallet_dir = "#{@config_dir}/wallets" if wallet_dir.to_s.empty?
|
||||
config = { 'gpg_key' => gpg_key,
|
||||
'lang' => lang,
|
||||
'wallet_dir' => wallet_dir,
|
||||
'default_wallet' => default_wallet,
|
||||
'gpg_exe' => gpg_exe,
|
||||
'password' => password,
|
||||
'pinmode' => pinmode,
|
||||
'wallet_paths' => @wallet_paths }
|
||||
|
||||
File.open(@config_file, 'w') do |file|
|
||||
file << config.to_yaml
|
||||
end
|
||||
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.config.write')}\n#{e}"
|
||||
end
|
||||
FileUtils.mkdir_p(@config_dir, mode: 0700)
|
||||
FileUtils.mkdir_p(wallet_dir, mode: 0700)
|
||||
|
||||
# Setup a new gpg key
|
||||
# @args: password -> the GPG key password
|
||||
# name -> the name of user
|
||||
# length -> length of the GPG key
|
||||
# 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.to_s.empty?
|
||||
raise "#{I18n.t('error.config.genkey_gpg.name')}"
|
||||
elsif password.to_s.empty?
|
||||
raise "#{I18n.t('error.config.genkey_gpg.password')}"
|
||||
end
|
||||
File.open(@config_file, 'w') do |file|
|
||||
file << config.to_yaml
|
||||
end
|
||||
rescue => e
|
||||
raise "#{I18n.t('error.config.write')}\n#{e}"
|
||||
end
|
||||
|
||||
param = ''
|
||||
param << '<GnupgKeyParms format="internal">' + "\n"
|
||||
param << "Key-Type: RSA\n"
|
||||
param << "Key-Length: #{length}\n"
|
||||
param << "Subkey-Type: ELG-E\n"
|
||||
param << "Subkey-Length: #{length}\n"
|
||||
param << "Name-Real: #{name}\n"
|
||||
param << "Name-Comment: #{name}\n"
|
||||
param << "Name-Email: #{@key}\n"
|
||||
param << "Expire-Date: #{expire}\n"
|
||||
param << "Passphrase: #{password}\n"
|
||||
param << "</GnupgKeyParms>\n"
|
||||
# Setup a new gpg key
|
||||
# @param password [String] gpg key password
|
||||
# @param name [String] the name of user
|
||||
# @param length [Integer] length of the gpg key
|
||||
# @param expire [Integer] time of expire to gpg key
|
||||
def setup_gpg_key(password, name, length = 4096, expire = 0)
|
||||
raise I18n.t('error.config.genkey_gpg.name') if name.to_s.empty?
|
||||
raise I18n.t('error.config.genkey_gpg.password') if password.to_s.empty?
|
||||
|
||||
ctx = GPGME::Ctx.new
|
||||
ctx.genkey(param, nil, nil)
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
|
||||
end
|
||||
param = ''
|
||||
param << '<GnupgKeyParms format="internal">' + "\n"
|
||||
param << "Key-Type: RSA\n"
|
||||
param << "Key-Length: #{length}\n"
|
||||
param << "Subkey-Type: ELG-E\n"
|
||||
param << "Subkey-Length: #{length}\n"
|
||||
param << "Name-Real: #{name}\n"
|
||||
param << "Name-Comment: #{name}\n"
|
||||
param << "Name-Email: #{@gpg_key}\n"
|
||||
param << "Expire-Date: #{expire}\n"
|
||||
param << "Passphrase: #{password}\n"
|
||||
param << "</GnupgKeyParms>\n"
|
||||
|
||||
# Load the config file
|
||||
def load_config
|
||||
config = YAML::load_file(@config_file)
|
||||
@key = config['key']
|
||||
@lang = config['lang']
|
||||
@wallet_dir = config['wallet_dir']
|
||||
@gpg_exe = config['gpg_exe']
|
||||
ctx = GPGME::Ctx.new
|
||||
ctx.genkey(param, nil, nil)
|
||||
rescue => e
|
||||
raise "#{I18n.t('error.config.genkey_gpg.exception')}\n#{e}"
|
||||
end
|
||||
|
||||
raise if @key.empty? or @wallet_dir.empty?
|
||||
|
||||
I18n.locale = @lang.to_sym
|
||||
# Load the config file
|
||||
def load_config
|
||||
config = YAML.load_file(@config_file)
|
||||
@gpg_key = config['gpg_key']
|
||||
@lang = config['lang']
|
||||
@wallet_dir = config['wallet_dir']
|
||||
@wallet_paths = config['wallet_paths'] || {}
|
||||
@default_wallet = config['default_wallet']
|
||||
@gpg_exe = config['gpg_exe']
|
||||
@password = config['password'] || {}
|
||||
@pinmode = config['pinmode'] || false
|
||||
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.config.load')}\n#{e}"
|
||||
end
|
||||
raise if @gpg_key.empty? || @wallet_dir.empty?
|
||||
|
||||
# Check if private key exist
|
||||
# @rtrn: true if the key exist, else false
|
||||
def check_gpg_key?
|
||||
ctx = GPGME::Ctx.new
|
||||
ctx.each_key(@key, true) do
|
||||
return true
|
||||
end
|
||||
I18n.locale = @lang.to_sym
|
||||
rescue => e
|
||||
raise "#{I18n.t('error.config.load')}\n#{e}"
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
# Check if private key exist
|
||||
# @return [Boolean] true if the key exist, else false
|
||||
def check_gpg_key?
|
||||
ctx = GPGME::Ctx.new
|
||||
ctx.each_key(@gpg_key, true) do
|
||||
return true
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Change the path of one wallet
|
||||
# @param path [String]new directory path
|
||||
# @param wallet [String] wallet name
|
||||
def set_wallet_path(path, wallet)
|
||||
path = @wallet_dir if path == 'default'
|
||||
path = File.absolute_path(path)
|
||||
|
||||
return if path == @wallet_dir && File.exist?("#{@wallet_dir}/#{wallet}.mpw")
|
||||
return if path == @wallet_paths[wallet]
|
||||
|
||||
old_wallet_file =
|
||||
if @wallet_paths.key?(wallet)
|
||||
"#{@wallet_paths[wallet]}/#{wallet}.mpw"
|
||||
else
|
||||
"#{@wallet_dir}/#{wallet}.mpw"
|
||||
end
|
||||
|
||||
FileUtils.mkdir_p(path) unless Dir.exist?(path)
|
||||
FileUtils.mv(old_wallet_file, "#{path}/#{wallet}.mpw") if File.exist?(old_wallet_file)
|
||||
|
||||
if path == @wallet_dir
|
||||
@wallet_paths.delete(wallet)
|
||||
else
|
||||
@wallet_paths[wallet] = path
|
||||
end
|
||||
|
||||
setup
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
53
lib/mpw/import/gorilla.rb
Normal file
53
lib/mpw/import/gorilla.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'csv'
|
||||
|
||||
module MPW
|
||||
module Import
|
||||
# Import an export mpw file
|
||||
# @param file [String] the file path to import
|
||||
def self.gorilla(file)
|
||||
data = {}
|
||||
|
||||
CSV.foreach(file, headers: true) do |row|
|
||||
id = row['uuid']
|
||||
comment =
|
||||
if row['title'] && row['notes']
|
||||
"#{row['title']} #{row['notes']}"
|
||||
elsif row['title']
|
||||
row['title']
|
||||
elsif row['notes']
|
||||
row['notes']
|
||||
end
|
||||
|
||||
data[id] = {
|
||||
'comment' => comment,
|
||||
'group' => row['group'],
|
||||
'password' => row['password'],
|
||||
'url' => row['url'],
|
||||
'user' => row['user']
|
||||
}
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
53
lib/mpw/import/keepass.rb
Normal file
53
lib/mpw/import/keepass.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'csv'
|
||||
|
||||
module MPW
|
||||
module Import
|
||||
# Import an keepass2 export csv file
|
||||
# @param file [String] the file path to import
|
||||
def self.keepass(file)
|
||||
data = {}
|
||||
|
||||
CSV.foreach(file, headers: true) do |row|
|
||||
id = "#{row['Group']} #{row['Title']}"
|
||||
comment =
|
||||
if row['Title'] && row['Notes']
|
||||
"#{row['Title']} #{row['Notes']}"
|
||||
elsif row['Title']
|
||||
row['Title']
|
||||
elsif row['Notes']
|
||||
row['Notes']
|
||||
end
|
||||
|
||||
data[id] = {
|
||||
'comment' => comment,
|
||||
'group' => row['Group'],
|
||||
'password' => row['Password'],
|
||||
'url' => row['URL'],
|
||||
'user' => row['Username']
|
||||
}
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
31
lib/mpw/import/mpw.rb
Normal file
31
lib/mpw/import/mpw.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'yaml'
|
||||
|
||||
module MPW
|
||||
module Import
|
||||
# Import an export mpw file
|
||||
# @param file [String] the file path to import
|
||||
def self.mpw(file)
|
||||
YAML.load_file(file)
|
||||
end
|
||||
end
|
||||
end
|
48
lib/mpw/import/mpw_old.rb
Normal file
48
lib/mpw/import/mpw_old.rb
Normal file
|
@ -0,0 +1,48 @@
|
|||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'yaml'
|
||||
|
||||
module MPW
|
||||
module Import
|
||||
# Import an export mpw file
|
||||
# @param file [String] the file path to import
|
||||
def self.mpw_old(file)
|
||||
data = {}
|
||||
YAML.load_file(file).each do |id, item|
|
||||
url = ''
|
||||
url += "#{item['protocol']}://" if item['protocol']
|
||||
url += item['host']
|
||||
url += ":#{item['port']}" if item['port']
|
||||
|
||||
data[id] = {
|
||||
'comment' => item['comment'],
|
||||
'group' => item['group'],
|
||||
'otp' => item['otp'],
|
||||
'password' => item['password'],
|
||||
'url' => url,
|
||||
'user' => item['user']
|
||||
}
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
187
lib/mpw/item.rb
187
lib/mpw/item.rb
|
@ -1,109 +1,108 @@
|
|||
#!/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.
|
||||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'i18n'
|
||||
|
||||
require 'uri'
|
||||
|
||||
module MPW
|
||||
class Item
|
||||
class Item
|
||||
attr_accessor :created
|
||||
attr_accessor :comment
|
||||
attr_accessor :group
|
||||
attr_accessor :host
|
||||
attr_accessor :id
|
||||
attr_accessor :otp
|
||||
attr_accessor :port
|
||||
attr_accessor :protocol
|
||||
attr_accessor :last_edit
|
||||
attr_accessor :url
|
||||
attr_accessor :user
|
||||
|
||||
attr_accessor :id
|
||||
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
|
||||
attr_accessor :created
|
||||
# @param options [Hash] the option :host is required
|
||||
def initialize(**options)
|
||||
@host = ''
|
||||
|
||||
# Constructor
|
||||
# Create a new 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?(:host) or options[:host].to_s.empty?
|
||||
raise I18n.t('error.update.host_empty')
|
||||
end
|
||||
if !options[:id] || !options[:created]
|
||||
@id = generate_id
|
||||
@created = Time.now.to_i
|
||||
else
|
||||
@id = options[:id]
|
||||
@created = options[:created]
|
||||
@last_edit = options[:last_edit]
|
||||
options[:no_update_last_edit] = true
|
||||
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
|
||||
@created = Time.now.to_i
|
||||
else
|
||||
@id = options[:id]
|
||||
@created = options[:created]
|
||||
@last_edit = options[:last_edit]
|
||||
options[:no_update_last_edit] = true
|
||||
end
|
||||
update(options)
|
||||
end
|
||||
|
||||
update(options)
|
||||
end
|
||||
# Update the item
|
||||
# @param options [Hash]
|
||||
def update(**options)
|
||||
unless options[:url] || options[:comment]
|
||||
raise I18n.t('error.update.host_and_comment_empty')
|
||||
end
|
||||
|
||||
# Update the item
|
||||
# @args: options -> a hash of parameter
|
||||
def update(options={})
|
||||
if options.has_key?(:host) and options[:host].to_s.empty?
|
||||
raise I18n.t('error.update.host_empty')
|
||||
end
|
||||
if options[:url]
|
||||
uri = URI(options[:url])
|
||||
@host = uri.host || options[:url]
|
||||
@port = uri.port || nil
|
||||
@protocol = uri.scheme || nil
|
||||
@url = options[:url]
|
||||
end
|
||||
|
||||
@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
|
||||
@comment = options[:comment] if options.key?(:comment)
|
||||
@group = options[:group] if options.key?(:group)
|
||||
@last_edit = Time.now.to_i unless options.key?(:no_update_last_edit)
|
||||
@otp = options[:otp] if options.key?(:otp)
|
||||
@user = options[:user] if options.key?(:user)
|
||||
end
|
||||
|
||||
# Update last_sync
|
||||
def set_last_sync
|
||||
@last_sync = Time.now.to_i
|
||||
end
|
||||
# Delete all data
|
||||
def delete
|
||||
@id = nil
|
||||
@comment = nil
|
||||
@created = nil
|
||||
@group = nil
|
||||
@host = nil
|
||||
@last_edit = nil
|
||||
@otp = nil
|
||||
@port = nil
|
||||
@protocol = nil
|
||||
@url = nil
|
||||
@user = nil
|
||||
end
|
||||
|
||||
# Delete all data
|
||||
def delete
|
||||
@id = nil
|
||||
@group = nil
|
||||
@host = nil
|
||||
@protocol = nil
|
||||
@user = nil
|
||||
@port = nil
|
||||
@otp = nil
|
||||
@comment = nil
|
||||
@created = nil
|
||||
@last_edit = nil
|
||||
@last_sync = nil
|
||||
end
|
||||
def empty?
|
||||
@id.to_s.empty?
|
||||
end
|
||||
|
||||
def empty?
|
||||
return @id.to_s.empty?
|
||||
end
|
||||
def nil?
|
||||
false
|
||||
end
|
||||
|
||||
def nil?
|
||||
return false
|
||||
end
|
||||
private
|
||||
|
||||
# Generate an random id
|
||||
private
|
||||
def generate_id
|
||||
return ([*('A'..'Z'),*('a'..'z'),*('0'..'9')]).sample(16).join
|
||||
end
|
||||
end
|
||||
# Generate an random id
|
||||
# @return [String] random string
|
||||
def generate_id
|
||||
[*('A'..'Z'), *('a'..'z'), *('0'..'9')].sample(16).join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
814
lib/mpw/mpw.rb
814
lib/mpw/mpw.rb
|
@ -1,20 +1,22 @@
|
|||
#!/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.
|
||||
#
|
||||
# Copyright:: 2013, Adrien Waksberg
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
require 'rubygems/package'
|
||||
require 'gpgme'
|
||||
|
@ -22,460 +24,328 @@ require 'i18n'
|
|||
require 'yaml'
|
||||
require 'rotp'
|
||||
require 'mpw/item'
|
||||
|
||||
|
||||
module MPW
|
||||
class MPW
|
||||
|
||||
# Constructor
|
||||
def initialize(key, wallet_file, gpg_pass=nil, gpg_exe=nil)
|
||||
@key = key
|
||||
@gpg_pass = gpg_pass
|
||||
@gpg_exe = gpg_exe
|
||||
@wallet_file = wallet_file
|
||||
|
||||
if not @gpg_exe.to_s.empty?
|
||||
GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg")
|
||||
end
|
||||
end
|
||||
|
||||
# Read mpw file
|
||||
def read_data
|
||||
@config = {}
|
||||
@data = []
|
||||
@keys = {}
|
||||
@passwords = {}
|
||||
@otp_keys = {}
|
||||
|
||||
data = nil
|
||||
|
||||
return if not File.exists?(@wallet_file)
|
||||
|
||||
Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
|
||||
tar.each do |f|
|
||||
case f.full_name
|
||||
when 'wallet/config.gpg'
|
||||
@config = YAML.load(decrypt(f.read))
|
||||
|
||||
when 'wallet/meta.gpg'
|
||||
data = decrypt(f.read)
|
||||
|
||||
when /^wallet\/keys\/(?<key>.+)\.pub$/
|
||||
key = Regexp.last_match('key')
|
||||
|
||||
if GPGME::Key.find(:public, key).length == 0
|
||||
GPGME::Key.import(f.read, armor: true)
|
||||
end
|
||||
|
||||
@keys[key] = f.read
|
||||
|
||||
when /^wallet\/passwords\/(?<id>[a-zA-Z0-9]+)\.gpg$/
|
||||
@passwords[Regexp.last_match('id')] = f.read
|
||||
|
||||
when /^wallet\/otp_keys\/(?<id>[a-zA-Z0-9]+)\.gpg$/
|
||||
@otp_keys[Regexp.last_match('id')] = f.read
|
||||
|
||||
else
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not data.nil? and not data.empty?
|
||||
YAML.load(data).each_value do |d|
|
||||
@data.push(Item.new(id: d['id'],
|
||||
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'],
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
add_key(@key) if @keys[@key].nil?
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
|
||||
end
|
||||
|
||||
# Encrypt a file
|
||||
def write_data
|
||||
data = {}
|
||||
tmp_file = "#{@wallet_file}.tmp"
|
||||
|
||||
@data.each do |item|
|
||||
next if item.empty?
|
||||
|
||||
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
|
||||
|
||||
@config['last_update'] = Time.now.to_i
|
||||
|
||||
Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
|
||||
data_encrypt = encrypt(data.to_yaml)
|
||||
tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
|
||||
io.write(data_encrypt)
|
||||
end
|
||||
|
||||
config = encrypt(@config.to_yaml)
|
||||
tar.add_file_simple('wallet/config.gpg', 0400, config.length) do |io|
|
||||
io.write(config)
|
||||
end
|
||||
|
||||
@passwords.each do |id, password|
|
||||
tar.add_file_simple("wallet/passwords/#{id}.gpg", 0400, password.length) do |io|
|
||||
io.write(password)
|
||||
end
|
||||
end
|
||||
|
||||
@otp_keys.each do |id, key|
|
||||
tar.add_file_simple("wallet/otp_keys/#{id}.gpg", 0400, key.length) do |io|
|
||||
io.write(key)
|
||||
end
|
||||
end
|
||||
|
||||
@keys.each do |id, key|
|
||||
tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
|
||||
io.write(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
File.rename(tmp_file, @wallet_file)
|
||||
rescue Exception => e
|
||||
File.unlink(tmp_file) if File.exist?(tmp_file)
|
||||
|
||||
raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
|
||||
end
|
||||
|
||||
# Get a password
|
||||
# args: id -> the item id
|
||||
def get_password(id)
|
||||
password = decrypt(@passwords[id])
|
||||
|
||||
if /^\$[a-zA-Z0-9]{4,9}::(?<password>.+)$/ =~ password
|
||||
return Regexp.last_match('password')
|
||||
else
|
||||
return password
|
||||
end
|
||||
end
|
||||
|
||||
# Set a password
|
||||
# args: id -> the item id
|
||||
# password -> the new password
|
||||
def set_password(id, password)
|
||||
salt = MPW::password(length: Random.rand(4..9))
|
||||
password = "$#{salt}::#{password}"
|
||||
|
||||
@passwords[id] = encrypt(password)
|
||||
end
|
||||
|
||||
# Return the list of all gpg keys
|
||||
# rtrn: an array with the gpg keys name
|
||||
def list_keys
|
||||
return @keys.keys
|
||||
end
|
||||
|
||||
# Add a public key
|
||||
# args: key -> new public key file or name
|
||||
def add_key(key)
|
||||
if File.exists?(key)
|
||||
data = File.open(key).read
|
||||
key_import = GPGME::Key.import(data, armor: true)
|
||||
key = GPGME::Key.get(key_import.imports[0].fpr).uids[0].email
|
||||
else
|
||||
data = GPGME::Key.export(key, armor: true).read
|
||||
end
|
||||
|
||||
if data.to_s.empty?
|
||||
raise I18n.t('error.export_key')
|
||||
end
|
||||
|
||||
@keys[key] = data
|
||||
end
|
||||
|
||||
# Delete a public key
|
||||
# args: key -> public key to delete
|
||||
def delete_key(key)
|
||||
@keys.delete(key)
|
||||
end
|
||||
|
||||
# Set config
|
||||
# args: config -> a hash with config options
|
||||
def set_config(options={})
|
||||
@config = {} if @config.nil?
|
||||
|
||||
@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
|
||||
# @args: item -> Object MPW::Item
|
||||
def add(item)
|
||||
if not item.instance_of?(Item)
|
||||
raise I18n.t('error.bad_class')
|
||||
elsif item.empty?
|
||||
raise I18n.t('error.empty')
|
||||
else
|
||||
@data.push(item)
|
||||
end
|
||||
end
|
||||
|
||||
# Search in some csv data
|
||||
# @args: options -> a hash with paramaters
|
||||
# @rtrn: a list with the resultat of the search
|
||||
def list(options={})
|
||||
result = []
|
||||
|
||||
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.to_s.downcase)
|
||||
|
||||
host = item.host.to_s.downcase
|
||||
comment = item.comment.to_s.downcase
|
||||
|
||||
if not host =~ /^.*#{search}.*$/ and not comment =~ /^.*#{search}.*$/
|
||||
next
|
||||
end
|
||||
|
||||
result.push(item)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
# Search in some csv data
|
||||
# @args: id -> the id item
|
||||
# @rtrn: a row with the result of the search
|
||||
def search_by_id(id)
|
||||
@data.each do |item|
|
||||
return item if item.id == id
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
# Get last sync
|
||||
def get_last_sync
|
||||
return @config['last_sync'].to_i
|
||||
rescue
|
||||
return 0
|
||||
end
|
||||
|
||||
# Sync data with remote file
|
||||
# @args: force -> force the sync
|
||||
def sync(force=false)
|
||||
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['protocol']
|
||||
when 'sftp', 'scp', 'ssh'
|
||||
require "mpw/sync/ssh"
|
||||
sync = SyncSSH.new(@config)
|
||||
when 'ftp'
|
||||
require 'mpw/sync/ftp'
|
||||
sync = SyncFTP.new(@config)
|
||||
else
|
||||
raise I18n.t('error.sync.unknown_type')
|
||||
end
|
||||
|
||||
sync.connect
|
||||
sync.get(tmp_file)
|
||||
|
||||
remote = MPW.new(@key, tmp_file, @gpg_pass, @gpg_exe)
|
||||
remote.read_data
|
||||
|
||||
File.unlink(tmp_file) if File.exist?(tmp_file)
|
||||
|
||||
return if remote.get_last_sync == @config['last_update']
|
||||
|
||||
if not remote.to_s.empty?
|
||||
@data.each do |item|
|
||||
update = false
|
||||
|
||||
remote.list.each do |r|
|
||||
next if item.id != r.id
|
||||
|
||||
# Update item
|
||||
if item.last_edit < r.last_edit
|
||||
item.update(group: r.group,
|
||||
host: r.host,
|
||||
protocol: r.protocol,
|
||||
user: r.user,
|
||||
port: r.port,
|
||||
comment: r.comment
|
||||
)
|
||||
set_password(item.id, remote.get_password(item.id))
|
||||
end
|
||||
|
||||
r.delete
|
||||
update = true
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
# Remove an old item
|
||||
if not update and item.last_sync.to_i < get_last_sync and item.last_edit < get_last_sync
|
||||
item.delete
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add item
|
||||
remote.list.each do |r|
|
||||
next if r.last_edit <= get_last_sync
|
||||
|
||||
item = Item.new(id: r.id,
|
||||
group: r.group,
|
||||
host: r.host,
|
||||
protocol: r.protocol,
|
||||
user: r.user,
|
||||
port: r.port,
|
||||
comment: r.comment,
|
||||
created: r.created,
|
||||
last_edit: r.last_edit
|
||||
)
|
||||
|
||||
set_password(item.id, remote.get_password(item.id))
|
||||
add(item)
|
||||
end
|
||||
|
||||
remote = nil
|
||||
|
||||
@data.each do |item|
|
||||
item.set_last_sync
|
||||
end
|
||||
|
||||
@config['last_sync'] = Time.now.to_i
|
||||
|
||||
write_data
|
||||
sync.update(@wallet_file)
|
||||
rescue Exception => e
|
||||
File.unlink(tmp_file) if File.exist?(tmp_file)
|
||||
|
||||
raise "#{I18n.t('error.sync.general')}\n#{e}"
|
||||
end
|
||||
|
||||
# Set an opt key
|
||||
# args: id -> the item id
|
||||
# key -> the new key
|
||||
def set_otp_key(id, 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)
|
||||
if not @otp_keys.has_key?(id)
|
||||
return 0
|
||||
else
|
||||
return ROTP::TOTP.new(decrypt(@otp_keys[id])).now
|
||||
end
|
||||
end
|
||||
|
||||
# Get remaining time before expire otp code
|
||||
# @rtrn: return time in seconde
|
||||
def get_otp_remaining_time
|
||||
return (Time.now.utc.to_i / 30 + 1) * 30 - Time.now.utc.to_i
|
||||
end
|
||||
|
||||
# Generate a random password
|
||||
# @args: options -> :length, :special, :alpha, :numeric
|
||||
# @rtrn: a random string
|
||||
def self.password(options={})
|
||||
if not options.include?(:length) or options[:length].to_i <= 0
|
||||
length = 8
|
||||
elsif options[:length].to_i >= 32768
|
||||
length = 32768
|
||||
else
|
||||
length = options[:length].to_i
|
||||
end
|
||||
|
||||
chars = []
|
||||
chars += [*('!'..'?')] - [*('0'..'9')] if options.include?(:special)
|
||||
chars += [*('A'..'Z'),*('a'..'z')] if options.include?(:alpha)
|
||||
chars += [*('0'..'9')] if options.include?(:numeric)
|
||||
chars = [*('A'..'Z'),*('a'..'z'),*('0'..'9')] if chars.empty?
|
||||
|
||||
result = ''
|
||||
while length > 62 do
|
||||
result << chars.sample(62).join
|
||||
length -= 62
|
||||
end
|
||||
result << chars.sample(length).join
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
# Decrypt a gpg file
|
||||
# @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')
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
|
||||
end
|
||||
|
||||
# Encrypt a file
|
||||
# args: data -> string to encrypt
|
||||
private
|
||||
def encrypt(data)
|
||||
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
|
||||
|
||||
return crypto.encrypt(data, recipients: recipients).read
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
|
||||
end
|
||||
|
||||
end
|
||||
class MPW
|
||||
# @param key [String] gpg key name
|
||||
# @param wallet_file [String] path of the wallet file
|
||||
# @param gpg_pass [String] password of the gpg key
|
||||
# @param gpg_exe [String] path of the gpg executable
|
||||
# @param pinmode [Boolean] enable the gpg pinmode
|
||||
def initialize(key, wallet_file, gpg_pass = nil, gpg_exe = nil, pinmode = false)
|
||||
@key = key
|
||||
@gpg_pass = gpg_pass
|
||||
@gpg_exe = gpg_exe
|
||||
@wallet_file = wallet_file
|
||||
@pinmode = pinmode
|
||||
|
||||
GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_exe, "#{Dir.home}/.gnupg") unless @gpg_exe.to_s.empty?
|
||||
end
|
||||
|
||||
# Read mpw file
|
||||
def read_data
|
||||
@data = []
|
||||
@keys = {}
|
||||
@passwords = {}
|
||||
@otp_keys = {}
|
||||
|
||||
data = nil
|
||||
|
||||
return unless File.exist?(@wallet_file)
|
||||
|
||||
Gem::Package::TarReader.new(File.open(@wallet_file)) do |tar|
|
||||
tar.each do |f|
|
||||
case f.full_name
|
||||
when 'wallet/meta.gpg'
|
||||
data = decrypt(f.read)
|
||||
|
||||
when %r{^wallet/keys/(?<key>.+)\.pub$}
|
||||
key = Regexp.last_match('key')
|
||||
|
||||
if GPGME::Key.find(:public, key).empty?
|
||||
GPGME::Key.import(f.read, armor: true)
|
||||
end
|
||||
|
||||
@keys[key] = f.read
|
||||
|
||||
when %r{^wallet/passwords/(?<id>[a-zA-Z0-9]+)\.gpg$}
|
||||
@passwords[Regexp.last_match('id')] = f.read
|
||||
|
||||
when %r{^wallet/otp_keys/(?<id>[a-zA-Z0-9]+)\.gpg$}
|
||||
@otp_keys[Regexp.last_match('id')] = f.read
|
||||
|
||||
else
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless data.to_s.empty?
|
||||
YAML.safe_load(data).each_value do |d|
|
||||
@data.push(
|
||||
Item.new(
|
||||
id: d['id'],
|
||||
group: d['group'],
|
||||
user: d['user'],
|
||||
url: d['url'],
|
||||
otp: @otp_keys.key?(d['id']),
|
||||
comment: d['comment'],
|
||||
last_edit: d['last_edit'],
|
||||
created: d['created']
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
add_key(@key) unless @keys.key?(@key)
|
||||
rescue => e
|
||||
raise "#{I18n.t('error.mpw_file.read_data')}\n#{e}"
|
||||
end
|
||||
|
||||
# Encrypt all data in tarball
|
||||
def write_data
|
||||
data = {}
|
||||
tmp_file = "#{@wallet_file}.tmp"
|
||||
|
||||
@data.each do |item|
|
||||
next if item.empty?
|
||||
|
||||
data.merge!(
|
||||
item.id => {
|
||||
'id' => item.id,
|
||||
'group' => item.group,
|
||||
'user' => item.user,
|
||||
'url' => item.url,
|
||||
'comment' => item.comment,
|
||||
'last_edit' => item.last_edit,
|
||||
'created' => item.created
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
Gem::Package::TarWriter.new(File.open(tmp_file, 'w+')) do |tar|
|
||||
data_encrypt = encrypt(data.to_yaml)
|
||||
tar.add_file_simple('wallet/meta.gpg', 0400, data_encrypt.length) do |io|
|
||||
io.write(data_encrypt)
|
||||
end
|
||||
|
||||
@passwords.each do |id, password|
|
||||
tar.add_file_simple("wallet/passwords/#{id}.gpg", 0400, password.length) do |io|
|
||||
io.write(password)
|
||||
end
|
||||
end
|
||||
|
||||
@otp_keys.each do |id, key|
|
||||
tar.add_file_simple("wallet/otp_keys/#{id}.gpg", 0400, key.length) do |io|
|
||||
io.write(key)
|
||||
end
|
||||
end
|
||||
|
||||
@keys.each do |id, key|
|
||||
tar.add_file_simple("wallet/keys/#{id}.pub", 0400, key.length) do |io|
|
||||
io.write(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
File.rename(tmp_file, @wallet_file)
|
||||
rescue => e
|
||||
File.unlink(tmp_file) if File.exist?(tmp_file)
|
||||
|
||||
raise "#{I18n.t('error.mpw_file.write_data')}\n#{e}"
|
||||
end
|
||||
|
||||
# Get a password
|
||||
# @param id [String] the item id
|
||||
def get_password(id)
|
||||
password = decrypt(@passwords[id])
|
||||
|
||||
if /^\$[a-zA-Z0-9]{4,9}::(?<password>.+)$/ =~ password
|
||||
Regexp.last_match('password')
|
||||
else
|
||||
password
|
||||
end
|
||||
end
|
||||
|
||||
# Set a new password for an item
|
||||
# @param id [String] the item id
|
||||
# @param password [String] the new password
|
||||
def set_password(id, password)
|
||||
salt = MPW.password(length: Random.rand(4..9))
|
||||
password = "$#{salt}::#{password}"
|
||||
|
||||
@passwords[id] = encrypt(password)
|
||||
end
|
||||
|
||||
# Return the list of all gpg keys
|
||||
# @return [Array] the gpg keys name
|
||||
def list_keys
|
||||
@keys.keys
|
||||
end
|
||||
|
||||
# Add a public key
|
||||
# @param key [String] new public key file or name
|
||||
def add_key(key)
|
||||
if File.exist?(key)
|
||||
data = File.open(key).read
|
||||
key_import = GPGME::Key.import(data, armor: true)
|
||||
key = GPGME::Key.get(key_import.imports[0].fpr).uids[0].email
|
||||
else
|
||||
data = GPGME::Key.export(key, armor: true).read
|
||||
end
|
||||
|
||||
raise I18n.t('error.export_key') if data.to_s.empty?
|
||||
|
||||
@keys[key] = data
|
||||
@passwords.each_key { |id| set_password(id, get_password(id)) }
|
||||
@otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
|
||||
end
|
||||
|
||||
# Delete a public key
|
||||
# @param key [String] public key to delete
|
||||
def delete_key(key)
|
||||
@keys.delete(key)
|
||||
@passwords.each_key { |id| set_password(id, get_password(id)) }
|
||||
@otp_keys.each_key { |id| set_otp_key(id, get_otp_key(id)) }
|
||||
end
|
||||
|
||||
# Add a new item
|
||||
# @param item [Item]
|
||||
def add(item)
|
||||
raise I18n.t('error.bad_class') unless item.instance_of?(Item)
|
||||
raise I18n.t('error.empty') if item.empty?
|
||||
|
||||
@data.push(item)
|
||||
end
|
||||
|
||||
# Search in some csv data
|
||||
# @param options [Hash]
|
||||
# @return [Array] a list with the resultat of the search
|
||||
def list(**options)
|
||||
result = []
|
||||
|
||||
search = options[:pattern].to_s.downcase
|
||||
group = options[:group].to_s.downcase
|
||||
|
||||
@data.each do |item|
|
||||
next if item.empty?
|
||||
next unless group.empty? || group.eql?(item.group.to_s.downcase)
|
||||
|
||||
host = item.host.to_s.downcase
|
||||
comment = item.comment.to_s.downcase
|
||||
|
||||
next unless host =~ /^.*#{search}.*$/ || comment =~ /^.*#{search}.*$/
|
||||
|
||||
result.push(item)
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# Search an item with an id
|
||||
# @param id [String]the id item
|
||||
# @return [Item] an item or nil
|
||||
def search_by_id(id)
|
||||
@data.each do |item|
|
||||
return item if item.id == id
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Set a new opt key
|
||||
# @param id [String] the item id
|
||||
# @param key [String] the new key
|
||||
def set_otp_key(id, key)
|
||||
@otp_keys[id] = encrypt(key.to_s) unless key.to_s.empty?
|
||||
end
|
||||
|
||||
# Get an opt key
|
||||
# @param id [String] the item id
|
||||
def get_otp_key(id)
|
||||
@otp_keys.key?(id) ? decrypt(@otp_keys[id]) : nil
|
||||
end
|
||||
|
||||
# Get an otp code
|
||||
# @param id [String] the item id
|
||||
# @return [String] an otp code
|
||||
def get_otp_code(id)
|
||||
@otp_keys.key?(id) ? ROTP::TOTP.new(decrypt(@otp_keys[id])).now : 0
|
||||
end
|
||||
|
||||
# Get remaining time before expire otp code
|
||||
# @return [Integer] time in seconde
|
||||
def get_otp_remaining_time
|
||||
(Time.now.utc.to_i / 30 + 1) * 30 - Time.now.utc.to_i
|
||||
end
|
||||
|
||||
# Generate a random password
|
||||
# @param options [Hash] :length, :special, :alpha, :numeric
|
||||
# @return [String] a random string
|
||||
def self.password(**options)
|
||||
length =
|
||||
if !options.include?(:length) || options[:length].to_i <= 0
|
||||
8
|
||||
elsif options[:length].to_i >= 32_768
|
||||
32_768
|
||||
else
|
||||
options[:length].to_i
|
||||
end
|
||||
|
||||
chars = []
|
||||
chars += [*('!'..'?')] - [*('0'..'9')] if options[:special]
|
||||
chars += [*('A'..'Z'), *('a'..'z')] if options[:alpha]
|
||||
chars += [*('0'..'9')] if options[:numeric]
|
||||
chars = [*('A'..'Z'), *('a'..'z'), *('0'..'9')] if chars.empty?
|
||||
|
||||
result = ''
|
||||
length.times do
|
||||
result << chars.sample
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Decrypt a gpg file
|
||||
# @param data [String] data to decrypt
|
||||
# @return [String] data decrypted
|
||||
def decrypt(data)
|
||||
return nil if data.to_s.empty?
|
||||
|
||||
password =
|
||||
if /^(1\.[0-9.]+|2\.0)(\.[0-9]+)?/ =~ GPGME::Engine.info.first.version || @pinmode
|
||||
{ password: @gpg_pass }
|
||||
else
|
||||
{ password: @gpg_pass,
|
||||
pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK }
|
||||
end
|
||||
|
||||
crypto = GPGME::Crypto.new(armor: true)
|
||||
crypto
|
||||
.decrypt(data, password)
|
||||
.read.force_encoding('utf-8')
|
||||
rescue => e
|
||||
raise "#{I18n.t('error.gpg_file.decrypt')}\n#{e}"
|
||||
end
|
||||
|
||||
# Encrypt a file
|
||||
# @param data [String] data to encrypt
|
||||
# @return [String] data encrypted
|
||||
def encrypt(data)
|
||||
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
|
||||
|
||||
crypto.encrypt(data, recipients: recipients).read
|
||||
rescue => e
|
||||
raise "#{I18n.t('error.gpg_file.encrypt')}\n#{e}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
#!/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 'i18n'
|
||||
require 'net/ftp'
|
||||
|
||||
module MPW
|
||||
class FTP
|
||||
|
||||
# Constructor
|
||||
# @args: config -> the config
|
||||
def initialize(config)
|
||||
@host = config['host']
|
||||
@user = config['user']
|
||||
@password = config['password']
|
||||
@path = config['path']
|
||||
@port = config['port'].instance_of?(Integer) ? 22 : config['port']
|
||||
end
|
||||
|
||||
|
||||
# Connect to server
|
||||
def connect
|
||||
Net::FTP.open(@host) do |ftp|
|
||||
ftp.login(@user, @password)
|
||||
break
|
||||
end
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.sync.connection')}\n#{e}"
|
||||
end
|
||||
|
||||
# Get data on server
|
||||
# @args: file_tmp -> the path where download the file
|
||||
def get(file_tmp)
|
||||
Net::FTP.open(@host) do |ftp|
|
||||
ftp.login(@user, @password)
|
||||
ftp.gettextfile(@path, file_tmp)
|
||||
end
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.sync.download')}\n#{e}"
|
||||
end
|
||||
|
||||
# Update the remote data
|
||||
# @args: file_gpg -> the data to send on server
|
||||
def update(file_gpg)
|
||||
Net::FTP.open(@host) do |ftp|
|
||||
ftp.login(@user, @password)
|
||||
ftp.puttextfile(file_gpg, @path)
|
||||
end
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.sync.upload')}\n#{e}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,67 +0,0 @@
|
|||
#!/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 'i18n'
|
||||
require 'net/ssh'
|
||||
require 'net/sftp'
|
||||
|
||||
module MPW
|
||||
class SyncSSH
|
||||
|
||||
# Constructor
|
||||
# @args: config -> the config
|
||||
def initialize(config)
|
||||
@host = config['host']
|
||||
@user = config['user']
|
||||
@password = config['password']
|
||||
@path = config['path']
|
||||
@port = config['port'].instance_of?(Integer) ? 22 : config['port']
|
||||
end
|
||||
|
||||
# Connect to server
|
||||
def connect
|
||||
Net::SSH.start(@host, @user, password: @password, port: @port) do
|
||||
break
|
||||
end
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.sync.connection')}\n#{e}"
|
||||
end
|
||||
|
||||
# Get data on server
|
||||
# @args: file_tmp -> the path where download the file
|
||||
def get(file_tmp)
|
||||
Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
|
||||
sftp.lstat(@path) do |response|
|
||||
sftp.download!(@path, file_tmp) if response.ok?
|
||||
end
|
||||
end
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.sync.download')}\n#{e}"
|
||||
end
|
||||
|
||||
# Update the remote data
|
||||
# @args: file_gpg -> the data to send on server
|
||||
def update(file_gpg)
|
||||
Net::SFTP.start(@host, @user, password: @password, port: @port) do |sftp|
|
||||
sftp.upload!(file_gpg, @path)
|
||||
end
|
||||
rescue Exception => e
|
||||
raise "#{I18n.t('error.sync.upload')}\n#{e}"
|
||||
end
|
||||
end
|
||||
end
|
21
mpw.gemspec
21
mpw.gemspec
|
@ -1,4 +1,3 @@
|
|||
# coding: utf-8
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
|
||||
|
@ -12,18 +11,18 @@ Gem::Specification.new do |spec|
|
|||
spec.homepage = 'https://github.com/nishiki/manage-password'
|
||||
spec.license = 'GPL-2.0'
|
||||
|
||||
spec.files = `git ls-files -z`.split("\x0")
|
||||
spec.files = %x(git ls-files -z).split("\x0")
|
||||
spec.executables = ['mpw']
|
||||
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
||||
spec.require_paths = ['lib']
|
||||
|
||||
spec.add_dependency "i18n", "~> 0.7", ">= 0.7.0"
|
||||
spec.add_dependency "gpgme", "~> 2.0", ">= 2.0.12"
|
||||
spec.add_dependency "highline", "~> 1.7", ">= 1.7.8"
|
||||
spec.add_dependency "locale", "~> 2.1", ">= 2.1.2"
|
||||
spec.add_dependency "colorize", "~> 0.8", ">= 0.8.1"
|
||||
spec.add_dependency "net-ssh", "~> 3.2", ">= 3.2.0"
|
||||
spec.add_dependency "net-sftp", "~> 2.1", ">= 2.1.2"
|
||||
spec.add_dependency "clipboard", "~> 1.1", ">= 1.1.1"
|
||||
spec.add_dependency "rotp", "~> 3.1", ">= 3.1.0"
|
||||
spec.required_ruby_version = '>= 2.1'
|
||||
|
||||
spec.add_dependency 'i18n', '~> 0.9', '>= 0.9.1'
|
||||
spec.add_dependency 'gpgme', '~> 2.0', '>= 2.0.14'
|
||||
spec.add_dependency 'highline', '~> 1.7', '>= 1.7.8'
|
||||
spec.add_dependency 'locale', '~> 2.1', '>= 2.1.2'
|
||||
spec.add_dependency 'colorize', '~> 0.8', '>= 0.8.1'
|
||||
spec.add_dependency 'clipboard', '~> 1.1', '>= 1.1.1'
|
||||
spec.add_dependency 'rotp', '~> 3.3', '>= 3.3.0'
|
||||
end
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
---
|
||||
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') %>
|
||||
# <%= I18n.t('form.add_item.url') %>
|
||||
url: <%= options[:url] %>
|
||||
# <%= I18n.t('form.add_item.login') %>
|
||||
user: <%= options[:user] %>
|
||||
# <%= I18n.t('form.add_item.group') %>
|
||||
group: <%= options[:group] %><% unless password %>
|
||||
# <%= I18n.t('form.add_item.password') %>
|
||||
password:<% end %>
|
||||
# <%= I18n.t('form.add_item.comment') %>
|
||||
comment: <%= options[:comment] %>
|
||||
# <%= I18n.t('form.add_item.otp_key') %>
|
||||
otp_key: <%= options[:otp] %>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
# <%= 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 %>
|
|
@ -1,17 +1,13 @@
|
|||
---
|
||||
# <%= I18n.t('form.update_item.host') %>
|
||||
host: <%= item.host %>
|
||||
# <%= I18n.t('form.update_item.url') %>
|
||||
host: <% if options[:url] %><%= options[:url] %><% else %><%= item.url %><% end %>
|
||||
# <%= I18n.t('form.update_item.login') %>
|
||||
user: <%= item.user %>
|
||||
user: <% if options[:user] %><%= options[:user] %><% else %><%= item.user %><% end %><% unless password %>
|
||||
# <%= I18n.t('form.update_item.password') %>
|
||||
password:
|
||||
password: <% end %>
|
||||
# <%= 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 %>
|
||||
group: <% if options[:group] %><%= options[:group] %><% else %><%= item.group %><% end %>
|
||||
# <%= I18n.t('form.update_item.otp_key') %>
|
||||
opt_key:
|
||||
otp_key: <% if options[:otp_key] %><%= options[:otp_key] %><% end %>
|
||||
# <%= I18n.t('form.update_item.comment') %>
|
||||
comment: <%= item.comment %>
|
||||
comment: <% if options[:comment] %><%= options[:comment] %><% else %><%= item.comment %><% end %>
|
||||
|
|
16
test/files/fixtures-import.yml
Normal file
16
test/files/fixtures-import.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
1:
|
||||
url: https://fric.com
|
||||
user: 230403
|
||||
group: Bank
|
||||
password: 5XdiTQOubRDw9B0aJoMlcEyL
|
||||
otp_key: 330223432
|
||||
comment: I love my bank
|
||||
2:
|
||||
url: https://assurance.com:443
|
||||
user: user_2132
|
||||
host: assurance.com
|
||||
group: Assurance
|
||||
password: DMyK6B3v4bWO52VzU7aTHIem
|
||||
otp_key:
|
||||
comment:
|
|
@ -1,28 +1,31 @@
|
|||
add_new:
|
||||
group: 'test_group'
|
||||
host: 'test_host'
|
||||
protocol: 'test_protocol'
|
||||
user: 'test_user'
|
||||
password: 'test_password'
|
||||
port: '42'
|
||||
comment: 'test_comment'
|
||||
add:
|
||||
url: 'https://example.com:8080'
|
||||
group: 'Bank'
|
||||
host: 'example.com'
|
||||
protocol: 'https'
|
||||
user: 'admin'
|
||||
password: 'VmfnCN6pPIqgRIbc'
|
||||
port: '8080'
|
||||
comment: 'the website'
|
||||
|
||||
add_existing:
|
||||
import:
|
||||
id: 'TEST-ID-XXXXX'
|
||||
group: 'test_group_existing'
|
||||
host: 'test_host_existing'
|
||||
protocol: 'test_protocol_existing'
|
||||
user: 'test_user_existing'
|
||||
password: 'test_password_existing'
|
||||
port: '44'
|
||||
comment: 'test_comment_existing'
|
||||
url: 'https://gogole.com:8081/toto'
|
||||
group: 'Cloud'
|
||||
host: 'gogole.com'
|
||||
protocol: 'https'
|
||||
user: 'gg-2304'
|
||||
password: 'TITl0kV9CDDa9sVK'
|
||||
port: '8081'
|
||||
comment: 'My little servers'
|
||||
created: 1386752948
|
||||
|
||||
update:
|
||||
group: 'test_group_update'
|
||||
host: 'test_host_update'
|
||||
protocol: 'test_protocol_update'
|
||||
user: 'test_user_update'
|
||||
password: 'test_password_update'
|
||||
port: '43'
|
||||
comment: 'test_comment_update'
|
||||
url: 'ssh://example2.com:2222'
|
||||
group: 'Assurance'
|
||||
host: 'example2.com'
|
||||
protocol: 'ssh'
|
||||
user: 'root'
|
||||
password: 'kbSrbv4WlMaVxaZ7'
|
||||
port: '2222'
|
||||
comment: 'i love ssh'
|
||||
|
|
4
test/files/import-gorilla.txt
Normal file
4
test/files/import-gorilla.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
uuid,group,title,url,user,password,notes
|
||||
49627979-e393-48c4-49ca-1cf66603238e,Bank,Fric,http://fric.com,12345,secret,money money
|
||||
49627979-e393-48c4-49ca-1cf66603238f,,My little server,server.com,secret2,
|
||||
49627979-e393-48c4-49ca-1cf66603238g,Cloud,,ssh://fric.com:4333,username,secret,bastion
|
3
test/files/import-keepass.txt
Normal file
3
test/files/import-keepass.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
"Group","Title","Username","Password","URL","Notes"
|
||||
"Racine","Bank","123456","ywcExJW8qmBVTSyi","http://bank.com/login","My little bank"
|
||||
"Racine/Cloud","GAFAM","wesh","superpassword","localhost.local",""
|
35
test/files/import-mpw_old.txt
Normal file
35
test/files/import-mpw_old.txt
Normal file
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
1:
|
||||
host: fric.com
|
||||
user: 12345
|
||||
group: Bank
|
||||
password: secret
|
||||
protocol: http
|
||||
port:
|
||||
otp_key:
|
||||
comment: Fric money money
|
||||
last_edit: 1487623641
|
||||
created: 1485729356
|
||||
2:
|
||||
host: server.com
|
||||
user: sercret2
|
||||
group:
|
||||
password:
|
||||
protocol:
|
||||
port: 4222
|
||||
otp_key:
|
||||
comment: My little server
|
||||
last_edit: 1487623641
|
||||
created: 1485729356
|
||||
3:
|
||||
host: fric.com
|
||||
user: username
|
||||
group: Cloud
|
||||
password:
|
||||
protocol: ssh
|
||||
port: 4333
|
||||
otp_key:
|
||||
comment: bastion
|
||||
last_edit: 1487623641
|
||||
created: 1485729356
|
||||
|
10
test/init.rb
10
test/init.rb
|
@ -1,13 +1,15 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'fileutils'
|
||||
require 'gpgme'
|
||||
|
||||
FileUtils.rm_rf("#{Dir.home}/.config/mpw")
|
||||
FileUtils.rm_rf("#{Dir.home}/.gnupg")
|
||||
|
||||
param = ''
|
||||
param << '<GnupgKeyParms format="internal">' + "\n"
|
||||
param << "Key-Type: RSA\n"
|
||||
param << "Key-Length: 2048\n"
|
||||
param << "Key-Length: 512\n"
|
||||
param << "Subkey-Type: ELG-E\n"
|
||||
param << "Subkey-Length: 2048\n"
|
||||
param << "Subkey-Length: 512\n"
|
||||
param << "Name-Real: test\n"
|
||||
param << "Name-Comment: test\n"
|
||||
param << "Name-Email: test2@example.com\n"
|
||||
|
|
256
test/test_cli.rb
Normal file
256
test/test_cli.rb
Normal file
|
@ -0,0 +1,256 @@
|
|||
require 'i18n'
|
||||
require 'test/unit'
|
||||
|
||||
class TestConfig < Test::Unit::TestCase
|
||||
def setup
|
||||
if defined?(I18n.enforce_available_locales)
|
||||
I18n.enforce_available_locales = true
|
||||
end
|
||||
|
||||
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
||||
I18n.load_path = ["#{File.expand_path('../../i18n', __FILE__)}/en.yml"]
|
||||
I18n.locale = :en
|
||||
|
||||
@password = 'password'
|
||||
@fixtures = YAML.load_file('./test/files/fixtures.yml')
|
||||
@gpg_key = 'test@example.com'
|
||||
end
|
||||
|
||||
def test_00_init_config
|
||||
output = %x(
|
||||
echo "#{@password}\n#{@password}" | mpw config \
|
||||
--init #{@gpg_key} \
|
||||
2>/dev/null
|
||||
)
|
||||
assert_match(I18n.t('form.setup_config.valid'), output)
|
||||
assert_match(I18n.t('form.setup_gpg_key.valid'), output)
|
||||
end
|
||||
|
||||
def test_01_add_item
|
||||
data = @fixtures['add']
|
||||
|
||||
output = %x(
|
||||
echo #{@password} | mpw add \
|
||||
--url #{data['url']} \
|
||||
--user #{data['user']} \
|
||||
--comment '#{data['comment']}' \
|
||||
--group #{data['group']} \
|
||||
--random \
|
||||
2>/dev/null
|
||||
)
|
||||
assert_match(I18n.t('form.add_item.valid'), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list 2>/dev/null)
|
||||
assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
|
||||
assert_match(data['user'], output)
|
||||
assert_match(data['comment'], output)
|
||||
assert_match(data['group'], output)
|
||||
end
|
||||
|
||||
def test_02_search
|
||||
data = @fixtures['add']
|
||||
|
||||
output = %x(echo #{@password} | mpw list --group #{data['group']} 2>/dev/null)
|
||||
assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --pattern #{data['host']} 2>/dev/null)
|
||||
assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --pattern #{data['comment']} 2>/dev/null)
|
||||
assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --group R1Pmfbp626TFpjlr 2>/dev/null)
|
||||
assert_match(I18n.t('display.nothing'), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --pattern h1IfnKqamaGM9oEX 2>/dev/null)
|
||||
assert_match(I18n.t('display.nothing'), output)
|
||||
end
|
||||
|
||||
def test_03_update_item
|
||||
data = @fixtures['update']
|
||||
|
||||
output = %x(
|
||||
echo #{@password} | mpw update \
|
||||
-p #{@fixtures['add']['host']} \
|
||||
--url #{data['url']} \
|
||||
--user #{data['user']} \
|
||||
--comment '#{data['comment']}' \
|
||||
--new-group #{data['group']} \
|
||||
2>/dev/null
|
||||
)
|
||||
assert_match(I18n.t('form.update_item.valid'), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list 2>/dev/null)
|
||||
assert_match(%r{#{data['protocol']}://.+#{data['host']}.+:#{data['port']}}, output)
|
||||
assert_match(data['user'], output)
|
||||
assert_match(data['comment'], output)
|
||||
assert_match(data['group'], output)
|
||||
end
|
||||
|
||||
def test_04_delete_item
|
||||
output = %x(
|
||||
echo "#{@password}\ny" | mpw delete \
|
||||
-p #{@fixtures['update']['host']} \
|
||||
2>/dev/null
|
||||
)
|
||||
assert_match(I18n.t('form.delete_item.valid'), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list 2>/dev/null)
|
||||
assert_match(I18n.t('display.nothing'), output)
|
||||
end
|
||||
|
||||
def test_05_import_export
|
||||
file_import = './test/files/fixtures-import.yml'
|
||||
file_export = '/tmp/test-mpw.yml'
|
||||
|
||||
output = %x(echo #{@password} | mpw import --file #{file_import} 2>/dev/null)
|
||||
assert_match(I18n.t('form.import.valid', file: file_import), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw export --file #{file_export} 2>/dev/null)
|
||||
assert_match(I18n.t('form.export.valid', file: file_export), output)
|
||||
assert(File.exist?(file_export))
|
||||
assert_equal(YAML.load_file(file_export).length, 2)
|
||||
|
||||
YAML.load_file(file_import).each_value do |import|
|
||||
error = true
|
||||
|
||||
YAML.load_file(file_export).each_value do |export|
|
||||
next if import['url'] != export['url']
|
||||
|
||||
%w[user group password protocol port otp_key comment].each do |key|
|
||||
assert_equal(import[key].to_s, export[key].to_s)
|
||||
end
|
||||
|
||||
error = false
|
||||
break
|
||||
end
|
||||
|
||||
assert(!error)
|
||||
end
|
||||
end
|
||||
|
||||
def test_06_copy
|
||||
data = YAML.load_file('./test/files/fixtures-import.yml')[2]
|
||||
|
||||
output = %x(
|
||||
echo "#{@password}\np\nq" | mpw copy \
|
||||
--disable-clipboard \
|
||||
-p #{data['host']} \
|
||||
2>/dev/null
|
||||
)
|
||||
assert_match(data['password'], output)
|
||||
end
|
||||
|
||||
def test_07_setup_wallet
|
||||
gpg_key = 'test2@example.com'
|
||||
|
||||
output = %x(echo #{@password} | mpw wallet --add-gpg-key #{gpg_key} 2>/dev/null)
|
||||
assert_match(I18n.t('form.add_key.valid'), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw wallet --list-keys 2>/dev/null)
|
||||
assert_match("| #{@gpg_key}", output)
|
||||
assert_match("| #{gpg_key}", output)
|
||||
|
||||
output = %x(echo #{@password} | mpw wallet --delete-gpg-key #{gpg_key} 2>/dev/null)
|
||||
assert_match(I18n.t('form.delete_key.valid'), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw wallet --list-keys 2>/dev/null)
|
||||
assert_match("| #{@gpg_key}", output)
|
||||
assert_no_match(/\| #{gpg_key}/, output)
|
||||
|
||||
output = %x(mpw wallet)
|
||||
assert_match('| default', output)
|
||||
|
||||
output = %x(mpw wallet --path '.')
|
||||
assert_match(I18n.t('form.set_wallet_path.valid'), output)
|
||||
|
||||
output = %x(mpw config)
|
||||
assert_match(%r{path_wallet_default.+\| #{Dir.pwd}/default.mpw}, output)
|
||||
assert(File.exist?("#{Dir.pwd}/default.mpw"))
|
||||
|
||||
output = %x(mpw wallet)
|
||||
assert_match('default', output)
|
||||
|
||||
output = %x(mpw wallet --default-path)
|
||||
assert_match(I18n.t('form.set_wallet_path.valid'), output)
|
||||
|
||||
output = %x(mpw config)
|
||||
assert_no_match(/path_wallet_default/, output)
|
||||
end
|
||||
|
||||
def test_08_setup_config
|
||||
gpg_key = 'test2@example.com'
|
||||
gpg_exe = '/usr/bin/gpg2'
|
||||
wallet_dir = '/tmp'
|
||||
length = 24
|
||||
wallet = 'work'
|
||||
|
||||
output = %x(
|
||||
mpw config \
|
||||
--gpg-exe #{gpg_exe} \
|
||||
--key #{gpg_key} \
|
||||
--enable-pinmode \
|
||||
--disable-alpha \
|
||||
--disable-special-chars \
|
||||
--disable-numeric \
|
||||
--length #{length} \
|
||||
--wallet-dir #{wallet_dir} \
|
||||
--default-wallet #{wallet}
|
||||
)
|
||||
assert_match(I18n.t('form.set_config.valid'), output)
|
||||
|
||||
output = %x(mpw config)
|
||||
assert_match(/gpg_key.+\| #{gpg_key}/, output)
|
||||
assert_match(/gpg_exe.+\| #{gpg_exe}/, output)
|
||||
assert_match(/pinmode.+\| true/, output)
|
||||
assert_match(/default_wallet.+\| #{wallet}/, output)
|
||||
assert_match(/wallet_dir.+\| #{wallet_dir}/, output)
|
||||
assert_match(/password_length.+\| #{length}/, output)
|
||||
%w[numeric alpha special].each do |k|
|
||||
assert_match(/password_#{k}.+\| false/, output)
|
||||
end
|
||||
|
||||
output = %x(
|
||||
mpw config \
|
||||
--gpg-exe '' \
|
||||
--key #{@gpg_key} \
|
||||
--alpha \
|
||||
--special-chars \
|
||||
--numeric \
|
||||
--disable-pinmode
|
||||
)
|
||||
assert_match(I18n.t('form.set_config.valid'), output)
|
||||
|
||||
output = %x(mpw config)
|
||||
assert_match(/gpg_key.+\| #{@gpg_key}/, output)
|
||||
assert_match(/pinmode.+\| false/, output)
|
||||
%w[numeric alpha special].each do |k|
|
||||
assert_match(/password_#{k}.+\| true/, output)
|
||||
end
|
||||
end
|
||||
|
||||
def test_09_generate_password
|
||||
length = 24
|
||||
|
||||
output = %x(
|
||||
mpw genpwd \
|
||||
--length #{length} \
|
||||
--alpha
|
||||
)
|
||||
assert_match(/[a-zA-Z]{#{length}}/, output)
|
||||
|
||||
output = %x(
|
||||
mpw genpwd \
|
||||
--length #{length} \
|
||||
--numeric
|
||||
)
|
||||
assert_match(/[0-9]{#{length}}/, output)
|
||||
|
||||
output = %x(
|
||||
mpw genpwd \
|
||||
--length #{length} \
|
||||
--special-chars
|
||||
)
|
||||
assert_no_match(/[a-zA-Z0-9]/, output)
|
||||
end
|
||||
end
|
|
@ -1,40 +1,79 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'mpw/config'
|
||||
require 'test/unit'
|
||||
require 'locale'
|
||||
require 'i18n'
|
||||
|
||||
class TestConfig < Test::Unit::TestCase
|
||||
def setup
|
||||
lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
|
||||
|
||||
if defined?(I18n.enforce_available_locales)
|
||||
I18n.enforce_available_locales = true
|
||||
end
|
||||
|
||||
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
||||
I18n.load_path = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
|
||||
I18n.default_locale = :en
|
||||
I18n.locale = lang.to_sym
|
||||
end
|
||||
def setup
|
||||
lang = Locale::Tag.parse(ENV['LANG']).to_simple.to_s[0..1]
|
||||
|
||||
def test_00_config
|
||||
data = { key: 'test@example.com',
|
||||
lang: 'en',
|
||||
wallet_dir: '/tmp/test',
|
||||
gpg_exe: '',
|
||||
}
|
||||
if defined?(I18n.enforce_available_locales)
|
||||
I18n.enforce_available_locales = true
|
||||
end
|
||||
|
||||
@config = MPW::Config.new
|
||||
@config.setup(data[:key], data[:lang], data[:wallet_dir], data[:gpg_exe])
|
||||
@config.load_config
|
||||
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
||||
I18n.load_path = Dir["#{File.expand_path('../../i18n', __FILE__)}/*.yml"]
|
||||
I18n.default_locale = :en
|
||||
I18n.locale = lang.to_sym
|
||||
end
|
||||
|
||||
data.each do |k,v|
|
||||
assert_equal(v, @config.send(k))
|
||||
end
|
||||
def test_00_config
|
||||
data = {
|
||||
gpg_key: 'test@example.com',
|
||||
lang: 'en',
|
||||
wallet_dir: '/tmp/test',
|
||||
gpg_exe: ''
|
||||
}
|
||||
|
||||
@config.setup_gpg_key('password', 'test@example.com', 2048)
|
||||
assert(@config.check_gpg_key?)
|
||||
end
|
||||
@config = MPW::Config.new
|
||||
@config.setup(data)
|
||||
@config.load_config
|
||||
|
||||
data.each do |k, v|
|
||||
assert_equal(v, @config.send(k))
|
||||
end
|
||||
|
||||
@config.setup_gpg_key('password', 'test@example.com', 2048)
|
||||
assert(@config.check_gpg_key?)
|
||||
end
|
||||
|
||||
def test_01_password
|
||||
data = {
|
||||
pwd_alpha: false,
|
||||
pwd_numeric: false,
|
||||
pwd_special: true,
|
||||
pwd_length: 32
|
||||
}
|
||||
|
||||
@config = MPW::Config.new
|
||||
@config.load_config
|
||||
|
||||
assert_equal(@config.password[:length], 16)
|
||||
assert(@config.password[:alpha])
|
||||
assert(@config.password[:numeric])
|
||||
assert(!@config.password[:special])
|
||||
|
||||
@config.setup(data)
|
||||
@config.load_config
|
||||
|
||||
assert_equal(@config.password[:length], data[:pwd_length])
|
||||
assert(!@config.password[:alpha])
|
||||
assert(!@config.password[:numeric])
|
||||
assert(@config.password[:special])
|
||||
end
|
||||
|
||||
def test_02_wallet_paths
|
||||
new_path = '/tmp/mpw-test'
|
||||
|
||||
@config = MPW::Config.new
|
||||
@config.load_config
|
||||
|
||||
assert(!@config.wallet_paths['default'])
|
||||
|
||||
@config.set_wallet_path(new_path, 'default')
|
||||
assert_equal(@config.wallet_paths['default'], new_path)
|
||||
|
||||
@config.set_wallet_path('default', 'default')
|
||||
assert(!@config.wallet_paths['default'])
|
||||
end
|
||||
end
|
||||
|
|
79
test/test_import.rb
Normal file
79
test/test_import.rb
Normal file
|
@ -0,0 +1,79 @@
|
|||
require 'i18n'
|
||||
require 'test/unit'
|
||||
|
||||
class TestImport < Test::Unit::TestCase
|
||||
def setup
|
||||
if defined?(I18n.enforce_available_locales)
|
||||
I18n.enforce_available_locales = true
|
||||
end
|
||||
|
||||
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
||||
I18n.load_path = ["#{File.expand_path('../../i18n', __FILE__)}/en.yml"]
|
||||
I18n.locale = :en
|
||||
|
||||
@password = 'password'
|
||||
end
|
||||
|
||||
def test_00_import_mpw_old
|
||||
file = './test/files/import-mpw_old.txt'
|
||||
format = 'mpw_old'
|
||||
|
||||
output = %x(
|
||||
mpw import \
|
||||
--file #{file} \
|
||||
--format #{format} \
|
||||
--wallet #{format}
|
||||
)
|
||||
assert_match(I18n.t('form.import.valid'), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --group Bank --wallet #{format})
|
||||
assert_match(%r{http://.*fric\.com.*12345.*Fric money money}, output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --group Cloud --wallet #{format})
|
||||
assert_match(%r{ssh://.*fric\.com.*:4333.*username.*bastion}, output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --wallet #{format})
|
||||
assert_match(/server\.com.*My little server/, output)
|
||||
end
|
||||
|
||||
def test_01_import_gorilla
|
||||
file = './test/files/import-gorilla.txt'
|
||||
format = 'gorilla'
|
||||
|
||||
output = %x(
|
||||
mpw import \
|
||||
--file #{file} \
|
||||
--format #{format} \
|
||||
--wallet #{format}
|
||||
)
|
||||
assert_match(I18n.t('form.import.valid'), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --group Bank --wallet #{format})
|
||||
assert_match(%r{http://.*fric\.com.*12345.*Fric money money}, output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --group Cloud --wallet #{format})
|
||||
assert_match(%r{ssh://.*fric\.com.*:4333.*username.*bastion}, output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --wallet #{format})
|
||||
assert_match(/server\.com.*My little server/, output)
|
||||
end
|
||||
|
||||
def test_02_import_keepass
|
||||
file = './test/files/import-keepass.txt'
|
||||
format = 'keepass'
|
||||
|
||||
output = %x(
|
||||
mpw import \
|
||||
--file #{file} \
|
||||
--format #{format} \
|
||||
--wallet #{format}
|
||||
)
|
||||
assert_match(I18n.t('form.import.valid'), output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --group 'Racine/Cloud' --wallet #{format})
|
||||
assert_match(/localhost\.local.*wesh.*GAFAM/, output)
|
||||
|
||||
output = %x(echo #{@password} | mpw list --wallet #{format})
|
||||
assert_match(%r{http://.*bank\.com.*123456.*Bank My little bank}, output)
|
||||
end
|
||||
end
|
|
@ -1,172 +1,165 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'mpw/item'
|
||||
require 'test/unit'
|
||||
require 'yaml'
|
||||
|
||||
|
||||
class TestItem < Test::Unit::TestCase
|
||||
def setup
|
||||
@fixture_file = 'test/files/fixtures.yml'
|
||||
@fixtures = YAML.load_file(@fixture_file)
|
||||
|
||||
if defined?(I18n.enforce_available_locales)
|
||||
I18n.enforce_available_locales = false
|
||||
end
|
||||
def setup
|
||||
if defined?(I18n.enforce_available_locales)
|
||||
I18n.enforce_available_locales = false
|
||||
end
|
||||
|
||||
I18n.load_path = Dir['./i18n/cli/*.yml']
|
||||
I18n.default_locale = :en
|
||||
I18n.load_path = Dir['./i18n/cli/*.yml']
|
||||
I18n.default_locale = :en
|
||||
|
||||
@fixtures = YAML.load_file('./test/files/fixtures.yml')
|
||||
end
|
||||
|
||||
puts
|
||||
end
|
||||
def test_00_add_without_name
|
||||
assert_raise(RuntimeError) { MPW::Item.new }
|
||||
end
|
||||
|
||||
def test_00_add_without_name
|
||||
assert_raise(RuntimeError){MPW::Item.new}
|
||||
end
|
||||
def test_01_add
|
||||
data = {
|
||||
group: @fixtures['add']['group'],
|
||||
user: @fixtures['add']['user'],
|
||||
url: @fixtures['add']['url'],
|
||||
comment: @fixtures['add']['comment']
|
||||
}
|
||||
|
||||
def test_01_add_new
|
||||
data = { group: @fixtures['add_new']['group'],
|
||||
host: @fixtures['add_new']['host'],
|
||||
protocol: @fixtures['add_new']['protocol'],
|
||||
user: @fixtures['add_new']['user'],
|
||||
port: @fixtures['add_new']['port'],
|
||||
comment: @fixtures['add_new']['comment'],
|
||||
}
|
||||
|
||||
item = MPW::Item.new(data)
|
||||
item = MPW::Item.new(data)
|
||||
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
|
||||
assert_equal(@fixtures['add_new']['group'], item.group)
|
||||
assert_equal(@fixtures['add_new']['host'], item.host)
|
||||
assert_equal(@fixtures['add_new']['protocol'], item.protocol)
|
||||
assert_equal(@fixtures['add_new']['user'], item.user)
|
||||
assert_equal(@fixtures['add_new']['port'].to_i, item.port)
|
||||
assert_equal(@fixtures['add_new']['comment'], item.comment)
|
||||
end
|
||||
assert_equal(@fixtures['add']['url'], item.url)
|
||||
assert_equal(@fixtures['add']['group'], item.group)
|
||||
assert_equal(@fixtures['add']['host'], item.host)
|
||||
assert_equal(@fixtures['add']['protocol'], item.protocol)
|
||||
assert_equal(@fixtures['add']['user'], item.user)
|
||||
assert_equal(@fixtures['add']['port'].to_i, item.port)
|
||||
assert_equal(@fixtures['add']['comment'], item.comment)
|
||||
end
|
||||
|
||||
def test_02_add_existing
|
||||
data = { id: @fixtures['add_existing']['id'],
|
||||
group: @fixtures['add_existing']['group'],
|
||||
host: @fixtures['add_existing']['host'],
|
||||
protocol: @fixtures['add_existing']['protocol'],
|
||||
user: @fixtures['add_existing']['user'],
|
||||
port: @fixtures['add_existing']['port'],
|
||||
comment: @fixtures['add_existing']['comment'],
|
||||
created: @fixtures['add_existing']['created'],
|
||||
}
|
||||
def test_02_import
|
||||
data = {
|
||||
id: @fixtures['import']['id'],
|
||||
group: @fixtures['import']['group'],
|
||||
user: @fixtures['import']['user'],
|
||||
url: @fixtures['import']['url'],
|
||||
comment: @fixtures['import']['comment'],
|
||||
created: @fixtures['import']['created']
|
||||
}
|
||||
|
||||
item = MPW::Item.new(data)
|
||||
item = MPW::Item.new(data)
|
||||
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
|
||||
assert_equal(@fixtures['add_existing']['id'], item.id)
|
||||
assert_equal(@fixtures['add_existing']['group'], item.group)
|
||||
assert_equal(@fixtures['add_existing']['host'], item.host)
|
||||
assert_equal(@fixtures['add_existing']['protocol'], item.protocol)
|
||||
assert_equal(@fixtures['add_existing']['user'], item.user)
|
||||
assert_equal(@fixtures['add_existing']['port'].to_i, item.port)
|
||||
assert_equal(@fixtures['add_existing']['comment'], item.comment)
|
||||
assert_equal(@fixtures['add_existing']['created'], item.created)
|
||||
end
|
||||
assert_equal(@fixtures['import']['id'], item.id)
|
||||
assert_equal(@fixtures['import']['url'], item.url)
|
||||
assert_equal(@fixtures['import']['group'], item.group)
|
||||
assert_equal(@fixtures['import']['host'], item.host)
|
||||
assert_equal(@fixtures['import']['protocol'], item.protocol)
|
||||
assert_equal(@fixtures['import']['user'], item.user)
|
||||
assert_equal(@fixtures['import']['port'].to_i, item.port)
|
||||
assert_equal(@fixtures['import']['comment'], item.comment)
|
||||
assert_equal(@fixtures['import']['created'], item.created)
|
||||
end
|
||||
|
||||
def test_03_update
|
||||
data = { group: @fixtures['add_new']['group'],
|
||||
host: @fixtures['add_new']['host'],
|
||||
protocol: @fixtures['add_new']['protocol'],
|
||||
user: @fixtures['add_new']['user'],
|
||||
port: @fixtures['add_new']['port'],
|
||||
comment: @fixtures['add_new']['comment'],
|
||||
}
|
||||
|
||||
item = MPW::Item.new(data)
|
||||
def test_03_update
|
||||
data = {
|
||||
group: @fixtures['add']['group'],
|
||||
user: @fixtures['add']['user'],
|
||||
url: @fixtures['add']['url'],
|
||||
comment: @fixtures['add']['comment']
|
||||
}
|
||||
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
item = MPW::Item.new(data)
|
||||
|
||||
created = item.created
|
||||
last_edit = item.last_edit
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
|
||||
data = { group: @fixtures['update']['group'],
|
||||
host: @fixtures['update']['host'],
|
||||
protocol: @fixtures['update']['protocol'],
|
||||
user: @fixtures['update']['user'],
|
||||
port: @fixtures['update']['port'],
|
||||
comment: @fixtures['update']['comment'],
|
||||
}
|
||||
|
||||
sleep(1)
|
||||
assert(item.update(data))
|
||||
created = item.created
|
||||
last_edit = item.last_edit
|
||||
|
||||
assert(!item.empty?)
|
||||
data = {
|
||||
group: @fixtures['update']['group'],
|
||||
user: @fixtures['update']['user'],
|
||||
url: @fixtures['update']['url'],
|
||||
comment: @fixtures['update']['comment']
|
||||
}
|
||||
|
||||
assert_equal(@fixtures['update']['group'], item.group)
|
||||
assert_equal(@fixtures['update']['host'], item.host)
|
||||
assert_equal(@fixtures['update']['protocol'], item.protocol)
|
||||
assert_equal(@fixtures['update']['user'], item.user)
|
||||
assert_equal(@fixtures['update']['port'].to_i, item.port)
|
||||
assert_equal(@fixtures['update']['comment'], item.comment)
|
||||
sleep(1)
|
||||
assert(item.update(data))
|
||||
|
||||
assert_equal(created, item.created)
|
||||
assert_not_equal(last_edit, item.last_edit)
|
||||
end
|
||||
assert(!item.empty?)
|
||||
|
||||
def test_05_update_one_element
|
||||
data = { group: @fixtures['add_new']['group'],
|
||||
host: @fixtures['add_new']['host'],
|
||||
protocol: @fixtures['add_new']['protocol'],
|
||||
user: @fixtures['add_new']['user'],
|
||||
port: @fixtures['add_new']['port'],
|
||||
comment: @fixtures['add_new']['comment'],
|
||||
}
|
||||
|
||||
item = MPW::Item.new(data)
|
||||
assert_equal(@fixtures['update']['url'], item.url)
|
||||
assert_equal(@fixtures['update']['group'], item.group)
|
||||
assert_equal(@fixtures['update']['host'], item.host)
|
||||
assert_equal(@fixtures['update']['protocol'], item.protocol)
|
||||
assert_equal(@fixtures['update']['user'], item.user)
|
||||
assert_equal(@fixtures['update']['port'].to_i, item.port)
|
||||
assert_equal(@fixtures['update']['comment'], item.comment)
|
||||
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
assert_equal(created, item.created)
|
||||
assert_not_equal(last_edit, item.last_edit)
|
||||
end
|
||||
|
||||
last_edit = item.last_edit
|
||||
def test_05_update_one_element
|
||||
data = {
|
||||
group: @fixtures['add']['group'],
|
||||
user: @fixtures['add']['user'],
|
||||
url: @fixtures['add']['url'],
|
||||
comment: @fixtures['add']['comment']
|
||||
}
|
||||
|
||||
sleep(1)
|
||||
assert(item.update({comment: @fixtures['update']['comment']}))
|
||||
item = MPW::Item.new(data)
|
||||
|
||||
assert_equal(@fixtures['add_new']['group'], item.group)
|
||||
assert_equal(@fixtures['add_new']['host'], item.host)
|
||||
assert_equal(@fixtures['add_new']['protocol'], item.protocol)
|
||||
assert_equal(@fixtures['add_new']['user'], item.user)
|
||||
assert_equal(@fixtures['add_new']['port'].to_i, item.port)
|
||||
assert_equal(@fixtures['update']['comment'], item.comment)
|
||||
|
||||
assert_not_equal(last_edit, item.last_edit)
|
||||
end
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
|
||||
def test_05_delete
|
||||
data = { group: @fixtures['add_new']['group'],
|
||||
host: @fixtures['add_new']['host'],
|
||||
protocol: @fixtures['add_new']['protocol'],
|
||||
user: @fixtures['add_new']['user'],
|
||||
port: @fixtures['add_new']['port'],
|
||||
comment: @fixtures['add_new']['comment'],
|
||||
}
|
||||
|
||||
item = MPW::Item.new(data)
|
||||
last_edit = item.last_edit
|
||||
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
sleep(1)
|
||||
item.update(comment: @fixtures['update']['comment'])
|
||||
|
||||
item.delete
|
||||
assert(!item.nil?)
|
||||
assert(item.empty?)
|
||||
assert_equal(@fixtures['add']['url'], item.url)
|
||||
assert_equal(@fixtures['add']['group'], item.group)
|
||||
assert_equal(@fixtures['add']['host'], item.host)
|
||||
assert_equal(@fixtures['add']['protocol'], item.protocol)
|
||||
assert_equal(@fixtures['add']['user'], item.user)
|
||||
assert_equal(@fixtures['add']['port'].to_i, item.port)
|
||||
assert_equal(@fixtures['update']['comment'], item.comment)
|
||||
|
||||
assert_equal(nil, item.id)
|
||||
assert_equal(nil, item.group)
|
||||
assert_equal(nil, item.host)
|
||||
assert_equal(nil, item.protocol)
|
||||
assert_equal(nil, item.user)
|
||||
assert_equal(nil, item.port)
|
||||
assert_equal(nil, item.comment)
|
||||
assert_equal(nil, item.created)
|
||||
end
|
||||
end
|
||||
assert_not_equal(last_edit, item.last_edit)
|
||||
end
|
||||
|
||||
def test_05_delete
|
||||
data = {
|
||||
group: @fixtures['add']['group'],
|
||||
user: @fixtures['add']['user'],
|
||||
url: @fixtures['add']['url'],
|
||||
comment: @fixtures['add']['comment']
|
||||
}
|
||||
|
||||
item = MPW::Item.new(data)
|
||||
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
|
||||
item.delete
|
||||
assert(!item.nil?)
|
||||
assert(item.empty?)
|
||||
|
||||
assert_equal(nil, item.id)
|
||||
assert_equal(nil, item.url)
|
||||
assert_equal(nil, item.group)
|
||||
assert_equal(nil, item.host)
|
||||
assert_equal(nil, item.protocol)
|
||||
assert_equal(nil, item.user)
|
||||
assert_equal(nil, item.port)
|
||||
assert_equal(nil, item.comment)
|
||||
assert_equal(nil, item.created)
|
||||
end
|
||||
end
|
||||
|
|
204
test/test_mpw.rb
204
test/test_mpw.rb
|
@ -1,140 +1,130 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'mpw/mpw'
|
||||
require 'mpw/item'
|
||||
require 'test/unit'
|
||||
require 'yaml'
|
||||
require 'csv'
|
||||
|
||||
|
||||
class TestMPW < Test::Unit::TestCase
|
||||
def setup
|
||||
fixture_file = './test/files/fixtures.yml'
|
||||
def setup
|
||||
wallet_file = 'default.gpg'
|
||||
key = 'test@example.com'
|
||||
password = 'password'
|
||||
|
||||
wallet_file = 'default.gpg'
|
||||
key = 'test@example.com'
|
||||
password = 'password'
|
||||
if defined?(I18n.enforce_available_locales)
|
||||
I18n.enforce_available_locales = false
|
||||
end
|
||||
|
||||
if defined?(I18n.enforce_available_locales)
|
||||
I18n.enforce_available_locales = false
|
||||
end
|
||||
@mpw = MPW::MPW.new(key, wallet_file, password)
|
||||
@fixtures = YAML.load_file('./test/files/fixtures.yml')
|
||||
end
|
||||
|
||||
@mpw = MPW::MPW.new(key, wallet_file, password)
|
||||
@fixtures = YAML.load_file(fixture_file)
|
||||
end
|
||||
|
||||
def test_00_decrypt_empty_file
|
||||
@mpw.read_data
|
||||
assert_equal(0, @mpw.list.length)
|
||||
end
|
||||
def test_00_decrypt_empty_file
|
||||
@mpw.read_data
|
||||
assert_equal(0, @mpw.list.length)
|
||||
end
|
||||
|
||||
def test_01_encrypt_empty_file
|
||||
@mpw.read_data
|
||||
@mpw.write_data
|
||||
end
|
||||
def test_01_encrypt_empty_file
|
||||
@mpw.read_data
|
||||
@mpw.write_data
|
||||
end
|
||||
|
||||
def test_02_add_item
|
||||
data = { group: @fixtures['add_new']['group'],
|
||||
host: @fixtures['add_new']['host'],
|
||||
protocol: @fixtures['add_new']['protocol'],
|
||||
user: @fixtures['add_new']['user'],
|
||||
port: @fixtures['add_new']['port'],
|
||||
comment: @fixtures['add_new']['comment'],
|
||||
}
|
||||
|
||||
item = MPW::Item.new(data)
|
||||
def test_02_add_item
|
||||
data = {
|
||||
group: @fixtures['add']['group'],
|
||||
user: @fixtures['add']['user'],
|
||||
url: @fixtures['add']['url'],
|
||||
comment: @fixtures['add']['comment']
|
||||
}
|
||||
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
item = MPW::Item.new(data)
|
||||
|
||||
@mpw.read_data
|
||||
@mpw.add(item)
|
||||
@mpw.set_password(item.id, @fixtures['add_new']['password'])
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
|
||||
assert_equal(1, @mpw.list.length)
|
||||
@mpw.read_data
|
||||
@mpw.add(item)
|
||||
@mpw.set_password(item.id, @fixtures['add']['password'])
|
||||
|
||||
item = @mpw.list[0]
|
||||
@fixtures['add_new'].each do |k,v|
|
||||
if k == 'password'
|
||||
assert_equal(v, @mpw.get_password(item.id))
|
||||
else
|
||||
assert_equal(v, item.send(k).to_s)
|
||||
end
|
||||
end
|
||||
assert_equal(1, @mpw.list.length)
|
||||
|
||||
@mpw.write_data
|
||||
end
|
||||
item = @mpw.list[0]
|
||||
@fixtures['add'].each do |k, v|
|
||||
if k == 'password'
|
||||
assert_equal(v, @mpw.get_password(item.id))
|
||||
else
|
||||
assert_equal(v, item.send(k).to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def test_03_decrypt_file
|
||||
@mpw.read_data
|
||||
assert_equal(1, @mpw.list.length)
|
||||
@mpw.write_data
|
||||
end
|
||||
|
||||
item = @mpw.list[0]
|
||||
@fixtures['add_new'].each do |k,v|
|
||||
if k == 'password'
|
||||
assert_equal(v, @mpw.get_password(item.id))
|
||||
else
|
||||
assert_equal(v, item.send(k).to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
def test_03_decrypt_file
|
||||
@mpw.read_data
|
||||
assert_equal(1, @mpw.list.length)
|
||||
|
||||
def test_04_delete_item
|
||||
@mpw.read_data
|
||||
item = @mpw.list[0]
|
||||
@fixtures['add'].each do |k, v|
|
||||
if k == 'password'
|
||||
assert_equal(v, @mpw.get_password(item.id))
|
||||
else
|
||||
assert_equal(v, item.send(k).to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal(1, @mpw.list.length)
|
||||
def test_04_delete_item
|
||||
@mpw.read_data
|
||||
assert_equal(1, @mpw.list.length)
|
||||
|
||||
@mpw.list.each do |item|
|
||||
item.delete
|
||||
end
|
||||
@mpw.list.each(&:delete)
|
||||
assert_equal(0, @mpw.list.length)
|
||||
|
||||
assert_equal(0, @mpw.list.length)
|
||||
@mpw.write_data
|
||||
end
|
||||
|
||||
@mpw.write_data
|
||||
end
|
||||
def test_05_search
|
||||
@mpw.read_data
|
||||
|
||||
def test_05_search
|
||||
@mpw.read_data
|
||||
@fixtures.each_value do |v|
|
||||
data = {
|
||||
group: v['group'],
|
||||
user: v['user'],
|
||||
url: v['url'],
|
||||
comment: v['comment']
|
||||
}
|
||||
|
||||
@fixtures.each_value do |v|
|
||||
data = { group: v['group'],
|
||||
host: v['host'],
|
||||
protocol: v['protocol'],
|
||||
user: v['user'],
|
||||
port: v['port'],
|
||||
comment: v['comment'],
|
||||
}
|
||||
|
||||
item = MPW::Item.new(data)
|
||||
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
|
||||
@mpw.add(item)
|
||||
@mpw.set_password(item.id, v['password'])
|
||||
end
|
||||
item = MPW::Item.new(data)
|
||||
|
||||
assert_equal(3, @mpw.list.length)
|
||||
assert_equal(1, @mpw.list(group: @fixtures['add_new']['group']).length)
|
||||
assert_equal(1, @mpw.list(pattern: 'existing').length)
|
||||
assert_equal(2, @mpw.list(pattern: 'host_[eu]').length)
|
||||
end
|
||||
assert(!item.nil?)
|
||||
assert(!item.empty?)
|
||||
|
||||
def test_06_add_gpg_key
|
||||
@mpw.read_data
|
||||
@mpw.add(item)
|
||||
@mpw.set_password(item.id, v['password'])
|
||||
end
|
||||
|
||||
@mpw.add_key('test2@example.com')
|
||||
assert_equal(2, @mpw.list_keys.length)
|
||||
assert_equal(3, @mpw.list.length)
|
||||
assert_equal(1, @mpw.list(group: @fixtures['add']['group']).length)
|
||||
assert_equal(1, @mpw.list(pattern: 'gogole').length)
|
||||
assert_equal(2, @mpw.list(pattern: 'example[2\.]').length)
|
||||
end
|
||||
|
||||
@mpw.write_data
|
||||
end
|
||||
def test_06_add_gpg_key
|
||||
@mpw.read_data
|
||||
|
||||
def test_07_delete_gpg_key
|
||||
@mpw.read_data
|
||||
assert_equal(2, @mpw.list_keys.length)
|
||||
@mpw.add_key('test2@example.com')
|
||||
assert_equal(2, @mpw.list_keys.length)
|
||||
|
||||
@mpw.delete_key('test2@example.com')
|
||||
assert_equal(1, @mpw.list_keys.length)
|
||||
@mpw.write_data
|
||||
end
|
||||
|
||||
@mpw.write_data
|
||||
end
|
||||
def test_07_delete_gpg_key
|
||||
@mpw.read_data
|
||||
assert_equal(2, @mpw.list_keys.length)
|
||||
|
||||
@mpw.delete_key('test2@example.com')
|
||||
assert_equal(1, @mpw.list_keys.length)
|
||||
|
||||
@mpw.write_data
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,31 +1,29 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'yaml'
|
||||
require 'test/unit'
|
||||
|
||||
class TestTranslate < Test::Unit::TestCase
|
||||
def test_00_check_translate
|
||||
missing = 0
|
||||
|
||||
Dir.glob('i18n/*.yml').each do |yaml|
|
||||
lang = File.basename(yaml, '.yml')
|
||||
translate = YAML.load_file(yaml)
|
||||
|
||||
`grep -r -o "I18n.t('.*')" bin/ lib/ | cut -d"'" -f2`.each_line do |line|
|
||||
begin
|
||||
t = translate[lang]
|
||||
line.strip.split('.').each do |v|
|
||||
t = t[v]
|
||||
end
|
||||
def test_00_check_translate
|
||||
missing = 0
|
||||
|
||||
assert(!t.to_s.empty?)
|
||||
rescue
|
||||
puts "#{lang}.#{line}"
|
||||
missing = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal(0, missing)
|
||||
end
|
||||
Dir.glob('i18n/*.yml').each do |yaml|
|
||||
lang = File.basename(yaml, '.yml')
|
||||
translate = YAML.load_file(yaml)
|
||||
|
||||
%x(grep -r -o "I18n.t('.*)" bin/ lib/ | cut -d"'" -f2).each_line do |line|
|
||||
begin
|
||||
t = translate[lang]
|
||||
line.strip.split('.').each do |v|
|
||||
t = t[v]
|
||||
end
|
||||
|
||||
assert(!t.to_s.empty?)
|
||||
rescue
|
||||
puts "#{lang}.#{line}"
|
||||
missing = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal(0, missing)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require_relative 'init.rb'
|
||||
require_relative 'test_config.rb'
|
||||
require_relative 'test_item.rb'
|
||||
require_relative 'test_mpw.rb'
|
||||
require_relative 'test_translate.rb'
|
Loading…
Add table
Reference in a new issue