chromium/chrome/browser/chromeos/enterprise/cloud_storage/one_drive_pref_observer.cc

// Copyright 2023 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/chromeos/enterprise/cloud_storage/one_drive_pref_observer.h"

#include "base/check_deref.h"
#include "base/check_is_test.h"
#include "base/functional/bind.h"
#include "base/values.h"
#include "chrome/browser/chromeos/enterprise/cloud_storage/policy_utils.h"
#include "chrome/browser/chromeos/extensions/odfs_config_private/odfs_config_private_api.h"
#include "chrome/browser/chromeos/upload_office_to_cloud/upload_office_to_cloud.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_selections.h"
#include "chrome/common/extensions/api/odfs_config_private.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/pref_names.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/event_router_factory.h"
#include "extensions/browser/extension_event_histogram_value.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_factory.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_system_provider.h"
#include "extensions/browser/extensions_browser_client.h"

using extensions::api::odfs_config_private::AccountRestrictionsInfo;
using extensions::api::odfs_config_private::Mount;
using extensions::api::odfs_config_private::MountInfo;

namespace chromeos::cloud_storage {

namespace {

// This class is responsible for watching the prefs for a particular profile.
class OneDrivePrefObserver : public KeyedService {
 public:
  ~OneDrivePrefObserver() override;

  static std::unique_ptr<OneDrivePrefObserver> Create(Profile* profile);

  // KeyedService:
  void Shutdown() override;

 private:
  explicit OneDrivePrefObserver(Profile* profile);

  // Sets up watchers.
  void Init();

  // Serves as callback for pref changes.
  void OnMicrosoftOneDriveMountPrefChanged();
  void OnMicrosoftOneDriveAccountRestrictionsPrefChanged();

  void BroadcastModeChanged(Mount mode);
  void BroadcastAccountRestrictionsChanged(base::Value::List restrictions);

  void MaybeUninstallOdfsExtension(Mount mode);

  // Keyed services are shut down from the embedder's destruction of the profile
  // and this pointer is reset in `ShutDown`. Therefore it is safe to use this
  // raw pointer.
  raw_ptr<Profile> profile_ = nullptr;

  // This keyed service depends on the EventRouter keyed service and will be
  // destroyed before the EventRouter. Therefore it is safe to use this raw
  // pointer.
  raw_ptr<extensions::EventRouter> event_router_ = nullptr;

  // The registrar used to watch prefs changes.
  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
};

OneDrivePrefObserver::OneDrivePrefObserver(Profile* profile)
    : profile_(profile),
      event_router_(extensions::EventRouter::Get(profile)),
      pref_change_registrar_(std::make_unique<PrefChangeRegistrar>()) {}

OneDrivePrefObserver::~OneDrivePrefObserver() = default;

std::unique_ptr<OneDrivePrefObserver> OneDrivePrefObserver::Create(
    Profile* profile) {
  auto watcher = base::WrapUnique(new OneDrivePrefObserver(profile));
  watcher->Init();
  return watcher;
}

void OneDrivePrefObserver::Init() {
  pref_change_registrar_->Init(profile_->GetPrefs());
  pref_change_registrar_->Add(
      prefs::kMicrosoftOneDriveMount,
      base::BindRepeating(
          &OneDrivePrefObserver::OnMicrosoftOneDriveMountPrefChanged,
          base::Unretained(this)));
  pref_change_registrar_->Add(
      prefs::kMicrosoftOneDriveAccountRestrictions,
      base::BindRepeating(&OneDrivePrefObserver::
                              OnMicrosoftOneDriveAccountRestrictionsPrefChanged,
                          base::Unretained(this)));
  OnMicrosoftOneDriveMountPrefChanged();
  OnMicrosoftOneDriveAccountRestrictionsPrefChanged();
}

void OneDrivePrefObserver::Shutdown() {
  pref_change_registrar_.reset();
  event_router_ = nullptr;
  profile_ = nullptr;
}

void OneDrivePrefObserver::OnMicrosoftOneDriveMountPrefChanged() {
  const Mount mount = GetMicrosoftOneDriveMount(profile_);
  BroadcastModeChanged(mount);
  MaybeUninstallOdfsExtension(mount);
}

void OneDrivePrefObserver::OnMicrosoftOneDriveAccountRestrictionsPrefChanged() {
  BroadcastAccountRestrictionsChanged(
      GetMicrosoftOneDriveAccountRestrictions(profile_.get()));
}

void OneDrivePrefObserver::MaybeUninstallOdfsExtension(Mount mount) {
  if (cloud_upload::IsMicrosoftOfficeOneDriveIntegrationAllowed(profile_) ||
      !CHECK_DEREF(extensions::ExtensionRegistry::Get(profile_))
           .enabled_extensions()
           .GetByID(extension_misc::kODFSExtensionId)) {
    return;
  }
  CHECK_DEREF(extensions::ExtensionSystem::Get(profile_)->extension_service())
      .RemoveComponentExtension(extension_misc::kODFSExtensionId);
}

}  // namespace

OneDrivePrefObserverFactory* OneDrivePrefObserverFactory::GetInstance() {
  static base::NoDestructor<OneDrivePrefObserverFactory> instance;
  return instance.get();
}

OneDrivePrefObserverFactory::OneDrivePrefObserverFactory()
    : ProfileKeyedServiceFactory(
          "OneDrivePrefObserverFactory",
          ProfileSelections::Builder()
              .WithRegular(ProfileSelection::kOriginalOnly)
              // TODO(crbug.com/41488885): Check if this service is needed for
              // Ash Internals.
              .WithAshInternals(ProfileSelection::kOriginalOnly)
              .Build()) {
  DependsOn(extensions::ExtensionRegistryFactory::GetInstance());
  DependsOn(extensions::EventRouterFactory::GetInstance());
  DependsOn(
      extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
}

OneDrivePrefObserverFactory::~OneDrivePrefObserverFactory() = default;

std::unique_ptr<KeyedService>
OneDrivePrefObserverFactory::BuildServiceInstanceForBrowserContext(
    content::BrowserContext* context) const {
  if (!features::IsUploadOfficeToCloudEnabled() ||
      !features::IsMicrosoftOneDriveIntegrationForEnterpriseEnabled()) {
    return nullptr;
  }

  Profile* profile = Profile::FromBrowserContext(context);
#if BUILDFLAG(IS_CHROMEOS_LACROS)
  if (!profile->IsMainProfile()) {
    return nullptr;
  }
#endif

  return OneDrivePrefObserver::Create(profile);
}

bool OneDrivePrefObserverFactory::ServiceIsCreatedWithBrowserContext() const {
  return true;
}

void OneDrivePrefObserver::BroadcastModeChanged(Mount mode) {
  if (!event_router_) {
    CHECK_IS_TEST();
    return;
  }

  MountInfo metadata;
  metadata.mode = mode;

  auto event = std::make_unique<extensions::Event>(
      extensions::events::ODFS_CONFIG_PRIVATE_MOUNT_CHANGED,
      extensions::api::odfs_config_private::OnMountChanged::kEventName,
      extensions::api::odfs_config_private::OnMountChanged::Create(
          std::move(metadata)),
      profile_);

  event_router_->BroadcastEvent(std::move(event));
}

void OneDrivePrefObserver::BroadcastAccountRestrictionsChanged(
    base::Value::List restrictions) {
  if (!event_router_) {
    CHECK_IS_TEST();
    return;
  }

  std::vector<std::string> restrictions_vector;
  for (auto& restriction : restrictions) {
    if (restriction.is_string()) {
      restrictions_vector.emplace_back(restriction.GetString());
    }
  }

  AccountRestrictionsInfo event_data;
  event_data.restrictions = std::move(restrictions_vector);

  auto event = std::make_unique<extensions::Event>(
      extensions::events::ODFS_CONFIG_PRIVATE_ACCOUNT_RESTRICTIONS_CHANGED,
      extensions::api::odfs_config_private::OnAccountRestrictionsChanged::
          kEventName,
      extensions::api::odfs_config_private::OnAccountRestrictionsChanged::
          Create(event_data),
      profile_);

  event_router_->BroadcastEvent(std::move(event));
}

}  // namespace chromeos::cloud_storage