htauth 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|