pwss 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: