Module Rufus::Verbs::DigestAuthMixin

  1. lib/rufus/verbs/digest.rb

Specified by www.ietf.org/rfc/rfc2617.txt Inspired by segment7.net/projects/ruby/snippets/digest_auth.rb

The EndPoint classes mixes this in to support digest authentication.

Public instance methods

check_authentication_info (res, opts)

Interprets the information in the response’s ‘Authorization-Info’ header.

[show source]
    # File lib/rufus/verbs/digest.rb, line 74
74:     def check_authentication_info (res, opts)
75: 
76:       return if no_digest_auth
77:         # not using digest authentication
78: 
79:       return unless @challenge
80:         # not yet authenticated
81: 
82:       authinfo = AuthInfo.new res
83:       @challenge.nonce = authinfo.nextnonce
84:     end
digest_auth (req, opts)

Makes sure digest_auth is on

[show source]
    # File lib/rufus/verbs/digest.rb, line 43
43:     def digest_auth (req, opts)
44: 
45:       #return if no_digest_auth
46:         # already done in add_authentication()
47: 
48:       @cnonce ||= generate_cnonce
49:       @nonce_count ||= 0
50: 
51:       mention_digest_auth(req, opts) \
52:         and return
53: 
54:       mention_digest_auth(req, opts) \
55:         if request_challenge(req, opts)
56:     end
mention_digest_auth (req, opts)

Sets the ‘Authorization’ header with the appropriate info.

[show source]
    # File lib/rufus/verbs/digest.rb, line 61
61:     def mention_digest_auth (req, opts)
62: 
63:       return false unless @challenge
64: 
65:       req['Authorization'] = generate_header req, opts
66: 
67:       true
68:     end

Protected instance methods

generate_cnonce ()

To be enhanced.

(For example www.intertwingly.net/blog/1585.html)

[show source]
     # File lib/rufus/verbs/digest.rb, line 102
102:     def generate_cnonce
103: 
104:       Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535)))
105:     end
generate_header (req, opts)

Generates the Authentication header that will be returned to the server.

[show source]
     # File lib/rufus/verbs/digest.rb, line 138
138:     def generate_header (req, opts)
139: 
140:       @nonce_count += 1
141: 
142:       user, pass = o(opts, :digest_authentication)
143:       realm = @challenge.realm || ""
144:       method = req.class.const_get(:METHOD)
145:       path = opts[:path]
146: 
147:       a1 = if @challenge.algorithm == 'MD5-sess'
148:         h(h(user, realm, pass), @challenge.nonce, @cnonce)
149:       else
150:         h(user, realm, pass)
151:       end
152: 
153:       a2, qop = if @challenge.qop.include?("auth-int")
154:         [ h(method, path, h(req.body)), "auth-int" ]
155:       else
156:         [ h(method, path), "auth" ]
157:       end
158: 
159:       nc = ('%08x' % @nonce_count)
160: 
161:       digest = h(
162:         #a1, @challenge.nonce, nc, @cnonce, @challenge.qop, a2)
163:         a1, @challenge.nonce, nc, @cnonce, "auth", a2)
164: 
165:       header = ""
166:       header << "Digest username=\"#{user}\", "
167:       header << "realm=\"#{realm}\", "
168:       header << "qop=\"#{qop}\", "
169:       header << "uri=\"#{path}\", "
170:       header << "nonce=\"#{@challenge.nonce}\", "
171:       #header << "nc=##{nc}, "
172:       header << "nc=#{nc}, "
173:       header << "cnonce=\"#{@cnonce}\", "
174:       header << "algorithm=\"#{@challenge.algorithm}\", "
175:       #header << "algorithm=\"MD5-sess\", "
176:       header << "response=\"#{digest}\", "
177:       header << "opaque=\"#{@challenge.opaque}\""
178: 
179:       header
180:     end
h (*args)

Generates an MD5 digest of the arguments (joined by “:”).

[show source]
     # File lib/rufus/verbs/digest.rb, line 129
129:     def h (*args)
130: 
131:       Digest::MD5.hexdigest(args.join(":"))
132:     end
no_digest_auth ()

Returns true if :digest_authentication is set at endpoint or request level.

[show source]
    # File lib/rufus/verbs/digest.rb, line 92
92:     def no_digest_auth
93: 
94:       (not o(opts, :digest_authentication))
95:     end
request_challenge (req, opts)
[show source]
     # File lib/rufus/verbs/digest.rb, line 107
107:     def request_challenge (req, opts)
108: 
109:       op = opts.dup
110: 
111:       op[:digest_authentication] = false
112:         # preventing an infinite loop
113: 
114:       method = req.class.const_get(:METHOD).downcase.to_sym
115:       #method = :get
116: 
117:       res = request(method, op)
118: 
119:       return false if res.code.to_i != 401
120: 
121:       @challenge = Challenge.new res
122: 
123:       true
124:     end