lib/httpx/plugins/digest_authentication.rb in httpx-0.19.8 vs lib/httpx/plugins/digest_authentication.rb in httpx-0.20.0

- old
+ new

@@ -1,159 +1,63 @@ # frozen_string_literal: true -require "digest" - module HTTPX module Plugins # # This plugin adds helper methods to implement HTTP Digest Auth (https://tools.ietf.org/html/rfc7616) # # https://gitlab.com/honeyryderchuck/httpx/wikis/Authentication#authentication # - module DigestAuthentication - using RegexpExtensions unless Regexp.method_defined?(:match?) - + module DigestAuth DigestError = Class.new(Error) class << self def extra_options(options) options.merge(max_concurrent_requests: 1) end def load_dependencies(*) - require "securerandom" - require "digest" + require_relative "authentication/digest" end end module OptionsMethods def option_digest(value) - raise TypeError, ":digest must be a Digest" unless value.is_a?(Digest) + raise TypeError, ":digest must be a Digest" unless value.is_a?(Authentication::Digest) value end end module InstanceMethods def digest_authentication(user, password) - with(digest: Digest.new(user, password)) + with(digest: Authentication::Digest.new(user, password)) end alias_method :digest_auth, :digest_authentication def send_requests(*requests) requests.flat_map do |request| digest = request.options.digest - if digest - probe_response = wrap { super(request).first } + unless digest + super(request) + next + end - if digest && !probe_response.is_a?(ErrorResponse) && - probe_response.status == 401 && probe_response.headers.key?("www-authenticate") && - /Digest .*/.match?(probe_response.headers["www-authenticate"]) + probe_response = wrap { super(request).first } - request.transition(:idle) - - token = digest.generate_header(request, probe_response) - request.headers["authorization"] = "Digest #{token}" - - super(request) - else - probe_response - end - else + if probe_response.status == 401 && digest.can_authenticate?(probe_response.headers["www-authenticate"]) + request.transition(:idle) + request.headers["authorization"] = digest.authenticate(request, probe_response.headers["www-authenticate"]) super(request) + else + probe_response end end end end - - class Digest - def initialize(user, password) - @user = user - @password = password - @nonce = 0 - end - - def generate_header(request, response, _iis = false) - meth = request.verb.to_s.upcase - www = response.headers["www-authenticate"] - - # discard first token, it's Digest - auth_info = www[/^(\w+) (.*)/, 2] - - uri = request.path - - params = Hash[auth_info.split(/ *, */) - .map { |val| val.split("=") } - .map { |k, v| [k, v.delete("\"")] }] - nonce = params["nonce"] - nc = next_nonce - - # verify qop - qop = params["qop"] - - if params["algorithm"] =~ /(.*?)(-sess)?$/ - alg = Regexp.last_match(1) - algorithm = ::Digest.const_get(alg) - raise DigestError, "unknown algorithm \"#{alg}\"" unless algorithm - - sess = Regexp.last_match(2) - params.delete("algorithm") - else - algorithm = ::Digest::MD5 - end - - if qop || sess - cnonce = make_cnonce - nc = format("%<nonce>08x", nonce: nc) - end - - a1 = if sess - [algorithm.hexdigest("#{@user}:#{params["realm"]}:#{@password}"), - nonce, - cnonce].join ":" - else - "#{@user}:#{params["realm"]}:#{@password}" - end - - ha1 = algorithm.hexdigest(a1) - ha2 = algorithm.hexdigest("#{meth}:#{uri}") - request_digest = [ha1, nonce] - request_digest.push(nc, cnonce, qop) if qop - request_digest << ha2 - request_digest = request_digest.join(":") - - header = [ - %(username="#{@user}"), - %(nonce="#{nonce}"), - %(uri="#{uri}"), - %(response="#{algorithm.hexdigest(request_digest)}"), - ] - header << %(realm="#{params["realm"]}") if params.key?("realm") - header << %(algorithm=#{params["algorithm"]}") if params.key?("algorithm") - header << %(opaque="#{params["opaque"]}") if params.key?("opaque") - header << %(cnonce="#{cnonce}") if cnonce - header << %(nc=#{nc}) - header << %(qop=#{qop}) if qop - header.join ", " - end - - private - - def make_cnonce - ::Digest::MD5.hexdigest [ - Time.now.to_i, - Process.pid, - SecureRandom.random_number(2**32), - ].join ":" - end - - def next_nonce - @nonce += 1 - end - end end - register_plugin :digest_authentication, DigestAuthentication + register_plugin :digest_authentication, DigestAuth end end