blob: f84607bc914cf01810835f81988ceeb9d402d27e [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
Andreea Costinascc4d54e2020-10-19 15:46:25 +020011#include <curl/curl.h>
12
Andreea Costinas44cefa22020-03-09 09:07:39 +010013#include <gmock/gmock.h>
14#include <gtest/gtest.h>
15#include <utility>
16
17#include <base/bind.h>
18#include <base/bind_helpers.h>
19#include <base/callback_helpers.h>
20#include <base/files/file_util.h>
21#include <base/files/scoped_file.h>
Andreea Costinase45d54b2020-03-10 09:21:14 +010022#include <base/strings/string_util.h>
Qijiang Fan34014672020-07-20 16:05:38 +090023#include <base/task/single_thread_task_executor.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010024#include <brillo/dbus/async_event_sequencer.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010025#include <brillo/message_loops/base_message_loop.h>
Garrick Evanscd8c2972020-04-14 14:35:52 +090026#include <chromeos/patchpanel/socket.h>
27#include <chromeos/patchpanel/socket_forwarder.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010028#include "bindings/worker_common.pb.h"
29#include "system-proxy/protobuf_util.h"
Andreea Costinase45d54b2020-03-10 09:21:14 +010030#include "system-proxy/proxy_connect_job.h"
Andreea Costinas44cefa22020-03-09 09:07:39 +010031
32namespace system_proxy {
33namespace {
Andreea Costinase45d54b2020-03-10 09:21:14 +010034constexpr char kUsername[] = "proxy:user";
35constexpr char kUsernameEncoded[] = "proxy%3Auser";
36constexpr char kPassword[] = "proxy password";
37constexpr char kPasswordEncoded[] = "proxy%20password";
Andreea Costinas44cefa22020-03-09 09:07:39 +010038constexpr int kTestPort = 3128;
Andreea Costinas5862b102020-03-19 14:45:36 +010039constexpr char kFakeProxyAddress[] = "http://127.0.0.1";
Andreea Costinase45d54b2020-03-10 09:21:14 +010040
Andreea Costinas44cefa22020-03-09 09:07:39 +010041} // namespace
42
Andreea Costinase45d54b2020-03-10 09:21:14 +010043using ::testing::_;
Andreea Costinas44cefa22020-03-09 09:07:39 +010044using ::testing::Return;
45
46class MockServerProxy : public ServerProxy {
47 public:
48 explicit MockServerProxy(base::OnceClosure quit_closure)
49 : ServerProxy(std::move(quit_closure)) {}
50 MockServerProxy(const MockServerProxy&) = delete;
51 MockServerProxy& operator=(const MockServerProxy&) = delete;
52 ~MockServerProxy() override = default;
53
54 MOCK_METHOD(int, GetStdinPipe, (), (override));
Andreea Costinas5862b102020-03-19 14:45:36 +010055 MOCK_METHOD(int, GetStdoutPipe, (), (override));
Andreea Costinas44cefa22020-03-09 09:07:39 +010056};
57
Andreea Costinase45d54b2020-03-10 09:21:14 +010058class MockProxyConnectJob : public ProxyConnectJob {
59 public:
Garrick Evans3388a032020-03-24 11:25:55 +090060 MockProxyConnectJob(std::unique_ptr<patchpanel::Socket> socket,
Andreea Costinase45d54b2020-03-10 09:21:14 +010061 const std::string& credentials,
62 ResolveProxyCallback resolve_proxy_callback,
Andreea Costinasbb2aa022020-06-13 00:03:23 +020063 AuthenticationRequiredCallback auth_required_callback,
Andreea Costinase45d54b2020-03-10 09:21:14 +010064 OnConnectionSetupFinishedCallback setup_finished_callback)
65 : ProxyConnectJob(std::move(socket),
66 credentials,
Andreea Costinascc4d54e2020-10-19 15:46:25 +020067 CURLAUTH_ANY,
Andreea Costinase45d54b2020-03-10 09:21:14 +010068 std::move(resolve_proxy_callback),
Andreea Costinasbb2aa022020-06-13 00:03:23 +020069 std::move(auth_required_callback),
Andreea Costinase45d54b2020-03-10 09:21:14 +010070 std::move(setup_finished_callback)) {}
71 MockProxyConnectJob(const MockProxyConnectJob&) = delete;
72 MockProxyConnectJob& operator=(const MockProxyConnectJob&) = delete;
73 ~MockProxyConnectJob() override = default;
74
75 MOCK_METHOD(bool, Start, (), (override));
76};
77
Andreea Costinas44cefa22020-03-09 09:07:39 +010078class ServerProxyTest : public ::testing::Test {
79 public:
80 ServerProxyTest() {
81 server_proxy_ =
82 std::make_unique<MockServerProxy>(brillo_loop_.QuitClosure());
83 }
84
85 ServerProxyTest(const ServerProxyTest&) = delete;
86 ServerProxyTest& operator=(const ServerProxyTest&) = delete;
87 ~ServerProxyTest() override {}
88
89 protected:
Andreea Costinas5862b102020-03-19 14:45:36 +010090 // Redirects the standard streams of the worker so that the tests can write
91 // data in the worker's stdin input and read data from the worker's stdout
92 // output.
Andreea Costinas44cefa22020-03-09 09:07:39 +010093 void RedirectStdPipes() {
94 int fds[2];
95 CHECK(base::CreateLocalNonBlockingPipe(fds));
Andreea Costinas5862b102020-03-19 14:45:36 +010096 stdin_read_fd_.reset(fds[0]);
97 stdin_write_fd_.reset(fds[1]);
98 CHECK(base::CreateLocalNonBlockingPipe(fds));
99 stdout_read_fd_.reset(fds[0]);
100 stdout_write_fd_.reset(fds[1]);
Andreea Costinas44cefa22020-03-09 09:07:39 +0100101
Andreea Costinas5862b102020-03-19 14:45:36 +0100102 ON_CALL(*server_proxy_, GetStdinPipe())
103 .WillByDefault(Return(stdin_read_fd_.get()));
104 // Don't redirect all the calls to |stdout_write_fd_| or the test result
105 // will not be printed in the console. Instead, when wanting to read the
106 // standard output, set the expectation to once return |stdout_write_fd_|.
107 ON_CALL(*server_proxy_, GetStdoutPipe())
108 .WillByDefault(Return(STDOUT_FILENO));
Andreea Costinas44cefa22020-03-09 09:07:39 +0100109 server_proxy_->Init();
110 }
111 // SystemProxyAdaptor instance that creates fake worker processes.
112 std::unique_ptr<MockServerProxy> server_proxy_;
Qijiang Fan34014672020-07-20 16:05:38 +0900113 base::SingleThreadTaskExecutor task_executor_{base::MessagePumpType::IO};
114 brillo::BaseMessageLoop brillo_loop_{task_executor_.task_runner()};
Andreea Costinas5862b102020-03-19 14:45:36 +0100115 base::ScopedFD stdin_read_fd_, stdin_write_fd_, stdout_read_fd_,
116 stdout_write_fd_;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100117};
118
119TEST_F(ServerProxyTest, FetchCredentials) {
Andreea Costinasaae97382020-05-05 13:31:58 +0200120 worker::Credentials credentials;
Andreea Costinase45d54b2020-03-10 09:21:14 +0100121 credentials.set_username(kUsername);
Andreea Costinas44cefa22020-03-09 09:07:39 +0100122 credentials.set_password(kPassword);
Andreea Costinascc4d54e2020-10-19 15:46:25 +0200123 credentials.add_policy_credentials_auth_schemes("basic");
124 credentials.add_policy_credentials_auth_schemes("digest");
125
Andreea Costinasaae97382020-05-05 13:31:58 +0200126 worker::WorkerConfigs configs;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100127 *configs.mutable_credentials() = credentials;
128 RedirectStdPipes();
129
Andreea Costinas5862b102020-03-19 14:45:36 +0100130 EXPECT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
Andreea Costinas44cefa22020-03-09 09:07:39 +0100131
132 brillo_loop_.RunOnce(false);
133
Andreea Costinase45d54b2020-03-10 09:21:14 +0100134 std::string expected_credentials =
135 base::JoinString({kUsernameEncoded, kPasswordEncoded}, ":");
Andreea Costinasbb2aa022020-06-13 00:03:23 +0200136 EXPECT_EQ(server_proxy_->system_credentials_, expected_credentials);
Andreea Costinascc4d54e2020-10-19 15:46:25 +0200137 EXPECT_EQ(server_proxy_->system_credentials_auth_schemes_,
138 CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NEGOTIATE);
Andreea Costinas44cefa22020-03-09 09:07:39 +0100139}
140
141TEST_F(ServerProxyTest, FetchListeningAddress) {
Andreea Costinasaae97382020-05-05 13:31:58 +0200142 worker::SocketAddress address;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100143 address.set_addr(INADDR_ANY);
144 address.set_port(kTestPort);
Andreea Costinasaae97382020-05-05 13:31:58 +0200145 worker::WorkerConfigs configs;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100146 *configs.mutable_listening_address() = address;
Andreea Costinas44cefa22020-03-09 09:07:39 +0100147 // Redirect the worker stdin and stdout pipes.
Andreea Costinas5862b102020-03-19 14:45:36 +0100148 RedirectStdPipes();
149 // Send the config to the worker's stdin input.
150 EXPECT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
Andreea Costinas44cefa22020-03-09 09:07:39 +0100151 brillo_loop_.RunOnce(false);
152
153 EXPECT_EQ(server_proxy_->listening_addr_, INADDR_ANY);
154 EXPECT_EQ(server_proxy_->listening_port_, kTestPort);
155}
156
Andreea Costinas5862b102020-03-19 14:45:36 +0100157// Tests that ServerProxy handles the basic flow of a connect request:
158// - server accepts a connection a creates a job for it until the connection is
159// finished;
160// - the connect request from the client socket is read and parsed;
161// - proxy resolution request is correctly handled by the job and ServerProxy;
162// - client is sent an HTTP error code in case of failure;
163// - the failed connection job is removed from the queue.
Andreea Costinase45d54b2020-03-10 09:21:14 +0100164TEST_F(ServerProxyTest, HandleConnectRequest) {
165 server_proxy_->listening_addr_ = htonl(INADDR_LOOPBACK);
166 server_proxy_->listening_port_ = kTestPort;
167 // Redirect the worker stdin and stdout pipes.
168 RedirectStdPipes();
169 server_proxy_->CreateListeningSocket();
Andreea Costinase45d54b2020-03-10 09:21:14 +0100170 CHECK_NE(-1, server_proxy_->listening_fd_->fd());
171 brillo_loop_.RunOnce(false);
172
173 struct sockaddr_in ipv4addr;
174 ipv4addr.sin_family = AF_INET;
175 ipv4addr.sin_port = htons(kTestPort);
176 ipv4addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
177
178 auto client_socket =
Garrick Evans3388a032020-03-24 11:25:55 +0900179 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM);
Andreea Costinase45d54b2020-03-10 09:21:14 +0100180 EXPECT_TRUE(client_socket->Connect((const struct sockaddr*)&ipv4addr,
181 sizeof(ipv4addr)));
182 brillo_loop_.RunOnce(false);
183
184 EXPECT_EQ(1, server_proxy_->pending_connect_jobs_.size());
Andreea Costinas5862b102020-03-19 14:45:36 +0100185 const std::string_view http_req =
186 "CONNECT www.example.server.com:443 HTTP/1.1\r\n\r\n";
187 client_socket->SendTo(http_req.data(), http_req.size());
188
189 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
190 .WillOnce(Return(stdout_write_fd_.get()));
191 brillo_loop_.RunOnce(false);
Andreea Costinasaae97382020-05-05 13:31:58 +0200192 worker::WorkerRequest request;
Andreea Costinas5862b102020-03-19 14:45:36 +0100193 // Read the request from the worker's stdout output.
194 ASSERT_TRUE(ReadProtobuf(stdout_read_fd_.get(), &request));
195 ASSERT_TRUE(request.has_proxy_resolution_request());
196
Andreea Costinasa89309d2020-05-08 15:51:12 +0200197 EXPECT_EQ("https://www.example.server.com:443",
Andreea Costinas5862b102020-03-19 14:45:36 +0100198 request.proxy_resolution_request().target_url());
199
200 EXPECT_EQ(1, server_proxy_->pending_proxy_resolution_requests_.size());
201
202 // Write reply with a fake proxy to the worker's standard input.
Andreea Costinasaae97382020-05-05 13:31:58 +0200203 worker::ProxyResolutionReply reply;
Andreea Costinas5862b102020-03-19 14:45:36 +0100204 reply.set_target_url(request.proxy_resolution_request().target_url());
205 reply.add_proxy_servers(kFakeProxyAddress);
Andreea Costinasaae97382020-05-05 13:31:58 +0200206 worker::WorkerConfigs configs;
Andreea Costinas5862b102020-03-19 14:45:36 +0100207 *configs.mutable_proxy_resolution_reply() = reply;
208
209 ASSERT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
210 brillo_loop_.RunOnce(false);
211
212 // Verify that the correct HTTP error code is sent to the client. Because
213 // curl_perform will fail, this will be reported as an internal server error.
214 const std::string expected_http_reply =
215 "HTTP/1.1 500 Internal Server Error - Origin: local proxy\r\n\r\n";
216 std::vector<char> buf(expected_http_reply.size());
217 ASSERT_TRUE(base::ReadFromFD(client_socket->fd(), buf.data(), buf.size()));
218 buf.push_back('\0');
219 const std::string actual_http_reply(buf.data());
220 EXPECT_EQ(expected_http_reply, actual_http_reply);
221 EXPECT_EQ(0, server_proxy_->pending_connect_jobs_.size());
Andreea Costinase45d54b2020-03-10 09:21:14 +0100222}
223
224// Tests the |OnConnectionSetupFinished| callback is handled correctly in case
225// of success or error.
226TEST_F(ServerProxyTest, HandlePendingJobs) {
227 int connection_count = 100;
228 int success_count = 51;
229 int failure_count = 49;
230 // Create |connection_count| connections.
231 for (int i = 0; i < connection_count; ++i) {
232 auto client_socket =
Garrick Evans3388a032020-03-24 11:25:55 +0900233 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM);
Andreea Costinase45d54b2020-03-10 09:21:14 +0100234 auto mock_connect_job = std::make_unique<MockProxyConnectJob>(
235 std::move(client_socket), "" /* credentials */,
236 base::BindOnce([](const std::string& target_url,
237 OnProxyResolvedCallback callback) {}),
Andreea Costinased9e6122020-08-12 12:06:19 +0200238 base::BindRepeating([](const std::string& proxy_url,
239 const std::string& realm,
240 const std::string& scheme,
241 const std::string& bad_cached_credentials,
242 OnAuthAcquiredCallback callback) {}),
Andreea Costinase45d54b2020-03-10 09:21:14 +0100243 base::BindOnce(&ServerProxy::OnConnectionSetupFinished,
244 base::Unretained(server_proxy_.get())));
245 server_proxy_->pending_connect_jobs_[mock_connect_job.get()] =
246 std::move(mock_connect_job);
247 }
248 // Resolve |failure_count| pending connections with error.
249 for (int i = 0; i < failure_count; ++i) {
250 auto job_iter = server_proxy_->pending_connect_jobs_.begin();
251 std::move(job_iter->second->setup_finished_callback_)
252 .Run(nullptr, job_iter->first);
253 }
254 // Expect failed requests have been cleared from the pending list and no
255 // forwarder.
256 EXPECT_EQ(success_count, server_proxy_->pending_connect_jobs_.size());
257 EXPECT_EQ(0, server_proxy_->forwarders_.size());
258
259 // Resolve |success_count| successful connections.
260 for (int i = 0; i < success_count; ++i) {
Garrick Evans3388a032020-03-24 11:25:55 +0900261 auto fwd = std::make_unique<patchpanel::SocketForwarder>(
Andreea Costinase45d54b2020-03-10 09:21:14 +0100262 "" /* thread name */,
Garrick Evans3388a032020-03-24 11:25:55 +0900263 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM),
264 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM));
Andreea Costinase45d54b2020-03-10 09:21:14 +0100265 fwd->Start();
266 auto job_iter = server_proxy_->pending_connect_jobs_.begin();
267 std::move(job_iter->second->setup_finished_callback_)
268 .Run(std::move(fwd), job_iter->first);
269 }
270
271 // Expect the successful requests to have been cleared and |success_count|
272 // active forwarders.
273 EXPECT_EQ(0, server_proxy_->pending_connect_jobs_.size());
274 EXPECT_EQ(success_count, server_proxy_->forwarders_.size());
275}
276
Andreea Costinas833eb7c2020-06-12 11:09:15 +0200277// Test to ensure proxy resolution requests are correctly handled if the
278// associated job is canceled before resolution.
279TEST_F(ServerProxyTest, HandleCanceledJobWhilePendingProxyResolution) {
280 server_proxy_->listening_addr_ = htonl(INADDR_LOOPBACK);
281 server_proxy_->listening_port_ = 3129;
282 // Redirect the worker stdin and stdout pipes.
283 RedirectStdPipes();
284 server_proxy_->CreateListeningSocket();
285 CHECK_NE(-1, server_proxy_->listening_fd_->fd());
286 brillo_loop_.RunOnce(false);
287
288 struct sockaddr_in ipv4addr;
289 ipv4addr.sin_family = AF_INET;
290 ipv4addr.sin_port = htons(3129);
291 ipv4addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
292
293 auto client_socket =
294 std::make_unique<patchpanel::Socket>(AF_INET, SOCK_STREAM);
295 EXPECT_TRUE(client_socket->Connect((const struct sockaddr*)&ipv4addr,
296 sizeof(ipv4addr)));
297 brillo_loop_.RunOnce(false);
298
299 EXPECT_EQ(1, server_proxy_->pending_connect_jobs_.size());
300 const std::string_view http_req =
301 "CONNECT www.example.server.com:443 HTTP/1.1\r\n\r\n";
302 client_socket->SendTo(http_req.data(), http_req.size());
303
304 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
305 .WillOnce(Return(stdout_write_fd_.get()));
306 brillo_loop_.RunOnce(false);
307
308 EXPECT_EQ(1, server_proxy_->pending_connect_jobs_.size());
309 server_proxy_->pending_connect_jobs_.clear();
310
311 EXPECT_EQ(1, server_proxy_->pending_proxy_resolution_requests_.size());
312 server_proxy_->OnProxyResolved("https://www.example.server.com:443", {});
313
314 EXPECT_EQ(0, server_proxy_->pending_proxy_resolution_requests_.size());
315}
316
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200317// This test verifies that the athentication request is forwarded to the parent
318// process and that the pending authentication requests are resolved when the
319// parent sends the credentials associated with the protection space included in
320// the request.
321TEST_F(ServerProxyTest, HandlePendingAuthRequests) {
322 RedirectStdPipes();
323
324 worker::ProtectionSpace protection_space;
325 protection_space.set_origin(kFakeProxyAddress);
326 protection_space.set_scheme("Basic");
327 protection_space.set_realm("Proxy test realm");
328 std::string actual_credentials = "";
329
330 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
331 .WillOnce(Return(stdout_write_fd_.get()));
332
333 server_proxy_->AuthenticationRequired(
334 protection_space.origin(), protection_space.scheme(),
Andreea Costinased9e6122020-08-12 12:06:19 +0200335 protection_space.realm(), /* bad_cached_credentials = */ "",
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200336 base::Bind(
337 [](std::string* actual_credentials, const std::string& credentials) {
338 *actual_credentials = credentials;
339 },
340 &actual_credentials));
341
342 EXPECT_EQ(1, server_proxy_->pending_auth_required_requests_.size());
343 EXPECT_EQ(protection_space.SerializeAsString(),
344 server_proxy_->pending_auth_required_requests_.begin()->first);
345
346 brillo_loop_.RunOnce(false);
347
348 worker::WorkerRequest request;
349 // Read the request from the worker's stdout output.
350 ASSERT_TRUE(ReadProtobuf(stdout_read_fd_.get(), &request));
351 ASSERT_TRUE(request.has_auth_required_request());
352 ASSERT_TRUE(request.auth_required_request().has_protection_space());
353 EXPECT_EQ(
354 request.auth_required_request().protection_space().SerializeAsString(),
355 protection_space.SerializeAsString());
356
357 // Write reply with a fake credentials to the worker's standard input.
358 worker::Credentials credentials;
359 *credentials.mutable_protection_space() = protection_space;
360 credentials.set_username("test_user");
361 credentials.set_password("test_pwd");
362 worker::WorkerConfigs configs;
363 *configs.mutable_credentials() = credentials;
364
365 ASSERT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
366 brillo_loop_.RunOnce(false);
367 EXPECT_EQ(0, server_proxy_->pending_auth_required_requests_.size());
368 EXPECT_EQ("test_user:test_pwd", actual_credentials);
369}
370
371// This test verifies that pending athentication requests are solved when the
372// parent returns empty credentials for the protection space.
373TEST_F(ServerProxyTest, HandlePendingAuthRequestsNoCredentials) {
374 RedirectStdPipes();
375
376 worker::ProtectionSpace protection_space;
377 protection_space.set_origin(kFakeProxyAddress);
378 protection_space.set_scheme("Basic");
379 protection_space.set_realm("Proxy test realm");
380 std::string actual_credentials = "";
381
382 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
383 .WillOnce(Return(stdout_write_fd_.get()));
384
385 server_proxy_->AuthenticationRequired(
386 protection_space.origin(), protection_space.scheme(),
Andreea Costinased9e6122020-08-12 12:06:19 +0200387 protection_space.realm(), /* bad_cached_credentials = */ "",
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200388 base::Bind(
389 [](std::string* actual_credentials, const std::string& credentials) {
390 *actual_credentials = credentials;
391 },
392 &actual_credentials));
393
394 EXPECT_EQ(1, server_proxy_->pending_auth_required_requests_.size());
395 EXPECT_EQ(protection_space.SerializeAsString(),
396 server_proxy_->pending_auth_required_requests_.begin()->first);
397
398 brillo_loop_.RunOnce(false);
399
400 worker::WorkerRequest request;
401 // Read the request from the worker's stdout output.
402 ASSERT_TRUE(ReadProtobuf(stdout_read_fd_.get(), &request));
403 ASSERT_TRUE(request.has_auth_required_request());
404 ASSERT_TRUE(request.auth_required_request().has_protection_space());
405 EXPECT_EQ(
406 request.auth_required_request().protection_space().SerializeAsString(),
407 protection_space.SerializeAsString());
408
409 // Write reply with a fake credentials to the worker's standard input.
410 worker::Credentials credentials;
411 *credentials.mutable_protection_space() = protection_space;
412 worker::WorkerConfigs configs;
413 *configs.mutable_credentials() = credentials;
414
415 ASSERT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
416 brillo_loop_.RunOnce(false);
417 EXPECT_EQ(0, server_proxy_->pending_auth_required_requests_.size());
418 EXPECT_EQ("", actual_credentials);
419}
420
421// This test verifies that the athentication request is solved with cached
422// credentials.
423TEST_F(ServerProxyTest, HandlePendingAuthRequestsCachedCredentials) {
424 RedirectStdPipes();
425
426 worker::ProtectionSpace protection_space;
427 protection_space.set_origin(kFakeProxyAddress);
428 protection_space.set_scheme("Basic");
429 protection_space.set_realm("Proxy test realm");
430 std::string actual_credentials = "";
431
432 server_proxy_->auth_cache_[protection_space.SerializeAsString()] =
433 "test_user:test_pwd";
434
435 server_proxy_->AuthenticationRequired(
436 protection_space.origin(), protection_space.scheme(),
Andreea Costinased9e6122020-08-12 12:06:19 +0200437 protection_space.realm(), /* bad_cached_credentials = */ "",
Andreea Costinasdb2cbee2020-06-15 11:43:44 +0200438 base::Bind(
439 [](std::string* actual_credentials, const std::string& credentials) {
440 *actual_credentials = credentials;
441 },
442 &actual_credentials));
443
444 brillo_loop_.RunOnce(false);
445 EXPECT_EQ(0, server_proxy_->pending_auth_required_requests_.size());
446 EXPECT_EQ("test_user:test_pwd", actual_credentials);
447}
448
Andreea Costinase9c73592020-07-17 15:27:54 +0200449// This test verifies that the stored credentials are removed when receiving a
450// |ClearUserCredentials| request.
451TEST_F(ServerProxyTest, ClearUserCredentials) {
452 worker::ProtectionSpace protection_space;
453 protection_space.set_origin(kFakeProxyAddress);
454 protection_space.set_scheme("Basic");
455 protection_space.set_realm("Proxy test realm");
456 // Add an entry in the cache.
457 server_proxy_->auth_cache_[protection_space.SerializeAsString()] =
458 "test_user:test_pwd";
459
460 worker::ClearUserCredentials clear_user_credentials;
461 worker::WorkerConfigs configs;
462 *configs.mutable_clear_user_credentials() = clear_user_credentials;
463 // Redirect the worker stdin and stdout pipes.
464 RedirectStdPipes();
465 // Send the config to the worker's stdin input.
466 EXPECT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
467 brillo_loop_.RunOnce(false);
468 // Expect that the credentials were cleared.
469 EXPECT_EQ(0, server_proxy_->auth_cache_.size());
470}
471
Andreea Costinased9e6122020-08-12 12:06:19 +0200472// Verifies that even if there are credentials in the cache for the remote
473// web-proxy, the ServerProxy sends a request to the parent web-proxy if the
474// credentials are flagged as bad.
475TEST_F(ServerProxyTest, AuthRequestsBadCachedCredentials) {
476 constexpr char kBadCachedCredetials[] = "bad_user:bad_pwd";
477 constexpr char kCredetials[] = "test_user:test_pwd";
478
479 RedirectStdPipes();
480 EXPECT_CALL(*server_proxy_, GetStdoutPipe())
481 .WillOnce(Return(stdout_write_fd_.get()));
482
483 // Add credentials to the cache for the proxy.
484 worker::ProtectionSpace protection_space;
485 protection_space.set_origin(kFakeProxyAddress);
486 protection_space.set_scheme("Basic");
487 protection_space.set_realm("Proxy test realm");
488 server_proxy_->auth_cache_[protection_space.SerializeAsString()] =
489 kBadCachedCredetials;
490
491 // Request credentials for the proxy.
492 std::string actual_credentials = "";
493 server_proxy_->AuthenticationRequired(
494 protection_space.origin(), protection_space.scheme(),
495 protection_space.realm(), kBadCachedCredetials,
496 base::Bind(
497 [](std::string* actual_credentials, const std::string& credentials) {
498 *actual_credentials = credentials;
499 },
500 &actual_credentials));
501
502 // Expect that the credentials are not served from the cache.
503 EXPECT_EQ(1, server_proxy_->pending_auth_required_requests_.size());
504 EXPECT_EQ(protection_space.SerializeAsString(),
505 server_proxy_->pending_auth_required_requests_.begin()->first);
506
507 brillo_loop_.RunOnce(false);
508
509 worker::WorkerRequest request;
510 // Read the request from the worker's stdout output.
511 ASSERT_TRUE(ReadProtobuf(stdout_read_fd_.get(), &request));
512 ASSERT_TRUE(request.has_auth_required_request());
513 ASSERT_TRUE(request.auth_required_request().has_protection_space());
514 EXPECT_EQ(
515 request.auth_required_request().protection_space().SerializeAsString(),
516 protection_space.SerializeAsString());
517
518 // Write reply with a fake credentials to the worker's standard input.
519 worker::Credentials credentials;
520 *credentials.mutable_protection_space() = protection_space;
521 credentials.set_username("test_user");
522 credentials.set_password("test_pwd");
523 worker::WorkerConfigs configs;
524 *configs.mutable_credentials() = credentials;
525
526 ASSERT_TRUE(WriteProtobuf(stdin_write_fd_.get(), configs));
527 brillo_loop_.RunOnce(false);
528 EXPECT_EQ(0, server_proxy_->pending_auth_required_requests_.size());
529 EXPECT_EQ(kCredetials, actual_credentials);
530}
531
Andreea Costinas44cefa22020-03-09 09:07:39 +0100532} // namespace system_proxy