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