httpauth 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2404f2197812b252663439a4537d6a068d4e09c4
4
+ data.tar.gz: 71ece448d2d0370456216b90059d181f246c2131
5
+ SHA512:
6
+ metadata.gz: a2e2f64693bd6e3c0ad4fa32dcd2bd01483cc8626dade2d3dde72a6de047fa29b9b9b8e05481fef81a212d317017e9990b4b4d0714f39f16134bfeef3976f8e2
7
+ data.tar.gz: 1d8d22a05e34731303255144c97bb29e6e7243bec8f36bec7ca6a2a651eec36b57815a413753f70f8c278469c199b468b85d6ad09dc7a09ddb051f84fe02cf28
data/README.md CHANGED
@@ -37,4 +37,4 @@ IE doesn't use the full URI for digest calculation, it chops off the query param
37
37
 
38
38
  ## Known server implementation issues
39
39
 
40
- Apache 2.0 sends Authorization-Info headers without a nextnonce directive.
40
+ Apache 2.0 sends Authorization-Info headers without a nextnonce directive.
@@ -1,4 +1,4 @@
1
1
  require 'httpauth/constants'
2
2
  require 'httpauth/exceptions'
3
3
  require 'httpauth/basic'
4
- require 'httpauth/digest'
4
+ require 'httpauth/digest'
@@ -7,7 +7,7 @@ module HTTPAuth
7
7
  # the server sends a challenge and the client has to respond to that with the correct credentials. These
8
8
  # credentials will have to be sent with every request from that point on.
9
9
  #
10
- # == On the server
10
+ # == On the server
11
11
  #
12
12
  # On the server you will have to check the headers for the 'Authorization' header. When you find one unpack
13
13
  # it and check it against your database of credentials. If the credentials are wrong you have to return a
@@ -53,47 +53,46 @@ module HTTPAuth
53
53
  # end
54
54
  class Basic
55
55
  class << self
56
-
57
56
  # Unpacks the HTTP Basic 'Authorization' credential header
58
57
  #
59
58
  # * <tt>authorization</tt>: The contents of the Authorization header
60
59
  # * Returns a list with two items: the username and password
61
- def unpack_authorization(authorization)
60
+ def unpack_authorization(authorization)
62
61
  d = authorization.split ' '
63
- raise ArgumentError.new("HTTPAuth::Basic can only unpack Basic Authentication headers") unless d[0] == 'Basic'
62
+ fail(ArgumentError, 'HTTPAuth::Basic can only unpack Basic Authentication headers') unless d[0] == 'Basic'
64
63
  Base64.decode64(d[1]).split(':')[0..1]
65
64
  end
66
-
65
+
67
66
  # Packs HTTP Basic credentials to an 'Authorization' header
68
67
  #
69
68
  # * <tt>username</tt>: A string with the username
70
69
  # * <tt>password</tt>: A string with the password
71
70
  def pack_authorization(username, password)
72
- "Basic %s" % Base64.encode64("#{username}:#{password}").gsub("\n", '')
71
+ format('Basic %s', Base64.encode64("#{username}:#{password}").gsub("\n", ''))
73
72
  end
74
-
73
+
75
74
  # Returns contents for the WWW-authenticate header
76
75
  #
77
76
  # * <tt>realm</tt>: A string with a recognizable title for the restricted resource
78
77
  def pack_challenge(realm)
79
- "Basic realm=\"%s\"" % realm.gsub('"', '')
78
+ format("Basic realm=\"%s\"", realm.gsub('"', ''))
80
79
  end
81
-
80
+
82
81
  # Returns the name of the realm in a WWW-Authenticate header
83
82
  #
84
83
  # * <tt>authenticate</tt>: The contents of the WWW-Authenticate header
85
84
  def unpack_challenge(authenticate)
86
85
  if authenticate =~ /Basic\srealm=\"([^\"]*)\"/
87
- return $1
86
+ return Regexp.last_match[1]
88
87
  else
89
88
  if authenticate =~ /^Basic/
90
- raise UnwellformedHeader.new("Can't parse the WWW-Authenticate header, it's probably not well formed")
89
+ fail(UnwellformedHeader, "Can't parse the WWW-Authenticate header, it's probably not well formed")
91
90
  else
92
- raise ArgumentError.new("HTTPAuth::Basic can only unpack Basic Authentication headers")
91
+ fail(ArgumentError, 'HTTPAuth::Basic can only unpack Basic Authentication headers')
93
92
  end
94
93
  end
95
94
  end
96
-
95
+
97
96
  # Finds and unpacks the authorization credentials in a hash with the CGI enviroment. Returns [nil,nil] if no
98
97
  # credentials were found. See HTTPAuth::CREDENTIAL_HEADERS for supported variable names.
99
98
  #
@@ -105,10 +104,10 @@ module HTTPAuth
105
104
  # RewriteEngine on
106
105
  # RewriteRule ^admin/ - [E=X-HTTP-AUTHORIZATION:%{HTTP:Authorization}]
107
106
  def get_credentials(env)
108
- d = HTTPAuth::CREDENTIAL_HEADERS.inject(false) { |d,h| env[h] || d }
109
- return unpack_authorization(d) unless !d or d.nil? or d.empty?
107
+ d = HTTPAuth::CREDENTIAL_HEADERS.inject(false) { |a, e| env[e] || a }
108
+ return unpack_authorization(d) unless !d || d.nil? || d.empty?
110
109
  [nil, nil]
111
110
  end
112
111
  end
113
112
  end
114
- end
113
+ end
@@ -4,11 +4,11 @@
4
4
  # For more information see RFC 2617 (http://www.ietf.org/rfc/rfc2617.txt)
5
5
  module HTTPAuth
6
6
  VERSION = '0.2'
7
-
7
+
8
8
  CREDENTIAL_HEADERS = %w{REDIRECT_X_HTTP_AUTHORIZATION X-HTTP-AUTHORIZATION X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION}
9
- SUPPORTED_SCHEMES = { :basic => 'Basic', :digest => 'Digest' }
10
- SUPPORTED_QOPS = ['auth', 'auth-int']
11
- SUPPORTED_ALGORITHMS = ['MD5', 'MD5-sess']
9
+ SUPPORTED_SCHEMES = {:basic => 'Basic', :digest => 'Digest'}
10
+ SUPPORTED_QOPS = %w[auth auth-int]
11
+ SUPPORTED_ALGORITHMS = %w[MD5 MD5-sess]
12
12
  PREFERRED_QOP = 'auth'
13
13
  PREFERRED_ALGORITHM = 'MD5'
14
- end
14
+ end
@@ -60,66 +60,66 @@ module HTTPAuth
60
60
  elsif variant == :challenge
61
61
  encode.merge! :qop => :list_to_comma_quoted_string
62
62
  else
63
- raise ArgumentError.new("#{variant} is not a valid value for `variant' use :auth, :credentials or :challenge")
63
+ fail(ArgumentError, "#{variant} is not a valid value for `variant' use :auth, :credentials or :challenge")
64
64
  end
65
65
  (variant == :auth ? '' : 'Digest ') + h.collect do |directive, value|
66
66
  '' << directive.to_s << '=' << if encode[directive]
67
- begin
68
- Conversions.send encode[directive], value
69
- rescue NoMethodError, ArgumentError
70
- raise ArgumentError.new("Can't encode #{directive}(#{value.inspect}) with #{encode[directive]}")
71
- end
72
- elsif encode[directive].nil?
73
- begin
74
- Conversions.quote_string value
75
- rescue NoMethodError, ArgumentError
76
- raise ArgumentError.new("Can't encode #{directive}(#{value.inspect}) with quote_string")
77
- end
78
- else
79
- value
67
+ begin
68
+ Conversions.send encode[directive], value
69
+ rescue NoMethodError, ArgumentError
70
+ raise(ArgumentError, "Can't encode #{directive}(#{value.inspect}) with #{encode[directive]}")
80
71
  end
81
- end.join(", ")
72
+ elsif encode[directive].nil?
73
+ begin
74
+ Conversions.quote_string value
75
+ rescue NoMethodError, ArgumentError
76
+ raise(ArgumentError, "Can't encode #{directive}(#{value.inspect}) with quote_string")
77
+ end
78
+ else
79
+ value
80
+ end
81
+ end.join(', ')
82
82
  end
83
83
 
84
84
  # Decodes digest directives from a header. Returns a hash with directives.
85
85
  #
86
86
  # * <tt>directives</tt>: The directives
87
87
  # * <tt>variant</tt>: Specifies whether the directives are for an Authorize header (:credentials),
88
- # for a WWW-Authenticate header (:challenge) or for a Authentication-Info header (:auth_info).
88
+ # for a WWW-Authenticate header (:challenge) or for a Authentication-Info header (:auth_info).
89
89
  def decode_directives(directives, variant)
90
- raise HTTPAuth::UnwellformedHeader.new("Can't decode directives which are nil") if directives.nil?
90
+ fail(HTTPAuth::UnwellformedHeader, "Can't decode directives which are nil") if directives.nil?
91
91
  decode = {:domain => :space_quoted_string_to_list, :algorithm => false, :stale => :str_to_bool, :nc => :hex_to_int}
92
92
  if [:credentials, :auth].include? variant
93
93
  decode.merge! :qop => false
94
94
  elsif variant == :challenge
95
95
  decode.merge! :qop => :comma_quoted_string_to_list
96
96
  else
97
- raise ArgumentError.new("#{variant} is not a valid value for `variant' use :auth, :credentials or :challenge")
97
+ fail(ArgumentError, "#{variant} is not a valid value for `variant' use :auth, :credentials or :challenge")
98
98
  end
99
-
100
- start = 0
101
- unless variant == :auth
99
+
100
+ start = 0
101
+ unless variant == :auth
102
102
  # The first six characters are 'Digest '
103
103
  start = 6
104
104
  scheme = directives[0..6].strip
105
- raise HTTPAuth::UnwellformedHeader.new("Scheme should be Digest, server responded with `#{directives}'") unless scheme == 'Digest'
105
+ fail(HTTPAuth::UnwellformedHeader, "Scheme should be Digest, server responded with `#{directives}'") unless scheme == 'Digest'
106
106
  end
107
-
107
+
108
108
  # The rest are the directives
109
109
  # TODO: split is ugly, I want a real parser (:
110
- directives[start..-1].split(',').inject({}) do |h,part|
110
+ directives[start..-1].split(',').inject({}) do |h, part|
111
111
  parts = part.split('=')
112
112
  name = parts[0].strip.intern
113
113
  value = parts[1..-1].join('=').strip
114
-
114
+
115
115
  # --- HACK
116
116
  # IE and Safari qoute qop values
117
117
  # IE also quotes algorithm values
118
- if variant != :challenge and [:qop, :algorithm].include?(name) and value =~ /^\"[^\"]+\"$/
118
+ if variant != :challenge && [:qop, :algorithm].include?(name) && value =~ /^\"[^\"]+\"$/
119
119
  value = Conversions.unquote_string(value)
120
120
  end
121
121
  # --- END HACK
122
-
122
+
123
123
  if decode[name]
124
124
  h[name] = Conversions.send decode[name], value
125
125
  elsif decode[name].nil?
@@ -130,19 +130,25 @@ module HTTPAuth
130
130
  h
131
131
  end
132
132
  end
133
-
133
+
134
134
  # Concat arguments the way it's done frequently in the Digest spec.
135
135
  #
136
136
  # digest_concat('a', 'b') #=> "a:b"
137
137
  # digest_concat('a', 'b', c') #=> "a:b:c"
138
- def digest_concat(*args); args.join ':'; end
139
-
138
+ def digest_concat(*args)
139
+ args.join ':'
140
+ end
141
+
140
142
  # Calculate the MD5 hexdigest for the string data
141
- def digest_h(data); ::Digest::MD5.hexdigest data; end
142
-
143
+ def digest_h(data)
144
+ ::Digest::MD5.hexdigest data
145
+ end
146
+
143
147
  # Calculate the KD value of a secret and data as explained in the RFC.
144
- def digest_kd(secret, data); digest_h digest_concat(secret, data); end
145
-
148
+ def digest_kd(secret, data)
149
+ digest_h digest_concat(secret, data)
150
+ end
151
+
146
152
  # Calculate the Digest for the credentials
147
153
  def htdigest(username, realm, password)
148
154
  digest_h digest_concat(username, realm, password)
@@ -162,7 +168,7 @@ module HTTPAuth
162
168
  h[:digest] || htdigest(h[:username], h[:realm], h[:password])
163
169
  end
164
170
  end
165
-
171
+
166
172
  # Calculate the H(A2) for the Authorize header as explained in the RFC.
167
173
  def request_digest_a2(h)
168
174
  # TODO: check for known qop values (look out for the safari qop quote bug)
@@ -186,7 +192,7 @@ module HTTPAuth
186
192
  #
187
193
  # * <tt>variant</tt>: Either <tt>:request</tt> or <tt>:response</tt>, as seen from the server.
188
194
  def calculate_digest(h, s, variant)
189
- raise ArgumentError.new("Variant should be either :request or :response, not #{variant}") unless [:request, :response].include?(variant)
195
+ fail(ArgumentError, "Variant should be either :request or :response, not #{variant}") unless [:request, :response].include?(variant)
190
196
  # Compatability with RFC 2069
191
197
  if h[:qop].nil?
192
198
  digest_kd digest_a1(h, s), digest_concat(
@@ -203,7 +209,7 @@ module HTTPAuth
203
209
  )
204
210
  end
205
211
  end
206
-
212
+
207
213
  # Return a hash with the keys in <tt>keys</tt> found in <tt>h</tt>.
208
214
  #
209
215
  # Example
@@ -211,16 +217,16 @@ module HTTPAuth
211
217
  # filter_h_on({1=>1,2=>2}, [1]) #=> {1=>1}
212
218
  # filter_h_on({1=>1,2=>2}, [1, 2]) #=> {1=>1,2=>2}
213
219
  def filter_h_on(h, keys)
214
- h.inject({}) { |r,l| keys.include?(l[0]) ? r.merge({l[0]=>l[1]}) : r }
220
+ h.inject({}) { |a, e| keys.include?(e[0]) ? a.merge(e[0] => e[1]) : a }
215
221
  end
216
-
222
+
217
223
  # Create a nonce value of the time and a salt. The nonce is created in such a
218
224
  # way that the issuer can check the age of the nonce.
219
225
  #
220
226
  # * <tt>salt</tt>: A reasonably long passphrase known only to the issuer.
221
227
  def create_nonce(salt)
222
228
  now = Time.now
223
- time = now.strftime("%Y-%m-%d %H:%M:%S").to_s + ':' + now.usec.to_s
229
+ time = now.strftime('%Y-%m-%d %H:%M:%S').to_s + ':' + now.usec.to_s
224
230
  Base64.encode64(
225
231
  digest_concat(
226
232
  time,
@@ -228,20 +234,21 @@ module HTTPAuth
228
234
  )
229
235
  ).gsub("\n", '')[0..-3]
230
236
  end
231
-
237
+
232
238
  # Create a 32 character long opaque string with a 'random' value
233
239
  def create_opaque
234
- s = []; 16.times { s << rand(127).chr }
240
+ s = []
241
+ 16.times { s << rand(127).chr }
235
242
  digest_h s.join
236
243
  end
237
244
  end
238
245
  end
239
-
246
+
240
247
  # Superclass for all the header container classes
241
248
  class AbstractHeader
242
249
  # holds directives and values for digest calculation
243
250
  attr_reader :h
244
-
251
+
245
252
  # Redirects attribute messages to the internal directives
246
253
  #
247
254
  # Example:
@@ -257,17 +264,16 @@ module HTTPAuth
257
264
  # c.username = 'Mary'
258
265
  # c.username #=> 'Mary'
259
266
  def method_missing(m, *a)
260
- if ((m.to_s =~ /^(.*)=$/) == 0) and @h.keys.include?($1.intern)
261
- @h[$1.intern] = a[0]
267
+ if ((m.to_s =~ /^(.*)=$/) == 0) && @h.keys.include?(Regexp.last_match[1].intern)
268
+ @h[Regexp.last_match[1].intern] = a[0]
262
269
  elsif @h.keys.include? m
263
270
  @h[m]
264
271
  else
265
- raise NameError.new("undefined method `#{m}' for #{self}")
272
+ fail(NameError, "undefined method `#{m}' for #{self}")
266
273
  end
267
274
  end
268
275
  end
269
-
270
-
276
+
271
277
  # The Credentials class handlers the Authorize header. The Authorize header is sent by a client who wants to
272
278
  # let the server know he has the credentials needed to access a resource.
273
279
  #
@@ -275,34 +281,34 @@ module HTTPAuth
275
281
  class Credentials < AbstractHeader
276
282
  # Holds an explanation why <tt>validate</tt> returned false.
277
283
  attr_reader :reason
278
-
284
+
279
285
  # Parses the information from an Authorize header and creates a new Credentials instance with the information.
280
286
  # The options hash allows you to specify additional information.
281
287
  #
282
288
  # * <tt>authorization</tt>: The contents of the Authorize header
283
289
  # See <tt>initialize</tt> for valid options.
284
- def self.from_header(authorization, options={})
290
+ def self.from_header(authorization, options = {})
285
291
  new Utils.decode_directives(authorization, :credentials), options
286
292
  end
287
-
293
+
288
294
  # Creates a new Credential instance based on a Challenge instance.
289
295
  #
290
296
  # * <tt>challenge</tt>: A Challenge instance
291
297
  # See <tt>initialize</tt> for valid options.
292
- def self.from_challenge(challenge, options={})
298
+ def self.from_challenge(challenge, options = {})
293
299
  credentials = new challenge.h
294
300
  credentials.update_from_challenge! options
295
301
  credentials
296
302
  end
297
303
 
298
- def self.load(filename, options={})
304
+ def self.load(filename, options = {})
299
305
  h = nil
300
306
  File.open(filename, 'r') do |f|
301
307
  h = Marshal.load f
302
308
  end
303
309
  new h, options
304
310
  end
305
-
311
+
306
312
  # Create a new instance.
307
313
  #
308
314
  # * <tt>h</tt>: A Hash with directives, normally this is filled with the directives coming from a Challenge instance.
@@ -314,26 +320,26 @@ module HTTPAuth
314
320
  # * <tt>:uri</tt>: Mostly set by the client to send the uri
315
321
  # * <tt>:method</tt>: The HTTP Method used by the client to send the request, this should be an uppercase string
316
322
  # with the name of the verb.
317
- def initialize(h, options={})
323
+ def initialize(h, options = {})
318
324
  @h = h
319
325
  @h.merge! options
320
326
  session = Session.new h[:opaque], :tmpdir => options[:tmpdir]
321
327
  @s = session.load
322
328
  @reason = 'There has been no validation yet'
323
329
  end
324
-
330
+
325
331
  # Convenience method, basically an alias for <code>validate(options.merge(:password => password))</code>
326
- def validate_password(password, options={})
332
+ def validate_password(password, options = {})
327
333
  options[:password] = password
328
334
  validate(options)
329
335
  end
330
-
336
+
331
337
  # Convenience method, basically an alias for <code>validate(options.merge(:digest => digest))</code>
332
- def validate_digest(digest, options={})
338
+ def validate_digest(digest, options = {})
333
339
  options[:digest] = digest
334
340
  validate(options)
335
341
  end
336
-
342
+
337
343
  # Validates the credential information stored in the Credentials instance. Returns <tt>true</tt> or
338
344
  # <tt>false</tt>. You can read the ue
339
345
  #
@@ -346,9 +352,9 @@ module HTTPAuth
346
352
  # provided.
347
353
  def validate(options)
348
354
  ho = @h.merge(options)
349
- raise ArgumentError.new("You have to set the :request_body value if you want to use :qop => 'auth-int'") if @h[:qop] == 'auth-int' and ho[:request_body].nil?
350
- raise ArgumentError.new("Please specify the request method :method (ie. GET)") if ho[:method].nil?
351
-
355
+ fail(ArgumentError, "You have to set the :request_body value if you want to use :qop => 'auth-int'") if @h[:qop] == 'auth-int' && ho[:request_body].nil?
356
+ fail(ArgumentError, 'Please specify the request method :method (ie. GET)') if ho[:method].nil?
357
+
352
358
  calculated_response = Utils.calculate_digest(ho, @s, :request)
353
359
  if ho[:response] == calculated_response
354
360
  @reason = ''
@@ -358,13 +364,13 @@ module HTTPAuth
358
364
  end
359
365
  false
360
366
  end
361
-
367
+
362
368
  # Encodeds directives and returns a string that can be used in the Authorize header
363
369
  def to_header
364
370
  Utils.encode_directives Utils.filter_h_on(@h,
365
- [:username, :realm, :nonce, :uri, :response, :algorithm, :cnonce, :opaque, :qop, :nc]), :credentials
371
+ [:username, :realm, :nonce, :uri, :response, :algorithm, :cnonce, :opaque, :qop, :nc]), :credentials
366
372
  end
367
-
373
+
368
374
  # Updates @h from options, generally called after an instance was created with <tt>from_challenge</tt>.
369
375
  def update_from_challenge!(options)
370
376
  # TODO: integrity checks
@@ -375,17 +381,17 @@ module HTTPAuth
375
381
  @h[:method] = options[:method]
376
382
  @h[:request_body] = options[:request_body]
377
383
  unless @h[:qop].nil?
378
- # Determine the QOP
379
- if !options[:qop].nil? and @h[:qop].include?(options[:qop])
384
+ # Determine the QOP
385
+ if !options[:qop].nil? && @h[:qop].include?(options[:qop])
380
386
  @h[:qop] = options[:qop]
381
387
  elsif @h[:qop].include?(HTTPAuth::PREFERRED_QOP)
382
388
  @h[:qop] = HTTPAuth::PREFERRED_QOP
383
389
  else
384
- qop = @h[:qop].detect { |qop| HTTPAuth::SUPPORTED_QOPS.include? qop }
385
- unless qop.nil?
386
- @h[:qop] = qop
390
+ qop = @h[:qop].detect { |qop_field| HTTPAuth::SUPPORTED_QOPS.include? qop_field }
391
+ if qop.nil?
392
+ fail(UnsupportedError, "HTTPAuth doesn't support any of the proposed qop values: #{@h[:qop].inspect}")
387
393
  else
388
- raise UnsupportedError.new("HTTPAuth doesn't support any of the proposed qop values: #{@h[:qop].inspect}")
394
+ @h[:qop] = qop
389
395
  end
390
396
  end
391
397
  @h[:cnonce] ||= Utils.create_nonce options[:salt]
@@ -399,25 +405,23 @@ module HTTPAuth
399
405
  Marshal.dump(Utils.filter_h_on(@h, [:username, :realm, :nonce, :algorithm, :cnonce, :opaque, :qop, :nc]), f)
400
406
  end
401
407
  end
402
-
403
408
  end
404
-
409
+
405
410
  # The Challenge class handlers the WWW-Authenticate header. The WWW-Authenticate header is sent by a server when
406
411
  # accessing a resource without credentials is prohibided. The header should always be sent together with a 401
407
412
  # status.
408
413
  #
409
414
  # See the Digest module for examples
410
415
  class Challenge < AbstractHeader
411
-
412
416
  # Parses the information from a WWW-Authenticate header and creates a new WWW-Authenticate instance with this
413
417
  # data.
414
418
  #
415
419
  # * <tt>challenge</tt>: The contents of a WWW-Authenticate header
416
420
  # See <tt>initialize</tt> for valid options.
417
- def self.from_header(challenge, options={})
421
+ def self.from_header(challenge, options = {})
418
422
  new Utils.decode_directives(challenge, :challenge), options
419
423
  end
420
-
424
+
421
425
  # Create a new instance.
422
426
  #
423
427
  # * <tt>h</tt>: A Hash with directives, normally this is filled with directives coming from a Challenge instance.
@@ -425,18 +429,18 @@ module HTTPAuth
425
429
  # * <tt>:realm</tt>: The name of the realm the client should authenticate for. The RFC suggests to use a string
426
430
  # like 'admin@yourhost.domain.com'. Be sure to use a reasonably long string to avoid brute force attacks.
427
431
  # * <tt>:qop</tt>: A list with supported qop values. For example: <code>['auth-int']</code>. This will default
428
- # to <code>['auth']</code>. Although this implementation supports both auth and auth-int, most
432
+ # to <code>['auth']</code>. Although this implementation supports both auth and auth-int, most
429
433
  # implementations don't. Some implementations get confused when they receive anything but 'auth'. For
430
434
  # maximum compatibility you should leave this setting alone.
431
435
  # * <tt>:algorithm</tt>: The preferred algorithm for calculating the digest. For
432
436
  # example: <code>'MD5-sess'</code>. This will default to <code>'MD5'</code>. For
433
437
  # maximum compatibility you should leave this setting alone.
434
438
  #
435
- def initialize(h, options={})
439
+ def initialize(h, options = {})
436
440
  @h = h
437
441
  @h.merge! options
438
442
  end
439
-
443
+
440
444
  # Encodes directives and returns a string that can be used as the WWW-Authenticate header
441
445
  def to_header
442
446
  @h[:nonce] ||= Utils.create_nonce @h[:salt]
@@ -444,36 +448,35 @@ module HTTPAuth
444
448
  @h[:algorithm] ||= HTTPAuth::PREFERRED_ALGORITHM
445
449
  @h[:qop] ||= [HTTPAuth::PREFERRED_QOP]
446
450
  Utils.encode_directives Utils.filter_h_on(@h,
447
- [:realm, :domain, :nonce, :opaque, :stale, :algorithm, :qop]), :challenge
451
+ [:realm, :domain, :nonce, :opaque, :stale, :algorithm, :qop]), :challenge
448
452
  end
449
453
  end
450
-
454
+
451
455
  # The AuthenticationInfo class handles the Authentication-Info header. Sending Authentication-Info headers will
452
- # allow the client to check the integrity of the response, but it isn't compulsory and will get in the way of
456
+ # allow the client to check the integrity of the response, but it isn't compulsory and will get in the way of
453
457
  # pipelined retrieval of resources.
454
458
  #
455
459
  # See the Digest module for examples
456
460
  class AuthenticationInfo < AbstractHeader
457
-
458
461
  # Parses the information from a Authentication-Info header and creates a new AuthenticationInfo instance with
459
462
  # this data.
460
463
  #
461
464
  # * <tt>auth_info</tt>: The contents of the Authentication-Info header
462
465
  # See <tt>initialize</tt> for valid options.
463
- def self.from_header(auth_info, options={})
466
+ def self.from_header(auth_info, options = {})
464
467
  new Utils.decode_directives(auth_info, :auth), options
465
468
  end
466
-
469
+
467
470
  # Creates a new AuthenticationInfo instance based on the information from Credentials instance.
468
471
  #
469
472
  # * <tt>credentials</tt>: A Credentials instance
470
473
  # See <tt>initialize</tt> for valid options.
471
- def self.from_credentials(credentials, options={})
474
+ def self.from_credentials(credentials, options = {})
472
475
  auth_info = new credentials.h
473
476
  auth_info.update_from_credentials! options
474
477
  auth_info
475
478
  end
476
-
479
+
477
480
  # Create a new instance.
478
481
  #
479
482
  # * <tt>h</tt>: A Hash with directives, normally this is filled with the directives coming from a
@@ -482,17 +485,17 @@ module HTTPAuth
482
485
  # * <tt>:digest</tt>: The digest for the specified username and realm.
483
486
  # * <tt>:response_body</tt> The body of the response that's going to be sent to the client. This is a
484
487
  # compulsory option if the qop directive is 'auth-int'.
485
- def initialize(h, options={})
488
+ def initialize(h, options = {})
486
489
  @h = h
487
490
  @h.merge! options
488
491
  end
489
-
492
+
490
493
  # Encodes directives and returns a string that can be used as the AuthorizationInfo header
491
494
  def to_header
492
495
  Utils.encode_directives Utils.filter_h_on(@h,
493
- [:nextnonce, :qop, :rspauth, :cnonce, :nc]), :auth
496
+ [:nextnonce, :qop, :rspauth, :cnonce, :nc]), :auth
494
497
  end
495
-
498
+
496
499
  # Updates @h from options, generally called after an instance was created with <tt>from_credentials</tt>.
497
500
  def update_from_credentials!(options)
498
501
  # TODO: update @h after nonce invalidation
@@ -512,16 +515,14 @@ module HTTPAuth
512
515
  # * <tt>:nonce</tt>:nonce
513
516
  def validate(options)
514
517
  ho = @h.merge(options)
515
- return @h[:rspauth] == Utils.calculate_digest(ho, @s, :response)
518
+ @h[:rspauth] == Utils.calculate_digest(ho, @s, :response)
516
519
  end
517
-
518
520
  end
519
-
521
+
520
522
  # Conversion for a number of internal data structures to and from directives in the headers. Implementations
521
523
  # shouldn't have to call any methods on Conversions.
522
524
  class Conversions
523
525
  class << self
524
-
525
526
  # Adds quotes around the string
526
527
  def quote_string(str)
527
528
  "\"#{str.gsub(/\"/, '')}\""
@@ -529,7 +530,7 @@ module HTTPAuth
529
530
 
530
531
  # Removes quotes from around a string
531
532
  def unquote_string(str)
532
- str =~ /^\"([^\"]*)\"$/ ? $1 : str
533
+ str =~ /^\"([^\"]*)\"$/ ? Regexp.last_match[1] : str
533
534
  end
534
535
 
535
536
  # Creates an int value from hex values
@@ -546,7 +547,7 @@ module HTTPAuth
546
547
  def str_to_bool(str)
547
548
  str == 'true'
548
549
  end
549
-
550
+
550
551
  # Creates a string value from a boolean => 'true' or 'false'
551
552
  def bool_to_str(bool)
552
553
  bool ? 'true' : 'false'
@@ -578,41 +579,39 @@ module HTTPAuth
578
579
  class Session
579
580
  attr_accessor :opaque
580
581
  attr_accessor :options
581
-
582
+
582
583
  # Initializes the new Session object.
583
584
  #
584
585
  # * <tt>opaque</tt> - A string to identify the session. This would normally be the <tt>opaque</tt> sent by the
585
586
  # client, but it could also be an identifier sent through a different mechanism.
586
587
  # * <tt>options</tt> - Additional options
587
588
  # * <tt>:tmpdir</tt> A tempory directory for storing the session data. Dir::tmpdir is the default.
588
- def initialize(opaque, options={})
589
+ def initialize(opaque, options = {})
589
590
  self.opaque = opaque
590
591
  self.options = options
591
592
  end
592
-
593
+
593
594
  # Associates the new data to the session and removes the old
594
595
  def save(data)
595
596
  File.open(filename, 'w') do |f|
596
597
  f.write Marshal.dump(data)
597
598
  end
598
599
  end
599
-
600
+
600
601
  # Returns the data from this session
601
602
  def load
602
- begin
603
- File.open(filename, 'r') do |f|
604
- Marshal.load f.read
605
- end
606
- rescue Errno::ENOENT
607
- {}
603
+ File.open(filename, 'r') do |f|
604
+ Marshal.load f.read
608
605
  end
606
+ rescue Errno::ENOENT
607
+ {}
609
608
  end
610
-
611
- protected
612
-
609
+
610
+ protected
611
+
613
612
  # The filename from which the session will be saved and read from
614
613
  def filename
615
- "#{options[:tmpdir] || Dir::tmpdir}/ruby_digest_cache.#{self.opaque}"
614
+ "#{options[:tmpdir] || Dir.tmpdir}/ruby_digest_cache.#{opaque}"
616
615
  end
617
616
  end
618
617
  end
@@ -5,4 +5,4 @@ module HTTPAuth
5
5
  class UnsupportedError < ArgumentError; end
6
6
  # Raise when validation on the request failed, most of the times this means that someone is trying to do replay attacks.
7
7
  class ValidationError < ArgumentError; end
8
- end
8
+ end
metadata CHANGED
@@ -1,72 +1,68 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: httpauth
3
- version: !ruby/object:Gem::Version
4
- hash: 23
5
- prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 0
10
- version: 0.2.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
11
5
  platform: ruby
12
- authors:
6
+ authors:
13
7
  - Manfred Stienstra
14
8
  autorequire:
15
9
  bindir: bin
16
10
  cert_chain: []
17
-
18
- date: 2012-09-25 00:00:00 Z
19
- dependencies: []
20
-
11
+ date: 2014-01-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
21
27
  description: Library for the HTTP Authentication protocol (RFC 2617)
22
28
  email: manfred@fngtpspec.com
23
29
  executables: []
24
-
25
30
  extensions: []
26
-
27
- extra_rdoc_files:
31
+ extra_rdoc_files:
28
32
  - README.md
29
33
  - LICENSE
30
- files:
31
- - README.md
34
+ files:
32
35
  - LICENSE
36
+ - README.md
37
+ - lib/httpauth.rb
33
38
  - lib/httpauth/basic.rb
34
39
  - lib/httpauth/constants.rb
35
40
  - lib/httpauth/digest.rb
36
41
  - lib/httpauth/exceptions.rb
37
- - lib/httpauth.rb
38
42
  homepage: https://github.com/Manfred/HTTPauth
39
- licenses: []
40
-
43
+ licenses:
44
+ - MIT
45
+ metadata: {}
41
46
  post_install_message:
42
- rdoc_options:
43
- - --charset=utf-8
44
- require_paths:
47
+ rdoc_options:
48
+ - "--charset=utf-8"
49
+ require_paths:
45
50
  - lib
46
- required_ruby_version: !ruby/object:Gem::Requirement
47
- none: false
48
- requirements:
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
49
53
  - - ">="
50
- - !ruby/object:Gem::Version
51
- hash: 3
52
- segments:
53
- - 0
54
- version: "0"
55
- required_rubygems_version: !ruby/object:Gem::Requirement
56
- none: false
57
- requirements:
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
58
  - - ">="
59
- - !ruby/object:Gem::Version
60
- hash: 3
61
- segments:
62
- - 0
63
- version: "0"
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
64
61
  requirements: []
65
-
66
62
  rubyforge_project:
67
- rubygems_version: 1.8.18
63
+ rubygems_version: 2.2.0
68
64
  signing_key:
69
- specification_version: 3
70
- summary: HTTPauth is a library supporting the full HTTP Authentication protocol as specified in RFC 2617; both Digest Authentication and Basic Authentication.
65
+ specification_version: 4
66
+ summary: HTTPauth is a library supporting the full HTTP Authentication protocol as
67
+ specified in RFC 2617; both Digest Authentication and Basic Authentication.
71
68
  test_files: []
72
-