http_auth_unittest.cc 9.8 KB

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