chromium/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc

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

#include "chrome/browser/extensions/api/platform_keys/platform_keys_test_base.h"

#include <array>
#include <string>
#include <string_view>

#include "base/functional/bind.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "chrome/browser/ash/platform_keys/platform_keys_service_factory.h"
#include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h"
#include "chrome/browser/extensions/mixin_based_extension_apitest.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/cloud/test/policy_builder.h"
#include "components/policy/policy_constants.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_launcher.h"
#include "crypto/scoped_test_system_nss_key_slot.h"
#include "extensions/test/result_catcher.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_switches.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

constexpr char kAffiliationID[] = "some-affiliation-id";
constexpr char kTestUserinfoToken[] = "fake-userinfo-token";

PlatformKeysTestBase::PlatformKeysTestBase(
    SystemTokenStatus system_token_status,
    EnrollmentStatus enrollment_status,
    UserStatus user_status)
    : system_token_status_(system_token_status),
      enrollment_status_(enrollment_status),
      user_status_(user_status),
      account_id_(AccountId::FromUserEmailGaiaId(
          policy::AffiliationTestHelper::kEnterpriseUserEmail,
          policy::AffiliationTestHelper::kEnterpriseUserGaiaId)) {
  // Command line should not be tweaked as if user is already logged in.
  set_chromeos_user_ = false;
  // We log in without running browser.
  set_exit_when_last_browser_closes(false);
  cryptohome_mixin_.MarkUserAsExisting(account_id_);
}

PlatformKeysTestBase::~PlatformKeysTestBase() {}

void PlatformKeysTestBase::SetUp() {
  base::FilePath test_data_dir;
  base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
  embedded_test_server()->ServeFilesFromDirectory(test_data_dir);

  net::EmbeddedTestServer::ServerCertificateConfig gaia_cert_config;
  gaia_cert_config.dns_names = {GaiaUrls::GetInstance()->gaia_url().host()};
  gaia_server_.SetSSLConfig(gaia_cert_config);
  gaia_server_.RegisterRequestHandler(base::BindRepeating(
      &FakeGaia::HandleRequest, base::Unretained(&fake_gaia_)));

  // Initialize the server to allocate a port, so that URLs can be pointed at it
  // in SetUpCommandLine(). Don't spin up the IO thread yet since no threads are
  // allowed while spawning sandbox host process. See crbug.com/322732.
  ASSERT_TRUE(gaia_server_.InitializeAndListen());

  ash::platform_keys::PlatformKeysServiceFactory::GetInstance()->SetTestingMode(
      true);

  if (system_token_status() == SystemTokenStatus::EXISTS) {
    CreateTestSystemSlot();
  }

  extensions::MixinBasedExtensionApiTest::SetUp();
}

void PlatformKeysTestBase::SetUpCommandLine(base::CommandLine* command_line) {
  extensions::MixinBasedExtensionApiTest::SetUpCommandLine(command_line);

  policy::AffiliationTestHelper::AppendCommandLineSwitchesForLoginManager(
      command_line);

  const GURL gaia_url =
      gaia_server_.GetURL(GaiaUrls::GetInstance()->gaia_url().host(), "/");
  command_line->AppendSwitchASCII(::switches::kGaiaUrl, gaia_url.spec());
  command_line->AppendSwitchASCII(::switches::kLsoUrl, gaia_url.spec());
  command_line->AppendSwitchASCII(::switches::kGoogleApisUrl, gaia_url.spec());

  fake_gaia_.Initialize();
  fake_gaia_.set_issue_oauth_code_cookie(true);
}

void PlatformKeysTestBase::SetUpInProcessBrowserTestFixture() {
  extensions::MixinBasedExtensionApiTest::SetUpInProcessBrowserTestFixture();

  ash::SessionManagerClient::InitializeFakeInMemory();

  policy::AffiliationTestHelper affiliation_helper =
      policy::AffiliationTestHelper::CreateForCloud(
          ash::FakeSessionManagerClient::Get());

  if (enrollment_status() == EnrollmentStatus::ENROLLED) {
    ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetDeviceAffiliationIDs(
        &device_policy_test_helper_,
        std::array{std::string_view(kAffiliationID)}));
    device_policy_test_helper_.InstallOwnerKey();
    install_attributes_.Get()->SetCloudManaged(
        policy::PolicyBuilder::kFakeDomain,
        policy::PolicyBuilder::kFakeDeviceId);
  }

  if (user_status() == UserStatus::MANAGED_AFFILIATED_DOMAIN) {
    policy::UserPolicyBuilder user_policy;
    ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetUserAffiliationIDs(
        &user_policy, account_id_,
        std::array{std::string_view(kAffiliationID)}));
  }

  mock_policy_provider_.SetDefaultReturns(
      /*is_initialization_complete_return=*/true,
      /*is_first_policy_load_complete_return=*/true);
  mock_policy_provider_.SetAutoRefresh();
  policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
      &mock_policy_provider_);
}

void PlatformKeysTestBase::SetUpOnMainThread() {
  host_resolver()->AddRule("*", "127.0.0.1");
  // Start the accept thread as the sandbox host process has already been
  // spawned.
  ASSERT_TRUE(embedded_test_server()->Start());
  gaia_server_.StartAcceptingConnections();

  FakeGaia::AccessTokenInfo token_info;
  token_info.scopes.insert(GaiaConstants::kDeviceManagementServiceOAuth);
  token_info.scopes.insert(GaiaConstants::kGoogleUserInfoEmail);
  token_info.audience = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
  token_info.token = kTestUserinfoToken;
  token_info.email = account_id_.GetUserEmail();
  fake_gaia_.IssueOAuthToken(policy::AffiliationTestHelper::kFakeRefreshToken,
                             token_info);

  // On PRE_ test stage list of users is empty at this point. Then in the body
  // of PRE_ test kEnterpriseUser is added. Afterwards in the main test flow
  // after PRE_ test the list of user contains one kEnterpriseUser user.
  // This user logs in.
  if (!IsPreTest()) {
    policy::AffiliationTestHelper::LoginUser(account_id_);

    if (user_status() != UserStatus::UNMANAGED) {
      policy::ProfilePolicyConnector* const connector =
          profile()->GetProfilePolicyConnector();
      connector->OverrideIsManagedForTesting(true);
    }
  }

  if (system_token_status() == SystemTokenStatus::EXISTS) {
    base::RunLoop loop;
    // Call specializations of the virtual method that configures the created
    // system slot.
    content::GetIOThreadTaskRunner({})->PostTaskAndReply(
        FROM_HERE,
        base::BindOnce(&PlatformKeysTestBase::PrepareTestSystemSlotOnIO,
                       base::Unretained(this),
                       base::Unretained(test_system_slot_.get())),
        loop.QuitClosure());
    loop.Run();
  }

  extensions::MixinBasedExtensionApiTest::SetUpOnMainThread();
}

void PlatformKeysTestBase::TearDownOnMainThread() {
  extensions::MixinBasedExtensionApiTest::TearDownOnMainThread();

  ash::platform_keys::PlatformKeysServiceFactory::GetInstance()->SetTestingMode(
      false);

  if (system_token_status() == SystemTokenStatus::EXISTS) {
    base::RunLoop loop;
    content::GetIOThreadTaskRunner({})->PostTaskAndReply(
        FROM_HERE,
        base::BindOnce(&PlatformKeysTestBase::TearDownTestSystemSlotOnIO,
                       base::Unretained(this)),
        loop.QuitClosure());
    loop.Run();
  }
  EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
}

void PlatformKeysTestBase::PrepareTestSystemSlotOnIO(
    crypto::ScopedTestSystemNSSKeySlot* system_slot) {}

void PlatformKeysTestBase::RunPreTest() {
  policy::AffiliationTestHelper::PreLoginUser(account_id_);
}

bool PlatformKeysTestBase::TestExtension(const std::string& page_url) {
  DCHECK(!page_url.empty()) << "page_url cannot be empty";
  Browser* const browser = CreateBrowser(profile());

  extensions::ResultCatcher catcher;
  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, GURL(page_url)));

  if (!catcher.GetNextResult()) {
    message_ = catcher.message();
    return false;
  }
  return true;
}

bool PlatformKeysTestBase::IsPreTest() {
  return content::IsPreTest();
}

void PlatformKeysTestBase::CreateTestSystemSlot() {
  test_system_slot_ = std::make_unique<crypto::ScopedTestSystemNSSKeySlot>(
      /*simulate_token_loader=*/false);
  ASSERT_TRUE(test_system_slot_->ConstructedSuccessfully());
}

void PlatformKeysTestBase::TearDownTestSystemSlotOnIO() {
  test_system_slot_.reset();
}