diff --git a/Rakefile b/Rakefile index 901a6a4..00aa3b6 100644 --- a/Rakefile +++ b/Rakefile @@ -12,6 +12,7 @@ begin gem.authors = ["Miron Cuperman","Thomas Flemming"] gem.executables = ["dav"] gem.add_dependency "nokogiri", ">= 1.3.0" + gem.add_dependency "net-http-digest_auth", ">=1.2" gem.add_development_dependency "rspec", ">= 1.2.0" gem.add_development_dependency "webrick-webdav", ">= 1.0" diff --git a/lib/net/dav.rb b/lib/net/dav.rb index 22d4a44..46d96f9 100644 --- a/lib/net/dav.rb +++ b/lib/net/dav.rb @@ -156,8 +156,12 @@ def handle_request(req, headers, limit = MAX_REDIRECTS, &block) case @authorization when :basic req.basic_auth @user, @pass - when :digest - digest_auth(req, @user, @pass, response) + when :digest + uri = @uri.merge(req.path) + uri.user = @user + uri.password = @pass + # re-calculate the digest header with the current uri path (and increment the nc) + req['Authorization'] = @digest_info.auth_header(uri, @lastServerDigestHeader, req.method) end response = nil @@ -186,6 +190,9 @@ def handle_request(req, headers, limit = MAX_REDIRECTS, &block) @authorization = :basic else @authorization = :digest + # Need to set up a new digest auth. + # Either we never had one, or the server wants a fresh one calculated. + digest_auth(req, @user, @pass, response) end return handle_request(req, headers, limit - 1, &block) when Net::HTTPRedirection then @@ -214,41 +221,19 @@ def clone_req(path, req, headers) return new_req end - CNONCE = Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535))).slice(0, 8) def digest_auth(request, user, password, response) - # based on http://segment7.net/projects/ruby/snippets/digest_auth.rb - @nonce_count = 0 if @nonce_count.nil? - @nonce_count += 1 raise "bad www-authenticate header" unless (response['www-authenticate'] =~ /^(\w+) (.*)/) - params = {} - $2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 } - - a_1 = "#{user}:#{params['realm']}:#{password}" - a_2 = "#{request.method}:#{request.path}" - request_digest = '' - request_digest << Digest::MD5.hexdigest(a_1) - request_digest << ':' << params['nonce'] - request_digest << ':' << ('%08x' % @nonce_count) - request_digest << ':' << CNONCE - request_digest << ':' << params['qop'] - request_digest << ':' << Digest::MD5.hexdigest(a_2) - - header = [] - header << "Digest username=\"#{user}\"" - header << "realm=\"#{params['realm']}\"" - header << "nonce=\"#{params['nonce']}\"" - header << "uri=\"#{request.path}\"" - header << "cnonce=\"#{CNONCE}\"" - header << "nc=#{'%08x' % @nonce_count}" - header << "qop=#{params['qop']}" - header << "response=\"#{Digest::MD5.hexdigest(request_digest)}\"" - header << "algorithm=\"MD5\"" - - header = header.join(', ') - request['Authorization'] = header + @digest_info = Net::HTTP::DigestAuth.new + uri = @uri + uri.user = user + uri.password = password + request['Authorization'] = @digest_info.auth_header(uri, response['www-authenticate'], request.method) + + # Keep this around so that we can send subsequent requests without hitting HTTPUnauthorized again. + @lastServerDigestHeader = response['www-authenticate'] end end diff --git a/spec/fixtures/file.html b/spec/fixtures/file.html index 39c9f36..a0fe451 100644 --- a/spec/fixtures/file.html +++ b/spec/fixtures/file.html @@ -1 +1 @@ -Content +File \ No newline at end of file diff --git a/spec/integration/net_dav_spec.rb b/spec/integration/net_dav_spec.rb index 6c823f5..6935c95 100644 --- a/spec/integration/net_dav_spec.rb +++ b/spec/integration/net_dav_spec.rb @@ -59,10 +59,10 @@ it "should write files to webdav server" do @props = find_props_or_error(@dav, @new_file_uri) - @props.should match(/404.*Not found/i) + @props.should match(/200 OK/i) @dav.put_string(@new_file_uri,"File contents") - dav.last_status.should == 200 + @dav.last_status.should == 200 @props = find_props_or_error(@dav, @new_file_uri ) @props.should match(/200 OK/i) @@ -75,7 +75,7 @@ @props.should match(/200 OK/i) @dav.delete(@new_file_uri) - dav.last_status.should == 204 + @dav.last_status.should == 204 @props = find_props_or_error(@dav, @new_file_uri) @props.should match(/404.*Not found/i) @@ -87,7 +87,7 @@ @props.should match(/200 OK/i) @dav.copy("/file.html", @copied_file_uri) - dav.last_status.should == 201 + @dav.last_status.should == 201 @props = find_props_or_error(@dav, @copied_file_uri) @props.should match(/200 OK/i) @@ -103,7 +103,7 @@ @props.should match(/200 OK/i) @dav.move("/file.html", @moved_file_uri) - dav.last_status.should == 201 + @dav.last_status.should == 201 @props = find_props_or_error(@dav, @moved_file_uri) @props.should match(/200 OK/i)