blob: 42163a6cb78266a5c87db74767d4bd544992d7ad [file] [log] [blame]
// Copyright 2021 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/policy/cbcm_invalidations_initializer.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/device_identity/device_oauth2_token_service.h"
#include "chrome/browser/device_identity/device_oauth2_token_service_factory.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/policy/device_account_initializer.h"
#include "google_apis/gaia/gaia_constants.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace policy {
// A helper class to make the appropriate calls into the device account
// initializer and manage the ChromeBrowserCloudManagementRegistrar callback's
// lifetime.
class CBCMInvalidationsInitializer::MachineLevelDeviceAccountInitializerHelper
: public DeviceAccountInitializer::Delegate {
public:
using Callback = base::OnceCallback<void(bool)>;
// |policy_client| should be registered and outlive this object.
MachineLevelDeviceAccountInitializerHelper(
const std::string& service_account_email,
policy::CloudPolicyClient* policy_client,
Callback callback,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: service_account_email_(service_account_email),
policy_client_(policy_client),
callback_(std::move(callback)),
url_loader_factory_(url_loader_factory) {
DCHECK(url_loader_factory_);
device_account_initializer_ =
std::make_unique<DeviceAccountInitializer>(policy_client_, this);
device_account_initializer_->FetchToken();
}
MachineLevelDeviceAccountInitializerHelper& operator=(
MachineLevelDeviceAccountInitializerHelper&) = delete;
MachineLevelDeviceAccountInitializerHelper(
MachineLevelDeviceAccountInitializerHelper&) = delete;
MachineLevelDeviceAccountInitializerHelper(
MachineLevelDeviceAccountInitializerHelper&&) = delete;
~MachineLevelDeviceAccountInitializerHelper() override = default;
// DeviceAccountInitializer::Delegate:
void OnDeviceAccountTokenFetched(bool empty_token) override {
if (empty_token) {
// Not being able to obtain a token isn't a showstopper for machine
// level policies: the browser will fallback to fetching policies on a
// regular schedule and won't support remote commands. Getting a refresh
// token will be reattempted on the next successful policy fetch.
std::move(callback_).Run(false);
return;
}
device_account_initializer_->StoreToken();
}
void OnDeviceAccountTokenStored() override {
// When the token is stored, the account init procedure is complete and
// it's now time to save the associated email address.
DeviceOAuth2TokenServiceFactory::Get()->SetServiceAccountEmail(
service_account_email_);
std::move(callback_).Run(true);
}
void OnDeviceAccountTokenFetchError(
absl::optional<DeviceManagementStatus> /*dm_status*/) override {
std::move(callback_).Run(false);
}
void OnDeviceAccountTokenStoreError() override {
std::move(callback_).Run(false);
}
void OnDeviceAccountClientError(DeviceManagementStatus status) override {
std::move(callback_).Run(false);
}
enterprise_management::DeviceServiceApiAccessRequest::DeviceType
GetRobotAuthCodeDeviceType() override {
return enterprise_management::DeviceServiceApiAccessRequest::CHROME_BROWSER;
}
std::set<std::string> GetRobotOAuthScopes() override {
return {
GaiaConstants::kGoogleUserInfoEmail,
GaiaConstants::kFCMOAuthScope,
};
}
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
override {
return url_loader_factory_;
}
std::string service_account_email_;
raw_ptr<policy::CloudPolicyClient> policy_client_;
std::unique_ptr<DeviceAccountInitializer> device_account_initializer_;
Callback callback_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
};
CBCMInvalidationsInitializer::CBCMInvalidationsInitializer(Delegate* delegate)
: delegate_(delegate) {}
CBCMInvalidationsInitializer::~CBCMInvalidationsInitializer() = default;
void CBCMInvalidationsInitializer::OnServiceAccountSet(
CloudPolicyClient* client,
const std::string& account_email) {
// If there's no invalidations service active yet, now's the time to start it.
// It will be notified when the service account for is ready to be used.
if (!delegate_->IsInvalidationsServiceStarted())
delegate_->StartInvalidations();
// If there's no refresh token when a policy has a service account, or the
// service account in the policy doesn't match the one in the token service,
// the service account has to be initialized to the one in the policy.
if (!DeviceOAuth2TokenServiceFactory::Get()->RefreshTokenIsAvailable() ||
DeviceOAuth2TokenServiceFactory::Get()->GetRobotAccountId() !=
CoreAccountId::FromRobotEmail(account_email)) {
// Initialize the device service account and fetch auth codes to exchange
// for a refresh token. Creating this object starts that process and the
// callback will be called from it whether it succeeds or not.
account_initializer_helper_ =
std::make_unique<MachineLevelDeviceAccountInitializerHelper>(
account_email, client,
base::BindOnce(&CBCMInvalidationsInitializer::AccountInitCallback,
base::Unretained(this), account_email),
delegate_->GetURLLoaderFactory()
? delegate_->GetURLLoaderFactory()
: g_browser_process->system_network_context_manager()
->GetSharedURLLoaderFactory());
}
}
void CBCMInvalidationsInitializer::AccountInitCallback(
const std::string& account_email,
bool success) {
account_initializer_helper_.reset();
if (!success) {
DVLOG(1)
<< "There was an error initializing the service account with email: "
<< account_email;
}
}
} // namespace policy