From bf597fddf268aed4703c5cc7da317919212e83b7 Mon Sep 17 00:00:00 2001
From: Maximilian Meister <mmeister@suse.de>
Date: Fri, 12 Aug 2016 01:04:14 +0200
Subject: [PATCH 1/4] require net-http-digest_auth

---
 lib/restclient.rb   | 1 +
 rest-client.gemspec | 1 +
 2 files changed, 2 insertions(+)

diff --git a/lib/restclient.rb b/lib/restclient.rb
index bdb53f0..0c72d72 100644
--- a/lib/restclient.rb
+++ b/lib/restclient.rb
@@ -1,4 +1,5 @@
 require 'net/http'
+require 'net/http/digest_auth'
 require 'openssl'
 require 'stringio'
 require 'uri'
diff --git a/rest-client.gemspec b/rest-client.gemspec
index b16d7e6..7ee4b40 100644
--- a/rest-client.gemspec
+++ b/rest-client.gemspec
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
   s.add_dependency('http-cookie', '>= 1.0.2', '< 2.0')
   s.add_dependency('mime-types', '>= 1.16', '< 4.0')
   s.add_dependency('netrc', '~> 0.8')
+  s.add_dependency('net-http-digest_auth', '~> 1.4')
 
   s.required_ruby_version = '>= 2.0.0'
 end

From ec4eca09612e38363bbc1b93637820c589bb1e5c Mon Sep 17 00:00:00 2001
From: Maximilian Meister <mmeister@suse.de>
Date: Fri, 12 Aug 2016 01:08:33 +0200
Subject: [PATCH 2/4] add digest authentication

and make the authentication type configurable
---
 lib/restclient/request.rb  | 28 +++++++++++++++++++++++++---
 lib/restclient/resource.rb |  4 ++++
 spec/unit/request_spec.rb  |  2 +-
 3 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/lib/restclient/request.rb b/lib/restclient/request.rb
index e818aad..6cd21cf 100644
--- a/lib/restclient/request.rb
+++ b/lib/restclient/request.rb
@@ -140,6 +140,12 @@ def initialize args
       @user = args[:user] if args.include?(:user)
       @password = args[:password] if args.include?(:password)
 
+      @auth_type = if args.include?(:auth_type)
+        args[:auth_type]
+      else
+        nil
+      end
+
       if args.include?(:timeout)
         @read_timeout = args[:timeout]
         @open_timeout = args[:timeout]
@@ -697,7 +703,7 @@ def transmit uri, req, payload, & block
       # this is only needed for Timeout exceptions thrown outside of Net::HTTP.
       established_connection = false
 
-      setup_credentials req
+      setup_credentials req, uri
 
       net = net_http_object(uri.hostname, uri.port)
       net.use_ssl = uri.is_a?(URI::HTTPS)
@@ -812,8 +818,24 @@ def transmit uri, req, payload, & block
       end
     end
 
-    def setup_credentials(req)
-      req.basic_auth(user, password) if user && !headers.has_key?("Authorization")
+    def setup_credentials(req, uri = nil)
+      if user && !headers.has_key?("Authorization")
+        if @auth_type == :digest
+          RestClient.head(uri.to_s) do |response, request, result|
+            auth_header = response.headers[:www_authenticate]
+            if auth_header =~ /Digest realm=/
+              digest_auth = Net::HTTP::DigestAuth.new
+              uri.user = user
+              uri.password = password
+              auth = digest_auth.auth_header(uri, auth_header, @method.upcase)
+
+              req["Authorization"] = auth
+            end
+          end
+        else
+          req.basic_auth(user, password)
+        end
+      end
     end
 
     def fetch_body(http_response)
diff --git a/lib/restclient/resource.rb b/lib/restclient/resource.rb
index 955f682..af73ac0 100644
--- a/lib/restclient/resource.rb
+++ b/lib/restclient/resource.rb
@@ -121,6 +121,10 @@ def open_timeout
       options[:open_timeout]
     end
 
+    def auth_type
+      options[:auth_type]
+    end
+
     # Construct a subresource, preserving authentication.
     #
     # Example:
diff --git a/spec/unit/request_spec.rb b/spec/unit/request_spec.rb
index b338947..4bc09c7 100644
--- a/spec/unit/request_spec.rb
+++ b/spec/unit/request_spec.rb
@@ -416,7 +416,7 @@
 
       allow(@request).to receive(:user).and_return('joe')
       allow(@request).to receive(:password).and_return('mypass')
-      expect(@request).to receive(:setup_credentials).with('req')
+      expect(@request).to receive(:setup_credentials).with('req', @uri)
 
       @request.send(:transmit, @uri, 'req', nil)
     end

From 0d739946119b8c0341e844ab61940789275c3eda Mon Sep 17 00:00:00 2001
From: Maximilian Meister <mmeister@suse.de>
Date: Fri, 12 Aug 2016 01:11:35 +0200
Subject: [PATCH 3/4] add unit test for digest authentication

unfortunately this is not covering the full codepath yet, as it is
still unclear to me how to go through the RestClient.head block
---
 spec/unit/request_spec.rb | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/spec/unit/request_spec.rb b/spec/unit/request_spec.rb
index 4bc09c7..e0dc2ec 100644
--- a/spec/unit/request_spec.rb
+++ b/spec/unit/request_spec.rb
@@ -444,6 +444,16 @@
       expect(req).not_to receive(:basic_auth)
       @request.send(:setup_credentials, req)
     end
+
+    it "sets up digest authentication" do
+      allow(@request).to receive(:user).and_return('joe')
+      allow(@request).to receive(:password).and_return('mypass')
+      allow(RestClient).to receive(:head).with(anything).and_return(true)
+      req = double("request")
+      # initialize request again to set the auth_type option
+      digest_request = RestClient::Request.new(@request.args.merge!(auth_type: :digest))
+      digest_request.send(:setup_credentials, req, @uri)
+    end
   end
 
   it "catches EOFError and shows the more informative ServerBrokeConnection" do

From bc4e66665b4596faade6ae3e3bda8279a8e09053 Mon Sep 17 00:00:00 2001
From: Maximilian Meister <mmeister@suse.de>
Date: Fri, 12 Aug 2016 01:15:19 +0200
Subject: [PATCH 4/4] add parsed_response instance method

this is convenient for further processing the response body as a Hash
the idea came about when migrating from HTTParty to RestClient
---
 lib/restclient/abstract_response.rb | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lib/restclient/abstract_response.rb b/lib/restclient/abstract_response.rb
index 7cd5b82..a737f60 100644
--- a/lib/restclient/abstract_response.rb
+++ b/lib/restclient/abstract_response.rb
@@ -31,6 +31,11 @@ def raw_headers
       @raw_headers ||= @net_http_res.to_hash
     end
 
+    # parse JSON response
+    def parsed_response
+      JSON.parse(self.body)
+    end
+
     def response_set_vars(net_http_res, request)
       @net_http_res = net_http_res
       @request = request
