| 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 |