aboutsummaryrefslogtreecommitdiffstats
path: root/activeresource
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2009-11-17 22:47:23 +0000
committerPratik Naik <pratiknaik@gmail.com>2009-11-17 22:47:23 +0000
commit5446d5cb05b50a9a3f317ded774be438e0eff909 (patch)
tree6b0b87efe3e95783763208215a3159fb63217a6d /activeresource
parent9754debb9a72f9385950e5282f3642b995ab76d8 (diff)
parentf8877d4b2a2a6f68770b376f0b1391a6295f62f2 (diff)
downloadrails-5446d5cb05b50a9a3f317ded774be438e0eff909.tar.gz
rails-5446d5cb05b50a9a3f317ded774be438e0eff909.tar.bz2
rails-5446d5cb05b50a9a3f317ded774be438e0eff909.zip
Merge remote branch 'mainstream/master'
Conflicts: activesupport/lib/active_support/core_ext/hash/conversions.rb
Diffstat (limited to 'activeresource')
-rw-r--r--activeresource/Rakefile25
-rw-r--r--activeresource/lib/active_resource/base.rb52
-rw-r--r--activeresource/lib/active_resource/connection.rb99
-rw-r--r--activeresource/test/abstract_unit.rb13
-rw-r--r--activeresource/test/cases/authorization_test.rb170
-rw-r--r--activeresource/test/cases/base/load_test.rb1
-rw-r--r--activeresource/test/cases/base_test.rb6
-rw-r--r--activeresource/test/connection_test.rb15
8 files changed, 310 insertions, 71 deletions
diff --git a/activeresource/Rakefile b/activeresource/Rakefile
index c87345e3b5..6566e84d4c 100644
--- a/activeresource/Rakefile
+++ b/activeresource/Rakefile
@@ -35,15 +35,16 @@ Rake::TestTask.new { |t|
t.warning = true
}
-task :isolated_test do
- ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
- activesupport_path = "#{File.dirname(__FILE__)}/../activesupport/lib"
- Dir.glob("test/**/*_test.rb").all? do |file|
- system(ruby, '-w', "-Ilib:test:#{activesupport_path}", file)
- end or raise "Failures"
+namespace :test do
+ task :isolated do
+ ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
+ activesupport_path = "#{File.dirname(__FILE__)}/../activesupport/lib"
+ Dir.glob("test/**/*_test.rb").all? do |file|
+ system(ruby, '-w', "-Ilib:test:#{activesupport_path}", file)
+ end or raise "Failures"
+ end
end
-
# Generate the RDoc documentation
Rake::RDocTask.new { |rdoc|
@@ -62,8 +63,6 @@ spec = eval(File.read('activeresource.gemspec'))
Rake::GemPackageTask.new(spec) do |p|
p.gem_spec = spec
- p.need_tar = true
- p.need_zip = true
end
task :lines do
@@ -80,10 +79,10 @@ task :lines do
codelines += 1
end
puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
-
+
total_lines += lines
total_codelines += codelines
-
+
lines, codelines = 0, 0
end
@@ -94,14 +93,14 @@ end
# Publishing ------------------------------------------------------
desc "Publish the beta gem"
-task :pgem => [:package] do
+task :pgem => [:package] do
require 'rake/contrib/sshpublisher'
Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
end
desc "Publish the API documentation"
-task :pdoc => [:rdoc] do
+task :pdoc => [:rdoc] do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
end
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index 803c6be53b..0e74592b0c 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -8,6 +8,7 @@ require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/misc'
+require 'active_support/core_ext/object/to_query'
require 'set'
require 'uri'
@@ -280,8 +281,8 @@ module ActiveResource
@site = nil
else
@site = create_site_uri_from(site)
- @user = URI.decode(@site.user) if @site.user
- @password = URI.decode(@site.password) if @site.password
+ @user = uri_parser.unescape(@site.user) if @site.user
+ @password = uri_parser.unescape(@site.password) if @site.password
end
end
@@ -333,6 +334,17 @@ module ActiveResource
@password = password
end
+ def auth_type
+ if defined?(@auth_type)
+ @auth_type
+ end
+ end
+
+ def auth_type=(auth_type)
+ @connection = nil
+ @auth_type = auth_type
+ end
+
# Sets the format that attributes are sent and received in from a mime type reference:
#
# Person.format = :json
@@ -404,6 +416,7 @@ module ActiveResource
@connection.proxy = proxy if proxy
@connection.user = user if user
@connection.password = password if password
+ @connection.auth_type = auth_type if auth_type
@connection.timeout = timeout if timeout
@connection.ssl_options = ssl_options if ssl_options
@connection
@@ -731,12 +744,12 @@ module ActiveResource
# Accepts a URI and creates the site URI from that.
def create_site_uri_from(site)
- site.is_a?(URI) ? site.dup : URI.parse(site)
+ site.is_a?(URI) ? site.dup : uri_parser.parse(site)
end
# Accepts a URI and creates the proxy URI from that.
def create_proxy_uri_from(proxy)
- proxy.is_a?(URI) ? proxy.dup : URI.parse(proxy)
+ proxy.is_a?(URI) ? proxy.dup : uri_parser.parse(proxy)
end
# contains a set of the current prefix parameters.
@@ -761,6 +774,10 @@ module ActiveResource
[ prefix_options, query_options ]
end
+
+ def uri_parser
+ @uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
+ end
end
attr_accessor :attributes #:nodoc:
@@ -1146,15 +1163,16 @@ module ActiveResource
def respond_to?(method, include_priv = false)
method_name = method.to_s
if attributes.nil?
- return super
+ super
elsif attributes.has_key?(method_name)
- return true
- elsif ['?','='].include?(method_name.last) && attributes.has_key?(method_name.first(-1))
- return true
+ true
+ elsif method_name =~ /(?:=|\?)$/ && attributes.include?($`)
+ true
+ else
+ # super must be called at the end of the method, because the inherited respond_to?
+ # would return true for generated readers, even if the attribute wasn't present
+ super
end
- # super must be called at the end of the method, because the inherited respond_to?
- # would return true for generated readers, even if the attribute wasn't present
- super
end
protected
@@ -1243,13 +1261,15 @@ module ActiveResource
def method_missing(method_symbol, *arguments) #:nodoc:
method_name = method_symbol.to_s
- case method_name.last
+ if method_name =~ /(=|\?)$/
+ case $1
when "="
- attributes[method_name.first(-1)] = arguments.first
+ attributes[$`] = arguments.first
when "?"
- attributes[method_name.first(-1)]
- else
- attributes.has_key?(method_name) ? attributes[method_name] : super
+ attributes[$`]
+ end
+ else
+ attributes.include?(method_name) ? attributes[method_name] : super
end
end
end
diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb
index 9d551f04e7..193be89a82 100644
--- a/activeresource/lib/active_resource/connection.rb
+++ b/activeresource/lib/active_resource/connection.rb
@@ -17,7 +17,7 @@ module ActiveResource
:head => 'Accept'
}
- attr_reader :site, :user, :password, :timeout, :proxy, :ssl_options
+ attr_reader :site, :user, :password, :auth_type, :timeout, :proxy, :ssl_options
attr_accessor :format
class << self
@@ -31,20 +31,21 @@ module ActiveResource
def initialize(site, format = ActiveResource::Formats::XmlFormat)
raise ArgumentError, 'Missing site URI' unless site
@user = @password = nil
+ @uri_parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
self.site = site
self.format = format
end
# Set URI for remote service.
def site=(site)
- @site = site.is_a?(URI) ? site : URI.parse(site)
- @user = URI.decode(@site.user) if @site.user
- @password = URI.decode(@site.password) if @site.password
+ @site = site.is_a?(URI) ? site : @uri_parser.parse(site)
+ @user = @uri_parser.unescape(@site.user) if @site.user
+ @password = @uri_parser.unescape(@site.password) if @site.password
end
# Set the proxy for remote service.
def proxy=(proxy)
- @proxy = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
+ @proxy = proxy.is_a?(URI) ? proxy : @uri_parser.parse(proxy)
end
# Sets the user for remote service.
@@ -57,6 +58,11 @@ module ActiveResource
@password = password
end
+ # Sets the auth type for remote service.
+ def auth_type=(auth_type)
+ @auth_type = legitimize_auth_type(auth_type)
+ end
+
# Sets the number of seconds after which HTTP requests to the remote service should time out.
def timeout=(timeout)
@timeout = timeout
@@ -70,31 +76,31 @@ module ActiveResource
# Executes a GET request.
# Used to get (find) resources.
def get(path, headers = {})
- format.decode(request(:get, path, build_request_headers(headers, :get)).body)
+ with_auth { format.decode(request(:get, path, build_request_headers(headers, :get, self.site.merge(path))).body) }
end
# Executes a DELETE request (see HTTP protocol documentation if unfamiliar).
# Used to delete resources.
def delete(path, headers = {})
- request(:delete, path, build_request_headers(headers, :delete))
+ with_auth { request(:delete, path, build_request_headers(headers, :delete, self.site.merge(path))) }
end
# Executes a PUT request (see HTTP protocol documentation if unfamiliar).
# Used to update resources.
def put(path, body = '', headers = {})
- request(:put, path, body.to_s, build_request_headers(headers, :put))
+ with_auth { request(:put, path, body.to_s, build_request_headers(headers, :put, self.site.merge(path))) }
end
# Executes a POST request.
# Used to create new resources.
def post(path, body = '', headers = {})
- request(:post, path, body.to_s, build_request_headers(headers, :post))
+ with_auth { request(:post, path, body.to_s, build_request_headers(headers, :post, self.site.merge(path))) }
end
# Executes a HEAD request.
# Used to obtain meta-information about resources, such as whether they exist and their size (via response headers).
def head(path, headers = {})
- request(:head, path, build_request_headers(headers, :head))
+ with_auth { request(:head, path, build_request_headers(headers, :head, self.site.merge(path))) }
end
@@ -198,13 +204,70 @@ module ActiveResource
end
# Builds headers for request to remote service.
- def build_request_headers(headers, http_method=nil)
- authorization_header.update(default_header).update(http_format_header(http_method)).update(headers)
+ def build_request_headers(headers, http_method, uri)
+ authorization_header(http_method, uri).update(default_header).update(http_format_header(http_method)).update(headers)
+ end
+
+ def response_auth_header
+ @response_auth_header ||= ""
+ end
+
+ def with_auth
+ retried ||= false
+ yield
+ rescue UnauthorizedAccess => e
+ raise if retried || auth_type != :digest
+ @response_auth_header = e.response['WWW-Authenticate']
+ retried = true
+ retry
+ end
+
+ def authorization_header(http_method, uri)
+ if @user || @password
+ if auth_type == :digest
+ { 'Authorization' => digest_auth_header(http_method, uri) }
+ else
+ { 'Authorization' => 'Basic ' + ["#{@user}:#{@password}"].pack('m').delete("\r\n") }
+ end
+ else
+ {}
+ end
end
- # Sets authorization header
- def authorization_header
- (@user || @password ? { 'Authorization' => 'Basic ' + ["#{@user}:#{ @password}"].pack('m').delete("\r\n") } : {})
+ def digest_auth_header(http_method, uri)
+ params = extract_params_from_response
+
+ ha1 = Digest::MD5.hexdigest("#{@user}:#{params['realm']}:#{@password}")
+ ha2 = Digest::MD5.hexdigest("#{http_method.to_s.upcase}:#{uri.path}")
+
+ params.merge!('cnonce' => client_nonce)
+ request_digest = Digest::MD5.hexdigest([ha1, params['nonce'], "0", params['cnonce'], params['qop'], ha2].join(":"))
+ "Digest #{auth_attributes_for(uri, request_digest, params)}"
+ end
+
+ def client_nonce
+ Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535)))
+ end
+
+ def extract_params_from_response
+ params = {}
+ if response_auth_header =~ /^(\w+) (.*)/
+ $2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
+ end
+ params
+ end
+
+ def auth_attributes_for(uri, request_digest, params)
+ [
+ %Q(username="#{@user}"),
+ %Q(realm="#{params['realm']}"),
+ %Q(qop="#{params['qop']}"),
+ %Q(uri="#{uri.path}"),
+ %Q(nonce="#{params['nonce']}"),
+ %Q(nc="0"),
+ %Q(cnonce="#{params['cnonce']}"),
+ %Q(opaque="#{params['opaque']}"),
+ %Q(response="#{request_digest}")].join(", ")
end
def http_format_header(http_method)
@@ -214,5 +277,11 @@ module ActiveResource
def logger #:nodoc:
Base.logger
end
+
+ def legitimize_auth_type(auth_type)
+ return :basic if auth_type.nil?
+ auth_type = auth_type.to_sym
+ [:basic, :digest].include?(auth_type) ? auth_type : :basic
+ end
end
end
diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb
index 05efac79cf..10849be20c 100644
--- a/activeresource/test/abstract_unit.rb
+++ b/activeresource/test/abstract_unit.rb
@@ -1,8 +1,17 @@
+root = File.expand_path('../../..', __FILE__)
+begin
+ require "#{root}/vendor/gems/environment"
+rescue LoadError
+ $:.unshift("#{root}/activesupport/lib")
+end
+
+lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
+$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
+
require 'rubygems'
require 'test/unit'
+require 'active_support'
require 'active_support/test_case'
-
-$:.unshift "#{File.dirname(__FILE__)}/../lib"
require 'active_resource'
$:.unshift "#{File.dirname(__FILE__)}/../test"
diff --git a/activeresource/test/cases/authorization_test.rb b/activeresource/test/cases/authorization_test.rb
index ca25f437e3..1a7c9ec8a4 100644
--- a/activeresource/test/cases/authorization_test.rb
+++ b/activeresource/test/cases/authorization_test.rb
@@ -8,46 +8,75 @@ class AuthorizationTest < Test::Unit::TestCase
@matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
@david = { :id => 2, :name => 'David' }.to_xml(:root => 'person')
@authenticated_conn = ActiveResource::Connection.new("http://david:test123@localhost")
- @authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' }
+ @basic_authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' }
+
+ @nonce = "MTI0OTUxMzc4NzpjYWI3NDM3NDNmY2JmODU4ZjQ2ZjcwNGZkMTJiMjE0NA=="
ActiveResource::HttpMock.respond_to do |mock|
- mock.get "/people/2.xml", @authorization_request_header, @david
- mock.put "/people/2.xml", @authorization_request_header, nil, 204
- mock.delete "/people/2.xml", @authorization_request_header, nil, 200
- mock.post "/people/2/addresses.xml", @authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5'
+ mock.get "/people/2.xml", @basic_authorization_request_header, @david
+ mock.get "/people/1.xml", @basic_authorization_request_header, nil, 401, { 'WWW-Authenticate' => 'i_should_be_ignored' }
+ mock.put "/people/2.xml", @basic_authorization_request_header, nil, 204
+ mock.delete "/people/2.xml", @basic_authorization_request_header, nil, 200
+ mock.post "/people/2/addresses.xml", @basic_authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5'
+ mock.head "/people/2.xml", @basic_authorization_request_header, nil, 200
+
+ mock.get "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "a10c9bd131c9d4d7755b8f4706fd04af") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.get "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "912c7a643f18cda562b8d9662c47b6f5") }, @david, 200
+ mock.get "/people/1.xml", { 'Authorization' => request_digest_auth_header("/people/1.xml", "d76e675c0ecfa2bb1abe01491b068a06") }, @matz, 200
+
+ mock.put "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "7de8a265a5be3c4c2d3a246562ecd6bd") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.put "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "3fb3b33d9d0b869cc75815aa11faacd9") }, nil, 204
+
+ mock.delete "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "07dfc32769a34ea3510d3a77d64ca495") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.delete "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "5d438610de7ec163b29096c9afcbb254") }, nil, 200
+
+ mock.post "/people/2/addresses.xml", { 'Authorization' => blank_digest_auth_header("/people/2/addresses.xml", "966dab13620421f928d051f2b9d7b9af") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.post "/people/2/addresses.xml", { 'Authorization' => request_digest_auth_header("/people/2/addresses.xml", "ed540d032c63f8ee34959116c090ec45") }, nil, 201, 'Location' => '/people/1/addresses/5'
+
+ mock.head "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "2854eeb92cce2aed29350ea0ce7ba1e2") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
+ mock.head "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "07cd4d247e9c130f92ba2501a080b328") }, nil, 200
+ end
+
+ # Make client nonce deterministic
+ class << @authenticated_conn
+ private
+
+ def client_nonce
+ 'i-am-a-client-nonce'
+ end
end
end
def test_authorization_header
- authorization_header = @authenticated_conn.__send__(:authorization_header)
- assert_equal @authorization_request_header['Authorization'], authorization_header['Authorization']
+ authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
+ assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization']
authorization = authorization_header["Authorization"].to_s.split
-
+
assert_equal "Basic", authorization[0]
assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
end
-
+
def test_authorization_header_with_username_but_no_password
@conn = ActiveResource::Connection.new("http://david:@localhost")
- authorization_header = @conn.__send__(:authorization_header)
+ authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
-
+
assert_equal "Basic", authorization[0]
assert_equal ["david"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
end
-
+
def test_authorization_header_with_password_but_no_username
@conn = ActiveResource::Connection.new("http://:test123@localhost")
- authorization_header = @conn.__send__(:authorization_header)
+ authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
-
+
assert_equal "Basic", authorization[0]
assert_equal ["", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
end
-
+
def test_authorization_header_with_decoded_credentials_from_url
@conn = ActiveResource::Connection.new("http://my%40email.com:%31%32%33@localhost")
- authorization_header = @conn.__send__(:authorization_header)
+ authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
@@ -58,8 +87,8 @@ class AuthorizationTest < Test::Unit::TestCase
@authenticated_conn = ActiveResource::Connection.new("http://@localhost")
@authenticated_conn.user = 'david'
@authenticated_conn.password = 'test123'
- authorization_header = @authenticated_conn.__send__(:authorization_header)
- assert_equal @authorization_request_header['Authorization'], authorization_header['Authorization']
+ authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
+ assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization']
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
@@ -69,7 +98,7 @@ class AuthorizationTest < Test::Unit::TestCase
def test_authorization_header_explicitly_setting_username_but_no_password
@conn = ActiveResource::Connection.new("http://@localhost")
@conn.user = "david"
- authorization_header = @conn.__send__(:authorization_header)
+ authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
@@ -79,38 +108,119 @@ class AuthorizationTest < Test::Unit::TestCase
def test_authorization_header_explicitly_setting_password_but_no_username
@conn = ActiveResource::Connection.new("http://@localhost")
@conn.password = "test123"
- authorization_header = @conn.__send__(:authorization_header)
+ authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
assert_equal ["", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
end
+ def test_authorization_header_if_credentials_supplied_and_auth_type_is_basic
+ @authenticated_conn.auth_type = :basic
+ authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
+ assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization']
+ authorization = authorization_header["Authorization"].to_s.split
+
+ assert_equal "Basic", authorization[0]
+ assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
+ end
+
+ def test_authorization_header_if_credentials_supplied_and_auth_type_is_digest
+ @authenticated_conn.auth_type = :digest
+ authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
+ assert_equal blank_digest_auth_header("/people/2.xml", "a10c9bd131c9d4d7755b8f4706fd04af"), authorization_header['Authorization']
+ end
+
def test_get
david = @authenticated_conn.get("/people/2.xml")
assert_equal "David", david["name"]
end
-
+
def test_post
response = @authenticated_conn.post("/people/2/addresses.xml")
assert_equal "/people/1/addresses/5", response["Location"]
end
-
+
def test_put
response = @authenticated_conn.put("/people/2.xml")
assert_equal 204, response.code
end
-
+
def test_delete
response = @authenticated_conn.delete("/people/2.xml")
assert_equal 200, response.code
end
+ def test_head
+ response = @authenticated_conn.head("/people/2.xml")
+ assert_equal 200, response.code
+ end
+
+ def test_get_with_digest_auth_handles_initial_401_response_and_retries
+ @authenticated_conn.auth_type = :digest
+ response = @authenticated_conn.get("/people/2.xml")
+ assert_equal "David", response["name"]
+ end
+
+ def test_post_with_digest_auth_handles_initial_401_response_and_retries
+ @authenticated_conn.auth_type = :digest
+ response = @authenticated_conn.post("/people/2/addresses.xml")
+ assert_equal "/people/1/addresses/5", response["Location"]
+ assert_equal 201, response.code
+ end
+
+ def test_put_with_digest_auth_handles_initial_401_response_and_retries
+ @authenticated_conn.auth_type = :digest
+ response = @authenticated_conn.put("/people/2.xml")
+ assert_equal 204, response.code
+ end
+
+ def test_delete_with_digest_auth_handles_initial_401_response_and_retries
+ @authenticated_conn.auth_type = :digest
+ response = @authenticated_conn.delete("/people/2.xml")
+ assert_equal 200, response.code
+ end
+
+ def test_head_with_digest_auth_handles_initial_401_response_and_retries
+ @authenticated_conn.auth_type = :digest
+ response = @authenticated_conn.head("/people/2.xml")
+ assert_equal 200, response.code
+ end
+
+ def test_get_with_digest_auth_caches_nonce
+ @authenticated_conn.auth_type = :digest
+ response = @authenticated_conn.get("/people/2.xml")
+ assert_equal "David", response["name"]
+
+ # There is no mock for this request with a non-cached nonce.
+ response = @authenticated_conn.get("/people/1.xml")
+ assert_equal "Matz", response["name"]
+ end
+
+ def test_retry_on_401_only_happens_with_digest_auth
+ assert_raise(ActiveResource::UnauthorizedAccess) { @authenticated_conn.get("/people/1.xml") }
+ assert_equal "", @authenticated_conn.send(:response_auth_header)
+ end
+
def test_raises_invalid_request_on_unauthorized_requests
- assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2.xml") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.xml") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.xml") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.xml") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.xml") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.xml") }
+ end
+
+ def test_raises_invalid_request_on_unauthorized_requests_with_digest_auth
+ @conn.auth_type = :digest
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.xml") }
+ assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.xml") }
+ end
+
+ def test_client_nonce_is_not_nil
+ assert_not_nil ActiveResource::Connection.new("http://david:test123@localhost").send(:client_nonce)
end
protected
@@ -119,4 +229,16 @@ class AuthorizationTest < Test::Unit::TestCase
@conn.__send__(:handle_response, Response.new(code))
end
end
+
+ def blank_digest_auth_header(uri, response)
+ %Q(Digest username="david", realm="", qop="", uri="#{uri}", nonce="", nc="0", cnonce="i-am-a-client-nonce", opaque="", response="#{response}")
+ end
+
+ def request_digest_auth_header(uri, response)
+ %Q(Digest username="david", realm="RailsTestApp", qop="auth", uri="#{uri}", nonce="#{@nonce}", nc="0", cnonce="i-am-a-client-nonce", opaque="ef6dfb078ba22298d366f99567814ffb", response="#{response}")
+ end
+
+ def response_digest_auth_header
+ %Q(Digest realm="RailsTestApp", qop="auth", algorithm=MD5, nonce="#{@nonce}", opaque="ef6dfb078ba22298d366f99567814ffb")
+ end
end
diff --git a/activeresource/test/cases/base/load_test.rb b/activeresource/test/cases/base/load_test.rb
index 189a4d81fe..7745a9439b 100644
--- a/activeresource/test/cases/base/load_test.rb
+++ b/activeresource/test/cases/base/load_test.rb
@@ -1,7 +1,6 @@
require 'abstract_unit'
require "fixtures/person"
require "fixtures/street_address"
-require 'active_support/core_ext/symbol'
require 'active_support/core_ext/hash/conversions'
module Highrise
diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb
index 1593e25595..1d3f7891ec 100644
--- a/activeresource/test/cases/base_test.rb
+++ b/activeresource/test/cases/base_test.rb
@@ -163,6 +163,12 @@ class BaseTest < Test::Unit::TestCase
assert_equal('test123', Forum.connection.password)
end
+ def test_should_accept_setting_auth_type
+ Forum.auth_type = :digest
+ assert_equal(:digest, Forum.auth_type)
+ assert_equal(:digest, Forum.connection.auth_type)
+ end
+
def test_should_accept_setting_timeout
Forum.timeout = 5
assert_equal(5, Forum.timeout)
diff --git a/activeresource/test/connection_test.rb b/activeresource/test/connection_test.rb
index 2a3e04272a..a2744d7531 100644
--- a/activeresource/test/connection_test.rb
+++ b/activeresource/test/connection_test.rb
@@ -225,6 +225,21 @@ class ConnectionTest < Test::Unit::TestCase
assert_raise(ActiveResource::SSLError) { @conn.get('/people/1.xml') }
end
+ def test_auth_type_can_be_string
+ @conn.auth_type = 'digest'
+ assert_equal(:digest, @conn.auth_type)
+ end
+
+ def test_auth_type_defaults_to_basic
+ @conn.auth_type = nil
+ assert_equal(:basic, @conn.auth_type)
+ end
+
+ def test_auth_type_ignores_nonsensical_values
+ @conn.auth_type = :wibble
+ assert_equal(:basic, @conn.auth_type)
+ end
+
protected
def assert_response_raises(klass, code)
assert_raise(klass, "Expected response code #{code} to raise #{klass}") do