blob: 2c15bb8b9b7f6db5e5f7eb464eeefc9031cd9bbb [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sync/trusted_vault/standalone_trusted_vault_backend.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/containers/cxx20_erase.h"
#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/sequence_checker.h"
#include "base/stl_util.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "components/os_crypt/os_crypt.h"
#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
#include "components/sync/base/time.h"
#include "components/sync/trusted_vault/proto_string_bytes_conversion.h"
#include "components/sync/trusted_vault/securebox.h"
#include "components/sync/trusted_vault/trusted_vault_server_constants.h"
#include "components/sync/trusted_vault/trusted_vault_switches.h"
#include "google_apis/gaia/gaia_auth_util.h"
namespace syncer {
namespace {
sync_pb::LocalTrustedVault ReadEncryptedFile(const base::FilePath& file_path) {
sync_pb::LocalTrustedVault proto;
std::string ciphertext;
std::string decrypted_content;
if (base::ReadFileToString(file_path, &ciphertext) &&
OSCrypt::DecryptString(ciphertext, &decrypted_content)) {
proto.ParseFromString(decrypted_content);
}
return proto;
}
void WriteToDisk(const sync_pb::LocalTrustedVault& data,
const base::FilePath& file_path) {
std::string encrypted_data;
if (!OSCrypt::EncryptString(data.SerializeAsString(), &encrypted_data)) {
DLOG(ERROR) << "Failed to encrypt trusted vault file.";
return;
}
if (!base::ImportantFileWriter::WriteFileAtomically(file_path,
encrypted_data)) {
DLOG(ERROR) << "Failed to write trusted vault file.";
}
}
bool HasNonConstantKey(
const sync_pb::LocalTrustedVaultPerUser& per_user_vault) {
std::string constant_key_as_proto_string;
AssignBytesToProtoString(GetConstantTrustedVaultKey(),
&constant_key_as_proto_string);
for (const auto& key : per_user_vault.vault_key()) {
if (key.key_material() != constant_key_as_proto_string) {
return true;
}
}
return false;
}
std::vector<std::vector<uint8_t>> GetAllVaultKeys(
const sync_pb::LocalTrustedVaultPerUser& per_user_vault) {
std::vector<std::vector<uint8_t>> vault_keys;
for (const sync_pb::LocalTrustedVaultKey& key : per_user_vault.vault_key()) {
vault_keys.emplace_back(ProtoStringToBytes(key.key_material()));
}
return vault_keys;
}
void RetrieveIsRecoverabilityDegradedCompleted(
base::OnceCallback<void(bool)> cb,
TrustedVaultRecoverabilityStatus status) {
std::move(cb).Run(status == TrustedVaultRecoverabilityStatus::kDegraded);
}
base::flat_set<std::string> GetGaiaIDs(
const std::vector<gaia::ListedAccount>& listed_accounts) {
base::flat_set<std::string> result;
for (const auto& listed_account : listed_accounts) {
result.insert(listed_account.gaia_id);
}
return result;
}
} // namespace
StandaloneTrustedVaultBackend::PendingTrustedRecoveryMethod::
PendingTrustedRecoveryMethod() = default;
StandaloneTrustedVaultBackend::PendingTrustedRecoveryMethod::
PendingTrustedRecoveryMethod(PendingTrustedRecoveryMethod&&) = default;
StandaloneTrustedVaultBackend::PendingTrustedRecoveryMethod&
StandaloneTrustedVaultBackend::PendingTrustedRecoveryMethod::operator=(
PendingTrustedRecoveryMethod&&) = default;
StandaloneTrustedVaultBackend::PendingTrustedRecoveryMethod::
~PendingTrustedRecoveryMethod() = default;
StandaloneTrustedVaultBackend::StandaloneTrustedVaultBackend(
const base::FilePath& file_path,
std::unique_ptr<Delegate> delegate,
std::unique_ptr<TrustedVaultConnection> connection)
: file_path_(file_path),
delegate_(std::move(delegate)),
connection_(std::move(connection)),
clock_(base::DefaultClock::GetInstance()) {}
StandaloneTrustedVaultBackend::~StandaloneTrustedVaultBackend() = default;
void StandaloneTrustedVaultBackend::ReadDataFromDisk() {
data_ = ReadEncryptedFile(file_path_);
}
void StandaloneTrustedVaultBackend::FetchKeys(
const CoreAccountInfo& account_info,
FetchKeysCallback callback) {
// Concurrent keys fetches aren't supported.
DCHECK(ongoing_fetch_keys_callback_.is_null());
DCHECK(!callback.is_null());
ongoing_fetch_keys_callback_ = std::move(callback);
ongoing_fetch_keys_gaia_id_ = account_info.gaia;
const sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(account_info.gaia);
// TODO(crbug.com/1094326): currently there is no guarantee that
// |primary_account_| is set before FetchKeys() call and this may cause
// redundant sync error in the UI (for key retrieval), especially during the
// browser startup. Try to find a way to avoid this issue.
if (!connection_ || !primary_account_.has_value() ||
primary_account_->gaia != account_info.gaia ||
!per_user_vault->local_device_registration_info().device_registered() ||
AreConnectionRequestsThrottled()) {
// Keys download attempt is not possible.
FulfillOngoingFetchKeys();
return;
}
if (HasNonConstantKey(*per_user_vault) && !per_user_vault->keys_are_stale()) {
// There are locally available keys, which weren't marked as stale. Keys
// download attempt is not needed.
FulfillOngoingFetchKeys();
return;
}
// Current state guarantees there is no ongoing requests to the server:
// 1. Current |primary_account_| is |account_info|, so there is no ongoing
// request for other accounts.
// 2. Device is already registered, so there is no device registration for
// |account_info|.
// 3. Concurrent FetchKeys() calls aren't supported, so there is no keys
// download for |account_info|.
DCHECK(!ongoing_connection_request_);
std::unique_ptr<SecureBoxKeyPair> key_pair =
SecureBoxKeyPair::CreateByPrivateKeyImport(
ProtoStringToBytes(per_user_vault->local_device_registration_info()
.private_key_material()));
if (!key_pair) {
// Corrupted state: device is registered, but |key_pair| can't be imported.
// TODO(crbug.com/1094326): restore from this state (throw away the key and
// trigger device registration again).
FulfillOngoingFetchKeys();
return;
}
// Guaranteed by |device_registered| check above.
DCHECK(!per_user_vault->vault_key().empty());
// |this| outlives |connection_| and |ongoing_connection_request_|, so it's
// safe to use base::Unretained() here.
ongoing_connection_request_ = connection_->DownloadNewKeys(
*primary_account_,
TrustedVaultKeyAndVersion(
ProtoStringToBytes(
per_user_vault->vault_key().rbegin()->key_material()),
per_user_vault->last_vault_key_version()),
std::move(key_pair),
base::BindOnce(&StandaloneTrustedVaultBackend::OnKeysDownloaded,
base::Unretained(this)));
DCHECK(ongoing_connection_request_);
}
void StandaloneTrustedVaultBackend::StoreKeys(
const std::string& gaia_id,
const std::vector<std::vector<uint8_t>>& keys,
int last_key_version) {
// Find or create user for |gaid_id|.
sync_pb::LocalTrustedVaultPerUser* per_user_vault = FindUserVault(gaia_id);
if (!per_user_vault) {
per_user_vault = data_.add_user();
per_user_vault->set_gaia_id(gaia_id);
}
// Replace all keys.
per_user_vault->set_last_vault_key_version(last_key_version);
per_user_vault->set_keys_are_stale(false);
per_user_vault->clear_vault_key();
for (const std::vector<uint8_t>& key : keys) {
AssignBytesToProtoString(
key, per_user_vault->add_vault_key()->mutable_key_material());
}
WriteToDisk(data_, file_path_);
MaybeRegisterDevice();
}
void StandaloneTrustedVaultBackend::SetPrimaryAccount(
const absl::optional<CoreAccountInfo>& primary_account) {
if (primary_account == primary_account_) {
// Still need to complete deferred deletion, e.g. if primary account was
// cleared before browser shutdown but not handled here.
RemoveNonPrimaryAccountKeysIfMarkedForDeletion();
return;
}
primary_account_ = primary_account;
AbandonConnectionRequest();
ongoing_get_recoverability_request_.reset();
ongoing_add_recovery_method_request_.reset();
RemoveNonPrimaryAccountKeysIfMarkedForDeletion();
if (!primary_account_.has_value()) {
DCHECK(!pending_trusted_recovery_method_.has_value());
return;
}
sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(primary_account->gaia);
if (!per_user_vault) {
per_user_vault = data_.add_user();
per_user_vault->set_gaia_id(primary_account->gaia);
}
const absl::optional<DeviceRegistrationStateForUMA> registration_state =
MaybeRegisterDevice();
if (registration_state.has_value() &&
!device_registration_state_recorded_to_uma_) {
device_registration_state_recorded_to_uma_ = true;
base::UmaHistogramEnumeration("Sync.TrustedVaultDeviceRegistrationState",
*registration_state);
}
if (pending_trusted_recovery_method_.has_value()) {
PendingTrustedRecoveryMethod recovery_method =
std::move(*pending_trusted_recovery_method_);
pending_trusted_recovery_method_.reset();
AddTrustedRecoveryMethod(recovery_method.gaia_id,
recovery_method.public_key,
recovery_method.method_type_hint,
std::move(recovery_method.completion_callback));
}
}
void StandaloneTrustedVaultBackend::UpdateAccountsInCookieJarInfo(
const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info) {
const base::flat_set<std::string> gaia_ids_in_cookie_jar =
base::STLSetUnion<base::flat_set<std::string>>(
GetGaiaIDs(accounts_in_cookie_jar_info.signed_in_accounts),
GetGaiaIDs(accounts_in_cookie_jar_info.signed_out_accounts));
// Primary account data shouldn't be removed immediately, but it needs to be
// removed once account become non-primary if it was ever removed from cookie
// jar.
if (primary_account_.has_value() &&
!base::Contains(gaia_ids_in_cookie_jar, primary_account_->gaia)) {
sync_pb::LocalTrustedVaultPerUser* primary_account_data_ =
FindUserVault(primary_account_->gaia);
primary_account_data_->set_should_delete_keys_when_non_primary(true);
}
auto should_remove_user_data =
[&gaia_ids_in_cookie_jar, &primary_account = primary_account_](
const sync_pb::LocalTrustedVaultPerUser& per_user_data) {
const std::string& gaia_id = per_user_data.gaia_id();
if (primary_account.has_value() && gaia_id == primary_account->gaia) {
// Don't delete primary account data.
return false;
}
// Delete data if account isn't in cookie jar.
return !base::Contains(gaia_ids_in_cookie_jar, gaia_id);
};
data_.mutable_user()->erase(
std::remove_if(data_.mutable_user()->begin(), data_.mutable_user()->end(),
should_remove_user_data),
data_.mutable_user()->end());
WriteToDisk(data_, file_path_);
}
bool StandaloneTrustedVaultBackend::MarkLocalKeysAsStale(
const CoreAccountInfo& account_info) {
sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(account_info.gaia);
if (!per_user_vault || per_user_vault->keys_are_stale()) {
// No keys available for |account_info| or they are already marked as stale.
return false;
}
per_user_vault->set_keys_are_stale(true);
WriteToDisk(data_, file_path_);
return true;
}
void StandaloneTrustedVaultBackend::GetIsRecoverabilityDegraded(
const CoreAccountInfo& account_info,
base::OnceCallback<void(bool)> cb) {
// TODO(crbug.com/1201659): Improve this logic properly and add test coverage,
// including throttling and periodic polling.
ongoing_get_recoverability_request_ =
connection_->RetrieveIsRecoverabilityDegraded(
account_info,
base::BindOnce(&RetrieveIsRecoverabilityDegradedCompleted,
std::move(cb)));
}
void StandaloneTrustedVaultBackend::AddTrustedRecoveryMethod(
const std::string& gaia_id,
const std::vector<uint8_t>& public_key,
int method_type_hint,
base::OnceClosure cb) {
if (public_key.empty()) {
std::move(cb).Run();
return;
}
if (!primary_account_.has_value()) {
// Defer until SetPrimaryAccount() gets called.
pending_trusted_recovery_method_ = PendingTrustedRecoveryMethod();
pending_trusted_recovery_method_->gaia_id = gaia_id;
pending_trusted_recovery_method_->public_key = public_key;
pending_trusted_recovery_method_->method_type_hint = method_type_hint;
pending_trusted_recovery_method_->completion_callback = std::move(cb);
return;
}
DCHECK(!pending_trusted_recovery_method_.has_value());
if (primary_account_->gaia != gaia_id) {
std::move(cb).Run();
return;
}
sync_pb::LocalTrustedVaultPerUser* per_user_vault = FindUserVault(gaia_id);
DCHECK(per_user_vault);
if (per_user_vault->vault_key().empty()) {
// Can't add recovery method while there are no local keys.
std::move(cb).Run();
return;
}
std::unique_ptr<SecureBoxPublicKey> imported_public_key =
SecureBoxPublicKey::CreateByImport(public_key);
if (!imported_public_key) {
// Invalid public key.
std::move(cb).Run();
return;
}
last_added_recovery_method_public_key_for_testing_ = public_key;
if (!connection_) {
// Feature disabled.
std::move(cb).Run();
return;
}
// |this| outlives |connection_| and
// |ongoing_add_recovery_method_request_|, so it's safe to use
// base::Unretained() here.
ongoing_add_recovery_method_request_ =
connection_->RegisterAuthenticationFactor(
*primary_account_, GetAllVaultKeys(*per_user_vault),
per_user_vault->last_vault_key_version(), *imported_public_key,
AuthenticationFactorType::kUnspecified, method_type_hint,
base::BindOnce(
&StandaloneTrustedVaultBackend::OnTrustedRecoveryMethodAdded,
base::Unretained(this), std::move(cb)));
}
absl::optional<CoreAccountInfo>
StandaloneTrustedVaultBackend::GetPrimaryAccountForTesting() const {
return primary_account_;
}
sync_pb::LocalDeviceRegistrationInfo
StandaloneTrustedVaultBackend::GetDeviceRegistrationInfoForTesting(
const std::string& gaia_id) {
sync_pb::LocalTrustedVaultPerUser* per_user_vault = FindUserVault(gaia_id);
if (!per_user_vault) {
return sync_pb::LocalDeviceRegistrationInfo();
}
return per_user_vault->local_device_registration_info();
}
std::vector<uint8_t>
StandaloneTrustedVaultBackend::GetLastAddedRecoveryMethodPublicKeyForTesting()
const {
return last_added_recovery_method_public_key_for_testing_;
}
void StandaloneTrustedVaultBackend::SetClockForTesting(base::Clock* clock) {
clock_ = clock;
}
absl::optional<StandaloneTrustedVaultBackend::DeviceRegistrationStateForUMA>
StandaloneTrustedVaultBackend::MaybeRegisterDevice() {
// TODO(crbug.com/1102340): in case of transient failure this function is
// likely to be not called until the browser restart; implement retry logic.
if (!connection_) {
// Feature disabled.
return absl::nullopt;
}
if (!primary_account_.has_value()) {
// Device registration is supported only for |primary_account_|.
return absl::nullopt;
}
// |per_user_vault| must be created before calling this function.
sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(primary_account_->gaia);
DCHECK(per_user_vault);
if (per_user_vault->vault_key().empty() &&
!base::FeatureList::IsEnabled(
switches::kAllowSilentTrustedVaultDeviceRegistration)) {
// Either vault key with known version should be available or registration
// without it should be allowed through feature flag.
return absl::nullopt;
}
if (per_user_vault->local_device_registration_info().device_registered()) {
return DeviceRegistrationStateForUMA::kAlreadyRegistered;
}
if (per_user_vault->keys_are_stale()) {
// Client already knows that existing vault keys (or their absence) isn't
// sufficient for device registration. Fresh keys should be obtained first.
return DeviceRegistrationStateForUMA::kLocalKeysAreStale;
}
if (AreConnectionRequestsThrottled()) {
return DeviceRegistrationStateForUMA::kThrottledClientSide;
}
std::unique_ptr<SecureBoxKeyPair> key_pair;
if (per_user_vault->has_local_device_registration_info()) {
key_pair = SecureBoxKeyPair::CreateByPrivateKeyImport(
/*private_key_bytes=*/ProtoStringToBytes(
per_user_vault->local_device_registration_info()
.private_key_material()));
}
const bool had_generated_key_pair = key_pair != nullptr;
if (!key_pair) {
key_pair = SecureBoxKeyPair::GenerateRandom();
// It's possible that device will be successfully registered, but the client
// won't persist this state (for example response doesn't reach the client
// or registration callback is cancelled). To avoid duplicated registrations
// device key is stored before sending the registration request, so the same
// key will be used for future registration attempts.
AssignBytesToProtoString(
key_pair->private_key().ExportToBytes(),
per_user_vault->mutable_local_device_registration_info()
->mutable_private_key_material());
WriteToDisk(data_, file_path_);
}
// Cancel existing callbacks passed to |connection_| to ensure there is only
// one ongoing request.
AbandonConnectionRequest();
// |this| outlives |connection_| and |ongoing_connection_request_|, so it's
// safe to use base::Unretained() here.
if (per_user_vault->vault_key().empty()) {
ongoing_connection_request_ = connection_->RegisterDeviceWithoutKeys(
*primary_account_, key_pair->public_key(),
base::BindOnce(
&StandaloneTrustedVaultBackend::OnDeviceRegisteredWithoutKeys,
base::Unretained(this)));
} else {
ongoing_connection_request_ = connection_->RegisterAuthenticationFactor(
*primary_account_, GetAllVaultKeys(*per_user_vault),
per_user_vault->last_vault_key_version(), key_pair->public_key(),
AuthenticationFactorType::kPhysicalDevice,
/*authentication_factor_type_hint=*/absl::nullopt,
base::BindOnce(&StandaloneTrustedVaultBackend::OnDeviceRegistered,
base::Unretained(this)));
}
DCHECK(ongoing_connection_request_);
return had_generated_key_pair ? DeviceRegistrationStateForUMA::
kAttemptingRegistrationWithExistingKeyPair
: DeviceRegistrationStateForUMA::
kAttemptingRegistrationWithNewKeyPair;
}
void StandaloneTrustedVaultBackend::OnDeviceRegistered(
TrustedVaultRegistrationStatus status) {
// If |primary_account_| was changed meanwhile, this callback must be
// cancelled.
DCHECK(primary_account_.has_value());
// This method should be called only as a result of
// |ongoing_connection_request_| completion/failure, verify this condition
// and destroy |ongoing_connection_request_| as it's not needed anymore.
DCHECK(ongoing_connection_request_);
ongoing_connection_request_ = nullptr;
sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(primary_account_->gaia);
DCHECK(per_user_vault);
switch (status) {
case TrustedVaultRegistrationStatus::kSuccess:
per_user_vault->mutable_local_device_registration_info()
->set_device_registered(true);
WriteToDisk(data_, file_path_);
return;
case TrustedVaultRegistrationStatus::kLocalDataObsolete:
per_user_vault->set_keys_are_stale(true);
return;
case TrustedVaultRegistrationStatus::kOtherError:
RecordFailedConnectionRequestForThrottling();
return;
}
}
void StandaloneTrustedVaultBackend::OnDeviceRegisteredWithoutKeys(
TrustedVaultRegistrationStatus status,
const TrustedVaultKeyAndVersion& vault_key_and_version) {
// If |primary_account_| was changed meanwhile, this callback must be
// cancelled.
DCHECK(primary_account_.has_value());
// This method should be called only as a result of
// |ongoing_connection_request_| completion/failure, verify this condition,
// |ongoing_connection_request_| will be destroyed later by
// OnDeviceRegistered() call.
DCHECK(ongoing_connection_request_);
sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(primary_account_->gaia);
DCHECK(per_user_vault);
// This method can be called only if device registration was triggered while
// no local keys available. Detected server-side key should be stored upon
// successful completion, but |vault_key| emptiness still needs to be checked
// before that - there might be StoreKeys() call during handling the request.
if (status == TrustedVaultRegistrationStatus::kSuccess &&
per_user_vault->vault_key().empty()) {
AssignBytesToProtoString(
vault_key_and_version.key,
per_user_vault->add_vault_key()->mutable_key_material());
per_user_vault->set_last_vault_key_version(vault_key_and_version.version);
// WriteToDisk() will be called by OnDeviceRegistered().
}
OnDeviceRegistered(status);
}
void StandaloneTrustedVaultBackend::OnKeysDownloaded(
TrustedVaultDownloadKeysStatus status,
const std::vector<std::vector<uint8_t>>& vault_keys,
int last_vault_key_version) {
DCHECK(primary_account_.has_value());
DCHECK(!ongoing_fetch_keys_callback_.is_null());
DCHECK_EQ(*ongoing_fetch_keys_gaia_id_, primary_account_->gaia);
// This method should be called only as a result of
// |ongoing_connection_request_| completion/failure, verify this condition
// and destroy |ongoing_connection_request_| as it's not needed anymore.
DCHECK(ongoing_connection_request_);
ongoing_connection_request_ = nullptr;
switch (status) {
case TrustedVaultDownloadKeysStatus::kSuccess:
// TODO(crbug.com/1102340): consider keeping old keys as well.
StoreKeys(primary_account_->gaia, vault_keys, last_vault_key_version);
break;
case TrustedVaultDownloadKeysStatus::kMemberNotFoundOrCorrupted:
case TrustedVaultDownloadKeysStatus::kNoNewKeys:
case TrustedVaultDownloadKeysStatus::kKeyProofsVerificationFailed: {
sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(primary_account_->gaia);
DCHECK(per_user_vault);
// Unable to download new keys due to known protocol errors. The only way
// to go out of these states is to receive new vault keys through external
// StoreKeys() call. It's safe to mark device as not registered regardless
// of the cause (device registration will be triggered once new vault keys
// are available).
per_user_vault->mutable_local_device_registration_info()
->set_device_registered(false);
WriteToDisk(data_, file_path_);
break;
}
case TrustedVaultDownloadKeysStatus::kOtherError:
RecordFailedConnectionRequestForThrottling();
break;
}
// Regardless of the |status| ongoing fetch keys request should be fulfilled.
FulfillOngoingFetchKeys();
}
void StandaloneTrustedVaultBackend::OnTrustedRecoveryMethodAdded(
base::OnceClosure cb,
TrustedVaultRegistrationStatus status) {
DCHECK(ongoing_add_recovery_method_request_);
ongoing_add_recovery_method_request_ = nullptr;
std::move(cb).Run();
delegate_->NotifyRecoverabilityDegradedChanged();
}
void StandaloneTrustedVaultBackend::AbandonConnectionRequest() {
ongoing_connection_request_ = nullptr;
FulfillOngoingFetchKeys();
}
void StandaloneTrustedVaultBackend::FulfillOngoingFetchKeys() {
if (!ongoing_fetch_keys_gaia_id_.has_value()) {
return;
}
DCHECK(!ongoing_fetch_keys_callback_.is_null());
const sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(*ongoing_fetch_keys_gaia_id_);
std::vector<std::vector<uint8_t>> vault_keys;
if (per_user_vault) {
vault_keys = GetAllVaultKeys(*per_user_vault);
base::EraseIf(vault_keys, [](const std::vector<uint8_t>& key) {
return key == GetConstantTrustedVaultKey();
});
}
std::move(ongoing_fetch_keys_callback_).Run(vault_keys);
ongoing_fetch_keys_callback_.Reset();
ongoing_fetch_keys_gaia_id_.reset();
}
bool StandaloneTrustedVaultBackend::AreConnectionRequestsThrottled() {
DCHECK(clock_);
DCHECK(primary_account_.has_value());
sync_pb::LocalTrustedVaultPerUser* per_user_vault =
FindUserVault(primary_account_->gaia);
DCHECK(per_user_vault);
const base::Time current_time = clock_->Now();
base::Time last_failed_request_time = ProtoTimeToTime(
per_user_vault->last_failed_request_millis_since_unix_epoch());
// Fix |last_failed_request_time| if it's set to the future.
if (last_failed_request_time > current_time) {
// Immediately unthrottle, but don't write new state to the file.
last_failed_request_time = base::Time();
}
return last_failed_request_time +
switches::kTrustedVaultServiceThrottlingDuration.Get() >
current_time;
}
void StandaloneTrustedVaultBackend::
RecordFailedConnectionRequestForThrottling() {
DCHECK(clock_);
DCHECK(primary_account_.has_value());
FindUserVault(primary_account_->gaia)
->set_last_failed_request_millis_since_unix_epoch(
TimeToProtoTime(clock_->Now()));
WriteToDisk(data_, file_path_);
}
void StandaloneTrustedVaultBackend::
RemoveNonPrimaryAccountKeysIfMarkedForDeletion() {
auto should_remove_user_data =
[&primary_account = primary_account_](
const sync_pb::LocalTrustedVaultPerUser& per_user_data) {
return per_user_data.should_delete_keys_when_non_primary() &&
(!primary_account.has_value() ||
primary_account->gaia != per_user_data.gaia_id());
};
data_.mutable_user()->erase(
std::remove_if(data_.mutable_user()->begin(), data_.mutable_user()->end(),
should_remove_user_data),
data_.mutable_user()->end());
WriteToDisk(data_, file_path_);
}
sync_pb::LocalTrustedVaultPerUser* StandaloneTrustedVaultBackend::FindUserVault(
const std::string& gaia_id) {
for (int i = 0; i < data_.user_size(); ++i) {
if (data_.user(i).gaia_id() == gaia_id) {
return data_.mutable_user(i);
}
}
return nullptr;
}
} // namespace syncer