chromium/chrome/browser/ash/policy/core/device_local_account_external_cache.cc

// Copyright 2022 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/device_local_account_external_cache.h"

#include <memory>
#include <set>
#include <string>
#include <utility>

#include "base/check.h"
#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/values.h"
#include "chrome/browser/ash/extensions/external_cache_impl.h"
#include "chrome/browser/browser_process.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"

namespace chromeos {

namespace {

base::Value::Dict Merge(base::Value::Dict first, base::Value::Dict second) {
  first.Merge(std::move(second));
  return first;
}

std::set<std::string> GetKeys(const base::Value::Dict& dict) {
  std::set<std::string> keys;
  for (auto [key, _] : dict) {
    keys.insert(key);
  }
  return keys;
}

base::Value::Dict FilterOnKeys(const base::Value::Dict& dict,
                               const std::set<std::string>& keys_to_keep) {
  base::Value::Dict result;
  for (auto [key, value] : dict) {
    if (keys_to_keep.contains(key)) {
      result.Set(key, value.Clone());
    }
  }
  return result;
}

}  // namespace

DeviceLocalAccountExternalCache::DeviceLocalAccountExternalCache(
    ExtensionListCallback ash_loader,
    ExtensionListCallback lacros_loader,
    const std::string& user_id,
    const base::FilePath& cache_dir)
    : user_id_(user_id),
      cache_dir_(cache_dir),
      ash_loader_(ash_loader),
      lacros_loader_(lacros_loader) {}

DeviceLocalAccountExternalCache::~DeviceLocalAccountExternalCache() = default;

void DeviceLocalAccountExternalCache::StartCache(
    const scoped_refptr<base::SequencedTaskRunner>& cache_task_runner) {
  DCHECK(!external_cache_);

  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory =
      g_browser_process->shared_url_loader_factory();
  external_cache_ = std::make_unique<ExternalCacheImpl>(
      cache_dir_, std::move(shared_url_loader_factory), cache_task_runner,
      /*delegate=*/this,
      /*always_check_updates=*/true,
      /*wait_for_cache_initialization=*/false,
      /*allow_scheduled_updates=*/false);
}

void DeviceLocalAccountExternalCache::UpdateExtensionsList(
    base::Value::Dict ash_extensions,
    base::Value::Dict lacros_extensions) {
  ash_extension_keys_ = GetKeys(ash_extensions);
  lacros_extension_keys_ = GetKeys(lacros_extensions);

  if (external_cache_) {
    external_cache_->UpdateExtensionsList(
        Merge(std::move(ash_extensions), std::move(lacros_extensions)));
  }
}

void DeviceLocalAccountExternalCache::StopCache(base::OnceClosure callback) {
  if (external_cache_) {
    external_cache_->Shutdown(std::move(callback));
    external_cache_.reset();
  } else {
    std::move(callback).Run();
  }

  base::Value::Dict empty_prefs;
  ash_loader_.Run(user_id_, empty_prefs.Clone());
  lacros_loader_.Run(user_id_, empty_prefs.Clone());
}

bool DeviceLocalAccountExternalCache::IsCacheRunning() const {
  return external_cache_ != nullptr;
}

void DeviceLocalAccountExternalCache::OnExtensionListsUpdated(
    const base::Value::Dict& prefs) {
  lacros_loader_.Run(user_id_, FilterOnKeys(prefs, lacros_extension_keys_));
  ash_loader_.Run(user_id_, FilterOnKeys(prefs, ash_extension_keys_));
}

bool DeviceLocalAccountExternalCache::IsRollbackAllowed() const {
  return true;
}

bool DeviceLocalAccountExternalCache::CanRollbackNow() const {
  // Allow immediate rollback only if current user is not this device local
  // account.
  if (auto* user = user_manager::UserManager::Get()->GetPrimaryUser()) {
    return user_id_ != user->GetAccountId().GetUserEmail();
  }
  return true;
}

base::Value::Dict
DeviceLocalAccountExternalCache::GetCachedExtensionsForTesting() const {
  return external_cache_->GetCachedExtensions().Clone();
}

void DeviceLocalAccountExternalCache::SetCacheResponseForTesting(
    const base::Value::Dict& cached_extensions) {
  OnExtensionListsUpdated(cached_extensions);
}

}  // namespace chromeos