httpauth 0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +18 -14
- data/{README → README.md} +11 -10
- data/lib/httpauth/basic.rb +1 -1
- data/lib/httpauth/constants.rb +1 -1
- data/lib/httpauth/digest.rb +55 -19
- data/lib/httpauth/exceptions.rb +2 -0
- metadata +55 -49
- data/Rakefile +0 -76
- data/examples/client_digest_secure +0 -132
- data/examples/server_digest_secure +0 -47
data/LICENSE
CHANGED
@@ -1,16 +1,20 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
Copyright (C) 2006-2011
|
2
|
+
Manfred Stienstra <manfred@fngtps.com>, Fingertips,
|
3
|
+
Tim Olsen <tim@brooklynpenguin.com>
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
6
|
+
in the Software without restriction, including without limitation the rights
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
9
|
+
furnished to do so, subject to the following conditions:
|
3
10
|
|
4
|
-
|
5
|
-
|
6
|
-
the Free Software Foundation; either version 2 of the License, or
|
7
|
-
(at your option) any later version.
|
11
|
+
The above copyright notice and this permission notice shall be included in
|
12
|
+
all copies or substantial portions of the Software.
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
THE SOFTWARE.
|
data/{README → README.md}
RENAMED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
HTTPauth
|
2
|
+
======
|
2
3
|
|
3
4
|
HTTPauth is a library supporting the full HTTP Authentication protocol as specified in RFC 2617; both Digest Authentication and Basic Authentication. We aim to make HTTPAuth as compliant as possible.
|
4
5
|
|
@@ -6,34 +7,34 @@ HTTPAuth is built to be completely agnostic of the HTTP implementation. If you h
|
|
6
7
|
|
7
8
|
This project is currently under development, don't use it in mission critical applications.
|
8
9
|
|
9
|
-
|
10
|
+
## Getting started
|
10
11
|
|
11
12
|
If you want to implement authentication for your application you should probably start by looking at the various examples. In the examples directory is an implementation of an HTTP client and server, you can use these to test your implementation. The examples are basic implementations of the protocol.
|
12
13
|
|
13
|
-
|
14
|
+
## Limitations
|
14
15
|
|
15
16
|
Currently the library doesn't check for consistency of the directives in the various headers, this means that implementations using this library can be vulnerable to request replay attacks. This will obviously be addressed before the final release.
|
16
17
|
|
17
|
-
|
18
|
+
## Plugins
|
18
19
|
|
19
|
-
|
20
|
+
### Ruby on Rails
|
20
21
|
|
21
22
|
A plugin for Ruby on Rails can be found here:
|
22
23
|
|
23
24
|
https://fngtps.com/svn/rails-plugins/trunk/digest_authentication
|
24
25
|
|
25
|
-
|
26
|
+
## Known client implementation issues
|
26
27
|
|
27
|
-
|
28
|
+
### Safari
|
28
29
|
|
29
|
-
Safari doesn't understand and parse the algorithm and qop directives correctly. For instance: it sends qop=auth as qop="auth" and when multiple qop values are suggested by the server, no authentication is triggered.
|
30
|
+
Safari doesn't understand and parse the algorithm and qop directives correctly. For instance: it sends qop=auth as qop="auth" and when multiple qop values are suggested by the server, no authentication is triggered.
|
30
31
|
|
31
|
-
|
32
|
+
### Internet Explorer
|
32
33
|
|
33
34
|
The qop and algorithm bug quoting bugs are also present in IE.
|
34
35
|
|
35
36
|
IE doesn't use the full URI for digest calculation, it chops off the query parameters. So a request on /script?q=a will response with uri='/script'.
|
36
37
|
|
37
|
-
|
38
|
+
## Known server implementation issues
|
38
39
|
|
39
40
|
Apache 2.0 sends Authorization-Info headers without a nextnonce directive.
|
data/lib/httpauth/basic.rb
CHANGED
@@ -106,7 +106,7 @@ module HTTPAuth
|
|
106
106
|
# RewriteRule ^admin/ - [E=X-HTTP-AUTHORIZATION:%{HTTP:Authorization}]
|
107
107
|
def get_credentials(env)
|
108
108
|
d = HTTPAuth::CREDENTIAL_HEADERS.inject(false) { |d,h| env[h] || d }
|
109
|
-
return unpack_authorization(d)
|
109
|
+
return unpack_authorization(d) unless !d or d.nil? or d.empty?
|
110
110
|
[nil, nil]
|
111
111
|
end
|
112
112
|
end
|
data/lib/httpauth/constants.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# For more information see RFC 2617 (http://www.ietf.org/rfc/rfc2617.txt)
|
5
5
|
module HTTPAuth
|
6
|
-
VERSION = '0.
|
6
|
+
VERSION = '0.2'
|
7
7
|
|
8
8
|
CREDENTIAL_HEADERS = %w{REDIRECT_X_HTTP_AUTHORIZATION X-HTTP-AUTHORIZATION X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION}
|
9
9
|
SUPPORTED_SCHEMES = { :basic => 'Basic', :digest => 'Digest' }
|
data/lib/httpauth/digest.rb
CHANGED
@@ -54,12 +54,11 @@ module HTTPAuth
|
|
54
54
|
# * <tt>variant</tt>: Specifies whether the directives are for an Authorize header (:credentials),
|
55
55
|
# for a WWW-Authenticate header (:challenge) or for a Authentication-Info header (:auth_info).
|
56
56
|
def encode_directives(h, variant)
|
57
|
-
encode = {:domain => :
|
58
|
-
:nextnonce => :int_to_hex}
|
57
|
+
encode = {:domain => :list_to_space_quoted_string, :algorithm => false, :stale => :bool_to_str, :nc => :int_to_hex}
|
59
58
|
if [:credentials, :auth].include? variant
|
60
59
|
encode.merge! :qop => false
|
61
60
|
elsif variant == :challenge
|
62
|
-
encode.merge! :qop => :
|
61
|
+
encode.merge! :qop => :list_to_comma_quoted_string
|
63
62
|
else
|
64
63
|
raise ArgumentError.new("#{variant} is not a valid value for `variant' use :auth, :credentials or :challenge")
|
65
64
|
end
|
@@ -89,12 +88,11 @@ module HTTPAuth
|
|
89
88
|
# for a WWW-Authenticate header (:challenge) or for a Authentication-Info header (:auth_info).
|
90
89
|
def decode_directives(directives, variant)
|
91
90
|
raise HTTPAuth::UnwellformedHeader.new("Can't decode directives which are nil") if directives.nil?
|
92
|
-
decode = {:domain => :
|
93
|
-
:nextnonce => :hex_to_int}
|
91
|
+
decode = {:domain => :space_quoted_string_to_list, :algorithm => false, :stale => :str_to_bool, :nc => :hex_to_int}
|
94
92
|
if [:credentials, :auth].include? variant
|
95
93
|
decode.merge! :qop => false
|
96
94
|
elsif variant == :challenge
|
97
|
-
decode.merge! :qop => :
|
95
|
+
decode.merge! :qop => :comma_quoted_string_to_list
|
98
96
|
else
|
99
97
|
raise ArgumentError.new("#{variant} is not a valid value for `variant' use :auth, :credentials or :challenge")
|
100
98
|
end
|
@@ -230,7 +228,7 @@ module HTTPAuth
|
|
230
228
|
)
|
231
229
|
).gsub("\n", '')[0..-3]
|
232
230
|
end
|
233
|
-
|
231
|
+
|
234
232
|
# Create a 32 character long opaque string with a 'random' value
|
235
233
|
def create_opaque
|
236
234
|
s = []; 16.times { s << rand(127).chr }
|
@@ -275,8 +273,10 @@ module HTTPAuth
|
|
275
273
|
#
|
276
274
|
# See the Digest module for examples
|
277
275
|
class Credentials < AbstractHeader
|
276
|
+
# Holds an explanation why <tt>validate</tt> returned false.
|
277
|
+
attr_reader :reason
|
278
278
|
|
279
|
-
# Parses the information from
|
279
|
+
# Parses the information from an Authorize header and creates a new Credentials instance with the information.
|
280
280
|
# The options hash allows you to specify additional information.
|
281
281
|
#
|
282
282
|
# * <tt>authorization</tt>: The contents of the Authorize header
|
@@ -294,6 +294,14 @@ module HTTPAuth
|
|
294
294
|
credentials.update_from_challenge! options
|
295
295
|
credentials
|
296
296
|
end
|
297
|
+
|
298
|
+
def self.load(filename, options={})
|
299
|
+
h = nil
|
300
|
+
File.open(filename, 'r') do |f|
|
301
|
+
h = Marshal.load f
|
302
|
+
end
|
303
|
+
new h, options
|
304
|
+
end
|
297
305
|
|
298
306
|
# Create a new instance.
|
299
307
|
#
|
@@ -334,7 +342,7 @@ module HTTPAuth
|
|
334
342
|
# * <tt>:method</tt>: The HTTP Verb in uppercase, ie. GET or POST.
|
335
343
|
# * <tt>:password</tt>: The password for the sent username and realm, either a password or digest should be
|
336
344
|
# provided.
|
337
|
-
# * <tt>:digest</tt>: The digest for the specified username and realm, either a digest or password should
|
345
|
+
# * <tt>:digest</tt>: The digest for the specified username and realm, either a digest or password should be
|
338
346
|
# provided.
|
339
347
|
def validate(options)
|
340
348
|
ho = @h.merge(options)
|
@@ -351,13 +359,8 @@ module HTTPAuth
|
|
351
359
|
false
|
352
360
|
end
|
353
361
|
|
354
|
-
# Returns a string with the reason <tt>validate</tt> returned false.
|
355
|
-
def reason
|
356
|
-
@reason
|
357
|
-
end
|
358
|
-
|
359
362
|
# Encodeds directives and returns a string that can be used in the Authorize header
|
360
|
-
def to_header
|
363
|
+
def to_header
|
361
364
|
Utils.encode_directives Utils.filter_h_on(@h,
|
362
365
|
[:username, :realm, :nonce, :uri, :response, :algorithm, :cnonce, :opaque, :qop, :nc]), :credentials
|
363
366
|
end
|
@@ -390,6 +393,13 @@ module HTTPAuth
|
|
390
393
|
end
|
391
394
|
@h[:response] = Utils.calculate_digest(@h, @s, :request)
|
392
395
|
end
|
396
|
+
|
397
|
+
def dump_sans_creds(filename)
|
398
|
+
File.open(filename, 'w') do |f|
|
399
|
+
Marshal.dump(Utils.filter_h_on(@h, [:username, :realm, :nonce, :algorithm, :cnonce, :opaque, :qop, :nc]), f)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
393
403
|
end
|
394
404
|
|
395
405
|
# The Challenge class handlers the WWW-Authenticate header. The WWW-Authenticate header is sent by a server when
|
@@ -469,6 +479,7 @@ module HTTPAuth
|
|
469
479
|
# * <tt>h</tt>: A Hash with directives, normally this is filled with the directives coming from a
|
470
480
|
# Credentials instance.
|
471
481
|
# * <tt>options</tt>: Used to set or override data from the Authentication-Info header
|
482
|
+
# * <tt>:digest</tt>: The digest for the specified username and realm.
|
472
483
|
# * <tt>:response_body</tt> The body of the response that's going to be sent to the client. This is a
|
473
484
|
# compulsory option if the qop directive is 'auth-int'.
|
474
485
|
def initialize(h, options={})
|
@@ -485,9 +496,25 @@ module HTTPAuth
|
|
485
496
|
# Updates @h from options, generally called after an instance was created with <tt>from_credentials</tt>.
|
486
497
|
def update_from_credentials!(options)
|
487
498
|
# TODO: update @h after nonce invalidation
|
499
|
+
[:digest, :username, :realm, :password].each do |k|
|
500
|
+
@h[k] = options[k] if options.include? k
|
501
|
+
end
|
488
502
|
@h[:response_body] = options[:response_body]
|
489
|
-
@h[:nextnonce] = @h[:
|
503
|
+
@h[:nextnonce] = Utils.create_nonce @h[:salt]
|
504
|
+
@h[:rspauth] = Utils.calculate_digest(@h, nil, :response)
|
490
505
|
end
|
506
|
+
|
507
|
+
# Validates rspauth. Returns <tt>true</tt> or <tt>false</tt>
|
508
|
+
#
|
509
|
+
# * <tt>options</tt>: The extra options needed to validate rspauth.
|
510
|
+
# * <tt>:digest</tt>: The H(a1) digest
|
511
|
+
# * <tt>:uri</tt>: request uri
|
512
|
+
# * <tt>:nonce</tt>:nonce
|
513
|
+
def validate(options)
|
514
|
+
ho = @h.merge(options)
|
515
|
+
return @h[:rspauth] == Utils.calculate_digest(ho, @s, :response)
|
516
|
+
end
|
517
|
+
|
491
518
|
end
|
492
519
|
|
493
520
|
# Conversion for a number of internal data structures to and from directives in the headers. Implementations
|
@@ -497,7 +524,7 @@ module HTTPAuth
|
|
497
524
|
|
498
525
|
# Adds quotes around the string
|
499
526
|
def quote_string(str)
|
500
|
-
"\"#{str.gsub(
|
527
|
+
"\"#{str.gsub(/\"/, '')}\""
|
501
528
|
end
|
502
529
|
|
503
530
|
# Removes quotes from around a string
|
@@ -526,14 +553,23 @@ module HTTPAuth
|
|
526
553
|
end
|
527
554
|
|
528
555
|
# Creates a quoted string with space separated items from a list
|
529
|
-
def
|
556
|
+
def list_to_space_quoted_string(list)
|
530
557
|
quote_string list.join(' ')
|
531
558
|
end
|
532
559
|
|
533
560
|
# Creates a list from a quoted space separated string of items
|
534
|
-
def
|
561
|
+
def space_quoted_string_to_list(string)
|
535
562
|
unquote_string(string).split ' '
|
536
563
|
end
|
564
|
+
|
565
|
+
# Creates a quoted string with comma separated items from a list
|
566
|
+
def list_to_comma_quoted_string(list)
|
567
|
+
quote_string list.join(',')
|
568
|
+
end
|
569
|
+
# Create a list from a quoted comma separated string of items
|
570
|
+
def comma_quoted_string_to_list(string)
|
571
|
+
unquote_string(string).split ','
|
572
|
+
end
|
537
573
|
end
|
538
574
|
end
|
539
575
|
|
data/lib/httpauth/exceptions.rb
CHANGED
@@ -3,4 +3,6 @@ module HTTPAuth
|
|
3
3
|
class UnwellformedHeader < ArgumentError; end
|
4
4
|
# Raised when the library finds data that is not strictly forbidden but doesn't know how to handle.
|
5
5
|
class UnsupportedError < ArgumentError; end
|
6
|
+
# Raise when validation on the request failed, most of the times this means that someone is trying to do replay attacks.
|
7
|
+
class ValidationError < ArgumentError; end
|
6
8
|
end
|
metadata
CHANGED
@@ -1,66 +1,72 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.8.10
|
3
|
-
specification_version: 1
|
4
2
|
name: httpauth
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
-
|
11
|
-
|
12
|
-
|
13
|
-
rubyforge_project:
|
14
|
-
description: HTTPauth is a library supporting the full HTTP Authentication protocol as specified in RFC 2617; both Digest Authentication and Basic Authentication.
|
15
|
-
autorequire:
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 1.8.0
|
24
|
-
version:
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
25
11
|
platform: ruby
|
26
12
|
authors:
|
27
13
|
- Manfred Stienstra
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-09-25 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Library for the HTTP Authentication protocol (RFC 2617)
|
22
|
+
email: manfred@fngtpspec.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.md
|
29
|
+
- LICENSE
|
28
30
|
files:
|
29
|
-
- README
|
31
|
+
- README.md
|
30
32
|
- LICENSE
|
31
|
-
- Rakefile
|
32
|
-
- lib/httpauth
|
33
|
-
- lib/httpauth.rb
|
34
33
|
- lib/httpauth/basic.rb
|
35
34
|
- lib/httpauth/constants.rb
|
36
35
|
- lib/httpauth/digest.rb
|
37
36
|
- lib/httpauth/exceptions.rb
|
38
|
-
-
|
39
|
-
|
40
|
-
|
37
|
+
- lib/httpauth.rb
|
38
|
+
homepage: https://github.com/Manfred/HTTPauth
|
39
|
+
licenses: []
|
41
40
|
|
41
|
+
post_install_message:
|
42
42
|
rdoc_options:
|
43
|
-
- --
|
44
|
-
|
45
|
-
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
-
|
50
|
-
-
|
51
|
-
|
52
|
-
|
53
|
-
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
43
|
+
- --charset=utf-8
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
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:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
63
64
|
requirements: []
|
64
65
|
|
65
|
-
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.8.18
|
68
|
+
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.
|
71
|
+
test_files: []
|
66
72
|
|
data/Rakefile
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
require 'rake'
|
2
|
-
require 'rake/clean'
|
3
|
-
require 'rake/testtask'
|
4
|
-
require 'rake/gempackagetask'
|
5
|
-
require 'rake/rdoctask'
|
6
|
-
|
7
|
-
NAME = 'httpauth'
|
8
|
-
VERSIE = '0.1'
|
9
|
-
RDOC_OPTS = ['--quiet', '--title', "HTTPAuth - A Ruby library for creating, parsing and validating HTTP authentication headers",
|
10
|
-
"--opname", "index.html",
|
11
|
-
"--line-numbers",
|
12
|
-
"--main", "README",
|
13
|
-
"--charset", "utf-8",
|
14
|
-
"--inline-source"]
|
15
|
-
CLEAN.include ['pkg', 'doc', '*.gem']
|
16
|
-
|
17
|
-
desc 'Default: run tests'
|
18
|
-
task :default => [:test]
|
19
|
-
task :package => [:clean]
|
20
|
-
|
21
|
-
desc 'Run tests'
|
22
|
-
Rake::TestTask.new(:test) do |t|
|
23
|
-
t.pattern = 'test/**/*_test.rb'
|
24
|
-
t.verbose = true
|
25
|
-
t.warning = true
|
26
|
-
end
|
27
|
-
|
28
|
-
desc 'Create documentation'
|
29
|
-
Rake::RDocTask.new("doc") do |rdoc|
|
30
|
-
rdoc.rdoc_dir = 'doc'
|
31
|
-
rdoc.options += RDOC_OPTS
|
32
|
-
rdoc.main = "README"
|
33
|
-
rdoc.rdoc_files.include('README')
|
34
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
35
|
-
end
|
36
|
-
|
37
|
-
desc 'Upload rdoc documentation to Rubyforge'
|
38
|
-
task :upload_doc => :doc do
|
39
|
-
`scp -r #{File.dirname(__FILE__)}/doc/* mst@rubyforge.org:/var/www/gforge-projects/httpauth/`
|
40
|
-
end
|
41
|
-
|
42
|
-
spec =
|
43
|
-
Gem::Specification.new do |s|
|
44
|
-
s.name = NAME
|
45
|
-
s.version = VERSIE
|
46
|
-
s.platform = Gem::Platform::RUBY
|
47
|
-
s.has_rdoc = true
|
48
|
-
s.extra_rdoc_files = ["README", "LICENSE"]
|
49
|
-
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|test)\/']
|
50
|
-
s.summary = "Library for the HTTP Authentication protocol (RFC 2617)"
|
51
|
-
s.description = "HTTPauth is a library supporting the full HTTP Authentication protocol as specified in RFC 2617; both Digest Authentication and Basic Authentication."
|
52
|
-
s.author = "Manfred Stienstra"
|
53
|
-
s.email = 'manfred@fngtps.com'
|
54
|
-
s.homepage = 'http://httpauth.rubyforge.org'
|
55
|
-
s.required_ruby_version = '>= 1.8.0'
|
56
|
-
|
57
|
-
s.files = %w(README LICENSE Rakefile) +
|
58
|
-
Dir.glob("lib/**/*") +
|
59
|
-
Dir.glob("examples/**/*")
|
60
|
-
|
61
|
-
s.require_path = "lib"
|
62
|
-
end
|
63
|
-
|
64
|
-
Rake::GemPackageTask.new(spec) do |p|
|
65
|
-
p.need_tar = true
|
66
|
-
p.gem_spec = spec
|
67
|
-
end
|
68
|
-
|
69
|
-
task :install do
|
70
|
-
sh %{rake package}
|
71
|
-
sh %{sudo gem install pkg/#{NAME}-#{VERSIE}}
|
72
|
-
end
|
73
|
-
|
74
|
-
task :uninstall => [:clean] do
|
75
|
-
sh %{sudo gem uninstall #{NAME}}
|
76
|
-
end
|
@@ -1,132 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
-
|
5
|
-
require 'rubygems' rescue LoadError
|
6
|
-
require 'uri'
|
7
|
-
require 'rfuzz/client'
|
8
|
-
require 'httpauth/digest'
|
9
|
-
|
10
|
-
SALT = 'My very very secret salt'
|
11
|
-
|
12
|
-
class AuthenticationCache
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
@uri = nil
|
16
|
-
@credentials = nil
|
17
|
-
end
|
18
|
-
|
19
|
-
def set_credentials_for(uri, credentials)
|
20
|
-
@uri = get_new_uri(@uri, uri)
|
21
|
-
@credentials = credentials
|
22
|
-
end
|
23
|
-
|
24
|
-
def get_credentials
|
25
|
-
@credentials
|
26
|
-
end
|
27
|
-
|
28
|
-
def update_usage_for(uri, nextnonce=nil)
|
29
|
-
if nextnonce
|
30
|
-
@credentials.nc = nextnonce
|
31
|
-
else
|
32
|
-
@credentials.nc += 1
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
protected
|
37
|
-
|
38
|
-
# Is uri1 more general than uri2
|
39
|
-
def more_general_uri?(uri1, uri2)
|
40
|
-
ua1 = uri1.nil? ? [] : uri1.split('/')
|
41
|
-
ua2 = uri2.nil? ? [] : uri2.split('/')
|
42
|
-
ua1.each_with_index do |p, i|
|
43
|
-
return false unless ua2[i] == p
|
44
|
-
end
|
45
|
-
true
|
46
|
-
end
|
47
|
-
|
48
|
-
def get_new_uri(uri1, uri2)
|
49
|
-
if more_general_uri?(uri1, uri2)
|
50
|
-
uri1
|
51
|
-
else
|
52
|
-
uri2
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class AuthenticatedClient
|
58
|
-
include HTTPAuth::Digest
|
59
|
-
|
60
|
-
def initialize(host, port)
|
61
|
-
@client = RFuzz::HttpClient.new host, port
|
62
|
-
@cache = AuthenticationCache.new
|
63
|
-
@username = nil
|
64
|
-
@password = nil
|
65
|
-
end
|
66
|
-
|
67
|
-
def get_credentials_from_user
|
68
|
-
if @username.nil?
|
69
|
-
print 'Username: '
|
70
|
-
@username = $stdin.gets.strip
|
71
|
-
end
|
72
|
-
if @password.nil?
|
73
|
-
print 'Password: '
|
74
|
-
@password = $stdin.gets.strip
|
75
|
-
end
|
76
|
-
[@username, @password]
|
77
|
-
end
|
78
|
-
|
79
|
-
# Get a resource from the server
|
80
|
-
def get(resource)
|
81
|
-
uri = URI.parse resource
|
82
|
-
|
83
|
-
# If credentials were stored, use them. Otherwise do a normal get
|
84
|
-
credentials = @cache.get_credentials
|
85
|
-
unless credentials.nil?
|
86
|
-
puts "sending credentials: #{credentials.to_header}"
|
87
|
-
response = @client.get resource, :head => {"Authorization" => credentials.to_header}
|
88
|
-
else
|
89
|
-
response = @client.get resource
|
90
|
-
end
|
91
|
-
# If response was 401, retry with authentication
|
92
|
-
if response.http_status == '401' and !response['WWW_AUTHENTICATE'].nil?
|
93
|
-
puts "got challenge: #{response['WWW_AUTHENTICATE']}"
|
94
|
-
challenge = Challenge.from_header(response['WWW_AUTHENTICATE'])
|
95
|
-
(stale = challenge.stale) rescue NoMethodError
|
96
|
-
unless stale
|
97
|
-
username, password = get_credentials_from_user
|
98
|
-
else
|
99
|
-
username = credentials.username
|
100
|
-
password = credentials.password
|
101
|
-
end
|
102
|
-
credentials = Credentials.from_challenge(challenge,
|
103
|
-
{:uri => resource, :username => username, :password => password, :method => 'GET'}
|
104
|
-
)
|
105
|
-
puts "sending credentials: #{credentials.to_header}"
|
106
|
-
@cache.set_credentials_for uri.path, credentials
|
107
|
-
response = @client.get resource, :head => {"Authorization" => credentials.to_header}
|
108
|
-
end
|
109
|
-
# If the server sends authentication info use the information for the next request
|
110
|
-
if response['AUTHENTICATION_INFO']
|
111
|
-
puts "got authentication-info: #{response['AUTHENTICATION_INFO']}"
|
112
|
-
auth_info = AuthenticationInfo.from_header(response['AUTHENTICATION_INFO'])
|
113
|
-
@cache.update_usage_for uri.path, auth_info.h[:nextnonce]
|
114
|
-
else
|
115
|
-
@cache.update_usage_for uri.path
|
116
|
-
end
|
117
|
-
response
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
if $0 == __FILE__
|
122
|
-
unless ARGV.length == 2
|
123
|
-
puts <<-EOT
|
124
|
-
Usage: client_digest_secure get <url>
|
125
|
-
EOT
|
126
|
-
exit 0
|
127
|
-
end
|
128
|
-
uri = URI.parse ARGV[1]
|
129
|
-
client = AuthenticatedClient.new uri.host, uri.port
|
130
|
-
response = client.send ARGV[0].intern, uri.query ? "#{uri.path}&#{uri.query}" : uri.path
|
131
|
-
puts response.http_body
|
132
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
-
|
5
|
-
require 'webrick'
|
6
|
-
require 'httpauth/digest'
|
7
|
-
require 'yaml'
|
8
|
-
|
9
|
-
include WEBrick
|
10
|
-
|
11
|
-
s = HTTPServer.new :Port => 2000, :AccessLog => [[File.open('/dev/null', 'w'), AccessLog::COMMON_LOG_FORMAT],
|
12
|
-
[File.open('/dev/null', 'w'), AccessLog::REFERER_LOG_FORMAT]]
|
13
|
-
|
14
|
-
class AuthenticationServlet < HTTPServlet::AbstractServlet
|
15
|
-
include HTTPAuth::Digest
|
16
|
-
def do_GET(request, response)
|
17
|
-
puts '-'*79
|
18
|
-
puts "request: Authorization: " + (request['Authorization'] || '')
|
19
|
-
|
20
|
-
credentials = Credentials.from_header(request['Authorization']) unless request['Authorization'].nil?
|
21
|
-
if !credentials.nil? and credentials.validate :password => 'secret', :method => 'GET'
|
22
|
-
response.status = 200
|
23
|
-
auth_info = AuthenticationInfo.from_credentials credentials
|
24
|
-
response['Authentication-Info'] = auth_info.to_header
|
25
|
-
response['Content-Type'] = 'text/plain; charset=utf-8'
|
26
|
-
response.body = 'You are authorized'
|
27
|
-
puts "response: Authentication-Info: " + response['Authentication-Info']
|
28
|
-
else
|
29
|
-
if credentials
|
30
|
-
puts '[!] FAILED: ' + credentials.reason
|
31
|
-
else
|
32
|
-
puts '[!] FAILED: No credentials specified'
|
33
|
-
end
|
34
|
-
response.status = 401
|
35
|
-
challenge = Challenge.new :realm => 'admin@httpauth.example.com', :qop => ['auth']
|
36
|
-
response['WWW-Authenticate'] = challenge.to_header
|
37
|
-
response['Content-Type'] = 'text/plain; charset=utf-8'
|
38
|
-
response.body = 'You are not authorized'
|
39
|
-
puts "response: WWW-Authenticate: " + response['WWW-Authenticate']
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
puts "\n>>> Open http://localhost:2000/ and login with password 'secret', any username should work\n\n"
|
45
|
-
s.mount '/', AuthenticationServlet
|
46
|
-
trap('INT') { s.shutdown }
|
47
|
-
s.start
|