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.
@@ -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