chromium/chrome/browser/ash/policy/external_data/device_policy_cloud_external_data_manager_browsertest.cc

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

#include <memory>
#include <string>

#include "ash/constants/ash_paths.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/json/json_writer.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/test/repeating_test_future.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/core/device_policy_builder.h"
#include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
#include "chrome/browser/ash/policy/external_data/cloud_external_data_manager_base.h"
#include "chrome/browser/ash/policy/external_data/cloud_external_data_manager_base_test_util.h"
#include "chrome/browser/ash/policy/external_data/device_policy_cloud_external_data_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/policy/core/common/cloud/cloud_policy_core.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/policy_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace policy {

namespace {

// The contents of these files are served as external data.
const char kExternalDataPath[] = "policy/printers_configuration.json";
const char kExternalDataPathUpdated[] =
    "policy/printers_configuration_updated.json";
const char kExternalDataPathOverSizeLimit[] =
    "policy/printers_configuration_over_size_limit.json";
// The name of an External Data Policy in Device Policy.
const char* const kPolicyName = key::kDevicePrinters;

const int64_t kTestCacheMaxSize = 64;

}  // namespace

class DevicePolicyCloudExternalDataManagerTest
    : public DevicePolicyCrosBrowserTest {
 public:
  DevicePolicyCloudExternalDataManagerTest() {
    DevicePolicyCloudExternalDataManager::SetCacheMaxSizeForTesting(
        kTestCacheMaxSize);
  }
  ~DevicePolicyCloudExternalDataManagerTest() override = default;

 protected:
  void SetUpOnMainThread() override {
    ASSERT_TRUE(embedded_test_server()->Start());
    DevicePolicyCrosBrowserTest::SetUpOnMainThread();

    BrowserPolicyConnectorAsh* policy_connector =
        g_browser_process->platform_part()->browser_policy_connector_ash();
    ASSERT_TRUE(policy_connector);
    policy_service_ = policy_connector->GetPolicyService();
    ASSERT_TRUE(
        policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
    policy_change_registrar_ = std::make_unique<PolicyChangeRegistrar>(
        policy_service_, PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
    policy_change_registrar_->Observe(
        kPolicyName, policy_changed_repeating_future_.GetRepeatingCallback());
  }

  void TearDownOnMainThread() override {
    policy_change_registrar_.reset();
    DevicePolicyCrosBrowserTest::TearDownOnMainThread();
  }

  std::unique_ptr<std::string> GetExternalData() {
    const PolicyMap& policies = policy_service_->GetPolicies(
        PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
    const PolicyMap::Entry* policy_entry = policies.Get(kPolicyName);
    EXPECT_TRUE(policy_entry);
    EXPECT_TRUE(policy_entry->external_data_fetcher);

    base::test::TestFuture<std::unique_ptr<std::string>, const base::FilePath&>
        fetch_data_future;
    policy_entry->external_data_fetcher->Fetch(fetch_data_future.GetCallback());

    std::unique_ptr<std::string> actual_external_data =
        std::move(std::get<0>(fetch_data_future.Take()));
    EXPECT_TRUE(actual_external_data);
    return actual_external_data;
  }

  int64_t ComputeExternalDataCacheDirectorySize() {
    const base::FilePath device_policy_external_data_path =
        base::PathService::CheckedGet(ash::DIR_DEVICE_POLICY_EXTERNAL_DATA);
    base::ScopedAllowBlockingForTesting allow_blocking;
    return base::ComputeDirectorySize(device_policy_external_data_path);
  }

  void SetDevicePrintersExternalData(const base::Value::Dict& policy_dict) {
    std::string policy;
    EXPECT_TRUE(base::JSONWriter::Write(policy_dict, &policy));
    device_policy()->payload().mutable_device_printers()->set_external_policy(
        policy);
    RefreshDevicePolicy();

    std::tuple<const base::Value*, const base::Value*> prev_curr_policy =
        policy_changed_repeating_future_.Take();
    ASSERT_TRUE(std::get<1>(prev_curr_policy));
    EXPECT_EQ(policy_dict, *std::get<1>(prev_curr_policy));
  }

  void ClearDevicePrintersExternalData() {
    device_policy()->payload().clear_device_printers();
    RefreshDevicePolicy();

    std::tuple<const base::Value*, const base::Value*> prev_curr_policy =
        policy_changed_repeating_future_.Take();
    ASSERT_FALSE(std::get<1>(prev_curr_policy));
  }

  std::string ReadExternalDataFile(const std::string& file_path) {
    base::FilePath test_data_dir;
    EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
    std::string external_data;
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      EXPECT_TRUE(base::ReadFileToString(test_data_dir.AppendASCII(file_path),
                                         &external_data));
    }
    return external_data;
  }

 private:
  raw_ptr<PolicyService, DanglingUntriaged> policy_service_ = nullptr;
  std::unique_ptr<PolicyChangeRegistrar> policy_change_registrar_;
  base::test::TestFuture<const base::Value*, const base::Value*>
      policy_changed_repeating_future_;
};

IN_PROC_BROWSER_TEST_F(DevicePolicyCloudExternalDataManagerTest,
                       FetchExternalData) {
  SetDevicePrintersExternalData(test::ConstructExternalDataPolicy(
      *embedded_test_server(), kExternalDataPath));
  EXPECT_EQ(ReadExternalDataFile(kExternalDataPath), *GetExternalData());
}

IN_PROC_BROWSER_TEST_F(DevicePolicyCloudExternalDataManagerTest,
                       FetchOverSizeLimitExternalData) {
  EXPECT_EQ(0, ComputeExternalDataCacheDirectorySize());

  std::string external_data =
      ReadExternalDataFile(kExternalDataPathOverSizeLimit);
  // Check that file size is greater than cache limit.
  ASSERT_GT(base::checked_cast<int64_t>(external_data.size()),
            kTestCacheMaxSize);
  SetDevicePrintersExternalData(test::ConstructExternalDataPolicy(
      *embedded_test_server(), kExternalDataPathOverSizeLimit));
  EXPECT_EQ(external_data, *GetExternalData());

  // Check that nothing is cached because file was too big.
  EXPECT_EQ(0, ComputeExternalDataCacheDirectorySize());
}

IN_PROC_BROWSER_TEST_F(DevicePolicyCloudExternalDataManagerTest,
                       CleanUpResourceCache) {
  EXPECT_EQ(0, ComputeExternalDataCacheDirectorySize());

  std::string external_data = ReadExternalDataFile(kExternalDataPath);
  SetDevicePrintersExternalData(test::ConstructExternalDataPolicy(
      *embedded_test_server(), kExternalDataPath));
  EXPECT_EQ(external_data, *GetExternalData());
  EXPECT_EQ(base::checked_cast<int64_t>(external_data.size()),
            ComputeExternalDataCacheDirectorySize());

  external_data = ReadExternalDataFile(kExternalDataPathUpdated);
  SetDevicePrintersExternalData(test::ConstructExternalDataPolicy(
      *embedded_test_server(), kExternalDataPathUpdated));
  EXPECT_EQ(external_data, *GetExternalData());
  // Check that previous policy data was cleared and replaced by new one.
  EXPECT_EQ(base::checked_cast<int64_t>(external_data.size()),
            ComputeExternalDataCacheDirectorySize());

  ClearDevicePrintersExternalData();
  // We have to wait until
  // CloudExternalDataManagerBase::Backend::OnMetadataUpdated(), which is
  // responsible for removing outdated external policy files, is completed.
  content::RunAllTasksUntilIdle();
  // Check that policy data was cleared.
  EXPECT_EQ(0, ComputeExternalDataCacheDirectorySize());
}

}  // namespace policy