chromium/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.cc

// Copyright 2012 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/policy/core/user_cloud_policy_manager_ash.h"

#include <memory>
#include <set>
#include <utility>

#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/syslog_logging.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/default_clock.h"
#include "base/values.h"
#include "chrome/browser/ash/login/helper.h"
#include "chrome/browser/ash/login/session/user_session_manager.h"
#include "chrome/browser/ash/login/users/affiliation.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/core/policy_oauth2_token_fetcher.h"
#include "chrome/browser/ash/policy/login/wildcard_login_checker.h"
#include "chrome/browser/ash/policy/remote_commands/user_commands_factory_ash.h"
#include "chrome/browser/ash/policy/reporting/arc_app_install_event_log_uploader.h"
#include "chrome/browser/ash/policy/skyvault/local_files_cleanup.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/enterprise/reporting/report_scheduler_desktop.h"
#include "chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h"
#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/policy/cloud/user_fm_registration_token_uploader_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/common/chrome_features.h"
#include "components/enterprise/browser/reporting/real_time_report_controller.h"
#include "components/enterprise/browser/reporting/report_generator.h"
#include "components/enterprise/browser/reporting/report_scheduler.h"
#include "components/invalidation/invalidation_listener.h"
#include "components/invalidation/profile_invalidation_provider.h"
#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
#include "components/policy/core/common/cloud/affiliation.h"
#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/cloud_policy_core.h"
#include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/core/common/remote_commands/remote_commands_invalidator_impl.h"
#include "components/policy/policy_constants.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/network_service_instance.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/gurl.h"

namespace em = enterprise_management;

namespace policy {

namespace {

// UMA histogram names.
const char kUMAInitialFetchOAuth2Error[] =
    "Enterprise.UserPolicyChromeOS.InitialFetch.OAuth2Error";
const char kUMAReregistrationResult[] =
    "Enterprise.UserPolicyChromeOS.ReregistrationResult";

// The oauth token consumer name.
const char kOAuthConsumerName[] = "user_cloud_policy_manager_ash";

// This enum is used in UMA, items should not be reordered/deleted. New values
// should also be added to enums.xml.
enum class RegistrationResult {
  kReregistrationTriggered = 0,
  kReregistrationSuccessful = 1,
  kReregistrationUnsuccessful = 2,
  kMaxValue = kReregistrationUnsuccessful,
};

void RegistrationResultUMA(RegistrationResult registration_result) {
  UMA_HISTOGRAM_ENUMERATION(kUMAReregistrationResult, registration_result);
}

// Returns whether user with |account_id| is a child. Returns false if user with
// |account_id| is not found.
bool IsChildUser(const AccountId& account_id) {
  const user_manager::User* const user =
      user_manager::UserManager::Get()->FindUser(account_id);
  return user && user->GetType() == user_manager::UserType::kChild;
}

// This class is used to subscribe for notifications that the current profile is
// being shut down.
class UserCloudPolicyManagerAshNotifierFactory
    : public BrowserContextKeyedServiceShutdownNotifierFactory {
 public:
  static UserCloudPolicyManagerAshNotifierFactory* GetInstance() {
    return base::Singleton<UserCloudPolicyManagerAshNotifierFactory>::get();
  }

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

 private:
  friend struct base::DefaultSingletonTraits<
      UserCloudPolicyManagerAshNotifierFactory>;

  UserCloudPolicyManagerAshNotifierFactory()
      : BrowserContextKeyedServiceShutdownNotifierFactory(
            "UserRemoteCommandsInvalidator") {
    DependsOn(invalidation::ProfileInvalidationProviderFactory::GetInstance());
    DependsOn(policy::UserFmRegistrationTokenUploaderFactory::GetInstance());
  }

  ~UserCloudPolicyManagerAshNotifierFactory() override = default;
};

// Returns true only if SkyVault TT is enabled, but GA is not.
bool IsSkyVaultTTEnabled() {
  return base::FeatureList::IsEnabled(features::kSkyVault) &&
         !base::FeatureList::IsEnabled(features::kSkyVaultV2);
}

}  // namespace

UserCloudPolicyManagerAsh::UserCloudPolicyManagerAsh(
    Profile* profile,
    std::unique_ptr<CloudPolicyStore> store,
    std::unique_ptr<CloudExternalDataManager> external_data_manager,
    const base::FilePath& component_policy_cache_path,
    PolicyEnforcement enforcement_type,
    PrefService* local_state,
    base::TimeDelta policy_refresh_timeout,
    base::OnceClosure fatal_error_callback,
    const AccountId& account_id,
    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
    : CloudPolicyManager(
          dm_protocol::kChromeUserPolicyType,
          std::string(),
          std::move(store),
          task_runner,
          base::BindRepeating(content::GetNetworkConnectionTracker)),
      profile_(profile),
      external_data_manager_(std::move(external_data_manager)),
      component_policy_cache_path_(component_policy_cache_path),
      waiting_for_policy_fetch_(enforcement_type ==
                                    PolicyEnforcement::kServerCheckRequired ||
                                !policy_refresh_timeout.is_zero()),
      enforcement_type_(enforcement_type),
      local_state_(local_state),
      account_id_(account_id),
      fatal_error_callback_(std::move(fatal_error_callback)) {
  DCHECK(profile_);
  DCHECK(local_state_);

  // If a refresh timeout was specified, set a timer to call us back.
  if (!policy_refresh_timeout.is_zero()) {
    // Shouldn't pass a timeout unless we're refreshing existing policy.
    DCHECK_EQ(enforcement_type_, PolicyEnforcement::kPolicyRequired);
    policy_refresh_timeout_.Start(
        FROM_HERE, policy_refresh_timeout,
        base::BindOnce(&UserCloudPolicyManagerAsh::OnPolicyRefreshTimeout,
                       base::Unretained(this)));
  }

  // Register for notification that profile creation is complete - this is used
  // for creating the invalidator for user remote commands. The invalidator must
  // not be initialized before then because the invalidation service cannot be
  // started because it depends on components initialized at the end of profile
  // creation. https://crbug.com/171406
  observed_profile_.Observe(profile_.get());
}

void UserCloudPolicyManagerAsh::ForceTimeoutForTest() {
  DCHECK(policy_refresh_timeout_.IsRunning());
  // Stop the timer to mimic what happens when a real timer fires, then invoke
  // the timer callback directly.
  policy_refresh_timeout_.Stop();
  OnPolicyRefreshTimeout();
}

void UserCloudPolicyManagerAsh::SetSignInURLLoaderFactoryForTests(
    scoped_refptr<network::SharedURLLoaderFactory> signin_url_loader_factory) {
  signin_url_loader_factory_for_tests_ = signin_url_loader_factory;
}

void UserCloudPolicyManagerAsh::SetSystemURLLoaderFactoryForTests(
    scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory) {
  system_url_loader_factory_for_tests_ = system_url_loader_factory;
}

UserCloudPolicyManagerAsh::~UserCloudPolicyManagerAsh() = default;

void UserCloudPolicyManagerAsh::ConnectManagementService(
    DeviceManagementService* device_management_service,
    scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory) {
  DCHECK(device_management_service);

  CHECK(!core()->client());

  // Note: |system_url_loader_factory| can be null for tests.
  // Use the system URL loader context here instead of a context derived
  // from the Profile because Connect() is called before the profile is
  // fully initialized (required so we can perform the initial policy load).
  std::unique_ptr<CloudPolicyClient> cloud_policy_client =
      std::make_unique<CloudPolicyClient>(
          device_management_service, system_url_loader_factory,
          ash::GetDeviceDMTokenForUserPolicyGetter(account_id_));
  CreateComponentCloudPolicyService(
      dm_protocol::kChromeExtensionPolicyType, component_policy_cache_path_,
      cloud_policy_client.get(), schema_registry());
  core()->Connect(std::move(cloud_policy_client));
  client()->AddObserver(this);

  external_data_manager_->Connect(system_url_loader_factory);

  // Determine the next step after the CloudPolicyService initializes.
  if (service()->IsInitializationComplete()) {
    // The CloudPolicyStore is already initialized here, which means we must
    // have done a synchronous load of policy - this only happens after a crash
    // and restart. If we crashed and restarted, it's not possible to block
    // waiting for a policy fetch (profile is loading synchronously and async
    // operations can't be handled).

    // If we are doing a synchronous load, then wait_for_policy_fetch_ should
    // never be set (because we can't wait).
    CHECK(!waiting_for_policy_fetch_);
    if (!client()->is_registered() &&
        enforcement_type_ != PolicyEnforcement::kPolicyOptional) {
      // We expected to load policy, but we don't have policy, so exit the
      // session.
      LOG(ERROR) << "Failed to load policy during synchronous restart "
                 << "- terminating session";
      if (fatal_error_callback_) {
        std::move(fatal_error_callback_).Run();
      }
      return;
    }

    // Initialization has completed before our observer was registered
    // so invoke our callback directly.
    OnCloudPolicyServiceInitializationCompleted();
  } else {
    // Wait for the CloudPolicyStore to finish initializing.
    service()->AddObserver(this);
  }

  app_install_event_log_uploader_ =
      std::make_unique<ArcAppInstallEventLogUploader>(client(), profile_);

  if (IsSkyVaultTTEnabled()) {
    // Local files should be deleted if required by policy.
    local_files_cleanup_ =
        std::make_unique<local_user_files::LocalFilesCleanup>();
  }
}

void UserCloudPolicyManagerAsh::OnAccessTokenAvailable(
    const std::string& access_token) {
  // This method should be called only once (at the beginning of the session)
  // for regular user.
  // For child user this method will be called multiple times (periodically).

  access_token_ = access_token;

  if (!wildcard_username_.empty()) {
    wildcard_login_checker_ = std::make_unique<WildcardLoginChecker>();
    // Safe to set a callback with an unretained pointer because the
    // WildcardLoginChecker is owned by this object and won't invoke the
    // callback after we destroy it.
    wildcard_login_checker_->StartWithAccessToken(
        access_token,
        base::BindOnce(&UserCloudPolicyManagerAsh::OnWildcardCheckCompleted,
                       base::Unretained(this), wildcard_username_));
  }

  if (service() && service()->IsInitializationComplete() && client()) {
    if (!client()->is_registered()) {
      OnOAuth2PolicyTokenFetched(
          access_token, GoogleServiceAuthError(GoogleServiceAuthError::NONE));
    } else if (RequiresOAuthTokenForChildUser()) {
      client()->SetOAuthTokenAsAdditionalAuth(access_token);
      StartRefreshSchedulerIfReady();
    }
  }
}

bool UserCloudPolicyManagerAsh::RequiresOAuthTokenForChildUser() const {
  return IsChildUser(account_id_) &&
         base::FeatureList::IsEnabled(features::kDMServerOAuthForChildUser);
}

void UserCloudPolicyManagerAsh::OnWildcardCheckCompleted(
    const std::string& username,
    WildcardLoginChecker::Result result) {
  if (result == WildcardLoginChecker::RESULT_BLOCKED) {
    LOG(ERROR) << "Online wildcard login check failed, terminating session.";

    // TODO(mnissler): This only removes the user pod from the login screen, but
    // the cryptohome remains. This is because deleting the cryptohome for a
    // logged-in session is not possible. Fix this either by delaying the
    // cryptohome deletion operation or by getting rid of the in-session
    // wildcard check.
    // Also note that, following |fatal_error_callback_| is practically
    // OnUserPolicyFatalError above, so is attempting to shutting down Chrome.
    // Thus, some asynchronous operations such as reporting in
    // UserAddedRemovedReporter are not guaranteed to be completed unless
    // task runners' priority/shutdown-behavior are configured.
    user_manager::UserManager::Get()->RemoveUserFromList(
        AccountId::FromUserEmail(username));
    if (fatal_error_callback_) {
      std::move(fatal_error_callback_).Run();
    }
  }
}

void UserCloudPolicyManagerAsh::EnableWildcardLoginCheck(
    const std::string& username) {
  DCHECK(access_token_.empty());
  wildcard_username_ = username;
}

ArcAppInstallEventLogUploader*
UserCloudPolicyManagerAsh::GetAppInstallEventLogUploader() {
  return app_install_event_log_uploader_.get();
}

void UserCloudPolicyManagerAsh::Shutdown() {
  observed_profile_.Reset();
  local_files_cleanup_.reset();
  app_install_event_log_uploader_.reset();
  report_scheduler_.reset();
  if (client()) {
    client()->RemoveObserver(this);
  }
  if (service()) {
    service()->RemoveObserver(this);
  }
  token_fetcher_.reset();
  external_data_manager_->Disconnect();
  CloudPolicyManager::Shutdown();
}

bool UserCloudPolicyManagerAsh::IsInitializationComplete(
    PolicyDomain domain) const {
  if (!CloudPolicyManager::IsInitializationComplete(domain)) {
    return false;
  }
  if (domain == POLICY_DOMAIN_CHROME) {
    return !waiting_for_policy_fetch_;
  }
  return true;
}

void UserCloudPolicyManagerAsh::OnCloudPolicyServiceInitializationCompleted() {
  service()->RemoveObserver(this);

  // If the CloudPolicyClient isn't registered at this stage then it needs an
  // OAuth token for the initial registration (there's no cached policy).
  //
  // If |waiting_for_policy_fetch_| is true then Profile initialization
  // is blocking on the initial policy fetch, so the token must be fetched
  // immediately. In that case, the signin Profile is used to authenticate a
  // Gaia request to fetch a refresh token, and then the policy token is
  // fetched.
  //
  // If |waiting_for_policy_fetch_| is false (meaning this is a
  // pre-existing session that doesn't have policy and we're just doing a
  // background check to see if the user has become managed since last signin)
  // then the UserCloudPolicyTokenForwarder service will eventually call
  // OnAccessTokenAvailable() once an access token is available. That call may
  // have already happened while waiting for initialization of the
  // CloudPolicyService, so in that case check if an access token is already
  // available.
  if (!client()->is_registered()) {
    if (waiting_for_policy_fetch_) {
      FetchPolicyOAuthToken();
    } else if (!access_token_.empty()) {
      OnAccessTokenAvailable(access_token_);
    }
  }

  // If this isn't blocking on a policy fetch then
  // CloudPolicyManager::OnStoreLoaded() already published the cached policy.
  // Start the refresh scheduler now, which will eventually refresh the
  // cached policy or make the first fetch once the OAuth2 token is
  // available. If refresh scheduler is already started this call will do
  // nothing.
  StartRefreshSchedulerIfReady();

  // Start the report scheduler to periodically upload usage data to DM server.
  StartReportSchedulerIfReady(true /* enable_delayed_creation */);
}

void UserCloudPolicyManagerAsh::OnPolicyFetched(CloudPolicyClient* client) {
  // No action required. If we're blocked on a policy fetch, we'll learn about
  // completion of it through OnInitialPolicyFetchComplete().
}

void UserCloudPolicyManagerAsh::OnRegistrationStateChanged(
    CloudPolicyClient* cloud_policy_client) {
  DCHECK_EQ(client(), cloud_policy_client);

  // Trigger re-registration. This happens if the client ID used for policy
  // fetches is unknown/purged from the DMServer.
  if (!client()->is_registered() && client()->requires_reregistration()) {
    RegistrationResultUMA(RegistrationResult::kReregistrationTriggered);
    is_in_reregistration_state_ = true;
    if (!access_token_.empty()) {
      OnOAuth2PolicyTokenFetched(
          access_token_, GoogleServiceAuthError(GoogleServiceAuthError::NONE));
    } else {
      FetchPolicyOAuthToken();
    }
    return;
  }
  // Reset re-registration state on successful registration.
  if (client()->is_registered() && is_in_reregistration_state_) {
    RegistrationResultUMA(RegistrationResult::kReregistrationSuccessful);
    is_in_reregistration_state_ = false;
  }

  if (waiting_for_policy_fetch_) {
    // If we're blocked on the policy fetch, now is a good time to issue it.
    if (client()->is_registered()) {
      service()->RefreshPolicy(
          base::BindOnce(
              &UserCloudPolicyManagerAsh::OnInitialPolicyFetchComplete,
              base::Unretained(this)),
          PolicyFetchReason::kRegistrationChanged);
    } else {
      // If the client has switched to not registered, we bail out as this
      // indicates the cloud policy setup flow has been aborted.
      CancelWaitForPolicyFetch(true, std::string());
    }
  }
}

void UserCloudPolicyManagerAsh::OnClientError(
    CloudPolicyClient* cloud_policy_client) {
  DCHECK_EQ(client(), cloud_policy_client);
  switch (client()->last_dm_status()) {
    case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
      // If management is not supported for this user, then a registration
      // error is to be expected - treat as a policy fetch success. Also
      // mark this profile as not requiring policy.
      SetPolicyRequired(false);
      CancelWaitForPolicyFetch(true, std::string());
      break;
    case DM_STATUS_SUCCESS:
      CancelWaitForPolicyFetch(true, std::string());
      break;
    default:
      // Unexpected error fetching policy.
      CancelWaitForPolicyFetch(
          false, "cloud policy client status: " +
                     base::NumberToString(client()->last_dm_status()));
      break;
  }
  // If we are in re-registration state and re-registration fails, we mark the
  // user to require an online sign-in on his next sign-in.
  if (is_in_reregistration_state_) {
    RegistrationResultUMA(RegistrationResult::kReregistrationUnsuccessful);
    LOG(ERROR) << "Re-registration failed, requiring the user to perform an "
                  "online sign-in.";
    user_manager::UserManager::Get()->SaveForceOnlineSignin(account_id_, true);
  }
}

void UserCloudPolicyManagerAsh::OnComponentCloudPolicyUpdated() {
  CloudPolicyManager::OnComponentCloudPolicyUpdated();
  StartRefreshSchedulerIfReady();
}

void UserCloudPolicyManagerAsh::OnUserProfileLoaded(
    const AccountId& account_id) {
  if (!user_manager::UserManager::Get()) {
    return;
  }

  const user_manager::User* primary_user =
      user_manager::UserManager::Get()->GetPrimaryUser();

  if (!primary_user || primary_user->GetAccountId() != account_id ||
      !primary_user->is_profile_created()) {
    return;
  }

  session_manager::SessionManager::Get()->RemoveObserver(this);
  StartReportSchedulerIfReady(false /* enable_delayed_creation */);
}

void UserCloudPolicyManagerAsh::OnStoreLoaded(
    CloudPolicyStore* cloud_policy_store) {
  CloudPolicyManager::OnStoreLoaded(cloud_policy_store);

  em::PolicyData const* const policy_data = cloud_policy_store->policy();

  if (policy_data) {
    // We have cached policy in the store, so update the various flags to
    // reflect that we have policy.
    SetPolicyRequired(true);

    // Policy was successfully loaded from disk, so it's OK if a subsequent
    // server fetch fails.
    enforcement_type_ = PolicyEnforcement::kPolicyOptional;

    DCHECK(policy_data->has_username());

    policy::BrowserPolicyConnectorAsh const* const connector =
        g_browser_process->platform_part()->browser_policy_connector_ash();
    const bool is_affiliated = policy::IsUserAffiliated(
        base::flat_set<std::string>(policy_data->user_affiliation_ids().begin(),
                                    policy_data->user_affiliation_ids().end()),
        connector->device_affiliation_ids(), account_id_.GetUserEmail());

    user_manager::UserManager::Get()->SetUserAffiliated(account_id_,
                                                        is_affiliated);
  }
}

void UserCloudPolicyManagerAsh::SetPolicyRequired(bool policy_required) {
  auto* user_manager = user_manager::UserManager::Get();
  user_manager::KnownUser known_user(local_state_);
  known_user.SetProfileRequiresPolicy(
      account_id_,
      policy_required ? user_manager::ProfileRequiresPolicy::kPolicyRequired
                      : user_manager::ProfileRequiresPolicy::kNoPolicyRequired);
  if (user_manager->IsCurrentUserNonCryptohomeDataEphemeral()) {
    // For ephemeral users, we need to set a flag via session manager - this
    // handles the case where the session restarts due to a crash (the restarted
    // instance will know whether policy is required via this flag). This
    // overwrites flags set by about://flags, but that's OK since we can't have
    // any of those flags set at startup anyway for ephemeral sessions.
    base::CommandLine command_line =
        base::CommandLine(base::CommandLine::NO_PROGRAM);
    command_line.AppendSwitchASCII(ash::switches::kProfileRequiresPolicy,
                                   policy_required ? "true" : "false");
    base::CommandLine::StringVector flags;
    flags.assign(command_line.argv().begin() + 1, command_line.argv().end());
    DCHECK_EQ(1u, flags.size());
    ash::UserSessionManager::GetInstance()->SetSwitchesForUser(
        account_id_,
        ash::UserSessionManager::CommandLineSwitchesType::kSessionControl,
        flags);
  }
}

void UserCloudPolicyManagerAsh::GetChromePolicy(PolicyMap* policy_map) {
  CloudPolicyManager::GetChromePolicy(policy_map);

  // If the store has a verified policy blob received from the server then apply
  // the defaults for policies that haven't been configured by the administrator
  // given that this is an enterprise user.
  if (!store()->has_policy()) {
    return;
  }

  // Don't apply enterprise defaults for Child user.
  if (IsChildUser(account_id_)) {
    return;
  }

  SetEnterpriseUsersDefaults(policy_map);
}

void UserCloudPolicyManagerAsh::FetchPolicyOAuthToken() {
  // By-pass token fetching for test.
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          ash::switches::kDisableGaiaServices)) {
    OnOAuth2PolicyTokenFetched(
        "fake_policy_token",
        GoogleServiceAuthError(GoogleServiceAuthError::NONE));
    return;
  }

  // TODO(jcivelli): Connect() is passed a SharedURLLoaderFactory but here we
  // retrieve it from |g_browser_process|. We should move away from retrieving
  // it from |g_browser_process| at which point we can remove
  // SetSystemURLLoaderFactoryForTests().
  scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory =
      system_url_loader_factory_for_tests_;
  if (!system_url_loader_factory) {
    system_url_loader_factory =
        g_browser_process->system_network_context_manager()
            ->GetSharedURLLoaderFactory();
  }

  std::string refresh_token = user_context_refresh_token_for_tests_.value_or(
      ash::UserSessionManager::GetInstance()->user_context().GetRefreshToken());

  if (!refresh_token.empty()) {
    token_fetcher_ =
        PolicyOAuth2TokenFetcher::CreateInstance(kOAuthConsumerName);
    token_fetcher_->StartWithRefreshToken(
        refresh_token, system_url_loader_factory,
        base::BindOnce(&UserCloudPolicyManagerAsh::OnOAuth2PolicyTokenFetched,
                       base::Unretained(this)));
    return;
  }

  LOG(ERROR) << "No refresh token for policy oauth token fetch!";
  OnOAuth2PolicyTokenFetched(
      std::string(),
      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
}

void UserCloudPolicyManagerAsh::OnOAuth2PolicyTokenFetched(
    const std::string& policy_token,
    const GoogleServiceAuthError& error) {
  DCHECK(!client()->is_registered());

  if (error.state() == GoogleServiceAuthError::NONE) {
    if (RequiresOAuthTokenForChildUser()) {
      client()->SetOAuthTokenAsAdditionalAuth(policy_token);
    }

    // Start client registration. Either OnRegistrationStateChanged() or
    // OnClientError() will be called back.
    CloudPolicyClient::RegistrationParameters parameters(
        em::DeviceRegisterRequest::USER,
        em::DeviceRegisterRequest::FLAVOR_USER_REGISTRATION);
    if (user_manager::UserManager::Get()
            ->IsCurrentUserCryptohomeDataEphemeral()) {
      parameters.lifetime = em::DeviceRegisterRequest::LIFETIME_EPHEMERAL_USER;
    }
    std::string client_id;
    if (client()->requires_reregistration()) {
      client_id = client()->client_id();
    }
    client()->Register(parameters, client_id, policy_token);
  } else {
    UMA_HISTOGRAM_ENUMERATION(kUMAInitialFetchOAuth2Error, error.state(),
                              GoogleServiceAuthError::NUM_STATES);
    // Failed to get a token, stop waiting if policy is not required for this
    // user.
    CancelWaitForPolicyFetch(
        false, "auth error state: " + base::NumberToString(error.state()));
  }

  token_fetcher_.reset();
}

void UserCloudPolicyManagerAsh::OnInitialPolicyFetchComplete(bool success) {
  CancelWaitForPolicyFetch(
      success,
      "policy fetch complete"
      ", policy client status: " +
          base::NumberToString(client()->last_dm_status()) +
          ", store status: " + base::NumberToString(store()->status()));
}

void UserCloudPolicyManagerAsh::OnPolicyRefreshTimeout() {
  DCHECK(waiting_for_policy_fetch_);
  LOG(WARNING) << "Timed out while waiting for the policy refresh. "
               << "The session will start with the cached policy.";
  CancelWaitForPolicyFetch(false, "policy refresh timeout");
}

void UserCloudPolicyManagerAsh::CancelWaitForPolicyFetch(
    bool success,
    const std::string& failure_reason) {
  if (!waiting_for_policy_fetch_) {
    return;
  }

  policy_refresh_timeout_.Stop();

  // If there was an error, and we don't want to allow profile initialization
  // to go forward after a failed policy fetch, then trigger a fatal error.
  if (!success && enforcement_type_ != PolicyEnforcement::kPolicyOptional) {
    LOG(ERROR) << "Policy fetch failed for the user. "
                  "Aborting profile initialization. "
               << failure_reason;
    // Also log to syslog so the message is visible even when the user home is
    // unmounted.
    SYSLOG(ERROR) << "Policy fetching failed. " << failure_reason;
    // Need to exit the current user, because we've already started this user's
    // session.
    if (fatal_error_callback_) {
      std::move(fatal_error_callback_).Run();
    }
    return;
  }

  waiting_for_policy_fetch_ = false;

  CheckAndPublishPolicy();
  // Now that |waiting_for_policy_fetch_| is guaranteed to be false, the
  // scheduler can be started.
  StartRefreshSchedulerIfReady();
}

void UserCloudPolicyManagerAsh::StartRefreshSchedulerIfReady() {
  if (core()->refresh_scheduler()) {
    return;  // Already started.
  }

  if (waiting_for_policy_fetch_) {
    return;  // Still waiting for the initial, blocking fetch.
  }

  if (!service() || !local_state_) {
    return;  // Not connected.
  }

  if (component_policy_service() &&
      !component_policy_service()->is_initialized()) {
    // If the client doesn't have the list of components to fetch yet then don't
    // start the scheduler. The |component_policy_service_| will call back into
    // OnComponentCloudPolicyUpdated() once it's ready.
    return;
  }

  // Do not start refresh scheduler until OAuth token is available for child
  // user, because policy refresh will fail.
  if (RequiresOAuthTokenForChildUser() && access_token_.empty()) {
    return;
  }

  core()->StartRefreshScheduler();
  core()->TrackRefreshDelayPref(local_state_,
                                policy_prefs::kUserPolicyRefreshRate);
}

void UserCloudPolicyManagerAsh::StartReportSchedulerIfReady(
    bool enable_delayed_creation) {
  if (!client() || !client()->is_registered()) {
    return;
  }

  if (!user_manager::UserManager::Get()) {
    return;
  }

  const user_manager::User* primary_user =
      user_manager::UserManager::Get()->GetPrimaryUser();

  if (!primary_user) {
    return;
  }

  // SessionManager uses another thread to load profiles. If the operation
  // doesn't finish, the creation of |report_scheduler_| will be delayed and
  // relies on SessionManagerObserver methods to monitor the progress.
  if (enable_delayed_creation && !primary_user->is_profile_created()) {
    session_manager::SessionManager::Get()->AddObserver(this);
    VLOG(0) << "Report scheduler is delayed to create because the primary "
               "user profile hasn't been loaded. This is a designed behavior.";
    return;
  }

  // TODO(crbug.com/40703888): Split up Chrome OS reporting code into its own
  // delegates, then use the Chrome OS delegate factory here.
  enterprise_reporting::ReportingDelegateFactoryDesktop delegate_factory;
  enterprise_reporting::ReportScheduler::CreateParams params;
  params.client = client();
  params.delegate = delegate_factory.GetReportSchedulerDelegate();
  params.report_generator =
      std::make_unique<enterprise_reporting::ReportGenerator>(
          &delegate_factory);

  delegate_factory.SetProfileForRealTimeController(profile_);
  params.real_time_report_controller =
      std::make_unique<enterprise_reporting::RealTimeReportController>(
          &delegate_factory);

  report_scheduler_ = std::make_unique<enterprise_reporting::ReportScheduler>(
      std::move(params));

  report_scheduler_->OnDMTokenUpdated();
}

void UserCloudPolicyManagerAsh::OnProfileInitializationComplete(
    Profile* profile) {
  DCHECK(observed_profile_.IsObservingSource(profile));
  observed_profile_.Reset();

  // Activate user remote commands only for unicorn accounts.
  // The server side only supports user-scoped remote commands for unicorn
  // accounts at the moment. See b/193450869 for more detail.
  if (!IsChildUser(account_id_)) {
    return;
  }

  invalidation::ProfileInvalidationProvider* const invalidation_provider =
      invalidation::ProfileInvalidationProviderFactory::GetForProfile(profile_);

  if (!invalidation_provider) {
    return;
  }

  core()->StartRemoteCommandsService(
      std::make_unique<UserCommandsFactoryAsh>(profile_),
      PolicyInvalidationScope::kUser);
  invalidator_ = std::make_unique<RemoteCommandsInvalidatorImpl>(
      core(), base::DefaultClock::GetInstance(),
      PolicyInvalidationScope::kUser);

  invalidator_->Initialize(
      invalidation_provider->GetInvalidationServiceOrListener(
          kPolicyFCMInvalidationSenderID,
          invalidation::InvalidationListener::kProjectNumberEnterprise));

  shutdown_subscription_ =
      UserCloudPolicyManagerAshNotifierFactory::GetInstance()
          ->Get(profile_)
          ->Subscribe(base::BindRepeating(
              &UserCloudPolicyManagerAsh::ShutdownRemoteCommands,
              base::Unretained(this)));
}

void UserCloudPolicyManagerAsh::ShutdownRemoteCommands() {
  // Unregister the RemoteCommandsInvalidatorImpl from the InvalidatorRegistrar.
  invalidator_->Shutdown();
  invalidator_.reset();
  shutdown_subscription_ = {};
}

void UserCloudPolicyManagerAsh::SetUserContextRefreshTokenForTests(
    const std::string& refresh_token) {
  DCHECK(!refresh_token.empty());
  DCHECK(!user_context_refresh_token_for_tests_);
  user_context_refresh_token_for_tests_ = std::make_optional(refresh_token);
}

enterprise_reporting::ReportScheduler*
UserCloudPolicyManagerAsh::GetReportSchedulerForTesting() {
  return report_scheduler_.get();
}

// static
void UserCloudPolicyManagerAsh::EnsureFactoryBuilt() {
  UserCloudPolicyManagerAshNotifierFactory::GetInstance();
}

std::string_view UserCloudPolicyManagerAsh::name() const {
  return "UserCloudPolicyManagerAsh";
}

}  // namespace policy