htauth 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/HISTORY.md +7 -0
- data/Manifest.txt +4 -0
- data/README.md +36 -22
- data/Rakefile +8 -4
- data/lib/htauth.rb +1 -0
- data/lib/htauth/algorithm.rb +32 -29
- data/lib/htauth/bcrypt.rb +35 -0
- data/lib/htauth/cli/digest.rb +1 -1
- data/lib/htauth/cli/passwd.rb +90 -30
- data/lib/htauth/console.rb +5 -1
- data/lib/htauth/crypt.rb +15 -4
- data/lib/htauth/descendant_tracker.rb +46 -0
- data/lib/htauth/md5.rb +27 -7
- data/lib/htauth/passwd_entry.rb +22 -17
- data/lib/htauth/passwd_file.rb +46 -12
- data/lib/htauth/plaintext.rb +11 -4
- data/lib/htauth/sha1.rb +8 -5
- data/lib/htauth/version.rb +1 -1
- data/spec/algorithm_spec.rb +8 -0
- data/spec/bcrypt_spec.rb +33 -0
- data/spec/cli/digest_spec.rb +22 -23
- data/spec/cli/passwd_spec.rb +160 -36
- data/spec/crypt_spec.rb +1 -5
- data/spec/digest_entry_spec.rb +19 -19
- data/spec/digest_file_spec.rb +10 -10
- data/spec/md5_spec.rb +1 -5
- data/spec/passwd_entry_spec.rb +63 -42
- data/spec/passwd_file_spec.rb +31 -13
- data/spec/plaintext_spec.rb +1 -5
- data/spec/sha1_spec.rb +1 -5
- data/tasks/default.rake +3 -3
- data/tasks/this.rb +2 -2
- metadata +32 -13
data/lib/htauth/console.rb
CHANGED
@@ -20,12 +20,16 @@ module HTAuth
|
|
20
20
|
|
21
21
|
def ask(prompt)
|
22
22
|
output.print prompt
|
23
|
-
answer =
|
23
|
+
answer = read_answer
|
24
24
|
output.puts
|
25
25
|
raise ConsoleError, "No input given" if answer.nil?
|
26
26
|
answer.strip!
|
27
27
|
raise ConsoleError, "No input given" if answer.length == 0
|
28
28
|
return answer
|
29
29
|
end
|
30
|
+
|
31
|
+
def read_answer
|
32
|
+
input.noecho(&:gets)
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
data/lib/htauth/crypt.rb
CHANGED
@@ -4,12 +4,23 @@ module HTAuth
|
|
4
4
|
# Internal: The basic crypt algorithm
|
5
5
|
class Crypt < Algorithm
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
ENTRY_LENGTH = 13
|
8
|
+
ENTRY_REGEX = %r{\A[^$:\s]{#{ENTRY_LENGTH}}\z}
|
9
|
+
|
10
|
+
def self.handles?(password_entry)
|
11
|
+
ENTRY_REGEX.match?(password_entry)
|
9
12
|
end
|
10
13
|
|
11
|
-
def
|
12
|
-
|
14
|
+
def self.extract_salt_from_existing_password_field(existing)
|
15
|
+
existing[0,2]
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(params = {})
|
19
|
+
if existing = (params['existing'] || params[:existing]) then
|
20
|
+
@salt = self.class.extract_salt_from_existing_password_field(existing)
|
21
|
+
else
|
22
|
+
@salt = params[:salt] || params['salt'] || gen_salt
|
23
|
+
end
|
13
24
|
end
|
14
25
|
|
15
26
|
def encode(password)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module HTAuth
|
2
|
+
#
|
3
|
+
# Use by either
|
4
|
+
#
|
5
|
+
# class Foo
|
6
|
+
# extend DescendantTracker
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# or
|
10
|
+
#
|
11
|
+
# class Foo
|
12
|
+
# class << self
|
13
|
+
# include DescendantTracker
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# It will track all the classes that inherit from the extended class and keep
|
18
|
+
# them in a Set that is available via the 'children' method.
|
19
|
+
#
|
20
|
+
module DescendantTracker
|
21
|
+
def inherited( klass )
|
22
|
+
return unless klass.instance_of?( Class )
|
23
|
+
self.children << klass
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# The list of children that are registered
|
28
|
+
#
|
29
|
+
def children
|
30
|
+
unless defined? @children
|
31
|
+
@children = Array.new
|
32
|
+
end
|
33
|
+
return @children
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# find the child that returns truthy for then given method and
|
38
|
+
# parameters
|
39
|
+
#
|
40
|
+
def find_child( method, *args )
|
41
|
+
children.find do |child|
|
42
|
+
child.send( method, *args )
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/htauth/md5.rb
CHANGED
@@ -6,21 +6,41 @@ module HTAuth
|
|
6
6
|
# as used in the apache htpasswd -m option
|
7
7
|
class Md5 < Algorithm
|
8
8
|
|
9
|
-
DIGEST_LENGTH
|
9
|
+
DIGEST_LENGTH = 16
|
10
|
+
PAD_LENGTH = 6
|
11
|
+
PREFIX = "$apr1$".freeze
|
12
|
+
SALT_CHARS_STR = SALT_CHARS.join('')
|
13
|
+
ENTRY_REGEX = %r[
|
14
|
+
\A
|
15
|
+
#{Regexp.escape(PREFIX)}
|
16
|
+
[#{SALT_CHARS_STR}]{#{SALT_LENGTH}}
|
17
|
+
#{Regexp.escape("$")}
|
18
|
+
[#{SALT_CHARS_STR}]{#{DIGEST_LENGTH + PAD_LENGTH}}
|
19
|
+
\z
|
20
|
+
]x
|
21
|
+
|
22
|
+
def self.handles?(password_entry)
|
23
|
+
ENTRY_REGEX.match?(password_entry)
|
24
|
+
end
|
10
25
|
|
11
|
-
def
|
12
|
-
|
26
|
+
def self.extract_salt_from_existing_password_field(existing)
|
27
|
+
p = existing.split("$")
|
28
|
+
return p[2]
|
13
29
|
end
|
14
30
|
|
15
|
-
def
|
16
|
-
|
31
|
+
def initialize(params = {})
|
32
|
+
if existing = (params['existing'] || params[:existing]) then
|
33
|
+
@salt = self.class.extract_salt_from_existing_password_field(existing)
|
34
|
+
else
|
35
|
+
@salt = params[:salt] || params['salt'] || gen_salt
|
36
|
+
end
|
17
37
|
end
|
18
38
|
|
19
39
|
# this algorigthm pulled straight from apr_md5_encode() and converted to ruby syntax
|
20
40
|
def encode(password)
|
21
41
|
primary = ::Digest::MD5.new
|
22
42
|
primary << password
|
23
|
-
primary <<
|
43
|
+
primary << PREFIX
|
24
44
|
primary << @salt
|
25
45
|
|
26
46
|
md5_t = ::Digest::MD5.digest("#{password}#{@salt}#{password}")
|
@@ -46,7 +66,7 @@ module HTAuth
|
|
46
66
|
|
47
67
|
pd = primary.digest
|
48
68
|
|
49
|
-
encoded_password = "#{
|
69
|
+
encoded_password = "#{PREFIX}#{@salt}$"
|
50
70
|
|
51
71
|
# apr_md5_encode has this comment about a 60Mhz Pentium above this loop.
|
52
72
|
1000.times do |x|
|
data/lib/htauth/passwd_entry.rb
CHANGED
@@ -12,6 +12,8 @@ module HTAuth
|
|
12
12
|
attr_accessor :digest
|
13
13
|
# Internal: the algorithm used to create the digest of this entry
|
14
14
|
attr_reader :algorithm
|
15
|
+
# Internal: the algorithm arguments used to create the digest of this entry
|
16
|
+
attr_reader :algorithm_args
|
15
17
|
|
16
18
|
class << self
|
17
19
|
# Internal: Create an instance of this class from a line of text
|
@@ -23,7 +25,7 @@ module HTAuth
|
|
23
25
|
parts = is_entry!(line)
|
24
26
|
d = PasswdEntry.new(parts[0])
|
25
27
|
d.digest = parts[1]
|
26
|
-
d.algorithm = Algorithm.
|
28
|
+
d.algorithm = Algorithm.algorithm_from_field(parts[1])
|
27
29
|
return d
|
28
30
|
end
|
29
31
|
|
@@ -67,23 +69,31 @@ module HTAuth
|
|
67
69
|
|
68
70
|
# Internal: set the algorithm for the entry
|
69
71
|
def algorithm=(alg)
|
70
|
-
if alg
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
return @algorithm if Algorithm::EXISTING == alg
|
73
|
+
case alg
|
74
|
+
when String
|
75
|
+
@algorithm = Algorithm.algorithm_from_name(alg)
|
76
|
+
when ::HTAuth::Algorithm
|
77
|
+
@algorithm = alg
|
76
78
|
else
|
77
|
-
|
79
|
+
raise InvalidAlgorithmError, "Unable to assign #{alg} to algorithm"
|
78
80
|
end
|
79
81
|
return @algorithm
|
80
82
|
end
|
81
83
|
|
84
|
+
# Internal: set fields on the algorithm
|
85
|
+
def algorithm_args=(args)
|
86
|
+
args.each do |key, value|
|
87
|
+
method = "#{key}="
|
88
|
+
@algorithm.send(method, value) if @algorithm.respond_to?(method)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
82
92
|
# Internal: Update the password of the entry with its new value
|
83
93
|
#
|
84
94
|
# If we have an array of algorithms, then we set it to CRYPT
|
85
95
|
def password=(new_password)
|
86
|
-
if algorithm.kind_of?(
|
96
|
+
if algorithm.kind_of?(HTAuth::Plaintext) then
|
87
97
|
@algorithm = Algorithm.algorithm_from_name(Algorithm::CRYPT)
|
88
98
|
end
|
89
99
|
@digest = calc_digest(new_password)
|
@@ -102,14 +112,9 @@ module HTAuth
|
|
102
112
|
# circuiting
|
103
113
|
def authenticated?(check_password)
|
104
114
|
authed = false
|
105
|
-
if algorithm.kind_of?(
|
106
|
-
|
107
|
-
|
108
|
-
if Algorithm.secure_compare(encoded, digest) then
|
109
|
-
@algorithm = alg
|
110
|
-
authed = true
|
111
|
-
end
|
112
|
-
end
|
115
|
+
if algorithm.kind_of?(Bcrypt) then
|
116
|
+
bc = ::BCrypt::Password.new(digest)
|
117
|
+
authed = bc.is_password?(check_password)
|
113
118
|
else
|
114
119
|
encoded = algorithm.encode(check_password)
|
115
120
|
authed = Algorithm.secure_compare(encoded, digest)
|
data/lib/htauth/passwd_file.rb
CHANGED
@@ -66,9 +66,13 @@ module HTAuth
|
|
66
66
|
# The file is not written to disk until #save! is called.
|
67
67
|
#
|
68
68
|
# username - the username of the entry
|
69
|
-
# password - the
|
69
|
+
# password - the password of the entry
|
70
70
|
# algorithm - the algorithm to use (default: "md5"). Valid options are:
|
71
|
-
# "md5", "sha1", "plaintext", or "crypt"
|
71
|
+
# "md5", "bcrypt", "sha1", "plaintext", or "crypt"
|
72
|
+
# algorithm_args - key-value pairs of arguments that are passed to the
|
73
|
+
# algorithm, currently this is only used to pass the cost
|
74
|
+
# to the bcrypt algorithm
|
75
|
+
#
|
72
76
|
#
|
73
77
|
# Examples
|
74
78
|
#
|
@@ -79,20 +83,23 @@ module HTAuth
|
|
79
83
|
# passwd_file.save!
|
80
84
|
#
|
81
85
|
# Returns nothing.
|
82
|
-
def add_or_update(username, password, algorithm = Algorithm::DEFAULT)
|
86
|
+
def add_or_update(username, password, algorithm = Algorithm::DEFAULT, algorithm_args = {})
|
83
87
|
if has_entry?(username) then
|
84
|
-
update(username, password, algorithm)
|
88
|
+
update(username, password, algorithm, algorithm_args)
|
85
89
|
else
|
86
|
-
add(username, password, algorithm)
|
90
|
+
add(username, password, algorithm, algorithm_args)
|
87
91
|
end
|
88
92
|
end
|
89
93
|
|
90
94
|
# Public: Add a new record to the file.
|
91
95
|
#
|
92
96
|
# username - the username of the entry
|
93
|
-
# password - the
|
97
|
+
# password - the password of the entry
|
94
98
|
# algorithm - the algorithm to use (default: "md5"). Valid options are:
|
95
|
-
# "md5", "sha1", "plaintext", or "crypt"
|
99
|
+
# "md5", "bcrypt", "sha1", "plaintext", or "crypt"
|
100
|
+
# algorithm_args - key-value pairs of arguments that are passed to the
|
101
|
+
# algorithm, currently this is only used to pass the cost
|
102
|
+
# to the bcrypt algorithm
|
96
103
|
#
|
97
104
|
# Examples
|
98
105
|
#
|
@@ -102,11 +109,14 @@ module HTAuth
|
|
102
109
|
# passwd_file.add("newuser", "password", "sha1")
|
103
110
|
# passwd_file.save!
|
104
111
|
#
|
112
|
+
# passwd_file.add("newuser", "password", "bcrypt", { cost: 12 })
|
113
|
+
# passwd_file.save!
|
114
|
+
#
|
105
115
|
# Returns nothing.
|
106
116
|
# Raises PasswdFileError if the give username already exists.
|
107
|
-
def add(username, password, algorithm = Algorithm::DEFAULT)
|
117
|
+
def add(username, password, algorithm = Algorithm::DEFAULT, algorithm_args = {})
|
108
118
|
raise PasswdFileError, "Unable to add already existing user #{username}" if has_entry?(username)
|
109
|
-
new_entry = PasswdEntry.new(username, password, algorithm)
|
119
|
+
new_entry = PasswdEntry.new(username, password, algorithm, algorithm_args)
|
110
120
|
new_index = @lines.size
|
111
121
|
@lines << new_entry.to_s
|
112
122
|
@entries[new_entry.key] = { 'entry' => new_entry, 'line_index' => new_index }
|
@@ -121,9 +131,12 @@ module HTAuth
|
|
121
131
|
# setting the `algorithm` parameter.
|
122
132
|
#
|
123
133
|
# username - the username of the entry
|
124
|
-
# password - the
|
134
|
+
# password - the password of the entry
|
125
135
|
# algorithm - the algorithm to use (default: "existing"). Valid options are:
|
126
|
-
# "existing", "md5", "sha1", "plaintext", or "crypt"
|
136
|
+
# "existing", "md5", "bcrypt", "sha1", "plaintext", or "crypt"
|
137
|
+
# algorithm_args - key-value pairs of arguments that are passed to the
|
138
|
+
# algorithm, currently this is only used to pass the cost
|
139
|
+
# to the bcrypt algorithm
|
127
140
|
#
|
128
141
|
# Examples
|
129
142
|
#
|
@@ -133,12 +146,16 @@ module HTAuth
|
|
133
146
|
# passwd_file.update("newuser", "password", "sha1")
|
134
147
|
# passwd_file.save!
|
135
148
|
#
|
149
|
+
# passwd_file.update("newuser", "password", "bcrypt", { cost: 12 })
|
150
|
+
# passwd_file.save!
|
151
|
+
#
|
136
152
|
# Returns nothing.
|
137
153
|
# Raises PasswdFileError if the give username does not exist.
|
138
|
-
def update(username, password, algorithm = Algorithm::EXISTING)
|
154
|
+
def update(username, password, algorithm = Algorithm::EXISTING, algorithm_args = {})
|
139
155
|
raise PasswdFileError, "Unable to update non-existent user #{username}" unless has_entry?(username)
|
140
156
|
ir = internal_record(username)
|
141
157
|
ir['entry'].algorithm = algorithm
|
158
|
+
ir['entry'].algorithm_args = algorithm_args.dup
|
142
159
|
ir['entry'].password = password
|
143
160
|
@lines[ir['line_index']] = ir['entry'].to_s
|
144
161
|
dirty!
|
@@ -164,6 +181,23 @@ module HTAuth
|
|
164
181
|
return ir['entry'].dup
|
165
182
|
end
|
166
183
|
|
184
|
+
# Public: authenticates the password of a given username
|
185
|
+
#
|
186
|
+
# Check the password file for the given user, and check the input password
|
187
|
+
# against the existing one.
|
188
|
+
#
|
189
|
+
# Examples
|
190
|
+
#
|
191
|
+
# authenticated = password_file.authenticated?("alice", "a secret")
|
192
|
+
#
|
193
|
+
# Returns true or false if the user exists
|
194
|
+
# Raises PasswordFileErrorif the given username does not exist
|
195
|
+
def authenticated?(username, password)
|
196
|
+
raise PasswdFileError, "Unable to authenticate a non-existent user #{username}" unless has_entry?(username)
|
197
|
+
ir = internal_record(username)
|
198
|
+
return ir['entry'].authenticated?(password)
|
199
|
+
end
|
200
|
+
|
167
201
|
# Internal: returns the class used for each entry
|
168
202
|
#
|
169
203
|
# Returns a Class
|
data/lib/htauth/plaintext.rb
CHANGED
@@ -3,12 +3,19 @@ require 'htauth/algorithm'
|
|
3
3
|
module HTAuth
|
4
4
|
# Internal: the plaintext algorithm, which does absolutly nothing
|
5
5
|
class Plaintext < Algorithm
|
6
|
-
|
7
|
-
|
6
|
+
|
7
|
+
ENTRY_REGEX = /\A[^$:]*\Z/
|
8
|
+
|
9
|
+
def self.entry_matches?(entry)
|
10
|
+
ENTRY_REGEX.match?(entry)
|
8
11
|
end
|
9
12
|
|
10
|
-
def
|
11
|
-
|
13
|
+
def self.handles?(password_entry)
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
# ignore parameters
|
18
|
+
def initialize(params = {})
|
12
19
|
end
|
13
20
|
|
14
21
|
def encode(password)
|
data/lib/htauth/sha1.rb
CHANGED
@@ -8,16 +8,19 @@ module HTAuth
|
|
8
8
|
#
|
9
9
|
class Sha1 < Algorithm
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
PREFIX = '{SHA}'.freeze
|
12
|
+
ENTRY_REGEX = %r[\A#{Regexp.escape(PREFIX)}[A-Za-z0-9+\/=]{28}\z].freeze
|
13
|
+
|
14
|
+
def self.handles?(password_entry)
|
15
|
+
ENTRY_REGEX.match?(password_entry)
|
13
16
|
end
|
14
17
|
|
15
|
-
|
16
|
-
|
18
|
+
# ignore the params
|
19
|
+
def initialize(params = {})
|
17
20
|
end
|
18
21
|
|
19
22
|
def encode(password)
|
20
|
-
"#{
|
23
|
+
"#{PREFIX}#{Base64.encode64(::Digest::SHA1.digest(password)).strip}"
|
21
24
|
end
|
22
25
|
end
|
23
26
|
end
|
data/lib/htauth/version.rb
CHANGED
data/spec/bcrypt_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'htauth/bcrypt'
|
3
|
+
|
4
|
+
describe HTAuth::Bcrypt do
|
5
|
+
it "encrypts the same way that apache does by default" do
|
6
|
+
apache_hash = '$2y$05$X7XeXxp0uAO92AGG2P4/fu0mj7MrRDQnlBTkwZLd9rKiH2OUBb9/K'
|
7
|
+
reparsed = ::BCrypt::Password.new(apache_hash)
|
8
|
+
cost = reparsed.cost
|
9
|
+
|
10
|
+
_(cost).must_equal HTAuth::Bcrypt::DEFAULT_APACHE_COST
|
11
|
+
_(reparsed.is_password?("a secret")).must_equal true
|
12
|
+
|
13
|
+
bcrypt = HTAuth::Bcrypt.new(:cost => cost)
|
14
|
+
local_hash = bcrypt.encode("a secret")
|
15
|
+
|
16
|
+
_(local_hash.is_password?("a secret")).must_equal true
|
17
|
+
_(local_hash.cost).must_equal cost
|
18
|
+
end
|
19
|
+
|
20
|
+
it "encrypts the same way that apache does with different cost" do
|
21
|
+
apache_hash = '$2y$12$O3mBah33UilOkwXrS0kXuOPFBKLBCIp7V.AVvEZQcbnAM5SJLQnfq'
|
22
|
+
reparsed = ::BCrypt::Password.new(apache_hash)
|
23
|
+
cost = reparsed.cost
|
24
|
+
|
25
|
+
_(reparsed.is_password?("a secret")).must_equal true
|
26
|
+
|
27
|
+
bcrypt = HTAuth::Bcrypt.new(:cost => cost)
|
28
|
+
local_hash = bcrypt.encode("a secret")
|
29
|
+
|
30
|
+
_(local_hash.is_password?("a secret")).must_equal true
|
31
|
+
_(local_hash.cost).must_equal cost
|
32
|
+
end
|
33
|
+
end
|
data/spec/cli/digest_spec.rb
CHANGED
@@ -41,8 +41,8 @@ describe HTAuth::CLI::Digest do
|
|
41
41
|
begin
|
42
42
|
@rdigest.run([ "-h" ])
|
43
43
|
rescue SystemExit => se
|
44
|
-
se.status.must_equal 1
|
45
|
-
@stdout.string.must_match( /passwordfile realm username/m )
|
44
|
+
_(se.status).must_equal 1
|
45
|
+
_(@stdout.string).must_match( /passwordfile realm username/m )
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -50,8 +50,8 @@ describe HTAuth::CLI::Digest do
|
|
50
50
|
begin
|
51
51
|
@rdigest.run([ "--version" ])
|
52
52
|
rescue SystemExit => se
|
53
|
-
se.status.must_equal 1
|
54
|
-
@stdout.string.must_match( /version #{HTAuth::VERSION}/ )
|
53
|
+
_(se.status).must_equal 1
|
54
|
+
_(@stdout.string).must_match( /version #{HTAuth::VERSION}/ )
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
@@ -62,8 +62,8 @@ describe HTAuth::CLI::Digest do
|
|
62
62
|
@stdin.rewind
|
63
63
|
@rdigest.run([ "-c", @new_file, "htauth", "bob" ])
|
64
64
|
rescue SystemExit => se
|
65
|
-
se.status.must_equal 0
|
66
|
-
IO.read(@new_file).must_equal IO.readlines(DIGEST_ORIGINAL_TEST_FILE).first
|
65
|
+
_(se.status).must_equal 0
|
66
|
+
_(IO.read(@new_file)).must_equal IO.readlines(DIGEST_ORIGINAL_TEST_FILE).first
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
@@ -74,8 +74,8 @@ describe HTAuth::CLI::Digest do
|
|
74
74
|
@stdin.rewind
|
75
75
|
@rdigest.run([ "-c", @tf.path, "htauth", "bob"])
|
76
76
|
rescue SystemExit => se
|
77
|
-
se.status.must_equal 0
|
78
|
-
IO.read(@tf.path).must_equal IO.read(DIGEST_DELETE_TEST_FILE)
|
77
|
+
_(se.status).must_equal 0
|
78
|
+
_(IO.read(@tf.path)).must_equal IO.read(DIGEST_DELETE_TEST_FILE)
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -86,8 +86,8 @@ describe HTAuth::CLI::Digest do
|
|
86
86
|
@stdin.rewind
|
87
87
|
@rdigest.run([ @tf.path, "htauth-new", "charlie" ])
|
88
88
|
rescue SystemExit => se
|
89
|
-
se.status.must_equal 0
|
90
|
-
IO.read(@tf.path).must_equal IO.read(DIGEST_ADD_TEST_FILE)
|
89
|
+
_(se.status).must_equal 0
|
90
|
+
_(IO.read(@tf.path)).must_equal IO.read(DIGEST_ADD_TEST_FILE)
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
@@ -98,9 +98,9 @@ describe HTAuth::CLI::Digest do
|
|
98
98
|
@stdin.rewind
|
99
99
|
@rdigest.run([ @tf.path, "htauth", "alice" ])
|
100
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)
|
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
104
|
end
|
105
105
|
end
|
106
106
|
|
@@ -108,9 +108,9 @@ describe HTAuth::CLI::Digest do
|
|
108
108
|
begin
|
109
109
|
@rdigest.run([ "-d", @tf.path, "htauth", "alice" ])
|
110
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)
|
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
114
|
end
|
115
115
|
end
|
116
116
|
|
@@ -121,8 +121,8 @@ describe HTAuth::CLI::Digest do
|
|
121
121
|
@stdin.rewind
|
122
122
|
@rdigest.run([ "-c", "/etc/you-cannot-create-me", "htauth", "alice"])
|
123
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
|
124
|
+
_(@stderr.string).must_match( %r{Could not open password file /etc/you-cannot-create-me}m )
|
125
|
+
_(se.status).must_equal 1
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
@@ -133,8 +133,8 @@ describe HTAuth::CLI::Digest do
|
|
133
133
|
@stdin.rewind
|
134
134
|
@rdigest.run([ @tf.path, "htauth", "alice"])
|
135
135
|
rescue SystemExit => se
|
136
|
-
@stderr.string.must_match( /They don't match, sorry./m )
|
137
|
-
se.status.must_equal 1
|
136
|
+
_(@stderr.string).must_match( /They don't match, sorry./m )
|
137
|
+
_(se.status).must_equal 1
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
@@ -142,9 +142,8 @@ describe HTAuth::CLI::Digest do
|
|
142
142
|
begin
|
143
143
|
@rdigest.run(["--blah"])
|
144
144
|
rescue SystemExit => se
|
145
|
-
@stderr.string.must_match( /ERROR:/m )
|
146
|
-
se.status.must_equal 1
|
145
|
+
_(@stderr.string).must_match( /ERROR:/m )
|
146
|
+
_(se.status).must_equal 1
|
147
147
|
end
|
148
148
|
end
|
149
|
-
|
150
149
|
end
|