chromium/device/fido/mac/scoped_touch_id_test_environment.h

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef DEVICE_FIDO_MAC_SCOPED_TOUCH_ID_TEST_ENVIRONMENT_H_
#define DEVICE_FIDO_MAC_SCOPED_TOUCH_ID_TEST_ENVIRONMENT_H_

#include <os/availability.h>

#include <memory>

#include "base/component_export.h"
#include "device/fido/mac/authenticator_config.h"

namespace crypto {
class ScopedFakeAppleKeychainV2;
}  // namespace crypto

namespace device::fido::mac {

class FakeTouchIdContext;
class TouchIdContext;

// ScopedTouchIdTestEnvironment overrides behavior of the Touch ID
// authenticator in testing. While in scope, it
//  - installs a fake Keychain to avoid writing to the macOS keychain, which
//    requires a valid code signature and keychain-access-group entitlement;
//  - allows faking TouchIdContext instances returned by TouchIdContext to stub
//    out Touch ID fingerprint prompts.
//  Overrides are reset when the instance is destroyed.
class COMPONENT_EXPORT(DEVICE_FIDO) ScopedTouchIdTestEnvironment {
 public:
  explicit ScopedTouchIdTestEnvironment(AuthenticatorConfig config);

  ScopedTouchIdTestEnvironment(const ScopedTouchIdTestEnvironment&) = delete;
  ScopedTouchIdTestEnvironment& operator=(const ScopedTouchIdTestEnvironment&) =
      delete;

  ~ScopedTouchIdTestEnvironment();

  // Injects a fake to be returned by the
  // next call to `TouchIdContext::Create()`, which will automatically resolve
  // calls to `PromptTouchId()` successfully.
  void SimulateTouchIdPromptSuccess();

  // Like `SimulateTouchIdPromptSuccess()`, but `PromptTouchId()` resolves with
  // a failure.
  void SimulateTouchIdPromptFailure();

  // Sets the value returned by TouchIdContext::TouchIdAvailable. The default on
  // instantiation of the test environment is true.
  bool SetTouchIdAvailable(bool available);

  // Will prevent the next call to PromptTouchId from running the callback.
  void DoNotResolveNextPrompt();

  crypto::ScopedFakeAppleKeychainV2* keychain() { return keychain_.get(); }

 private:
  static std::unique_ptr<TouchIdContext> ForwardCreate();
  static bool ForwardTouchIdAvailable(AuthenticatorConfig);

  std::unique_ptr<TouchIdContext> CreateTouchIdContext();
  bool TouchIdAvailable(AuthenticatorConfig);

  using CreateFuncPtr = decltype(&ForwardCreate);
  CreateFuncPtr touch_id_context_create_ptr_;
  using TouchIdAvailableFuncPtr = decltype(&ForwardTouchIdAvailable);
  TouchIdAvailableFuncPtr touch_id_context_touch_id_available_ptr_;

  AuthenticatorConfig config_;
  std::unique_ptr<crypto::ScopedFakeAppleKeychainV2> keychain_;
  std::unique_ptr<FakeTouchIdContext> next_touch_id_context_;
  bool touch_id_available_ = true;
};

}  // namespace device::fido::mac

#endif  // DEVICE_FIDO_MAC_SCOPED_TOUCH_ID_TEST_ENVIRONMENT_H_