aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--lib/webrick/httpauth/digestauth.rb7
-rw-r--r--test/webrick/test_httpauth.rb85
3 files changed, 98 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog
index e49ff03220..129163388c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Tue Jul 5 14:05:43 2011 NARUSE, Yui <naruse@ruby-lang.org>
+
+ * lib/webrick/httpauth/digestauth.rb (_authenticate):
+ Literal texts in HTTP ABNF is case-insensitive (RFC2616 2.1),
+ and a ample implementation in RFC2617 also ignores the case
+ of algorithms. So now this ignores those cases.
+ [ruby-dev:43965] [Feature #4936]
+
+ * lib/webrick/httpauth/digestauth.rb (initialize):
+ Because of above, opera_hack is useless and removed.
+
Tue Jul 5 01:30:01 2011 Yusuke Endoh <mame@tsg.ne.jp>
* thread_pthread.c (native_sleep): cut the waiting time up to
diff --git a/lib/webrick/httpauth/digestauth.rb b/lib/webrick/httpauth/digestauth.rb
index 8f7f32f82b..4e47fe163f 100644
--- a/lib/webrick/httpauth/digestauth.rb
+++ b/lib/webrick/httpauth/digestauth.rb
@@ -83,7 +83,6 @@ module WEBrick
@nonce_expire_period = @config[:NonceExpirePeriod]
@nonce_expire_delta = @config[:NonceExpireDelta]
@internet_explorer_hack = @config[:InternetExplorerHack]
- @opera_hack = @config[:OperaHack]
case @algorithm
when 'MD5','MD5-sess'
@@ -175,8 +174,7 @@ module WEBrick
end
auth_req['algorithm'] ||= 'MD5'
- if auth_req['algorithm'] != @algorithm &&
- (@opera_hack && auth_req['algorithm'] != @algorithm.upcase)
+ if auth_req['algorithm'].upcase != @algorithm.upcase
error('%s: algorithm unmatch. "%s" for "%s"',
auth_req['username'], auth_req['algorithm'], @algorithm)
return false
@@ -212,8 +210,7 @@ module WEBrick
nonce_is_invalid = true
end
- if /-sess$/ =~ auth_req['algorithm'] ||
- (@opera_hack && /-SESS$/ =~ auth_req['algorithm'])
+ if /-sess$/i =~ auth_req['algorithm']
ha1 = hexdigest(password, auth_req['nonce'], auth_req['cnonce'])
else
ha1 = password
diff --git a/test/webrick/test_httpauth.rb b/test/webrick/test_httpauth.rb
index a6066908c1..6cfdcc4553 100644
--- a/test/webrick/test_httpauth.rb
+++ b/test/webrick/test_httpauth.rb
@@ -79,4 +79,89 @@ class TestWEBrickHTTPAuth < Test::Unit::TestCase
}
tmpfile.close(true)
end
+
+ DIGESTRES_ = /
+ ([a-zA-z\-]+)
+ [\s\t]*(?:\r\n[\s\t]*)*
+ =
+ [\s\t]*(?:\r\n[\s\t]*)*
+ (?:
+ "((?:[^"]+|\\[\x00-\x7F])*)" |
+ ([!\#$%&'*+\-.0-9A-Z^_`a-z|~]+)
+ )/x
+
+ def test_digest_auth
+ TestWEBrick.start_httpserver{|server, addr, port, log|
+ realm = "WEBrick's realm"
+ path = "/digest_auth"
+
+ tmpfile = Tempfile.new("test_webrick_auth")
+ tmpfile.close
+ tmp_pass = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path)
+ tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
+ tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
+ tmp_pass.flush
+
+ htdigest = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path)
+ users = []
+ htdigest.each{|user, pass| users << user }
+ assert_equal(2, users.size, log.call)
+ assert(users.member?("webrick"), log.call)
+ assert(users.member?("foo"), log.call)
+
+ auth = WEBrick::HTTPAuth::DigestAuth.new(
+ :Realm => realm, :UserDB => htdigest,
+ :Algorithm => 'MD5',
+ :Logger => server.logger
+ )
+ server.mount_proc(path){|req, res|
+ auth.authenticate(req, res)
+ res.body = "hoge"
+ }
+
+ Net::HTTP.start(addr, port) do |http|
+ g = Net::HTTP::Get.new(path)
+ params = {}
+ http.request(g) do |res|
+ assert_equal('401', res.code, log.call)
+ res["www-authenticate"].scan(DIGESTRES_) do |key, quoted, token|
+ params[key.downcase] = token || quoted.delete('\\')
+ end
+ params['uri'] = "http://#{addr}:#{port}#{path}"
+ end
+
+ g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params)
+ http.request(g){|res| assert_equal("hoge", res.body, log.call)}
+
+ params['algorithm'].downcase! #4936
+ g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params)
+ http.request(g){|res| assert_equal("hoge", res.body, log.call)}
+
+ g['Authorization'] = credentials_for_request('webrick', "not super", params)
+ http.request(g){|res| assert_not_equal("hoge", res.body, log.call)}
+ end
+ }
+ end
+
+ private
+ def credentials_for_request(user, password, params)
+ cnonce = "hoge"
+ nonce_count = 1
+ ha1 = "#{user}:#{params['realm']}:#{password}"
+ ha2 = "GET:#{params['uri']}"
+ request_digest =
+ "#{Digest::MD5.hexdigest(ha1)}:" \
+ "#{params['nonce']}:#{'%08x' % nonce_count}:#{cnonce}:#{params['qop']}:" \
+ "#{Digest::MD5.hexdigest(ha2)}"
+ p header = "Digest username=\"#{user}\"" \
+ ", realm=\"#{params['realm']}\"" \
+ ", nonce=\"#{params['nonce']}\"" \
+ ", uri=\"#{params['uri']}\"" \
+ ", qop=#{params['qop']}" \
+ ", nc=#{'%08x' % nonce_count}" \
+ ", cnonce=\"#{cnonce}\"" \
+ ", response=\"#{Digest::MD5.hexdigest(request_digest)}\"" \
+ ", opaque=\"#{params['opaque']}\"" \
+ ", algorithm=#{params['algorithm']}"
+ end
end