chromium/chromeos/ash/services/auth_factor_config/recovery_factor_editor.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 "chromeos/ash/services/auth_factor_config/recovery_factor_editor.h"

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/values.h"
#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
#include "chromeos/ash/services/auth_factor_config/auth_factor_config.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_manager.h"

namespace ash::auth {

RecoveryFactorEditor::RecoveryFactorEditor(AuthFactorConfig* auth_factor_config)
    : auth_factor_config_(auth_factor_config),
      auth_factor_editor_(UserDataAuthClient::Get()) {
  DCHECK(auth_factor_config_);
}
RecoveryFactorEditor::~RecoveryFactorEditor() = default;

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

void RecoveryFactorEditor::Configure(
    const std::string& auth_token,
    bool enabled,
    base::OnceCallback<void(mojom::ConfigureResult)> callback) {
  auth_factor_config_->IsEditable(
      auth_token, mojom::AuthFactor::kRecovery,
      base::BindOnce(&RecoveryFactorEditor::OnGetEditable,
                     weak_factory_.GetWeakPtr(), auth_token, enabled,
                     std::move(callback)));
}

void RecoveryFactorEditor::OnGetEditable(
    const std::string& auth_token,
    bool should_enable,
    base::OnceCallback<void(mojom::ConfigureResult)> callback,
    bool is_editable) {
  if (!is_editable) {
    LOG(ERROR) << "Recovery configuration not editable";
    std::move(callback).Run(mojom::ConfigureResult::kInvalidTokenError);
    return;
  }

  if (!ash::AuthSessionStorage::Get()->IsValid(auth_token)) {
    LOG(ERROR) << "Invalid auth token";
    std::move(callback).Run(mojom::ConfigureResult::kInvalidTokenError);
    return;
  }
  ash::AuthSessionStorage::Get()->BorrowAsync(
      FROM_HERE, auth_token,
      base::BindOnce(&RecoveryFactorEditor::ConfigureWithContext,
                     weak_factory_.GetWeakPtr(), auth_token, should_enable,
                     std::move(callback), is_editable));
}

void RecoveryFactorEditor::ConfigureWithContext(
    const std::string& auth_token,
    bool should_enable,
    base::OnceCallback<void(mojom::ConfigureResult)> callback,
    bool is_editable,
    std::unique_ptr<UserContext> user_context) {
  const bool currently_enabled =
      user_context->GetAuthFactorsConfiguration().HasConfiguredFactor(
          cryptohome::AuthFactorType::kRecovery);

  if (should_enable == currently_enabled) {
    ash::AuthSessionStorage::Get()->Return(auth_token, std::move(user_context));
    std::move(callback).Run(mojom::ConfigureResult::kSuccess);
    return;
  }

  auto on_configured_callback = base::BindOnce(
      &RecoveryFactorEditor::OnRecoveryFactorConfigured,
      weak_factory_.GetWeakPtr(), std::move(callback), auth_token);

  if (should_enable) {
    auth_factor_editor_.AddRecoveryFactor(std::move(user_context),
                                          std::move(on_configured_callback));
  } else {
    auth_factor_editor_.RemoveRecoveryFactor(std::move(user_context),
                                             std::move(on_configured_callback));
  }
}

void RecoveryFactorEditor::OnRecoveryFactorConfigured(
    base::OnceCallback<void(mojom::ConfigureResult)> callback,
    const std::string& auth_token,
    std::unique_ptr<UserContext> context,
    std::optional<AuthenticationError> error) {
  if (error.has_value()) {
    if (error->get_cryptohome_code() ==
        user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN) {
      // Handle expired auth session gracefully.
      ash::AuthSessionStorage::Get()->Return(auth_token, std::move(context));
      ash::AuthSessionStorage::Get()->Invalidate(auth_token, base::DoNothing());
      std::move(callback).Run(mojom::ConfigureResult::kInvalidTokenError);
      return;
    }

    LOG(ERROR) << "Configuring recovery factor failed, code "
               << error->get_cryptohome_code();
    auth_factor_config_->NotifyFactorObserversAfterFailure(
        auth_token, std::move(context),
        base::BindOnce(std::move(callback),
                       mojom::ConfigureResult::kFatalError));
    return;
  }

  auth_factor_config_->NotifyFactorObserversAfterSuccess(
      {mojom::AuthFactor::kRecovery}, auth_token, std::move(context),
      std::move(callback));
}

}  // namespace ash::auth