pws 0.9.1 → 0.9.2
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.
- data/README.md +11 -0
- data/Rakefile +4 -2
- data/bin/pws +2 -85
- data/features/generate.feature +35 -2
- data/features/get.feature +10 -1
- data/features/show.feature +20 -0
- data/features/step_definitions/pws_steps.rb +6 -1
- data/features/support/env.rb +1 -1
- data/lib/pws.rb +63 -38
- data/lib/pws/runner.rb +111 -0
- data/lib/pws/version.rb +1 -1
- metadata +55 -24
data/README.md
CHANGED
@@ -19,9 +19,20 @@ Originally based on [this tutorial](http://rbjl.net/41-tutorial-build-your-own-p
|
|
19
19
|
|
20
20
|
Cucumber specs loosely based on [the ones](https://github.com/thecatwasnot/passwordsafe/blob/master/features/) by thecatwasnot - thanks
|
21
21
|
|
22
|
+
Features coming in 1.0
|
23
|
+
---
|
24
|
+
Around March or April 2012
|
25
|
+
|
26
|
+
* Significantly improved crypto methods
|
27
|
+
* Tests for the crypto stuff
|
28
|
+
* Different storage format, but will be backwards compatible
|
29
|
+
* Little UI tweaks
|
30
|
+
|
22
31
|
Contributions by
|
23
32
|
---
|
24
33
|
* [brianewing](https://github.com/brianewing/)
|
34
|
+
* [dquimper](https://github.com/dquimper/)
|
35
|
+
* [grapz](https://github.com/grapz/)
|
25
36
|
|
26
37
|
Copyright
|
27
38
|
---
|
data/Rakefile
CHANGED
@@ -28,8 +28,10 @@ task :gemspec do
|
|
28
28
|
gemspec.validate
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
desc 'Run cucumber and rspec specs'
|
32
|
+
task :spec do
|
33
|
+
sh %[rspec spec] and sh %[cucumber features]
|
34
|
+
end
|
33
35
|
|
34
36
|
task :default => :spec
|
35
37
|
task :test => :spec
|
data/bin/pws
CHANGED
@@ -1,89 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require_relative '../lib/pws'
|
4
|
-
|
5
|
-
action_or_namespace = $*.shift
|
6
|
-
|
7
|
-
if action_or_namespace =~ /^-.*$/
|
8
|
-
@action = $*.shift
|
9
|
-
@namespace = $& unless $& == ?-
|
10
|
-
else
|
11
|
-
@action = action_or_namespace
|
12
|
-
@namespace = nil
|
13
|
-
end
|
14
|
-
|
15
|
-
@action = @action ? @action.to_sym : :show
|
16
|
-
@args = [*$*]
|
17
|
-
|
18
|
-
begin
|
19
|
-
case @action
|
20
|
-
when :v, :version
|
21
|
-
puts "pws #{PWS::VERSION} by " + Paint["J-_-L", :bold] + " <https://github.com/janlelis/pws>"
|
22
|
-
when :help, :actions, :commands
|
23
|
-
puts \
|
24
|
-
<<HELP
|
25
|
-
|
26
|
-
#{Paint["Usage", :underline]}
|
27
|
-
|
28
|
-
#{Paint['pws', :bold]} [-namespace] action [arguments]
|
29
|
-
|
30
|
-
#{Paint["Info", :underline]}
|
31
|
-
|
32
|
-
pws allows you to manage passwords in encryted password files (safes). It
|
33
|
-
operates on the file specified in the environment variable PWS or on "~/.pws".
|
34
|
-
You can apply a namespace as first parameter that will be appended to the
|
35
|
-
filename, e.g. `pws -work show` with usual env would use "~/.pws-work".
|
36
|
-
|
37
|
-
#{Paint["Available actions", :underline]}
|
38
|
-
|
39
|
-
#{Paint['ls', :bold]} / list / show / status
|
40
|
-
Lists all available password entries.
|
41
|
-
|
42
|
-
#{Paint['add', :bold]} / set / store / create ( name, password = nil )
|
43
|
-
Stores a new password entry. The second argument can be the password, but
|
44
|
-
it's recommended to not pass it, but enter it interactively.
|
45
|
-
|
46
|
-
#{Paint['get', :bold]} / entry / copy / password / for ( name, seconds = 10 )
|
47
|
-
Copies the password for <name> to the clipboard. The second argument specifies,
|
48
|
-
how long the password is kept in the clipboard (0 = no deletion).
|
49
|
-
|
50
|
-
#{Paint['gen', :bold]} / generate ( name, seconds = 10, length = 64, char_pool )
|
51
|
-
Generates a new password for <name> and then copies it to the clipboard, like
|
52
|
-
get (the second argument is the time - it gets passed to get). The third
|
53
|
-
argument sets the password length. The fourth argument allows you to pass a
|
54
|
-
character pool that is used for generating the passwords.
|
55
|
-
|
56
|
-
#{Paint['rm', :bold]} / remove / del / delete ( name )
|
57
|
-
Removes a password entry.
|
58
|
-
|
59
|
-
#{Paint['mv', :bold]} / move / rename ( old_name, new_name )
|
60
|
-
Renames a password entry.
|
61
|
-
|
62
|
-
#{Paint['master', :bold]} ( password = nil )
|
63
|
-
Changes the master password.
|
64
|
-
|
65
|
-
#{Paint['v', :bold]} / version
|
66
|
-
Displays version and website.
|
67
|
-
|
68
|
-
#{Paint['help', :bold]} / actions / commands
|
69
|
-
Displays this help.
|
70
|
-
|
71
|
-
HELP
|
72
|
-
else # redirect to safe
|
73
|
-
if PWS.public_instance_methods(false).include?(@action)
|
74
|
-
PWS.new(nil, @namespace).send @action, *@args
|
75
|
-
else
|
76
|
-
pa "Unknown action: #@action\nPlease see `pws help` for a list of available commands!", :red
|
77
|
-
end
|
78
|
-
end
|
79
|
-
rescue PWS::NoAccess
|
80
|
-
# pa $!.message.capitalize, :red, :bold
|
81
|
-
pa "NO ACCESS", :red, :bold
|
82
|
-
rescue ArgumentError
|
83
|
-
pa $!.message.capitalize, :red
|
84
|
-
rescue Interrupt
|
85
|
-
system 'stty echo' if $stdin.tty? # ensure terminal's working
|
86
|
-
pa "..canceled", :red
|
87
|
-
end
|
3
|
+
require_relative '../lib/pws/runner'
|
4
|
+
PWS::Runner.run(*PWS::Runner.parse_cli_arguments)
|
88
5
|
|
89
6
|
# J-_-L
|
data/features/generate.feature
CHANGED
@@ -21,7 +21,18 @@ Feature: Generate
|
|
21
21
|
Then the output should contain "Master password:"
|
22
22
|
And the output should contain "The password for github has been added"
|
23
23
|
And the output should contain "The password for github is now available in your clipboard for 1 second"
|
24
|
-
|
24
|
+
|
25
|
+
@wait-11s
|
26
|
+
@slow-hack
|
27
|
+
Scenario: Generate a new password for "github", PWS_SECONDS set to 5, gets passed to the get as keep-in-clipboard time
|
28
|
+
Given A safe exists with master password "my_master_password"
|
29
|
+
When I set env variable "PWS_SECONDS" to "5"
|
30
|
+
And I run `pws generate github` interactively
|
31
|
+
And I type "my_master_password"
|
32
|
+
Then the output should contain "Master password:"
|
33
|
+
And the output should contain "The password for github has been added"
|
34
|
+
And the output should contain "The password for github is now available in your clipboard for 5 seconds"
|
35
|
+
|
25
36
|
@slow-hack
|
26
37
|
Scenario: Generate a new password for "github", third parameter defines password length
|
27
38
|
Given A safe exists with master password "my_master_password"
|
@@ -42,6 +53,17 @@ Feature: Generate
|
|
42
53
|
And the output should contain "The password for github has been copied to your clipboard"
|
43
54
|
And the clipboard should match /^.{64}$/
|
44
55
|
|
56
|
+
@slow-hack
|
57
|
+
Scenario: Generate a new password for "github", default length of PWS_GEN_LENGTH
|
58
|
+
Given A safe exists with master password "my_master_password"
|
59
|
+
When I set env variable "PWS_LENGTH" to "15"
|
60
|
+
And I run `pws generate github 0` interactively
|
61
|
+
And I type "my_master_password"
|
62
|
+
Then the output should contain "Master password:"
|
63
|
+
And the output should contain "The password for github has been added"
|
64
|
+
And the output should contain "The password for github has been copied to your clipboard"
|
65
|
+
And the clipboard should match /^.{15}$/
|
66
|
+
|
45
67
|
@slow-hack
|
46
68
|
Scenario: Generate a new password for "github", fourth parameter defines a char pool used for generation
|
47
69
|
Given A safe exists with master password "my_master_password"
|
@@ -62,4 +84,15 @@ Feature: Generate
|
|
62
84
|
And the output should contain "The password for github has been copied to your clipboard"
|
63
85
|
And the clipboard should match ^[!\"\#$%&'()*+,\-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~]+$
|
64
86
|
|
65
|
-
|
87
|
+
@slow-hack
|
88
|
+
Scenario: Generate a new password for "github", the default char pool PWS_GEN_CHARPOOL
|
89
|
+
Given A safe exists with master password "my_master_password"
|
90
|
+
When I set env variable "PWS_CHARPOOL" to "a"
|
91
|
+
When I run `pws generate github 0` interactively
|
92
|
+
And I type "my_master_password"
|
93
|
+
Then the output should contain "Master password:"
|
94
|
+
And the output should contain "The password for github has been added"
|
95
|
+
And the output should contain "The password for github has been copied to your clipboard"
|
96
|
+
And the clipboard should match ^a{64}$
|
97
|
+
|
98
|
+
|
data/features/get.feature
CHANGED
@@ -25,7 +25,16 @@ Feature: Get
|
|
25
25
|
And I type "my_master_password"
|
26
26
|
Then the output should contain "Master password:"
|
27
27
|
And the output should contain "The password for github is now available in your clipboard for 1 second"
|
28
|
-
|
28
|
+
|
29
|
+
@wait-11s
|
30
|
+
Scenario: Get the password for "github" (which exists) and keep it in the clipboard for 5 seconds when PWS_SECONDS is set to 5
|
31
|
+
Given A safe exists with master password "my_master_password" and a key "github" with password "github_password"
|
32
|
+
When I set env variable "PWS_SECONDS" to "5"
|
33
|
+
And I run `pws get github` interactively
|
34
|
+
And I type "my_master_password"
|
35
|
+
Then the output should contain "Master password:"
|
36
|
+
And the output should contain "The password for github is now available in your clipboard for 5 seconds"
|
37
|
+
|
29
38
|
Scenario: Get the password for "github" (which exists) and ensure that the original clipboard content gets restored
|
30
39
|
Given A safe exists with master password "my_master_password" and a key "github" with password "github_password"
|
31
40
|
Given A clipboard content of "blubb"
|
data/features/show.feature
CHANGED
@@ -15,6 +15,26 @@ Feature: Show
|
|
15
15
|
And the output should contain "password"
|
16
16
|
And the output should contain "entries"
|
17
17
|
|
18
|
+
Scenario: Show the list and filter for regex
|
19
|
+
Given A safe exists with master password "my_master_password" and keys
|
20
|
+
| some-abc | 123 |
|
21
|
+
| password-aDc! | 345 |
|
22
|
+
| entries | 678 |
|
23
|
+
When I run `pws show a.c` interactively
|
24
|
+
And I type "my_master_password"
|
25
|
+
Then the output should contain "Entries"
|
26
|
+
And the output should contain "some-abc"
|
27
|
+
And the output should contain "password-aDc!"
|
28
|
+
|
29
|
+
Scenario: Show the list and filter for regex, but regex is invalid
|
30
|
+
Given A safe exists with master password "my_master_password" and keys
|
31
|
+
| some-abc | 123 |
|
32
|
+
| password-aDc! | 345 |
|
33
|
+
| entries | 678 |
|
34
|
+
When I run `pws show "(("` interactively
|
35
|
+
And I type "my_master_password"
|
36
|
+
Then the output should contain "Invalid regex"
|
37
|
+
|
18
38
|
Scenario: Show the list (but there is no entry yet)
|
19
39
|
Given A safe exists with master password "my_master_password"
|
20
40
|
When I run `pws show` interactively
|
@@ -1,6 +1,7 @@
|
|
1
1
|
def create_safe(master, key_hash = {})
|
2
|
+
ENV["PWS_CHARPOOL"] = ENV["PWS_LENGTH"] = ENV["PWS_SECONDS"] = nil
|
2
3
|
restore, $stdout = $stdout, StringIO.new # tmp silence $stdout
|
3
|
-
pws = PWS.new
|
4
|
+
pws = PWS.new(password: master)
|
4
5
|
key_hash.each{ |key, password|
|
5
6
|
pws.add key, password
|
6
7
|
}
|
@@ -34,3 +35,7 @@ end
|
|
34
35
|
Then /^the clipboard should match ([^\/].+)$/ do |expected|
|
35
36
|
assert_matching_output(expected, Clipboard.paste)
|
36
37
|
end
|
38
|
+
|
39
|
+
When /^I set env variable "(\w+)" to "([^"]*)"$/ do |var, value|
|
40
|
+
ENV[var] = value
|
41
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -21,7 +21,7 @@ END{
|
|
21
21
|
}
|
22
22
|
|
23
23
|
Around do |_, block|
|
24
|
-
# NOTE: You cannot parallelize the tests, because they use the clipboard and the env
|
24
|
+
# NOTE: You cannot parallelize the tests, because they use the clipboard and the env vars...
|
25
25
|
Clipboard.clear
|
26
26
|
ENV["PWS"] = File.expand_path('pws-test-' + SecureRandom.uuid)
|
27
27
|
|
data/lib/pws.rb
CHANGED
@@ -10,36 +10,56 @@ require 'paint/pa'
|
|
10
10
|
class PWS
|
11
11
|
class NoAccess < StandardError; end
|
12
12
|
|
13
|
+
attr_reader :filename, :options
|
14
|
+
|
13
15
|
# Creates a new password safe. Takes the path to the password file, by default: ~/.pws
|
14
16
|
# Second parameter allows namespaces that get appended to the file name (uses another safe)
|
15
17
|
# You can pass the master password as third parameter (not recommended)
|
16
|
-
def initialize(
|
17
|
-
|
18
|
-
@
|
19
|
-
|
18
|
+
def initialize(options)
|
19
|
+
collect_options(options)
|
20
|
+
@filename = File.expand_path(@options[:filename])
|
21
|
+
@filename << '-' << @options[:namespace] if @options[:namespace]
|
22
|
+
|
23
|
+
access_safe(options[:password])
|
20
24
|
read_safe
|
21
25
|
end
|
22
26
|
|
27
|
+
def collect_options(options = {})
|
28
|
+
@options = options
|
29
|
+
@options[:filename] ||= ENV["PWS"] || '~/.pws'
|
30
|
+
@options[:seconds] ||= ENV['PWS_SECONDS'] || 10
|
31
|
+
@options[:length] ||= ENV['PWS_LENGTH'] || 64
|
32
|
+
@options[:charpool] ||= ENV['PWS_CHARPOOL'] || (33..126).map(&:chr).join
|
33
|
+
end
|
34
|
+
|
23
35
|
# Shows a password entry list
|
24
|
-
def show
|
25
|
-
if @
|
26
|
-
pa %[There aren't any passwords stored at #{@
|
36
|
+
def show(pattern = nil)
|
37
|
+
if @data.empty?
|
38
|
+
pa %[There aren't any passwords stored at #{@filename}, yet], :red
|
27
39
|
else
|
28
|
-
|
29
|
-
|
40
|
+
if pattern
|
41
|
+
keys = @data.keys.grep /#{pattern}/
|
42
|
+
else
|
43
|
+
keys = @data.keys
|
44
|
+
end
|
45
|
+
puts Paint["Entries", :underline] + %[ in ] + @filename
|
46
|
+
puts keys.sort.map{ |key| %[- #{key}\n] }.join
|
30
47
|
end
|
31
48
|
return true
|
49
|
+
rescue RegexpError
|
50
|
+
pa %[Invalid regex given], :red
|
51
|
+
return false
|
32
52
|
end
|
33
53
|
aliases_for :show, :ls, :list, :status
|
34
54
|
|
35
55
|
# Add a password entry, params: name, password (optional, opens prompt if not given)
|
36
56
|
def add(key, password = nil)
|
37
|
-
if @
|
57
|
+
if @data[key]
|
38
58
|
pa %[There is already a password stored for #{key}. You need to remove it before creating a new one!], :red
|
39
59
|
return false
|
40
60
|
else
|
41
|
-
@
|
42
|
-
if @
|
61
|
+
@data[key] = password || ask_for_password(%[please enter a password for #{key}], :yellow)
|
62
|
+
if @data[key].empty?
|
43
63
|
pa %[Cannot add an empty password!], :red
|
44
64
|
return false
|
45
65
|
else
|
@@ -49,11 +69,11 @@ class PWS
|
|
49
69
|
end
|
50
70
|
end
|
51
71
|
end
|
52
|
-
aliases_for :add, :set, :store, :create, :[]=
|
72
|
+
aliases_for :add, :set, :store, :create, :[]=
|
53
73
|
|
54
74
|
# Gets the password entry and copies it to the clipboard. The second parameter is the time in seconds it stays there
|
55
|
-
def get(key, seconds =
|
56
|
-
if pw_plaintext = @
|
75
|
+
def get(key, seconds = @options[:seconds])
|
76
|
+
if pw_plaintext = @data[key]
|
57
77
|
if seconds && seconds.to_i > 0
|
58
78
|
original_clipboard_content = Clipboard.paste
|
59
79
|
Clipboard.copy pw_plaintext
|
@@ -81,13 +101,13 @@ class PWS
|
|
81
101
|
# Adds a password entry with a freshly generated random password
|
82
102
|
def generate(
|
83
103
|
key,
|
84
|
-
seconds
|
85
|
-
length
|
86
|
-
|
104
|
+
seconds = @options[:seconds],
|
105
|
+
length = @options[:length],
|
106
|
+
charpool = @options[:charpool]
|
87
107
|
)
|
88
|
-
|
108
|
+
charpool_size = charpool.size
|
89
109
|
new_pw = (1..length.to_i).map{
|
90
|
-
|
110
|
+
charpool[SecureRandom.random_number(charpool_size)]
|
91
111
|
}.join
|
92
112
|
|
93
113
|
if add(key, new_pw)
|
@@ -98,7 +118,7 @@ class PWS
|
|
98
118
|
|
99
119
|
# Removes a specific password entry
|
100
120
|
def remove(key)
|
101
|
-
if @
|
121
|
+
if @data.delete key
|
102
122
|
write_safe
|
103
123
|
pa %[The password for #{key} has been removed], :green
|
104
124
|
return true
|
@@ -111,14 +131,14 @@ class PWS
|
|
111
131
|
|
112
132
|
# Removes a specific password entry
|
113
133
|
def rename(old_key, new_key)
|
114
|
-
if !@
|
134
|
+
if !@data[old_key]
|
115
135
|
pa %[No password found for #{old_key}!], :red
|
116
136
|
return false
|
117
|
-
elsif @
|
137
|
+
elsif @data[new_key]
|
118
138
|
pa %[There is already a password stored for #{new_key}. You need to remove it before naming another one #{new_key}!], :red
|
119
139
|
return false
|
120
140
|
else
|
121
|
-
@
|
141
|
+
@data[new_key] = @data.delete(old_key)
|
122
142
|
write_safe
|
123
143
|
pa %[The password entry #{old_key} has been renamed to #{new_key}], :green
|
124
144
|
return true
|
@@ -136,7 +156,7 @@ class PWS
|
|
136
156
|
return false
|
137
157
|
end
|
138
158
|
end
|
139
|
-
@
|
159
|
+
@hash = Encryptor.hash(password)
|
140
160
|
write_safe
|
141
161
|
pa %[The master password has been changed], :green
|
142
162
|
return true
|
@@ -152,35 +172,40 @@ class PWS
|
|
152
172
|
|
153
173
|
# Tries to load and decrypt the password safe from the pwfile
|
154
174
|
def read_safe
|
155
|
-
pwdata_raw = File.read(@
|
175
|
+
pwdata_raw = File.read(@filename)
|
156
176
|
pwdata_encrypted = pwdata_raw.force_encoding("ascii")
|
157
|
-
pwdata_dump = Encryptor.decrypt(pwdata_encrypted, @
|
177
|
+
pwdata_dump = Encryptor.decrypt(pwdata_encrypted, @hash)
|
158
178
|
pwdata_with_redundancy = Marshal.load(pwdata_dump)
|
159
|
-
@
|
179
|
+
@data = remove_redundancy(pwdata_with_redundancy)
|
160
180
|
pa %[ACCESS GRANTED], :green
|
161
181
|
rescue
|
162
182
|
fail NoAccess, %[Could not load and decrypt the password safe!]
|
163
183
|
end
|
164
184
|
|
165
185
|
# Tries to encrypt and save the password safe into the pwfile
|
166
|
-
def write_safe
|
167
|
-
pwdata_with_redundancy = add_redundancy(@
|
186
|
+
def write_safe(new_safe = false)
|
187
|
+
pwdata_with_redundancy = add_redundancy(@data || {})
|
168
188
|
pwdata_dump = Marshal.dump(pwdata_with_redundancy)
|
169
|
-
pwdata_encrypted = Encryptor.encrypt(pwdata_dump, @
|
170
|
-
|
189
|
+
pwdata_encrypted = Encryptor.encrypt(pwdata_dump, @hash)
|
190
|
+
if new_safe
|
191
|
+
FileUtils.mkdir_p(File.dirname(@filename))
|
192
|
+
FileUtils.touch(@filename)
|
193
|
+
File.chmod(0600, @filename)
|
194
|
+
end
|
195
|
+
File.open(@filename, 'w'){ |f| f.write(pwdata_encrypted) }
|
171
196
|
rescue
|
172
197
|
fail NoAccess, %[Could not encrypt and save the password safe!]
|
173
198
|
end
|
174
199
|
|
175
200
|
# Checks if the file is accessible or create a new one
|
176
201
|
def access_safe(password = nil)
|
177
|
-
if !File.file? @
|
178
|
-
pa %[No password safe detected, creating one at #@
|
179
|
-
@
|
180
|
-
write_safe
|
202
|
+
if !File.file? @filename
|
203
|
+
pa %[No password safe detected, creating one at #@filename], :blue, :bold
|
204
|
+
@hash = Encryptor.hash password || ask_for_password(%[please enter a new master password], :yellow, :bold)
|
205
|
+
write_safe(true)
|
181
206
|
else
|
182
|
-
print %[Access password safe at #@
|
183
|
-
@
|
207
|
+
print %[Access password safe at #@filename | ]
|
208
|
+
@hash = Encryptor.hash password || ask_for_password(%[master password])
|
184
209
|
end
|
185
210
|
end
|
186
211
|
|
data/lib/pws/runner.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require_relative '../pws'
|
2
|
+
|
3
|
+
module PWS::Runner
|
4
|
+
class << self
|
5
|
+
# some simple option parsing
|
6
|
+
# returns action, arguments, options
|
7
|
+
# only accepts options with value as next arg, except breaking special cases
|
8
|
+
def parse_cli_arguments(argv = $*.dup)
|
9
|
+
action = nil
|
10
|
+
options = {}
|
11
|
+
arguments = []
|
12
|
+
argv.unshift(nil) # easier parsing
|
13
|
+
|
14
|
+
argv.each_cons(2){ |prev_arg, arg|
|
15
|
+
case arg
|
16
|
+
when '-'
|
17
|
+
# ignore
|
18
|
+
when /^--(help|version)$/
|
19
|
+
return [$1.to_sym, [], {}]
|
20
|
+
when /^--/
|
21
|
+
# parse option in next iteration
|
22
|
+
when /^-([^-].*)$/
|
23
|
+
options[:namespace] = $1
|
24
|
+
else
|
25
|
+
if prev_arg =~ /^--(.+)$/
|
26
|
+
options[$1.to_sym] = arg
|
27
|
+
elsif !action
|
28
|
+
action = arg.to_sym
|
29
|
+
else
|
30
|
+
arguments << arg
|
31
|
+
end
|
32
|
+
end
|
33
|
+
}
|
34
|
+
|
35
|
+
[action || :show, arguments, options]
|
36
|
+
end
|
37
|
+
|
38
|
+
# makes the Ruby safe more usable
|
39
|
+
def run(action, arguments, options)
|
40
|
+
case action
|
41
|
+
when :v, :version
|
42
|
+
puts "pws #{PWS::VERSION} by " + Paint["J-_-L", :bold] + " <https://github.com/janlelis/pws>"
|
43
|
+
when :help, :actions, :commands
|
44
|
+
puts(<<HELP)
|
45
|
+
|
46
|
+
#{Paint["Usage", :underline]}
|
47
|
+
|
48
|
+
#{Paint['pws', :bold]} [-namespace] action [arguments]
|
49
|
+
|
50
|
+
#{Paint["Info", :underline]}
|
51
|
+
|
52
|
+
pws allows you to manage passwords in encryted password files (safes). It
|
53
|
+
operates on the file specified in the environment variable PWS or on "~/.pws".
|
54
|
+
You can apply a namespace as first parameter that will be appended to the
|
55
|
+
filename, e.g. `pws -work show` with usual env would use "~/.pws-work".
|
56
|
+
|
57
|
+
#{Paint["Available actions", :underline]}
|
58
|
+
|
59
|
+
#{Paint['ls', :bold]} / list / show / status ( pattern = nil )
|
60
|
+
Lists all available password entries. Optionally takes a regex filter.
|
61
|
+
|
62
|
+
#{Paint['add', :bold]} / set / store / create ( name, password = nil )
|
63
|
+
Stores a new password entry. The second argument can be the password, but
|
64
|
+
it's recommended to not pass it, but enter it interactively.
|
65
|
+
|
66
|
+
#{Paint['get', :bold]} / entry / copy / password / for ( name, seconds = 10 )
|
67
|
+
Copies the password for <name> to the clipboard. The second argument specifies,
|
68
|
+
how long the password is kept in the clipboard (0 = no deletion).
|
69
|
+
|
70
|
+
#{Paint['gen', :bold]} / generate ( name, seconds = 10, length = 64, char_pool )
|
71
|
+
Generates a new password for <name> and then copies it to the clipboard, like
|
72
|
+
get (the second argument is the time - it gets passed to get). The third
|
73
|
+
argument sets the password length. The fourth argument allows you to pass a
|
74
|
+
character pool that is used for generating the passwords.
|
75
|
+
|
76
|
+
#{Paint['rm', :bold]} / remove / del / delete ( name )
|
77
|
+
Removes a password entry.
|
78
|
+
|
79
|
+
#{Paint['mv', :bold]} / move / rename ( old_name, new_name )
|
80
|
+
Renames a password entry.
|
81
|
+
|
82
|
+
#{Paint['master', :bold]} ( password = nil )
|
83
|
+
Changes the master password.
|
84
|
+
|
85
|
+
#{Paint['v', :bold]} / version
|
86
|
+
Displays version and website.
|
87
|
+
|
88
|
+
#{Paint['help', :bold]} / actions / commands
|
89
|
+
Displays this help.
|
90
|
+
|
91
|
+
HELP
|
92
|
+
else # redirect to safe
|
93
|
+
if PWS.public_instance_methods(false).include?(action)
|
94
|
+
PWS.new(options).public_send(action, *arguments)
|
95
|
+
else
|
96
|
+
pa "Unknown action: #{action}\nPlease see `pws --help` for a list of available commands!", :red
|
97
|
+
end
|
98
|
+
end
|
99
|
+
rescue PWS::NoAccess
|
100
|
+
# pa $!.message.capitalize, :red, :bold
|
101
|
+
pa "NO ACCESS", :red, :bold
|
102
|
+
rescue ArgumentError
|
103
|
+
pa $!.message.capitalize, :red
|
104
|
+
rescue Interrupt
|
105
|
+
system 'stty echo' if $stdin.tty? # ensure terminal's working
|
106
|
+
pa "..canceled", :red
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# J-_-L
|
data/lib/pws/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pws
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: clipboard
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 1.0.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.1
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: zucker
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '12.1'
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '12.1'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: paint
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: 0.8.4
|
44
54
|
type: :runtime
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.8.4
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: rake
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
@@ -54,10 +69,15 @@ dependencies:
|
|
54
69
|
version: '0'
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: cucumber
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
83
|
- - ! '>='
|
@@ -65,10 +85,15 @@ dependencies:
|
|
65
85
|
version: '0'
|
66
86
|
type: :development
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
69
94
|
- !ruby/object:Gem::Dependency
|
70
95
|
name: aruba
|
71
|
-
requirement:
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
72
97
|
none: false
|
73
98
|
requirements:
|
74
99
|
- - ! '>='
|
@@ -76,7 +101,12 @@ dependencies:
|
|
76
101
|
version: '0'
|
77
102
|
type: :development
|
78
103
|
prerelease: false
|
79
|
-
version_requirements:
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
80
110
|
description: pws is a command-line password safe. Please run `pws help` for usage
|
81
111
|
information.
|
82
112
|
email: mail@janlelis.de
|
@@ -87,22 +117,23 @@ extra_rdoc_files:
|
|
87
117
|
- README.md
|
88
118
|
- LICENSE
|
89
119
|
files:
|
90
|
-
- lib/pws/encryptor.rb
|
91
120
|
- lib/pws/version.rb
|
121
|
+
- lib/pws/encryptor.rb
|
122
|
+
- lib/pws/runner.rb
|
92
123
|
- lib/pws.rb
|
93
124
|
- bin/pws
|
94
|
-
- features/step_definitions/pws_steps.rb
|
95
|
-
- features/remove.feature
|
96
|
-
- features/namespaces.feature
|
97
|
-
- features/generate.feature
|
98
|
-
- features/rename.feature
|
99
125
|
- features/show.feature
|
100
|
-
- features/add.feature
|
101
126
|
- features/access.feature
|
102
|
-
- features/
|
127
|
+
- features/misc.feature
|
103
128
|
- features/get.feature
|
129
|
+
- features/master.feature
|
130
|
+
- features/remove.feature
|
131
|
+
- features/namespaces.feature
|
132
|
+
- features/add.feature
|
104
133
|
- features/support/env.rb
|
105
|
-
- features/
|
134
|
+
- features/rename.feature
|
135
|
+
- features/generate.feature
|
136
|
+
- features/step_definitions/pws_steps.rb
|
106
137
|
- Rakefile
|
107
138
|
- pws.gemspec
|
108
139
|
- README.md
|
@@ -130,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
161
|
version: '0'
|
131
162
|
requirements: []
|
132
163
|
rubyforge_project:
|
133
|
-
rubygems_version: 1.8.
|
164
|
+
rubygems_version: 1.8.23
|
134
165
|
signing_key:
|
135
166
|
specification_version: 3
|
136
167
|
summary: pws is a cli password safe.
|