mirror of
https://github.com/jnunemaker/httparty
synced 2023-03-27 23:23:07 -04:00
Merge pull request #429 from bonybrown/md5sess_200
Added support for RFC2617 MD5-sess algorithm type
This commit is contained in:
commit
777563f5b9
5 changed files with 104 additions and 2 deletions
|
@ -18,3 +18,13 @@ Feature: Digest Authentication
|
|||
| username | password |
|
||||
| jcash | maninblack |
|
||||
Then the return value should match 'Digest Authenticated Page'
|
||||
|
||||
Scenario: Passing proper credentials to a page requiring Digest Authentication using md5-sess algorithm
|
||||
Given a remote service that returns 'Digest Authenticated Page Using MD5-sess'
|
||||
And that service is accessed at the path '/digest_auth.html'
|
||||
And that service is protected by MD5-sess Digest Authentication
|
||||
And that service requires the username 'jcash' with the password 'maninblack'
|
||||
When I call HTTParty#get with '/digest_auth.html' and a digest_auth hash:
|
||||
| username | password |
|
||||
| jcash | maninblack |
|
||||
Then the return value should match 'Digest Authenticated Page Using MD5-sess'
|
||||
|
|
|
@ -88,6 +88,39 @@ module DigestAuthentication
|
|||
end
|
||||
end
|
||||
|
||||
module DigestAuthenticationUsingMD5Sess
|
||||
NONCE = 'nonce'
|
||||
REALM = 'testrealm@host.com'
|
||||
QOP = 'auth,auth-int'
|
||||
def self.extended(base)
|
||||
base.custom_headers["WWW-Authenticate"] = %(Digest realm="#{REALM}",qop="#{QOP}",algorithm="MD5-sess",nonce="#{NONCE}",opaque="opaque"')
|
||||
end
|
||||
|
||||
def process(request, response)
|
||||
if authorized?(request)
|
||||
super
|
||||
else
|
||||
reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
|
||||
end
|
||||
end
|
||||
|
||||
def md5(str)
|
||||
Digest::MD5.hexdigest(str)
|
||||
end
|
||||
|
||||
def authorized?(request)
|
||||
auth = request.params["HTTP_AUTHORIZATION"]
|
||||
params = {}
|
||||
auth.to_s.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }.gsub(/(\w+)=([^,]*)/) { params[$1] = $2 }
|
||||
a1a = [@username,REALM,@password].join(':')
|
||||
a1 = [md5(a1a),NONCE,params['cnonce'] ].join(':')
|
||||
a2 = [ request.params["REQUEST_METHOD"], request.params["REQUEST_URI"] ] .join(':')
|
||||
expected_response = md5( [md5(a1), NONCE, params['nc'], params['cnonce'], QOP, md5(a2)].join(':') )
|
||||
expected_response == params['response']
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def new_mongrel_redirector(target_url, relative_path = false)
|
||||
target_url = "http://#{@host_and_port}#{target_url}" unless relative_path
|
||||
Mongrel::RedirectHandler.new(target_url)
|
||||
|
|
|
@ -55,6 +55,10 @@ Given /that service is protected by Digest Authentication/ do
|
|||
@handler.extend DigestAuthentication
|
||||
end
|
||||
|
||||
Given /that service is protected by MD5-sess Digest Authentication/ do
|
||||
@handler.extend DigestAuthenticationUsingMD5Sess
|
||||
end
|
||||
|
||||
Given /that service requires the username '(.*)' with the password '(.*)'/ do |username, password|
|
||||
@handler.username = username
|
||||
@handler.password = password
|
||||
|
|
|
@ -41,6 +41,8 @@ module Net
|
|||
%(response="#{request_digest}")
|
||||
]
|
||||
|
||||
header << %(algorithm="#{@response['algorithm']}") if algorithm_present?
|
||||
|
||||
if qop_present?
|
||||
fields = [
|
||||
%(cnonce="#{@cnonce}"),
|
||||
|
@ -66,7 +68,8 @@ module Net
|
|||
|
||||
header =~ /Digest (.*)/
|
||||
params = {}
|
||||
$1.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
|
||||
non_quoted = $1.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
|
||||
non_quoted.gsub(/(\w+)=([^,]*)/) { params[$1] = $2 }
|
||||
params
|
||||
end
|
||||
|
||||
|
@ -105,8 +108,21 @@ module Net
|
|||
Digest::MD5.hexdigest(str)
|
||||
end
|
||||
|
||||
def algorithm_present?
|
||||
@response.key?('algorithm') && !@response['algorithm'].empty?
|
||||
end
|
||||
|
||||
def use_md5_sess?
|
||||
algorithm_present? && @response['algorithm'] == 'MD5-sess'
|
||||
end
|
||||
|
||||
def a1
|
||||
[@username, @response['realm'], @password].join(":")
|
||||
a1_user_realm_pwd = [@username, @response['realm'], @password].join(':')
|
||||
if use_md5_sess?
|
||||
[ md5(a1_user_realm_pwd), @response['nonce'], @cnonce ].join(':')
|
||||
else
|
||||
a1_user_realm_pwd
|
||||
end
|
||||
end
|
||||
|
||||
def a2
|
||||
|
|
|
@ -188,4 +188,43 @@ RSpec.describe Net::HTTPHeader::DigestAuthenticator do
|
|||
expect(authorization_header).to include(%(response="#{request_digest}"))
|
||||
end
|
||||
end
|
||||
|
||||
context "with algorithm specified" do
|
||||
before do
|
||||
@digest = setup_digest({
|
||||
'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth", algorithm=MD5'
|
||||
})
|
||||
end
|
||||
|
||||
it "should recognise algorithm was specified" do
|
||||
expect( @digest.send :algorithm_present? ).to be(true)
|
||||
end
|
||||
|
||||
it "should set the algorithm header" do
|
||||
expect(authorization_header).to include('algorithm="MD5"')
|
||||
end
|
||||
end
|
||||
|
||||
context "with md5-sess algorithm specified" do
|
||||
before do
|
||||
@digest = setup_digest({
|
||||
'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth", algorithm=MD5-sess'
|
||||
})
|
||||
end
|
||||
|
||||
it "should recognise algorithm was specified" do
|
||||
expect( @digest.send :algorithm_present? ).to be(true)
|
||||
end
|
||||
|
||||
it "should set the algorithm header" do
|
||||
expect(authorization_header).to include('algorithm="MD5-sess"')
|
||||
end
|
||||
|
||||
it "should set response using md5-sess algorithm" do
|
||||
request_digest = "md5(md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(deadbeef)):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
|
||||
expect(authorization_header).to include(%(response="#{request_digest}"))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue