summaryrefslogtreecommitdiffstats
path: root/chrome/browser/policy/cloud_policy_controller.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/policy/cloud_policy_controller.cc')
-rw-r--r--chrome/browser/policy/cloud_policy_controller.cc309
1 files changed, 309 insertions, 0 deletions
diff --git a/chrome/browser/policy/cloud_policy_controller.cc b/chrome/browser/policy/cloud_policy_controller.cc
new file mode 100644
index 0000000..7ace406
--- /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