1require "test/unit" 2require "net/http" 3require "tempfile" 4require "webrick" 5require "webrick/httpauth/basicauth" 6require_relative "utils" 7 8class TestWEBrickHTTPAuth < Test::Unit::TestCase 9 def test_basic_auth 10 TestWEBrick.start_httpserver{|server, addr, port, log| 11 realm = "WEBrick's realm" 12 path = "/basic_auth" 13 14 server.mount_proc(path){|req, res| 15 WEBrick::HTTPAuth.basic_auth(req, res, realm){|user, pass| 16 user == "webrick" && pass == "supersecretpassword" 17 } 18 res.body = "hoge" 19 } 20 http = Net::HTTP.new(addr, port) 21 g = Net::HTTP::Get.new(path) 22 g.basic_auth("webrick", "supersecretpassword") 23 http.request(g){|res| assert_equal("hoge", res.body, log.call)} 24 g.basic_auth("webrick", "not super") 25 http.request(g){|res| assert_not_equal("hoge", res.body, log.call)} 26 } 27 end 28 29 def test_basic_auth2 30 TestWEBrick.start_httpserver{|server, addr, port, log| 31 realm = "WEBrick's realm" 32 path = "/basic_auth2" 33 34 tmpfile = Tempfile.new("test_webrick_auth") 35 tmpfile.close 36 tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path) 37 tmp_pass.set_passwd(realm, "webrick", "supersecretpassword") 38 tmp_pass.set_passwd(realm, "foo", "supersecretpassword") 39 tmp_pass.flush 40 41 htpasswd = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path) 42 users = [] 43 htpasswd.each{|user, pass| users << user } 44 assert_equal(2, users.size, log.call) 45 assert(users.member?("webrick"), log.call) 46 assert(users.member?("foo"), log.call) 47 48 server.mount_proc(path){|req, res| 49 auth = WEBrick::HTTPAuth::BasicAuth.new( 50 :Realm => realm, :UserDB => htpasswd, 51 :Logger => server.logger 52 ) 53 auth.authenticate(req, res) 54 res.body = "hoge" 55 } 56 http = Net::HTTP.new(addr, port) 57 g = Net::HTTP::Get.new(path) 58 g.basic_auth("webrick", "supersecretpassword") 59 http.request(g){|res| assert_equal("hoge", res.body, log.call)} 60 g.basic_auth("webrick", "not super") 61 http.request(g){|res| assert_not_equal("hoge", res.body, log.call)} 62 tmpfile.close(true) 63 } 64 end 65 66 def test_basic_auth3 67 tmpfile = Tempfile.new("test_webrick_auth") 68 tmpfile.puts("webrick:{SHA}GJYFRpBbdchp595jlh3Bhfmgp8k=") 69 tmpfile.flush 70 assert_raise(NotImplementedError){ 71 WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path) 72 } 73 tmpfile.close(true) 74 75 tmpfile = Tempfile.new("test_webrick_auth") 76 tmpfile.puts("webrick:$apr1$IOVMD/..$rmnOSPXr0.wwrLPZHBQZy0") 77 tmpfile.flush 78 assert_raise(NotImplementedError){ 79 WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path) 80 } 81 tmpfile.close(true) 82 end 83 84 DIGESTRES_ = / 85 ([a-zA-z\-]+) 86 [\s\t]*(?:\r\n[\s\t]*)* 87 = 88 [\s\t]*(?:\r\n[\s\t]*)* 89 (?: 90 "((?:[^"]+|\\[\x00-\x7F])*)" | 91 ([!\#$%&'*+\-.0-9A-Z^_`a-z|~]+) 92 )/x 93 94 def test_digest_auth 95 TestWEBrick.start_httpserver{|server, addr, port, log| 96 realm = "WEBrick's realm" 97 path = "/digest_auth" 98 99 tmpfile = Tempfile.new("test_webrick_auth") 100 tmpfile.close 101 tmp_pass = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) 102 tmp_pass.set_passwd(realm, "webrick", "supersecretpassword") 103 tmp_pass.set_passwd(realm, "foo", "supersecretpassword") 104 tmp_pass.flush 105 106 htdigest = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) 107 users = [] 108 htdigest.each{|user, pass| users << user } 109 assert_equal(2, users.size, log.call) 110 assert(users.member?("webrick"), log.call) 111 assert(users.member?("foo"), log.call) 112 113 auth = WEBrick::HTTPAuth::DigestAuth.new( 114 :Realm => realm, :UserDB => htdigest, 115 :Algorithm => 'MD5', 116 :Logger => server.logger 117 ) 118 server.mount_proc(path){|req, res| 119 auth.authenticate(req, res) 120 res.body = "hoge" 121 } 122 123 Net::HTTP.start(addr, port) do |http| 124 g = Net::HTTP::Get.new(path) 125 params = {} 126 http.request(g) do |res| 127 assert_equal('401', res.code, log.call) 128 res["www-authenticate"].scan(DIGESTRES_) do |key, quoted, token| 129 params[key.downcase] = token || quoted.delete('\\') 130 end 131 params['uri'] = "http://#{addr}:#{port}#{path}" 132 end 133 134 g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params) 135 http.request(g){|res| assert_equal("hoge", res.body, log.call)} 136 137 params['algorithm'].downcase! #4936 138 g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params) 139 http.request(g){|res| assert_equal("hoge", res.body, log.call)} 140 141 g['Authorization'] = credentials_for_request('webrick', "not super", params) 142 http.request(g){|res| assert_not_equal("hoge", res.body, log.call)} 143 end 144 tmpfile.close(true) 145 } 146 end 147 148 private 149 def credentials_for_request(user, password, params) 150 cnonce = "hoge" 151 nonce_count = 1 152 ha1 = "#{user}:#{params['realm']}:#{password}" 153 ha2 = "GET:#{params['uri']}" 154 request_digest = 155 "#{Digest::MD5.hexdigest(ha1)}:" \ 156 "#{params['nonce']}:#{'%08x' % nonce_count}:#{cnonce}:#{params['qop']}:" \ 157 "#{Digest::MD5.hexdigest(ha2)}" 158 "Digest username=\"#{user}\"" \ 159 ", realm=\"#{params['realm']}\"" \ 160 ", nonce=\"#{params['nonce']}\"" \ 161 ", uri=\"#{params['uri']}\"" \ 162 ", qop=#{params['qop']}" \ 163 ", nc=#{'%08x' % nonce_count}" \ 164 ", cnonce=\"#{cnonce}\"" \ 165 ", response=\"#{Digest::MD5.hexdigest(request_digest)}\"" \ 166 ", opaque=\"#{params['opaque']}\"" \ 167 ", algorithm=#{params['algorithm']}" 168 end 169end 170