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