class EventMachine::Middleware::DigestAuth

Attributes

auth_digest[RW]
is_digest_auth[RW]

Public Class Methods

new(www_authenticate, opts = {}) click to toggle source
# File lib/em-http/middleware/digest_auth.rb, line 11
def initialize(www_authenticate, opts = {})
  @nonce_count = -1
  @opts = opts
  @digest_params = {
      algorithm: 'MD5' # MD5 is the default hashing algorithm
  }
  if (@is_digest_auth = www_authenticate =~ /^Digest/)
    get_params(www_authenticate)
  end
end

Public Instance Methods

build_auth_digest(method, uri, params = nil) click to toggle source
# File lib/em-http/middleware/digest_auth.rb, line 40
def build_auth_digest(method, uri, params = nil)
  params = @opts.merge(@digest_params) if !params
  nonce_count = next_nonce

  user = unescape params[:username]
  password = unescape params[:password]

  splitted_algorithm = params[:algorithm].split('-')
  sess = "-sess" if splitted_algorithm[1]
  raw_algorithm = splitted_algorithm[0]
  if %w(MD5 SHA1 SHA2 SHA256 SHA384 SHA512 RMD160).include? raw_algorithm
    algorithm = eval("Digest::#{raw_algorithm}")
  else
    raise "Unknown algorithm: #{raw_algorithm}"
  end
  qop = params[:qop]
  cnonce = make_cnonce if qop or sess
  a1 = if sess
    [
      algorithm.hexdigest("#{params[:username]}:#{params[:realm]}:#{params[:password]}"),
      params[:nonce],
      cnonce,
      ].join ':'
  else
    "#{params[:username]}:#{params[:realm]}:#{params[:password]}"
  end
  ha1 = algorithm.hexdigest a1
  ha2 = algorithm.hexdigest "#{method}:#{uri}"

  request_digest = [ha1, params[:nonce]]
  request_digest.push(('%08x' % @nonce_count), cnonce, qop) if qop
  request_digest << ha2
  request_digest = request_digest.join ':'
  header = [
    "Digest username=\"#{params[:username]}\"",
    "realm=\"#{params[:realm]}\"",
    "algorithm=#{raw_algorithm}#{sess}",
    "uri=\"#{uri}\"",
    "nonce=\"#{params[:nonce]}\"",
    "response=\"#{algorithm.hexdigest(request_digest)[0, 32]}\"",
  ]
  if params[:qop]
    header << "qop=#{qop}"
    header << "nc=#{'%08x' % @nonce_count}"
    header << "cnonce=\"#{cnonce}\""
  end
  header << "opaque=\"#{params[:opaque]}\"" if params.key? :opaque
  header.join(', ')
end
get_params(www_authenticate) click to toggle source

Process the WWW_AUTHENTICATE header to get the authentication parameters

# File lib/em-http/middleware/digest_auth.rb, line 91
def get_params(www_authenticate)
  www_authenticate.scan(/(\w+)="?(.*?)"?(,|\z)/).each do |match|
    @digest_params[match[0].to_sym] = match[1]
  end
end
make_cnonce() click to toggle source

Generate a client nonce

# File lib/em-http/middleware/digest_auth.rb, line 98
def make_cnonce
  Digest::MD5.hexdigest [
    Time.now.to_i,
    $$,
    SecureRandom.random_number(2**32),
  ].join ':'
end
next_nonce() click to toggle source

Keep track of the nounce count

# File lib/em-http/middleware/digest_auth.rb, line 107
def next_nonce
  @nonce_count += 1
end
request(client, head, body) click to toggle source
# File lib/em-http/middleware/digest_auth.rb, line 22
def request(client, head, body)
  # Allow HTTP basic auth fallback
  if @is_digest_auth
    head['Authorization'] = build_auth_digest(client.req.method, client.req.uri.path, @opts.merge(@digest_params))
  else
    head['Authorization'] = [@opts[:username], @opts[:password]]
  end
  [head, body]
end
response(resp) click to toggle source
# File lib/em-http/middleware/digest_auth.rb, line 32
def response(resp)
  # If the server responds with the Authentication-Info header, set the nonce to the new value
  if @is_digest_auth && (authentication_info = resp.response_header['Authentication-Info'])
    authentication_info =~ /nextnonce="?(.*?)"?(,|\z)/
    @digest_params[:nonce] = $1
  end
end