htauth 1.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.
- data/CHANGES +4 -0
- data/LICENSE +19 -0
- data/README +94 -0
- data/bin/htdigest-ruby +12 -0
- data/bin/htpasswd-ruby +12 -0
- data/lib/htauth/algorithm.rb +67 -0
- data/lib/htauth/crypt.rb +20 -0
- data/lib/htauth/digest.rb +128 -0
- data/lib/htauth/digest_entry.rb +72 -0
- data/lib/htauth/digest_file.rb +85 -0
- data/lib/htauth/entry.rb +9 -0
- data/lib/htauth/file.rb +102 -0
- data/lib/htauth/gemspec.rb +52 -0
- data/lib/htauth/md5.rb +82 -0
- data/lib/htauth/passwd.rb +174 -0
- data/lib/htauth/passwd_entry.rb +97 -0
- data/lib/htauth/passwd_file.rb +86 -0
- data/lib/htauth/plaintext.rb +18 -0
- data/lib/htauth/sha1.rb +23 -0
- data/lib/htauth/specification.rb +128 -0
- data/lib/htauth/version.rb +18 -0
- data/lib/htauth.rb +27 -0
- data/spec/crypt_spec.rb +18 -0
- data/spec/digest_entry_spec.rb +61 -0
- data/spec/digest_file_spec.rb +66 -0
- data/spec/digest_spec.rb +150 -0
- data/spec/md5_spec.rb +17 -0
- data/spec/passwd_entry_spec.rb +139 -0
- data/spec/passwd_file_spec.rb +67 -0
- data/spec/passwd_spec.rb +208 -0
- data/spec/plaintext_spec.rb +18 -0
- data/spec/sha1_spec.rb +17 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/test.add.digest +3 -0
- data/spec/test.add.passwd +3 -0
- data/spec/test.delete.digest +1 -0
- data/spec/test.delete.passwd +1 -0
- data/spec/test.original.digest +2 -0
- data/spec/test.original.passwd +2 -0
- data/spec/test.update.digest +2 -0
- data/spec/test.update.passwd +2 -0
- metadata +122 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"spec_helper.rb")
|
2
|
+
|
3
|
+
require 'htauth/digest_entry'
|
4
|
+
|
5
|
+
describe HTAuth::DigestEntry do
|
6
|
+
before(:each) do
|
7
|
+
@alice = HTAuth::DigestEntry.new("alice", "htauth")
|
8
|
+
@bob = HTAuth::DigestEntry.new("bob", "htauth", "b secret")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "initializes with a user and realm" do
|
12
|
+
@alice.user.should == "alice"
|
13
|
+
@alice.realm.should == "htauth"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has the correct digest for a password" do
|
17
|
+
@alice.password = "digit"
|
18
|
+
@alice.digest.should == "4ed9e5744c6747af8f292d28afd6372e"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns username:realm for a key" do
|
22
|
+
@alice.key.should == "alice:htauth"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "checks the password correctly" do
|
26
|
+
@bob.authenticated?("b secret").should == true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "formats correctly when put to a string" do
|
30
|
+
@bob.to_s.should == "bob:htauth:fcbeab6821d2ab3b00934c958db0fd1e"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "parses an input line" do
|
34
|
+
@bob_new = HTAuth::DigestEntry.from_line("bob:htauth:fcbeab6821d2ab3b00934c958db0fd1e")
|
35
|
+
@bob.user.should == @bob_new.user
|
36
|
+
@bob.digest.should == @bob_new.digest
|
37
|
+
@bob.realm.should == @bob_new.realm
|
38
|
+
end
|
39
|
+
|
40
|
+
it "knows if an input line is a possible entry and raises an exception" do
|
41
|
+
lambda { HTAuth::DigestEntry.is_entry!("#stuff") }.should raise_error(HTAuth::InvalidDigestEntry)
|
42
|
+
lambda { HTAuth::DigestEntry.is_entry!("this:that:other:stuff") }.should raise_error(HTAuth::InvalidDigestEntry)
|
43
|
+
lambda { HTAuth::DigestEntry.is_entry!("this:that:other") }.should raise_error(HTAuth::InvalidDigestEntry)
|
44
|
+
lambda { HTAuth::DigestEntry.is_entry!("this:that:0a90549e8ffb2dd62f98252a95d88xyz") }.should raise_error(HTAuth::InvalidDigestEntry)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "knows if an input line is a possible entry and returns false" do
|
48
|
+
HTAuth::DigestEntry.is_entry?("#stuff").should == false
|
49
|
+
HTAuth::DigestEntry.is_entry?("this:that:other:stuff").should == false
|
50
|
+
HTAuth::DigestEntry.is_entry?("this:that:other").should == false
|
51
|
+
HTAuth::DigestEntry.is_entry?("this:that:0a90549e8ffb2dd62f98252a95d88xyz").should == false
|
52
|
+
end
|
53
|
+
|
54
|
+
it "knows if an input line is a possible entry and returns true" do
|
55
|
+
HTAuth::DigestEntry.is_entry?("bob:htauth:0a90549e8ffb2dd62f98252a95d88697").should == true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "duplicates itself" do
|
59
|
+
@alice.dup.to_s.should == @alice.to_s
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"spec_helper.rb")
|
2
|
+
|
3
|
+
require 'htauth/digest_file'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
describe HTAuth::DigestFile do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@tf = Tempfile.new("rpasswrd-digest")
|
10
|
+
@tf.write(IO.read(DIGEST_ORIGINAL_TEST_FILE))
|
11
|
+
@tf.close
|
12
|
+
@digest_file = HTAuth::DigestFile.new(@tf.path)
|
13
|
+
|
14
|
+
@tf2 = Tempfile.new("rpasswrd-digest-empty")
|
15
|
+
@tf2.close
|
16
|
+
@empty_digest_file = HTAuth::DigestFile.new(@tf2.path)
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:each) do
|
20
|
+
@tf2.close(true)
|
21
|
+
@tf.close(true)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "can add a new entry to an already existing digest file" do
|
25
|
+
@digest_file.add_or_update("charlie", "htauth-new", "c secret")
|
26
|
+
@digest_file.contents.should == IO.read(DIGEST_ADD_TEST_FILE)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can tell if an entry already exists in the digest file" do
|
30
|
+
@digest_file.has_entry?("alice", "htauth").should == true
|
31
|
+
@digest_file.has_entry?("alice", "some other realm").should == false
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can update an entry in an already existing digest file" do
|
35
|
+
@digest_file.add_or_update("alice", "htauth", "a new secret")
|
36
|
+
@digest_file.contents.should == IO.read(DIGEST_UPDATE_TEST_FILE)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "fetches a copy of an entry" do
|
40
|
+
@digest_file.fetch("alice", "htauth").to_s.should == "alice:htauth:2f361db93147d84831eb34f19d05bfbb"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "raises an error if an attempt is made to alter a non-existenet file" do
|
44
|
+
lambda { HTAuth::DigestFile.new("some-file") }.should raise_error(HTAuth::FileAccessError)
|
45
|
+
end
|
46
|
+
|
47
|
+
# this test will only work on systems that have /etc/ssh_host_rsa_key
|
48
|
+
it "raises an error if an attempt is made to open a file where no permissions are granted" do
|
49
|
+
lambda { HTAuth::DigestFile.new("/etc/ssh_host_rsa_key") }.should raise_error(HTAuth::FileAccessError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "deletes an entry" do
|
53
|
+
@digest_file.delete("alice", "htauth")
|
54
|
+
@digest_file.contents.should == IO.read(DIGEST_DELETE_TEST_FILE)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "is usable in a ruby manner and yeilds itself when opened" do
|
58
|
+
HTAuth::DigestFile.open(@tf.path) do |pf|
|
59
|
+
pf.add_or_update("alice", "htauth", "a secret")
|
60
|
+
pf.delete('bob', 'htauth')
|
61
|
+
end
|
62
|
+
lines = IO.readlines(@tf.path)
|
63
|
+
lines.size.should == 1
|
64
|
+
lines.first.strip.should == "alice:htauth:2f361db93147d84831eb34f19d05bfbb"
|
65
|
+
end
|
66
|
+
end
|
data/spec/digest_spec.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"spec_helper.rb")
|
2
|
+
require 'htauth/digest'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe HTAuth::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::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 = StringIO.new
|
20
|
+
@old_stdout = $stdout
|
21
|
+
$stdout = @stdout
|
22
|
+
|
23
|
+
@stderr = StringIO.new
|
24
|
+
@old_stderr = $stderr
|
25
|
+
$stderr = @stderr
|
26
|
+
|
27
|
+
@stdin = StringIO.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.should == 1
|
45
|
+
@stdout.string.should =~ /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.should == 1
|
54
|
+
@stdout.string.should =~ /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.should == 0
|
66
|
+
IO.read(@new_file).should == 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.should == 0
|
78
|
+
IO.read(@tf.path).should == 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.should == 0
|
90
|
+
IO.read(@tf.path).should == 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.should == ""
|
102
|
+
se.status.should == 0
|
103
|
+
IO.read(@tf.path).should == 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.should == ""
|
112
|
+
se.status.should == 0
|
113
|
+
IO.read(@tf.path).should == 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.should =~ %r{Could not open password file /etc/you-cannot-create-me}m
|
125
|
+
se.status.should == 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.should =~ /They don't match, sorry./m
|
137
|
+
se.status.should == 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.should =~ /ERROR:/m
|
146
|
+
se.status.should == 1
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
data/spec/md5_spec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"spec_helper.rb")
|
2
|
+
|
3
|
+
require 'htauth/md5'
|
4
|
+
|
5
|
+
describe HTAuth::Md5 do
|
6
|
+
it "has a prefix" do
|
7
|
+
HTAuth::Md5.new.prefix.should == "$apr1$"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "encrypts the same way that apache does" do
|
11
|
+
apache_salt = "L0LDd/.."
|
12
|
+
apache_result = "$apr1$L0LDd/..$yhUzDjpxam5F1kWdtwMco1"
|
13
|
+
md5 = HTAuth::Md5.new({ 'salt' => apache_salt })
|
14
|
+
md5.encode("a secret").should == apache_result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__),"spec_helper.rb")
|
3
|
+
|
4
|
+
require 'htauth/passwd_entry'
|
5
|
+
|
6
|
+
describe HTAuth::PasswdEntry do
|
7
|
+
before(:each) do
|
8
|
+
@alice = HTAuth::PasswdEntry.new("alice", "a secret", "crypt", { :salt => "mD" })
|
9
|
+
@bob = HTAuth::PasswdEntry.new("bob", "b secret", "crypt", { :salt => "b8"})
|
10
|
+
end
|
11
|
+
|
12
|
+
it "initializes with a user and realm" do
|
13
|
+
@alice.user.should == "alice"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has the correct crypt password" do
|
17
|
+
@alice.password = "a secret"
|
18
|
+
@alice.digest.should == "mDwdZuXalQ5zk"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should encrypt correctly for md5" do
|
22
|
+
bob = HTAuth::PasswdEntry.new("bob", "b secret", "md5", { :salt => "lo1tk/.." })
|
23
|
+
bob.digest.should == "$apr1$lo1tk/..$CarApvZPee0F6Wj1U0GxZ1"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should encrypt correctly for sha1" do
|
27
|
+
bob = HTAuth::PasswdEntry.new("bob", "b secret", "sha1", { :salt => @salt })
|
28
|
+
bob.digest.should == "{SHA}b/tjGXbX80MEKVnF200S43ca4hY="
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should encrypt correctly for plaintext" do
|
32
|
+
bob = HTAuth::PasswdEntry.new("bob", "b secret", "plaintext", { :salt => @salt })
|
33
|
+
bob.digest.should == "b secret"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "encrypts with crypt as a default, when parsed from crypt()'d line" do
|
37
|
+
bob2 = HTAuth::PasswdEntry.from_line(@bob.to_s)
|
38
|
+
bob2.algorithm.should be_an_instance_of(Array)
|
39
|
+
bob2.algorithm.should have(2).items
|
40
|
+
bob2.password = "another secret"
|
41
|
+
bob2.algorithm.should be_an_instance_of(HTAuth::Crypt)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "encrypts with crypt as a default, when parsed from plaintext line" do
|
45
|
+
p = HTAuth::PasswdEntry.new('paul', 'p secret', 'plaintext')
|
46
|
+
p2 = HTAuth::PasswdEntry.from_line(p.to_s)
|
47
|
+
p2.algorithm.should be_an_instance_of(Array)
|
48
|
+
p2.algorithm.should have(2).items
|
49
|
+
p2.password = "another secret"
|
50
|
+
p2.algorithm.should be_an_instance_of(HTAuth::Crypt)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "encrypts with md5 as default, when parsed from an md5 line" do
|
54
|
+
m = HTAuth::PasswdEntry.new("mary", "m secret", "md5")
|
55
|
+
m2 = HTAuth::PasswdEntry.from_line(m.to_s)
|
56
|
+
m2.algorithm.should be_an_instance_of(HTAuth::Md5)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "encrypts with sha1 as default, when parsed from an sha1 line" do
|
60
|
+
s = HTAuth::PasswdEntry.new("steve", "s secret", "sha1")
|
61
|
+
s2 = HTAuth::PasswdEntry.from_line(s.to_s)
|
62
|
+
s2.algorithm.should be_an_instance_of(HTAuth::Sha1)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "determins the algorithm to be crypt when checking a password" do
|
66
|
+
bob2 = HTAuth::PasswdEntry.from_line(@bob.to_s)
|
67
|
+
bob2.algorithm.should be_an_instance_of(Array)
|
68
|
+
bob2.algorithm.should have(2).items
|
69
|
+
bob2.authenticated?("b secret").should == true
|
70
|
+
bob2.algorithm.should be_an_instance_of(HTAuth::Crypt)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "determins the algorithm to be plain when checking a password" do
|
74
|
+
bob2 = HTAuth::PasswdEntry.from_line("bob:b secret")
|
75
|
+
bob2.algorithm.should be_an_instance_of(Array)
|
76
|
+
bob2.algorithm.should have(2).items
|
77
|
+
bob2.authenticated?("b secret").should == true
|
78
|
+
bob2.algorithm.should be_an_instance_of(HTAuth::Plaintext)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "authenticates correctly against md5" do
|
82
|
+
m = HTAuth::PasswdEntry.new("mary", "m secret", "md5")
|
83
|
+
m2 = HTAuth::PasswdEntry.from_line(m.to_s)
|
84
|
+
m2.authenticated?("m secret").should == true
|
85
|
+
end
|
86
|
+
|
87
|
+
it "authenticates correctly against sha1" do
|
88
|
+
s = HTAuth::PasswdEntry.new("steve", "s secret", "sha1")
|
89
|
+
s2 = HTAuth::PasswdEntry.from_line(s.to_s)
|
90
|
+
s2.authenticated?("s secret").should == true
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
it "returns username for a key" do
|
97
|
+
@alice.key.should == "alice"
|
98
|
+
end
|
99
|
+
|
100
|
+
it "checks the password correctly" do
|
101
|
+
@bob.authenticated?("b secret").should == true
|
102
|
+
end
|
103
|
+
|
104
|
+
it "formats correctly when put to a string" do
|
105
|
+
@bob.to_s.should == "bob:b8Ml4Jp9I0N8E"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "parses an input line" do
|
109
|
+
@bob_new = HTAuth::PasswdEntry.from_line("bob:b8Ml4Jp9I0N8E")
|
110
|
+
@bob.user.should == @bob_new.user
|
111
|
+
@bob.digest.should == @bob_new.digest
|
112
|
+
end
|
113
|
+
|
114
|
+
it "knows if an input line is a possible entry and raises an exception" do
|
115
|
+
lambda { HTAuth::PasswdEntry.is_entry!("#stuff") }.should raise_error(HTAuth::InvalidPasswdEntry)
|
116
|
+
lambda { HTAuth::PasswdEntry.is_entry!("this:that:other:stuff") }.should raise_error(HTAuth::InvalidPasswdEntry)
|
117
|
+
lambda { HTAuth::PasswdEntry.is_entry!("this:that:other") }.should raise_error(HTAuth::InvalidPasswdEntry)
|
118
|
+
lambda { HTAuth::PasswdEntry.is_entry!("this:that:0a90549e8ffb2dd62f98252a95d88xyz") }.should raise_error(HTAuth::InvalidPasswdEntry)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "knows if an input line is a possible entry and returns false" do
|
122
|
+
HTAuth::PasswdEntry.is_entry?("#stuff").should == false
|
123
|
+
HTAuth::PasswdEntry.is_entry?("this:that:other:stuff").should == false
|
124
|
+
HTAuth::PasswdEntry.is_entry?("this:that:other").should == false
|
125
|
+
HTAuth::PasswdEntry.is_entry?("this:that:0a90549e8ffb2dd62f98252a95d88xyz").should == false
|
126
|
+
end
|
127
|
+
|
128
|
+
it "knows if an input line is a possible entry and returns true" do
|
129
|
+
HTAuth::PasswdEntry.is_entry?("bob:irRm0g.SDfCyI").should == true
|
130
|
+
HTAuth::PasswdEntry.is_entry?("bob:b secreat").should == true
|
131
|
+
HTAuth::PasswdEntry.is_entry?("bob:{SHA}b/tjGXbX80MEKVnF200S43ca4hY=").should == true
|
132
|
+
HTAuth::PasswdEntry.is_entry?("bob:$apr1$lo1tk/..$CarApvZPee0F6Wj1U0GxZ1").should == true
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
it "duplicates itself" do
|
137
|
+
@alice.dup.to_s.should == @alice.to_s
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"spec_helper.rb")
|
2
|
+
|
3
|
+
require 'htauth/passwd_file'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
describe HTAuth::PasswdFile do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@tf = Tempfile.new("rpasswrd-passwd")
|
10
|
+
@tf.write(IO.read(PASSWD_ORIGINAL_TEST_FILE))
|
11
|
+
@tf.close
|
12
|
+
@passwd_file = HTAuth::PasswdFile.new(@tf.path)
|
13
|
+
|
14
|
+
@tf2 = Tempfile.new("rpasswrd-passwd-empty")
|
15
|
+
@tf2.close
|
16
|
+
@empty_passwd_file = HTAuth::PasswdFile.new(@tf2.path)
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:each) do
|
20
|
+
@tf2.close(true)
|
21
|
+
@tf.close(true)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "can add a new entry to an already existing passwd file" do
|
25
|
+
@passwd_file.add_or_update("charlie", "c secret", "sha1")
|
26
|
+
@passwd_file.contents.should == IO.read(PASSWD_ADD_TEST_FILE)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can tell if an entry already exists in the passwd file" do
|
30
|
+
@passwd_file.has_entry?("alice").should == true
|
31
|
+
@passwd_file.has_entry?("david").should == false
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can update an entry in an already existing passwd file, algorithm can change" do
|
35
|
+
@passwd_file.add_or_update("alice", "a new secret", "sha1")
|
36
|
+
@passwd_file.contents.should == IO.read(PASSWD_UPDATE_TEST_FILE)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "fetches a copy of an entry" do
|
40
|
+
@passwd_file.fetch("alice").to_s.should == "alice:$apr1$DghnA...$CsPcgerfsI/Ryy0AOAJtb0"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "raises an error if an attempt is made to alter a non-existenet file" do
|
44
|
+
lambda { HTAuth::PasswdFile.new("some-file") }.should raise_error(HTAuth::FileAccessError)
|
45
|
+
end
|
46
|
+
|
47
|
+
# this test will only work on systems that have /etc/ssh_host_rsa_key
|
48
|
+
it "raises an error if an attempt is made to open a file where no permissions are granted" do
|
49
|
+
lambda { HTAuth::PasswdFile.new("/etc/ssh_host_rsa_key") }.should raise_error(HTAuth::FileAccessError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "deletes an entry" do
|
53
|
+
@passwd_file.delete("bob")
|
54
|
+
@passwd_file.contents.should == IO.read(PASSWD_DELETE_TEST_FILE)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "is usable in a ruby manner and yeilds itself when opened" do
|
58
|
+
HTAuth::PasswdFile.open(@tf.path) do |pf|
|
59
|
+
pf.add_or_update("alice", "a new secret", "md5")
|
60
|
+
pf.delete('bob')
|
61
|
+
end
|
62
|
+
lines = IO.readlines(@tf.path)
|
63
|
+
lines.size.should == 1
|
64
|
+
lines.first.split(':').first.should == "alice"
|
65
|
+
lines.first.split(':').last.should =~ /\$apr1\$/
|
66
|
+
end
|
67
|
+
end
|