htauth 1.0.0

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