| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "attestation/pca_agent/server/pca_request.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include <base/time/time.h> |
| #include <brillo/errors/error.h> |
| #include <brillo/http/http_connection_fake.h> |
| #include <brillo/mime_utils.h> |
| #include <brillo/streams/memory_stream.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "attestation/pca_agent/server/fake_transport_factory.h" |
| #include "attestation/pca_agent/server/mock_pca_http_utils.h" |
| #include "attestation/pca_agent/server/response_with_verifier.h" |
| |
| namespace { |
| |
| using ::testing::ByRef; |
| using ::testing::Types; |
| |
| constexpr char kFakeUrl[] = "fake.url.org"; |
| constexpr char kFakeRequest[] = "fake request"; |
| constexpr char kFakeResponse[] = "fake response"; |
| constexpr char kFakeErrMessage[] = "a tactical error"; |
| constexpr char kDummyHandlerName[] = "testing"; |
| |
| constexpr char kFakeProxy1[] = "https://fake-proxy1:8000"; |
| constexpr char kFakeProxy2[] = "https://fake-proxy2:8000"; |
| constexpr char kFakeProxy3[] = "https://fake-proxy3:8000"; |
| |
| void FakeMethodHandler(int status_code, |
| const brillo::http::fake::ServerRequest& request, |
| brillo::http::fake::ServerResponse* response) { |
| response->ReplyText( |
| status_code, |
| status_code == brillo::http::status_code::Ok ? kFakeResponse : "", |
| brillo::mime::text::kPlain); |
| } |
| |
| // testing::InvokeArgument<N> does not work with base::Callback, need to use |
| // |ACTION_TAMPLATE| along with predefined |args| tuple. |
| ACTION_TEMPLATE(InvokeChromeProxyServersCallback, |
| HAS_1_TEMPLATE_PARAMS(int, k), |
| AND_2_VALUE_PARAMS(p0, p1)) { |
| std::get<k>(args).Run(p0, p1); |
| } |
| |
| } // namespace |
| |
| namespace attestation { |
| namespace pca_agent { |
| |
| template <typename ReplyType> |
| class PcaRequestTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| EXPECT_CALL(mock_pca_http_utils_, GetChromeProxyServersAsync(_, _)) |
| .WillRepeatedly(InvokeChromeProxyServersCallback<1>( |
| ByRef(proxy_success_), ByRef(proxy_servers_))); |
| request_ = MakePcaRequest(); |
| } |
| |
| scoped_refptr<PcaRequest<ReplyType>> MakePcaRequest() { |
| // Sets up the verifier. See |Verify| below. Here we choose lambda over |
| // base::Bind. |
| auto v = [this](const ReplyType& reply) { this->Verify(reply); }; |
| auto response = MakeResponseWithVerifier<ReplyType>(v); |
| auto request = new PcaRequest<ReplyType>(kDummyHandlerName, kFakeUrl, |
| kFakeRequest, std::move(response)); |
| |
| // testing objects injected to the request. |
| request->set_transport_factory_for_testing(&fake_trasport_factory_); |
| request->set_pca_http_utils_for_testing(&mock_pca_http_utils_); |
| return request; |
| } |
| |
| // Sets the expected result to be returned when getting proxy servers. |
| void set_proxy_servers(const std::vector<std::string>& proxy_servers) { |
| proxy_servers_ = proxy_servers; |
| } |
| |
| // Checks the invariant of the reply -- When STATUS_SUCCESS, we also make sure |
| // the response is set. |
| void Verify(const ReplyType& reply) { |
| EXPECT_EQ(reply.status(), expected_attestation_status_); |
| if (expected_attestation_status_ == STATUS_SUCCESS) { |
| EXPECT_EQ(reply.response(), std::string(kFakeResponse)); |
| } |
| } |
| |
| // Mock/fake objects. |
| FakeTransportFactory fake_trasport_factory_; |
| MockPcaHttpUtils mock_pca_http_utils_; |
| |
| // Expected return value for getting proxy servers. |
| bool proxy_success_{true}; |
| std::vector<std::string> proxy_servers_; |
| |
| // Expected result status. |
| AttestationStatus expected_attestation_status_{STATUS_SUCCESS}; |
| |
| // The request under test. |
| scoped_refptr<PcaRequest<ReplyType>> request_; |
| }; |
| |
| using ReplyTypes = testing::Types<EnrollReply, GetCertificateReply>; |
| TYPED_TEST_SUITE(PcaRequestTest, ReplyTypes); |
| |
| TYPED_TEST(PcaRequestTest, SuccessNoProxy) { |
| this->expected_attestation_status_ = STATUS_SUCCESS; |
| this->fake_trasport_factory_.get_fake_transport(brillo::http::kDirectProxy) |
| ->AddHandler( |
| kFakeUrl, brillo::http::request_type::kPost, |
| base::Bind(FakeMethodHandler, brillo::http::status_code::Ok)); |
| this->request_->SendRequest(); |
| } |
| |
| TYPED_TEST(PcaRequestTest, SuccessFailedToGetProxy) { |
| this->expected_attestation_status_ = STATUS_SUCCESS; |
| this->proxy_success_ = false; |
| this->fake_trasport_factory_.get_fake_transport(brillo::http::kDirectProxy) |
| ->AddHandler( |
| kFakeUrl, brillo::http::request_type::kPost, |
| base::Bind(FakeMethodHandler, brillo::http::status_code::Ok)); |
| this->request_->SendRequest(); |
| } |
| |
| TYPED_TEST(PcaRequestTest, SuccessSecondProxy) { |
| this->expected_attestation_status_ = STATUS_SUCCESS; |
| this->set_proxy_servers({kFakeProxy1, kFakeProxy2, kFakeProxy3}); |
| this->fake_trasport_factory_.get_fake_transport(kFakeProxy1) |
| ->AddHandler(kFakeUrl, brillo::http::request_type::kPost, |
| base::Bind(FakeMethodHandler, |
| brillo::http::status_code::InternalServerError)); |
| this->fake_trasport_factory_.get_fake_transport(kFakeProxy2) |
| ->AddHandler( |
| kFakeUrl, brillo::http::request_type::kPost, |
| base::Bind(FakeMethodHandler, brillo::http::status_code::Ok)); |
| auto not_reached = [](const brillo::http::fake::ServerRequest& request, |
| brillo::http::fake::ServerResponse* response) { |
| ASSERT_FALSE("Should not be reached."); |
| }; |
| this->fake_trasport_factory_.get_fake_transport(kFakeProxy3) |
| ->AddHandler(kFakeUrl, brillo::http::request_type::kPost, |
| base::Bind(not_reached)); |
| this->request_->SendRequest(); |
| } |
| |
| TYPED_TEST(PcaRequestTest, FailedConnectionError) { |
| this->expected_attestation_status_ = STATUS_CA_NOT_AVAILABLE; |
| brillo::ErrorPtr error; |
| brillo::Error::AddTo(&error, FROM_HERE, "", "", kFakeErrMessage); |
| this->fake_trasport_factory_.get_fake_transport(kFakeProxy1) |
| ->SetCreateConnectionError(std::move(error)); |
| this->request_->SendRequest(); |
| } |
| |
| TYPED_TEST(PcaRequestTest, FailedAllProxies) { |
| this->expected_attestation_status_ = STATUS_CA_NOT_AVAILABLE; |
| this->set_proxy_servers({kFakeProxy1, kFakeProxy2, kFakeProxy3}); |
| this->fake_trasport_factory_.get_fake_transport(kFakeProxy1) |
| ->AddHandler(kFakeUrl, brillo::http::request_type::kPost, |
| base::Bind(FakeMethodHandler, |
| brillo::http::status_code::InternalServerError)); |
| brillo::ErrorPtr error; |
| brillo::Error::AddTo(&error, FROM_HERE, "", "", kFakeErrMessage); |
| this->fake_trasport_factory_.get_fake_transport(kFakeProxy2) |
| ->SetCreateConnectionError(std::move(error)); |
| this->fake_trasport_factory_.get_fake_transport(kFakeProxy3) |
| ->AddHandler(kFakeUrl, brillo::http::request_type::kPost, |
| base::Bind(FakeMethodHandler, |
| brillo::http::status_code::InternalServerError)); |
| this->request_->SendRequest(); |
| } |
| |
| TYPED_TEST(PcaRequestTest, FailedNotSupported) { |
| this->expected_attestation_status_ = STATUS_NOT_SUPPORTED; |
| // Sets the status code to partial to 'Partial`, which should recognized as an |
| // unsupported HTTP status code. |
| this->fake_trasport_factory_.get_fake_transport(brillo::http::kDirectProxy) |
| ->AddHandler( |
| kFakeUrl, brillo::http::request_type::kPost, |
| base::Bind(FakeMethodHandler, brillo::http::status_code::Partial)); |
| this->request_->SendRequest(); |
| } |
| |
| } // namespace pca_agent |
| } // namespace attestation |