chromium/chromeos/ash/components/osauth/impl/auth_hub_vector_lifecycle.h

// 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.

#ifndef CHROMEOS_ASH_COMPONENTS_OSAUTH_IMPL_AUTH_HUB_VECTOR_LIFECYCLE_H_
#define CHROMEOS_ASH_COMPONENTS_OSAUTH_IMPL_AUTH_HUB_VECTOR_LIFECYCLE_H_

#include <optional>

#include "base/component_export.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/osauth/impl/auth_hub_common.h"
#include "chromeos/ash/components/osauth/public/auth_factor_engine.h"
#include "chromeos/ash/components/osauth/public/common_types.h"

namespace ash {

// This class manages inner lifecycle of the auth hub.
// Outer lifecycle includes switching between `AuthAttemptVector`s,
// basically a combination of `AccountId`+`AuthPurpose`.
// This class is re-created every time owning `AuthHub` changes mode,
// and receives initialized engines as parameter.
// Lifecycle is:
//  * Call each engine's `StartAuthFlow` with auth vector, and wait for
//  completion.
//  * Notify `Owner` about available / failed auth factors.
//  * Once attempt is canceled, execute a two-stage finish:
//    1. Clean-up substage
//      1.1 Call each engine's `CleanUp`, and wait for completion.
//      1.2 Notify `Owner` that attempt is cleaned up.
//    2.Shutdown substage
//      2.1 Call each engine's `StopAuthFlow`, and wait for completion.
//      2.2 Notify `Owner` that attempt is finished.
// `AuthHubVectorLifecycle` correctly handles attempt start/finish, even if
// request to start/finish was requested in the middle of ongoing start/finish
// sequence.

class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_OSAUTH) AuthHubVectorLifecycle
    : public AuthFactorEngine::FactorEngineObserver {
 public:
  // Interface to interact with owning AuthHub:
  class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_OSAUTH) Owner {
   public:
    virtual ~Owner();
    virtual AuthFactorEngine::FactorEngineObserver* AsEngineObserver() = 0;
    virtual void OnAttemptStarted(const AuthAttemptVector& attempt,
                                  AuthFactorsSet available_factors,
                                  AuthFactorsSet failed_factors) = 0;
    virtual void OnAttemptCleanedUp(const AuthAttemptVector& attempt) = 0;
    virtual void OnAttemptFinished(const AuthAttemptVector& attempt) = 0;
    virtual void OnAttemptCancelled(const AuthAttemptVector& attempt) = 0;
    virtual void OnIdle() = 0;
  };

  AuthHubVectorLifecycle(Owner* owner,
                         AuthHubMode mode,
                         const AuthEnginesMap& engines);
  ~AuthHubVectorLifecycle() override;

  void StartAttempt(const AuthAttemptVector& vector);
  void OnFactorInitialized(AshAuthFactor factor);

  // Calling any of those those methods would trigger the same cleanup code path
  // within this class. The difference is the `owner_` aka `AuthHub` method that
  // we call at the end of the cleanup, indicating either that the flow has been
  // shut down due to a request from the user to cancel the flow, or that the
  // flow has been shut down due to a successful auth attempt.
  void CancelAttempt();
  void FinishAttempt();

  bool IsIdle() const;

  // AuthFactorEngine::FactorEngineObserver:
  void OnFactorPresenceChecked(AshAuthFactor factor,
                               bool factor_present) override;
  void OnFactorAttempt(AshAuthFactor factor) override;
  void OnFactorAttemptResult(AshAuthFactor factor, bool success) override;
  void OnPolicyChanged(AshAuthFactor factor) override;
  void OnLockoutChanged(AshAuthFactor factor) override;
  void OnFactorSpecificRestrictionsChanged(AshAuthFactor factor) override;
  void OnCriticalError(AshAuthFactor factor) override;
  void OnFactorCustomSignal(AshAuthFactor factor) override;

 private:
  enum class Stage {
    kIdle,
    kStartingAttempt,
    kStarted,
    kCleaningUpAttempt,
    kFinishingAttempt,
  };

  // When an attempt is finished, or canceled after request form the user we
  // start shutting the engines as part of
  // `AuthHubVectorLifecycle::ShutdownAttempt`, We need to notify `Owner` (a.k.a
  // AuthHub) that we have finished all necessary steps for the shutdown, and
  // thus, when finishing or canceling an attempt, we need to make sure we are
  // properly notifying `AuthHub`.
  using OnShutdownAttemptNotifyOwner =
      base::OnceCallback<void(const AuthAttemptVector&)>;

  struct FactorAttemptState;

  void StartForTargetAttempt();
  void OnAttemptStartWatchdog();
  void ProceedIfAllFactorsStarted();

  void ShutdownAttempt(OnShutdownAttemptNotifyOwner on_shutdown_attempt);
  void OnFactorCleanedUp(AshAuthFactor factor);
  void OnAttemptCleanedUpWatchdog();
  void FinishIfAllFactorsCleanedUp();
  void OnFactorFinished(AshAuthFactor factor);
  void OnAttemptFinishWatchdog();
  void ProceedIfAllFactorsFinished();

  void OnCancelAttempt(const AuthAttemptVector& current_attempt);
  void OnFinishAttempt(const AuthAttemptVector& current_attempt);

  Stage stage_ = Stage::kIdle;

  std::optional<AuthAttemptVector> current_attempt_;
  std::optional<AuthAttemptVector> target_attempt_;
  std::optional<AuthAttemptVector> initializing_for_;
  std::optional<AuthAttemptVector> last_started_attempt_;

  AuthEnginesMap available_engines_;

  base::flat_map<AshAuthFactor, FactorAttemptState> engines_;

  base::OneShotTimer watchdog_;
  raw_ptr<Owner> owner_;
  OnShutdownAttemptNotifyOwner on_shutdown_attempt_;
  base::WeakPtrFactory<AuthHubVectorLifecycle> weak_factory_{this};
};

}  // namespace ash

#endif  // CHROMEOS_ASH_COMPONENTS_OSAUTH_IMPL_AUTH_HUB_VECTOR_LIFECYCLE_H_