OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <set> | |
6 #include <string> | 5 #include <string> |
7 | 6 |
8 #include "base/memory/ref_counted.h" | |
9 #include "base/memory/scoped_ptr.h" | |
10 #include "base/strings/string_util.h" | |
11 #include "net/base/net_errors.h" | |
12 #include "net/dns/mock_host_resolver.h" | |
13 #include "net/http/http_auth.h" | 7 #include "net/http/http_auth.h" |
14 #include "net/http/http_auth_challenge_tokenizer.h" | |
15 #include "net/http/http_auth_filter.h" | |
16 #include "net/http/http_auth_handler.h" | |
17 #include "net/http/http_auth_handler_factory.h" | |
18 #include "net/http/http_auth_handler_mock.h" | |
19 #include "net/http/http_auth_scheme_set.h" | |
20 #include "net/http/http_response_headers.h" | |
21 #include "net/http/http_util.h" | |
22 #include "net/http/mock_allow_url_security_manager.h" | |
23 #include "testing/gtest/include/gtest/gtest.h" | 8 #include "testing/gtest/include/gtest/gtest.h" |
24 | 9 |
25 namespace net { | 10 namespace net { |
26 | 11 |
27 namespace { | |
28 | |
29 HttpAuthHandlerMock* CreateMockHandler(bool expect_multiple_challenges) { | |
30 HttpAuthHandlerMock* auth_handler = new HttpAuthHandlerMock(); | |
31 auth_handler->set_expect_multiple_challenges(expect_multiple_challenges); | |
32 std::string challenge_text = "Mock"; | |
33 HttpAuthChallengeTokenizer challenge(challenge_text.begin(), | |
34 challenge_text.end()); | |
35 GURL origin("www.example.com"); | |
36 EXPECT_EQ(OK, auth_handler->HandleInitialChallenge( | |
37 challenge, HttpAuth::AUTH_SERVER, origin, BoundNetLog())); | |
38 return auth_handler; | |
39 } | |
40 | |
41 HttpResponseHeaders* HeadersFromResponseText(const std::string& response) { | |
42 return new HttpResponseHeaders( | |
43 HttpUtil::AssembleRawHeaders(response.c_str(), response.length())); | |
44 } | |
45 | |
46 HttpAuth::AuthorizationResult HandleChallengeResponse( | |
47 bool expect_multiple_challenges, | |
48 const std::string& headers_text, | |
49 std::string* challenge_used) { | |
50 scoped_ptr<HttpAuthHandlerMock> mock_handler( | |
51 CreateMockHandler(expect_multiple_challenges)); | |
52 HttpAuthSchemeSet disabled_schemes; | |
53 scoped_refptr<HttpResponseHeaders> headers( | |
54 HeadersFromResponseText(headers_text)); | |
55 return HttpAuth::HandleChallengeResponse(mock_handler.get(), headers.get(), | |
56 HttpAuth::AUTH_SERVER, | |
57 disabled_schemes, challenge_used); | |
58 } | |
59 | |
60 } // namespace | |
61 | |
62 TEST(HttpAuthTest, ChooseBestChallenge) { | |
63 static const struct { | |
64 const char* headers; | |
65 const char* challenge_scheme; | |
66 const char* challenge_realm; | |
67 } tests[] = { | |
68 { | |
69 // Basic is the only challenge type, pick it. | |
70 "Y: Digest realm=\"X\", nonce=\"aaaaaaaaaa\"\n" | |
71 "www-authenticate: Basic realm=\"BasicRealm\"\n", | |
72 | |
73 "basic", "BasicRealm", | |
74 }, | |
75 { | |
76 // Fake is the only challenge type, but it is unsupported. | |
77 "Y: Digest realm=\"FooBar\", nonce=\"aaaaaaaaaa\"\n" | |
78 "www-authenticate: Fake realm=\"FooBar\"\n", | |
79 | |
80 nullptr, "", | |
81 }, | |
82 { | |
83 // Pick Digest over Basic. | |
84 "www-authenticate: Basic realm=\"FooBar\"\n" | |
85 "www-authenticate: Fake realm=\"FooBar\"\n" | |
86 "www-authenticate: nonce=\"aaaaaaaaaa\"\n" | |
87 "www-authenticate: Digest realm=\"DigestRealm\", " | |
88 "nonce=\"aaaaaaaaaa\"\n", | |
89 | |
90 "digest", "DigestRealm", | |
91 }, | |
92 { | |
93 // Handle an empty header correctly. | |
94 "Y: Digest realm=\"X\", nonce=\"aaaaaaaaaa\"\n" | |
95 "www-authenticate:\n", | |
96 | |
97 nullptr, "", | |
98 }, | |
99 { | |
100 // NTLM appears earlier in the list, but HttAuth should pick | |
101 // Negotiate. | |
102 "WWW-Authenticate: NTLM\n" | |
103 "WWW-Authenticate: Negotiate\n", | |
104 | |
105 #if defined(USE_KERBEROS) && !defined(OS_ANDROID) | |
106 // Choose Negotiate over NTLM on all platforms. | |
107 // TODO(ahendrickson): This may be flaky on Linux and OSX as it | |
108 // relies on being able to load one of the known .so files | |
109 // for gssapi. | |
110 "negotiate", | |
111 #else | |
112 // On systems that don't use Kerberos fall back to NTLM. | |
113 "ntlm", | |
114 #endif // defined(USE_KERBEROS) | |
115 "", | |
116 }}; | |
117 GURL origin("http://www.example.com"); | |
118 HttpAuthSchemeSet disabled_schemes; | |
119 MockAllowURLSecurityManager url_security_manager; | |
120 scoped_ptr<HostResolver> host_resolver(new MockHostResolver()); | |
121 scoped_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory( | |
122 HttpAuthHandlerFactory::CreateDefault(host_resolver.get())); | |
123 http_auth_handler_factory->SetURLSecurityManager( | |
124 "negotiate", &url_security_manager); | |
125 | |
126 for (size_t i = 0; i < arraysize(tests); ++i) { | |
127 // Make a HttpResponseHeaders object. | |
128 std::string headers_with_status_line("HTTP/1.1 401 Unauthorized\n"); | |
129 headers_with_status_line += tests[i].headers; | |
130 scoped_refptr<HttpResponseHeaders> headers( | |
131 HeadersFromResponseText(headers_with_status_line)); | |
132 | |
133 scoped_ptr<HttpAuthHandler> handler; | |
134 HttpAuth::ChooseBestChallenge(http_auth_handler_factory.get(), | |
135 headers.get(), | |
136 HttpAuth::AUTH_SERVER, | |
137 origin, | |
138 disabled_schemes, | |
139 BoundNetLog(), | |
140 &handler); | |
141 | |
142 if (handler.get()) { | |
143 EXPECT_EQ(tests[i].challenge_scheme, handler->auth_scheme()); | |
144 EXPECT_STREQ(tests[i].challenge_realm, handler->realm().c_str()); | |
145 } else { | |
146 EXPECT_EQ(nullptr, tests[i].challenge_scheme); | |
147 EXPECT_STREQ("", tests[i].challenge_realm); | |
148 } | |
149 } | |
150 } | |
151 | |
152 TEST(HttpAuthTest, HandleChallengeResponse) { | |
153 std::string challenge_used; | |
154 const char* const kMockChallenge = | |
155 "HTTP/1.1 401 Unauthorized\n" | |
156 "WWW-Authenticate: Mock token_here\n"; | |
157 const char* const kBasicChallenge = | |
158 "HTTP/1.1 401 Unauthorized\n" | |
159 "WWW-Authenticate: Basic realm=\"happy\"\n"; | |
160 const char* const kMissingChallenge = | |
161 "HTTP/1.1 401 Unauthorized\n"; | |
162 const char* const kEmptyChallenge = | |
163 "HTTP/1.1 401 Unauthorized\n" | |
164 "WWW-Authenticate: \n"; | |
165 const char* const kBasicAndMockChallenges = | |
166 "HTTP/1.1 401 Unauthorized\n" | |
167 "WWW-Authenticate: Basic realm=\"happy\"\n" | |
168 "WWW-Authenticate: Mock token_here\n"; | |
169 const char* const kTwoMockChallenges = | |
170 "HTTP/1.1 401 Unauthorized\n" | |
171 "WWW-Authenticate: Mock token_a\n" | |
172 "WWW-Authenticate: Mock token_b\n"; | |
173 | |
174 // Request based schemes should treat any new challenges as rejections of the | |
175 // previous authentication attempt. (There is a slight exception for digest | |
176 // authentication and the stale parameter, but that is covered in the | |
177 // http_auth_handler_digest_unittests). | |
178 EXPECT_EQ( | |
179 HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
180 HandleChallengeResponse(false, kMockChallenge, &challenge_used)); | |
181 EXPECT_EQ("Mock token_here", challenge_used); | |
182 | |
183 EXPECT_EQ( | |
184 HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
185 HandleChallengeResponse(false, kBasicChallenge, &challenge_used)); | |
186 EXPECT_EQ("", challenge_used); | |
187 | |
188 EXPECT_EQ( | |
189 HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
190 HandleChallengeResponse(false, kMissingChallenge, &challenge_used)); | |
191 EXPECT_EQ("", challenge_used); | |
192 | |
193 EXPECT_EQ( | |
194 HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
195 HandleChallengeResponse(false, kEmptyChallenge, &challenge_used)); | |
196 EXPECT_EQ("", challenge_used); | |
197 | |
198 EXPECT_EQ( | |
199 HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
200 HandleChallengeResponse(false, kBasicAndMockChallenges, &challenge_used)); | |
201 EXPECT_EQ("Mock token_here", challenge_used); | |
202 | |
203 EXPECT_EQ( | |
204 HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
205 HandleChallengeResponse(false, kTwoMockChallenges, &challenge_used)); | |
206 EXPECT_EQ("Mock token_a", challenge_used); | |
207 | |
208 // Connection based schemes will treat new auth challenges for the same scheme | |
209 // as acceptance (and continuance) of the current approach. If there are | |
210 // no auth challenges for the same scheme, the response will be treated as | |
211 // a rejection. | |
212 EXPECT_EQ( | |
213 HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
214 HandleChallengeResponse(true, kMockChallenge, &challenge_used)); | |
215 EXPECT_EQ("Mock token_here", challenge_used); | |
216 | |
217 EXPECT_EQ( | |
218 HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
219 HandleChallengeResponse(true, kBasicChallenge, &challenge_used)); | |
220 EXPECT_EQ("", challenge_used); | |
221 | |
222 EXPECT_EQ( | |
223 HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
224 HandleChallengeResponse(true, kMissingChallenge, &challenge_used)); | |
225 EXPECT_EQ("", challenge_used); | |
226 | |
227 EXPECT_EQ( | |
228 HttpAuth::AUTHORIZATION_RESULT_REJECT, | |
229 HandleChallengeResponse(true, kEmptyChallenge, &challenge_used)); | |
230 EXPECT_EQ("", challenge_used); | |
231 | |
232 EXPECT_EQ( | |
233 HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
234 HandleChallengeResponse(true, kBasicAndMockChallenges, &challenge_used)); | |
235 EXPECT_EQ("Mock token_here", challenge_used); | |
236 | |
237 EXPECT_EQ( | |
238 HttpAuth::AUTHORIZATION_RESULT_ACCEPT, | |
239 HandleChallengeResponse(true, kTwoMockChallenges, &challenge_used)); | |
240 EXPECT_EQ("Mock token_a", challenge_used); | |
241 } | |
242 | |
243 TEST(HttpAuthTest, GetChallengeHeaderName) { | 12 TEST(HttpAuthTest, GetChallengeHeaderName) { |
244 std::string name; | 13 std::string name; |
245 | 14 |
246 name = HttpAuth::GetChallengeHeaderName(HttpAuth::AUTH_SERVER); | 15 name = HttpAuth::GetChallengeHeaderName(HttpAuth::AUTH_SERVER); |
247 EXPECT_STREQ("WWW-Authenticate", name.c_str()); | 16 EXPECT_STREQ("WWW-Authenticate", name.c_str()); |
248 | 17 |
249 name = HttpAuth::GetChallengeHeaderName(HttpAuth::AUTH_PROXY); | 18 name = HttpAuth::GetChallengeHeaderName(HttpAuth::AUTH_PROXY); |
250 EXPECT_STREQ("Proxy-Authenticate", name.c_str()); | 19 EXPECT_STREQ("Proxy-Authenticate", name.c_str()); |
251 } | 20 } |
252 | 21 |
253 TEST(HttpAuthTest, GetAuthorizationHeaderName) { | 22 TEST(HttpAuthTest, GetAuthorizationHeaderName) { |
254 std::string name; | 23 std::string name; |
255 | 24 |
256 name = HttpAuth::GetAuthorizationHeaderName(HttpAuth::AUTH_SERVER); | 25 name = HttpAuth::GetAuthorizationHeaderName(HttpAuth::AUTH_SERVER); |
257 EXPECT_STREQ("Authorization", name.c_str()); | 26 EXPECT_STREQ("Authorization", name.c_str()); |
258 | 27 |
259 name = HttpAuth::GetAuthorizationHeaderName(HttpAuth::AUTH_PROXY); | 28 name = HttpAuth::GetAuthorizationHeaderName(HttpAuth::AUTH_PROXY); |
260 EXPECT_STREQ("Proxy-Authorization", name.c_str()); | 29 EXPECT_STREQ("Proxy-Authorization", name.c_str()); |
261 } | 30 } |
262 | 31 |
263 } // namespace net | 32 } // namespace net |
OLD | NEW |