httpauth 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-