// 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.
#ifndef CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_AUTH_FACTOR_CONFIG_H_
#define CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_AUTH_FACTOR_CONFIG_H_
#include <optional>
#include "base/containers/enum_set.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ref.h"
#include "chromeos/ash/components/login/auth/auth_factor_editor.h"
#include "chromeos/ash/components/login/auth/public/authentication_error.h"
#include "chromeos/ash/services/auth_factor_config/chrome_browser_delegates.h"
#include "chromeos/ash/services/auth_factor_config/public/mojom/auth_factor_config.mojom.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote_set.h"
namespace ash::auth {
// The implementation of the AuthFactorConfig service.
class AuthFactorConfig : public mojom::AuthFactorConfig {
public:
class TestApi {
public:
explicit TestApi(AuthFactorConfig& auth_factor_config)
: auth_factor_config_(auth_factor_config) {}
// Injects a callback that gets invoked after knowledge factor
// is added.
void SetAddKnowledgeFactorCallback(base::OnceClosure callback) {
auth_factor_config_->SetAddKnowledgeFactorCallbackForTesting(
std::move(callback));
}
// Instructs AuthFactorConfig not to inform
// UserDirectoryIntegrityManager about added factors.
void SetSkipUserIntegrityNotification(bool skip_notification) {
auth_factor_config_->SetSkipUserIntegrityNotificationForTesting(
skip_notification);
}
private:
const raw_ref<AuthFactorConfig> auth_factor_config_;
};
using AuthFactorSet = base::EnumSet<mojom::AuthFactor,
mojom::AuthFactor::kMinValue,
mojom::AuthFactor::kMaxValue>;
explicit AuthFactorConfig(QuickUnlockStorageDelegate*,
PrefService* local_state);
~AuthFactorConfig() override;
AuthFactorConfig(const AuthFactorConfig&) = delete;
AuthFactorConfig& operator=(const AuthFactorConfig&) = delete;
static void RegisterPrefs(PrefRegistrySimple* registry);
void BindReceiver(mojo::PendingReceiver<mojom::AuthFactorConfig> receiver);
void ObserveFactorChanges(
mojo::PendingRemote<mojom::FactorObserver>) override;
void IsSupported(const std::string& auth_token,
mojom::AuthFactor factor,
base::OnceCallback<void(bool)>) override;
void IsConfigured(const std::string& auth_token,
mojom::AuthFactor factor,
base::OnceCallback<void(bool)>) override;
void GetManagementType(
const std::string& auth_token,
mojom::AuthFactor factor,
base::OnceCallback<void(mojom::ManagementType)>) override;
void IsEditable(const std::string& auth_token,
mojom::AuthFactor factor,
base::OnceCallback<void(bool)>) override;
// Reload auth factor data from cryptohome and notify factor change observers
// of the change. This method must be called after successful mutating
// UserDataAuth calls so that the list of auth factors remains up to date.
// `context` should be a copy of the user context stored in quick unlock
// storage. In particular, `context` should contain an authenticated auth
// session
void NotifyFactorObserversAfterSuccess(
AuthFactorSet changed_factor,
const std::string& auth_token,
std::unique_ptr<UserContext> context,
base::OnceCallback<void(mojom::ConfigureResult)> callback);
// Like NotifyFactorObserversAfterSuccess, but supposed to be called before
// we return a `kFatalError` result because of a failed mutating UserDataAuth
// call. This method will reload auth factors and send a change notification
// to observers for all auth factors.
// This is useful because a likely source of errors is outdated information
// about the status of configured auth factors, resulting in an invalid
// UserDataAuth call. For example, we might think that an auth factor is
// configured and try to update it. If some other system has removed this
// auth factor without our knowledge, the update call will fail. By
// refreshing our information on what auth factors are configured, we can
// recover so that the user can try again.
void NotifyFactorObserversAfterFailure(const std::string& auth_token,
std::unique_ptr<UserContext> context,
base::OnceCallback<void()> callback);
// Called when user is known to have knowledge factor set up.
void OnUserHasKnowledgeFactor(const UserContext& context);
private:
friend class TestApi;
using FactorStatusCheckOperation =
base::OnceCallback<void(std::unique_ptr<UserContext>)>;
using FactorStatusCheckResultCallback = base::OnceCallback<void(bool)>;
using OnRefreshAuthFactorsConfiguration =
base::OnceCallback<void(std::unique_ptr<UserContext>)>;
void ObtainContext(
const std::string& auth_token,
base::OnceCallback<void(std::unique_ptr<UserContext>)> callback);
void IsSupportedWithContext(const std::string& auth_token,
mojom::AuthFactor factor,
FactorStatusCheckResultCallback callback,
std::unique_ptr<UserContext> context);
void IsConfiguredWithContext(const std::string& auth_token,
mojom::AuthFactor factor,
FactorStatusCheckResultCallback,
std::unique_ptr<UserContext> context);
void IsEditableWithContext(const std::string& auth_token,
mojom::AuthFactor factor,
FactorStatusCheckResultCallback,
std::unique_ptr<UserContext> context);
void OnGetAuthFactorsConfiguration(
AuthFactorSet changed_factors,
base::OnceCallback<void(mojom::ConfigureResult)> callback,
const std::string& auth_token,
std::unique_ptr<UserContext> context,
std::optional<AuthenticationError> error);
void SetAddKnowledgeFactorCallbackForTesting(base::OnceClosure callback);
void SetSkipUserIntegrityNotificationForTesting(bool skip_notification);
void RefreshAuthFactorsConfiguration(
std::unique_ptr<UserContext> context,
OnRefreshAuthFactorsConfiguration continuation);
void OnAuthFactorConfigurationRefreshed(
OnRefreshAuthFactorsConfiguration continuation,
std::unique_ptr<UserContext> context,
std::optional<AuthenticationError> error);
void MaybeRetryFactorStatusCheckOnConfigRefresh(
FactorStatusCheckOperation,
FactorStatusCheckResultCallback,
const std::string& auth_token,
std::unique_ptr<UserContext> context);
raw_ptr<QuickUnlockStorageDelegate> quick_unlock_storage_;
// This instance is held by browser process (see in_process_instances)
// as well as local_state_, so they should be local state would become
// invalid quite late in the flow, by that time it should not be possible
// to interact with AuthFactorConfig.
raw_ptr<PrefService, DisableDanglingPtrDetection> local_state_;
mojo::ReceiverSet<mojom::AuthFactorConfig> receivers_;
mojo::RemoteSet<mojom::FactorObserver> observers_;
AuthFactorEditor auth_factor_editor_;
// Used for testing, invoked when a knowledge factor is added.
base::OnceClosure add_knowledge_factor_callback_;
std::optional<bool> skip_user_integrity_notification_;
base::WeakPtrFactory<AuthFactorConfig> weak_factory_{this};
};
} // namespace ash::auth
#endif // CHROMEOS_ASH_SERVICES_AUTH_FACTOR_CONFIG_AUTH_FACTOR_CONFIG_H_