chromium/chrome/browser/ash/sync/sync_explicit_passphrase_client_ash.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/sync/sync_explicit_passphrase_client_ash.h"

#include <memory>
#include <optional>
#include <utility>
#include <vector>

#include "base/containers/span.h"
#include "components/account_manager_core/account.h"
#include "components/account_manager_core/account_manager_util.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/sync/chromeos/explicit_passphrase_mojo_utils.h"
#include "components/sync/engine/nigori/nigori.h"
#include "components/sync/service/sync_user_settings.h"

namespace ash {

namespace {

bool IsPassphraseAvailable(const syncer::SyncService& sync_service) {
  return sync_service.GetUserSettings()->IsUsingExplicitPassphrase() &&
         !sync_service.GetUserSettings()->IsPassphraseRequired();
}

}  // namespace

SyncExplicitPassphraseClientAsh::SyncExplicitPassphraseClientAsh(
    syncer::SyncService* sync_service)
    : sync_service_(sync_service),
      is_passphrase_required_(
          sync_service_->GetUserSettings()->IsPassphraseRequired()),
      is_passphrase_available_(IsPassphraseAvailable(*sync_service_)) {
  sync_service_->AddObserver(this);
}

SyncExplicitPassphraseClientAsh::~SyncExplicitPassphraseClientAsh() {
  sync_service_->RemoveObserver(this);
}

void SyncExplicitPassphraseClientAsh::BindReceiver(
    mojo::PendingReceiver<crosapi::mojom::SyncExplicitPassphraseClient>
        receiver) {
  receivers_.Add(this, std::move(receiver));
}

void SyncExplicitPassphraseClientAsh::AddObserver(
    mojo::PendingRemote<crosapi::mojom::SyncExplicitPassphraseClientObserver>
        observer) {
  auto observer_id = observers_.Add(std::move(observer));
  // Immediately notify observer if passphrase is required or available.
  // |is_passphrase_required_| and |is_passphrase_available_| are mutually
  // exclusive.
  DCHECK(!is_passphrase_required_ || !is_passphrase_available_);
  if (is_passphrase_required_) {
    observers_.Get(observer_id)->OnPassphraseRequired();
  } else if (is_passphrase_available_) {
    observers_.Get(observer_id)->OnPassphraseAvailable();
  }
}

void SyncExplicitPassphraseClientAsh::GetDecryptionNigoriKey(
    crosapi::mojom::AccountKeyPtr mojo_account_key,
    GetDecryptionNigoriKeyCallback callback) {
  if (!ValidateAccountKey(mojo_account_key)) {
    std::move(callback).Run(nullptr);
    return;
  }

  std::unique_ptr<syncer::Nigori> decryption_key =
      sync_service_->GetUserSettings()
          ->GetExplicitPassphraseDecryptionNigoriKey();
  if (!decryption_key) {
    std::move(callback).Run(nullptr);
    return;
  }

  std::move(callback).Run(syncer::NigoriToMojo(*decryption_key));
}

void SyncExplicitPassphraseClientAsh::SetDecryptionNigoriKey(
    crosapi::mojom::AccountKeyPtr mojo_account_key,
    crosapi::mojom::NigoriKeyPtr mojo_nigori_key) {
  if (!ValidateAccountKey(mojo_account_key) || !mojo_nigori_key) {
    return;
  }

  std::unique_ptr<syncer::Nigori> nigori_key =
      syncer::NigoriFromMojo(*mojo_nigori_key);
  if (!nigori_key) {
    // Deserialization failed, |mojo_nigori_key| doesn't represent an actual
    // Nigori key.
    return;
  }
  sync_service_->GetUserSettings()->SetExplicitPassphraseDecryptionNigoriKey(
      std::move(nigori_key));
}

void SyncExplicitPassphraseClientAsh::OnStateChanged(
    syncer::SyncService* sync_service) {
  bool new_is_passphrase_required =
      sync_service->GetUserSettings()->IsPassphraseRequired();
  if (new_is_passphrase_required && !is_passphrase_required_) {
    for (auto& observer : observers_) {
      observer->OnPassphraseRequired();
    }
  }
  is_passphrase_required_ = new_is_passphrase_required;

  bool new_is_passphrase_available = IsPassphraseAvailable(*sync_service);
  if (new_is_passphrase_available && !is_passphrase_available_) {
    for (auto& observer : observers_) {
      observer->OnPassphraseAvailable();
    }
  }
  is_passphrase_available_ = new_is_passphrase_available;
}

void SyncExplicitPassphraseClientAsh::FlushMojoForTesting() {
  observers_.FlushForTesting();  // IN-TEST
}

bool SyncExplicitPassphraseClientAsh::ValidateAccountKey(
    const crosapi::mojom::AccountKeyPtr& mojo_account_key) const {
  const std::optional<account_manager::AccountKey> account_key =
      account_manager::FromMojoAccountKey(mojo_account_key);
  if (!account_key.has_value()) {
    return false;
  }

  if (account_key->account_type() != account_manager::AccountType::kGaia) {
    // ActiveDirectory accounts are not supported.
    return false;
  }

  return !account_key->id().empty() &&
         account_key->id() == sync_service_->GetAccountInfo().gaia;
}

}  // namespace ash