pwss 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d81cc493cb6f1b3dc7f917cdfa7fcbb81c844af4
4
- data.tar.gz: 44c566b64b9fc0cf9b72f00a0396a9a72a400180
3
+ metadata.gz: f0d6de35d7d59295771fff8e0710f121b9fcaf3c
4
+ data.tar.gz: 15b6b84a494a01fdb2c94833481dc1bbc5c08818
5
5
  SHA512:
6
- metadata.gz: 33cbef0aa13c6f8cf89313fddb500a289917e4c0c1e380b77b9494b85eee0c446601091573d01525e11ac2c704ced7312283e7adb2e4b2c0c99873a27f315bcc
7
- data.tar.gz: aab0df3a4bff00ec6157e442d878706312209f4085ffa96cab604e7208d6bed0f3269f2e01724a03211df77b9cda74d1026349674643e8bb4649968380623370
6
+ metadata.gz: 2a6951a825180bdda38b6a26da7895bbb422862466cadfc1ea95703a050ae2c542ba2b61fb45a489c2ed3af83f6429843bc8dec6a668933acba6f305258b077e
7
+ data.tar.gz: 1ce2596579e801b3313ec443a98aac562ec94b3c2a933b59b3ad51bc35f1c8bdcd730c0c3795a0ff8318be528fb1a37177e6c0ec7b9288ea6fc35f9fe05a25e4
data/README.textile CHANGED
@@ -2,49 +2,72 @@ h1. Pwss
2
2
 
3
3
  A password manager in the spirit of "pws":https://github.com/janlelis/pws.
4
4
 
5
+ PWSS can store multiple entries in a file and, more important, it allows user to specify various types of entries (e.g., BankAccount) and different infos for each entry (e.g., URL for Internet passwords).
6
+
5
7
  Features:
6
8
 
7
- * Each entry stores: title, username, password, url, description
8
- * A file (or safe) can store many entries
9
- * The user can manage different files (safes)
9
+ * PWSS manages password files. A password file store passwords and other
10
+ sensitive information
11
+
12
+ * Entries in a password file can be of the following pre-defined types:
13
+ Entry, CreditCard, BankAccount, SoftwareLicense
14
+
15
+ * Information stored for each type:
16
+
17
+ - Entry: title, username, password, url, description
18
+ - CreditCard: title, issuer, name_on_card, card_number, valid_from,
19
+ valid_till, verification_number, pin, url, notes
20
+ - BankAccount: title, name, iban, url, description
21
+ - SoftwareLicense: title, version, license_number, licensed_to,
22
+ email, purchased_on
23
+
10
24
  * CRUD (create, read, update, delete) commands are available to
11
- operate on entries and safes
12
- * pwss can generate completely random passwords for you
13
- * Safes can be encrypted (to improve security)
14
- * Encrypted safes can be decrypted (for instance to batch process
15
- entries, to migrate to another tool, or to manually edit entries)
16
- * Entries are human readable and editable (when not encrypted)
25
+ operate on entries and password files
26
+
27
+ * The user can manage different password files (e.g., work, personal)
28
+ * Password files can be encrypted
29
+ * Encrypted password files can be decrypted, for instance, to batch process
30
+ entries, to migrate to another tool, or to manually edit entries
31
+ * Entries are human-readable (and editable), when the password file is not
32
+ encrypted
17
33
 
18
34
  h2. Installation
19
35
 
20
- Add this line to your application's Gemfile:
36
+ Type from the command line:
37
+
38
+ bc. $ gem install pwss
21
39
 
22
- bc. gem 'pwss'
40
+ h2. Quick Start
23
41
 
24
- And then execute:
42
+ Try the following:
25
43
 
26
- bc. $ bundle
44
+ bc. $ pwss init
45
+ $ pwss add
46
+ $ pwss get <title you gave with previous command>
27
47
 
28
- Or install it yourself as:
48
+ For some more information:
29
49
 
30
- bc. $ gem install pwss
50
+ bc. $ pwss help # get information from the command line
51
+ $ pwss -h # command syntax
31
52
 
32
- h2. Usage
53
+ h2. Detailed Instructions
33
54
 
34
- *Getting started.* @pwss@ stores passwords in a YAML file (also called "password safe" in the following), possibly encrypted.
55
+ *Getting started.* @pwss@ stores passwords in a YAML file (also called "safe" or "password file" in the following), possibly encrypted.
35
56
 
36
57
  A typical usage scenario is the following:
37
58
 
38
59
  # @pwss init@ will create a new encrypted password safe in @~/.pwss.yaml.enc@
39
60
  # @pwss add@ will add a new entry to the file
40
- # @pwss get entry@ will retrieve all entries whose *title* contains @entry@ and make the password of the chosen entry available in the clipboard
61
+ # @pwss get string@ will retrieve all entries whose *title* contains @string@ and make the password of the chosen entry available in the clipboard for a given period (the default is 30 seconds; the option @-w@ controls the amount of time the password is available)
41
62
 
42
- *Using multiple safes.* If you want to create multiple password safes or store the password safe in a non-standard location, use the @-f@ (@--filename@) option:
63
+ *Using multiple safes.* If you want to create multiple password files or store a password file in a non-standard location, use the @-f@ (@--filename@) option:
43
64
 
44
65
  # @pwss -f MYFILE init@
45
66
  # @pwss -f MYFILE add@
46
67
  # @pwss -f MYFILE get@
47
68
 
69
+ *Do not forget to use the extension @.enc@, if your password file to be encrypted.* (See "Encrypted and Plain files", below.)
70
+
48
71
  *Controlling how long passwords are made available* Use the @-w@ option to determine how long the password is made available in the clipboard. For instance:
49
72
 
50
73
  bc. $ pwss get my_email -w 3
@@ -53,7 +76,13 @@ will retrieve entry whose title is @my_email@ and make the password available in
53
76
 
54
77
  Use @0@ to keep the password in the clipboard till a key is pressed.
55
78
 
56
- *Automatically Generated Password* pwss can automatically generate passwords for entries which are added or updated, using the @-g LENGTH@ option, where @LENGTH@ is the length of the password to generate. If you want to generate password including only alphabetic and numeric characters, use the @-a@ option. *The passwords are made available in the clipboard, so that it can be used as needed*.
79
+ *Automatically Generated Password* pwss can automatically generate passwords for entries which are added or updated.
80
+
81
+ The generated passwords are random sequences of chars and symbols and no attempt is made to make them readable or simpler to remember. You can use the @-a@ option to limit the generator to use only digits and letters ([0-9a-zA-Z]): this is useful, for instance, if the website you're generating the password for does not accept certain classes of characters.
82
+
83
+ Use the @-g LENGTH@ option to determine the password length.
84
+
85
+ *The automatically generated password is made available in the clipboard, so that it can be used as needed*.
57
86
 
58
87
  For instance:
59
88
 
@@ -61,27 +90,25 @@ bc. $ pwss update my_email -g 10 -a -w 20
61
90
 
62
91
  will update the @my_email@ entry, by replacing the existing password with one of length @10@ automatically generated by @pwss@; the password contains only alphabetic characters and digits. The new password will be made available in the clipboard for @20@ seconds.
63
92
 
64
- *Encrypted and Plain Files.* @pwss@ works equally well with encrypted and plain files. More in details, the file extension determines whether @pwss@ tries to decrypt/encrypt the file or not. The @.enc@ extension tells @pwss@ that the file is encrypted; any other extension will tell @pwss@ to treat the file as plain text.
93
+ *Encrypted and Plain Files.* @pwss@ works equally well with encrypted and plain files. More in details, the file extension determines whether @pwss@ tries to decrypt/encrypt the file or not. Use @.enc@ extension to tell @pwss@ the file is encrypted; any other extension will tell @pwss@ to treat the file as plain text.
65
94
 
66
95
  For instance:
67
96
 
68
- bc. $ pwss -f a.yaml.enc init
97
+ bc. $ pwss init -f a.yaml.enc
69
98
 
70
- will store the password in the *encrypted* file @a.yaml.enc@.
99
+ will initialize an encrypted safe @a.yaml.enc@.
71
100
 
72
101
  By contrast,
73
102
 
74
- bc. $ pwss -f a.yaml get entry
103
+ bc. $ pwss init -f a.yaml
75
104
 
76
- will try to retrieve @entry@ from file @a.yaml@; the file is assumed to be in plain text by @pwss@ which will not ask for a master password, nor will try to decrypt it.
105
+ Encrypting sensitive information is a good idea. (Just in case you were looking for a witty statement.) However, if you use @pwss@ to store non-critical infomation, prefer to edit the password safe with a text editor, or use another application for managing encryption and decryption, using @pwss@ with the file in plain format might be more convenient.
77
106
 
78
- Encrypting important passwords is a good idea. (Just in case you were looking for a witty statement.) However, if you use @pwss@ to store non-critical infomation, prefer to edit the password safe with a text editor, or use another application for managing encryption and decryption, using @pwss@ with the file in plain format might be more convenient.
79
-
80
- *Moving from plain to encrypted.* Use the @encrypt@ and @decrypt@ commands at any time to move from the plain to the encrypted format.
107
+ *Moving from plain to encrypted.* You can use the @encrypt@ and @decrypt@ commands at any time to move from the plain to the encrypted format.
81
108
 
82
109
  bc. $ pwss -f YOURFILE encrypt
83
110
 
84
- will encrypt @YOURFILE@ while @decrypt@ will perform the opposite operation.
111
+ will encrypt @YOURFILE@, while @decrypt@ will perform the opposite operation.
85
112
 
86
113
  *Starting from an Existing File.* You can also start from an existing file, as long as it is an array of YAML records, each containing, at least, a @title@ and a @password@ field. (See next section, for the file structure.)
87
114
 
@@ -136,20 +163,29 @@ Notice that only @title@ and @password@ are required.
136
163
 
137
164
  h2. Changelog
138
165
 
166
+ * *Release 0.3.0*
167
+ ** internal refactoring: CLI parsing is now based on "Slop":https://github.com/leejarvis/slop. The documentation has been revised and should now be simpler to understand.
168
+ ** added some controls to avoid overwriting existing files (in particular: init, encrypt, and decrypt). The command is now less Unix-like, but I hope you will appreciate a bit more safety.
169
+
170
+ * *Release 0.2.0* (never really made it to the public -- use version 0.3.0)
171
+ ** it is now possible to add entries of various types (= with different fields). The supported types include: CreditCard, BankAccount, SoftwareLicense. Use the -e (--entry) option to specify the type of entry to add
172
+ ** an empty string can now be used to exit (instead of -1) when multiple matches are found
173
+
139
174
  * *Release 0.1.0*
140
175
  ** the update command now allows one to update the password or any other field of existing entries
141
176
  ** a simple password generator allows pwss to generate a random password
142
177
  ** most commands make the password of the selected entry available in the clipboard (useful, for instance, if you automatically generate a password)
143
178
  ** a destroy command allows one to delete an entry from a safe. Similar to get, all entries matching a query are shown. The user is then asked to select which entry has to be deleted or stop. User confirmation is required even in case of a single match.
144
179
 
145
- h2. License and Additional Disclaimer
180
+ h2. License
146
181
 
147
182
  Licensed under the terms of the MIT License.
148
183
 
184
+
149
185
  h2. Contributing
150
186
 
151
- 1. Fork it ( http://github.com/<my-github-username>/pwss/fork )
152
- 2. Create your feature branch (`git checkout -b my-new-feature`)
153
- 3. Commit your changes (`git commit -am 'Add some feature'`)
154
- 4. Push to the branch (`git push origin my-new-feature`)
187
+ 1. Fork it (http://github.com/<my-github-username>/pwss/fork )
188
+ 2. Create your feature branch (@git checkout -b my-new-feature@)
189
+ 3. Commit your changes (@git commit -am 'Add some feature'@)
190
+ 4. Push to the branch (@git push origin my-new-feature@)
155
191
  5. Create new Pull Request
data/bin/pwss CHANGED
@@ -1,45 +1,130 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'fileutils'
4
- require 'mercenary'
4
+ require 'slop'
5
5
  require 'date'
6
6
 
7
7
  require "pwss"
8
8
  require "pwss/version"
9
9
  require "pwss/cipher"
10
10
  require "pwss/entry"
11
+ require "pwss/credit_card"
12
+ require "pwss/bank_account"
13
+ require "pwss/software_license"
11
14
  require "pwss/fileops"
12
15
 
13
- Mercenary.program(:pwss) do |p|
14
- DEFAULT_FILENAME = File.join(Dir.home, ".pwss.yaml.enc")
16
+ # load filename and decrypt, if necessary
17
+ # return the filename as string and the password (in case you need to save)
18
+ def file2string filename
19
+ string = FileOps::load filename
20
+ if FileOps::encrypted? filename then
21
+ password = Cipher::ask_password
22
+ string = Cipher::decrypt string, password
23
+ end
24
+ [string, password]
25
+ end
26
+
27
+
28
+ version = Pwss::VERSION
29
+ man = <<EOS
30
+ NAME
31
+ pwss -- A command-line password manager
32
+
33
+ SYNOPSYS
34
+ pwss [-h|-v]
35
+ pwss command [options] [args]
36
+
37
+ DESCRIPTION
38
+ PWSS is a password manager, in the spirit of pws.
39
+
40
+ Features:
41
+
42
+ * PWSS manages password files. A password file store passwords and other
43
+ sensitive information
44
+
45
+ * Entries in a password file can be of the following pre-defined types:
46
+ Entry, CreditCard, BankAccount, SoftwareLicense
47
+
48
+ * Information stored for each type:
49
+
50
+ - Entry: title, username, password, url, description
51
+ - CreditCard: title, issuer, name_on_card, card_number, valid_from,
52
+ valid_till, verification_number, pin, url, notes
53
+ - BankAccount: title, name, iban, url, description
54
+ - SoftwareLicense: title, version, license_number, licensed_to,
55
+ email, purchased_on
56
+
57
+ * CRUD (create, read, update, delete) commands are available to
58
+ operate on entries and password files
59
+
60
+ * The user can manage different password files (e.g., work, personal)
61
+ * Password files can be encrypted
62
+ * Encrypted password files can be decrypted, for instance, to batch process
63
+ entries, to migrate to another tool, or to manually edit entries
64
+ * Entries are human-readable (and editable), when the password file is not
65
+ encrypted
66
+
67
+ EXAMPLES
68
+ pwss -h # get syntax of each command
69
+
70
+ # scenario
71
+ pwss init -f a.enc # generate an encrypted safe a.enc
72
+ pwss add -f a.enc -g 16 -a # add an entry (generating a password)
73
+ pwss get -f a.enc my secret account # find an entry
15
74
 
16
- p.version Pwss::VERSION
17
- p.description <<EOS
18
- PWSS is a password safe, in the spirit of pws
19
-
20
- Features:
21
-
22
- * Each entry stores: title, username, password, url, description
23
- * A file (or safe) can store many entries
24
- * The user can manage different files (safes)
25
- * CRUD (create, read, update, delete) commands are available to
26
- operate on entries and safes
27
- * Safes can be encrypted
28
- * Encrypted safes can be decrypted, for instance, to batch process
29
- entries, to migrate to another tool, or to manually edit entries
30
- * Entries are human-readable (and editable), when not encrypted
75
+ VERSION
76
+ This is version #{version}
77
+
78
+ LICENSE
79
+ MIT
80
+
81
+ SEE ALSO
82
+ pwss -h
83
+ https://github.com/avillafiorita/pwss
31
84
  EOS
32
85
 
33
- p.syntax "pwss <subcommand> [options] [args]"
34
- p.option 'filename', '-f FILE', '--filename FILE', 'Password file'
35
- p.option 'wait', '-w SECS', '--wait SECS', 'Number of seconds password is available in the clipboard (use 0 for interactive).'
36
86
 
37
- p.command(:init) do |c|
38
- c.syntax "init"
39
- c.description 'Init a new password file (= password safe)'
40
87
 
41
- c.action do |args, opts|
42
- filename = opts['filename'] || DEFAULT_FILENAME
88
+ #
89
+ # Main App Starts Here!
90
+ #
91
+ opts = Slop.parse :help => true do
92
+ # the default filename
93
+ DEFAULT_FILENAME = File.join(Dir.home, ".pwss.yaml.enc")
94
+ # the default number of seconds password is available in the clipboard
95
+ DEFAULT_SECS = 30
96
+
97
+ banner "pwss [-h|-v]\npwss command [options] [args]"
98
+
99
+ ##############################################################################
100
+ on "-v", "--version", 'Print version information' do
101
+ puts "pwss version #{version}"
102
+ end
103
+
104
+ ##############################################################################
105
+ command :help do
106
+ banner "pwss help"
107
+ description "Print detailed information about how to use pwss"
108
+
109
+ run do |_, _|
110
+ puts man
111
+ end
112
+ end
113
+
114
+ ##############################################################################
115
+ command :init do
116
+ banner "pwss init [options]"
117
+ description "Init a new password file"
118
+
119
+ on "-f", "--filename=", "Password file to create. Use extension '.enc' to encrypt it."
120
+
121
+ run do |opts, args|
122
+ filename = opts.to_hash[:filename] || DEFAULT_FILENAME
123
+
124
+ if File.exists?(filename)
125
+ puts "Error: file #{filename} already exists."
126
+ exit 1
127
+ end
43
128
 
44
129
  empty_safe = "# safe created on #{Date.today}\n"
45
130
 
@@ -58,12 +143,15 @@ EOS
58
143
  end
59
144
  end
60
145
 
61
- p.command(:list) do |c|
62
- c.syntax "list"
63
- c.description "List all entries in a safe"
64
-
65
- c.action do |args, opts|
66
- filename = opts['filename'] || DEFAULT_FILENAME
146
+ ##############################################################################
147
+ command :list do
148
+ banner "pwss list [options]"
149
+ description "List all entries of a file (e.g., to decrypt or batch process)"
150
+
151
+ on "-f", "--filename=", "Password file to use."
152
+
153
+ run do |opts, args|
154
+ filename = opts.to_hash[:filename] || DEFAULT_FILENAME
67
155
 
68
156
  string, _ = file2string filename
69
157
  entries = YAML::load(string) || Array.new
@@ -72,39 +160,55 @@ EOS
72
160
  end
73
161
  end
74
162
 
75
- p.command(:get) do |c|
76
- c.syntax "get string"
77
- c.description "Get password for entry matching <string> in the title field"
163
+ ##############################################################################
164
+ command :get do
165
+ banner "pwss get [options] string"
166
+ description "Get password for entry matching <string> in the title field"
167
+
168
+ on "-f", "--filename=", "Password file to use."
169
+ on "-w", "--wait=", "Seconds password is available in the clipboard (0 = interactive).", as: Integer
170
+
171
+ run do |opts, args|
172
+ filename = opts.to_hash[:filename] || DEFAULT_FILENAME
173
+ waiting = opts.to_hash[:wait] || DEFAULT_SECS
78
174
 
79
- c.action do |args, opts|
80
- filename = opts['filename'] || DEFAULT_FILENAME
81
- waiting = opts['wait'] ? opts['wait'].to_i : 30
82
-
83
175
  string, _ = file2string filename
84
176
  entries = YAML::load(string) || Array.new
85
177
 
86
178
  password = Pwss::get args.join(" "), entries
87
- Cipher.password_to_clipboard password, waiting
179
+ if password
180
+ Cipher.password_to_clipboard password, waiting
181
+ end
88
182
  end
89
183
  end
90
184
 
91
- p.command(:add) do |c|
92
- c.syntax "add"
93
- c.description "Add an entry to a password safe"
185
+
186
+ ##############################################################################
187
+ command :add do
188
+ banner "pwss add [options]"
189
+ description "Add an entry and copy its password in the clipboard"
190
+
191
+ on "-f", "--filename=", "Password file to use."
192
+ on "-w", "--wait=", "Seconds password is available in the clipboard (0 = interactive).", as: Integer
94
193
 
95
- c.option "generate", "-g LENGTH", "--generate LENGTH", "automatically generate a password of length LENGTH"
96
- c.option "alnum", "-a", "--alnum", "use only alphanumeric characters for the generated password"
194
+ on "-e", "--entry=", "Create an entry of type TYPE (Entry, CreditCard, BankAccount, SoftwareLicense).\n Default to 'Entry', which is good enough for websites credentials."
97
195
 
98
- c.action do |args, opts|
99
- filename = opts['filename'] || DEFAULT_FILENAME
100
- waiting = opts['wait'] ? opts['wait'].to_i : 30
101
- length = opts['generate'] ? opts['generate'].to_i : 0
196
+ on "-g", "--generate=", "Generate a random password of given length.", as: Integer
197
+ on "-a", "--alnum", "Use only alphanumeric chars for the randomly generated password."
198
+
199
+ run do |opts, args|
200
+ filename = opts.to_hash[:filename] || DEFAULT_FILENAME
201
+ waiting = opts.to_hash[:wait] || DEFAULT_SECS
202
+ length = opts.to_hash[:generate] || 0
203
+ type = opts.to_hash[:type] || "Entry"
204
+ alnum = opts.to_hash[:alnum]
102
205
 
103
206
  string, password = file2string filename
104
207
 
208
+ puts "Adding an entry of type: #{type}"
105
209
  # ask for a new entry
106
- pe = Pwss::Entry.new
107
- pe.ask length, opts['alnum']
210
+ pe = eval("Pwss::" + type).new
211
+ pe.ask length, alnum
108
212
 
109
213
  # add the entry to the safe
110
214
  entries = YAML::load(string) || Array.new
@@ -122,24 +226,30 @@ EOS
122
226
 
123
227
  puts "Entry added."
124
228
 
125
- # make password available in the clipboard
126
- Cipher.password_to_clipboard pe.entry["password"], waiting
229
+ # make password available in the clipboard, if there is a password to make available
230
+ if pe.entry["password"]
231
+ Cipher.password_to_clipboard pe.entry["password"], waiting
232
+ end
127
233
  end
128
234
  end
129
235
 
130
- p.command(:update) do |c|
131
- c.syntax "update a field of an existing entry"
132
- c.description "Update some field of an existing entry (default: password)"
236
+ ##############################################################################
237
+ command :update do
238
+ banner "pwss update [options] string"
239
+ description "Update given field of user-selected entry matching <string>"
133
240
 
134
- c.option 'field', '--field FIELD', 'Field to update (if not specified, update the password field)'
135
- c.option "generate", "-g LENGTH", "--generate LENGTH", "generate a password of length LENGTH"
136
- c.option "alnum", "-a", "--alnum", "use only alphabetic and numeric characters for the generated password"
241
+ on "-f", "--filename=", "Password file to use."
242
+ on "-w", "--wait=", "Seconds password is available in the clipboard (0 = interactive).", as: Integer
243
+ on "-g", "--generate=", "Generate a random password of given length.", as: Integer
244
+ on "-a", "--alnum", "Use only alphanumeric chars for the randomly generated password."
245
+ on '--field=', 'Field to update (if not specified, update the password field).'
137
246
 
138
- c.action do |args, opts|
139
- filename = opts['filename'] || DEFAULT_FILENAME
140
- waiting = opts['wait'] ? opts['wait'].to_i : 30
141
- length = opts['generate'] ? opts['generate'].to_i : 0
142
- field = opts['field']
247
+ run do |opts, args|
248
+ filename = opts.to_hash[:filename] || DEFAULT_FILENAME
249
+ waiting = opts.to_hash[:wait] || DEFAULT_SECS
250
+ length = opts.to_hash[:generate] || 0
251
+ alnum = opts.to_hash[:alnum]
252
+ field = opts.to_hash[:field]
143
253
 
144
254
  string, password = file2string filename
145
255
 
@@ -150,7 +260,7 @@ EOS
150
260
  entries, entry_password = Pwss::update_field args.join(" "), entries, field
151
261
  else
152
262
  # update password
153
- entries, entry_password = Pwss::update args.join(" "), entries, length, opts["alnum"]
263
+ entries, entry_password = Pwss::update args.join(" "), entries, length, alnum
154
264
  end
155
265
 
156
266
  # check status of input file and encrypt if necessary
@@ -166,22 +276,25 @@ EOS
166
276
  puts "Entry updated."
167
277
 
168
278
  # copy to clipboard the new password
169
- Cipher.password_to_clipboard entry_password, waiting
279
+ if entry_password
280
+ Cipher.password_to_clipboard entry_password, waiting
281
+ end
170
282
  end
171
283
  end
172
284
 
285
+ ##############################################################################
286
+ # Look for entries matching string, offer the user to select one of the
287
+ # matching entries, and destroy the entry.
288
+ # The command asks for confirmation even if there is only one matching entry.
289
+ # Destroyed entries cannot be recovered (unless you dig in the backup file).
290
+ command :destroy do
291
+ banner "pwss destroy [options] string"
292
+ description "Destroy a user-selected entry matching <string>, after user confirmation."
173
293
 
174
- p.command(:destroy) do |c|
175
- c.syntax "destroy string"
176
- c.description "Show entries matching <string> and let the user select one to destroy"
294
+ on "-f", "--filename=", "Password file to create. Use extension '.enc' to encrypt it."
177
295
 
178
- # Look for entries matching string, offer the user to select one of the
179
- # matching entries, and destroy the entry.
180
- # The command asks for confirmation even if there is only one matching entry.
181
- # Destroyed entries cannot be recovered (unless you dig in the backup file).
182
-
183
- c.action do |args, opts|
184
- filename = opts['filename'] || DEFAULT_FILENAME
296
+ run do |opts, args|
297
+ filename = opts.to_hash[:filename] || DEFAULT_FILENAME
185
298
 
186
299
  string, password = file2string filename
187
300
  entries = YAML::load(string)
@@ -201,64 +314,71 @@ EOS
201
314
  puts "Entry deleted."
202
315
  end
203
316
  end
204
-
205
317
 
206
- #
207
- # Operations on the master file
208
- #
318
+ ##############################################################################
319
+ # OPERATIONS ON PASSWORD FILES
320
+ ##############################################################################
209
321
 
210
- p.command(:encrypt) do |c|
211
- c.syntax "encrypt"
212
- c.description "Encrypt a password safe"
213
-
214
- c.action do |_, opts|
215
- filename = opts['filename'] || DEFAULT_FILENAME.sub(/\.enc$/, "")
322
+ ##############################################################################
323
+ command :encrypt do
324
+ banner "pwss encrypt [options]"
325
+ description "Encrypt a password safe"
326
+
327
+ on "-f", "--filename=", "Password file to encrypt. Write to <file>.enc."
328
+
329
+ run do |opts, _|
330
+ filename = opts.to_hash[:filename] || DEFAULT_FILENAME.sub(/\.enc$/, "")
331
+
332
+ if not File.exists?(filename)
333
+ puts "Error: file #{filename} does not exist."
334
+ exit 1
335
+ end
216
336
 
217
337
  password = Cipher::check_password
218
338
  data = FileOps::load filename
219
339
  encrypted = Cipher::encrypt data, password
220
340
 
221
341
  enc_filename = filename + ".enc"
342
+
343
+ if File.exists?(enc_filename)
344
+ FileOps::backup enc_filename
345
+ puts "Warning: existing #{enc_filename} backupped to #{enc_filename}~"
346
+ end
222
347
  FileOps::save enc_filename, encrypted
223
348
  puts "An encrypted copy now lives in #{enc_filename}"
224
349
  puts "You might want to check everything is ok and delete the plain file: #{filename}"
225
350
  end
226
351
  end
227
352
 
228
- p.command(:decrypt) do |c|
229
- c.syntax "decrypt"
230
- c.description "Decrypt a password safe"
231
-
232
- c.action do |_, opts|
233
- filename = opts['filename'] || DEFAULT_FILENAME
353
+ ##############################################################################
354
+ command :decrypt do
355
+ banner "pwss decrypt [options]"
356
+ description "Decrypt a password safe"
357
+
358
+ on "-f", "--filename=", "Password file to decrypt. Write to <file>, without '.enc'."
359
+
360
+ run do |opts, _|
361
+ filename = opts.to_hash[:filename] || DEFAULT_FILENAME
362
+
363
+ if not File.exists?(filename)
364
+ puts "Error: file #{filename} does not exist."
365
+ exit 1
366
+ end
234
367
 
235
368
  password = Cipher::ask_password
236
369
  data = FileOps::load filename
237
370
  decrypted = Cipher::decrypt data, password
238
371
 
239
372
  dec_filename = filename.sub(/\.enc$/,"")
373
+ if File.exists?(dec_filename)
374
+ FileOps::backup dec_filename
375
+ puts "Warning: existing #{dec_filename} backupped to #{dec_filename}~"
376
+ end
377
+
240
378
  FileOps::save dec_filename, decrypted
241
379
  puts "A decrypted copy now lives in #{dec_filename}"
242
380
  puts "You might want to check everything is ok and delete #{filename}, if you wish."
243
381
  end
244
382
  end
245
383
 
246
- p.command(:help) do |c|
247
- c.action do |_,_|
248
- puts p.to_s
249
- end
250
- end
251
-
252
- p.default_command(:help)
253
-
254
- # load filename and decrypt, if necessary
255
- # return the filename as string and the password (in case you need to save)
256
- def file2string filename
257
- string = FileOps::load filename
258
- if FileOps::encrypted? filename then
259
- password = Cipher::ask_password
260
- string = Cipher::decrypt string, password
261
- end
262
- [string, password]
263
- end
264
384
  end
data/lib/pwss.rb CHANGED
@@ -85,12 +85,13 @@ module Pwss
85
85
 
86
86
  if found.size > 1 or confirm_even_if_one then
87
87
  printf "\nVarious matches." if found.size > 1
88
- printf "\nSelect entry by ID (0..#{found.size-1}) or -1 to exit: "
88
+ printf "\nSelect entry by ID (0..#{found.size-1}); -1 or empty string to exit: "
89
89
 
90
90
  id = STDIN.gets.chomp.to_i
91
91
  while (id < -1 or id >= found.size)
92
- printf "Select entry by ID (0..#{found.size-1}) or -1 to exit: "
93
- id = STDIN.gets.chomp.to_i
92
+ printf "Select entry by ID (0..#{found.size-1}); -1 or empty string to exit: "
93
+ response = STDIN.gets.chomp
94
+ id = response == "" ? -1 : response.to_i
94
95
  end
95
96
  if id == -1 then
96
97
  exit -1
@@ -0,0 +1,18 @@
1
+ require 'pwss/entry'
2
+
3
+ module Pwss
4
+ class BankAccount < Entry
5
+ def initialize
6
+ super
7
+ @fields = {
8
+ "title" => ["Readline.readline('title: ')", "'title'"],
9
+ "name" => ["Readline.readline('name: ')", "'name'"],
10
+ "iban" => ["Readline.readline('iban: ')", "ITkk xaaa aabb bbbc cccc cccc ccc"],
11
+ "created_at" => ["", "Date.today"],
12
+ "updated_at" => ["", "nil"],
13
+ "url" => ["Readline.readline('url: ')", "''"],
14
+ "description" => ["get_lines", "''"]
15
+ }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ require 'pwss/entry'
2
+
3
+ module Pwss
4
+ class CreditCard < Entry
5
+ def initialize
6
+ super
7
+ @fields = {
8
+ "title" => ["Readline.readline('title: ')", "'title'"],
9
+ "issuer" => ["Readline.readline('issuer: ')", "MasterCard"],
10
+ "name_on_card" => ["Readline.readline('name on card: ')", "'john doe'"],
11
+ "card_number" => ["Readline.readline('number: ')", "000-0000-0000-0000"],
12
+ "valid_from" => ["Readline.readline('valid from: ')", "Sep 2014"],
13
+ "valid_till" => ["Readline.readline('valid till: ')", "Sep 2018"],
14
+ "verification_number" => ["Readline.readline('verification number: ')", "000"],
15
+ "pin" => ["Readline.readline('pin: ')", "0000"],
16
+ "created_at" => ["", "Date.today"],
17
+ "updated_at" => ["", "nil"],
18
+ "url" => ["Readline.readline('url: ')", "''"],
19
+ "notes" => ["get_lines", "''"]
20
+ }
21
+ end
22
+ end
23
+ end
data/lib/pwss/entry.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'readline'
2
+ require 'pwss/cipher'
3
+ require 'date'
2
4
 
3
5
  module Pwss
4
6
  #
@@ -6,41 +8,41 @@ module Pwss
6
8
  # It is a wrapper to a Hash
7
9
  #
8
10
  class Entry
9
- INPUT_F=0
10
- DEFAULT=1
11
- PROMPT=2
11
+ INPUT_F = 0
12
+ DEFAULT = 1
12
13
 
13
- # the fields of an entry, together with the function to ask the and default value
14
- FIELDS = {
15
- "title" => ["Readline.readline('title: ')", "'title'"],
16
- "username" => ["Readline.readline('username: ')", "''"],
17
- "password" => ["Cipher.check_or_generate('password for entry', length, alnum)", "''"],
18
- "created_at" => ["", "Date.today"],
19
- "updated_at" => ["", "nil"],
20
- "url" => ["Readline.readline('url: ')", "''"],
21
- "description" => ["get_lines", "''"]
22
- }
23
-
24
- # the values (a Hash) of this issue
25
- attr_reader :entry
14
+ attr_reader :entry, :fields
26
15
 
27
16
  def initialize
28
17
  @entry = Hash.new
18
+
19
+ # the fields of an entry, together with:
20
+ # - the function to ask the
21
+ # - the default value
22
+ @fields = {
23
+ "title" => ["Readline.readline('title: ')", "'title'"],
24
+ "username" => ["Readline.readline('username: ')", "''"],
25
+ "password" => ["Cipher.check_or_generate('password for entry', length, alnum)", "''"],
26
+ "created_at" => ["", "Date.today"],
27
+ "updated_at" => ["", "nil"],
28
+ "url" => ["Readline.readline('url: ')", "''"],
29
+ "description" => ["get_lines", "''"]
30
+ }
29
31
  end
30
32
 
31
33
  # interactively ask from command line all fields specified in FIELDS
32
34
  # arguments length and alnum are for password generation
33
- def ask length, alnum
34
- FIELDS.keys.each do |key|
35
- @entry[key] = (eval FIELDS[key][INPUT_F]) || (eval FIELDS[key][DEFAULT])
35
+ def ask length = 8, alnum = true
36
+ @fields.keys.each do |key|
37
+ @entry[key] = (eval @fields[key][INPUT_F]) || (eval @fields[key][DEFAULT])
36
38
  end
37
39
  end
38
40
 
39
41
  # initialize all fields with the default values
40
42
  # (and set title to the argument)
41
43
  # def set_fields title
42
- # FIELDS.keys.each do |k|
43
- # @entry[k] = eval(FIELDS[k][DEFAULT])
44
+ # fields.keys.each do |k|
45
+ # @entry[k] = eval(fields[k][DEFAULT])
44
46
  # end
45
47
  # @entry['title'] = title
46
48
  # end
@@ -0,0 +1,21 @@
1
+ require 'pwss/entry'
2
+
3
+ module Pwss
4
+ class SoftwareLicense < Entry
5
+ def initialize
6
+ super
7
+ @fields = {
8
+ "title" => ["Readline.readline('title: ')", "'title'"],
9
+ "version" => ["Readline.readline('version: ')", "0.0.1"],
10
+ "license_number" => ["Readline.readline('license number: ')", "000-0000-0000-0000"],
11
+ "licensed_to" => ["Readline.readline('licensed to: ')", "'John Doe'"],
12
+ "email" => ["Readline.readline('email: ')", "jdoe@example.com"],
13
+ "purchased_on" => ["Readline.readline('purchased on: ')", "'Date.today'"],
14
+ "created_at" => ["", "Date.today"],
15
+ "updated_at" => ["", "nil"],
16
+ "url" => ["Readline.readline('url: ')", "''"],
17
+ "notes" => ["get_lines", "''"]
18
+ }
19
+ end
20
+ end
21
+ end
data/lib/pwss/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pwss
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/pwss.gemspec CHANGED
@@ -9,12 +9,14 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Adolfo Villafiorita"]
10
10
  spec.email = ["adolfo.villafiorita@me.com"]
11
11
  spec.summary = %q{A password manager in the spirit of pwss}
12
- spec.description = %q{PWSS is a password safe, in the spirit of pws
12
+ spec.description = %q{PWSS is a command-line password manager, in the spirit of pws
13
13
  Distinguishing features:
14
- - all entries are stored in a single file
15
- - entries are "complex" records, with username, password, url, description
16
- - the safe file can be stored encrypted or not
17
- - decrypt and encrypt command allow to operate directly on the password file
14
+ - the command manages different password files
15
+ - a password file can store multiple entries
16
+ - entries are of different types (Entry, CreditCard, BankAccount)
17
+ - each type stores specific information (e.g., name, card_number for CreditCards)
18
+ - a password file can be encrypted or in plain text (if you wish to do so)
19
+ - decrypt and encrypt commands allow to edit password files directly
18
20
  }
19
21
  spec.homepage = "http://www.github.com/avillafiorita/pwss"
20
22
  spec.license = "MIT"
@@ -27,6 +29,6 @@ Distinguishing features:
27
29
  spec.add_development_dependency "bundler", "~> 1.5"
28
30
  spec.add_development_dependency "rake"
29
31
 
30
- spec.add_runtime_dependency 'mercenary', '~> 0.3.4', '>= 0.3.4'
32
+ spec.add_runtime_dependency 'slop', '~> 3.6.0', '>= 3.6.0'
31
33
  spec.add_runtime_dependency 'encryptor', '~> 1.3.0', '>= 1.3.0'
32
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adolfo Villafiorita
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-25 00:00:00.000000000 Z
11
+ date: 2015-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -39,25 +39,25 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: mercenary
42
+ name: slop
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.3.4
47
+ version: 3.6.0
48
48
  - - ">="
49
49
  - !ruby/object:Gem::Version
50
- version: 0.3.4
50
+ version: 3.6.0
51
51
  type: :runtime
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - "~>"
56
56
  - !ruby/object:Gem::Version
57
- version: 0.3.4
57
+ version: 3.6.0
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 0.3.4
60
+ version: 3.6.0
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: encryptor
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -79,12 +79,14 @@ dependencies:
79
79
  - !ruby/object:Gem::Version
80
80
  version: 1.3.0
81
81
  description: |
82
- PWSS is a password safe, in the spirit of pws
82
+ PWSS is a command-line password manager, in the spirit of pws
83
83
  Distinguishing features:
84
- - all entries are stored in a single file
85
- - entries are "complex" records, with username, password, url, description
86
- - the safe file can be stored encrypted or not
87
- - decrypt and encrypt command allow to operate directly on the password file
84
+ - the command manages different password files
85
+ - a password file can store multiple entries
86
+ - entries are of different types (Entry, CreditCard, BankAccount)
87
+ - each type stores specific information (e.g., name, card_number for CreditCards)
88
+ - a password file can be encrypted or in plain text (if you wish to do so)
89
+ - decrypt and encrypt commands allow to edit password files directly
88
90
  email:
89
91
  - adolfo.villafiorita@me.com
90
92
  executables:
@@ -99,9 +101,12 @@ files:
99
101
  - Rakefile
100
102
  - bin/pwss
101
103
  - lib/pwss.rb
104
+ - lib/pwss/bank_account.rb
102
105
  - lib/pwss/cipher.rb
106
+ - lib/pwss/credit_card.rb
103
107
  - lib/pwss/entry.rb
104
108
  - lib/pwss/fileops.rb
109
+ - lib/pwss/software_license.rb
105
110
  - lib/pwss/version.rb
106
111
  - pwss.gemspec
107
112
  homepage: http://www.github.com/avillafiorita/pwss
@@ -129,3 +134,4 @@ signing_key:
129
134
  specification_version: 4
130
135
  summary: A password manager in the spirit of pwss
131
136
  test_files: []
137
+ has_rdoc: