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 24namespace net { 25 26namespace { 27 28HttpAuthHandlerMock* 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 42HttpResponseHeaders* HeadersFromResponseText(const std::string& response) { 43 return new HttpResponseHeaders( 44 HttpUtil::AssembleRawHeaders(response.c_str(), response.length())); 45} 46 47HttpAuth::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 66TEST(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_UNSAFE(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 158TEST(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 249TEST(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 259TEST(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 270