blob: 3106b59ba516d3ecaa1affe103cd1f1596d34755 [file] [log] [blame]
Andreea Costinas44cefa22020-03-09 09:07:39 +01001// Copyright 2020 The Chromium OS 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 "system-proxy/server_proxy.h"
6
7#include <netinet/in.h>
Andreea Costinase45d54b2020-03-10 09:21:14 +01008#include <sys/socket.h>
9#include <sys/types.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010010
11#include <gmock/gmock.h>
12#include <gtest/gtest.h>
13#include <utility>
14
15#include <base/bind.h>
16#include <base/bind_helpers.h>
17#include <base/callback_helpers.h>
18#include <base/files/file_util.h>
19#include <base/files/scoped_file.h>
Andreea Costinase45d54b2020-03-10 09:21:14 +010020#include <base/strings/string_util.h>
Qijiang Fan34014672020-07-20 16:05:38 +090021#include <base/task/single_thread_task_executor.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010022#include <brillo/dbus/async_event_sequencer.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010023#include <brillo/message_loops/base_message_loop.h>
Garrick Evanscd8c2972020-04-14 14:35:52 +090024#include <chromeos/patchpanel/socket.h>
25#include <chromeos/patchpanel/socket_forwarder.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010026#include "bindings/worker_common.pb.h"
27#include "system-proxy/protobuf_util.h"
Andreea Costinase45d54b2020-03-10 09:21:14 +010028#include "system-proxy/proxy_connect_job.h"
Andreea Costinas44cefa22020-03-09 09:07:39 +010029
30namespace system_proxy {
31namespace {
Andreea Costinase45d54b2020-03-10 09:21:14 +010032constexpr char kUsername[] = "proxy:user";
33constexpr char kUsernameEncoded[] = "proxy%3Auser";
34constexpr char kPassword[] = "proxy password";
35constexpr char kPasswordEncoded[] = "proxy%20password";
Andreea Costinas44cefa22020-03-09 09:07:39 +010036constexpr int kTestPort = 3128;
Andreea Costinas5862b102020-03-19 14:45:36 +010037constexpr char kFakeProxyAddress[] = "http://127.0.0.1";
Andreea Costinase45d54b2020-03-10 09:21:14 +010038
Andreea Costinas44cefa22020-03-09 09:07:39 +010039} // namespace
40
Andreea Costinase45d54b2020-03-10 09:21:14 +010041using ::testing::_;
Andreea Costinas44cefa22020-03-09 09:07:39 +010042using ::testing::Return;
43
44class MockServerProxy : public ServerProxy {
45 public:
46 explicit MockServerProxy(base::OnceClosure quit_closure)
47 : ServerProxy(std::move(quit_closure)) {}
48 MockServerProxy(const MockServerProxy&) = delete;
49 MockServerProxy& operator=(const MockServerProxy&) = delete;
50 ~MockServerProxy() override = default;
51
52 MOCK_METHOD(int, GetStdinPipe, (), (override));
Andreea Costinas5862b102020-03-19 14:45:36 +010053 MOCK_METHOD(int, GetStdoutPipe, (), (override));
Andreea Costinas44cefa22020-03-09 09:07:39 +010054};
55
Andreea Costinase45d54b2020-03-10 09:21:14 +010056class MockProxyConnectJob : public ProxyConnectJob {
57 public:
Garrick Evans3388a032020-03-24 11:25:55 +090058 MockProxyConnectJob(std::unique_ptr<patchpanel::Socket> socket,
Andreea Costinase45d54b2020-03-10 09:21:14 +010059 const std::string& credentials,
60 ResolveProxyCallback resolve_proxy_callback,
Andreea Costinasbb2aa022020-06-13 00:03:23 +020061 AuthenticationRequiredCallback auth_required_callback,
Andreea Costinase45d54b2020-03-10 09:21:14 +010062 OnConnectionSetupFinishedCallback setup_finished_callback)
63 : ProxyConnectJob(std::move(socket),
64 credentials,
65 std::move(resolve_proxy_callback),
Andreea Costinasbb2aa022020-06-13 00:03:23 +020066 std::move(auth_required_callback),
Andreea Costinase45d54b2020-03-10 09:21:14 +010067 std::move(setup_finished_callback)) {}
68 MockProxyConnectJob(const MockProxyConnectJob&) = delete;
69 MockProxyConnectJob& operator=(const MockProxyConnectJob&) = delete;
70 ~MockProxyConnectJob() override = default;
71
72 MOCK_METHOD(bool, Start, (), (override));
73};
74
Andreea Costinas44cefa22020-03-09 09:07:39 +010075class ServerProxyTest : public ::testing::Test {
76 public:
77 ServerProxyTest() {
78 server_proxy_ =
79 std::make_unique<MockServerProxy>(brillo_loop_.QuitClosure());
80 }
81
82 ServerProxyTest(const ServerProxyTest&) = delete;
83 ServerProxyTest& operator=(const ServerProxyTest&) = delete;
84 ~ServerProxyTest() override {}
85
86 protected:
Andreea Costinas5862b102020-03-19 14:45:36 +010087 // Redirects the standard streams of the worker so that the tests can write
88 // data in the worker's stdin input and read data from the worker's stdout
89 // output.
Andreea Costinas44cefa22020-03-09 09:07:39 +010090 void RedirectStdPipes() {
91 int fds[2];
92 CHECK(base::CreateLocalNonBlockingPipe(fds));
Andreea Costinas5862b102020-03-19 14:45:36 +010093 stdin_read_fd_.reset(fds[0]);
94 stdin_write_fd_.reset(fds[1]);
95 CHECK(base::CreateLocalNonBlockingPipe(fds));
96 stdout_read_fd_.reset(fds[0]);
97 stdout_write_fd_.reset(fds[1]);
Andreea Costinas44cefa22020-03-09 09:07:39 +010098
Andreea Costinas5862b102020-03-19 14:45:36 +010099 ON_CALL(*server_proxy_, GetStdinPipe())
100 .WillByDefault(Return(stdin_read_fd_.get()));
101 // Don't redirect all the calls to |stdout_write_fd_| or the test result
102 // will not be printed in the console. Instead, when wanting to read the
103 // standard output, set the expectation to once return |stdout_write_fd_|.
104 ON_CALL(*server_proxy_, GetStdoutPipe())
105 .WillByDefault(Return(STDOUT_FILENO));
Andreea Costinas44cefa22020-03-09 09:07:39 +0100106 server_proxy_->Init();
107 }
108 // SystemProxyAdaptor instance that creates fake worker processes.
109 std::unique_ptr<MockServerProxy> server_proxy_;
Qijiang Fan34014672020-07-20 16:05:38 +0900110 base::SingleThreadTaskExecutor task_executor_{base::MessagePumpType::IO};
111 brillo::BaseMessageLoop brillo_loop_{task_executor_.task_runner()};
Andreea Costinas5862b102020-03-19 14:45:36 +0100112 base::ScopedFD stdin_read_fd_, stdin_write_fd_, stdout_read_fd_,
113 stdout_write_fd_;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100114};
115
116TEST_F(ServerProxyTest, FetchCredentials) {
Andreea Costinasaae97382020-05-05 13:31:58 +0200117 worker::Credentials credentials;
Andreea Costinase45d54b2020-03-10 09:21:14 +0100118 credentials.set_username(kUsername);
Andreea Costinas44cefa22020-03-09 09:07:39 +0100119 credentials.set_password(kPassword);
Andreea Costinasaae97382020-05-05 13:31:58 +0200120 worker::WorkerConfigs configs;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100121 *configs.mutable_credentials() = credentials;
122 RedirectStdPipes();
123
Andreea Costinas5862b102020-03-19 14:45:36 +0100124 EXPECT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
Andreea Costinas44cefa22020-03-09 09:07:39 +0100125
126 brillo_loop_.RunOnce(false);
127
Andreea Costinase45d54b2020-03-10 09:21:14 +0100128 std::string expected_credentials =
129 base::JoinString({kUsernameEncoded, kPasswordEncoded}, ":");
Andreea Costinasbb2aa022020-06-13 00:03:23 +0200130 EXPECT_EQ(server_proxy_->system_credentials_, expected_credentials);
Andreea Costinas44cefa22020-03-09 09:07:39 +0100131}
132
133TEST_F(ServerProxyTest, FetchListeningAddress) {
Andreea Costinasaae97382020-05-05 13:31:58 +0200134 worker::SocketAddress address;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100135 address.set_addr(INADDR_ANY);
136 address.set_port(kTestPort);
Andreea Costinasaae97382020-05-05 13:31:58 +0200137 worker::WorkerConfigs configs;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100138 *configs.mutable_listening_address() = address;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100139 // Redirect the worker stdin and stdout pipes.
Andreea Costinas5862b102020-03-19 14:45:36 +0100140 RedirectStdPipes();
141 // Send the config to the worker's stdin input.
142 EXPECT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
Andreea Costinas44cefa22020-03-09 09:07:39 +0100143 brillo_loop_.RunOnce(false);
144
145 EXPECT_EQ(server_proxy_->listening_addr_, INADDR_ANY);
146 EXPECT_EQ(server_proxy_->listening_port_, kTestPort);
147}
148
Andreea Costinas5862b102020-03-19 14:45:36 +0100149// Tests that ServerProxy handles the basic flow of a connect request:
150// - server accepts a connection a creates a job for it until the connection is
151// finished;
152// - the connect request from the client socket is read and parsed;
153// - proxy resolution request is correctly handled by the job and ServerProxy;
154// - client is sent an HTTP error code in case of failure;
155// - the failed connection job is removed from the queue.
Andreea Costinase45d54b2020-03-10 09:21:14 +0100156TEST_F(ServerProxyTest, HandleConnectRequest) {
157 server_proxy_->listening_addr_ = htonl(INADDR_LOOPBACK);
158 server_proxy_->listening_port_ = kTestPort;
159 // Redirect the worker stdin and stdout pipes.
160 RedirectStdPipes();
161 server_proxy_->CreateListeningSocket();
Andreea Costinase45d54b2020-03-10 09:21:14 +0100162 CHECK_NE(-1, server_proxy_->listening_fd_->fd());
163 brillo_loop_.RunOnce(false);
164
165 struct sockaddr_in ipv4addr;
166 ipv4addr.sin_family = AF_INET;
167 ipv4addr.sin_port = htons(kTestPort);
168 ipv4addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
169
170 auto client_socket =
Garrick Evans3388a032020-03-24 11:25:55 +0900171 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM);
Andreea Costinase45d54b2020-03-10 09:21:14 +0100172 EXPECT_TRUE(client_socket->Connect((const struct sockaddr*)&ipv4addr,
173 sizeof(ipv4addr)));
174 brillo_loop_.RunOnce(false);
175
176 EXPECT_EQ(1, server_proxy_->pending_connect_jobs_.size());
Andreea Costinas5862b102020-03-19 14:45:36 +0100177 const std::string_view http_req =
178 "CONNECT www.example.server.com:443 HTTP/1.1\r\n\r\n";
179 client_socket->SendTo(http_req.data(), http_req.size());
180
181 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
182 .WillOnce(Return(stdout_write_fd_.get()));
183 brillo_loop_.RunOnce(false);
Andreea Costinasaae97382020-05-05 13:31:58 +0200184 worker::WorkerRequest request;
Andreea Costinas5862b102020-03-19 14:45:36 +0100185 // Read the request from the worker's stdout output.
186 ASSERT_TRUE(ReadProtobuf(stdout_read_fd_.get(), &request));
187 ASSERT_TRUE(request.has_proxy_resolution_request());
188
Andreea Costinasa89309d2020-05-08 15:51:12 +0200189 EXPECT_EQ("https://www.example.server.com:443",
Andreea Costinas5862b102020-03-19 14:45:36 +0100190 request.proxy_resolution_request().target_url());
191
192 EXPECT_EQ(1, server_proxy_->pending_proxy_resolution_requests_.size());
193
194 // Write reply with a fake proxy to the worker's standard input.
Andreea Costinasaae97382020-05-05 13:31:58 +0200195 worker::ProxyResolutionReply reply;
Andreea Costinas5862b102020-03-19 14:45:36 +0100196 reply.set_target_url(request.proxy_resolution_request().target_url());
197 reply.add_proxy_servers(kFakeProxyAddress);
Andreea Costinasaae97382020-05-05 13:31:58 +0200198 worker::WorkerConfigs configs;
Andreea Costinas5862b102020-03-19 14:45:36 +0100199 *configs.mutable_proxy_resolution_reply() = reply;
200
201 ASSERT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
202 brillo_loop_.RunOnce(false);
203
204 // Verify that the correct HTTP error code is sent to the client. Because
205 // curl_perform will fail, this will be reported as an internal server error.
206 const std::string expected_http_reply =
207 "HTTP/1.1 500 Internal Server Error - Origin: local proxy\r\n\r\n";
208 std::vector<char> buf(expected_http_reply.size());
209 ASSERT_TRUE(base::ReadFromFD(client_socket->fd(), buf.data(), buf.size()));
210 buf.push_back('\0');
211 const std::string actual_http_reply(buf.data());
212 EXPECT_EQ(expected_http_reply, actual_http_reply);
213 EXPECT_EQ(0, server_proxy_->pending_connect_jobs_.size());
Andreea Costinase45d54b2020-03-10 09:21:14 +0100214}
215
216// Tests the |OnConnectionSetupFinished| callback is handled correctly in case
217// of success or error.
218TEST_F(ServerProxyTest, HandlePendingJobs) {
219 int connection_count = 100;
220 int success_count = 51;
221 int failure_count = 49;
222 // Create |connection_count| connections.
223 for (int i = 0; i < connection_count; ++i) {
224 auto client_socket =
Garrick Evans3388a032020-03-24 11:25:55 +0900225 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM);
Andreea Costinase45d54b2020-03-10 09:21:14 +0100226 auto mock_connect_job = std::make_unique<MockProxyConnectJob>(
227 std::move(client_socket), "" /* credentials */,
228 base::BindOnce([](const std::string& target_url,
229 OnProxyResolvedCallback callback) {}),
Andreea Costinased9e6122020-08-12 12:06:19 +0200230 base::BindRepeating([](const std::string& proxy_url,
231 const std::string& realm,
232 const std::string& scheme,
233 const std::string& bad_cached_credentials,
234 OnAuthAcquiredCallback callback) {}),
Andreea Costinase45d54b2020-03-10 09:21:14 +0100235 base::BindOnce(&ServerProxy::OnConnectionSetupFinished,
236 base::Unretained(server_proxy_.get())));
237 server_proxy_->pending_connect_jobs_[mock_connect_job.get()] =
238 std::move(mock_connect_job);
239 }
240 // Resolve |failure_count| pending connections with error.
241 for (int i = 0; i < failure_count; ++i) {
242 auto job_iter = server_proxy_->pending_connect_jobs_.begin();
243 std::move(job_iter->second->setup_finished_callback_)
244 .Run(nullptr, job_iter->first);
245 }
246 // Expect failed requests have been cleared from the pending list and no
247 // forwarder.
248 EXPECT_EQ(success_count, server_proxy_->pending_connect_jobs_.size());
249 EXPECT_EQ(0, server_proxy_->forwarders_.size());
250
251 // Resolve |success_count| successful connections.
252 for (int i = 0; i < success_count; ++i) {
Garrick Evans3388a032020-03-24 11:25:55 +0900253 auto fwd = std::make_unique<patchpanel::SocketForwarder>(
Andreea Costinase45d54b2020-03-10 09:21:14 +0100254 "" /* thread name */,
Garrick Evans3388a032020-03-24 11:25:55 +0900255 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM),
256 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM));
Andreea Costinase45d54b2020-03-10 09:21:14 +0100257 fwd->Start();
258 auto job_iter = server_proxy_->pending_connect_jobs_.begin();
259 std::move(job_iter->second->setup_finished_callback_)
260 .Run(std::move(fwd), job_iter->first);
261 }
262
263 // Expect the successful requests to have been cleared and |success_count|
264 // active forwarders.
265 EXPECT_EQ(0, server_proxy_->pending_connect_jobs_.size());
266 EXPECT_EQ(success_count, server_proxy_->forwarders_.size());
267}
268
Andreea Costinas833eb7c2020-06-12 11:09:15 +0200269// Test to ensure proxy resolution requests are correctly handled if the
270// associated job is canceled before resolution.
271TEST_F(ServerProxyTest, HandleCanceledJobWhilePendingProxyResolution) {
272 server_proxy_->listening_addr_ = htonl(INADDR_LOOPBACK);
273 server_proxy_->listening_port_ = 3129;
274 // Redirect the worker stdin and stdout pipes.
275 RedirectStdPipes();
276 server_proxy_->CreateListeningSocket();
277 CHECK_NE(-1, server_proxy_->listening_fd_->fd());
278 brillo_loop_.RunOnce(false);
279
280 struct sockaddr_in ipv4addr;
281 ipv4addr.sin_family = AF_INET;
282 ipv4addr.sin_port = htons(3129);
283 ipv4addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
284
285 auto client_socket =
286 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM);
287 EXPECT_TRUE(client_socket->Connect((const struct sockaddr*)&ipv4addr,
288 sizeof(ipv4addr)));
289 brillo_loop_.RunOnce(false);
290
291 EXPECT_EQ(1, server_proxy_->pending_connect_jobs_.size());
292 const std::string_view http_req =
293 "CONNECT www.example.server.com:443 HTTP/1.1\r\n\r\n";
294 client_socket->SendTo(http_req.data(), http_req.size());
295
296 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
297 .WillOnce(Return(stdout_write_fd_.get()));
298 brillo_loop_.RunOnce(false);
299
300 EXPECT_EQ(1, server_proxy_->pending_connect_jobs_.size());
301 server_proxy_->pending_connect_jobs_.clear();
302
303 EXPECT_EQ(1, server_proxy_->pending_proxy_resolution_requests_.size());
304 server_proxy_->OnProxyResolved("https://www.example.server.com:443", {});
305
306 EXPECT_EQ(0, server_proxy_->pending_proxy_resolution_requests_.size());
307}
308
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200309// This test verifies that the athentication request is forwarded to the parent
310// process and that the pending authentication requests are resolved when the
311// parent sends the credentials associated with the protection space included in
312// the request.
313TEST_F(ServerProxyTest, HandlePendingAuthRequests) {
314 RedirectStdPipes();
315
316 worker::ProtectionSpace protection_space;
317 protection_space.set_origin(kFakeProxyAddress);
318 protection_space.set_scheme("Basic");
319 protection_space.set_realm("Proxy test realm");
320 std::string actual_credentials = "";
321
322 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
323 .WillOnce(Return(stdout_write_fd_.get()));
324
325 server_proxy_->AuthenticationRequired(
326 protection_space.origin(), protection_space.scheme(),
Andreea Costinased9e6122020-08-12 12:06:19 +0200327 protection_space.realm(), /* bad_cached_credentials = */ "",
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200328 base::Bind(
329 [](std::string* actual_credentials, const std::string& credentials) {
330 *actual_credentials = credentials;
331 },
332 &actual_credentials));
333
334 EXPECT_EQ(1, server_proxy_->pending_auth_required_requests_.size());
335 EXPECT_EQ(protection_space.SerializeAsString(),
336 server_proxy_->pending_auth_required_requests_.begin()->first);
337
338 brillo_loop_.RunOnce(false);
339
340 worker::WorkerRequest request;
341 // Read the request from the worker's stdout output.
342 ASSERT_TRUE(ReadProtobuf(stdout_read_fd_.get(), &request));
343 ASSERT_TRUE(request.has_auth_required_request());
344 ASSERT_TRUE(request.auth_required_request().has_protection_space());
345 EXPECT_EQ(
346 request.auth_required_request().protection_space().SerializeAsString(),
347 protection_space.SerializeAsString());
348
349 // Write reply with a fake credentials to the worker's standard input.
350 worker::Credentials credentials;
351 *credentials.mutable_protection_space() = protection_space;
352 credentials.set_username("test_user");
353 credentials.set_password("test_pwd");
354 worker::WorkerConfigs configs;
355 *configs.mutable_credentials() = credentials;
356
357 ASSERT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
358 brillo_loop_.RunOnce(false);
359 EXPECT_EQ(0, server_proxy_->pending_auth_required_requests_.size());
360 EXPECT_EQ("test_user:test_pwd", actual_credentials);
361}
362
363// This test verifies that pending athentication requests are solved when the
364// parent returns empty credentials for the protection space.
365TEST_F(ServerProxyTest, HandlePendingAuthRequestsNoCredentials) {
366 RedirectStdPipes();
367
368 worker::ProtectionSpace protection_space;
369 protection_space.set_origin(kFakeProxyAddress);
370 protection_space.set_scheme("Basic");
371 protection_space.set_realm("Proxy test realm");
372 std::string actual_credentials = "";
373
374 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
375 .WillOnce(Return(stdout_write_fd_.get()));
376
377 server_proxy_->AuthenticationRequired(
378 protection_space.origin(), protection_space.scheme(),
Andreea Costinased9e6122020-08-12 12:06:19 +0200379 protection_space.realm(), /* bad_cached_credentials = */ "",
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200380 base::Bind(
381 [](std::string* actual_credentials, const std::string& credentials) {
382 *actual_credentials = credentials;
383 },
384 &actual_credentials));
385
386 EXPECT_EQ(1, server_proxy_->pending_auth_required_requests_.size());
387 EXPECT_EQ(protection_space.SerializeAsString(),
388 server_proxy_->pending_auth_required_requests_.begin()->first);
389
390 brillo_loop_.RunOnce(false);
391
392 worker::WorkerRequest request;
393 // Read the request from the worker's stdout output.
394 ASSERT_TRUE(ReadProtobuf(stdout_read_fd_.get(), &request));
395 ASSERT_TRUE(request.has_auth_required_request());
396 ASSERT_TRUE(request.auth_required_request().has_protection_space());
397 EXPECT_EQ(
398 request.auth_required_request().protection_space().SerializeAsString(),
399 protection_space.SerializeAsString());
400
401 // Write reply with a fake credentials to the worker's standard input.
402 worker::Credentials credentials;
403 *credentials.mutable_protection_space() = protection_space;
404 worker::WorkerConfigs configs;
405 *configs.mutable_credentials() = credentials;
406
407 ASSERT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
408 brillo_loop_.RunOnce(false);
409 EXPECT_EQ(0, server_proxy_->pending_auth_required_requests_.size());
410 EXPECT_EQ("", actual_credentials);
411}
412
413// This test verifies that the athentication request is solved with cached
414// credentials.
415TEST_F(ServerProxyTest, HandlePendingAuthRequestsCachedCredentials) {
416 RedirectStdPipes();
417
418 worker::ProtectionSpace protection_space;
419 protection_space.set_origin(kFakeProxyAddress);
420 protection_space.set_scheme("Basic");
421 protection_space.set_realm("Proxy test realm");
422 std::string actual_credentials = "";
423
424 server_proxy_->auth_cache_[protection_space.SerializeAsString()] =
425 "test_user:test_pwd";
426
427 server_proxy_->AuthenticationRequired(
428 protection_space.origin(), protection_space.scheme(),
Andreea Costinased9e6122020-08-12 12:06:19 +0200429 protection_space.realm(), /* bad_cached_credentials = */ "",
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200430 base::Bind(
431 [](std::string* actual_credentials, const std::string& credentials) {
432 *actual_credentials = credentials;
433 },
434 &actual_credentials));
435
436 brillo_loop_.RunOnce(false);
437 EXPECT_EQ(0, server_proxy_->pending_auth_required_requests_.size());
438 EXPECT_EQ("test_user:test_pwd", actual_credentials);
439}
440
Andreea Costinase9c73592020-07-17 15:27:54 +0200441// This test verifies that the stored credentials are removed when receiving a
442// |ClearUserCredentials| request.
443TEST_F(ServerProxyTest, ClearUserCredentials) {
444 worker::ProtectionSpace protection_space;
445 protection_space.set_origin(kFakeProxyAddress);
446 protection_space.set_scheme("Basic");
447 protection_space.set_realm("Proxy test realm");
448 // Add an entry in the cache.
449 server_proxy_->auth_cache_[protection_space.SerializeAsString()] =
450 "test_user:test_pwd";
451
452 worker::ClearUserCredentials clear_user_credentials;
453 worker::WorkerConfigs configs;
454 *configs.mutable_clear_user_credentials() = clear_user_credentials;
455 // Redirect the worker stdin and stdout pipes.
456 RedirectStdPipes();
457 // Send the config to the worker's stdin input.
458 EXPECT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
459 brillo_loop_.RunOnce(false);
460 // Expect that the credentials were cleared.
461 EXPECT_EQ(0, server_proxy_->auth_cache_.size());
462}
463
Andreea Costinased9e6122020-08-12 12:06:19 +0200464// Verifies that even if there are credentials in the cache for the remote
465// web-proxy, the ServerProxy sends a request to the parent web-proxy if the
466// credentials are flagged as bad.
467TEST_F(ServerProxyTest, AuthRequestsBadCachedCredentials) {
468 constexpr char kBadCachedCredetials[] = "bad_user:bad_pwd";
469 constexpr char kCredetials[] = "test_user:test_pwd";
470
471 RedirectStdPipes();
472 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
473 .WillOnce(Return(stdout_write_fd_.get()));
474
475 // Add credentials to the cache for the proxy.
476 worker::ProtectionSpace protection_space;
477 protection_space.set_origin(kFakeProxyAddress);
478 protection_space.set_scheme("Basic");
479 protection_space.set_realm("Proxy test realm");
480 server_proxy_->auth_cache_[protection_space.SerializeAsString()] =
481 kBadCachedCredetials;
482
483 // Request credentials for the proxy.
484 std::string actual_credentials = "";
485 server_proxy_->AuthenticationRequired(
486 protection_space.origin(), protection_space.scheme(),
487 protection_space.realm(), kBadCachedCredetials,
488 base::Bind(
489 [](std::string* actual_credentials, const std::string& credentials) {
490 *actual_credentials = credentials;
491 },
492 &actual_credentials));
493
494 // Expect that the credentials are not served from the cache.
495 EXPECT_EQ(1, server_proxy_->pending_auth_required_requests_.size());
496 EXPECT_EQ(protection_space.SerializeAsString(),
497 server_proxy_->pending_auth_required_requests_.begin()->first);
498
499 brillo_loop_.RunOnce(false);
500
501 worker::WorkerRequest request;
502 // Read the request from the worker's stdout output.
503 ASSERT_TRUE(ReadProtobuf(stdout_read_fd_.get(), &request));
504 ASSERT_TRUE(request.has_auth_required_request());
505 ASSERT_TRUE(request.auth_required_request().has_protection_space());
506 EXPECT_EQ(
507 request.auth_required_request().protection_space().SerializeAsString(),
508 protection_space.SerializeAsString());
509
510 // Write reply with a fake credentials to the worker's standard input.
511 worker::Credentials credentials;
512 *credentials.mutable_protection_space() = protection_space;
513 credentials.set_username("test_user");
514 credentials.set_password("test_pwd");
515 worker::WorkerConfigs configs;
516 *configs.mutable_credentials() = credentials;
517
518 ASSERT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
519 brillo_loop_.RunOnce(false);
520 EXPECT_EQ(0, server_proxy_->pending_auth_required_requests_.size());
521 EXPECT_EQ(kCredetials, actual_credentials);
522}
523
Andreea Costinas44cefa22020-03-09 09:07:39 +0100524} // namespace system_proxy