chromium/remoting/host/it2me/it2me_host_unittest.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/it2me/it2me_host.h"

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/policy/policy_constants.h"
#include "net/base/network_change_notifier.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/session_policies.h"
#include "remoting/host/chromeos/chromeos_enterprise_params.h"
#include "remoting/host/chromeos/features.h"
#include "remoting/host/chromoting_host.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/host_event_reporter.h"
#include "remoting/host/it2me/it2me_confirmation_dialog.h"
#include "remoting/host/policy_watcher.h"
#include "remoting/host/xmpp_register_support_host_request.h"
#include "remoting/protocol/errors.h"
#include "remoting/protocol/transport_context.h"
#include "remoting/signaling/fake_signal_strategy.h"
#include "remoting/signaling/xmpp_log_to_server.h"
#include "services/network/test/test_shared_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "base/linux_util.h"
#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_LINUX)
#include "remoting/host/linux/wayland_manager.h"
#include "remoting/host/linux/wayland_utils.h"
#endif  // BUILDFLAG(IS_LINUX)

namespace remoting {

ErrorCode;

namespace {

// Shortening some type names for readability.
ValidationResult;
DialogResult;

const char kTestUserName[] =;
const char kTestClientJid[] =;
const char kTestClientJid2[] =;
const char kTestClientUsernameNoJid[] =;
const char kTestClientJidWithSlash[] =;
const char kResourceOnly[] =;
const char kMatchingDomain[] =;
const char kMismatchedDomain1[] =;
const char kMismatchedDomain2[] =;
const char kMismatchedDomain3[] =;
// Note that this is intentionally different from the default port range.
const char kPortRange[] =;

const char kTestStunServer[] =;

class HostEventReporterStub : public HostEventReporter {};

#if BUILDFLAG(IS_CHROMEOS_ASH)
std::unique_ptr<HostEventReporter> CreateHostEventReporterStub(
    scoped_refptr<HostStatusMonitor>) {
  return std::make_unique<HostEventReporterStub>();
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

}  // namespace

// This is invoked automatically by the gtest framework, and improves the error
// messages when a test fails (by properly formatting the host state instead
// of printing their byte value).
void PrintTo(It2MeHostState state, std::ostream* os) {}

class FakeIt2MeConfirmationDialog : public It2MeConfirmationDialog {};

FakeIt2MeConfirmationDialog::FakeIt2MeConfirmationDialog() = default;

FakeIt2MeConfirmationDialog::FakeIt2MeConfirmationDialog(
    const std::string& remote_user_email,
    DialogResult dialog_result)
    :{}

FakeIt2MeConfirmationDialog::~FakeIt2MeConfirmationDialog() = default;

void FakeIt2MeConfirmationDialog::Show(const std::string& remote_user_email,
                                       ResultCallback callback) {}

class FakeIt2MeDialogFactory : public It2MeConfirmationDialogFactory {};

FakeIt2MeDialogFactory::FakeIt2MeDialogFactory()
    :{}

FakeIt2MeDialogFactory::~FakeIt2MeDialogFactory() = default;

std::unique_ptr<It2MeConfirmationDialog> FakeIt2MeDialogFactory::Create() {}

class It2MeHostTest : public testing::Test, public It2MeHost::Observer {};

It2MeHostTest::It2MeHostTest() = default;
It2MeHostTest::~It2MeHostTest() = default;

void It2MeHostTest::SetUp() {}

void It2MeHostTest::TearDown() {}

void It2MeHostTest::OnValidationComplete(base::OnceClosure resume_callback,
                                         ValidationResult validation_result) {}

void It2MeHostTest::SetPolicies(
    std::initializer_list<std::pair<std::string_view, const base::Value&>>
        policies) {}

void It2MeHostTest::StartupHostStateHelper(
    const base::RepeatingClosure& quit_closure) {}

void It2MeHostTest::StartHost() {}

void It2MeHostTest::StartHost(
    std::optional<ChromeOsEnterpriseParams> enterprise_params) {}

void It2MeHostTest::RunUntilStateChanged(It2MeHostState expected_state) {}

void It2MeHostTest::RunValidationCallback(const std::string& remote_jid) {}

void It2MeHostTest::OnClientAuthenticated(const std::string& client_username) {}

void It2MeHostTest::OnStoreAccessCode(const std::string& access_code,
                                      base::TimeDelta access_code_lifetime) {}

void It2MeHostTest::OnNatPoliciesChanged(bool nat_traversal_enabled,
                                         bool relay_connections_allowed) {}

void It2MeHostTest::OnStateChanged(It2MeHostState state, ErrorCode error_code) {}

void It2MeHostTest::ShutdownHost() {}

base::Value It2MeHostTest::MakeList(
    std::initializer_list<std::string_view> values) {}

// Callback to receive IceConfig from TransportContext
void ReceiveIceConfig(protocol::IceConfig* ice_config,
                      const protocol::IceConfig& received_ice_config) {}

TEST_F(It2MeHostTest, StartAndStop) {}

// Verify that IceConfig is passed to the TransportContext.
TEST_F(It2MeHostTest, IceConfig) {}

TEST_F(It2MeHostTest, NatTraversalPolicyEnabled) {}

TEST_F(It2MeHostTest, NatTraversalPolicyDisabled) {}

// TODO(crbug.com/40715894): flaky test.
TEST_F(It2MeHostTest,
       DISABLED_NatTraversalPolicyDisabledTransitionCausesDisconnect) {}

TEST_F(It2MeHostTest, RelayPolicyEnabled) {}

TEST_F(It2MeHostTest, RelayPolicyDisabled) {}

// TODO(crbug.com/40718796): Flaky test.
TEST_F(It2MeHostTest, DISABLED_RelayPolicyDisabledTransitionCausesDisconnect) {}

TEST_F(It2MeHostTest, HostValidationHostDomainListPolicyMatchingDomain) {}

TEST_F(It2MeHostTest, HostValidationHostDomainListPolicyMatchStart) {}

TEST_F(It2MeHostTest, HostValidationHostDomainListPolicyMatchEnd) {}

TEST_F(It2MeHostTest, HostValidationHostDomainListPolicyMatchFirst) {}

TEST_F(It2MeHostTest, HostValidationHostDomainListPolicyMatchSecond) {}

TEST_F(It2MeHostTest, HostValidationHostDomainListPolicyNoMatch) {}

TEST_F(It2MeHostTest, ConnectionValidationNoClientDomainListPolicyValidJid) {}

TEST_F(It2MeHostTest, ConnectionValidationNoClientDomainListPolicyInvalidJid) {}

TEST_F(It2MeHostTest,
       ConnectionValidationNoClientDomainListPolicyInvalidUsername) {}

TEST_F(It2MeHostTest,
       ConnectionValidationNoClientDomainListPolicyResourceOnly) {}

TEST_F(It2MeHostTest,
       ConnectionValidationClientDomainListPolicyMatchingDomain) {}

TEST_F(It2MeHostTest,
       ConnectionValidationClientDomainListPolicyInvalidUserName) {}

TEST_F(It2MeHostTest, ConnectionValidationClientDomainListPolicyNoJid) {}

TEST_F(It2MeHostTest, ConnectionValidationWrongClientDomainMatchStart) {}

TEST_F(It2MeHostTest, ConnectionValidationWrongClientDomainMatchEnd) {}

TEST_F(It2MeHostTest, ConnectionValidationClientDomainListPolicyMatchFirst) {}

TEST_F(It2MeHostTest, ConnectionValidationClientDomainListPolicyMatchSecond) {}

TEST_F(It2MeHostTest, ConnectionValidationClientDomainListPolicyNoMatch) {}

TEST_F(It2MeHostTest, AuthorizedHelperCanConnect) {}

TEST_F(It2MeHostTest, UnauthorizedHelperIsRejected) {}

TEST_F(It2MeHostTest, HostUdpPortRangePolicyValidRange) {}

TEST_F(It2MeHostTest, HostUdpPortRangePolicyNoRange) {}

TEST_F(It2MeHostTest, ConnectionValidationConfirmationDialogAccept) {}

TEST_F(It2MeHostTest, ConnectionValidationConfirmationDialogReject) {}

TEST_F(It2MeHostTest, MultipleConnectionsTriggerDisconnect) {}

TEST_F(It2MeHostTest, AllowSupportHostConnectionsPolicyEnabled) {}

TEST_F(It2MeHostTest, AllowSupportHostConnectionsPolicyDisabled) {}

TEST_F(It2MeHostTest, FileTransferDisallowedByDefault) {}

TEST_F(It2MeHostTest, UriForwardingDisallowedByDefault) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(It2MeHostTest, ConnectRespectsSuppressDialogsParameter) {
  StartHost(ChromeOsEnterpriseParams{.suppress_user_dialogs = true});

  EXPECT_FALSE(dialog_factory_->dialog_created());
  EXPECT_FALSE(
      GetHost()->desktop_environment_options().enable_user_interface());
}

TEST_F(It2MeHostTest, ConnectRespectsSuppressNotificationsParameter) {
  StartHost(ChromeOsEnterpriseParams{.suppress_notifications = true});

  EXPECT_FALSE(dialog_factory_->dialog_created());
  EXPECT_FALSE(GetHost()->desktop_environment_options().enable_notifications());
}

TEST_F(It2MeHostTest, ConnectRespectsTerminateUponInputParameter) {
  StartHost(ChromeOsEnterpriseParams{.terminate_upon_input = true});

  EXPECT_TRUE(GetHost()->desktop_environment_options().terminate_upon_input());
}

TEST_F(It2MeHostTest, TerminateUponInputDefaultsToFalse) {
  StartHost(/*enterprise_params=*/std::nullopt);

  EXPECT_FALSE(GetHost()->desktop_environment_options().terminate_upon_input());
}

TEST_F(It2MeHostTest, ConnectRespectsEnableCurtainingParameter) {
  StartHost(ChromeOsEnterpriseParams{.curtain_local_user_session = true});

  EXPECT_TRUE(GetHost()->desktop_environment_options().enable_curtaining());
}

TEST_F(It2MeHostTest, EnableCurtainingDefaultsToFalse) {
  StartHost(/*enterprise_params=*/std::nullopt);

  EXPECT_FALSE(GetHost()->desktop_environment_options().enable_curtaining());
}

TEST_F(It2MeHostTest, AllowEnterpriseFileTransferWithPolicyEnabled) {
  SetPolicies({{policy::key::kRemoteAccessHostAllowEnterpriseFileTransfer,
                base::Value(true)}});

  StartHost(ChromeOsEnterpriseParams{.allow_file_transfer = true});

  EXPECT_TRUE(*get_local_session_policies().allow_file_transfer);
}

TEST_F(It2MeHostTest, AllowEnterpriseFileTransferWithPolicyDisabled) {
  SetPolicies({{policy::key::kRemoteAccessHostAllowEnterpriseFileTransfer,
                base::Value(false)}});

  StartHost(ChromeOsEnterpriseParams{.allow_file_transfer = true});

  EXPECT_FALSE(*get_local_session_policies().allow_file_transfer);
}

TEST_F(It2MeHostTest,
       AllowEnterpriseFileTransferWithPolicyEnabledForNonEnterpriseSession) {
  SetPolicies({{policy::key::kRemoteAccessHostAllowEnterpriseFileTransfer,
                base::Value(true)}});

  StartHost(/*enterprise_params=*/std::nullopt);

  EXPECT_FALSE(*get_local_session_policies().allow_file_transfer);
}

TEST_F(It2MeHostTest, AllowEnterpriseFileTransferWithPolicyNotSet) {
  SetPolicies({});

  StartHost(ChromeOsEnterpriseParams{.allow_file_transfer = true});

  EXPECT_FALSE(*get_local_session_policies().allow_file_transfer);
}

TEST_F(It2MeHostTest, EnableFileTransferDefaultsToFalse) {
  StartHost(/*enterprise_params=*/std::nullopt);

  EXPECT_FALSE(*get_local_session_policies().allow_file_transfer);
}

TEST_F(It2MeHostTest,
       EnterpriseSessionsSucceedWhenRemoteSupportConnectionsPolicyDisabled) {
  SetPolicies({{policy::key::kRemoteAccessHostAllowRemoteSupportConnections,
                base::Value(false)}});

  StartHost(ChromeOsEnterpriseParams());
  ASSERT_EQ(It2MeHostState::kReceivedAccessCode, last_host_state_);

  ShutdownHost();
  ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
  ASSERT_EQ(ErrorCode::OK, last_error_code_);
}

TEST_F(It2MeHostTest, EnterpriseSessionsShouldNotCheckHostDomain) {
  SetPolicies({{policy::key::kRemoteAccessHostDomainList,
                MakeList({"other-domain.com"})}});

  StartHost(ChromeOsEnterpriseParams());
  ASSERT_EQ(It2MeHostState::kReceivedAccessCode, last_host_state_);

  ShutdownHost();
  ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
  ASSERT_EQ(ErrorCode::OK, last_error_code_);
}

TEST_F(
    It2MeHostTest,
    EnterpriseSessionsFailWhenEnterpriseRemoteSupportConnectionsPolicyDisabled) {
  SetPolicies(
      {{policy::key::kRemoteAccessHostAllowEnterpriseRemoteSupportConnections,
        base::Value(false)}});

  StartHost(ChromeOsEnterpriseParams());
  ASSERT_EQ(It2MeHostState::kError, last_host_state_);
  ASSERT_EQ(ErrorCode::DISALLOWED_BY_POLICY, last_error_code_);
}

TEST_F(
    It2MeHostTest,
    RemoteSupportSessionsSucceedWhenEnterpriseRemoteSupportConnectionsPolicyDisabled) {
  SetPolicies(
      {{policy::key::kRemoteAccessHostAllowEnterpriseRemoteSupportConnections,
        base::Value(false)}});

  StartHost(/*enterprise_params=*/std::nullopt);
  ASSERT_EQ(It2MeHostState::kReceivedAccessCode, last_host_state_);
}
#endif

}  // namespace remoting