class HTTPClient::DigestAuth

Authentication filter for handling DigestAuth negotiation. Used in WWWAuth.

Public Class Methods

new() click to toggle source

Creates new DigestAuth filter.

Calls superclass method HTTPClient::AuthBase::new
# File lib/httpclient/auth.rb, line 335
def initialize
  super('Digest')
  @auth = {}
  @nonce_count = 0
end

Public Instance Methods

challenge(uri, param_str) click to toggle source

Challenge handler: remember URL and challenge token for response.

# File lib/httpclient/auth.rb, line 377
def challenge(uri, param_str)
  synchronize {
    @challenge[uri] = parse_challenge_param(param_str)
    true
  }
end
get(req) click to toggle source

Response handler: returns credential. It sends cred only when a given uri is;

  • child page of challengeable(got *Authenticate before) uri and,

  • child page of defined credential

# File lib/httpclient/auth.rb, line 361
def get(req)
  target_uri = req.header.request_uri
  synchronize {
    param = Util.hash_find_value(@challenge) { |uri, v|
      Util.uri_part_of(target_uri, uri)
    }
    return nil unless param
    user, passwd = Util.hash_find_value(@auth) { |uri, auth_data|
      Util.uri_part_of(target_uri, uri)
    }
    return nil unless user
    calc_cred(req, user, passwd, param)
  }
end
set(uri, user, passwd) click to toggle source

Set authentication credential. uri == nil is ignored.

# File lib/httpclient/auth.rb, line 343
def set(uri, user, passwd)
  synchronize do
    if uri
      uri = Util.uri_dirname(uri)
      @auth[uri] = [user, passwd]
    end
  end
end
set?() click to toggle source

have we marked this as set - ie that it’s valid to use in this context?

# File lib/httpclient/auth.rb, line 353
def set?
  @auth.any?
end

Private Instance Methods

calc_cred(req, user, passwd, param) click to toggle source

this method is implemented by sromano and posted to tools.assembla.com/breakout/wiki/DigestForSoap Thanks! supported algorithms: MD5, MD5-sess

# File lib/httpclient/auth.rb, line 390
def calc_cred(req, user, passwd, param)
  method = req.header.request_method
  path = req.header.create_query_uri
  a_1 = "#{user}:#{param['realm']}:#{passwd}"
  a_2 = "#{method}:#{path}"
  qop = param['qop']
  nonce = param['nonce']
  cnonce = nil
  if qop || param['algorithm'] =~ /MD5-sess/
    cnonce = generate_cnonce()
  end
  a_1_md5sum = Digest::MD5.hexdigest(a_1)
  if param['algorithm'] =~ /MD5-sess/
    a_1_md5sum = Digest::MD5.hexdigest("#{a_1_md5sum}:#{nonce}:#{cnonce}")
    algorithm = "MD5-sess"
  else
    algorithm = "MD5"
  end
  message_digest = []
  message_digest << a_1_md5sum
  message_digest << nonce
  if qop
    @nonce_count += 1
    message_digest << ('%08x' % @nonce_count)
    message_digest << cnonce
    message_digest << param['qop']
  end
  message_digest << Digest::MD5.hexdigest(a_2)
  header = []
  header << "username=\"#{user}\""
  header << "realm=\"#{param['realm']}\""
  header << "nonce=\"#{nonce}\""
  header << "uri=\"#{path}\""
  if cnonce
    header << "cnonce=\"#{cnonce}\""
  end
  if qop
    header << "nc=#{'%08x' % @nonce_count}"
    header << "qop=#{param['qop']}"
  end
  header << "response=\"#{Digest::MD5.hexdigest(message_digest.join(":"))}\""
  header << "algorithm=#{algorithm}"
  header << "opaque=\"#{param['opaque']}\"" if param.key?('opaque')
  header.join(", ")
end
generate_cnonce() click to toggle source

cf. WEBrick::HTTPAuth::DigestAuth#generate_next_nonce(aTime)

# File lib/httpclient/auth.rb, line 437
def generate_cnonce
  now = "%012d" % Time.now.to_i
  pk = Digest::MD5.hexdigest([now, self.__id__, Process.pid, rand(65535)].join)[0, 32]
  [now + ':' + pk].pack('m*').chop
end
parse_challenge_param(param_str) click to toggle source
# File lib/httpclient/auth.rb, line 443
def parse_challenge_param(param_str)
  param = {}
  param_str.scan(/\s*([^\,]+(?:\\.[^\,]*)*)/).each do |str|
    key, value = str[0].scan(/\A([^=]+)=(.*)\z/)[0]
    if /\A"(.*)"\z/ =~ value
      value = $1.gsub(/\\(.)/, '\1')
    end
    param[key] = value
  end
  param
end