chromium/chrome/browser/ash/platform_keys/key_permissions/arc_key_permissions_manager_delegate.cc

// Copyright 2020 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/platform_keys/key_permissions/arc_key_permissions_manager_delegate.h"

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

#include "base/functional/bind.h"
#include "base/observer_list_types.h"
#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ash/arc/arc_util.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/chromeos/platform_keys/extension_key_permissions_service.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/policy_constants.h"

namespace ash {
namespace platform_keys {

namespace {

// Owned by `ChromeBrowserMainPartsAsh`.
SystemTokenArcKpmDelegate* g_system_token_arc_usage_manager_delegate = nullptr;

}  // namespace

ArcKpmDelegate::Observer::Observer() = default;
ArcKpmDelegate::Observer::~Observer() = default;

ArcKpmDelegate::ArcKpmDelegate() = default;
ArcKpmDelegate::~ArcKpmDelegate() = default;

void ArcKpmDelegate::Shutdown() {}

void ArcKpmDelegate::AddObserver(Observer* observer) {
  observer_list_.AddObserver(observer);
}

void ArcKpmDelegate::RemoveObserver(Observer* observer) {
  observer_list_.RemoveObserver(observer);
}

void ArcKpmDelegate::NotifyArcUsageAllowanceForCorporateKeysChanged(
    bool allowed) {
  for (auto& observer : observer_list_) {
    observer.OnArcUsageAllowanceForCorporateKeysChanged(allowed);
  }
}

UserPrivateTokenArcKpmDelegate::UserPrivateTokenArcKpmDelegate(Profile* profile)
    : profile_(profile),
      is_primary_profile_(ProfileHelper::IsPrimaryProfile(profile)),
      policy_service_(profile->GetProfilePolicyConnector()->policy_service()) {
  DCHECK(profile_);
  DCHECK(policy_service_);

  if (is_primary_profile_) {
    SystemTokenArcKpmDelegate::Get()->SetPrimaryUserArcKpmDelegate(this);
  }

  policy_change_registrar_ = std::make_unique<policy::PolicyChangeRegistrar>(
      policy_service_, policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
                                               /*component_id=*/std::string()));

  policy_change_registrar_->Observe(
      policy::key::kKeyPermissions,
      base::BindRepeating(
          &UserPrivateTokenArcKpmDelegate::OnKeyPermissionsPolicyChanged,
          base::Unretained(this)));

  auto* arc_app_list_prefs = ArcAppListPrefs::Get(profile_);
  if (arc_app_list_prefs) {
    arc_app_list_prefs->AddObserver(this);
  }

  auto* arc_session_manager = arc::ArcSessionManager::Get();
  if (arc_session_manager) {
    arc_session_manager->AddObserver(this);
  }

  CheckArcKeyAvailibility();
}

UserPrivateTokenArcKpmDelegate::~UserPrivateTokenArcKpmDelegate() {
  if (is_primary_profile_) {
    SetArcUsageAllowance(false);
    SystemTokenArcKpmDelegate::Get()->SetPrimaryUserArcKpmDelegate(nullptr);
  }
}

bool UserPrivateTokenArcKpmDelegate::AreCorporateKeysAllowedForArcUsage()
    const {
  return corporate_keys_allowed_for_arc_usage_;
}

void UserPrivateTokenArcKpmDelegate::Shutdown() {
  if (is_shutdown_) {
    return;
  }

  is_shutdown_ = true;

  auto* arc_session_manager = arc::ArcSessionManager::Get();
  if (arc_session_manager) {
    arc_session_manager->RemoveObserver(this);
  }

  DCHECK(profile_);
  auto* arc_app_list_prefs = ArcAppListPrefs::Get(profile_);
  if (arc_app_list_prefs) {
    arc_app_list_prefs->RemoveObserver(this);
  }

  policy_change_registrar_.reset();

  profile_ = nullptr;
  policy_service_ = nullptr;
}

void UserPrivateTokenArcKpmDelegate::CheckArcKeyAvailibility() {
  if (!arc::IsArcAllowedForProfile(profile_)) {
    SetArcUsageAllowance(false);
    return;
  }

  std::vector<std::string> corporate_key_usage_allowed_app_ids =
      chromeos::platform_keys::ExtensionKeyPermissionsService::
          GetCorporateKeyUsageAllowedAppIds(policy_service_);

  for (const auto& package_name : corporate_key_usage_allowed_app_ids) {
    auto* arc_app_list_prefs = ArcAppListPrefs::Get(profile_);
    DCHECK(arc_app_list_prefs);

    if (arc_app_list_prefs->ArcAppListPrefs::IsPackageInstalled(package_name)) {
      SetArcUsageAllowance(true);
      return;
    }
  }

  SetArcUsageAllowance(false);
}

void UserPrivateTokenArcKpmDelegate::SetArcUsageAllowance(bool allowed) {
  if (corporate_keys_allowed_for_arc_usage_ != allowed) {
    corporate_keys_allowed_for_arc_usage_ = allowed;
    NotifyArcUsageAllowanceForCorporateKeysChanged(allowed);
  }
}

void UserPrivateTokenArcKpmDelegate::OnArcPlayStoreEnabledChanged(
    bool enabled) {
  CheckArcKeyAvailibility();
}

void UserPrivateTokenArcKpmDelegate::OnKeyPermissionsPolicyChanged(
    const base::Value* old_value,
    const base::Value* new_value) {
  CheckArcKeyAvailibility();
}

void UserPrivateTokenArcKpmDelegate::OnPackageInstalled(
    const arc::mojom::ArcPackageInfo& package_info) {
  CheckArcKeyAvailibility();
}

void UserPrivateTokenArcKpmDelegate::OnPackageRemoved(
    const std::string& package_name,
    bool uninstalled) {
  CheckArcKeyAvailibility();
}

// static
SystemTokenArcKpmDelegate* SystemTokenArcKpmDelegate::Get() {
  return g_system_token_arc_usage_manager_delegate;
}

// static
void SystemTokenArcKpmDelegate::SetSystemTokenArcKpmDelegateForTesting(
    SystemTokenArcKpmDelegate* system_token_arc_kpm_delegate) {
  g_system_token_arc_usage_manager_delegate = system_token_arc_kpm_delegate;
}

SystemTokenArcKpmDelegate::SystemTokenArcKpmDelegate() {
  DCHECK(!g_system_token_arc_usage_manager_delegate);
  g_system_token_arc_usage_manager_delegate = this;

  DCHECK(!primary_user_arc_usage_manager_);

  // Notification for the initial state of the usage allowance. The state may
  // change after SetPrimaryUserArcKpmDelegate is called.
  NotifyArcUsageAllowanceForCorporateKeysChanged(false);
}

SystemTokenArcKpmDelegate::~SystemTokenArcKpmDelegate() {
  DCHECK(g_system_token_arc_usage_manager_delegate);
  g_system_token_arc_usage_manager_delegate = nullptr;

  ClearPrimaryUserArcKpmDelegate();
}

bool SystemTokenArcKpmDelegate::AreCorporateKeysAllowedForArcUsage() const {
  if (!primary_user_arc_usage_manager_) {
    return false;
  }

  return primary_user_arc_usage_manager_->AreCorporateKeysAllowedForArcUsage();
}

// TODO(crbug.com/1144820): Make setting the primary user's ARC KPM delegate in
// SystemTokenArcKpmDelegate more robust.
void SystemTokenArcKpmDelegate::SetPrimaryUserArcKpmDelegate(
    UserPrivateTokenArcKpmDelegate* primary_user_arc_usage_manager) {
  ClearPrimaryUserArcKpmDelegate();

  if (primary_user_arc_usage_manager == nullptr) {
    return;
  }

  primary_user_arc_usage_manager_ = primary_user_arc_usage_manager;
  primary_user_arc_usage_manager_delegate_observation_.Observe(
      primary_user_arc_usage_manager_.get());
  OnArcUsageAllowanceForCorporateKeysChanged(
      primary_user_arc_usage_manager_->AreCorporateKeysAllowedForArcUsage());
}

void SystemTokenArcKpmDelegate::ClearPrimaryUserArcKpmDelegate() {
  if (!primary_user_arc_usage_manager_) {
    return;
  }

  DCHECK(primary_user_arc_usage_manager_delegate_observation_.IsObservingSource(
      primary_user_arc_usage_manager_.get()));
  primary_user_arc_usage_manager_delegate_observation_.Reset();
  primary_user_arc_usage_manager_ = nullptr;
  OnArcUsageAllowanceForCorporateKeysChanged(false);
}

void SystemTokenArcKpmDelegate::OnArcUsageAllowanceForCorporateKeysChanged(
    bool allowed) {
  if (corporate_keys_allowed_for_arc_usage_ == allowed) {
    return;
  }

  corporate_keys_allowed_for_arc_usage_ = allowed;
  NotifyArcUsageAllowanceForCorporateKeysChanged(allowed);
}

}  // namespace platform_keys
}  // namespace ash