htauth 1.2.0 → 2.0.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 +4 -4
- data/HISTORY.md +7 -0
- data/Manifest.txt +7 -5
- data/README.md +0 -1
- data/Rakefile +0 -2
- data/bin/htdigest-ruby +2 -9
- data/bin/htpasswd-ruby +2 -9
- data/lib/htauth.rb +2 -3
- data/lib/htauth/algorithm.rb +54 -14
- data/lib/htauth/cli.rb +8 -0
- data/lib/htauth/cli/digest.rb +130 -0
- data/lib/htauth/cli/passwd.rb +179 -0
- data/lib/htauth/console.rb +31 -0
- data/lib/htauth/crypt.rb +1 -2
- data/lib/htauth/digest_entry.rb +31 -11
- data/lib/htauth/digest_file.rb +95 -9
- data/lib/htauth/entry.rb +2 -2
- data/lib/htauth/error.rb +18 -0
- data/lib/htauth/file.rb +87 -16
- data/lib/htauth/md5.rb +1 -2
- data/lib/htauth/passwd_entry.rb +47 -15
- data/lib/htauth/passwd_file.rb +108 -11
- data/lib/htauth/plaintext.rb +1 -2
- data/lib/htauth/sha1.rb +13 -13
- data/lib/htauth/version.rb +2 -19
- data/spec/cli/digest_spec.rb +150 -0
- data/spec/cli/passwd_spec.rb +206 -0
- data/spec/digest_entry_spec.rb +55 -55
- data/spec/digest_file_spec.rb +50 -50
- data/spec/md5_spec.rb +9 -9
- data/spec/passwd_entry_spec.rb +133 -133
- data/spec/passwd_file_spec.rb +50 -50
- data/spec/plaintext_spec.rb +8 -8
- data/spec/sha1_spec.rb +8 -8
- data/spec/spec_helper.rb +7 -0
- metadata +11 -23
- data/lib/htauth/digest.rb +0 -132
- data/lib/htauth/errors.rb +0 -10
- data/lib/htauth/passwd.rb +0 -181
- data/spec/digest_spec.rb +0 -150
- data/spec/passwd_spec.rb +0 -206
data/lib/htauth/md5.rb
CHANGED
@@ -2,8 +2,7 @@ require 'htauth/algorithm'
|
|
2
2
|
require 'digest/md5'
|
3
3
|
|
4
4
|
module HTAuth
|
5
|
-
|
6
|
-
# an implementation of the MD5 based encoding algorithm
|
5
|
+
# Internal: an implementation of the MD5 based encoding algorithm
|
7
6
|
# as used in the apache htpasswd -m option
|
8
7
|
class Md5 < Algorithm
|
9
8
|
|
data/lib/htauth/passwd_entry.rb
CHANGED
@@ -1,18 +1,24 @@
|
|
1
|
-
require 'htauth/
|
1
|
+
require 'htauth/error'
|
2
2
|
require 'htauth/entry'
|
3
3
|
require 'htauth/algorithm'
|
4
4
|
|
5
5
|
module HTAuth
|
6
|
-
|
7
|
-
|
8
|
-
# A single record in an htdigest file.
|
6
|
+
# Internal: Object version of a single entry from a htpasswd file
|
9
7
|
class PasswdEntry < Entry
|
10
8
|
|
9
|
+
# Internal: the user of this entry
|
11
10
|
attr_accessor :user
|
11
|
+
# Internal: the password digest of this entry
|
12
12
|
attr_accessor :digest
|
13
|
+
# Internal: the algorithm used to create the digest of this entry
|
13
14
|
attr_reader :algorithm
|
14
15
|
|
15
16
|
class << self
|
17
|
+
# Internal: Create an instance of this class from a line of text
|
18
|
+
#
|
19
|
+
# line - a line of text from a htpasswd file
|
20
|
+
#
|
21
|
+
# Returns an instance of PasswdEntry
|
16
22
|
def from_line(line)
|
17
23
|
parts = is_entry!(line)
|
18
24
|
d = PasswdEntry.new(parts[0])
|
@@ -21,17 +27,26 @@ module HTAuth
|
|
21
27
|
return d
|
22
28
|
end
|
23
29
|
|
24
|
-
# test if
|
25
|
-
#
|
26
|
-
#
|
30
|
+
# Internal: test if the given line is valid for this Entry class
|
31
|
+
#
|
32
|
+
# A valid entry is a single line composed of two parts; a username and a
|
33
|
+
# password separated by a ':' character. Neither the username nor the
|
34
|
+
# password may contain a ':' character
|
35
|
+
#
|
36
|
+
# line - a line of text from a file
|
37
|
+
#
|
38
|
+
# Returns the individual parts of the line
|
39
|
+
# Raises InvalidPasswdEntry if it is not an valid entry
|
27
40
|
def is_entry!(line)
|
28
41
|
raise InvalidPasswdEntry, "line commented out" if line =~ /\A#/
|
29
42
|
parts = line.strip.split(":")
|
30
|
-
raise InvalidPasswdEntry, "line must be of the format username:
|
43
|
+
raise InvalidPasswdEntry, "line must be of the format username:password" if parts.size != 2
|
31
44
|
return parts
|
32
45
|
end
|
33
46
|
|
34
|
-
#
|
47
|
+
# Internal: Returns whether or not the line is a valid entry
|
48
|
+
#
|
49
|
+
# Returns true or false
|
35
50
|
def is_entry?(line)
|
36
51
|
begin
|
37
52
|
is_entry!(line)
|
@@ -42,13 +57,15 @@ module HTAuth
|
|
42
57
|
end
|
43
58
|
end
|
44
59
|
|
60
|
+
# Internal: Create a new Entry with the given user, password, and algorithm
|
45
61
|
def initialize(user, password = nil, alg = Algorithm::DEFAULT, alg_params = {} )
|
46
62
|
@user = user
|
47
63
|
alg = Algorithm::DEFAULT if alg == Algorithm::EXISTING
|
48
64
|
@algorithm = Algorithm.algorithm_from_name(alg, alg_params)
|
49
|
-
@digest =
|
65
|
+
@digest = calc_digest(password)
|
50
66
|
end
|
51
67
|
|
68
|
+
# Internal: set the algorithm for the entry
|
52
69
|
def algorithm=(alg)
|
53
70
|
if alg.kind_of?(Array) then
|
54
71
|
if alg.size == 1 then
|
@@ -62,35 +79,50 @@ module HTAuth
|
|
62
79
|
return @algorithm
|
63
80
|
end
|
64
81
|
|
82
|
+
# Internal: Update the password of the entry with its new value
|
83
|
+
#
|
84
|
+
# If we have an array of algorithms, then we set it to CRYPT
|
65
85
|
def password=(new_password)
|
66
86
|
if algorithm.kind_of?(Array) then
|
67
|
-
@algorithm = Algorithm.algorithm_from_name(
|
87
|
+
@algorithm = Algorithm.algorithm_from_name(Algorithm::CRYPT)
|
68
88
|
end
|
69
|
-
@digest =
|
89
|
+
@digest = calc_digest(new_password)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Internal: calculate the new digest of the given password
|
93
|
+
def calc_digest(password)
|
94
|
+
return nil unless password
|
95
|
+
algorithm.encode(password)
|
70
96
|
end
|
71
97
|
|
98
|
+
# Public: Check if the given password is the password of this entry
|
72
99
|
# check the password and make sure it works, in the case that the algorithm is unknown it
|
73
100
|
# tries all of the ones that it thinks it could be, and marks the algorithm if it matches
|
101
|
+
# when looking for a matche, we always compare all of them, no short
|
102
|
+
# circuiting
|
74
103
|
def authenticated?(check_password)
|
75
104
|
authed = false
|
76
105
|
if algorithm.kind_of?(Array) then
|
77
106
|
algorithm.each do |alg|
|
78
|
-
|
107
|
+
encoded = alg.encode(check_password)
|
108
|
+
if Algorithm.secure_compare(encoded, digest) then
|
79
109
|
@algorithm = alg
|
80
110
|
authed = true
|
81
|
-
break
|
82
111
|
end
|
83
112
|
end
|
84
113
|
else
|
85
|
-
|
114
|
+
encoded = algorithm.encode(check_password)
|
115
|
+
authed = Algorithm.secure_compare(encoded, digest)
|
86
116
|
end
|
87
117
|
return authed
|
88
118
|
end
|
89
119
|
|
120
|
+
# Internal: Returns the key of this entry
|
90
121
|
def key
|
91
122
|
return "#{user}"
|
92
123
|
end
|
93
124
|
|
125
|
+
# Internal: Returns the file line for this entry
|
94
126
|
def to_s
|
95
127
|
"#{user}:#{digest}"
|
96
128
|
end
|
data/lib/htauth/passwd_file.rb
CHANGED
@@ -1,25 +1,52 @@
|
|
1
1
|
require 'stringio'
|
2
2
|
require 'tempfile'
|
3
3
|
|
4
|
-
require 'htauth/
|
4
|
+
require 'htauth/error'
|
5
5
|
require 'htauth/file'
|
6
6
|
require 'htauth/passwd_entry'
|
7
7
|
|
8
8
|
module HTAuth
|
9
|
-
|
10
|
-
|
11
|
-
#
|
9
|
+
# Public: An API for managing an 'htpasswd' file
|
10
|
+
#
|
11
|
+
# Examples
|
12
|
+
#
|
13
|
+
# ::HTAuth::PasswdFile.open("my.passwd") do |pf|
|
14
|
+
# pf.has_entry?('myuser', 'myrealm')
|
15
|
+
# pf.add_or_update('someuser', 'myrealm', 'a password')
|
16
|
+
# pf.delete('someolduser', 'myotherrealm')
|
17
|
+
# end
|
18
|
+
#
|
12
19
|
class PasswdFile < HTAuth::File
|
13
20
|
|
21
|
+
# Private: The class implementing a single entry in the PasswdFile
|
14
22
|
ENTRY_KLASS = HTAuth::PasswdEntry
|
15
23
|
|
16
|
-
#
|
24
|
+
# Public: Checks if the given username exists in the file
|
25
|
+
#
|
26
|
+
# username - the username to check
|
27
|
+
#
|
28
|
+
# Examples
|
29
|
+
#
|
30
|
+
# passwd_file.has_entry?("myuser")
|
31
|
+
# # => true
|
32
|
+
#
|
33
|
+
# Returns true or false if the username
|
17
34
|
def has_entry?(username)
|
18
35
|
test_entry = PasswdEntry.new(username)
|
19
36
|
@entries.has_key?(test_entry.key)
|
20
37
|
end
|
21
38
|
|
22
|
-
# remove
|
39
|
+
# Public: remove the given username from the file
|
40
|
+
# The file is not written to disk until #save! is called.
|
41
|
+
#
|
42
|
+
# username - the username to remove
|
43
|
+
#
|
44
|
+
# Examples
|
45
|
+
#
|
46
|
+
# passwd_file.delete("myuser")
|
47
|
+
# passwd_file.save!
|
48
|
+
#
|
49
|
+
# Returns nothing
|
23
50
|
def delete(username)
|
24
51
|
if has_entry?(username) then
|
25
52
|
ir = internal_record(username)
|
@@ -31,7 +58,27 @@ module HTAuth
|
|
31
58
|
nil
|
32
59
|
end
|
33
60
|
|
34
|
-
#
|
61
|
+
# Public: Add or update the username entry with the new password and
|
62
|
+
# algorithm. This will add a new entry if the username does not exist in
|
63
|
+
# the file. If the entry does exist in the file, then the password
|
64
|
+
# of the entry is updated to the new password / algorithm
|
65
|
+
#
|
66
|
+
# The file is not written to disk until #save! is called.
|
67
|
+
#
|
68
|
+
# username - the username of the entry
|
69
|
+
# password - the username of the entry
|
70
|
+
# algorithm - the algorithm to use (default: "md5"). Valid options are:
|
71
|
+
# "md5", "sha1", "plaintext", or "crypt"
|
72
|
+
#
|
73
|
+
# Examples
|
74
|
+
#
|
75
|
+
# passwd_file.add_or_update("newuser", "password", Algorithm::SHA1)
|
76
|
+
# passwd_file.save!
|
77
|
+
#
|
78
|
+
# passwd_file.add_or_update("newuser", "password")
|
79
|
+
# passwd_file.save!
|
80
|
+
#
|
81
|
+
# Returns nothing.
|
35
82
|
def add_or_update(username, password, algorithm = Algorithm::DEFAULT)
|
36
83
|
if has_entry?(username) then
|
37
84
|
update(username, password, algorithm)
|
@@ -40,7 +87,23 @@ module HTAuth
|
|
40
87
|
end
|
41
88
|
end
|
42
89
|
|
43
|
-
#
|
90
|
+
# Public: Add a new record to the file.
|
91
|
+
#
|
92
|
+
# username - the username of the entry
|
93
|
+
# password - the username of the entry
|
94
|
+
# algorithm - the algorithm to use (default: "md5"). Valid options are:
|
95
|
+
# "md5", "sha1", "plaintext", or "crypt"
|
96
|
+
#
|
97
|
+
# Examples
|
98
|
+
#
|
99
|
+
# passwd_file.add("newuser", "password")
|
100
|
+
# passwd_file.save!
|
101
|
+
#
|
102
|
+
# passwd_file.add("newuser", "password", "sha1")
|
103
|
+
# passwd_file.save!
|
104
|
+
#
|
105
|
+
# Returns nothing.
|
106
|
+
# Raises PasswdFileError if the give username already exists.
|
44
107
|
def add(username, password, algorithm = Algorithm::DEFAULT)
|
45
108
|
raise PasswdFileError, "Unable to add already existing user #{username}" if has_entry?(username)
|
46
109
|
new_entry = PasswdEntry.new(username, password, algorithm)
|
@@ -51,7 +114,27 @@ module HTAuth
|
|
51
114
|
return nil
|
52
115
|
end
|
53
116
|
|
54
|
-
#
|
117
|
+
# Public: Update an existing record in the file.
|
118
|
+
#
|
119
|
+
# By default, the same algorithm that already exists for the entry will be
|
120
|
+
# used with the new password. You may change the algorithm for an entry by
|
121
|
+
# setting the `algorithm` parameter.
|
122
|
+
#
|
123
|
+
# username - the username of the entry
|
124
|
+
# password - the username of the entry
|
125
|
+
# algorithm - the algorithm to use (default: "existing"). Valid options are:
|
126
|
+
# "existing", "md5", "sha1", "plaintext", or "crypt"
|
127
|
+
#
|
128
|
+
# Examples
|
129
|
+
#
|
130
|
+
# passwd_file.update("newuser", "password")
|
131
|
+
# passwd_file.save!
|
132
|
+
#
|
133
|
+
# passwd_file.update("newuser", "password", "sha1")
|
134
|
+
# passwd_file.save!
|
135
|
+
#
|
136
|
+
# Returns nothing.
|
137
|
+
# Raises PasswdFileError if the give username does not exist.
|
55
138
|
def update(username, password, algorithm = Algorithm::EXISTING)
|
56
139
|
raise PasswdFileError, "Unable to update non-existent user #{username}" unless has_entry?(username)
|
57
140
|
ir = internal_record(username)
|
@@ -62,14 +145,28 @@ module HTAuth
|
|
62
145
|
return nil
|
63
146
|
end
|
64
147
|
|
65
|
-
#
|
66
|
-
#
|
148
|
+
# Public: Returns a copy of then given PasswdEntry from the file.
|
149
|
+
#
|
150
|
+
# Updating the PasswdEntry instance returned by this method will NOT update
|
151
|
+
# the file. To update the file, use #update and #save!
|
152
|
+
#
|
153
|
+
# username - the username of the entry
|
154
|
+
#
|
155
|
+
# Examples
|
156
|
+
#
|
157
|
+
# entry = password_file.fetch("myuser")
|
158
|
+
#
|
159
|
+
# Returns a PasswdEntry if the entry is found
|
160
|
+
# Returns nil if the entry is not found
|
67
161
|
def fetch(username)
|
68
162
|
return nil unless has_entry?(username)
|
69
163
|
ir = internal_record(username)
|
70
164
|
return ir['entry'].dup
|
71
165
|
end
|
72
166
|
|
167
|
+
# Internal: returns the class used for each entry
|
168
|
+
#
|
169
|
+
# Returns a Class
|
73
170
|
def entry_klass
|
74
171
|
ENTRY_KLASS
|
75
172
|
end
|
data/lib/htauth/plaintext.rb
CHANGED
data/lib/htauth/sha1.rb
CHANGED
@@ -3,21 +3,21 @@ require 'digest/sha1'
|
|
3
3
|
require 'base64'
|
4
4
|
|
5
5
|
module HTAuth
|
6
|
+
# Internal: an implementation of the SHA based encoding algorithm
|
7
|
+
# as used in the apache htpasswd -s option
|
8
|
+
#
|
9
|
+
class Sha1 < Algorithm
|
6
10
|
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# ignore the params
|
12
|
-
def initialize(params = {})
|
13
|
-
end
|
11
|
+
# ignore the params
|
12
|
+
def initialize(params = {})
|
13
|
+
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def prefix
|
16
|
+
"{SHA}"
|
17
|
+
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
19
|
+
def encode(password)
|
20
|
+
"#{prefix}#{Base64.encode64(::Digest::SHA1.digest(password)).strip}"
|
22
21
|
end
|
22
|
+
end
|
23
23
|
end
|
data/lib/htauth/version.rb
CHANGED
@@ -1,21 +1,4 @@
|
|
1
1
|
module HTAuth
|
2
|
-
|
3
|
-
|
4
|
-
STRING = HTAuth::VERSION
|
5
|
-
def to_a
|
6
|
-
STRING.split(".")
|
7
|
-
end
|
8
|
-
|
9
|
-
def to_s
|
10
|
-
STRING
|
11
|
-
end
|
12
|
-
|
13
|
-
module_function :to_a
|
14
|
-
module_function :to_s
|
15
|
-
|
16
|
-
MAJOR = Version.to_a[0]
|
17
|
-
MINOR = Version.to_a[1]
|
18
|
-
BUILD = Version.to_a[2]
|
19
|
-
|
20
|
-
end
|
2
|
+
# Public: The version of the htauth library
|
3
|
+
VERSION = "2.0.0"
|
21
4
|
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'htauth/cli/digest'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe HTAuth::CLI::Digest do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
|
9
|
+
# existing
|
10
|
+
@tf = Tempfile.new("rpasswrd-digest-test")
|
11
|
+
@tf.write(IO.read(DIGEST_ORIGINAL_TEST_FILE))
|
12
|
+
@tf.close
|
13
|
+
@rdigest = HTAuth::CLI::Digest.new
|
14
|
+
|
15
|
+
# new file
|
16
|
+
@new_file = File.join(File.dirname(@tf.path), "new-testfile")
|
17
|
+
|
18
|
+
# rework stdout and stderr
|
19
|
+
@stdout = ConsoleIO.new
|
20
|
+
@old_stdout = $stdout
|
21
|
+
$stdout = @stdout
|
22
|
+
|
23
|
+
@stderr = ConsoleIO.new
|
24
|
+
@old_stderr = $stderr
|
25
|
+
$stderr = @stderr
|
26
|
+
|
27
|
+
@stdin = ConsoleIO.new
|
28
|
+
@old_stdin = $stdin
|
29
|
+
$stdin = @stdin
|
30
|
+
end
|
31
|
+
|
32
|
+
after(:each) do
|
33
|
+
@tf.close(true)
|
34
|
+
$stderr = @old_stderr
|
35
|
+
$stdout = @old_stdout
|
36
|
+
$stdin = @old_stdin
|
37
|
+
File.unlink(@new_file) if File.exist?(@new_file)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "displays help appropriately" do
|
41
|
+
begin
|
42
|
+
@rdigest.run([ "-h" ])
|
43
|
+
rescue SystemExit => se
|
44
|
+
se.status.must_equal 1
|
45
|
+
@stdout.string.must_match( /passwordfile realm username/m )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "displays the version appropriately" do
|
50
|
+
begin
|
51
|
+
@rdigest.run([ "--version" ])
|
52
|
+
rescue SystemExit => se
|
53
|
+
se.status.must_equal 1
|
54
|
+
@stdout.string.must_match( /version #{HTAuth::VERSION}/ )
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "creates a new file with one entries" do
|
59
|
+
begin
|
60
|
+
@stdin.puts "b secret"
|
61
|
+
@stdin.puts "b secret"
|
62
|
+
@stdin.rewind
|
63
|
+
@rdigest.run([ "-c", @new_file, "htauth", "bob" ])
|
64
|
+
rescue SystemExit => se
|
65
|
+
se.status.must_equal 0
|
66
|
+
IO.read(@new_file).must_equal IO.readlines(DIGEST_ORIGINAL_TEST_FILE).first
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "truncates an exiting file if told to create a new file" do
|
71
|
+
begin
|
72
|
+
@stdin.puts "b secret"
|
73
|
+
@stdin.puts "b secret"
|
74
|
+
@stdin.rewind
|
75
|
+
@rdigest.run([ "-c", @tf.path, "htauth", "bob"])
|
76
|
+
rescue SystemExit => se
|
77
|
+
se.status.must_equal 0
|
78
|
+
IO.read(@tf.path).must_equal IO.read(DIGEST_DELETE_TEST_FILE)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it "adds an entry to an existing file" do
|
83
|
+
begin
|
84
|
+
@stdin.puts "c secret"
|
85
|
+
@stdin.puts "c secret"
|
86
|
+
@stdin.rewind
|
87
|
+
@rdigest.run([ @tf.path, "htauth-new", "charlie" ])
|
88
|
+
rescue SystemExit => se
|
89
|
+
se.status.must_equal 0
|
90
|
+
IO.read(@tf.path).must_equal IO.read(DIGEST_ADD_TEST_FILE)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "updates an entry in an existing file" do
|
95
|
+
begin
|
96
|
+
@stdin.puts "a new secret"
|
97
|
+
@stdin.puts "a new secret"
|
98
|
+
@stdin.rewind
|
99
|
+
@rdigest.run([ @tf.path, "htauth", "alice" ])
|
100
|
+
rescue SystemExit => se
|
101
|
+
@stderr.string.must_equal ""
|
102
|
+
se.status.must_equal 0
|
103
|
+
IO.read(@tf.path).must_equal IO.read(DIGEST_UPDATE_TEST_FILE)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "deletes an entry in an existing file" do
|
108
|
+
begin
|
109
|
+
@rdigest.run([ "-d", @tf.path, "htauth", "alice" ])
|
110
|
+
rescue SystemExit => se
|
111
|
+
@stderr.string.must_equal ""
|
112
|
+
se.status.must_equal 0
|
113
|
+
IO.read(@tf.path).must_equal IO.read(DIGEST_DELETE_TEST_FILE)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "has an error if it does not have permissions on the file" do
|
118
|
+
begin
|
119
|
+
@stdin.puts "a secret"
|
120
|
+
@stdin.puts "a secret"
|
121
|
+
@stdin.rewind
|
122
|
+
@rdigest.run([ "-c", "/etc/you-cannot-create-me", "htauth", "alice"])
|
123
|
+
rescue SystemExit => se
|
124
|
+
@stderr.string.must_match( %r{Could not open password file /etc/you-cannot-create-me}m )
|
125
|
+
se.status.must_equal 1
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it "has an error if the input passwords do not match" do
|
130
|
+
begin
|
131
|
+
@stdin.puts "a secret"
|
132
|
+
@stdin.puts "a bad secret"
|
133
|
+
@stdin.rewind
|
134
|
+
@rdigest.run([ @tf.path, "htauth", "alice"])
|
135
|
+
rescue SystemExit => se
|
136
|
+
@stderr.string.must_match( /They don't match, sorry./m )
|
137
|
+
se.status.must_equal 1
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
it "has an error if the options are incorrect" do
|
142
|
+
begin
|
143
|
+
@rdigest.run(["--blah"])
|
144
|
+
rescue SystemExit => se
|
145
|
+
@stderr.string.must_match( /ERROR:/m )
|
146
|
+
se.status.must_equal 1
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|