| Index: chrome/browser/policy/cloud_policy_controller.cc
|
| diff --git a/chrome/browser/policy/cloud_policy_controller.cc b/chrome/browser/policy/cloud_policy_controller.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7ace4061ce7f9bc715157a11fd8e13f551b7e5ee
|
| --- /dev/null
|
| +++ b/chrome/browser/policy/cloud_policy_controller.cc
|
| @@ -0,0 +1,309 @@
|
| +// Copyright (c) 2011 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/policy/cloud_policy_controller.h"
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/message_loop.h"
|
| +#include "base/rand_util.h"
|
| +#include "base/string_util.h"
|
| +#include "chrome/browser/policy/cloud_policy_cache.h"
|
| +#include "chrome/browser/policy/cloud_policy_subsystem.h"
|
| +#include "chrome/browser/policy/device_management_backend.h"
|
| +#include "chrome/browser/policy/proto/device_management_constants.h"
|
| +
|
| +// Domain names that are known not to be managed.
|
| +// We don't register the device when such a user logs in.
|
| +static const char* kNonManagedDomains[] = {
|
| + "@googlemail.com",
|
| + "@gmail.com"
|
| +};
|
| +
|
| +// Checks the domain part of the given username against the list of known
|
| +// non-managed domain names. Returns false if |username| is empty or
|
| +// in a domain known not to be managed.
|
| +static bool CanBeInManagedDomain(const std::string& username) {
|
| + if (username.empty()) {
|
| + // This means incognito user in case of ChromiumOS and
|
| + // no logged-in user in case of Chromium (SigninService).
|
| + return false;
|
| + }
|
| + for (size_t i = 0; i < arraysize(kNonManagedDomains); i++) {
|
| + if (EndsWith(username, kNonManagedDomains[i], true)) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +namespace policy {
|
| +
|
| +namespace em = enterprise_management;
|
| +
|
| +// The maximum ratio in percent of the policy refresh rate we use for adjusting
|
| +// the policy refresh time instant. The rationale is to avoid load spikes from
|
| +// many devices that were set up in sync for some reason.
|
| +static const int kPolicyRefreshDeviationFactorPercent = 10;
|
| +// Maximum deviation we are willing to accept.
|
| +static const int64 kPolicyRefreshDeviationMaxInMilliseconds = 30 * 60 * 1000;
|
| +
|
| +// These are the base values for delays before retrying after an error. They
|
| +// will be doubled each time they are used.
|
| +static const int64 kPolicyRefreshErrorDelayInMilliseconds =
|
| + 3 * 1000; // 3 seconds
|
| +
|
| +// Default value for the policy refresh rate.
|
| +static const int kPolicyRefreshRateInMilliseconds =
|
| + 3 * 60 * 60 * 1000; // 3 hours.
|
| +
|
| +CloudPolicyController::CloudPolicyController(
|
| + CloudPolicyCache* cache,
|
| + DeviceManagementBackend* backend,
|
| + DeviceTokenFetcher* token_fetcher,
|
| + CloudPolicyIdentityStrategy* identity_strategy)
|
| + : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
|
| + Initialize(cache,
|
| + backend,
|
| + token_fetcher,
|
| + identity_strategy,
|
| + kPolicyRefreshRateInMilliseconds,
|
| + kPolicyRefreshDeviationFactorPercent,
|
| + kPolicyRefreshDeviationMaxInMilliseconds,
|
| + kPolicyRefreshErrorDelayInMilliseconds);
|
| +}
|
| +
|
| +CloudPolicyController::~CloudPolicyController() {
|
| + token_fetcher_->RemoveObserver(this);
|
| + identity_strategy_->RemoveObserver(this);
|
| + CancelDelayedWork();
|
| +}
|
| +
|
| +void CloudPolicyController::SetRefreshRate(int64 refresh_rate_milliseconds) {
|
| + policy_refresh_rate_ms_ = refresh_rate_milliseconds;
|
| +
|
| + // Reschedule the refresh task if necessary.
|
| + if (state_ == STATE_POLICY_VALID)
|
| + SetState(STATE_POLICY_VALID);
|
| +}
|
| +
|
| +void CloudPolicyController::HandlePolicyResponse(
|
| + const em::DevicePolicyResponse& response) {
|
| + if (state_ == STATE_TOKEN_UNAVAILABLE)
|
| + return;
|
| +
|
| + cache_->SetDevicePolicy(response);
|
| + SetState(STATE_POLICY_VALID);
|
| +}
|
| +
|
| +void CloudPolicyController::HandleCloudPolicyResponse(
|
| + const em::CloudPolicyResponse& response) {
|
| + if (state_ == STATE_TOKEN_UNAVAILABLE)
|
| + return;
|
| +
|
| + cache_->SetPolicy(response);
|
| + SetState(STATE_POLICY_VALID);
|
| +}
|
| +
|
| +void CloudPolicyController::OnError(DeviceManagementBackend::ErrorCode code) {
|
| + if (state_ == STATE_TOKEN_UNAVAILABLE)
|
| + return;
|
| +
|
| + if (code == DeviceManagementBackend::kErrorServiceDeviceNotFound ||
|
| + code == DeviceManagementBackend::kErrorServiceManagementTokenInvalid) {
|
| + LOG(WARNING) << "The device token was either invalid or unknown to the "
|
| + << "device manager, re-registering device.";
|
| + SetState(STATE_TOKEN_UNAVAILABLE);
|
| + } else if (code ==
|
| + DeviceManagementBackend::kErrorServiceManagementNotSupported) {
|
| + VLOG(1) << "The device is no longer managed, resetting device token.";
|
| + SetState(STATE_TOKEN_UNAVAILABLE);
|
| + } else if (!fallback_to_old_protocol_ &&
|
| + code == DeviceManagementBackend::kErrorRequestInvalid) {
|
| + LOG(WARNING) << "Device manager doesn't understand new protocol, falling "
|
| + << "back to old request.";
|
| + fallback_to_old_protocol_ = true;
|
| + SetState(STATE_TOKEN_VALID); // Triggers SendPolicyRequest() immediately.
|
| + } else {
|
| + LOG(WARNING) << "Could not provide policy from the device manager (error = "
|
| + << code << "), will retry in "
|
| + << (effective_policy_refresh_error_delay_ms_ / 1000)
|
| + << " seconds.";
|
| + SetState(STATE_POLICY_ERROR);
|
| + }
|
| +}
|
| +
|
| +void CloudPolicyController::OnDeviceTokenAvailable() {
|
| + identity_strategy_->OnDeviceTokenAvailable(token_fetcher_->GetDeviceToken());
|
| +}
|
| +
|
| +void CloudPolicyController::OnDeviceTokenChanged() {
|
| + if (identity_strategy_->GetDeviceToken().empty())
|
| + SetState(STATE_TOKEN_UNAVAILABLE);
|
| + else
|
| + SetState(STATE_TOKEN_VALID);
|
| +}
|
| +
|
| +void CloudPolicyController::OnCredentialsChanged() {
|
| + SetState(STATE_TOKEN_UNAVAILABLE);
|
| +}
|
| +
|
| +CloudPolicyController::CloudPolicyController(
|
| + CloudPolicyCache* cache,
|
| + DeviceManagementBackend* backend,
|
| + DeviceTokenFetcher* token_fetcher,
|
| + CloudPolicyIdentityStrategy* identity_strategy,
|
| + int64 policy_refresh_rate_ms,
|
| + int policy_refresh_deviation_factor_percent,
|
| + int64 policy_refresh_deviation_max_ms,
|
| + int64 policy_refresh_error_delay_ms)
|
| + : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
|
| + Initialize(cache,
|
| + backend,
|
| + token_fetcher,
|
| + identity_strategy,
|
| + policy_refresh_rate_ms,
|
| + policy_refresh_deviation_factor_percent,
|
| + policy_refresh_deviation_max_ms,
|
| + policy_refresh_error_delay_ms);
|
| +}
|
| +
|
| +void CloudPolicyController::Initialize(
|
| + CloudPolicyCache* cache,
|
| + DeviceManagementBackend* backend,
|
| + DeviceTokenFetcher* token_fetcher,
|
| + CloudPolicyIdentityStrategy* identity_strategy,
|
| + int64 policy_refresh_rate_ms,
|
| + int policy_refresh_deviation_factor_percent,
|
| + int64 policy_refresh_deviation_max_ms,
|
| + int64 policy_refresh_error_delay_ms) {
|
| + DCHECK(cache);
|
| +
|
| + cache_ = cache;
|
| + backend_.reset(backend);
|
| + token_fetcher_ = token_fetcher;
|
| + identity_strategy_ = identity_strategy;
|
| + state_ = STATE_TOKEN_UNAVAILABLE;
|
| + fallback_to_old_protocol_ = false;
|
| + delayed_work_task_ = NULL;
|
| + policy_refresh_rate_ms_ = policy_refresh_rate_ms;
|
| + policy_refresh_deviation_factor_percent_ =
|
| + policy_refresh_deviation_factor_percent;
|
| + policy_refresh_deviation_max_ms_ = policy_refresh_deviation_max_ms;
|
| + policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms;
|
| + effective_policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms;
|
| +
|
| + token_fetcher_->AddObserver(this);
|
| + identity_strategy_->AddObserver(this);
|
| + if (!identity_strategy_->GetDeviceToken().empty())
|
| + SetState(STATE_TOKEN_VALID);
|
| + else
|
| + SetState(STATE_TOKEN_UNAVAILABLE);
|
| +}
|
| +
|
| +void CloudPolicyController::FetchToken() {
|
| + std::string username;
|
| + std::string auth_token;
|
| + std::string device_id = identity_strategy_->GetDeviceID();
|
| + if (identity_strategy_->GetCredentials(&username, &auth_token) &&
|
| + CanBeInManagedDomain(username)) {
|
| + token_fetcher_->FetchToken(auth_token, device_id);
|
| + }
|
| +}
|
| +
|
| +void CloudPolicyController::SendPolicyRequest() {
|
| + DCHECK(!identity_strategy_->GetDeviceToken().empty());
|
| + if (!fallback_to_old_protocol_) {
|
| + em::CloudPolicyRequest policy_request;
|
| + policy_request.set_policy_scope(kChromePolicyScope);
|
| + backend_->ProcessCloudPolicyRequest(identity_strategy_->GetDeviceToken(),
|
| + identity_strategy_->GetDeviceID(),
|
| + policy_request, this);
|
| + } else {
|
| + em::DevicePolicyRequest policy_request;
|
| + policy_request.set_policy_scope(kChromePolicyScope);
|
| + em::DevicePolicySettingRequest* setting =
|
| + policy_request.add_setting_request();
|
| + setting->set_key(kChromeDevicePolicySettingKey);
|
| + setting->set_watermark("");
|
| + backend_->ProcessPolicyRequest(identity_strategy_->GetDeviceToken(),
|
| + identity_strategy_->GetDeviceID(),
|
| + policy_request, this);
|
| + }
|
| +}
|
| +
|
| +void CloudPolicyController::DoDelayedWork() {
|
| + DCHECK(delayed_work_task_);
|
| + delayed_work_task_ = NULL;
|
| +
|
| + switch (state_) {
|
| + case STATE_TOKEN_UNAVAILABLE:
|
| + FetchToken();
|
| + return;
|
| + case STATE_TOKEN_VALID:
|
| + case STATE_POLICY_VALID:
|
| + case STATE_POLICY_ERROR:
|
| + SendPolicyRequest();
|
| + return;
|
| + }
|
| +
|
| + NOTREACHED() << "Unhandled state" << state_;
|
| +}
|
| +
|
| +void CloudPolicyController::CancelDelayedWork() {
|
| + if (delayed_work_task_) {
|
| + delayed_work_task_->Cancel();
|
| + delayed_work_task_ = NULL;
|
| + }
|
| +}
|
| +
|
| +void CloudPolicyController::SetState(
|
| + CloudPolicyController::ControllerState new_state) {
|
| + state_ = new_state;
|
| +
|
| + base::Time now(base::Time::NowFromSystemTime());
|
| + base::Time refresh_at;
|
| + base::Time last_refresh(cache_->last_policy_refresh_time());
|
| + if (last_refresh.is_null())
|
| + last_refresh = now;
|
| +
|
| + // Determine when to take the next step.
|
| + switch (state_) {
|
| + case STATE_TOKEN_UNAVAILABLE:
|
| + case STATE_TOKEN_VALID:
|
| + refresh_at = now;
|
| + break;
|
| + case STATE_POLICY_VALID:
|
| + effective_policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms_;
|
| + refresh_at =
|
| + last_refresh + base::TimeDelta::FromMilliseconds(GetRefreshDelay());
|
| + break;
|
| + case STATE_POLICY_ERROR:
|
| + refresh_at = now + base::TimeDelta::FromMilliseconds(
|
| + effective_policy_refresh_error_delay_ms_);
|
| + effective_policy_refresh_error_delay_ms_ *= 2;
|
| + if (effective_policy_refresh_error_delay_ms_ > policy_refresh_rate_ms_)
|
| + effective_policy_refresh_error_delay_ms_ = policy_refresh_rate_ms_;
|
| + break;
|
| + }
|
| +
|
| + // Update the delayed work task.
|
| + CancelDelayedWork();
|
| + if (!refresh_at.is_null()) {
|
| + int64 delay = std::max<int64>((refresh_at - now).InMilliseconds(), 0);
|
| + delayed_work_task_ = method_factory_.NewRunnableMethod(
|
| + &CloudPolicyController::DoDelayedWork);
|
| + MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_work_task_,
|
| + delay);
|
| + }
|
| +}
|
| +
|
| +int64 CloudPolicyController::GetRefreshDelay() {
|
| + int64 deviation = (policy_refresh_deviation_factor_percent_ *
|
| + policy_refresh_rate_ms_) / 100;
|
| + deviation = std::min(deviation, policy_refresh_deviation_max_ms_);
|
| + return policy_refresh_rate_ms_ - base::RandGenerator(deviation + 1);
|
| +}
|
| +
|
| +} // namespace policy
|
|
|