PageRenderTime 415ms CodeModel.GetById 90ms app.highlight 73ms RepoModel.GetById 122ms app.codeStats 113ms

/lib/httparty/net_digest_auth.rb

http://github.com/jnunemaker/httparty
Ruby | 136 lines | 110 code | 26 blank | 0 comment | 9 complexity | 808fae2602f25c3af648718072dd4492 MD5 | raw file
  1require 'digest/md5'
  2require 'net/http'
  3
  4module Net
  5  module HTTPHeader
  6    def digest_auth(username, password, response)
  7      authenticator = DigestAuthenticator.new(
  8        username,
  9        password,
 10        @method,
 11        @path,
 12        response
 13      )
 14
 15      authenticator.authorization_header.each do |v|
 16        add_field('Authorization', v)
 17      end
 18
 19      authenticator.cookie_header.each do |v|
 20        add_field('Cookie', v)
 21      end
 22    end
 23
 24    class DigestAuthenticator
 25      def initialize(username, password, method, path, response_header)
 26        @username = username
 27        @password = password
 28        @method   = method
 29        @path     = path
 30        @response = parse(response_header)
 31        @cookies  = parse_cookies(response_header)
 32      end
 33
 34      def authorization_header
 35        @cnonce = md5(random)
 36        header = [
 37          %(Digest username="#{@username}"),
 38          %(realm="#{@response['realm']}"),
 39          %(nonce="#{@response['nonce']}"),
 40          %(uri="#{@path}"),
 41          %(response="#{request_digest}")
 42        ]
 43
 44        header << %(algorithm="#{@response['algorithm']}") if algorithm_present?
 45
 46        if qop_present?
 47          fields = [
 48            %(cnonce="#{@cnonce}"),
 49            %(qop="#{@response['qop']}"),
 50            "nc=00000001"
 51          ]
 52          fields.each { |field| header << field }
 53        end
 54
 55        header << %(opaque="#{@response['opaque']}") if opaque_present?
 56        header
 57      end
 58
 59      def cookie_header
 60        @cookies
 61      end
 62
 63      private
 64
 65      def parse(response_header)
 66        header = response_header['www-authenticate']
 67
 68        header = header.gsub(/qop=(auth(?:-int)?)/, 'qop="\\1"')
 69
 70        header =~ /Digest (.*)/
 71        params = {}
 72        if $1
 73          non_quoted = $1.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
 74          non_quoted.gsub(/(\w+)=([^,]*)/) { params[$1] = $2 }
 75        end
 76        params
 77      end
 78
 79      def parse_cookies(response_header)
 80        return [] unless response_header['Set-Cookie']
 81
 82        cookies = response_header['Set-Cookie'].split('; ')
 83
 84        cookies.reduce([]) do |ret, cookie|
 85          ret << cookie
 86          ret
 87        end
 88
 89        cookies
 90      end
 91
 92      def opaque_present?
 93        @response.key?('opaque') && !@response['opaque'].empty?
 94      end
 95
 96      def qop_present?
 97        @response.key?('qop') && !@response['qop'].empty?
 98      end
 99
100      def random
101        format "%x", (Time.now.to_i + rand(65535))
102      end
103
104      def request_digest
105        a = [md5(a1), @response['nonce'], md5(a2)]
106        a.insert(2, "00000001", @cnonce, @response['qop']) if qop_present?
107        md5(a.join(":"))
108      end
109
110      def md5(str)
111        Digest::MD5.hexdigest(str)
112      end
113
114      def algorithm_present?
115        @response.key?('algorithm') && !@response['algorithm'].empty?
116      end
117
118      def use_md5_sess?
119        algorithm_present? && @response['algorithm'] == 'MD5-sess'
120      end
121
122      def a1
123        a1_user_realm_pwd =  [@username, @response['realm'], @password].join(':')
124        if use_md5_sess?
125          [ md5(a1_user_realm_pwd), @response['nonce'], @cnonce ].join(':')
126        else
127          a1_user_realm_pwd
128        end
129      end
130
131      def a2
132        [@method, @path].join(":")
133      end
134    end
135  end
136end