File: client_digest_secure

package info (click to toggle)
ruby-httpauth 0.2.1%2Bgh-1.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, trixie
  • size: 192 kB
  • sloc: ruby: 925; makefile: 2
file content (131 lines) | stat: -rwxr-xr-x 3,557 bytes parent folder | download | duplicates (3)
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