1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
#!/usr/bin/env ruby
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
require 'rubygems' rescue LoadError
require 'uri'
require 'rfuzz/client'
require 'httpauth/digest'
SALT = 'My very very secret salt'
class AuthenticationCache
def initialize
@uri = nil
@credentials = nil
end
def set_credentials_for(uri, credentials)
@uri = get_new_uri(@uri, uri)
@credentials = credentials
end
def get_credentials
@credentials
end
def update_usage_for(uri, reset_count_for_uri = false)
if reset_count_for_uri
@credentials.nc = 0
else
@credentials.nc += 1
end
end
protected
# Is uri1 more general than uri2
def more_general_uri?(uri1, uri2)
ua1 = uri1.nil? ? [] : uri1.split('/')
ua2 = uri2.nil? ? [] : uri2.split('/')
ua1.each_with_index do |p, i|
return false unless ua2[i] == p
end
true
end
def get_new_uri(uri1, uri2)
if more_general_uri?(uri1, uri2)
uri1
else
uri2
end
end
end
class AuthenticatedClient
include HTTPAuth::Digest
def initialize(host, port)
@client = RFuzz::HttpClient.new host, port
@cache = AuthenticationCache.new
@username = nil
@password = nil
end
def get_credentials_from_user
if @username.nil?
print 'Username: '
@username = $stdin.gets.strip
end
if @password.nil?
print 'Password: '
@password = $stdin.gets.strip
end
[@username, @password]
end
# Get a resource from the server
def get(resource)
uri = URI.parse resource
# If credentials were stored, use them. Otherwise do a normal get
credentials = @cache.get_credentials
if credentials.nil?
response = @client.get resource
else
puts "sending credentials: #{credentials.to_header}"
response = @client.get resource, :head => {'Authorization' => credentials.to_header}
end
# If response was 401, retry with authentication
if response.http_status == '401' && !response['WWW_AUTHENTICATE'].nil?
puts "got challenge: #{response['WWW_AUTHENTICATE']}"
challenge = Challenge.from_header(response['WWW_AUTHENTICATE'])
(stale = challenge.stale) rescue NoMethodError
if stale
username = credentials.username
password = credentials.password
else
username, password = get_credentials_from_user
end
credentials = Credentials.from_challenge(challenge,
:uri => resource, :username => username, :password => password, :method => 'GET'
)
puts "sending credentials: #{credentials.to_header}"
@cache.set_credentials_for uri.path, credentials
response = @client.get resource, :head => {'Authorization' => credentials.to_header}
end
# If the server sends authentication info use the information for the next request
if response['AUTHENTICATION_INFO']
puts "got authentication-info: #{response['AUTHENTICATION_INFO']}"
auth_info = AuthenticationInfo.from_header(response['AUTHENTICATION_INFO'])
@cache.update_usage_for uri.path, auth_info.h[:nextnonce]
else
@cache.update_usage_for uri.path
end
response
end
end
if $PROGRAM_NAME == __FILE__
unless ARGV.length == 2
puts <<-EOT
Usage: client_digest_secure get <url>
EOT
exit 0
end
uri = URI.parse ARGV[1]
client = AuthenticatedClient.new uri.host, uri.port
response = client.send ARGV[0].intern, uri.query ? "#{uri.path}&#{uri.query}" : uri.path
puts response.http_body
end
|