mechanize 2.3 → 2.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mechanize might be problematic. Click here for more details.
- data.tar.gz.sig +0 -0
- data/CHANGELOG.rdoc +24 -1
- data/Manifest.txt +2 -0
- data/examples/spider.rb +1 -0
- data/lib/mechanize.rb +33 -8
- data/lib/mechanize/file_response.rb +11 -3
- data/lib/mechanize/http/agent.rb +69 -26
- data/lib/mechanize/http/auth_challenge.rb +7 -0
- data/lib/mechanize/http/auth_store.rb +115 -0
- data/lib/mechanize/http/www_authenticate_parser.rb +4 -0
- data/lib/mechanize/page.rb +1 -1
- data/lib/mechanize/response_code_error.rb +7 -2
- data/lib/mechanize/test_case.rb +37 -29
- data/lib/mechanize/unauthorized_error.rb +19 -0
- data/test/test_mechanize.rb +26 -14
- data/test/test_mechanize_http_agent.rb +50 -25
- data/test/test_mechanize_http_auth_challenge.rb +10 -0
- data/test/test_mechanize_http_auth_store.rb +172 -0
- data/test/test_mechanize_http_www_authenticate_parser.rb +22 -6
- metadata +25 -21
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.rdoc
CHANGED
@@ -1,8 +1,31 @@
|
|
1
1
|
= Mechanize CHANGELOG
|
2
2
|
|
3
|
-
=== 2.
|
3
|
+
=== 2.4
|
4
|
+
|
5
|
+
* Security fix:
|
6
|
+
|
7
|
+
Mechanize#auth and Mechanize#basic_auth allowed disclosure of passwords to
|
8
|
+
malicious servers and have been removed.
|
9
|
+
|
10
|
+
In prior versions of mechanize only one set of HTTP authentication
|
11
|
+
credentials were allowed for all connections. If a mechanize instance
|
12
|
+
connected to more than one server then a malicious server detecting
|
13
|
+
mechanize could ask for HTTP Basic authentication. This would expose the
|
14
|
+
username and password intended only for one server.
|
15
|
+
|
16
|
+
Mechanize#auth and Mechanize#basic_auth now warn when used.
|
17
|
+
|
18
|
+
To fix the warning switch to Mechanize#add_auth which requires at the URI
|
19
|
+
the credentials are intended for, the username and the password.
|
20
|
+
Optionally an HTTP authentication realm or NTLM domain may be provided.
|
4
21
|
|
5
22
|
* Minor enhancement
|
23
|
+
* Improved exception messages for 401 Unauthorized responses. Mechanize now
|
24
|
+
tells you if you were missing credentials, had an incorrect password, etc.
|
25
|
+
|
26
|
+
=== 2.3 / 2012-02-20
|
27
|
+
|
28
|
+
* Minor enhancements
|
6
29
|
* Add support for the Max-Age attribute in the Set-Cookie header.
|
7
30
|
* Added Mechanize::Download#body for compatibility with Mechanize::File when
|
8
31
|
using Mechanize#get_file with Mechanize::Image or other Download-based
|
data/Manifest.txt
CHANGED
@@ -46,6 +46,7 @@ lib/mechanize/http.rb
|
|
46
46
|
lib/mechanize/http/agent.rb
|
47
47
|
lib/mechanize/http/auth_challenge.rb
|
48
48
|
lib/mechanize/http/auth_realm.rb
|
49
|
+
lib/mechanize/http/auth_store.rb
|
49
50
|
lib/mechanize/http/content_disposition_parser.rb
|
50
51
|
lib/mechanize/http/www_authenticate_parser.rb
|
51
52
|
lib/mechanize/image.rb
|
@@ -145,6 +146,7 @@ test/test_mechanize_history.rb
|
|
145
146
|
test/test_mechanize_http_agent.rb
|
146
147
|
test/test_mechanize_http_auth_challenge.rb
|
147
148
|
test/test_mechanize_http_auth_realm.rb
|
149
|
+
test/test_mechanize_http_auth_store.rb
|
148
150
|
test/test_mechanize_http_content_disposition_parser.rb
|
149
151
|
test/test_mechanize_http_www_authenticate_parser.rb
|
150
152
|
test/test_mechanize_image.rb
|
data/examples/spider.rb
CHANGED
data/lib/mechanize.rb
CHANGED
@@ -73,7 +73,7 @@ class Mechanize
|
|
73
73
|
##
|
74
74
|
# The version of Mechanize you are using.
|
75
75
|
|
76
|
-
VERSION = '2.
|
76
|
+
VERSION = '2.4'
|
77
77
|
|
78
78
|
##
|
79
79
|
# Base mechanize error class
|
@@ -209,7 +209,7 @@ class Mechanize
|
|
209
209
|
##
|
210
210
|
# Maximum number of items allowed in the history. The default setting is 50
|
211
211
|
# pages. Note that the size of the history multiplied by the maximum
|
212
|
-
# response body size
|
212
|
+
# response body size
|
213
213
|
|
214
214
|
def max_history
|
215
215
|
@agent.history.max_size
|
@@ -618,17 +618,42 @@ class Mechanize
|
|
618
618
|
attr_reader :proxy_user
|
619
619
|
|
620
620
|
##
|
621
|
-
#
|
622
|
-
#
|
621
|
+
# *NOTE*: These credentials will be used as a default for any challenge
|
622
|
+
# exposing your password to disclosure to malicious servers. Use of this
|
623
|
+
# method will warn. This method is deprecated and will be removed in
|
624
|
+
# mechanize 3.
|
625
|
+
#
|
626
|
+
# Sets the +user+ and +password+ as the default credentials to be used for
|
627
|
+
# HTTP authentication for any server. The +domain+ is used for NTLM
|
628
|
+
# authentication.
|
629
|
+
|
630
|
+
def auth user, password, domain = nil
|
631
|
+
caller.first =~ /(.*?):(\d+).*?$/
|
632
|
+
|
633
|
+
warn <<-WARNING
|
634
|
+
At #{$1} line #{$2}
|
635
|
+
|
636
|
+
Use of #auth and #basic_auth are deprecated due to a security vulnerability.
|
637
|
+
|
638
|
+
WARNING
|
623
639
|
|
624
|
-
|
625
|
-
@agent.user = user
|
626
|
-
@agent.password = password
|
627
|
-
@agent.domain = domain
|
640
|
+
@agent.add_default_auth user, password, domain
|
628
641
|
end
|
629
642
|
|
630
643
|
alias basic_auth auth
|
631
644
|
|
645
|
+
##
|
646
|
+
# Adds credentials +user+, +pass+ for +uri+. If +realm+ is set the
|
647
|
+
# credentials are used only for that realm. If +realm+ is not set the
|
648
|
+
# credentials become the default for any realm on that URI.
|
649
|
+
#
|
650
|
+
# +domain+ and +realm+ are exclusive as NTLM does not follow RFC 2617. If
|
651
|
+
# +domain+ is given it is only used for NTLM authentication.
|
652
|
+
|
653
|
+
def add_auth uri, user, password, realm = nil, domain = nil
|
654
|
+
@agent.add_auth uri, user, password, realm, domain
|
655
|
+
end
|
656
|
+
|
632
657
|
##
|
633
658
|
# Are If-Modified-Since conditional requests enabled?
|
634
659
|
|
@@ -2,12 +2,15 @@
|
|
2
2
|
# Fake response for dealing with file:/// requests
|
3
3
|
|
4
4
|
class Mechanize::FileResponse
|
5
|
+
|
5
6
|
def initialize(file_path)
|
6
7
|
@file_path = file_path
|
8
|
+
@uri = nil
|
7
9
|
end
|
8
10
|
|
9
11
|
def read_body
|
10
|
-
raise Mechanize::ResponseCodeError
|
12
|
+
raise Mechanize::ResponseCodeError.new(self) unless
|
13
|
+
File.exist? @file_path
|
11
14
|
|
12
15
|
if directory?
|
13
16
|
yield dir_body
|
@@ -30,10 +33,10 @@ class Mechanize::FileResponse
|
|
30
33
|
def each_header; end
|
31
34
|
|
32
35
|
def [](key)
|
33
|
-
return nil
|
36
|
+
return nil if key.casecmp('Content-Type') != 0
|
34
37
|
return 'text/html' if directory?
|
35
38
|
return 'text/html' if ['.html', '.xhtml'].any? { |extn|
|
36
|
-
@file_path
|
39
|
+
@file_path.end_with?(extn)
|
37
40
|
}
|
38
41
|
nil
|
39
42
|
end
|
@@ -53,6 +56,10 @@ class Mechanize::FileResponse
|
|
53
56
|
File.exist?(@file_path) ? 'OK' : 'Not Found'
|
54
57
|
end
|
55
58
|
|
59
|
+
def uri
|
60
|
+
@uri ||= URI "file://#{@file_path}"
|
61
|
+
end
|
62
|
+
|
56
63
|
private
|
57
64
|
|
58
65
|
def dir_body
|
@@ -70,5 +77,6 @@ class Mechanize::FileResponse
|
|
70
77
|
def directory?
|
71
78
|
File.directory?(@file_path)
|
72
79
|
end
|
80
|
+
|
73
81
|
end
|
74
82
|
|
data/lib/mechanize/http/agent.rb
CHANGED
@@ -43,11 +43,9 @@ class Mechanize::HTTP::Agent
|
|
43
43
|
|
44
44
|
# :section: HTTP Authentication
|
45
45
|
|
46
|
+
attr_reader :auth_store # :nodoc:
|
46
47
|
attr_reader :authenticate_methods # :nodoc:
|
47
48
|
attr_reader :digest_challenges # :nodoc:
|
48
|
-
attr_accessor :user
|
49
|
-
attr_accessor :password
|
50
|
-
attr_accessor :domain
|
51
49
|
|
52
50
|
# :section: Redirection
|
53
51
|
|
@@ -139,6 +137,7 @@ class Mechanize::HTTP::Agent
|
|
139
137
|
@webrobots = nil
|
140
138
|
|
141
139
|
# HTTP Authentication
|
140
|
+
@auth_store = Mechanize::HTTP::AuthStore.new
|
142
141
|
@authenticate_parser = Mechanize::HTTP::WWWAuthenticateParser.new
|
143
142
|
@authenticate_methods = Hash.new do |methods, uri|
|
144
143
|
methods[uri] = Hash.new do |realms, auth_scheme|
|
@@ -147,9 +146,6 @@ class Mechanize::HTTP::Agent
|
|
147
146
|
end
|
148
147
|
@digest_auth = Net::HTTP::DigestAuth.new
|
149
148
|
@digest_challenges = {}
|
150
|
-
@password = nil # HTTP auth password
|
151
|
-
@user = nil # HTTP auth user
|
152
|
-
@domain = nil # NTLM HTTP domain
|
153
149
|
|
154
150
|
# SSL
|
155
151
|
@pass = nil
|
@@ -170,6 +166,33 @@ class Mechanize::HTTP::Agent
|
|
170
166
|
@http.keep_alive = 300
|
171
167
|
end
|
172
168
|
|
169
|
+
##
|
170
|
+
# Adds credentials +user+, +pass+ for +uri+. If +realm+ is set the
|
171
|
+
# credentials are used only for that realm. If +realm+ is not set the
|
172
|
+
# credentials become the default for any realm on that URI.
|
173
|
+
#
|
174
|
+
# +domain+ and +realm+ are exclusive as NTLM does not follow RFC 2617. If
|
175
|
+
# +domain+ is given it is only used for NTLM authentication.
|
176
|
+
|
177
|
+
def add_auth uri, user, password, realm = nil, domain = nil
|
178
|
+
@auth_store.add_auth uri, user, password, realm, domain
|
179
|
+
end
|
180
|
+
|
181
|
+
##
|
182
|
+
# USE OF add_default_auth IS NOT RECOMMENDED AS IT MAY EXPOSE PASSWORDS TO
|
183
|
+
# THIRD PARTIES
|
184
|
+
#
|
185
|
+
# Adds credentials +user+, +pass+ as the default authentication credentials.
|
186
|
+
# If no other credentials are available these will be returned from
|
187
|
+
# credentials_for.
|
188
|
+
#
|
189
|
+
# If +domain+ is given it is only used for NTLM authentication.
|
190
|
+
|
191
|
+
def add_default_auth user, password, domain = nil # :nodoc:
|
192
|
+
@auth_store.add_default_auth user, password, domain
|
193
|
+
end
|
194
|
+
|
195
|
+
##
|
173
196
|
# Retrieves +uri+ and parses it into a page or other object according to
|
174
197
|
# PluggableParser. If the URI is an HTTP or HTTPS scheme URI the given HTTP
|
175
198
|
# +method+ is used to retrieve it, along with the HTTP +headers+, request
|
@@ -265,7 +288,7 @@ class Mechanize::HTTP::Agent
|
|
265
288
|
response_authenticate(response, page, uri, request, headers, params,
|
266
289
|
referer)
|
267
290
|
else
|
268
|
-
raise Mechanize::ResponseCodeError.new(page
|
291
|
+
raise Mechanize::ResponseCodeError.new(page, 'unhandled response')
|
269
292
|
end
|
270
293
|
end
|
271
294
|
|
@@ -470,16 +493,18 @@ class Mechanize::HTTP::Agent
|
|
470
493
|
request_auth_digest request, uri, realm, base_uri, false
|
471
494
|
elsif realm = schemes[:iis_digest].find { |r| r.uri == base_uri } then
|
472
495
|
request_auth_digest request, uri, realm, base_uri, true
|
473
|
-
elsif schemes[:basic].find { |r| r.uri == base_uri } then
|
474
|
-
|
496
|
+
elsif realm = schemes[:basic].find { |r| r.uri == base_uri } then
|
497
|
+
user, password, = @auth_store.credentials_for uri, realm.realm
|
498
|
+
request.basic_auth user, password
|
475
499
|
end
|
476
500
|
end
|
477
501
|
|
478
502
|
def request_auth_digest request, uri, realm, base_uri, iis
|
479
503
|
challenge = @digest_challenges[realm]
|
480
504
|
|
481
|
-
|
482
|
-
uri.
|
505
|
+
user, password, = @auth_store.credentials_for uri, realm.realm
|
506
|
+
uri.user = user
|
507
|
+
uri.password = password
|
483
508
|
|
484
509
|
auth = @digest_auth.auth_header uri, challenge.to_s, request.method, iis
|
485
510
|
request['Authorization'] = auth
|
@@ -523,8 +548,8 @@ class Mechanize::HTTP::Agent
|
|
523
548
|
# major browsers do.
|
524
549
|
def request_referer request, uri, referer
|
525
550
|
return unless referer
|
526
|
-
return if 'https'
|
527
|
-
'https' !=
|
551
|
+
return if 'https'.casecmp(referer.scheme) == 0 and
|
552
|
+
'https'.casecmp(uri.scheme) != 0
|
528
553
|
if referer.fragment || referer.user || referer.password
|
529
554
|
referer = referer.dup
|
530
555
|
referer.fragment = referer.user = referer.password = nil
|
@@ -643,14 +668,20 @@ class Mechanize::HTTP::Agent
|
|
643
668
|
|
644
669
|
def response_authenticate(response, page, uri, request, headers, params,
|
645
670
|
referer)
|
646
|
-
raise Mechanize::UnauthorizedError, page unless @user || @password
|
647
|
-
|
648
671
|
www_authenticate = response['www-authenticate']
|
649
672
|
|
650
|
-
|
651
|
-
|
673
|
+
unless www_authenticate = response['www-authenticate'] then
|
674
|
+
message = 'WWW-Authenticate header missing in response'
|
675
|
+
raise Mechanize::UnauthorizedError.new(page, nil, message)
|
676
|
+
end
|
677
|
+
|
652
678
|
challenges = @authenticate_parser.parse www_authenticate
|
653
679
|
|
680
|
+
unless @auth_store.credentials? uri, challenges then
|
681
|
+
message = "no credentials found, provide some with #add_auth"
|
682
|
+
raise Mechanize::UnauthorizedError.new(page, challenges, message)
|
683
|
+
end
|
684
|
+
|
654
685
|
if challenge = challenges.find { |c| c.scheme =~ /^Digest$/i } then
|
655
686
|
realm = challenge.realm uri
|
656
687
|
|
@@ -662,23 +693,30 @@ class Mechanize::HTTP::Agent
|
|
662
693
|
|
663
694
|
existing_realms = @authenticate_methods[realm.uri][auth_scheme]
|
664
695
|
|
665
|
-
|
666
|
-
|
696
|
+
if existing_realms.include? realm
|
697
|
+
message = 'Digest authentication failed'
|
698
|
+
raise Mechanize::UnauthorizedError.new(page, challeges, message)
|
699
|
+
end
|
667
700
|
|
668
701
|
existing_realms << realm
|
669
702
|
@digest_challenges[realm] = challenge
|
670
703
|
elsif challenge = challenges.find { |c| c.scheme == 'NTLM' } then
|
671
704
|
existing_realms = @authenticate_methods[uri + '/'][:ntlm]
|
672
705
|
|
673
|
-
|
674
|
-
|
706
|
+
if existing_realms.include?(realm) and not challenge.params then
|
707
|
+
message = 'NTLM authentication failed'
|
708
|
+
raise Mechanize::UnauthorizedError.new(page, challenges, message)
|
709
|
+
end
|
675
710
|
|
676
711
|
existing_realms << realm
|
677
712
|
|
678
713
|
if challenge.params then
|
679
714
|
type_2 = Net::NTLM::Message.decode64 challenge.params
|
680
715
|
|
681
|
-
|
716
|
+
user, password, domain = @auth_store.credentials_for uri, nil
|
717
|
+
|
718
|
+
type_3 = type_2.response({ :user => user, :password => password,
|
719
|
+
:domain => domain },
|
682
720
|
{ :ntlmv2 => true }).encode64
|
683
721
|
|
684
722
|
headers['Authorization'] = "NTLM #{type_3}"
|
@@ -691,12 +729,15 @@ class Mechanize::HTTP::Agent
|
|
691
729
|
|
692
730
|
existing_realms = @authenticate_methods[realm.uri][:basic]
|
693
731
|
|
694
|
-
|
695
|
-
|
732
|
+
if existing_realms.include? realm then
|
733
|
+
message = 'Basic authentication failed'
|
734
|
+
raise Mechanize::UnauthorizedError.new(page, challenges, message)
|
735
|
+
end
|
696
736
|
|
697
737
|
existing_realms << realm
|
698
738
|
else
|
699
|
-
|
739
|
+
message = 'unsupported authentication scheme'
|
740
|
+
raise Mechanize::UnauthorizedError.new(page, challenges, message)
|
700
741
|
end
|
701
742
|
|
702
743
|
fetch uri, request.method.downcase.to_sym, headers, params, referer
|
@@ -840,7 +881,7 @@ class Mechanize::HTTP::Agent
|
|
840
881
|
body_io.flush
|
841
882
|
body_io.rewind
|
842
883
|
|
843
|
-
raise Mechanize::ResponseCodeError,
|
884
|
+
raise Mechanize::ResponseCodeError.new(response, uri) if
|
844
885
|
Net::HTTPUnknownResponse === response
|
845
886
|
|
846
887
|
content_length = response.content_length
|
@@ -1126,3 +1167,5 @@ class Mechanize::HTTP::Agent
|
|
1126
1167
|
|
1127
1168
|
end
|
1128
1169
|
|
1170
|
+
require 'mechanize/http/auth_store'
|
1171
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
##
|
2
|
+
# A credential store for HTTP authentication.
|
3
|
+
#
|
4
|
+
# uri = URI 'http://example'
|
5
|
+
#
|
6
|
+
# store = Mechanize::HTTP::AuthStore.new
|
7
|
+
# store.add_auth uri, 'user1', 'pass'
|
8
|
+
# store.add_auth uri, 'user2', 'pass', 'realm'
|
9
|
+
#
|
10
|
+
# user, pass = store.credentials_for uri, 'realm' #=> 'user2', 'pass'
|
11
|
+
# user, pass = store.credentials_for uri, 'other' #=> 'user1', 'pass'
|
12
|
+
#
|
13
|
+
# store.remove_auth uri # removes all credentials
|
14
|
+
|
15
|
+
class Mechanize::HTTP::AuthStore
|
16
|
+
|
17
|
+
attr_reader :auth_accounts # :nodoc:
|
18
|
+
|
19
|
+
attr_reader :default_auth # :nodoc:
|
20
|
+
|
21
|
+
##
|
22
|
+
# Creates a new AuthStore
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@auth_accounts = Hash.new do |h, uri|
|
26
|
+
h[uri] = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
@default_auth = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Adds credentials +user+, +pass+ for the server at +uri+. If +realm+ is
|
34
|
+
# set the credentials are used only for that realm. If +realm+ is not set
|
35
|
+
# the credentials become the default for any realm on that URI.
|
36
|
+
#
|
37
|
+
# +domain+ and +realm+ are exclusive as NTLM does not follow RFC
|
38
|
+
# 2617. If +domain+ is given it is only used for NTLM authentication.
|
39
|
+
|
40
|
+
def add_auth uri, user, pass, realm = nil, domain = nil
|
41
|
+
raise ArgumentError,
|
42
|
+
'NTLM domain given with realm which NTLM does not use' if
|
43
|
+
realm and domain
|
44
|
+
|
45
|
+
uri += '/'
|
46
|
+
|
47
|
+
auth_accounts[uri][realm] = [user, pass, domain]
|
48
|
+
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# USE OF add_default_auth IS NOT RECOMMENDED AS IT MAY EXPOSE PASSWORDS TO
|
54
|
+
# THIRD PARTIES
|
55
|
+
#
|
56
|
+
# Adds credentials +user+, +pass+ as the default authentication credentials.
|
57
|
+
# If no other credentials are available these will be returned from
|
58
|
+
# credentials_for.
|
59
|
+
#
|
60
|
+
# If +domain+ is given it is only used for NTLM authentication.
|
61
|
+
|
62
|
+
def add_default_auth user, pass, domain = nil
|
63
|
+
warn <<-WARN
|
64
|
+
You have supplied default authentication credentials that apply to ANY SERVER.
|
65
|
+
Your username and password can be retrieved by ANY SERVER using Basic
|
66
|
+
authentication.
|
67
|
+
|
68
|
+
THIS EXPOSES YOUR USERNAME AND PASSWORD TO DISCLOSURE WITHOUT YOUR KNOWLEDGE.
|
69
|
+
|
70
|
+
Use add_auth to set authentication credentials that will only be delivered
|
71
|
+
only to a particular server you specify.
|
72
|
+
WARN
|
73
|
+
|
74
|
+
@default_auth = [user, pass, domain]
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Returns true if credentials exist for the +challenges+ from the server at
|
79
|
+
# +uri+.
|
80
|
+
|
81
|
+
def credentials? uri, challenges
|
82
|
+
challenges.any? do |challenge|
|
83
|
+
credentials_for uri, challenge.realm_name
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Retrieves credentials for +realm+ on the server at +uri+.
|
89
|
+
|
90
|
+
def credentials_for uri, realm
|
91
|
+
uri += '/'
|
92
|
+
|
93
|
+
realms = @auth_accounts[uri]
|
94
|
+
|
95
|
+
realms[realm] || realms[nil] || @default_auth
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Removes credentials for +realm+ on the server at +uri+. If +realm+ is not
|
100
|
+
# set all credentials for the server at +uri+ are removed.
|
101
|
+
|
102
|
+
def remove_auth uri, realm = nil
|
103
|
+
uri += '/'
|
104
|
+
|
105
|
+
if realm then
|
106
|
+
auth_accounts[uri].delete realm
|
107
|
+
else
|
108
|
+
auth_accounts.delete uri
|
109
|
+
end
|
110
|
+
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
@@ -45,6 +45,8 @@ class Mechanize::HTTP::WWWAuthenticateParser
|
|
45
45
|
|
46
46
|
challenges << challenge
|
47
47
|
next
|
48
|
+
else
|
49
|
+
scheme.capitalize!
|
48
50
|
end
|
49
51
|
|
50
52
|
next unless space
|
@@ -55,6 +57,8 @@ class Mechanize::HTTP::WWWAuthenticateParser
|
|
55
57
|
pos = @scanner.pos
|
56
58
|
name, value = auth_param
|
57
59
|
|
60
|
+
name.downcase! if name =~ /^realm$/i
|
61
|
+
|
58
62
|
unless name then
|
59
63
|
challenge.params = params
|
60
64
|
challenges << challenge
|
data/lib/mechanize/page.rb
CHANGED
@@ -84,7 +84,7 @@ class Mechanize::Page < Mechanize::File
|
|
84
84
|
|
85
85
|
if @parser
|
86
86
|
parser_encoding = @parser.encoding
|
87
|
-
if
|
87
|
+
if parser_encoding && encoding && parser_encoding.casecmp(encoding) != 0
|
88
88
|
# lazy reinitialize the parser with the new encoding
|
89
89
|
@parser = nil
|
90
90
|
end
|
@@ -7,13 +7,18 @@ class Mechanize::ResponseCodeError < Mechanize::Error
|
|
7
7
|
attr_reader :response_code
|
8
8
|
attr_reader :page
|
9
9
|
|
10
|
-
def initialize(page)
|
10
|
+
def initialize(page, message = nil)
|
11
|
+
super message
|
12
|
+
|
11
13
|
@page = page
|
12
14
|
@response_code = page.code.to_s
|
13
15
|
end
|
14
16
|
|
15
17
|
def to_s
|
16
|
-
|
18
|
+
response_class = Net::HTTPResponse::CODE_TO_OBJ[@response_code]
|
19
|
+
out = "#{@response_code} => #{response_class} "
|
20
|
+
out << "for #{@page.uri} " if @page.respond_to? :uri # may be HTTPResponse
|
21
|
+
out << "-- #{super}"
|
17
22
|
end
|
18
23
|
|
19
24
|
alias inspect to_s
|
data/lib/mechanize/test_case.rb
CHANGED
@@ -120,21 +120,24 @@ end
|
|
120
120
|
|
121
121
|
class BasicAuthServlet < WEBrick::HTTPServlet::AbstractServlet
|
122
122
|
def do_GET(req,res)
|
123
|
-
htpd =
|
124
|
-
|
123
|
+
htpd = nil
|
124
|
+
Tempfile.open 'dot.htpasswd' do |io|
|
125
|
+
htpd = WEBrick::HTTPAuth::Htpasswd.new(io.path)
|
126
|
+
htpd.set_passwd('Blah', 'user', 'pass')
|
127
|
+
end
|
128
|
+
|
125
129
|
authenticator = WEBrick::HTTPAuth::BasicAuth.new({
|
126
130
|
:UserDB => htpd,
|
127
131
|
:Realm => 'Blah',
|
128
132
|
:Logger => Logger.new(nil)
|
129
|
-
}
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
FileUtils.rm('dot.htpasswd')
|
133
|
+
})
|
134
|
+
|
135
|
+
begin
|
136
|
+
authenticator.authenticate(req,res)
|
137
|
+
res.body = 'You are authenticated'
|
138
|
+
rescue WEBrick::HTTPStatus::Unauthorized
|
139
|
+
res.status = 401
|
140
|
+
end
|
138
141
|
end
|
139
142
|
alias :do_POST :do_GET
|
140
143
|
end
|
@@ -148,29 +151,34 @@ class ContentTypeServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
148
151
|
end
|
149
152
|
|
150
153
|
class DigestAuthServlet < WEBrick::HTTPServlet::AbstractServlet
|
151
|
-
htpd =
|
152
|
-
|
154
|
+
htpd = nil
|
155
|
+
|
156
|
+
Tempfile.open 'digest.htpasswd' do |io|
|
157
|
+
htpd = WEBrick::HTTPAuth::Htdigest.new(io.path)
|
158
|
+
htpd.set_passwd('Blah', 'user', 'pass')
|
159
|
+
end
|
160
|
+
|
153
161
|
@@authenticator = WEBrick::HTTPAuth::DigestAuth.new({
|
154
162
|
:UserDB => htpd,
|
155
163
|
:Realm => 'Blah',
|
156
164
|
:Algorithm => 'MD5',
|
157
165
|
:Logger => Logger.new(nil)
|
158
|
-
}
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
166
|
+
})
|
167
|
+
|
168
|
+
def do_GET(req,res)
|
169
|
+
def req.request_time; Time.now; end
|
170
|
+
def req.request_uri; '/digest_auth'; end
|
171
|
+
def req.request_method; "GET"; end
|
172
|
+
|
173
|
+
begin
|
174
|
+
@@authenticator.authenticate(req,res)
|
175
|
+
res.body = 'You are authenticated'
|
176
|
+
rescue WEBrick::HTTPStatus::Unauthorized
|
177
|
+
res.status = 401
|
178
|
+
end
|
179
|
+
FileUtils.rm('digest.htpasswd') if File.exists?('digest.htpasswd')
|
180
|
+
end
|
181
|
+
alias :do_POST :do_GET
|
174
182
|
end
|
175
183
|
|
176
184
|
class FileUploadServlet < WEBrick::HTTPServlet::AbstractServlet
|
@@ -1,3 +1,22 @@
|
|
1
1
|
class Mechanize::UnauthorizedError < Mechanize::ResponseCodeError
|
2
|
+
|
3
|
+
attr_reader :challenges
|
4
|
+
|
5
|
+
def initialize page, challenges, message
|
6
|
+
super page, message
|
7
|
+
@challenges = challenges
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
out = super
|
12
|
+
|
13
|
+
if @challenges then
|
14
|
+
realms = @challenges.map { |challenge| challenge.realm_name }.join ', '
|
15
|
+
out << " -- available realms: #{realms}"
|
16
|
+
end
|
17
|
+
|
18
|
+
out
|
19
|
+
end
|
20
|
+
|
2
21
|
end
|
3
22
|
|
data/test/test_mechanize.rb
CHANGED
@@ -41,9 +41,17 @@ class TestMechanize < Mechanize::TestCase
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def test_basic_auth
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
_, err = capture_io do
|
45
|
+
@mech.basic_auth 'user', 'pass' # warns
|
46
|
+
end
|
47
|
+
|
48
|
+
line = __LINE__ - 3
|
49
|
+
file = File.basename __FILE__
|
50
|
+
|
51
|
+
assert_match "#{file} line #{line}", err
|
52
|
+
|
53
|
+
page = @mech.get @uri + '/basic_auth'
|
54
|
+
assert_equal 'You are authenticated', page.body
|
47
55
|
end
|
48
56
|
|
49
57
|
def test_cert_key_file
|
@@ -326,7 +334,11 @@ but not <a href="/" rel="me nofollow">this</a>!
|
|
326
334
|
|
327
335
|
def test_get_HTTP
|
328
336
|
page = @mech.get('HTTP://localhost/', { :q => 'hello' })
|
329
|
-
|
337
|
+
|
338
|
+
assert_kind_of URI::HTTP, page.uri
|
339
|
+
assert_equal 'localhost', page.uri.host
|
340
|
+
assert_equal 80, page.uri.port
|
341
|
+
assert_equal '/?q=hello', page.uri.request_uri
|
330
342
|
end
|
331
343
|
|
332
344
|
def test_get_anchor
|
@@ -340,19 +352,19 @@ but not <a href="/" rel="me nofollow">this</a>!
|
|
340
352
|
end
|
341
353
|
end
|
342
354
|
|
343
|
-
def
|
344
|
-
@mech.
|
355
|
+
def test_get_auth_bad
|
356
|
+
@mech.add_auth(@uri, 'aaron', 'aaron')
|
345
357
|
|
346
358
|
e = assert_raises Mechanize::UnauthorizedError do
|
347
|
-
@mech.get(
|
359
|
+
@mech.get(@uri + '/basic_auth')
|
348
360
|
end
|
349
361
|
|
350
362
|
assert_equal("401", e.response_code)
|
351
363
|
end
|
352
364
|
|
353
|
-
def
|
365
|
+
def test_get_auth_none
|
354
366
|
e = assert_raises Mechanize::UnauthorizedError do
|
355
|
-
@mech.get(
|
367
|
+
@mech.get(@uri + '/basic_auth')
|
356
368
|
end
|
357
369
|
|
358
370
|
assert_equal("401", e.response_code)
|
@@ -374,7 +386,7 @@ but not <a href="/" rel="me nofollow">this</a>!
|
|
374
386
|
def test_get_digest_auth
|
375
387
|
block_called = false
|
376
388
|
|
377
|
-
@mech.
|
389
|
+
@mech.add_auth(@uri, 'user', 'pass')
|
378
390
|
|
379
391
|
@mech.pre_connect_hooks << lambda { |_, request|
|
380
392
|
block_called = true
|
@@ -383,7 +395,7 @@ but not <a href="/" rel="me nofollow">this</a>!
|
|
383
395
|
end
|
384
396
|
}
|
385
397
|
|
386
|
-
page = @mech.get(
|
398
|
+
page = @mech.get(@uri + '/digest_auth')
|
387
399
|
assert_equal('You are authenticated', page.body)
|
388
400
|
assert block_called
|
389
401
|
end
|
@@ -869,15 +881,15 @@ but not <a href="/" rel="me nofollow">this</a>!
|
|
869
881
|
assert_equal "gender=female", requests.first.body
|
870
882
|
end
|
871
883
|
|
872
|
-
def
|
884
|
+
def test_post_auth
|
873
885
|
requests = []
|
874
886
|
|
875
887
|
@mech.pre_connect_hooks << proc { |agent, request|
|
876
888
|
requests << request.class
|
877
889
|
}
|
878
890
|
|
879
|
-
@mech.
|
880
|
-
page = @mech.post(
|
891
|
+
@mech.add_auth(@uri, 'user', 'pass')
|
892
|
+
page = @mech.post(@uri + '/basic_auth')
|
881
893
|
assert_equal('You are authenticated', page.body)
|
882
894
|
assert_equal(2, requests.length)
|
883
895
|
r1 = requests[0]
|
@@ -164,7 +164,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
164
164
|
@agent.fetch uri
|
165
165
|
end
|
166
166
|
|
167
|
-
|
167
|
+
assert_match "404 => Net::HTTPNotFound for #{uri}", e.message
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
@@ -436,8 +436,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
436
436
|
end
|
437
437
|
|
438
438
|
def test_request_auth_basic
|
439
|
-
@agent.
|
440
|
-
@agent.password = 'password'
|
439
|
+
@agent.add_auth @uri, 'user', 'password'
|
441
440
|
|
442
441
|
auth_realm @uri, 'Basic', :basic
|
443
442
|
|
@@ -447,8 +446,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
447
446
|
end
|
448
447
|
|
449
448
|
def test_request_auth_digest
|
450
|
-
@agent.
|
451
|
-
@agent.password = 'password'
|
449
|
+
@agent.add_auth @uri, 'user', 'password'
|
452
450
|
|
453
451
|
realm = auth_realm @uri, 'Digest', :digest
|
454
452
|
@agent.digest_challenges[realm] = 'Digest realm=r, qop="auth"'
|
@@ -460,8 +458,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
460
458
|
end
|
461
459
|
|
462
460
|
def test_request_auth_iis_digest
|
463
|
-
@agent.
|
464
|
-
@agent.password = 'password'
|
461
|
+
@agent.add_auth @uri, 'user', 'password'
|
465
462
|
|
466
463
|
realm = auth_realm @uri, 'Digest', :digest
|
467
464
|
@agent.digest_challenges[realm] = 'Digest realm=r, qop="auth"'
|
@@ -672,9 +669,9 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
672
669
|
end
|
673
670
|
|
674
671
|
def test_response_authenticate
|
672
|
+
@agent.add_auth @uri, 'user', 'password'
|
673
|
+
|
675
674
|
@res.instance_variable_set :@header, 'www-authenticate' => ['Basic realm=r']
|
676
|
-
@agent.user = 'user'
|
677
|
-
@agent.password = 'password'
|
678
675
|
|
679
676
|
@agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
|
680
677
|
|
@@ -684,10 +681,10 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
684
681
|
end
|
685
682
|
|
686
683
|
def test_response_authenticate_digest
|
684
|
+
@agent.add_auth @uri, 'user', 'password'
|
685
|
+
|
687
686
|
@res.instance_variable_set(:@header,
|
688
687
|
'www-authenticate' => ['Digest realm=r'])
|
689
|
-
@agent.user = 'user'
|
690
|
-
@agent.password = 'password'
|
691
688
|
|
692
689
|
@agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
|
693
690
|
|
@@ -700,12 +697,11 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
700
697
|
end
|
701
698
|
|
702
699
|
def test_response_authenticate_digest_iis
|
700
|
+
@agent.add_auth @uri, 'user', 'password'
|
701
|
+
|
703
702
|
@res.instance_variable_set(:@header,
|
704
703
|
'www-authenticate' => ['Digest realm=r'],
|
705
704
|
'server' => ['Microsoft-IIS'])
|
706
|
-
@agent.user = 'user'
|
707
|
-
@agent.password = 'password'
|
708
|
-
|
709
705
|
@agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
|
710
706
|
|
711
707
|
base_uri = @uri + '/'
|
@@ -714,11 +710,11 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
714
710
|
end
|
715
711
|
|
716
712
|
def test_response_authenticate_multiple
|
713
|
+
@agent.add_auth @uri, 'user', 'password'
|
714
|
+
|
717
715
|
@res.instance_variable_set(:@header,
|
718
716
|
'www-authenticate' =>
|
719
717
|
['Basic realm=r, Digest realm=r'])
|
720
|
-
@agent.user = 'user'
|
721
|
-
@agent.password = 'password'
|
722
718
|
|
723
719
|
@agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
|
724
720
|
|
@@ -729,25 +725,39 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
729
725
|
assert_empty @agent.authenticate_methods[base_uri][:basic]
|
730
726
|
end
|
731
727
|
|
728
|
+
def test_response_authenticate_no_credentials
|
729
|
+
@res.instance_variable_set :@header, 'www-authenticate' => ['Basic realm=r']
|
730
|
+
|
731
|
+
e = assert_raises Mechanize::UnauthorizedError do
|
732
|
+
@agent.response_authenticate @res, fake_page, @uri, @req, {}, nil, nil
|
733
|
+
end
|
734
|
+
|
735
|
+
assert_match 'no credentials', e.message
|
736
|
+
assert_match 'available realms: r', e.message
|
737
|
+
end
|
738
|
+
|
732
739
|
def test_response_authenticate_no_www_authenticate
|
733
|
-
|
734
|
-
|
735
|
-
|
740
|
+
@agent.add_auth @uri, 'user', 'password'
|
741
|
+
|
742
|
+
denied_uri = URI('http://example/denied')
|
743
|
+
|
744
|
+
denied = page denied_uri, 'text/html', '', 401
|
736
745
|
|
737
746
|
e = assert_raises Mechanize::UnauthorizedError do
|
738
747
|
@agent.response_authenticate @res, denied, @uri, @req, {}, nil, nil
|
739
748
|
end
|
740
749
|
|
741
|
-
assert_equal
|
750
|
+
assert_equal "401 => Net::HTTPUnauthorized for #{denied_uri} -- " \
|
751
|
+
"WWW-Authenticate header missing in response",
|
752
|
+
e.message
|
742
753
|
end
|
743
754
|
|
744
755
|
def test_response_authenticate_ntlm
|
745
756
|
@uri += '/ntlm'
|
757
|
+
@agent.add_auth @uri, 'user', 'password'
|
758
|
+
|
746
759
|
@res.instance_variable_set(:@header,
|
747
760
|
'www-authenticate' => ['Negotiate, NTLM'])
|
748
|
-
@agent.user = 'user'
|
749
|
-
@agent.password = 'password'
|
750
|
-
@agent.domain = 'domain'
|
751
761
|
|
752
762
|
page = @agent.response_authenticate @res, nil, @uri, @req, {}, nil, nil
|
753
763
|
|
@@ -755,8 +765,8 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
755
765
|
end
|
756
766
|
|
757
767
|
def test_response_authenticate_unknown
|
758
|
-
@agent.
|
759
|
-
|
768
|
+
@agent.add_auth @uri, 'user', 'password'
|
769
|
+
|
760
770
|
page = Mechanize::File.new nil, nil, nil, 401
|
761
771
|
@res.instance_variable_set(:@header,
|
762
772
|
'www-authenticate' => ['Unknown realm=r'])
|
@@ -1229,6 +1239,21 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
|
|
1229
1239
|
end
|
1230
1240
|
end
|
1231
1241
|
|
1242
|
+
def test_file_response_content_type
|
1243
|
+
Tempfile.open ['pi','.nothtml'] do |tempfile|
|
1244
|
+
res = Mechanize::FileResponse.new tempfile.path
|
1245
|
+
assert_equal nil, res['content-type']
|
1246
|
+
end
|
1247
|
+
Tempfile.open ['pi','.xhtml'] do |tempfile|
|
1248
|
+
res = Mechanize::FileResponse.new tempfile.path
|
1249
|
+
assert_equal 'text/html', res['content-type']
|
1250
|
+
end
|
1251
|
+
Tempfile.open ['pi','.html'] do |tempfile|
|
1252
|
+
res = Mechanize::FileResponse.new tempfile.path
|
1253
|
+
assert_equal 'text/html', res['Content-Type']
|
1254
|
+
end
|
1255
|
+
end
|
1256
|
+
|
1232
1257
|
def test_response_read_large
|
1233
1258
|
@agent.max_file_buffer = 10240
|
1234
1259
|
|
@@ -35,5 +35,15 @@ class TestMechanizeHttpAuthChallenge < Mechanize::TestCase
|
|
35
35
|
assert_equal 'unknown HTTP authentication scheme Unknown', e.message
|
36
36
|
end
|
37
37
|
|
38
|
+
def test_realm_name
|
39
|
+
assert_equal 'r', @challenge.realm_name
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_realm_name_ntlm
|
43
|
+
challenge = @AC.new 'Negotiate, NTLM'
|
44
|
+
|
45
|
+
assert_nil challenge.realm_name
|
46
|
+
end
|
47
|
+
|
38
48
|
end
|
39
49
|
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'mechanize/test_case'
|
4
|
+
|
5
|
+
class TestMechanizeHttpAuthStore < Mechanize::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
|
10
|
+
@store = Mechanize::HTTP::AuthStore.new
|
11
|
+
|
12
|
+
@uri = URI.parse 'http://example/'
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_add_auth
|
16
|
+
@store.add_auth @uri + '/path', 'user', 'pass'
|
17
|
+
|
18
|
+
expected = {
|
19
|
+
@uri => {
|
20
|
+
nil => ['user', 'pass', nil],
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
assert_equal expected, @store.auth_accounts
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_add_auth_domain
|
28
|
+
@store.add_auth @uri + '/path', 'user1', 'pass', nil, 'domain'
|
29
|
+
|
30
|
+
expected = {
|
31
|
+
@uri => {
|
32
|
+
nil => %w[user1 pass domain],
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
assert_equal expected, @store.auth_accounts
|
37
|
+
|
38
|
+
e = assert_raises ArgumentError do
|
39
|
+
@store.add_auth @uri, 'user3', 'pass', 'realm', 'domain'
|
40
|
+
end
|
41
|
+
|
42
|
+
assert_equal 'NTLM domain given with realm which NTLM does not use',
|
43
|
+
e.message
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_add_auth_realm
|
47
|
+
@store.add_auth @uri, 'user1', 'pass'
|
48
|
+
@store.add_auth @uri, 'user2', 'pass', 'realm'
|
49
|
+
|
50
|
+
expected = {
|
51
|
+
@uri => {
|
52
|
+
nil => ['user1', 'pass', nil],
|
53
|
+
'realm' => ['user2', 'pass', nil],
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
assert_equal expected, @store.auth_accounts
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_add_default_auth
|
61
|
+
_, err = capture_io do
|
62
|
+
@store.add_default_auth 'user', 'pass'
|
63
|
+
end
|
64
|
+
|
65
|
+
expected = ['user', 'pass', nil]
|
66
|
+
|
67
|
+
assert_equal expected, @store.default_auth
|
68
|
+
|
69
|
+
assert_match 'DISCLOSURE WITHOUT YOUR KNOWLEDGE', err
|
70
|
+
|
71
|
+
capture_io do
|
72
|
+
@store.add_default_auth 'user', 'pass', 'realm'
|
73
|
+
end
|
74
|
+
|
75
|
+
expected = %w[user pass realm]
|
76
|
+
|
77
|
+
assert_equal expected, @store.default_auth
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_credentials_eh
|
81
|
+
challenges = [
|
82
|
+
Mechanize::HTTP::AuthChallenge.new('Basic', 'realm' => 'r'),
|
83
|
+
Mechanize::HTTP::AuthChallenge.new('Digest', 'realm' => 'r'),
|
84
|
+
]
|
85
|
+
|
86
|
+
refute @store.credentials? @uri, challenges
|
87
|
+
|
88
|
+
@store.add_auth @uri, 'user', 'pass'
|
89
|
+
|
90
|
+
assert @store.credentials? @uri, challenges
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_credentials_for
|
94
|
+
assert_nil @store.credentials_for(@uri, 'realm')
|
95
|
+
|
96
|
+
@store.add_auth @uri, 'user', 'pass', 'realm'
|
97
|
+
|
98
|
+
assert_equal ['user', 'pass', nil], @store.credentials_for(@uri, 'realm')
|
99
|
+
assert_nil @store.credentials_for(@uri, 'other')
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_credentials_for_default
|
103
|
+
assert_nil @store.credentials_for(@uri, 'realm')
|
104
|
+
|
105
|
+
capture_io do
|
106
|
+
@store.add_default_auth 'user1', 'pass'
|
107
|
+
end
|
108
|
+
|
109
|
+
assert_equal ['user1', 'pass', nil], @store.credentials_for(@uri, 'realm')
|
110
|
+
|
111
|
+
@store.add_auth @uri, 'user2', 'pass'
|
112
|
+
|
113
|
+
assert_equal ['user2', 'pass', nil], @store.credentials_for(@uri, 'realm')
|
114
|
+
assert_equal ['user2', 'pass', nil], @store.credentials_for(@uri, 'other')
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_credentials_for_no_realm
|
118
|
+
@store.add_auth @uri, 'user', 'pass' # no realm set
|
119
|
+
|
120
|
+
assert_equal ['user', 'pass', nil], @store.credentials_for(@uri, 'realm')
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_credentials_for_realm
|
124
|
+
@store.add_auth @uri, 'user1', 'pass'
|
125
|
+
@store.add_auth @uri, 'user2', 'pass', 'realm'
|
126
|
+
|
127
|
+
assert_equal ['user2', 'pass', nil], @store.credentials_for(@uri, 'realm')
|
128
|
+
assert_equal ['user1', 'pass', nil], @store.credentials_for(@uri, 'other')
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_credentials_for_path
|
132
|
+
@store.add_auth @uri, 'user', 'pass', 'realm'
|
133
|
+
|
134
|
+
uri = @uri + '/path'
|
135
|
+
|
136
|
+
assert_equal ['user', 'pass', nil], @store.credentials_for(uri, 'realm')
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_remove_auth
|
140
|
+
@store.remove_auth @uri
|
141
|
+
|
142
|
+
assert_empty @store.auth_accounts
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_remove_auth_both
|
146
|
+
@store.add_auth @uri, 'user1', 'pass'
|
147
|
+
@store.add_auth @uri, 'user2', 'pass', 'realm'
|
148
|
+
|
149
|
+
uri = @uri + '/path'
|
150
|
+
|
151
|
+
@store.remove_auth uri
|
152
|
+
|
153
|
+
assert_empty @store.auth_accounts
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_remove_auth_realm
|
157
|
+
@store.add_auth @uri, 'user1', 'pass'
|
158
|
+
@store.add_auth @uri, 'user2', 'pass', 'realm'
|
159
|
+
|
160
|
+
@store.remove_auth @uri, 'realm'
|
161
|
+
|
162
|
+
expected = {
|
163
|
+
@uri => {
|
164
|
+
nil => ['user1', 'pass', nil]
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
assert_equal expected, @store.auth_accounts
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
@@ -52,20 +52,20 @@ class TestMechanizeHttpWwwAuthenticateParser < Mechanize::TestCase
|
|
52
52
|
|
53
53
|
def test_parse_multiple
|
54
54
|
expected = [
|
55
|
-
challenge('Basic',
|
56
|
-
challenge('Digest', { 'realm' => '
|
55
|
+
challenge('Basic', { 'realm' => 'foo' }),
|
56
|
+
challenge('Digest', { 'realm' => 'bar' }),
|
57
57
|
]
|
58
58
|
|
59
|
-
assert_equal expected, @parser.parse('Basic realm=foo, Digest realm=
|
59
|
+
assert_equal expected, @parser.parse('Basic realm=foo, Digest realm=bar')
|
60
60
|
end
|
61
61
|
|
62
62
|
def test_parse_multiple_blank
|
63
63
|
expected = [
|
64
|
-
challenge('Basic',
|
65
|
-
challenge('Digest', { 'realm' => '
|
64
|
+
challenge('Basic', { 'realm' => 'foo' }),
|
65
|
+
challenge('Digest', { 'realm' => 'bar' }),
|
66
66
|
]
|
67
67
|
|
68
|
-
assert_equal expected, @parser.parse('Basic realm=foo,, Digest realm=
|
68
|
+
assert_equal expected, @parser.parse('Basic realm=foo,, Digest realm=bar')
|
69
69
|
end
|
70
70
|
|
71
71
|
def test_parse_ntlm_init
|
@@ -84,6 +84,22 @@ class TestMechanizeHttpWwwAuthenticateParser < Mechanize::TestCase
|
|
84
84
|
assert_equal expected, @parser.parse('NTLM foo=')
|
85
85
|
end
|
86
86
|
|
87
|
+
def test_parse_realm_uppercase
|
88
|
+
expected = [
|
89
|
+
challenge('Basic', { 'realm' => 'foo' }),
|
90
|
+
]
|
91
|
+
|
92
|
+
assert_equal expected, @parser.parse('Basic ReAlM=foo')
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_parse_scheme_uppercase
|
96
|
+
expected = [
|
97
|
+
challenge('Basic', { 'realm' => 'foo' }),
|
98
|
+
]
|
99
|
+
|
100
|
+
assert_equal expected, @parser.parse('BaSiC realm=foo')
|
101
|
+
end
|
102
|
+
|
87
103
|
def test_quoted_string
|
88
104
|
@parser.scanner = StringScanner.new '"text"'
|
89
105
|
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mechanize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: "2.
|
8
|
+
- 4
|
9
|
+
version: "2.4"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Eric Hodel
|
@@ -18,9 +18,9 @@ bindir: bin
|
|
18
18
|
cert_chain:
|
19
19
|
- |
|
20
20
|
-----BEGIN CERTIFICATE-----
|
21
|
-
|
21
|
+
MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRAwDgYDVQQDDAdkcmJy
|
22
22
|
YWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZFgNu
|
23
|
-
|
23
|
+
ZXQwHhcNMTIwMjI4MTc1NDI1WhcNMTMwMjI3MTc1NDI1WjBBMRAwDgYDVQQDDAdk
|
24
24
|
cmJyYWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZ
|
25
25
|
FgNuZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbbgLrGLGIDE76
|
26
26
|
LV/cvxdEzCuYuS3oG9PrSZnuDweySUfdp/so0cDq+j8bqy6OzZSw07gdjwFMSd6J
|
@@ -28,17 +28,18 @@ cert_chain:
|
|
28
28
|
Gj/okWrQl0NjYOYBpDi+9PPmaH2RmLJu0dB/NylsDnW5j6yN1BEI8MfJRR+HRKZY
|
29
29
|
mUtgzBwF1V4KIZQ8EuL6I/nHVu07i6IkrpAgxpXUfdJQJi0oZAqXurAV3yTxkFwd
|
30
30
|
g62YrrW26mDe+pZBzR6bpLE+PmXCzz7UxUq3AE0gPHbiMXie3EFE0oxnsU3lIduh
|
31
|
-
|
31
|
+
sCANiQ8BAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
|
32
32
|
BBS5k4Z75VSpdM0AclG2UvzFA/VW5DAfBgNVHREEGDAWgRRkcmJyYWluQHNlZ21l
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
bnQ3Lm5ldDAfBgNVHRIEGDAWgRRkcmJyYWluQHNlZ21lbnQ3Lm5ldDANBgkqhkiG
|
34
|
+
9w0BAQUFAAOCAQEAPeWzFnrcvC6eVzdlhmjUub2s6qieBkongKRDHQz5MEeQv4LS
|
35
|
+
SARnoHY+uCAVL/1xGAhmpzqQ3fJGWK9eBacW/e8E5GF9xQcV3mE1bA0WNaiDlX5j
|
36
|
+
U2aI+ZGSblqvHUCxKBHR1s7UMHsbz1saOmgdRTyPx0juJs68ocbUTeYBLWu9V4KP
|
37
|
+
zdGAG2JXO2gONg3b4tYDvpBLbry+KOX27iAJulUaH9TiTOULL4ITJVFsK0mYVqmR
|
38
|
+
Q8Tno9S3e4XGGP1ZWfLrTWEJbavFfhGHut2iMRwfC7s/YILAHNATopaJdH9DNpd1
|
39
|
+
U81zGHMUBOvz/VGT6wJwYJ3emS2nfA2NOHFfgA==
|
39
40
|
-----END CERTIFICATE-----
|
40
41
|
|
41
|
-
date: 2012-
|
42
|
+
date: 2012-04-21 00:00:00 Z
|
42
43
|
dependencies:
|
43
44
|
- !ruby/object:Gem::Dependency
|
44
45
|
name: net-http-digest_auth
|
@@ -201,11 +202,11 @@ dependencies:
|
|
201
202
|
requirements:
|
202
203
|
- - ~>
|
203
204
|
- !ruby/object:Gem::Version
|
204
|
-
hash:
|
205
|
+
hash: 21
|
205
206
|
segments:
|
206
207
|
- 2
|
207
|
-
-
|
208
|
-
version: "2.
|
208
|
+
- 11
|
209
|
+
version: "2.11"
|
209
210
|
type: :development
|
210
211
|
version_requirements: *id008
|
211
212
|
- !ruby/object:Gem::Dependency
|
@@ -231,11 +232,11 @@ dependencies:
|
|
231
232
|
requirements:
|
232
233
|
- - ~>
|
233
234
|
- !ruby/object:Gem::Version
|
234
|
-
hash:
|
235
|
+
hash: 35
|
235
236
|
segments:
|
236
237
|
- 2
|
237
|
-
-
|
238
|
-
version: "2.
|
238
|
+
- 16
|
239
|
+
version: "2.16"
|
239
240
|
type: :development
|
240
241
|
version_requirements: *id010
|
241
242
|
description: |-
|
@@ -254,11 +255,11 @@ executables: []
|
|
254
255
|
extensions: []
|
255
256
|
|
256
257
|
extra_rdoc_files:
|
257
|
-
- Manifest.txt
|
258
258
|
- CHANGELOG.rdoc
|
259
259
|
- EXAMPLES.rdoc
|
260
260
|
- GUIDE.rdoc
|
261
261
|
- LICENSE.rdoc
|
262
|
+
- Manifest.txt
|
262
263
|
- README.rdoc
|
263
264
|
files:
|
264
265
|
- .autotest
|
@@ -309,6 +310,7 @@ files:
|
|
309
310
|
- lib/mechanize/http/agent.rb
|
310
311
|
- lib/mechanize/http/auth_challenge.rb
|
311
312
|
- lib/mechanize/http/auth_realm.rb
|
313
|
+
- lib/mechanize/http/auth_store.rb
|
312
314
|
- lib/mechanize/http/content_disposition_parser.rb
|
313
315
|
- lib/mechanize/http/www_authenticate_parser.rb
|
314
316
|
- lib/mechanize/image.rb
|
@@ -408,6 +410,7 @@ files:
|
|
408
410
|
- test/test_mechanize_http_agent.rb
|
409
411
|
- test/test_mechanize_http_auth_challenge.rb
|
410
412
|
- test/test_mechanize_http_auth_realm.rb
|
413
|
+
- test/test_mechanize_http_auth_store.rb
|
411
414
|
- test/test_mechanize_http_content_disposition_parser.rb
|
412
415
|
- test/test_mechanize_http_www_authenticate_parser.rb
|
413
416
|
- test/test_mechanize_image.rb
|
@@ -459,7 +462,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
459
462
|
requirements: []
|
460
463
|
|
461
464
|
rubyforge_project: mechanize
|
462
|
-
rubygems_version: 1.8.
|
465
|
+
rubygems_version: 1.8.21
|
463
466
|
signing_key:
|
464
467
|
specification_version: 3
|
465
468
|
summary: The Mechanize library is used for automating interaction with websites
|
@@ -490,6 +493,7 @@ test_files:
|
|
490
493
|
- test/test_mechanize_http_agent.rb
|
491
494
|
- test/test_mechanize_http_auth_challenge.rb
|
492
495
|
- test/test_mechanize_http_auth_realm.rb
|
496
|
+
- test/test_mechanize_http_auth_store.rb
|
493
497
|
- test/test_mechanize_http_content_disposition_parser.rb
|
494
498
|
- test/test_mechanize_http_www_authenticate_parser.rb
|
495
499
|
- test/test_mechanize_image.rb
|
metadata.gz.sig
CHANGED
Binary file
|