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

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/notreached.h"
#include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/ash/components/cryptohome/auth_factor.h"
#include "chromeos/ash/components/login/auth/auth_performer.h"
#include "chromeos/ash/components/login/auth/public/auth_callbacks.h"
#include "chromeos/ash/components/login/auth/public/authentication_error.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
#include "components/prefs/pref_service.h"

namespace ash {
namespace {

const char kFactorsOptionAll[] = "all";
const char kFactorsOptionFingerprint[] = "FINGERPRINT";

bool HasPolicyValue(const PrefService& pref_service,
                    LegacyFingerprintEngine::Purpose purpose,
                    const char* value) {
  const base::Value::List* factors = nullptr;
  switch (purpose) {
    case LegacyFingerprintEngine::Purpose::kUnlock:
      factors = &pref_service.GetList(prefs::kQuickUnlockModeAllowlist);
      break;
    case LegacyFingerprintEngine::Purpose::kWebAuthn:
      factors = &pref_service.GetList(prefs::kWebAuthnFactors);
      break;
    default:
      return false;
  }
  return base::Contains(*factors, base::Value(value));
}

// Check if fingerprint is disabled for a specific purpose (so not including
// kAny) by reading the policy value.
bool IsFingerprintDisabledByPolicySinglePurpose(
    const PrefService& pref_service,
    LegacyFingerprintEngine::Purpose purpose) {
  DCHECK(purpose != LegacyFingerprintEngine::Purpose::kAny);
  const bool enabled =
      HasPolicyValue(pref_service, purpose, kFactorsOptionAll) ||
      HasPolicyValue(pref_service, purpose, kFactorsOptionFingerprint);
  return !enabled;
}

bool HasRecord(const PrefService& pref_service) {
  return pref_service.GetInteger(prefs::kQuickUnlockFingerprintRecord) != 0;
}

}  // namespace

LegacyFingerprintEngine::LegacyFingerprintEngine(AuthPerformer* auth_performer)
    : auth_performer_(auth_performer) {}

LegacyFingerprintEngine::~LegacyFingerprintEngine() = default;

LegacyFingerprintEngine::Purpose
LegacyFingerprintEngine::FromQuickUnlockPurpose(
    quick_unlock::Purpose purpose) const {
  switch (purpose) {
    case quick_unlock::Purpose::kAny:
      return LegacyFingerprintEngine::Purpose::kAny;
    case quick_unlock::Purpose::kUnlock:
      return LegacyFingerprintEngine::Purpose::kUnlock;
    case quick_unlock::Purpose::kWebAuthn:
      return LegacyFingerprintEngine::Purpose::kWebAuthn;
    case quick_unlock::Purpose::kNumOfPurposes:
      NOTREACHED_IN_MIGRATION();
  }
  NOTREACHED_IN_MIGRATION();
  return LegacyFingerprintEngine::Purpose::kAny;
}

quick_unlock::Purpose LegacyFingerprintEngine::ToQuickUnlockPurpose(
    LegacyFingerprintEngine::Purpose purpose) const {
  switch (purpose) {
    case LegacyFingerprintEngine::Purpose::kAny:
      return quick_unlock::Purpose::kAny;
    case LegacyFingerprintEngine::Purpose::kUnlock:
      return quick_unlock::Purpose::kUnlock;
    case LegacyFingerprintEngine::Purpose::kWebAuthn:
      return quick_unlock::Purpose::kWebAuthn;
  }
  NOTREACHED_IN_MIGRATION();
  return quick_unlock::Purpose::kAny;
}

bool LegacyFingerprintEngine::IsFingerprintDisabledByPolicy(
    const PrefService& pref_service,
    LegacyFingerprintEngine::Purpose purpose) const {
  // TODO(b/274087315): Create a TestApi for this class.
  if (auto* test_api = quick_unlock::TestApi::Get();
      test_api && test_api->IsQuickUnlockOverridden()) {
    return !test_api->IsFingerprintEnabledByPolicy(
        ToQuickUnlockPurpose(purpose));
  }

  if (purpose == LegacyFingerprintEngine::Purpose::kAny) {
    return IsFingerprintDisabledByPolicySinglePurpose(
               pref_service, LegacyFingerprintEngine::Purpose::kUnlock) &&
           IsFingerprintDisabledByPolicySinglePurpose(
               pref_service, LegacyFingerprintEngine::Purpose::kWebAuthn);
  }
  return IsFingerprintDisabledByPolicySinglePurpose(pref_service, purpose);
}

bool IsFingerprintSupported() {
  const base::CommandLine* command_line =
      base::CommandLine::ForCurrentProcess();
  return base::FeatureList::IsEnabled(features::kQuickUnlockFingerprint) &&
         command_line->HasSwitch(switches::kFingerprintSensorLocation);
}

bool LegacyFingerprintEngine::IsFingerprintEnabled(
    const PrefService& pref_service,
    LegacyFingerprintEngine::Purpose purpose) const {
  // TODO(b/274087315): Create a TestApi for this class.
  if (auto* test_api = quick_unlock::TestApi::Get();
      test_api && test_api->IsQuickUnlockOverridden()) {
    // When we override behavior for test we don't need to check
    // `IsFingerprintSupported`
    return !IsFingerprintDisabledByPolicy(pref_service, purpose);
  }

  return !IsFingerprintDisabledByPolicy(pref_service, purpose) &&
         IsFingerprintSupported();
}

bool LegacyFingerprintEngine::IsFingerprintAvailable(
    Purpose purpose,
    const AccountId& account_id) {
  auto* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id);

  auto* pref_service = profile->GetPrefs();

  if (!pref_service) {
    return false;
  }

  if (profile != ProfileManager::GetPrimaryUserProfile() ||
      IsFingerprintDisabledByPolicy(*pref_service, purpose)) {
    return false;
  }

  return HasRecord(*pref_service);
}

void LegacyFingerprintEngine::PrepareLegacyFingerprintFactor(
    std::unique_ptr<UserContext> user_context,
    AuthOperationCallback callback) {
  auth_performer_->PrepareAuthFactor(
      std::move(user_context), cryptohome::AuthFactorType::kLegacyFingerprint,
      std::move(callback));
}

void LegacyFingerprintEngine::TerminateLegacyFingerprintFactor(
    std::unique_ptr<UserContext> user_context,
    AuthOperationCallback callback) {
  auth_performer_->TerminateAuthFactor(
      std::move(user_context), cryptohome::AuthFactorType::kLegacyFingerprint,
      std::move(callback));
}

}  // namespace ash