• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_handler_basic.h"
6 
7 #include <memory>
8 #include <string>
9 #include <string_view>
10 
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "net/base/net_errors.h"
14 #include "net/base/network_anonymization_key.h"
15 #include "net/base/test_completion_callback.h"
16 #include "net/dns/mock_host_resolver.h"
17 #include "net/http/http_auth_challenge_tokenizer.h"
18 #include "net/http/http_auth_preferences.h"
19 #include "net/http/http_request_info.h"
20 #include "net/log/net_log_with_source.h"
21 #include "net/ssl/ssl_info.h"
22 #include "net/test/gtest_util.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "url/scheme_host_port.h"
26 
27 using net::test::IsError;
28 using net::test::IsOk;
29 
30 namespace net {
31 
TEST(HttpAuthHandlerBasicTest,GenerateAuthToken)32 TEST(HttpAuthHandlerBasicTest, GenerateAuthToken) {
33   static const struct {
34     const char* username;
35     const char* password;
36     const char* expected_credentials;
37   } tests[] = {
38     { "foo", "bar", "Basic Zm9vOmJhcg==" },
39     // Empty username
40     { "", "foobar", "Basic OmZvb2Jhcg==" },
41     // Empty password
42     { "anon", "", "Basic YW5vbjo=" },
43     // Empty username and empty password.
44     { "", "", "Basic Og==" },
45   };
46   url::SchemeHostPort scheme_host_port(GURL("http://www.example.com"));
47   HttpAuthHandlerBasic::Factory factory;
48   for (const auto& test : tests) {
49     std::string challenge = "Basic realm=\"Atlantis\"";
50     SSLInfo null_ssl_info;
51     auto host_resolver = std::make_unique<MockHostResolver>();
52     std::unique_ptr<HttpAuthHandler> basic;
53     EXPECT_EQ(OK, factory.CreateAuthHandlerFromString(
54                       challenge, HttpAuth::AUTH_SERVER, null_ssl_info,
55                       NetworkAnonymizationKey(), scheme_host_port,
56                       NetLogWithSource(), host_resolver.get(), &basic));
57     AuthCredentials credentials(base::ASCIIToUTF16(test.username),
58                                 base::ASCIIToUTF16(test.password));
59     HttpRequestInfo request_info;
60     std::string auth_token;
61     TestCompletionCallback callback;
62     int rv = basic->GenerateAuthToken(&credentials, &request_info,
63                                       callback.callback(), &auth_token);
64     EXPECT_THAT(rv, IsOk());
65     EXPECT_STREQ(test.expected_credentials, auth_token.c_str());
66   }
67 }
68 
TEST(HttpAuthHandlerBasicTest,HandleAnotherChallenge)69 TEST(HttpAuthHandlerBasicTest, HandleAnotherChallenge) {
70   static const struct {
71     const char* challenge;
72     HttpAuth::AuthorizationResult expected_rv;
73   } tests[] = {
74     // The handler is initialized using this challenge.  The first
75     // time HandleAnotherChallenge is called with it should cause it
76     // to treat the second challenge as a rejection since it is for
77     // the same realm.
78     {
79       "Basic realm=\"First\"",
80       HttpAuth::AUTHORIZATION_RESULT_REJECT
81     },
82 
83     // A challenge for a different realm.
84     {
85       "Basic realm=\"Second\"",
86       HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM
87     },
88 
89     // Although RFC 2617 isn't explicit about this case, if there is
90     // more than one realm directive, we pick the last one.  So this
91     // challenge should be treated as being for "First" realm.
92     {
93       "Basic realm=\"Second\",realm=\"First\"",
94       HttpAuth::AUTHORIZATION_RESULT_REJECT
95     },
96 
97     // And this one should be treated as if it was for "Second."
98     {
99       "basic realm=\"First\",realm=\"Second\"",
100       HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM
101     }
102   };
103 
104   url::SchemeHostPort scheme_host_port(GURL("http://www.example.com"));
105   HttpAuthHandlerBasic::Factory factory;
106   SSLInfo null_ssl_info;
107   auto host_resolver = std::make_unique<MockHostResolver>();
108   std::unique_ptr<HttpAuthHandler> basic;
109   EXPECT_EQ(OK, factory.CreateAuthHandlerFromString(
110                     tests[0].challenge, HttpAuth::AUTH_SERVER, null_ssl_info,
111                     NetworkAnonymizationKey(), scheme_host_port,
112                     NetLogWithSource(), host_resolver.get(), &basic));
113 
114   for (const auto& test : tests) {
115     HttpAuthChallengeTokenizer tok(test.challenge);
116     EXPECT_EQ(test.expected_rv, basic->HandleAnotherChallenge(&tok));
117   }
118 }
119 
TEST(HttpAuthHandlerBasicTest,InitFromChallenge)120 TEST(HttpAuthHandlerBasicTest, InitFromChallenge) {
121   static const struct {
122     const char* challenge;
123     int expected_rv;
124     const char* expected_realm;
125   } tests[] = {
126     // No realm (we allow this even though realm is supposed to be required
127     // according to RFC 2617.)
128     {
129       "Basic",
130       OK,
131       "",
132     },
133 
134     // Realm is empty string.
135     {
136       "Basic realm=\"\"",
137       OK,
138       "",
139     },
140 
141     // Realm is valid.
142     {
143       "Basic realm=\"test_realm\"",
144       OK,
145       "test_realm",
146     },
147 
148     // The parser ignores tokens which aren't known.
149     {
150       "Basic realm=\"test_realm\",unknown_token=foobar",
151       OK,
152       "test_realm",
153     },
154 
155     // The parser skips over tokens which aren't known.
156     {
157       "Basic unknown_token=foobar,realm=\"test_realm\"",
158       OK,
159       "test_realm",
160     },
161 
162 #if 0
163     // TODO(cbentzel): It's unclear what the parser should do in these cases.
164     //                 It seems like this should either be treated as invalid,
165     //                 or the spaces should be used as a separator.
166     {
167       "Basic realm=\"test_realm\" unknown_token=foobar",
168       OK,
169       "test_realm",
170     },
171 
172     // The parser skips over tokens which aren't known.
173     {
174       "Basic unknown_token=foobar realm=\"test_realm\"",
175       OK,
176       "test_realm",
177     },
178 #endif
179 
180     // The parser fails when the first token is not "Basic".
181     {
182       "Negotiate",
183       ERR_INVALID_RESPONSE,
184       ""
185     },
186 
187     // Although RFC 2617 isn't explicit about this case, if there is
188     // more than one realm directive, we pick the last one.
189     {
190       "Basic realm=\"foo\",realm=\"bar\"",
191       OK,
192       "bar",
193     },
194 
195     // Handle ISO-8859-1 character as part of the realm. The realm is converted
196     // to UTF-8.
197     {
198       "Basic realm=\"foo-\xE5\"",
199       OK,
200       "foo-\xC3\xA5",
201     },
202   };
203   HttpAuthHandlerBasic::Factory factory;
204   url::SchemeHostPort scheme_host_port(GURL("http://www.example.com"));
205   for (const auto& test : tests) {
206     std::string challenge = test.challenge;
207     SSLInfo null_ssl_info;
208     auto host_resolver = std::make_unique<MockHostResolver>();
209     std::unique_ptr<HttpAuthHandler> basic;
210     int rv = factory.CreateAuthHandlerFromString(
211         challenge, HttpAuth::AUTH_SERVER, null_ssl_info,
212         NetworkAnonymizationKey(), scheme_host_port, NetLogWithSource(),
213         host_resolver.get(), &basic);
214     EXPECT_EQ(test.expected_rv, rv);
215     if (rv == OK)
216       EXPECT_EQ(test.expected_realm, basic->realm());
217   }
218 }
219 
220 // Test that when Basic is configured to forbid HTTP, attempting to create a
221 // Basic auth handler for a HTTP context is rejected.
TEST(HttpAuthHandlerBasicTest,BasicAuthRequiresHTTPS)222 TEST(HttpAuthHandlerBasicTest, BasicAuthRequiresHTTPS) {
223   url::SchemeHostPort nonsecure_scheme_host_port(
224       GURL("http://www.example.com"));
225   HttpAuthHandlerBasic::Factory factory;
226   HttpAuthPreferences http_auth_preferences;
227   http_auth_preferences.set_basic_over_http_enabled(false);
228   factory.set_http_auth_preferences(&http_auth_preferences);
229 
230   std::string challenge = "Basic realm=\"Atlantis\"";
231   SSLInfo null_ssl_info;
232   auto host_resolver = std::make_unique<MockHostResolver>();
233   std::unique_ptr<HttpAuthHandler> basic;
234 
235   // Ensure that HTTP is disallowed.
236   EXPECT_THAT(factory.CreateAuthHandlerFromString(
237                   challenge, HttpAuth::AUTH_SERVER, null_ssl_info,
238                   NetworkAnonymizationKey(), nonsecure_scheme_host_port,
239                   NetLogWithSource(), host_resolver.get(), &basic),
240               IsError(ERR_UNSUPPORTED_AUTH_SCHEME));
241 
242   // Ensure that HTTPS is allowed.
243   url::SchemeHostPort secure_scheme_host_port(GURL("https://www.example.com"));
244   EXPECT_THAT(factory.CreateAuthHandlerFromString(
245                   challenge, HttpAuth::AUTH_SERVER, null_ssl_info,
246                   NetworkAnonymizationKey(), secure_scheme_host_port,
247                   NetLogWithSource(), host_resolver.get(), &basic),
248               IsOk());
249 }
250 
251 }  // namespace net
252