blob: 29b4391621bcdd1a61f025b14ba4aa6ea3c1bd71 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/metrics/sparse_histogram.h"
#include "base/sequenced_task_runner.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.h"
#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h"
#include "chrome/browser/chromeos/policy/wildcard_login_checker.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "components/policy/core/common/cloud/cloud_external_data_manager.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/cloud/system_policy_request_context.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "content/public/common/content_client.h"
#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
namespace em = enterprise_management;
namespace policy {
namespace {
// UMA histogram names.
const char kUMADelayInitialization[] =
"Enterprise.UserPolicyChromeOS.DelayInitialization";
const char kUMAInitialFetchClientError[] =
"Enterprise.UserPolicyChromeOS.InitialFetch.ClientError";
const char kUMAInitialFetchDelayClientRegister[] =
"Enterprise.UserPolicyChromeOS.InitialFetch.DelayClientRegister";
const char kUMAInitialFetchDelayOAuth2Token[] =
"Enterprise.UserPolicyChromeOS.InitialFetch.DelayOAuth2Token";
const char kUMAInitialFetchDelayPolicyFetch[] =
"Enterprise.UserPolicyChromeOS.InitialFetch.DelayPolicyFetch";
const char kUMAInitialFetchDelayTotal[] =
"Enterprise.UserPolicyChromeOS.InitialFetch.DelayTotal";
const char kUMAInitialFetchOAuth2Error[] =
"Enterprise.UserPolicyChromeOS.InitialFetch.OAuth2Error";
const char kUMAInitialFetchOAuth2NetworkError[] =
"Enterprise.UserPolicyChromeOS.InitialFetch.OAuth2NetworkError";
void OnWildcardCheckCompleted(const std::string& username, bool result) {
if (!result) {
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.
chromeos::UserManager::Get()->RemoveUserFromList(username);
chrome::AttemptUserExit();
}
}
} // namespace
UserCloudPolicyManagerChromeOS::UserCloudPolicyManagerChromeOS(
scoped_ptr<CloudPolicyStore> store,
scoped_ptr<CloudExternalDataManager> external_data_manager,
const base::FilePath& component_policy_cache_path,
bool wait_for_policy_fetch,
base::TimeDelta initial_policy_fetch_timeout,
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
const scoped_refptr<base::SequencedTaskRunner>& file_task_runner,
const scoped_refptr<base::SequencedTaskRunner>& io_task_runner)
: CloudPolicyManager(
PolicyNamespaceKey(dm_protocol::kChromeUserPolicyType, std::string()),
store.get(),
task_runner,
file_task_runner,
io_task_runner),
store_(store.Pass()),
external_data_manager_(external_data_manager.Pass()),
component_policy_cache_path_(component_policy_cache_path),
wait_for_policy_fetch_(wait_for_policy_fetch),
policy_fetch_timeout_(false, false) {
time_init_started_ = base::Time::Now();
if (wait_for_policy_fetch_) {
policy_fetch_timeout_.Start(
FROM_HERE,
initial_policy_fetch_timeout,
base::Bind(&UserCloudPolicyManagerChromeOS::CancelWaitForPolicyFetch,
base::Unretained(this)));
}
}
UserCloudPolicyManagerChromeOS::~UserCloudPolicyManagerChromeOS() {}
void UserCloudPolicyManagerChromeOS::Connect(
PrefService* local_state,
DeviceManagementService* device_management_service,
scoped_refptr<net::URLRequestContextGetter> system_request_context,
UserAffiliation user_affiliation) {
DCHECK(device_management_service);
DCHECK(local_state);
local_state_ = local_state;
scoped_refptr<net::URLRequestContextGetter> request_context;
if (system_request_context) {
// |system_request_context| can be null for tests.
// Use the system request 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).
// TODO(atwilson): Change this to use a UserPolicyRequestContext once
// Connect() is called after profile initialization. http://crbug.com/323591
request_context = new SystemPolicyRequestContext(
system_request_context,
content::GetUserAgent(GURL(
device_management_service->GetServerUrl())));
}
scoped_ptr<CloudPolicyClient> cloud_policy_client(
new CloudPolicyClient(std::string(), std::string(), user_affiliation,
NULL, device_management_service,
request_context));
core()->Connect(cloud_policy_client.Pass());
client()->AddObserver(this);
external_data_manager_->Connect(request_context);
CreateComponentCloudPolicyService(component_policy_cache_path_,
request_context);
// Determine the next step after the CloudPolicyService initializes.
if (service()->IsInitializationComplete()) {
OnInitializationCompleted(service());
} else {
service()->AddObserver(this);
}
}
void UserCloudPolicyManagerChromeOS::OnAccessTokenAvailable(
const std::string& access_token) {
access_token_ = access_token;
if (!wildcard_username_.empty()) {
wildcard_login_checker_.reset(new WildcardLoginChecker());
wildcard_login_checker_->StartWithAccessToken(
access_token,
base::Bind(&OnWildcardCheckCompleted, wildcard_username_));
}
if (service() && service()->IsInitializationComplete() &&
client() && !client()->is_registered()) {
OnOAuth2PolicyTokenFetched(
access_token, GoogleServiceAuthError(GoogleServiceAuthError::NONE));
}
}
bool UserCloudPolicyManagerChromeOS::IsClientRegistered() const {
return client() && client()->is_registered();
}
void UserCloudPolicyManagerChromeOS::EnableWildcardLoginCheck(
const std::string& username) {
DCHECK(access_token_.empty());
wildcard_username_ = username;
}
void UserCloudPolicyManagerChromeOS::Shutdown() {
if (client())
client()->RemoveObserver(this);
if (service())
service()->RemoveObserver(this);
token_fetcher_.reset();
external_data_manager_->Disconnect();
CloudPolicyManager::Shutdown();
}
bool UserCloudPolicyManagerChromeOS::IsInitializationComplete(
PolicyDomain domain) const {
if (!CloudPolicyManager::IsInitializationComplete(domain))
return false;
if (domain == POLICY_DOMAIN_CHROME)
return !wait_for_policy_fetch_;
return true;
}
void UserCloudPolicyManagerChromeOS::OnInitializationCompleted(
CloudPolicyService* cloud_policy_service) {
DCHECK_EQ(service(), cloud_policy_service);
cloud_policy_service->RemoveObserver(this);
time_init_completed_ = base::Time::Now();
UMA_HISTOGRAM_TIMES(kUMADelayInitialization,
time_init_completed_ - time_init_started_);
// If the CloudPolicyClient isn't registered at this stage then it needs an
// OAuth token for the initial registration.
//
// If |wait_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 |wait_for_policy_fetch_| is false 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 (wait_for_policy_fetch_) {
FetchPolicyOAuthTokenUsingSigninProfile();
} else if (!access_token_.empty()) {
OnAccessTokenAvailable(access_token_);
}
}
if (!wait_for_policy_fetch_) {
// 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.
StartRefreshSchedulerIfReady();
}
}
void UserCloudPolicyManagerChromeOS::OnPolicyFetched(
CloudPolicyClient* client) {
// No action required. If we're blocked on a policy fetch, we'll learn about
// completion of it through OnInitialPolicyFetchComplete().
}
void UserCloudPolicyManagerChromeOS::OnRegistrationStateChanged(
CloudPolicyClient* cloud_policy_client) {
DCHECK_EQ(client(), cloud_policy_client);
if (wait_for_policy_fetch_) {
time_client_registered_ = base::Time::Now();
if (!time_token_available_.is_null()) {
UMA_HISTOGRAM_TIMES(kUMAInitialFetchDelayClientRegister,
time_client_registered_ - time_token_available_);
}
// If we're blocked on the policy fetch, now is a good time to issue it.
if (client()->is_registered()) {
service()->RefreshPolicy(
base::Bind(
&UserCloudPolicyManagerChromeOS::OnInitialPolicyFetchComplete,
base::Unretained(this)));
} else {
// If the client has switched to not registered, we bail out as this
// indicates the cloud policy setup flow has been aborted.
CancelWaitForPolicyFetch();
}
}
}
void UserCloudPolicyManagerChromeOS::OnClientError(
CloudPolicyClient* cloud_policy_client) {
DCHECK_EQ(client(), cloud_policy_client);
if (wait_for_policy_fetch_) {
UMA_HISTOGRAM_SPARSE_SLOWLY(kUMAInitialFetchClientError,
cloud_policy_client->status());
}
CancelWaitForPolicyFetch();
}
void UserCloudPolicyManagerChromeOS::OnComponentCloudPolicyUpdated() {
CloudPolicyManager::OnComponentCloudPolicyUpdated();
StartRefreshSchedulerIfReady();
}
void UserCloudPolicyManagerChromeOS::FetchPolicyOAuthTokenUsingSigninProfile() {
scoped_refptr<net::URLRequestContextGetter> signin_context;
Profile* signin_profile = chromeos::ProfileHelper::GetSigninProfile();
if (signin_profile)
signin_context = signin_profile->GetRequestContext();
if (!signin_context.get()) {
LOG(ERROR) << "No signin Profile for policy oauth token fetch!";
OnOAuth2PolicyTokenFetched(
std::string(), GoogleServiceAuthError(GoogleServiceAuthError::NONE));
return;
}
token_fetcher_.reset(new PolicyOAuth2TokenFetcher(
signin_context.get(),
g_browser_process->system_request_context(),
base::Bind(&UserCloudPolicyManagerChromeOS::OnOAuth2PolicyTokenFetched,
base::Unretained(this))));
token_fetcher_->Start();
}
void UserCloudPolicyManagerChromeOS::OnOAuth2PolicyTokenFetched(
const std::string& policy_token,
const GoogleServiceAuthError& error) {
DCHECK(!client()->is_registered());
time_token_available_ = base::Time::Now();
if (wait_for_policy_fetch_) {
UMA_HISTOGRAM_TIMES(kUMAInitialFetchDelayOAuth2Token,
time_token_available_ - time_init_completed_);
}
if (error.state() == GoogleServiceAuthError::NONE) {
// Start client registration. Either OnRegistrationStateChanged() or
// OnClientError() will be called back.
client()->Register(em::DeviceRegisterRequest::USER,
policy_token, std::string(), false, std::string());
} else {
// Failed to get a token, stop waiting and use an empty policy.
CancelWaitForPolicyFetch();
UMA_HISTOGRAM_ENUMERATION(kUMAInitialFetchOAuth2Error,
error.state(),
GoogleServiceAuthError::NUM_STATES);
if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) {
UMA_HISTOGRAM_SPARSE_SLOWLY(kUMAInitialFetchOAuth2NetworkError,
error.network_error());
}
}
token_fetcher_.reset();
}
void UserCloudPolicyManagerChromeOS::OnInitialPolicyFetchComplete(
bool success) {
const base::Time now = base::Time::Now();
UMA_HISTOGRAM_TIMES(kUMAInitialFetchDelayPolicyFetch,
now - time_client_registered_);
UMA_HISTOGRAM_TIMES(kUMAInitialFetchDelayTotal, now - time_init_started_);
CancelWaitForPolicyFetch();
}
void UserCloudPolicyManagerChromeOS::CancelWaitForPolicyFetch() {
if (!wait_for_policy_fetch_)
return;
wait_for_policy_fetch_ = false;
CheckAndPublishPolicy();
// Now that |wait_for_policy_fetch_| is guaranteed to be false, the scheduler
// can be started.
StartRefreshSchedulerIfReady();
}
void UserCloudPolicyManagerChromeOS::StartRefreshSchedulerIfReady() {
if (core()->refresh_scheduler())
return; // Already started.
if (wait_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;
}
core()->StartRefreshScheduler();
core()->TrackRefreshDelayPref(local_state_,
policy_prefs::kUserPolicyRefreshRate);
}
} // namespace policy