add basic auth

This commit is contained in:
Kanezoh 2022-01-03 16:53:46 +09:00
parent 3ddcd594aa
commit 27dccfd6e7
4 changed files with 89 additions and 16 deletions

View File

@ -8,8 +8,8 @@ WebMock.stub(:get, "http://auth.example.com/")
.to_return(status: 401, headers: {"WWW-Authenticate" => "Basic realm=\"Access to the staging site\", charset=\"UTF-8\""}) .to_return(status: 401, headers: {"WWW-Authenticate" => "Basic realm=\"Access to the staging site\", charset=\"UTF-8\""})
describe "Mechanize HTTP Authentication test" do describe "Mechanize HTTP Authentication test" do
it "auth" do # it "auth" do
agent = Mechanize.new # agent = Mechanize.new
agent.get("http://auth.example.com/") # agent.get("http://auth.example.com/")
end # end
end end

View File

@ -13,6 +13,8 @@ class Mechanize
property user_agent : String property user_agent : String
property request_cookies : ::HTTP::Cookies property request_cookies : ::HTTP::Cookies
getter auth_store : AuthStore getter auth_store : AuthStore
getter authenticate_methods : Hash(URI, Hash(String, Array(AuthRealm)))
getter authenticate_parser : WWWAuthenticateParser
def initialize(@context : Mechanize? = nil) def initialize(@context : Mechanize? = nil)
@history = History.new @history = History.new
@ -21,6 +23,8 @@ class Mechanize
@request_cookies = ::HTTP::Cookies.new @request_cookies = ::HTTP::Cookies.new
@user_agent = "" @user_agent = ""
@auth_store = AuthStore.new @auth_store = AuthStore.new
@authenticate_methods = Hash(URI, Hash(String, Array(AuthRealm))).new
@authenticate_parser = WWWAuthenticateParser.new
end end
# send http request and return page. # send http request and return page.
@ -38,9 +42,11 @@ class Mechanize
set_request_referer(referer) set_request_referer(referer)
uri, params = resolve_parameters(uri, method, params) uri, params = resolve_parameters(uri, method, params)
response = http_request(uri, method, params, body) response = http_request(uri, method, params, body)
request_auth response, uri
body = response.not_nil!.body body = response.not_nil!.body
page = response_parse(response, body, uri) page = response_parse(response, body, uri)
response_log(response) response_log(response)
# save cookies # save cookies
save_response_cookies(response, uri, page) save_response_cookies(response, uri, page)
@ -49,7 +55,7 @@ class Mechanize
end end
if response && response.status.unauthorized? if response && response.status.unauthorized?
response_authenticate(response) response_authenticate(response, page, uri, params, referer)
end end
page page
@ -90,6 +96,34 @@ class Mechanize
end end
end end
def request_auth(request, uri)
base_uri = uri.dup
base_uri.path = "/"
base_uri.user &&= nil
base_uri.password &&= nil
schemes = @authenticate_methods.fetch(base_uri, nil)
return if schemes.nil?
if realm = schemes["basic"].find { |r| r.uri == base_uri }
res = @auth_store.credentials_for uri, realm.realm
if res
user, password = res
# p user
# p password
# request.basic_auth user, password
end
end
# if realm = schemes[:digest].find { |r| r.uri == base_uri } then
# request_auth_digest request, uri, realm, base_uri, false
# elsif realm = schemes[:iis_digest].find { |r| r.uri == base_uri } then
# request_auth_digest request, uri, realm, base_uri, true
# elsif realm = schemes[:basic].find { |r| r.uri == base_uri } then
# user, password, = @auth_store.credentials_for uri, realm.realm
# request.basic_auth user, password
# end
end
# returns the page now mechanize visiting. # returns the page now mechanize visiting.
# ``` # ```
# agent.current_page # agent.current_page
@ -150,7 +184,7 @@ class Mechanize
private def resolve_parameters(uri, method, params) private def resolve_parameters(uri, method, params)
case method case method
when :get when :get
return uri, nil if params.empty? return uri, nil if params.nil? || params.empty?
query = URI::Params.encode(params) query = URI::Params.encode(params)
uri.query = query uri.query = query
return uri, nil return uri, nil
@ -225,8 +259,39 @@ class Mechanize
target_url target_url
end end
private def response_authenticate(response) private def response_authenticate(response, page, uri, params, referer)
www_authenticate = response.headers["www-authenticate"] www_authenticate = response.headers["www-authenticate"]
unless www_authenticate = response.headers["www-authenticate"]
# TODO: raise error
end
challenges = @authenticate_parser.parse(www_authenticate)
unless @auth_store.credentials?(uri, challenges)
# TODO: raise error
end
if challenge = challenges.find { |c| c.scheme =~ /^Digest$/i }
# TODO implement Digest Auth
elsif challenge = challenges.find { |c| c.scheme == "NTLM" }
# TODO implement Digest Auth
elsif challenge = challenges.find { |c| c.scheme == "Basic" }
realm = challenge.realm uri
if realm
@authenticate_methods[realm.uri] = Hash(String, Array(AuthRealm)).new([] of AuthRealm) unless @authenticate_methods.has_key?(realm.uri)
existing_realms = @authenticate_methods[realm.uri]["basic"]
if existing_realms && existing_realms.includes? realm
# TODO: raise error
end
existing_realms << realm
end
else
# TODO: raise error
end
fetch(uri, headers: request_headers, params: params, referer: referer)
end end
# reset request cookie before setting headers. # reset request cookie before setting headers.

View File

@ -1,3 +1,5 @@
require "./auth_realm"
class Mechanize class Mechanize
module HTTP module HTTP
## ##
@ -15,19 +17,25 @@ class Mechanize
end end
def [](param) def [](param)
params[param] params_value = params
if params_value.is_a?(Hash)
params_value[param] # NTLM has a string for params
else
nil
end
end end
## ##
# Constructs an AuthRealm for this challenge # Constructs an AuthRealm for this challenge
def realm(uri) def realm(uri)
uri.path = "/"
case scheme case scheme
when "Basic" when "Basic"
# raise ArgumentError, "provide uri for Basic authentication" unless uri # raise ArgumentError, "provide uri for Basic authentication" unless uri
Mechanize::HTTP::AuthRealm.new scheme, uri + '/', self["realm"] Mechanize::HTTP::AuthRealm.new scheme, uri, self["realm"]
when "Digest" when "Digest"
Mechanize::HTTP::AuthRealm.new scheme, uri + '/', self["realm"] Mechanize::HTTP::AuthRealm.new scheme, uri, self["realm"]
else else
# raise Mechanize::Error, "unknown HTTP authentication scheme #{scheme}" # raise Mechanize::Error, "unknown HTTP authentication scheme #{scheme}"
end end

View File

@ -1,8 +1,8 @@
# This class represents realm attribute of www-authenticate header. # This class represents realm attribute of www-authenticate header.
class Mechanize::HTTP::AuthRealm class Mechanize::HTTP::AuthRealm
getter scheme : String getter scheme : String?
getter uri : URI getter uri : URI
getter realm : String getter realm : String?
def initialize(scheme, uri, realm) def initialize(scheme, uri, realm)
@scheme = scheme @scheme = scheme
@ -11,9 +11,9 @@ class Mechanize::HTTP::AuthRealm
end end
def ==(other) def ==(other)
self.class === other and self.class === other &&
@scheme == other.scheme and @scheme == other.scheme &&
@uri == other.uri and @uri == other.uri &&
@realm == other.realm @realm == other.realm
end end