Class: EventMachine::Middleware::DigestAuth

Inherits:
Object
  • Object
show all
Includes:
HttpEncoding
Defined in:
lib/em-http/middleware/digest_auth.rb

Constant Summary

Constants included from HttpEncoding

HttpEncoding::FIELD_ENCODING, HttpEncoding::HTTP_REQUEST_HEADER

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from HttpEncoding

#bytesize, #encode_auth, #encode_cookie, #encode_field, #encode_headers, #encode_host, #encode_param, #encode_query, #encode_request, #escape, #form_encode_body, #munge_header_keys, #unescape

Constructor Details

#initialize(www_authenticate, opts = {}) ⇒ DigestAuth

Returns a new instance of DigestAuth.



11
12
13
14
15
16
17
18
19
20
# 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

Instance Attribute Details

#auth_digestObject

Returns the value of attribute auth_digest.



9
10
11
# File 'lib/em-http/middleware/digest_auth.rb', line 9

def auth_digest
  @auth_digest
end

#is_digest_authObject

Returns the value of attribute is_digest_auth.



9
10
11
# File 'lib/em-http/middleware/digest_auth.rb', line 9

def is_digest_auth
  @is_digest_auth
end

Instance Method Details

#build_auth_digest(method, uri, params = nil) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 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) ⇒ Object

Process the WWW_AUTHENTICATE header to get the authentication parameters



91
92
93
94
95
# 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_cnonceObject

Generate a client nonce



98
99
100
101
102
103
104
# 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_nonceObject

Keep track of the nounce count



107
108
109
# File 'lib/em-http/middleware/digest_auth.rb', line 107

def next_nonce
  @nonce_count += 1
end

#request(client, head, body) ⇒ Object



22
23
24
25
26
27
28
29
30
# 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) ⇒ Object



32
33
34
35
36
37
38
# 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