OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/http/http_auth_controller.h" | |
6 | |
7 #include "base/strings/utf_string_conversions.h" | |
8 #include "net/base/net_errors.h" | |
9 #include "net/base/net_log.h" | |
10 #include "net/base/test_completion_callback.h" | |
11 #include "net/http/http_auth_cache.h" | |
12 #include "net/http/http_auth_challenge_tokenizer.h" | |
13 #include "net/http/http_auth_handler_mock.h" | |
14 #include "net/http/http_request_info.h" | |
15 #include "net/http/http_response_headers.h" | |
16 #include "net/http/http_util.h" | |
17 #include "testing/gtest/include/gtest/gtest.h" | |
18 | |
19 namespace net { | |
20 | |
21 namespace { | |
22 | |
23 enum HandlerRunMode { | |
24 RUN_HANDLER_SYNC, | |
25 RUN_HANDLER_ASYNC | |
26 }; | |
27 | |
28 enum SchemeState { | |
29 SCHEME_IS_DISABLED, | |
30 SCHEME_IS_ENABLED | |
31 }; | |
32 | |
33 scoped_refptr<HttpResponseHeaders> HeadersFromString(const char* string) { | |
34 std::string raw_string(string); | |
35 std::string headers_string = HttpUtil::AssembleRawHeaders( | |
36 raw_string.c_str(), raw_string.length()); | |
37 scoped_refptr<HttpResponseHeaders> headers( | |
38 new HttpResponseHeaders(headers_string)); | |
39 return headers; | |
40 } | |
41 | |
42 // Runs an HttpAuthController with a single round mock auth handler | |
43 // that returns |handler_rv| on token generation. The handler runs in | |
44 // async if |run_mode| is RUN_HANDLER_ASYNC. Upon completion, the | |
45 // return value of the controller is tested against | |
46 // |expected_controller_rv|. |scheme_state| indicates whether the | |
47 // auth scheme used should be disabled after this run. | |
48 void RunSingleRoundAuthTest(HandlerRunMode run_mode, | |
49 int handler_rv, | |
50 int expected_controller_rv, | |
51 SchemeState scheme_state) { | |
52 BoundNetLog dummy_log; | |
53 HttpAuthCache dummy_auth_cache; | |
54 | |
55 HttpRequestInfo request; | |
56 request.method = "GET"; | |
57 request.url = GURL("http://example.com"); | |
58 | |
59 scoped_refptr<HttpResponseHeaders> headers(HeadersFromString( | |
60 "HTTP/1.1 407\r\n" | |
61 "Proxy-Authenticate: MOCK foo\r\n" | |
62 "\r\n")); | |
63 | |
64 HttpAuthHandlerMock::Factory auth_handler_factory; | |
65 HttpAuthHandlerMock* auth_handler = new HttpAuthHandlerMock(); | |
66 auth_handler->SetGenerateExpectation((run_mode == RUN_HANDLER_ASYNC), | |
67 handler_rv); | |
68 auth_handler_factory.AddMockHandler(auth_handler, HttpAuth::AUTH_PROXY); | |
69 auth_handler_factory.set_do_init_from_challenge(true); | |
70 | |
71 scoped_refptr<HttpAuthController> controller( | |
72 new HttpAuthController(HttpAuth::AUTH_PROXY, | |
73 GURL("http://example.com"), | |
74 &dummy_auth_cache, &auth_handler_factory)); | |
75 ASSERT_EQ(OK, | |
76 controller->HandleAuthChallenge(headers, false, false, dummy_log)); | |
77 ASSERT_TRUE(controller->HaveAuthHandler()); | |
78 controller->ResetAuth(AuthCredentials()); | |
79 EXPECT_TRUE(controller->HaveAuth()); | |
80 | |
81 TestCompletionCallback callback; | |
82 EXPECT_EQ((run_mode == RUN_HANDLER_ASYNC)? ERR_IO_PENDING: | |
83 expected_controller_rv, | |
84 controller->MaybeGenerateAuthToken(&request, callback.callback(), | |
85 dummy_log)); | |
86 if (run_mode == RUN_HANDLER_ASYNC) | |
87 EXPECT_EQ(expected_controller_rv, callback.WaitForResult()); | |
88 EXPECT_EQ((scheme_state == SCHEME_IS_DISABLED), | |
89 controller->IsAuthSchemeDisabled(HttpAuth::AUTH_SCHEME_MOCK)); | |
90 } | |
91 | |
92 } // namespace | |
93 | |
94 // If an HttpAuthHandler returns an error code that indicates a | |
95 // permanent error, the HttpAuthController should disable the scheme | |
96 // used and retry the request. | |
97 TEST(HttpAuthControllerTest, PermanentErrors) { | |
98 | |
99 // Run a synchronous handler that returns | |
100 // ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS. We expect a return value | |
101 // of OK from the controller so we can retry the request. | |
102 RunSingleRoundAuthTest(RUN_HANDLER_SYNC, | |
103 ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS, | |
104 OK, SCHEME_IS_DISABLED); | |
105 | |
106 // Now try an async handler that returns | |
107 // ERR_MISSING_AUTH_CREDENTIALS. Async and sync handlers invoke | |
108 // different code paths in HttpAuthController when generating | |
109 // tokens. | |
110 RunSingleRoundAuthTest(RUN_HANDLER_ASYNC, ERR_MISSING_AUTH_CREDENTIALS, OK, | |
111 SCHEME_IS_DISABLED); | |
112 | |
113 // If a non-permanent error is returned by the handler, then the | |
114 // controller should report it unchanged. | |
115 RunSingleRoundAuthTest(RUN_HANDLER_ASYNC, ERR_INVALID_AUTH_CREDENTIALS, | |
116 ERR_INVALID_AUTH_CREDENTIALS, SCHEME_IS_ENABLED); | |
117 } | |
118 | |
119 // If an HttpAuthHandler indicates that it doesn't allow explicit | |
120 // credentials, don't prompt for credentials. | |
121 TEST(HttpAuthControllerTest, NoExplicitCredentialsAllowed) { | |
122 // Modified mock HttpAuthHandler for this test. | |
123 class MockHandler : public HttpAuthHandlerMock { | |
124 public: | |
125 MockHandler(int expected_rv, HttpAuth::Scheme scheme) | |
126 : expected_scheme_(scheme) { | |
127 SetGenerateExpectation(false, expected_rv); | |
128 } | |
129 | |
130 protected: | |
131 bool Init(HttpAuthChallengeTokenizer* challenge) override { | |
132 HttpAuthHandlerMock::Init(challenge); | |
133 set_allows_default_credentials(true); | |
134 set_allows_explicit_credentials(false); | |
135 set_connection_based(true); | |
136 // Pretend to be SCHEME_BASIC so we can test failover logic. | |
137 if (challenge->scheme() == "Basic") { | |
138 auth_scheme_ = HttpAuth::AUTH_SCHEME_BASIC; | |
139 --score_; // Reduce score, so we rank below Mock. | |
140 set_allows_explicit_credentials(true); | |
141 } | |
142 EXPECT_EQ(expected_scheme_, auth_scheme_); | |
143 return true; | |
144 } | |
145 | |
146 int GenerateAuthTokenImpl(const AuthCredentials* credentials, | |
147 const HttpRequestInfo* request, | |
148 const CompletionCallback& callback, | |
149 std::string* auth_token) override { | |
150 int result = | |
151 HttpAuthHandlerMock::GenerateAuthTokenImpl(credentials, | |
152 request, callback, | |
153 auth_token); | |
154 EXPECT_TRUE(result != OK || | |
155 !AllowsExplicitCredentials() || | |
156 !credentials->Empty()); | |
157 return result; | |
158 } | |
159 | |
160 private: | |
161 HttpAuth::Scheme expected_scheme_; | |
162 }; | |
163 | |
164 BoundNetLog dummy_log; | |
165 HttpAuthCache dummy_auth_cache; | |
166 HttpRequestInfo request; | |
167 request.method = "GET"; | |
168 request.url = GURL("http://example.com"); | |
169 | |
170 HttpRequestHeaders request_headers; | |
171 scoped_refptr<HttpResponseHeaders> headers(HeadersFromString( | |
172 "HTTP/1.1 401\r\n" | |
173 "WWW-Authenticate: Mock\r\n" | |
174 "WWW-Authenticate: Basic\r\n" | |
175 "\r\n")); | |
176 | |
177 HttpAuthHandlerMock::Factory auth_handler_factory; | |
178 | |
179 // Handlers for the first attempt at authentication. AUTH_SCHEME_MOCK handler | |
180 // accepts the default identity and successfully constructs a token. | |
181 auth_handler_factory.AddMockHandler( | |
182 new MockHandler(OK, HttpAuth::AUTH_SCHEME_MOCK), HttpAuth::AUTH_SERVER); | |
183 auth_handler_factory.AddMockHandler( | |
184 new MockHandler(ERR_UNEXPECTED, HttpAuth::AUTH_SCHEME_BASIC), | |
185 HttpAuth::AUTH_SERVER); | |
186 | |
187 // Handlers for the second attempt. Neither should be used to generate a | |
188 // token. Instead the controller should realize that there are no viable | |
189 // identities to use with the AUTH_SCHEME_MOCK handler and fail. | |
190 auth_handler_factory.AddMockHandler( | |
191 new MockHandler(ERR_UNEXPECTED, HttpAuth::AUTH_SCHEME_MOCK), | |
192 HttpAuth::AUTH_SERVER); | |
193 auth_handler_factory.AddMockHandler( | |
194 new MockHandler(ERR_UNEXPECTED, HttpAuth::AUTH_SCHEME_BASIC), | |
195 HttpAuth::AUTH_SERVER); | |
196 | |
197 // Fallback handlers for the second attempt. The AUTH_SCHEME_MOCK handler | |
198 // should be discarded due to the disabled scheme, and the AUTH_SCHEME_BASIC | |
199 // handler should successfully be used to generate a token. | |
200 auth_handler_factory.AddMockHandler( | |
201 new MockHandler(ERR_UNEXPECTED, HttpAuth::AUTH_SCHEME_MOCK), | |
202 HttpAuth::AUTH_SERVER); | |
203 auth_handler_factory.AddMockHandler( | |
204 new MockHandler(OK, HttpAuth::AUTH_SCHEME_BASIC), | |
205 HttpAuth::AUTH_SERVER); | |
206 auth_handler_factory.set_do_init_from_challenge(true); | |
207 | |
208 scoped_refptr<HttpAuthController> controller( | |
209 new HttpAuthController(HttpAuth::AUTH_SERVER, | |
210 GURL("http://example.com"), | |
211 &dummy_auth_cache, &auth_handler_factory)); | |
212 ASSERT_EQ(OK, | |
213 controller->HandleAuthChallenge(headers, false, false, dummy_log)); | |
214 ASSERT_TRUE(controller->HaveAuthHandler()); | |
215 controller->ResetAuth(AuthCredentials()); | |
216 EXPECT_TRUE(controller->HaveAuth()); | |
217 | |
218 // Should only succeed if we are using the AUTH_SCHEME_MOCK MockHandler. | |
219 EXPECT_EQ(OK, controller->MaybeGenerateAuthToken( | |
220 &request, CompletionCallback(), dummy_log)); | |
221 controller->AddAuthorizationHeader(&request_headers); | |
222 | |
223 // Once a token is generated, simulate the receipt of a server response | |
224 // indicating that the authentication attempt was rejected. | |
225 ASSERT_EQ(OK, | |
226 controller->HandleAuthChallenge(headers, false, false, dummy_log)); | |
227 ASSERT_TRUE(controller->HaveAuthHandler()); | |
228 controller->ResetAuth(AuthCredentials(base::ASCIIToUTF16("Hello"), | |
229 base::string16())); | |
230 EXPECT_TRUE(controller->HaveAuth()); | |
231 EXPECT_TRUE(controller->IsAuthSchemeDisabled(HttpAuth::AUTH_SCHEME_MOCK)); | |
232 EXPECT_FALSE(controller->IsAuthSchemeDisabled(HttpAuth::AUTH_SCHEME_BASIC)); | |
233 | |
234 // Should only succeed if we are using the AUTH_SCHEME_BASIC MockHandler. | |
235 EXPECT_EQ(OK, controller->MaybeGenerateAuthToken( | |
236 &request, CompletionCallback(), dummy_log)); | |
237 } | |
238 | |
239 } // namespace net | |
OLD | NEW |