Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

net-http-digest_auth

  • Loading branch information...
commit 598fe3e2acaeaedab4136a3c2d30fe3fc36516d1 0 parents
@drbrain authored
23 .autotest
@@ -0,0 +1,23 @@
+# -*- ruby -*-
+
+require 'autotest/restart'
+
+# Autotest.add_hook :initialize do |at|
+# at.extra_files << "../some/external/dependency.rb"
+#
+# at.libs << ":../some/external"
+#
+# at.add_exception 'vendor'
+#
+# at.add_mapping(/dependency.rb/) do |f, _|
+# at.files_matching(/test_.*rb$/)
+# end
+#
+# %w(TestA TestB).each do |klass|
+# at.extra_class_map[klass] = "test/test_misc.rb"
+# end
+# end
+
+# Autotest.add_hook :run_command do |at|
+# system "rake build"
+# end
4 .gitinfo
@@ -0,0 +1,4 @@
+/TAGS
+/pkg
+/doc
+*.swp
5 History.txt
@@ -0,0 +1,5 @@
+=== 1.0 / 2010-09-10
+
+* Major enhancements
+ * Birthday!
+
7 Manifest.txt
@@ -0,0 +1,7 @@
+.autotest
+History.txt
+Manifest.txt
+README.txt
+Rakefile
+lib/net/http/digest_auth.rb
+test/test_net_http_digest_auth.rb
48 README.txt
@@ -0,0 +1,48 @@
+= net-http-digest_auth
+
+* http://github.com/drbrain/net-http-digest_auth
+* http://www.rfc-editor.org/rfc/rfc2617.txt
+
+== DESCRIPTION:
+
+An implementation of RFC 2617 - Digest Access Authentication. At this time
+the gem does not fully integrate with Net::HTTP and can be used for with other
+HTTP clients.
+
+== FEATURES/PROBLEMS:
+
+* Implements RFC 2617 for digest authentication
+* Does not fully integrate with Net::HTTP
+
+== SYNOPSIS:
+
+ See Net::HTTP::DigestAuth
+
+== INSTALL:
+
+ gem install net-http-digest_auth
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) 2010 Eric Hodel
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 Rakefile
@@ -0,0 +1,11 @@
+# -*- ruby -*-
+
+require 'rubygems'
+require 'hoe'
+
+Hoe.spec 'net-http-digest_auth' do
+ self.rubyforge_name = 'seattlerb'
+ developer 'Eric Hodel', 'drbrain@segment7.net'
+end
+
+# vim: syntax=Ruby
91 lib/net/http/digest_auth.rb
@@ -0,0 +1,91 @@
+require 'net/http'
+require 'digest'
+require 'cgi'
+
+##
+# An implementation of RFC 2617 Digest Access Authentication.
+#
+# http://www.rfc-editor.org/rfc/rfc2617.txt
+
+class Net::HTTP::DigestAuth
+
+ ##
+ # Version of Net::HTTP::DigestAuth you are using
+
+ VERSION = '1.0'
+
+ ##
+ # Creates a new DigestAuth header creator.
+ #
+ # +cnonce+ is the client nonce value. This should be an MD5 hexdigest of a
+ # secret value.
+
+ def initialize cnonce = make_cnonce
+ @nonce_count = -1
+ @cnonce = cnonce
+ end
+
+ ##
+ # Creates a digest auth header for +uri+ from +auth_header+ for HTTP method
+ # +method+.
+ #
+ # The result of this method should be sent along with the HTTP request as
+ # the "Authorization" header. In Net::HTTP this will look like:
+ #
+ # request.add_field 'Authorization', digest_auth.auth_header # ...
+ #
+ # See Net::HTTP::DigestAuth for a complete example.
+ #
+ # IIS servers handle the "qop" parameter of digest authentication
+ # differently so you may need to set +iis+ to true for such servers.
+
+ def auth_header uri, auth_header, method, iis = false
+ @nonce_count += 1
+
+ user = CGI.unescape uri.user
+ password = CGI.unescape uri.password
+
+ auth_header =~ /^(\w+) (.*)/
+
+ params = {}
+ $2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
+
+ a_1 = "#{user}:#{params['realm']}:#{password}"
+ a_2 = "#{method}:#{uri.path}"
+
+ request_digest = []
+ request_digest << Digest::MD5.hexdigest(a_1)
+ request_digest << params['nonce']
+ request_digest << ('%08x' % @nonce_count)
+ request_digest << @cnonce
+ request_digest << params['qop']
+ request_digest << Digest::MD5.hexdigest(a_2)
+ request_digest = request_digest.join ':'
+
+ header = []
+ header << "Digest username=\"#{user}\""
+ header << "realm=\"#{params['realm']}\""
+ if iis then
+ header << "qop=\"#{params['qop']}\""
+ else
+ header << "qop=#{params['qop']}"
+ end
+ header << "uri=\"#{uri.path}\""
+ header << "nonce=\"#{params['nonce']}\""
+ header << "nc=#{'%08x' % @nonce_count}"
+ header << "cnonce=\"#{@cnonce}\""
+ header << "response=\"#{Digest::MD5.hexdigest request_digest}\""
+
+ header.join ', '
+ end
+
+ ##
+ # Creates a client nonce value that is used across all requests based on the
+ # current time.
+
+ def make_cnonce
+ Digest::MD5.hexdigest "%x" % (Time.now.to_i + rand(65535))
+ end
+
+end
+
72 test/test_net_http_digest_auth.rb
@@ -0,0 +1,72 @@
+require 'minitest/autorun'
+require 'net/http/digest_auth'
+
+class TestNetHttpDigestAuth < MiniTest::Unit::TestCase
+
+ def setup
+ @uri = URI.parse "http://www.example.com/"
+ @uri.user = 'user'
+ @uri.password = 'password'
+
+ @cnonce = '9ea5ff3bd34554a4165bbdc1df91dcff'
+
+ @header = [
+ 'Digest qop="auth"',
+ 'realm="www.example.com"',
+ 'nonce="4107baa081a592a6021660200000cd6c5686ff5f579324402b374d83e2c9"'
+ ].join ', '
+
+ @da = Net::HTTP::DigestAuth.new @cnonce
+ end
+
+ def test_auth_header
+ expected = [
+ 'Digest username="user"',
+ 'realm="www.example.com"',
+ 'qop=auth',
+ 'uri="/"',
+ 'nonce="4107baa081a592a6021660200000cd6c5686ff5f579324402b374d83e2c9"',
+ 'nc=00000000',
+ 'cnonce="9ea5ff3bd34554a4165bbdc1df91dcff"',
+ 'response="67be92a5e7b38d08679957db04f5da04"'
+ ].join ', '
+
+ assert_equal expected, @da.auth_header(@uri, @header, 'GET')
+ end
+
+ def test_auth_header_iis
+ expected = [
+ 'Digest username="user"',
+ 'realm="www.example.com"',
+ 'qop="auth"',
+ 'uri="/"',
+ 'nonce="4107baa081a592a6021660200000cd6c5686ff5f579324402b374d83e2c9"',
+ 'nc=00000000',
+ 'cnonce="9ea5ff3bd34554a4165bbdc1df91dcff"',
+ 'response="67be92a5e7b38d08679957db04f5da04"'
+ ].join ', '
+
+ assert_equal expected, @da.auth_header(@uri, @header, 'GET', true)
+ end
+
+ def test_auth_header_post
+ expected = [
+ 'Digest username="user"',
+ 'realm="www.example.com"',
+ 'qop=auth',
+ 'uri="/"',
+ 'nonce="4107baa081a592a6021660200000cd6c5686ff5f579324402b374d83e2c9"',
+ 'nc=00000000',
+ 'cnonce="9ea5ff3bd34554a4165bbdc1df91dcff"',
+ 'response="d82219e1e5430b136bbae1670fa51d48"'
+ ].join ', '
+
+ assert_equal expected, @da.auth_header(@uri, @header, 'POST')
+ end
+
+ def test_make_cnonce
+ assert_match %r%\A[a-f\d]{32}\z%, @da.make_cnonce
+ end
+
+end
+
Please sign in to comment.
Something went wrong with that request. Please try again.