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 "net/http/http_auth_controller.h" | 5 #include "net/http/http_auth_controller.h" |
6 | 6 |
| 7 #include "base/memory/ref_counted.h" |
| 8 #include "base/memory/scoped_ptr.h" |
| 9 #include "base/strings/string_util.h" |
7 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
8 #include "net/base/net_errors.h" | 11 #include "net/base/net_errors.h" |
9 #include "net/base/test_completion_callback.h" | 12 #include "net/base/test_completion_callback.h" |
| 13 #include "net/dns/mock_host_resolver.h" |
| 14 #include "net/http/http_auth.h" |
10 #include "net/http/http_auth_cache.h" | 15 #include "net/http/http_auth_cache.h" |
11 #include "net/http/http_auth_challenge_tokenizer.h" | 16 #include "net/http/http_auth_challenge_tokenizer.h" |
| 17 #include "net/http/http_auth_filter.h" |
| 18 #include "net/http/http_auth_handler.h" |
| 19 #include "net/http/http_auth_handler_factory.h" |
12 #include "net/http/http_auth_handler_mock.h" | 20 #include "net/http/http_auth_handler_mock.h" |
13 #include "net/http/http_request_info.h" | 21 #include "net/http/http_request_info.h" |
14 #include "net/http/http_response_headers.h" | 22 #include "net/http/http_response_headers.h" |
| 23 #include "net/http/http_response_info.h" |
15 #include "net/http/http_util.h" | 24 #include "net/http/http_util.h" |
| 25 #include "net/http/mock_allow_url_security_manager.h" |
16 #include "net/log/net_log.h" | 26 #include "net/log/net_log.h" |
17 #include "testing/gtest/include/gtest/gtest.h" | 27 #include "testing/gtest/include/gtest/gtest.h" |
18 | 28 |
| 29 using ::testing::ValuesIn; |
| 30 |
19 namespace net { | 31 namespace net { |
20 | 32 |
21 namespace { | 33 namespace { |
22 | 34 |
23 enum HandlerRunMode { | 35 enum HandlerRunMode { |
24 RUN_HANDLER_SYNC, | 36 RUN_HANDLER_SYNC, |
25 RUN_HANDLER_ASYNC | 37 RUN_HANDLER_ASYNC |
26 }; | 38 }; |
27 | 39 |
28 enum SchemeState { | 40 enum SchemeState { |
(...skipping 13 matching lines...) Expand all Loading... |
42 // Runs an HttpAuthController with a single round mock auth handler | 54 // Runs an HttpAuthController with a single round mock auth handler |
43 // that returns |handler_rv| on token generation. The handler runs in | 55 // that returns |handler_rv| on token generation. The handler runs in |
44 // async if |run_mode| is RUN_HANDLER_ASYNC. Upon completion, the | 56 // async if |run_mode| is RUN_HANDLER_ASYNC. Upon completion, the |
45 // return value of the controller is tested against | 57 // return value of the controller is tested against |
46 // |expected_controller_rv|. |scheme_state| indicates whether the | 58 // |expected_controller_rv|. |scheme_state| indicates whether the |
47 // auth scheme used should be disabled after this run. | 59 // auth scheme used should be disabled after this run. |
48 void RunSingleRoundAuthTest(HandlerRunMode run_mode, | 60 void RunSingleRoundAuthTest(HandlerRunMode run_mode, |
49 int handler_rv, | 61 int handler_rv, |
50 int expected_controller_rv, | 62 int expected_controller_rv, |
51 SchemeState scheme_state) { | 63 SchemeState scheme_state) { |
| 64 SCOPED_TRACE(::testing::Message() |
| 65 << "run_mode:" << run_mode << " handler_rv:" << handler_rv |
| 66 << " expected_controller_rv:" << expected_controller_rv |
| 67 << " scheme_state:" << scheme_state); |
52 BoundNetLog dummy_log; | 68 BoundNetLog dummy_log; |
53 HttpAuthCache dummy_auth_cache; | 69 HttpAuthCache dummy_auth_cache; |
54 | 70 |
55 HttpRequestInfo request; | 71 HttpRequestInfo request; |
56 request.method = "GET"; | 72 request.method = "GET"; |
57 request.url = GURL("http://example.com"); | 73 request.url = GURL("http://example.com"); |
58 | 74 |
59 scoped_refptr<HttpResponseHeaders> headers(HeadersFromString( | 75 HttpResponseInfo response; |
| 76 response.headers = HeadersFromString( |
60 "HTTP/1.1 407\r\n" | 77 "HTTP/1.1 407\r\n" |
61 "Proxy-Authenticate: MOCK foo\r\n" | 78 "Proxy-Authenticate: MOCK foo\r\n" |
62 "\r\n")); | 79 "\r\n"); |
63 | 80 |
64 HttpAuthHandlerMock::Factory auth_handler_factory; | 81 HttpAuthHandlerMock::Factory auth_handler_factory; |
65 scoped_ptr<HttpAuthHandlerMock> auth_handler(new HttpAuthHandlerMock()); | 82 scoped_ptr<HttpAuthHandlerMock> auth_handler(new HttpAuthHandlerMock()); |
66 auth_handler->SetGenerateExpectation((run_mode == RUN_HANDLER_ASYNC), | 83 auth_handler->SetGenerateExpectation((run_mode == RUN_HANDLER_ASYNC), |
67 handler_rv); | 84 handler_rv); |
68 auth_handler_factory.AddMockHandler(auth_handler.Pass(), | 85 auth_handler_factory.AddMockHandler(auth_handler.Pass(), |
69 HttpAuthHandlerCreateReason::CHALLENGE); | 86 HttpAuthHandlerCreateReason::CHALLENGE); |
70 | 87 |
71 scoped_refptr<HttpAuthController> controller( | 88 scoped_refptr<HttpAuthController> controller( |
72 new HttpAuthController(HttpAuth::AUTH_PROXY, | 89 new HttpAuthController(HttpAuth::AUTH_PROXY, |
73 GURL("http://example.com"), | 90 GURL("http://example.com"), |
74 &dummy_auth_cache, &auth_handler_factory)); | 91 &dummy_auth_cache, &auth_handler_factory)); |
75 ASSERT_EQ(OK, | 92 TestCompletionCallback completion_callback; |
76 controller->HandleAuthChallenge(headers, false, false, dummy_log)); | 93 int result = controller->HandleAuthChallenge( |
| 94 response, completion_callback.callback(), dummy_log); |
| 95 ASSERT_EQ(OK, completion_callback.GetResult(result)); |
77 ASSERT_TRUE(controller->HaveAuthHandler()); | 96 ASSERT_TRUE(controller->HaveAuthHandler()); |
| 97 EXPECT_FALSE(controller->HaveAuth()); |
78 controller->ResetAuth( | 98 controller->ResetAuth( |
79 AuthCredentials(base::ASCIIToUTF16("a"), base::ASCIIToUTF16("b"))); | 99 AuthCredentials(base::ASCIIToUTF16("a"), base::ASCIIToUTF16("b"))); |
80 EXPECT_TRUE(controller->HaveAuth()); | 100 EXPECT_TRUE(controller->HaveAuth()); |
81 | 101 |
82 TestCompletionCallback callback; | 102 TestCompletionCallback callback; |
83 EXPECT_EQ((run_mode == RUN_HANDLER_ASYNC)? ERR_IO_PENDING: | 103 result = controller->MaybeGenerateAuthToken(&request, callback.callback(), |
84 expected_controller_rv, | 104 dummy_log); |
85 controller->MaybeGenerateAuthToken(&request, callback.callback(), | |
86 dummy_log)); | |
87 if (run_mode == RUN_HANDLER_ASYNC) | 105 if (run_mode == RUN_HANDLER_ASYNC) |
88 EXPECT_EQ(expected_controller_rv, callback.WaitForResult()); | 106 ASSERT_EQ(ERR_IO_PENDING, result); |
| 107 EXPECT_EQ(expected_controller_rv, callback.GetResult(result)); |
89 EXPECT_EQ((scheme_state == SCHEME_IS_DISABLED), | 108 EXPECT_EQ((scheme_state == SCHEME_IS_DISABLED), |
90 controller->IsAuthSchemeDisabled("mock")); | 109 controller->IsAuthSchemeDisabled("mock")); |
91 } | 110 } |
92 | 111 |
93 } // namespace | 112 } // namespace |
94 | 113 |
95 // If an HttpAuthHandler returns an error code that indicates a | 114 // If an HttpAuthHandler returns an error code that indicates a |
96 // permanent error, the HttpAuthController should disable the scheme | 115 // permanent error, the HttpAuthController should disable the scheme |
97 // used and retry the request. | 116 // used and retry the request. |
98 TEST(HttpAuthControllerTest, PermanentErrors) { | 117 TEST(HttpAuthControllerTest, PermanentErrors_Sync) { |
99 | |
100 // Run a synchronous handler that returns | |
101 // ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS. We expect a return value | |
102 // of OK from the controller so we can retry the request. | |
103 RunSingleRoundAuthTest(RUN_HANDLER_SYNC, | 118 RunSingleRoundAuthTest(RUN_HANDLER_SYNC, |
104 ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS, | 119 ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS, |
105 OK, SCHEME_IS_DISABLED); | 120 OK, SCHEME_IS_DISABLED); |
| 121 } |
106 | 122 |
107 // Now try an async handler that returns | 123 // Similar to the above test. Now try an async handler that returns |
108 // ERR_MISSING_AUTH_CREDENTIALS. Async and sync handlers invoke | 124 // ERR_MISSING_AUTH_CREDENTIALS. Async and sync handlers invoke different code |
109 // different code paths in HttpAuthController when generating | 125 // paths in HttpAuthController when generating tokens. |
110 // tokens. | 126 TEST(HttpAuthControllerTest, PermanentErrors_Async) { |
111 RunSingleRoundAuthTest(RUN_HANDLER_ASYNC, ERR_MISSING_AUTH_CREDENTIALS, OK, | 127 RunSingleRoundAuthTest(RUN_HANDLER_ASYNC, ERR_MISSING_AUTH_CREDENTIALS, OK, |
112 SCHEME_IS_DISABLED); | 128 SCHEME_IS_DISABLED); |
| 129 } |
113 | 130 |
114 // If a non-permanent error is returned by the handler, then the | 131 // If a non-permanent error is returned by the handler, then the controller |
115 // controller should report it unchanged. | 132 // should leave the scheme enabled. |
| 133 TEST(HttpAuthControllerTest, NonPermanentErrors_Sync) { |
| 134 RunSingleRoundAuthTest(RUN_HANDLER_SYNC, ERR_INVALID_AUTH_CREDENTIALS, |
| 135 ERR_INVALID_AUTH_CREDENTIALS, SCHEME_IS_ENABLED); |
| 136 } |
| 137 |
| 138 // If a non-permanent error is returned by the handler, then the controller |
| 139 // should leave the scheme enabled. |
| 140 TEST(HttpAuthControllerTest, NonPermanentErrors_Async) { |
116 RunSingleRoundAuthTest(RUN_HANDLER_ASYNC, ERR_INVALID_AUTH_CREDENTIALS, | 141 RunSingleRoundAuthTest(RUN_HANDLER_ASYNC, ERR_INVALID_AUTH_CREDENTIALS, |
117 ERR_INVALID_AUTH_CREDENTIALS, SCHEME_IS_ENABLED); | 142 ERR_INVALID_AUTH_CREDENTIALS, SCHEME_IS_ENABLED); |
118 } | 143 } |
119 | 144 |
120 // If an HttpAuthHandler indicates that it doesn't allow explicit | 145 // If an HttpAuthHandler indicates that it doesn't allow explicit |
121 // credentials, don't prompt for credentials. | 146 // credentials, don't prompt for credentials. |
122 TEST(HttpAuthControllerTest, NoExplicitCredentialsAllowed) { | 147 TEST(HttpAuthControllerTest, NoExplicitCredentialsAllowed) { |
123 BoundNetLog dummy_log; | 148 BoundNetLog dummy_log; |
124 HttpAuthCache dummy_auth_cache; | 149 HttpAuthCache dummy_auth_cache; |
125 HttpRequestInfo request; | 150 HttpRequestInfo request; |
126 request.method = "GET"; | 151 request.method = "GET"; |
127 request.url = GURL("http://example.com"); | 152 request.url = GURL("http://example.com"); |
128 | 153 |
129 HttpRequestHeaders request_headers; | 154 HttpRequestHeaders request_headers; |
130 scoped_refptr<HttpResponseHeaders> headers( | 155 HttpResponseInfo response; |
131 HeadersFromString("HTTP/1.1 401\r\n" | 156 response.headers = HeadersFromString( |
132 "WWW-Authenticate: Ernie\r\n" | 157 "HTTP/1.1 401\r\n" |
133 "WWW-Authenticate: Bert\r\n" | 158 "WWW-Authenticate: Ernie\r\n" |
134 "\r\n")); | 159 "WWW-Authenticate: Bert\r\n" |
| 160 "\r\n"); |
135 | 161 |
136 HttpAuthHandlerMock::Factory auth_handler_factory; | 162 HttpAuthHandlerMock::Factory auth_handler_factory; |
137 | 163 |
138 // Handler for the first attempt at authentication. "Ernie" handler accepts | 164 // Handler for the first attempt at authentication. "Ernie" handler accepts |
139 // the default identity and successfully constructs a token. Handler for the | 165 // the default identity and successfully constructs a token. Handler for the |
140 scoped_ptr<HttpAuthHandlerMock> auth_handler(new HttpAuthHandlerMock()); | 166 scoped_ptr<HttpAuthHandlerMock> auth_handler(new HttpAuthHandlerMock()); |
141 auth_handler->set_allows_default_credentials(true); | 167 auth_handler->set_allows_default_credentials(true); |
142 auth_handler->set_allows_explicit_credentials(false); | 168 auth_handler->set_allows_explicit_credentials(false); |
143 auth_handler->set_expected_auth_scheme("ernie"); | 169 auth_handler->set_expected_auth_scheme("ernie"); |
144 auth_handler_factory.AddMockHandler(auth_handler.Pass(), | 170 auth_handler_factory.AddMockHandler(auth_handler.Pass(), |
145 HttpAuthHandlerCreateReason::CHALLENGE); | 171 HttpAuthHandlerCreateReason::CHALLENGE); |
146 | 172 |
147 scoped_refptr<HttpAuthController> controller( | 173 scoped_refptr<HttpAuthController> controller( |
148 new HttpAuthController(HttpAuth::AUTH_SERVER, | 174 new HttpAuthController(HttpAuth::AUTH_SERVER, |
149 GURL("http://example.com"), | 175 GURL("http://example.com"), |
150 &dummy_auth_cache, &auth_handler_factory)); | 176 &dummy_auth_cache, &auth_handler_factory)); |
151 ASSERT_EQ(OK, | 177 TestCompletionCallback completion_calback; |
152 controller->HandleAuthChallenge(headers, false, false, dummy_log)); | 178 int rv = controller->HandleAuthChallenge( |
| 179 response, completion_calback.callback(), dummy_log); |
| 180 ASSERT_EQ(OK, completion_calback.GetResult(rv)); |
153 ASSERT_TRUE(controller->HaveAuthHandler()); | 181 ASSERT_TRUE(controller->HaveAuthHandler()); |
154 controller->ResetAuth(AuthCredentials()); | |
155 EXPECT_TRUE(controller->HaveAuth()); | 182 EXPECT_TRUE(controller->HaveAuth()); |
156 EXPECT_FALSE(auth_handler_factory.HaveAuthHandlers()); | 183 EXPECT_FALSE(auth_handler_factory.HaveAuthHandlers()); |
157 | 184 |
158 // Should only succeed if we are using the "Ernie" MockHandler. | 185 // Should only succeed if we are using the "Ernie" MockHandler. |
159 EXPECT_EQ(OK, controller->MaybeGenerateAuthToken( | 186 ASSERT_EQ(OK, controller->MaybeGenerateAuthToken( |
160 &request, CompletionCallback(), dummy_log)); | 187 &request, completion_calback.callback(), dummy_log)); |
161 controller->AddAuthorizationHeader(&request_headers); | 188 controller->AddAuthorizationHeader(&request_headers); |
162 | 189 |
163 // Handlers for the second attempt. Neither should be used to generate a | 190 // Handlers for the second attempt. Neither should be used to generate a |
164 // token. Instead the controller should realize that there are no viable | 191 // token. Instead the controller should realize that there are no viable |
165 // identities to use with the "Ernie" handler and fail. | 192 // identities to use with the "Ernie" handler and fail. |
166 auth_handler.reset(new HttpAuthHandlerMock()); | 193 auth_handler.reset(new HttpAuthHandlerMock()); |
167 auth_handler->set_allows_default_credentials(true); | 194 auth_handler->set_allows_default_credentials(true); |
168 auth_handler->set_allows_explicit_credentials(false); | 195 auth_handler->set_allows_explicit_credentials(false); |
169 auth_handler->set_expected_auth_scheme("ernie"); | 196 auth_handler->set_expected_auth_scheme("ernie"); |
170 auth_handler_factory.AddMockHandler(auth_handler.Pass(), | 197 auth_handler_factory.AddMockHandler(auth_handler.Pass(), |
171 HttpAuthHandlerCreateReason::CHALLENGE); | 198 HttpAuthHandlerCreateReason::CHALLENGE); |
172 | 199 |
173 // Fallback handlers for the second attempt. The "Ernie" handler should be | 200 // Fallback handlers for the second attempt. The "Ernie" handler should be |
174 // discarded due to the disabled scheme, and the "Bert" handler should | 201 // discarded due to the disabled scheme, and the "Bert" handler should |
175 // successfully be used to generate a token. | 202 // successfully be used to generate a token. |
176 auth_handler.reset(new HttpAuthHandlerMock()); | 203 auth_handler.reset(new HttpAuthHandlerMock()); |
177 auth_handler->set_allows_default_credentials(false); | 204 auth_handler->set_allows_default_credentials(false); |
178 auth_handler->set_allows_explicit_credentials(true); | 205 auth_handler->set_allows_explicit_credentials(true); |
179 auth_handler->set_expected_auth_scheme("bert"); | 206 auth_handler->set_expected_auth_scheme("bert"); |
180 auth_handler_factory.AddMockHandler(auth_handler.Pass(), | 207 auth_handler_factory.AddMockHandler(auth_handler.Pass(), |
181 HttpAuthHandlerCreateReason::CHALLENGE); | 208 HttpAuthHandlerCreateReason::CHALLENGE); |
182 | 209 |
183 // Once a token is generated, simulate the receipt of a server response | 210 // Once a token is generated, simulate the receipt of a server response |
184 // indicating that the authentication attempt was rejected. | 211 // indicating that the authentication attempt was rejected. |
185 ASSERT_EQ(OK, | 212 TestCompletionCallback completion_calback_rejected; |
186 controller->HandleAuthChallenge(headers, false, false, dummy_log)); | 213 rv = controller->HandleAuthChallenge( |
| 214 response, completion_calback_rejected.callback(), dummy_log); |
| 215 ASSERT_EQ(OK, completion_calback_rejected.GetResult(rv)); |
187 ASSERT_TRUE(controller->HaveAuthHandler()); | 216 ASSERT_TRUE(controller->HaveAuthHandler()); |
| 217 EXPECT_FALSE(controller->HaveAuth()); |
188 controller->ResetAuth(AuthCredentials(base::ASCIIToUTF16("Hello"), | 218 controller->ResetAuth(AuthCredentials(base::ASCIIToUTF16("Hello"), |
189 base::string16())); | 219 base::string16())); |
190 EXPECT_TRUE(controller->HaveAuth()); | 220 EXPECT_TRUE(controller->HaveAuth()); |
191 EXPECT_TRUE(controller->IsAuthSchemeDisabled("ernie")); | 221 EXPECT_TRUE(controller->IsAuthSchemeDisabled("ernie")); |
192 EXPECT_FALSE(controller->IsAuthSchemeDisabled("bert")); | 222 EXPECT_FALSE(controller->IsAuthSchemeDisabled("bert")); |
193 | 223 |
194 // Should only succeed if we are using the "Bert" MockHandler. | 224 // Should only succeed if we are using the "Bert" MockHandler. |
195 EXPECT_EQ(OK, controller->MaybeGenerateAuthToken( | 225 EXPECT_EQ(OK, controller->MaybeGenerateAuthToken( |
196 &request, CompletionCallback(), dummy_log)); | 226 &request, completion_calback.callback(), dummy_log)); |
197 } | 227 } |
198 | 228 |
| 229 namespace { |
| 230 |
| 231 struct ChallengeResponse { |
| 232 const char* const response_headers = nullptr; |
| 233 const char* const authorization_header = nullptr; |
| 234 |
| 235 ChallengeResponse(const char* const response_headers, |
| 236 const char* const authorization_header) |
| 237 : response_headers(response_headers), |
| 238 authorization_header(authorization_header) {} |
| 239 }; |
| 240 |
| 241 struct MockAuthHandler { |
| 242 std::string scheme; |
| 243 int init_rv = OK; |
| 244 int generate_rv = OK; |
| 245 |
| 246 MockAuthHandler(const std::string& scheme, int int_rv, int generate_rv) |
| 247 : scheme(scheme), init_rv(int_rv), generate_rv(generate_rv) {} |
| 248 }; |
| 249 |
| 250 struct ChallengeResponseTestCase { |
| 251 std::vector<ChallengeResponse> rounds; |
| 252 std::vector<MockAuthHandler> handlers; |
| 253 bool run_handler_async = false; |
| 254 |
| 255 ChallengeResponseTestCase& WithChallengeResponse( |
| 256 const char* response_headers, |
| 257 const char* authorization_header) { |
| 258 rounds.push_back(ChallengeResponse(response_headers, authorization_header)); |
| 259 return *this; |
| 260 } |
| 261 |
| 262 ChallengeResponseTestCase& WithHandler(const base::StringPiece& scheme, |
| 263 int init_rv, |
| 264 int generate_rv) { |
| 265 handlers.push_back( |
| 266 MockAuthHandler(scheme.as_string(), init_rv, generate_rv)); |
| 267 return *this; |
| 268 } |
| 269 }; |
| 270 |
| 271 std::vector<ChallengeResponseTestCase> SyncAndAsync( |
| 272 const ChallengeResponseTestCase& test_case) { |
| 273 std::vector<ChallengeResponseTestCase> test_cases; |
| 274 ChallengeResponseTestCase test_case_copy = test_case; |
| 275 |
| 276 test_case_copy.run_handler_async = false; |
| 277 test_cases.push_back(test_case_copy); |
| 278 |
| 279 test_case_copy.run_handler_async = true; |
| 280 test_cases.push_back(test_case_copy); |
| 281 return test_cases; |
| 282 } |
| 283 |
| 284 class HttpAuthControllerTest |
| 285 : public ::testing::TestWithParam<ChallengeResponseTestCase> {}; |
| 286 |
| 287 } // namespace |
| 288 |
| 289 TEST_P(HttpAuthControllerTest, Run) { |
| 290 const ChallengeResponseTestCase& params = GetParam(); |
| 291 BoundNetLog dummy_log; |
| 292 HttpAuthCache dummy_auth_cache; |
| 293 GURL url("http://example.com"); |
| 294 HttpAuthHandlerMock::Factory auth_factory; |
| 295 |
| 296 for (const auto& handler : params.handlers) { |
| 297 scoped_ptr<HttpAuthHandlerMock> mock_handler(new HttpAuthHandlerMock); |
| 298 mock_handler->set_expected_auth_scheme(handler.scheme); |
| 299 mock_handler->set_expect_multiple_challenges(handler.scheme == "ntlm" || |
| 300 handler.scheme == "negotiate"); |
| 301 mock_handler->SetInitExpectation(params.run_handler_async, handler.init_rv); |
| 302 mock_handler->SetGenerateExpectation(params.run_handler_async, |
| 303 handler.generate_rv); |
| 304 auth_factory.AddMockHandler(mock_handler.Pass(), |
| 305 HttpAuthHandlerCreateReason::CHALLENGE); |
| 306 } |
| 307 |
| 308 HttpRequestInfo request_info; |
| 309 |
| 310 scoped_refptr<HttpAuthController> auth_controller(new HttpAuthController( |
| 311 HttpAuth::AUTH_SERVER, url, &dummy_auth_cache, &auth_factory)); |
| 312 for (const auto& round : params.rounds) { |
| 313 if (!round.response_headers) |
| 314 break; |
| 315 HttpResponseInfo response_info; |
| 316 response_info.headers = HeadersFromString(round.response_headers); |
| 317 TestCompletionCallback callback; |
| 318 |
| 319 int result = auth_controller->HandleAuthChallenge( |
| 320 response_info, callback.callback(), dummy_log); |
| 321 EXPECT_EQ(OK, callback.GetResult(result)); |
| 322 |
| 323 if (!auth_controller->HaveAuthHandler()) { |
| 324 EXPECT_STREQ("", round.authorization_header); |
| 325 continue; |
| 326 } |
| 327 |
| 328 if (!auth_controller->HaveAuth()) { |
| 329 auth_controller->ResetAuth( |
| 330 AuthCredentials(base::ASCIIToUTF16("a"), base::ASCIIToUTF16("b"))); |
| 331 } |
| 332 |
| 333 result = auth_controller->MaybeGenerateAuthToken( |
| 334 &request_info, callback.callback(), dummy_log); |
| 335 EXPECT_EQ(OK, callback.GetResult(result)); |
| 336 HttpRequestHeaders headers; |
| 337 auth_controller->AddAuthorizationHeader(&headers); |
| 338 std::string authorization_header; |
| 339 headers.GetHeader( |
| 340 HttpAuth::GetAuthorizationHeaderName(HttpAuth::AUTH_SERVER), |
| 341 &authorization_header); |
| 342 EXPECT_STREQ(round.authorization_header, authorization_header.c_str()); |
| 343 } |
| 344 |
| 345 EXPECT_FALSE(auth_factory.HaveAuthHandlers()); |
| 346 } |
| 347 |
| 348 // Picks Basic because that's the only supported scheme. |
| 349 INSTANTIATE_TEST_CASE_P( |
| 350 PickSupportedScheme, |
| 351 HttpAuthControllerTest, |
| 352 ValuesIn( |
| 353 SyncAndAsync(ChallengeResponseTestCase() |
| 354 .WithChallengeResponse( |
| 355 "HTTP/1.1 401\n" |
| 356 "Y: Digest realm=\"X\", nonce=\"aaaaaaaaaa\"\n" |
| 357 "www-authenticate: Basic realm=\"BasicRealm\"\n", |
| 358 "basic auth_token,realm=\"BasicRealm\"") |
| 359 .WithHandler("basic", OK, OK)))); |
| 360 |
| 361 // No supported schemes. |
| 362 INSTANTIATE_TEST_CASE_P( |
| 363 NoSupportedSchemes, |
| 364 HttpAuthControllerTest, |
| 365 ValuesIn(SyncAndAsync(ChallengeResponseTestCase().WithChallengeResponse( |
| 366 "HTTP/1.1 401\n" |
| 367 "Y: Digest realm=\"FooBar\", nonce=\"aaaaaaaaaa\"\n" |
| 368 "www-authenticate: Fake realm=\"FooBar\"\n", |
| 369 "")))); |
| 370 |
| 371 // Pick Digest over Basic. |
| 372 INSTANTIATE_TEST_CASE_P( |
| 373 DigestOverBasic, |
| 374 HttpAuthControllerTest, |
| 375 ValuesIn(SyncAndAsync( |
| 376 ChallengeResponseTestCase() |
| 377 .WithChallengeResponse( |
| 378 "HTTP/1.1 401\n" |
| 379 "www-authenticate: Basic realm=\"FooBar\"\n" |
| 380 "www-authenticate: Fake realm=\"FooBar\"\n" |
| 381 "www-authenticate: nonce=\"aaaaaaaaaa\"\n" |
| 382 "www-authenticate: Digest realm=\"DigestRealm\", " |
| 383 "nonce=\"aaaaaaaaaa\"\n", |
| 384 "digest auth_token,realm=\"DigestRealm\", nonce=\"aaaaaaaaaa\"") |
| 385 .WithHandler("digest", OK, OK)))); |
| 386 |
| 387 // Handle an empty header correctly. |
| 388 INSTANTIATE_TEST_CASE_P( |
| 389 EmptyHeader, |
| 390 HttpAuthControllerTest, |
| 391 ValuesIn(SyncAndAsync(ChallengeResponseTestCase().WithChallengeResponse( |
| 392 "HTTP/1.1 401\n" |
| 393 "Y: Digest realm=\"X\", nonce=\"aaaaaaaaaa\"\n" |
| 394 "www-authenticate:\n", |
| 395 "")))); |
| 396 |
| 397 // NTLM appears earlier in the list, Negotiate is preferred. |
| 398 INSTANTIATE_TEST_CASE_P( |
| 399 NegotiateOverNTLM, |
| 400 HttpAuthControllerTest, |
| 401 ValuesIn( |
| 402 SyncAndAsync(ChallengeResponseTestCase() |
| 403 .WithChallengeResponse("HTTP/1.1 401\n" |
| 404 "WWW-Authenticate: NTLM\n" |
| 405 "WWW-Authenticate: Negotiate\n", |
| 406 "negotiate auth_token") |
| 407 .WithHandler("negotiate", OK, OK)))); |
| 408 |
| 409 // Two rounds. Basic isn't connection oriented and will use up a new handler |
| 410 // for the second challenge. |
| 411 INSTANTIATE_TEST_CASE_P( |
| 412 SecondChallengeForBasic, |
| 413 HttpAuthControllerTest, |
| 414 ValuesIn(SyncAndAsync( |
| 415 ChallengeResponseTestCase() |
| 416 .WithChallengeResponse("HTTP/1.1 401\n" |
| 417 "WWW-Authenticate: Basic first_round\n", |
| 418 "basic auth_token,first_round") |
| 419 .WithHandler("basic", OK, OK) |
| 420 .WithChallengeResponse("HTTP/1.1 401\n" |
| 421 "WWW-Authenticate: Basic second_round\n", |
| 422 "basic auth_token,second_round") |
| 423 .WithHandler("basic", OK, OK)))); |
| 424 |
| 425 // Correctly deal with a missing challenge for the second round. |
| 426 INSTANTIATE_TEST_CASE_P( |
| 427 SecondChallengeIsMissing, |
| 428 HttpAuthControllerTest, |
| 429 ValuesIn(SyncAndAsync( |
| 430 ChallengeResponseTestCase() |
| 431 .WithChallengeResponse("HTTP/1.1 401\n" |
| 432 "WWW-Authenticate: Basic first_round\n", |
| 433 "basic auth_token,first_round") |
| 434 .WithHandler("basic", OK, OK) |
| 435 .WithChallengeResponse("HTTP/1.1 401\n", "")))); |
| 436 |
| 437 // Correctly deal with an empty challenge for the second round. |
| 438 INSTANTIATE_TEST_CASE_P( |
| 439 SecondChallengeIsEmpty, |
| 440 HttpAuthControllerTest, |
| 441 ValuesIn(SyncAndAsync( |
| 442 ChallengeResponseTestCase() |
| 443 .WithChallengeResponse("HTTP/1.1 401\n" |
| 444 "WWW-Authenticate: Basic first_round\n", |
| 445 "basic auth_token,first_round") |
| 446 .WithHandler("basic", OK, OK) |
| 447 .WithChallengeResponse("HTTP/1.1 401\n" |
| 448 "WWW-Authenticate:\n", |
| 449 "")))); |
| 450 |
| 451 // The second round introduces a preferred scheme. |
| 452 INSTANTIATE_TEST_CASE_P( |
| 453 SecondChallengeHasPreferredScheme, |
| 454 HttpAuthControllerTest, |
| 455 ValuesIn(SyncAndAsync( |
| 456 ChallengeResponseTestCase() |
| 457 .WithChallengeResponse("HTTP/1.1 401\n" |
| 458 "WWW-Authenticate: Basic first_round\n", |
| 459 "basic auth_token,first_round") |
| 460 .WithHandler("basic", OK, OK) |
| 461 .WithChallengeResponse("HTTP/1.1 401\n" |
| 462 "WWW-Authenticate: Basic second_round\n" |
| 463 "WWW-Authenticate: Negotiate\n", |
| 464 "negotiate auth_token") |
| 465 .WithHandler("negotiate", OK, OK)))); |
| 466 |
| 467 // If there are two challenges, then the auth controller should pick the first |
| 468 // one. |
| 469 INSTANTIATE_TEST_CASE_P( |
| 470 MultipleChallengesForSameScheme, |
| 471 HttpAuthControllerTest, |
| 472 ValuesIn(SyncAndAsync( |
| 473 ChallengeResponseTestCase() |
| 474 .WithChallengeResponse("HTTP/1.1 401\n" |
| 475 "WWW-Authenticate: Basic first_round\n", |
| 476 "basic auth_token,first_round") |
| 477 .WithHandler("basic", OK, OK) |
| 478 .WithChallengeResponse("HTTP/1.1 401\n" |
| 479 "WWW-Authenticate: Basic second_round_1\n" |
| 480 "WWW-Authenticate: Basic second_round_2\n", |
| 481 "basic auth_token,second_round_1") |
| 482 .WithHandler("basic", OK, OK)))); |
| 483 |
| 484 // As above, but the handler will reject the first challenge of the second |
| 485 // round. The auth controller should use the second challenge instead. |
| 486 INSTANTIATE_TEST_CASE_P( |
| 487 RejectChallengeForTheSameSchemeInSingleResponse, |
| 488 HttpAuthControllerTest, |
| 489 ValuesIn(SyncAndAsync( |
| 490 ChallengeResponseTestCase() |
| 491 .WithChallengeResponse("HTTP/1.1 401\n" |
| 492 "WWW-Authenticate: Basic first_round\n", |
| 493 "basic auth_token,first_round") |
| 494 .WithChallengeResponse("HTTP/1.1 401\n" |
| 495 "WWW-Authenticate: Basic second_round_1\n" |
| 496 "WWW-Authenticate: Basic second_round_2\n", |
| 497 "basic auth_token,second_round_2") |
| 498 .WithHandler("basic", OK, OK) |
| 499 .WithHandler("basic", ERR_INVALID_AUTH_CREDENTIALS, OK) |
| 500 .WithHandler("basic", OK, OK)))); |
| 501 |
| 502 // Connection based schemes will treat new auth challenges for the same scheme |
| 503 // as acceptance and continuance of the current handshake if there's a |
| 504 // parameter. |
| 505 INSTANTIATE_TEST_CASE_P( |
| 506 MultiRoundAuth_Continuations, |
| 507 HttpAuthControllerTest, |
| 508 ValuesIn(SyncAndAsync( |
| 509 ChallengeResponseTestCase() |
| 510 .WithChallengeResponse("HTTP/1.1 401\n" |
| 511 "WWW-Authenticate: Negotiate\n", |
| 512 "negotiate auth_token") |
| 513 .WithHandler("negotiate", OK, OK) |
| 514 .WithChallengeResponse("HTTP/1.1 401\n" |
| 515 "WWW-Authenticate: Negotiate foo\n", |
| 516 "negotiate continuation,foo")))); |
| 517 |
| 518 // As above, but picks the first challenge for the second round. |
| 519 INSTANTIATE_TEST_CASE_P( |
| 520 MultiRoundAuth_ContinuationsWithMultipleChallenges, |
| 521 HttpAuthControllerTest, |
| 522 ValuesIn(SyncAndAsync( |
| 523 ChallengeResponseTestCase() |
| 524 .WithChallengeResponse("HTTP/1.1 401\n" |
| 525 "WWW-Authenticate: Negotiate\n", |
| 526 "negotiate auth_token") |
| 527 .WithHandler("negotiate", OK, OK) |
| 528 .WithChallengeResponse("HTTP/1.1 401\n" |
| 529 "WWW-Authenticate: Negotiate foo\n" |
| 530 "WWW-Authenticate: Negotiate\n", |
| 531 "negotiate continuation,foo")))); |
| 532 |
| 533 // As above. The first challenge in the second round counts as a rejection, so |
| 534 // the controller should create a new handler and restart the handshake. |
| 535 INSTANTIATE_TEST_CASE_P( |
| 536 MultiRoundAuth_ContinuationsWithMultipleChallengesAndRejection, |
| 537 HttpAuthControllerTest, |
| 538 ValuesIn(SyncAndAsync( |
| 539 ChallengeResponseTestCase() |
| 540 .WithChallengeResponse("HTTP/1.1 401\n" |
| 541 "WWW-Authenticate: Negotiate\n", |
| 542 "negotiate auth_token") |
| 543 .WithHandler("negotiate", OK, OK) |
| 544 .WithChallengeResponse("HTTP/1.1 401\n" |
| 545 "WWW-Authenticate: Negotiate\n" |
| 546 "WWW-Authenticate: Negotiate continuation\n", |
| 547 "negotiate auth_token") |
| 548 .WithHandler("negotiate", OK, OK)))); |
| 549 |
| 550 // Multiround authentication with rejection. The rejection comes in the form of |
| 551 // an empty challenge for the same scheme. |
| 552 INSTANTIATE_TEST_CASE_P( |
| 553 MultiRoundAuth_RejectionViaEmptyChallenge, |
| 554 HttpAuthControllerTest, |
| 555 ValuesIn( |
| 556 SyncAndAsync(ChallengeResponseTestCase() |
| 557 .WithChallengeResponse("HTTP/1.1 401\n" |
| 558 "WWW-Authenticate: Negotiate\n", |
| 559 "negotiate auth_token") |
| 560 .WithHandler("negotiate", OK, OK) |
| 561 .WithChallengeResponse("HTTP/1.1 401\n" |
| 562 "WWW-Authenticate: Negotiate\n", |
| 563 "negotiate auth_token") |
| 564 .WithHandler("negotiate", OK, OK) |
| 565 .WithChallengeResponse("HTTP/1.1 401\n" |
| 566 "WWW-Authenticate: Negotiate\n", |
| 567 "negotiate auth_token") |
| 568 .WithHandler("negotiate", OK, OK)))); |
| 569 |
| 570 // Multiround authentication with rejection. The rejection comes in the form of |
| 571 // a response with no challenge for the original scheme. |
| 572 INSTANTIATE_TEST_CASE_P( |
| 573 MultiRoundAuth_RejectionViaSchemeChange, |
| 574 HttpAuthControllerTest, |
| 575 ValuesIn( |
| 576 SyncAndAsync(ChallengeResponseTestCase() |
| 577 .WithChallengeResponse("HTTP/1.1 401\n" |
| 578 "WWW-Authenticate: Negotiate\n", |
| 579 "negotiate auth_token") |
| 580 .WithHandler("negotiate", OK, OK) |
| 581 .WithChallengeResponse("HTTP/1.1 401\n" |
| 582 "WWW-Authenticate: Basic foo\n", |
| 583 "basic auth_token,foo") |
| 584 .WithHandler("basic", OK, OK)))); |
| 585 |
| 586 // Dealing with empty and missing second round challenges for connection |
| 587 // oriented schemes. |
| 588 INSTANTIATE_TEST_CASE_P( |
| 589 MultiRoundAuth_EmptySecondChallenge, |
| 590 HttpAuthControllerTest, |
| 591 ValuesIn( |
| 592 SyncAndAsync(ChallengeResponseTestCase() |
| 593 .WithChallengeResponse("HTTP/1.1 401\n" |
| 594 "WWW-Authenticate: Negotiate\n", |
| 595 "negotiate auth_token") |
| 596 .WithHandler("negotiate", OK, OK) |
| 597 .WithChallengeResponse("HTTP/1.1 401\n" |
| 598 "WWW-Authenticate:\n", |
| 599 "")))); |
| 600 INSTANTIATE_TEST_CASE_P( |
| 601 MultiRoundAuth_MissingSecondChallenge, |
| 602 HttpAuthControllerTest, |
| 603 ValuesIn( |
| 604 SyncAndAsync(ChallengeResponseTestCase() |
| 605 .WithChallengeResponse("HTTP/1.1 401\n" |
| 606 "WWW-Authenticate: Negotiate\n", |
| 607 "negotiate auth_token") |
| 608 .WithHandler("negotiate", OK, OK) |
| 609 .WithChallengeResponse("HTTP/1.1 401\n", "")))); |
| 610 |
| 611 // Should pick matching challenge even if other challenges are present. |
| 612 INSTANTIATE_TEST_CASE_P( |
| 613 MultiRoundAuth_PicksCorrectChallenge, |
| 614 HttpAuthControllerTest, |
| 615 ValuesIn(SyncAndAsync( |
| 616 ChallengeResponseTestCase() |
| 617 .WithChallengeResponse("HTTP/1.1 401\n" |
| 618 "WWW-Authenticate: Negotiate\n", |
| 619 "negotiate auth_token") |
| 620 .WithHandler("negotiate", OK, OK) |
| 621 .WithChallengeResponse("HTTP/1.1 401\n" |
| 622 "WWW-Authenticate: Basic foo\n" |
| 623 "WWW-Authenticate: Negotiate bar\n", |
| 624 "negotiate continuation,bar")))); |
| 625 INSTANTIATE_TEST_CASE_P( |
| 626 MultiRoundAuth_PicksCorrectChallengeWithPreferredChallenge, |
| 627 HttpAuthControllerTest, |
| 628 ValuesIn( |
| 629 SyncAndAsync(ChallengeResponseTestCase() |
| 630 .WithChallengeResponse("HTTP/1.1 401\n" |
| 631 "WWW-Authenticate: NTLM\n", |
| 632 "ntlm auth_token") |
| 633 .WithHandler("ntlm", OK, OK) |
| 634 .WithChallengeResponse("HTTP/1.1 401\n" |
| 635 "WWW-Authenticate: Negotiate\n" |
| 636 "WWW-Authenticate: NTLM bar\n", |
| 637 "ntlm continuation,bar")))); |
| 638 |
| 639 // If an auth handler fails to initialize, the controller should try the next |
| 640 // suitable challenge. |
| 641 INSTANTIATE_TEST_CASE_P( |
| 642 FallThroughInvalidHeaders, |
| 643 HttpAuthControllerTest, |
| 644 ValuesIn(SyncAndAsync( |
| 645 ChallengeResponseTestCase() |
| 646 .WithChallengeResponse("HTTP/1.1 401\n" |
| 647 "WWW-Authenticate: Basic abc;\n" |
| 648 "WWW-Authenticate: Digest abc;\n" |
| 649 "WWW-Authenticate: Basic valid", |
| 650 "basic auth_token,valid") |
| 651 // The first challenge chosen is Digest. But the handler fails. |
| 652 .WithHandler("digest", ERR_INVALID_AUTH_CREDENTIALS, OK) |
| 653 // Next the controller tries the first Basic challenge, which also |
| 654 // fails. |
| 655 .WithHandler("basic", ERR_INVALID_AUTH_CREDENTIALS, OK) |
| 656 // Finally, the second Basic challenge succeeds. |
| 657 .WithHandler("basic", OK, OK)))); |
| 658 |
| 659 // Identities are used in the correct order. |
| 660 TEST(HttpAuthControllerTest, IdentityPriorityOrder) {} |
| 661 |
| 662 // Identities from abstract sources should be available to a new handler if the |
| 663 // server changes authentication scheme or if all identity sources were |
| 664 // exhausted for a handler. |
| 665 TEST(HttpAuthControllerTest, IdentityReuseAcrossHandlers) {} |
| 666 |
199 } // namespace net | 667 } // namespace net |
OLD | NEW |