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