chromium/chrome/browser/ash/policy/core/cached_policy_key_loader_unittest.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/ash/policy/core/cached_policy_key_loader.h"

#include <memory>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
#include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h"
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace policy {

namespace {

const char kDummyKey1[] = "dummy-key-1";
const char kDummyKey2[] = "dummy-key-2";
const char kTestUserName[] = "[email protected]";

class CachedPolicyKeyLoaderTest : public testing::Test {
 protected:
  CachedPolicyKeyLoaderTest() = default;

  CachedPolicyKeyLoaderTest(const CachedPolicyKeyLoaderTest&) = delete;
  CachedPolicyKeyLoaderTest& operator=(const CachedPolicyKeyLoaderTest&) =
      delete;

  void SetUp() override {
    ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());

    cached_policy_key_loader_ = std::make_unique<CachedPolicyKeyLoader>(
        &cryptohome_misc_client_, task_environment_.GetMainThreadTaskRunner(),
        account_id_, user_policy_keys_dir());
  }

  void StoreUserPolicyKey(const std::string& public_key) {
    ASSERT_TRUE(base::CreateDirectory(user_policy_key_file().DirName()));
    ASSERT_TRUE(base::WriteFile(user_policy_key_file(), public_key));
  }

  base::FilePath user_policy_keys_dir() const {
    return tmp_dir_.GetPath().AppendASCII("var_run_user_policy");
  }

  base::FilePath user_policy_key_file() const {
    const std::string sanitized_username =
        ash::UserDataAuthClient::GetStubSanitizedUsername(cryptohome_id_);
    return user_policy_keys_dir()
        .AppendASCII(sanitized_username)
        .AppendASCII("policy.pub");
  }

  void OnPolicyKeyLoaded() { ++policy_key_loaded_callback_invocations_; }

  void CallEnsurePolicyKeyLoaded() {
    cached_policy_key_loader_->EnsurePolicyKeyLoaded(base::BindOnce(
        &CachedPolicyKeyLoaderTest::OnPolicyKeyLoaded, base::Unretained(this)));
  }

  void CallReloadPolicyKey() {
    cached_policy_key_loader_->ReloadPolicyKey(base::BindOnce(
        &CachedPolicyKeyLoaderTest::OnPolicyKeyLoaded, base::Unretained(this)));
  }

  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::MainThreadType::UI};
  ash::FakeCryptohomeMiscClient cryptohome_misc_client_;
  const AccountId account_id_ = AccountId::FromUserEmail(kTestUserName);
  const cryptohome::AccountIdentifier cryptohome_id_ =
      cryptohome::CreateAccountIdentifierFromAccountId(account_id_);

  std::unique_ptr<CachedPolicyKeyLoader> cached_policy_key_loader_;

  // Counts how many times OnPolicyKeyLoaded() has been invoked.
  int policy_key_loaded_callback_invocations_ = 0;

 private:
  base::ScopedTempDir tmp_dir_;
};

// Loads an existing key file using EnsurePolicyKeyLoaded.
TEST_F(CachedPolicyKeyLoaderTest, Basic) {
  StoreUserPolicyKey(kDummyKey1);

  CallEnsurePolicyKeyLoaded();

  task_environment_.RunUntilIdle();

  EXPECT_EQ(1, policy_key_loaded_callback_invocations_);
  EXPECT_EQ(kDummyKey1, cached_policy_key_loader_->cached_policy_key());
}

// Tries to load key using EnsurePolicyKeyLoaded, but the key is missing.
TEST_F(CachedPolicyKeyLoaderTest, KeyFileMissing) {
  CallEnsurePolicyKeyLoaded();

  task_environment_.RunUntilIdle();

  EXPECT_EQ(1, policy_key_loaded_callback_invocations_);
  EXPECT_EQ(std::string(), cached_policy_key_loader_->cached_policy_key());
}

// Loads an existing key file using EnsurePolicyKeyLoaded. While the load is in
// progress, EnsurePolicyKeyLoaded is called again.
TEST_F(CachedPolicyKeyLoaderTest, EnsureCalledTwice) {
  StoreUserPolicyKey(kDummyKey1);

  CallEnsurePolicyKeyLoaded();
  CallEnsurePolicyKeyLoaded();

  EXPECT_EQ(0, policy_key_loaded_callback_invocations_);

  task_environment_.RunUntilIdle();

  // We expect that the callback was called for each EnsurePolicyKeyLoaded
  // invocation.
  EXPECT_EQ(2, policy_key_loaded_callback_invocations_);
  EXPECT_EQ(kDummyKey1, cached_policy_key_loader_->cached_policy_key());
}

// After a successful load, changes the policy key file and calls
// EnsurePolicyKeyLoaded.
TEST_F(CachedPolicyKeyLoaderTest, EnsureAfterSuccessfulLoad) {
  StoreUserPolicyKey(kDummyKey1);

  CallEnsurePolicyKeyLoaded();
  EXPECT_EQ(0, policy_key_loaded_callback_invocations_);

  task_environment_.RunUntilIdle();

  EXPECT_EQ(1, policy_key_loaded_callback_invocations_);
  EXPECT_EQ(kDummyKey1, cached_policy_key_loader_->cached_policy_key());

  // Change the policy key file.
  StoreUserPolicyKey(kDummyKey2);

  CallEnsurePolicyKeyLoaded();

  task_environment_.RunUntilIdle();

  // We expect that the callback was invoked, but that the cached policy key is
  // still the old one. EnsurePolicyKeyLoaded is not supposed to reload the key.
  EXPECT_EQ(2, policy_key_loaded_callback_invocations_);
  EXPECT_EQ(kDummyKey1, cached_policy_key_loader_->cached_policy_key());
}

// After a successful load, changes the policy key file and calls
// ReloadPolicyKey.
TEST_F(CachedPolicyKeyLoaderTest, ReloadAfterEnsure) {
  StoreUserPolicyKey(kDummyKey1);

  CallEnsurePolicyKeyLoaded();
  EXPECT_EQ(0, policy_key_loaded_callback_invocations_);

  task_environment_.RunUntilIdle();

  EXPECT_EQ(1, policy_key_loaded_callback_invocations_);
  EXPECT_EQ(kDummyKey1, cached_policy_key_loader_->cached_policy_key());

  // Change the policy key file.
  StoreUserPolicyKey(kDummyKey2);

  CallReloadPolicyKey();

  task_environment_.RunUntilIdle();

  // We expect that the callback was invoked, and that the policy key file has
  // been reloded, so the cached policy key is now the new policy key.
  EXPECT_EQ(2, policy_key_loaded_callback_invocations_);
  EXPECT_EQ(kDummyKey2, cached_policy_key_loader_->cached_policy_key());
}

// During a load, ReloadPolicyKeyFile is invoked.
TEST_F(CachedPolicyKeyLoaderTest, ReloadWhileLoading) {
  StoreUserPolicyKey(kDummyKey1);

  CallEnsurePolicyKeyLoaded();
  CallReloadPolicyKey();
  EXPECT_EQ(0, policy_key_loaded_callback_invocations_);

  task_environment_.RunUntilIdle();

  // We expect that the callback was called for both the EnsurePolicyKeyLoaded
  // and ReloadPolicyKey invocation.
  EXPECT_EQ(2, policy_key_loaded_callback_invocations_);
  EXPECT_EQ(kDummyKey1, cached_policy_key_loader_->cached_policy_key());
}

// Synchronous load on the caller's thread.
TEST_F(CachedPolicyKeyLoaderTest, LoadImmediately) {
  StoreUserPolicyKey(kDummyKey1);

  cached_policy_key_loader_->LoadPolicyKeyImmediately();

  EXPECT_EQ(kDummyKey1, cached_policy_key_loader_->cached_policy_key());
}

}  // namespace

}  // namespace policy