keybox 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,163 @@
1
+ module Keybox
2
+ # including this module assumes that the class included has
3
+ # @stdout and @stdin member variables.
4
+ #
5
+ # This module also assumes that stty is available
6
+ module TermIO
7
+
8
+ # http://pueblo.sourceforge.net/doc/manual/ansi_color_codes.html
9
+ #
10
+ ESCAPE = "\e"
11
+ BOLD_ON = "[1m"
12
+ RESET = "[0m"
13
+
14
+ FG_BLACK = "[30m"
15
+ FG_RED = "[31m"
16
+ FG_GREEN = "[32m"
17
+ FG_YELLOW = "[33m"
18
+ FG_BLUE = "[34m"
19
+ FG_MAGENTA = "[35m"
20
+ FG_CYAN = "[36m"
21
+ FG_WHITE = "[37m"
22
+
23
+ COLORS = {
24
+ :black => FG_BLACK,
25
+ :red => FG_RED,
26
+ :green => FG_GREEN,
27
+ :yellow => FG_YELLOW,
28
+ :blue => FG_BLUE,
29
+ :magenta => FG_MAGENTA,
30
+ :cyan => FG_CYAN,
31
+ :white => FG_WHITE,
32
+ }
33
+
34
+ VALID_COLORS = COLORS.keys()
35
+
36
+
37
+ STTY = "stty"
38
+ STTY_SAVE_CMD = "#{STTY} -g"
39
+ STTY_RAW_CMD = "#{STTY} raw -echo isig"
40
+
41
+ EOL_CHARS = [10, # '\n'
42
+ 13, # '\r'
43
+ ]
44
+ #
45
+ # prompt for input, returning what was typed. If echo is false,
46
+ # then '*' is printed out for each character typed in. If it is
47
+ # any other character then that is output instead.
48
+ #
49
+ # If validate is set to true, then it will prompt twice and make
50
+ # sure that the two values match
51
+ #
52
+ def prompt(p,echo = true, validate = false, width = 20)
53
+ validated = false
54
+ line = ""
55
+ extra_prompt = " (again)"
56
+ original_prompt = p
57
+ validation_prompt = original_prompt + extra_prompt
58
+ width += extra_prompt.length
59
+
60
+ until validated do
61
+ line = prompt_and_return(original_prompt.rjust(width),echo)
62
+
63
+ # if we are validating then prompt again to validate
64
+ if validate then
65
+ v = prompt_and_return(validation_prompt.rjust(width),echo)
66
+ if v != line then
67
+ color_puts "Entries do not match, try again.", :red
68
+ else
69
+ validated = true
70
+ end
71
+ else
72
+ validated = true
73
+ end
74
+ end
75
+ return line
76
+ end
77
+
78
+ def prompt_y_n(p)
79
+ response = prompt(p)
80
+ if response.size > 0 and response.downcase[0].chr == 'y' then
81
+ true
82
+ else
83
+ false
84
+ end
85
+ end
86
+
87
+ def get_one_char
88
+ stty_original = %x{#{STTY_SAVE_CMD}}
89
+ char = nil
90
+ begin
91
+ system STTY_RAW_CMD
92
+ char = @stdin.getc
93
+ ensure
94
+ system "#{STTY} #{stty_original}"
95
+ end
96
+
97
+ return char
98
+ end
99
+
100
+ def prompt_and_return(the_prompt,echo)
101
+ line = ""
102
+ color_print "#{the_prompt} : ", :white
103
+ if echo != true then
104
+
105
+ echo_char = echo || '*'
106
+
107
+ if has_stty? then
108
+ stty_original = %x{#{STTY_SAVE_CMD}}
109
+
110
+ begin
111
+ system STTY_RAW_CMD
112
+ while char = @stdin.getc
113
+ line << char
114
+ break if EOL_CHARS.include? char
115
+ @stdout.putc echo_char
116
+ end
117
+ ensure
118
+ system "#{STTY} #{stty_original}"
119
+ end
120
+ @stdout.puts
121
+ end
122
+ else
123
+ line = @stdin.gets
124
+ end
125
+
126
+ # if we got end of file or some other input resulting in
127
+ # line becoming nil then set it to the empty string
128
+ line = line || ""
129
+
130
+ return line.rstrip
131
+ end
132
+
133
+ def has_stty?
134
+ system "which stty > /dev/null 2>&1"
135
+ end
136
+
137
+ def colorize(text,color,bold=true)
138
+ before = ""
139
+ after = ""
140
+ if VALID_COLORS.include?(color) then
141
+ before = ESCAPE + COLORS[color]
142
+ before = ESCAPE + BOLD_ON + before if bold
143
+ after = ESCAPE + RESET
144
+ end
145
+ "#{before}#{text}#{after}"
146
+ end
147
+
148
+ def colorize_if_io_isatty(io,text,color,bold)
149
+ if io.tty? then
150
+ text = colorize(text,color,bold)
151
+ end
152
+ text
153
+ end
154
+
155
+ def color_puts(text, color, bold = true)
156
+ @stdout.puts colorize_if_io_isatty(@stdout,text,color,bold)
157
+ end
158
+
159
+ def color_print(text,color, bold = true)
160
+ @stdout.print colorize_if_io_isatty(@stdout,text,color,bold)
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,86 @@
1
+ require 'keybox/randomizer'
2
+ require 'yaml'
3
+ module Keybox
4
+ #
5
+ # A quick implementation of a UUID class using the internal
6
+ # randomizer for byte generation
7
+ #
8
+ class UUID
9
+
10
+ attr_reader :bytes
11
+
12
+ XF = "%0.2x"
13
+ FORMAT = sprintf("%s-%s-%s-%s-%s", XF * 4, XF * 2, XF * 2, XF * 2, XF * 6)
14
+ REGEX = %r|^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$|
15
+
16
+ #
17
+ # the UUID can be initialized with:
18
+ # - nothing ( default case ) in this case the UUID is generated
19
+ # - a string in the standarde uuid format, in this case it is
20
+ # decoded and converted to the internal format
21
+ # - a string of bytes, in this case they are considered to be
22
+ # the bytes used internally so 16 of them are taken
23
+ def initialize(bytes = nil)
24
+
25
+ if bytes.nil? then
26
+ @bytes = Keybox::RandomDevice.random_bytes(16)
27
+ elsif bytes.size == 36 and bytes.split("-").size == 5 then
28
+ if bytes =~ REGEX then
29
+ # remove the dashes and make sure that we're all
30
+ # lowercase
31
+ b = bytes.gsub(/-/,'').downcase
32
+
33
+ # convert to an array of hex strings
34
+ b = b.unpack("a2"*16)
35
+
36
+ # convert the hex strings to integers
37
+ b.collect! { |x| x.to_i(16) }
38
+
39
+ # and pack those integers into a string
40
+ @bytes = b.pack("C*")
41
+
42
+ # of course this could all be done in one line with
43
+ # @bytes = bytes.gsub(/-/,'').downcase.unpack("a2"*16").collect {|x| x.to_i(16) }.pack("C*")
44
+ else
45
+ raise ArgumentError, "[#{bytes}] is not a hex encoded UUID"
46
+ end
47
+ elsif bytes.kind_of?(String) and bytes.length >= 16
48
+ @bytes = bytes[0..16]
49
+ else
50
+ raise ArgumentError, "[#{bytes}] cannot be converted to a UUID"
51
+ end
52
+ end
53
+
54
+ #
55
+ # convert the bytes to the hex encoded string format of
56
+ # XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
57
+ #
58
+ def to_s
59
+ sprintf(FORMAT,*to_a)
60
+ end
61
+
62
+ def to_a
63
+ @bytes.unpack("C*")
64
+ end
65
+
66
+ def ==(other)
67
+ self.eql?(other)
68
+ end
69
+
70
+ def eql?(other)
71
+ case other
72
+ when Keybox::UUID
73
+ self.bytes == other.bytes
74
+ when String
75
+ begin
76
+ o = Keybox::UUID.new(other)
77
+ self == o
78
+ rescue
79
+ false
80
+ end
81
+ else
82
+ false
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,56 @@
1
+ require 'keybox'
2
+ require 'keybox/application/base'
3
+
4
+ context "Keybox Base Application" do
5
+
6
+ specify "nil argv should do nothing" do
7
+ kba = Keybox::Application::Base.new(nil)
8
+ kba.error_message.should_be nil
9
+ end
10
+
11
+ specify "executing with no args should have output on stdout" do
12
+ kba = Keybox::Application::Base.new(nil)
13
+ kba.stdout = StringIO.new
14
+ kba.run
15
+ kba.stdout.string.size.should_be > 0
16
+ end
17
+
18
+
19
+ specify "invalid options set the error message, exit 1 and have output on stderr" do
20
+ kba = Keybox::Application::Base.new(["--invalid-option"])
21
+ kba.stderr = StringIO.new
22
+ kba.stdout = StringIO.new
23
+ begin
24
+ kba.run
25
+ rescue SystemExit => se
26
+ kba.error_message.should_satisfy { |msg| msg =~ /Try.*--help/m }
27
+ kba.stderr.string.should_satisfy { |msg| msg =~ /Try.*--help/m }
28
+ kba.stdout.string.size.should_eql 0
29
+ se.status.should == 1
30
+ end
31
+ end
32
+
33
+ specify "help has output on stdout and exits 0" do
34
+ kba = Keybox::Application::Base.new(["--h"])
35
+ kba.stdout = StringIO.new
36
+ begin
37
+ kba.run
38
+ rescue SystemExit => se
39
+ se.status.should_eql 0
40
+ kba.stdout.string.length.should_be > 0
41
+ end
42
+ end
43
+
44
+ specify "version has output on stdout and exits 0" do
45
+ kba = Keybox::Application::Base.new(["--ver"])
46
+ kba.stdout = StringIO.new
47
+ begin
48
+ kba.run
49
+ rescue SystemExit => se
50
+ se.status.should_eql 0
51
+ kba.stdout.string.length.should_be > 0
52
+ end
53
+ end
54
+
55
+ end
56
+
@@ -0,0 +1,46 @@
1
+ require 'tempfile'
2
+ require 'keybox'
3
+ require 'keybox/convert/csv'
4
+ context "CSV Convert class" do
5
+ setup do
6
+ @import_csv = Tempfile.new("keybox_import.csv")
7
+ @import_csv.puts "title,hostname,username,password,additional_info"
8
+ @import_csv.puts "example host,host.example.com,guest,mysecretpassword,use this account only for honeybots"
9
+ @import_csv.puts "example site,http://www.example.com,guest,mywebpassword,web forum login"
10
+ @import_csv.close
11
+
12
+ @bad_import_csv = Tempfile.new("keybox_bad_header.csv")
13
+ # missing a valid header
14
+ @bad_import_csv.puts "ttle,host,username,password,additional_info"
15
+ @bad_import_csv.puts "example host,host.example.com,guest,mysecretpassword,use this account only for honeybots"
16
+ @bad_import_csv.puts "example site,http://www.example.com,guest,mywebpassword,web forum login"
17
+
18
+ @export_csv = Tempfile.new("keybox_export.csv")
19
+
20
+ end
21
+
22
+ teardown do
23
+ @import_csv.unlink
24
+ @bad_import_csv.unlink
25
+ @export_csv.unlink
26
+ end
27
+
28
+ specify "able to load records from a file" do
29
+ entries = Keybox::Convert::CSV.from_file(@import_csv.path)
30
+ entries.size.should_eql 2
31
+ entries[0].hostname.should_eql "host.example.com"
32
+ entries[1].password.should_eql "mywebpassword"
33
+ end
34
+
35
+ specify "throws error if the header is bad" do
36
+ lambda { Keybox::Convert::CSV.from_file(@bad_import_csv.path) }.should_raise Keybox::ValidationError
37
+ end
38
+
39
+ specify "able to write to a csv file" do
40
+ entries = Keybox::Convert::CSV.from_file(@import_csv.path)
41
+ Keybox::Convert::CSV.to_file(entries,@export_csv.path)
42
+ @export_csv.open
43
+ data = @export_csv.read
44
+ data.size.should_be > 0
45
+ end
46
+ end
@@ -0,0 +1,63 @@
1
+ require 'keybox'
2
+ context "Account Entry" do
3
+ specify "fields get set correctly" do
4
+ k = Keybox::AccountEntry.new("a test title", "user")
5
+ k.username.should_eql "user"
6
+ k.title.should_eql "a test title"
7
+ end
8
+
9
+ specify "fields can be accessed" do
10
+ k = Keybox::AccountEntry.new("a test title", "user")
11
+ k.fields.should_include "title"
12
+ k.fields.should_include "username"
13
+ k.fields.should_include "additional_info"
14
+ end
15
+
16
+ specify "fields can be private or visible" do
17
+ k = Keybox::AccountEntry.new("a test title", "user")
18
+ k.private_fields.size.should_eql 0
19
+ k.visible_field?("title").should_eql true
20
+ end
21
+ end
22
+
23
+ context "Host Account" do
24
+ specify "fields get set correctly" do
25
+ ha = Keybox::HostAccountEntry.new("a title", "host", "user", "password")
26
+ ha.title.should_eql "a title"
27
+ ha.username.should_eql "user"
28
+ ha.hostname.should_eql "host"
29
+ ha.password.should_eql "password"
30
+ end
31
+
32
+ specify "password is displayable, private and non-visible" do
33
+ ha = Keybox::HostAccountEntry.new("a title", "host", "user", "password")
34
+ ha.display_fields.should_include "password"
35
+ ha.private_fields.should_include "password"
36
+ ha.visible_fields.should_not_include "password"
37
+ end
38
+ end
39
+
40
+ context "URL Account" do
41
+ specify "fields get set correctly" do
42
+ urla = Keybox::URLAccountEntry.new("url title", "http://www.example.com", "someuser")
43
+ urla.title.should_eql "url title"
44
+ urla.url.should_eql "http://www.example.com"
45
+ urla.username.should_eql "someuser"
46
+ end
47
+
48
+ specify "password hash is used" do
49
+ container = Keybox::Storage::Container.new("i love ruby", "/tmp/junk.yml")
50
+ urla = Keybox::URLAccountEntry.new("url title", "http://www.example.com", "someuser")
51
+ container << urla
52
+ urla.password.should_eql "589c0d91d"
53
+ end
54
+
55
+ specify "there is no password storage field, but there a private password field" do
56
+ urla = Keybox::URLAccountEntry.new("url title", "http://www.example.com", "someuser")
57
+ urla.fields.should_not_include "password"
58
+ urla.private_fields.should_include "password"
59
+ urla.private_field?("password").should_eql true
60
+ end
61
+
62
+ end
63
+
@@ -0,0 +1,268 @@
1
+ require 'tempfile'
2
+ require 'keybox'
3
+ require 'keybox/application/password_safe'
4
+
5
+ context "Keybox Password Safe Application" do
6
+ setup do
7
+
8
+ @passphrase = "i love ruby"
9
+ @testing_db = Tempfile.new("kps_db.yml")
10
+ @testing_cfg = Tempfile.new("kps_cfg.yml")
11
+ @path = @testing_db.path
12
+ container = Keybox::Storage::Container.new(@passphrase, @testing_db.path)
13
+ container << Keybox::HostAccountEntry.new("test account","localhost","guest", "rubyrocks")
14
+ container << Keybox::URLAccountEntry.new("example site", "http://www.example.com", "rubyhacker")
15
+ container.save
16
+ container.save("/tmp/jjh-db.yml")
17
+
18
+ @import_csv = Tempfile.new("keybox_import.csv")
19
+ @import_csv.puts "title,hostname,username,password,additional_info"
20
+ @import_csv.puts "example host,host.example.com,guest,mysecretpassword,use this account only for honeybots"
21
+ @import_csv.puts "example site,http://www.example.com,guest,mywebpassword,web forum login"
22
+ @import_csv.close
23
+
24
+ @bad_import_csv = Tempfile.new("keybox_bad_header.csv")
25
+ # missing a valid header
26
+ @bad_import_csv.puts "title,host,username,password,additional_info"
27
+ @bad_import_csv.puts "example host,host.example.com,guest,mysecretpassword,use this account only for honeybots"
28
+ @bad_import_csv.puts "example site,http://www.example.com,guest,mywebpassword,web forum login"
29
+
30
+ @export_csv = Tempfile.new("keybox_export.csv")
31
+
32
+ end
33
+
34
+ teardown do
35
+ @testing_db.unlink
36
+ @testing_cfg.unlink
37
+ @import_csv.unlink
38
+ @bad_import_csv.unlink
39
+ @export_csv.unlink
40
+ end
41
+
42
+ specify "nil argv should do nothing" do
43
+ kps = Keybox::Application::PasswordSafe.new
44
+ kps.error_message.should_be nil
45
+ end
46
+
47
+ specify "executing with no args should have output on stdout" do
48
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path])
49
+ kps.stdout = StringIO.new
50
+ kps.stdin = StringIO.new(@passphrase)
51
+ kps.run
52
+ kps.stdout.string.size.should_be > 0
53
+ end
54
+
55
+ specify "general options get set correctly" do
56
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--debug", "--no-use-hash-for-url"])
57
+ kps.merge_options
58
+ kps.options.db_file.should_eql @testing_db.path
59
+ kps.options.config_file.should_eql @testing_cfg.path
60
+ kps.options.debug.should_eql true
61
+ kps.options.use_password_hash_for_url.should_eql false
62
+ end
63
+
64
+ specify "more than one command options is an error" do
65
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--add", "account", "--edit", "account"])
66
+ kps.stderr = StringIO.new
67
+ kps.stdout = StringIO.new
68
+ begin
69
+ kps.run
70
+ rescue SystemExit => se
71
+ kps.error_message.should_satisfy { |msg| msg =~ /Only one of/m }
72
+ kps.stderr.string.should_satisfy { |msg| msg =~ /Only one of/m }
73
+ se.status.should_eql 1
74
+ end
75
+ end
76
+
77
+ specify "space separated words are okay for names" do
78
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--add", "An Example"])
79
+ kps.stderr = StringIO.new
80
+ kps.stdout = StringIO.new
81
+ prompted_values = [@passphrase, "An example"] + %w(example.com someuser apassword apassword noinfo yes)
82
+ kps.stdin = StringIO.new(prompted_values.join("\n"))
83
+ kps.run
84
+ kps.db.records.size.should_eql 3
85
+ end
86
+
87
+
88
+ specify "invalid options set the error message, exit 1 and have output on stderr" do
89
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path,"--invalid-option"])
90
+ kps.stderr = StringIO.new
91
+ kps.stdout = StringIO.new
92
+ begin
93
+ kps.run
94
+ rescue SystemExit => se
95
+ kps.error_message.should_satisfy { |msg| msg =~ /Try.*--help/m }
96
+ kps.stderr.string.should_satisfy { |msg| msg =~ /Try.*--help/m }
97
+ kps.stdout.string.size.should_eql 0
98
+ se.status.should == 1
99
+ end
100
+ end
101
+
102
+ specify "help has output on stdout and exits 0" do
103
+ kps = Keybox::Application::PasswordSafe.new(["--h"])
104
+ kps.stdout = StringIO.new
105
+ begin
106
+ kps.run
107
+ rescue SystemExit => se
108
+ se.status.should_eql 0
109
+ kps.stdout.string.length.should_be > 0
110
+ end
111
+ end
112
+
113
+ specify "version has output on stdout and exits 0" do
114
+ kps = Keybox::Application::PasswordSafe.new(["--ver"])
115
+ kps.stdout = StringIO.new
116
+ begin
117
+ kps.run
118
+ rescue SystemExit => se
119
+ se.status.should_eql 0
120
+ kps.stdout.string.length.should_be > 0
121
+ end
122
+ end
123
+
124
+ specify "prompted for password twice to create database initially" do
125
+ File.unlink(@testing_db.path)
126
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path])
127
+ kps.stdout = StringIO.new
128
+ kps.stdin = StringIO.new([@passphrase,@passphrase].join("\n"))
129
+ kps.run
130
+ kps.db.records.size.should_eql 0
131
+ kps.stdout.string.should_satisfy { |msg| msg =~ /Creating initial database./m }
132
+ kps.stdout.string.should_satisfy { |msg| msg =~ /Initial Password for/m }
133
+ end
134
+
135
+ specify "file can be opened with password" do
136
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path])
137
+ kps.stdout = StringIO.new
138
+ kps.stdin = StringIO.new(@passphrase + "\n")
139
+ kps.run
140
+ kps.db.records.size.should_eql 2
141
+ end
142
+
143
+ specify "adding an entry to the database works" do
144
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--add", "example.com"])
145
+ kps.stdout = StringIO.new
146
+ prompted_values = [@passphrase] + %w(example.com example.com someuser apassword apassword noinfo yes)
147
+ kps.stdin = StringIO.new(prompted_values.join("\n"))
148
+ kps.run
149
+ kps.db.records.size.should_eql 3
150
+ end
151
+
152
+ specify "editing an entry in the database works" do
153
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--edit", "localhost"])
154
+ kps.stdout = StringIO.new
155
+ prompted_values = [@passphrase] + %w(yes example.com example.com someother anewpassword anewpassword someinfo yes)
156
+ kps.stdin = StringIO.new(prompted_values.join("\n"))
157
+ kps.run
158
+ kps.db.records.size.should_eql 2
159
+ kps.db.find("someother")[0].additional_info.should_eql "someinfo"
160
+ end
161
+
162
+ specify "add a url entry to the database" do
163
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--use-hash-for-url", "--add", "http://www.example.com"])
164
+ kps.stdout = StringIO.new
165
+ prompted_values = [@passphrase] + %w(www.example.com http://www.example.com someuser noinfo yes)
166
+ kps.stdin = StringIO.new(prompted_values.join("\n"))
167
+ kps.run
168
+ kps.db.records.size.should_eql 3
169
+ end
170
+
171
+ specify "double prompting on failed password for entry to the database works" do
172
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--add", "example.com"])
173
+ kps.stdout = StringIO.new
174
+ prompted_values = [@passphrase, ""] + %w(example.com someuser apassword abadpassword abcdef abcdef noinfo yes)
175
+ kps.stdin = StringIO.new(prompted_values.join("\n"))
176
+ kps.run
177
+ kps.db.records.size.should_eql 3
178
+ end
179
+
180
+ specify "able to delete an entry" do
181
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--delete", "example"])
182
+ kps.stdout = StringIO.new
183
+ prompted_values = [@passphrase] + %w(Yes)
184
+ kps.stdin = StringIO.new(prompted_values.join("\n"))
185
+ kps.run
186
+ kps.db.records.size.should_eql 1
187
+ kps.stdout.string.should_satisfy { |msg| msg =~ /example' deleted/ }
188
+ end
189
+
190
+ specify "able to cancel deletion" do
191
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--delete", "example"])
192
+ kps.stdout = StringIO.new
193
+ prompted_values = [@passphrase] + %w(No)
194
+ kps.stdin = StringIO.new(prompted_values.join("\n"))
195
+ kps.run
196
+ kps.db.records.size.should_eql 2
197
+ kps.stdout.string.should_satisfy { |msg| msg =~ /example' deleted/ }
198
+ end
199
+
200
+ specify "list all the entries" do
201
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--list"])
202
+ kps.stdout = StringIO.new
203
+ kps.stdin = StringIO.new(@passphrase)
204
+ kps.run
205
+ kps.stdout.string.should_satisfy { |msg| msg =~ /2./m }
206
+ end
207
+
208
+ specify "listing no entries found" do
209
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--list", "nothing"])
210
+ kps.stdout = StringIO.new
211
+ kps.stdin = StringIO.new(@passphrase)
212
+ kps.run
213
+ kps.stdout.string.should_satisfy { |msg| msg =~ /No matching records were found./ }
214
+ end
215
+
216
+ specify "showing no entries found" do
217
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--show", "nothing"])
218
+ kps.stdout = StringIO.new
219
+ kps.stdin = StringIO.new(@passphrase)
220
+ kps.run
221
+ kps.stdout.string.should_satisfy { |msg| msg =~ /No matching records were found./ }
222
+ end
223
+
224
+
225
+ specify "show all the entries" do
226
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--show"])
227
+ kps.stdout = StringIO.new
228
+ kps.stdin = StringIO.new(@passphrase)
229
+ kps.run
230
+ kps.stdout.string.should_satisfy { |msg| msg =~ /2./m }
231
+ end
232
+
233
+ specify "changing master password works" do
234
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path, "--master-password"])
235
+ kps.stdout = StringIO.new
236
+ kps.stdin = StringIO.new([@passphrase, "I really love ruby.", "I really love ruby."].join("\n"))
237
+ kps.run
238
+ kps.stdout.string.should_satisfy { |msg| msg =~ /New master password set/m }
239
+ end
240
+
241
+ specify "importing from a valid csv file" do
242
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path,"-i", @import_csv.path])
243
+ kps.stdout = StringIO.new
244
+ kps.stdin = StringIO.new([@passphrase, @passphrase].join("\n"))
245
+ kps.run
246
+ kps.stdout.string.should_satisfy { |msg| msg =~ /Imported \d* records from/m }
247
+ end
248
+
249
+ specify "Error message give on invalid imported csv" do
250
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path,"-i", @bad_import_csv.path])
251
+ kps.stdout = StringIO.new
252
+ kps.stdin = StringIO.new([@passphrase, @passphrase].join("\n"))
253
+ begin
254
+ kps.run
255
+ rescue SystemExit => se
256
+ kps.stdout.string.should_satisfy { |msg| msg =~ /Error: There must be a header on the CSV /m }
257
+ se.status.should_eql 1
258
+ end
259
+ end
260
+
261
+ specify "able to export to a csv" do
262
+ kps = Keybox::Application::PasswordSafe.new(["-f", @testing_db.path, "-c", @testing_cfg.path,"-x", @export_csv.path])
263
+ kps.stdout = StringIO.new
264
+ kps.stdin = StringIO.new([@passphrase, @passphrase].join("\n"))
265
+ kps.run
266
+ kps.stdout.string.should_satisfy { |msg| msg =~ /Exported \d* records to/m }
267
+ end
268
+ end