Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4991)

Unified Diff: chrome/browser/chromeos/device_settings_provider.cc

Issue 8727037: Signed settings refactoring: Proper caching and more tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed comments. Fixed small bugs. Rebased to ToT. Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/device_settings_provider.cc
diff --git a/chrome/browser/chromeos/device_settings_provider.cc b/chrome/browser/chromeos/device_settings_provider.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d04c4fb5c851fe4ba69815ad4cb26cdbe3fe9a9c
--- /dev/null
+++ b/chrome/browser/chromeos/device_settings_provider.cc
@@ -0,0 +1,523 @@
+// 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/chromeos/device_settings_provider.h"
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/hash_tables.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/cros/cros_library.h"
+#include "chrome/browser/chromeos/cros/network_library.h"
+#include "chrome/browser/chromeos/cros_settings.h"
+#include "chrome/browser/chromeos/cros_settings_names.h"
+#include "chrome/browser/chromeos/login/ownership_service.h"
+#include "chrome/browser/chromeos/login/signed_settings_cache.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/policy/proto/chrome_device_policy.pb.h"
+#include "chrome/browser/policy/proto/device_management_backend.pb.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/prefs/pref_value_map.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/ui/options/options_util.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/installer/util/google_update_settings.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+
+using content::BrowserThread;
+using google::protobuf::RepeatedPtrField;
+
+namespace chromeos {
+
+namespace {
+
+const char* kBooleanSettings[] = {
+ kAccountsPrefAllowNewUser,
+ kAccountsPrefAllowGuest,
+ kAccountsPrefShowUserNamesOnSignIn,
+ kSignedDataRoamingEnabled,
+ kStatsReportingPref
+};
+
+const char* kStringSettings[] = {
+ kDeviceOwner,
+ kReleaseChannel,
+ kSettingProxyEverywhere
+};
+
+const char* kListSettings[] = {
+ kAccountsPrefUsers
+};
+
+// Upper bound for number of retries to fetch a signed setting.
+static const int kNumRetriesLimit = 9;
+
+bool IsControlledBooleanSetting(const std::string& pref_path) {
+ const char** end = kBooleanSettings + arraysize(kBooleanSettings);
+ return std::find(kBooleanSettings, end, pref_path) != end;
+}
+
+bool IsControlledStringSetting(const std::string& pref_path) {
+ const char** end = kStringSettings + arraysize(kStringSettings);
+ return std::find(kStringSettings, end, pref_path) != end;
+}
+
+bool IsControlledListSetting(const std::string& pref_path) {
+ const char** end = kListSettings + arraysize(kListSettings);
+ return std::find(kListSettings, end, pref_path) != end;
+}
+
+bool IsControlledSetting(const std::string& pref_path) {
+ return (IsControlledBooleanSetting(pref_path) ||
+ IsControlledStringSetting(pref_path) ||
+ IsControlledListSetting(pref_path));
+}
+
+bool HasOldMetricsFile() {
+ // TODO(pastarmovj): Remove this once migration is not needed anymore.
+ // If the value is not set we should try to migrate legacy consent file.
+ // Loading consent file state causes us to do blocking IO on UI thread.
+ // Temporarily allow it until we fix http://crbug.com/62626
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ return GoogleUpdateSettings::GetCollectStatsConsent();
+}
+
+} // namespace
+
+DeviceSettingsProvider::DeviceSettingsProvider()
+ : ownership_status_(OwnershipService::GetSharedInstance()->GetStatus(true)),
+ migration_helper_(new SignedSettingsMigrationHelper()),
+ retries_left_(kNumRetriesLimit),
+ trusted_(false) {
+ // Register for notification when ownership is taken so that we can update
+ // the |ownership_status_| and reload if needed.
+ registrar_.Add(this, chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED,
+ content::NotificationService::AllSources());
+ // Make sure we have at least the cache data immediately.
+ RetrieveCachedData();
+ // Start prefetching preferences.
+ Reload();
+}
+
+DeviceSettingsProvider::~DeviceSettingsProvider() {
+}
+
+void DeviceSettingsProvider::Reload() {
+ // While fetching we can't trust the cache anymore.
+ trusted_ = false;
+ if (ownership_status_ == OwnershipService::OWNERSHIP_NONE) {
+ RetrieveCachedData();
+ } else {
+ // Retrieve the real data.
+ SignedSettingsHelper::Get()->StartRetrievePolicyOp(
+ base::Bind(&DeviceSettingsProvider::OnRetrievePolicyCompleted,
+ base::Unretained(this)));
+ }
+}
+
+void DeviceSettingsProvider::DoSet(const std::string& path,
+ const base::Value& in_value) {
+ if (!UserManager::Get()->current_user_is_owner() &&
+ ownership_status_ != OwnershipService::OWNERSHIP_NONE) {
+ LOG(WARNING) << "Changing settings from non-owner, setting=" << path;
+
+ // Revert UI change.
+ CrosSettings::Get()->FireObservers(path.c_str());
+ return;
+ }
+
+ if (IsControlledSetting(path))
+ SetInPolicy(path, in_value);
+ else
+ NOTREACHED() << "Try to set unhandled cros setting " << path;
+}
+
+void DeviceSettingsProvider::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (type == chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED &&
+ UserManager::Get()->current_user_is_owner()) {
+ // Reload the initial policy blob, apply settings from temp storage,
+ // and write back the blob.
+ ownership_status_ = OwnershipService::OWNERSHIP_TAKEN;
+ Reload();
+ }
+}
+
+const em::PolicyData DeviceSettingsProvider::policy() const {
+ return policy_;
+}
+
+void DeviceSettingsProvider::RetrieveCachedData() {
+ // If there is no owner yet, this function will pull the policy cache from the
+ // temp storage and use that instead.
+ em::PolicyData policy;
+ if (!signed_settings_cache::Retrieve(&policy,
+ g_browser_process->local_state())) {
+ VLOG(1) << "Can't retrieve temp store possibly not created yet.";
+ // Prepare empty data for the case we don't have temp cache yet.
+ policy.set_policy_type(kDevicePolicyType);
+ em::ChromeDeviceSettingsProto pol;
+ policy.set_policy_value(pol.SerializeAsString());
+ }
+
+ policy_ = policy;
+ UpdateValuesCache();
+}
+
+void DeviceSettingsProvider::SetInPolicy(const std::string& prop,
+ const base::Value& value) {
+ if (prop == kDeviceOwner) {
+ // Just store it in the memory cache without trusted checks or persisting.
+ std::string owner;
+ if (value.GetAsString(&owner)) {
+ policy_.set_username(owner);
+ values_cache_.SetValue(prop, value.DeepCopy());
+ CrosSettings::Get()->FireObservers(prop.c_str());
+ // We can't trust this value anymore until we reload the real username.
+ trusted_ = false;
+ } else {
+ NOTREACHED();
+ }
+ return;
+ }
+
+ if (!RequestTrustedEntity()) {
+ // Otherwise we should first reload and apply on top of that.
+ SignedSettingsHelper::Get()->StartRetrievePolicyOp(
+ base::Bind(&DeviceSettingsProvider::FinishSetInPolicy,
+ base::Unretained(this),
+ prop, base::Owned(value.DeepCopy())));
+ return;
+ }
+
+ trusted_ = false;
+ em::PolicyData data = policy();
+ em::ChromeDeviceSettingsProto pol;
+ pol.ParseFromString(data.policy_value());
+ if (prop == kAccountsPrefAllowNewUser) {
+ em::AllowNewUsersProto* allow = pol.mutable_allow_new_users();
+ bool allow_value;
+ if (value.GetAsBoolean(&allow_value))
+ allow->set_allow_new_users(allow_value);
+ else
+ NOTREACHED();
+ } else if (prop == kAccountsPrefAllowGuest) {
+ em::GuestModeEnabledProto* guest = pol.mutable_guest_mode_enabled();
+ bool guest_value;
+ if (value.GetAsBoolean(&guest_value))
+ guest->set_guest_mode_enabled(guest_value);
+ else
+ NOTREACHED();
+ } else if (prop == kAccountsPrefShowUserNamesOnSignIn) {
+ em::ShowUserNamesOnSigninProto* show = pol.mutable_show_user_names();
+ bool show_value;
+ if (value.GetAsBoolean(&show_value))
+ show->set_show_user_names(show_value);
+ else
+ NOTREACHED();
+ } else if (prop == kSignedDataRoamingEnabled) {
+ em::DataRoamingEnabledProto* roam = pol.mutable_data_roaming_enabled();
+ bool roaming_value = false;
+ if (value.GetAsBoolean(&roaming_value))
+ roam->set_data_roaming_enabled(roaming_value);
+ else
+ NOTREACHED();
+ ApplyRoamingSetting(roaming_value);
+ } else if (prop == kSettingProxyEverywhere) {
+ // TODO(cmasone): NOTIMPLEMENTED() once http://crosbug.com/13052 is fixed.
+ std::string proxy_value;
+ if (value.GetAsString(&proxy_value)) {
+ bool success =
+ pol.mutable_device_proxy_settings()->ParseFromString(proxy_value);
+ DCHECK(success);
+ } else {
+ NOTREACHED();
+ }
+ } else if (prop == kReleaseChannel) {
+ em::ReleaseChannelProto* release_channel = pol.mutable_release_channel();
+ std::string channel_value;
+ if (value.GetAsString(&channel_value))
+ release_channel->set_release_channel(channel_value);
+ else
+ NOTREACHED();
+ } else if (prop == kStatsReportingPref) {
+ em::MetricsEnabledProto* metrics = pol.mutable_metrics_enabled();
+ bool metrics_value = false;
+ if (value.GetAsBoolean(&metrics_value))
+ metrics->set_metrics_enabled(metrics_value);
+ else
+ NOTREACHED();
+ ApplyMetricsSetting(false, metrics_value);
+ } else if (prop == kAccountsPrefUsers) {
+ em::UserWhitelistProto* whitelist_proto = pol.mutable_user_whitelist();
+ whitelist_proto->clear_user_whitelist();
+ const base::ListValue& users = static_cast<const base::ListValue&>(value);
+ for (base::ListValue::const_iterator i = users.begin();
+ i != users.end(); ++i) {
+ std::string email;
+ if ((*i)->GetAsString(&email))
+ whitelist_proto->add_user_whitelist(email.c_str());
+ }
+ } else {
+ NOTREACHED();
+ }
+ data.set_policy_value(pol.SerializeAsString());
+ // Set the cache to the updated value.
+ policy_ = data;
+ UpdateValuesCache();
+ CrosSettings::Get()->FireObservers(prop.c_str());
+
+ if (!signed_settings_cache::Store(data, g_browser_process->local_state()))
+ LOG(ERROR) << "Couldn't store to the temp storage.";
+
+ if (ownership_status_ == OwnershipService::OWNERSHIP_TAKEN) {
+ em::PolicyFetchResponse policy_envelope;
+ policy_envelope.set_policy_data(policy_.SerializeAsString());
+ SignedSettingsHelper::Get()->StartStorePolicyOp(
+ policy_envelope,
+ base::Bind(&DeviceSettingsProvider::OnStorePolicyCompleted,
+ base::Unretained(this)));
+ }
+}
+
+void DeviceSettingsProvider::FinishSetInPolicy(
+ const std::string& prop,
+ const base::Value* value,
+ SignedSettings::ReturnCode code,
+ const em::PolicyFetchResponse& policy) {
+ if (code != SignedSettings::SUCCESS) {
+ LOG(ERROR) << "Can't serialize to policy error code: " << code;
+ Reload();
+ return;
+ }
+ SetInPolicy(prop, *value);
+}
+
+void DeviceSettingsProvider::UpdateValuesCache() {
+ const em::PolicyData data = policy();
+ values_cache_.Clear();
+
+ if (data.has_username() && !data.has_request_token())
+ values_cache_.SetString(kDeviceOwner, data.username());
+
+ em::ChromeDeviceSettingsProto pol;
+ pol.ParseFromString(data.policy_value());
+
+ // For all our boolean settings the following is applicable:
+ // true is default permissive value and false is safe prohibitive value.
+ // Exception: kSignedDataRoamingEnabled which has default value of false.
+ if (pol.has_allow_new_users() &&
+ pol.allow_new_users().has_allow_new_users() &&
+ pol.allow_new_users().allow_new_users()) {
+ // New users allowed, user_whitelist() ignored.
+ values_cache_.SetBoolean(kAccountsPrefAllowNewUser, true);
+ } else if (!pol.has_user_whitelist()) {
+ // If we have the allow_new_users bool, and it is true, we honor that above.
+ // In all other cases (don't have it, have it and it is set to false, etc),
+ // We will honor the user_whitelist() if it is there and populated.
+ // Otherwise we default to allowing new users.
+ values_cache_.SetBoolean(kAccountsPrefAllowNewUser, true);
+ } else {
+ values_cache_.SetBoolean(kAccountsPrefAllowNewUser,
+ pol.user_whitelist().user_whitelist_size() == 0);
+ }
+
+ values_cache_.SetBoolean(
+ kAccountsPrefAllowGuest,
+ !pol.has_guest_mode_enabled() ||
+ !pol.guest_mode_enabled().has_guest_mode_enabled() ||
+ pol.guest_mode_enabled().guest_mode_enabled());
+
+ values_cache_.SetBoolean(
+ kAccountsPrefShowUserNamesOnSignIn,
+ !pol.has_show_user_names() ||
+ !pol.show_user_names().has_show_user_names() ||
+ pol.show_user_names().show_user_names());
+
+ values_cache_.SetBoolean(
+ kSignedDataRoamingEnabled,
+ pol.has_data_roaming_enabled() &&
+ pol.data_roaming_enabled().has_data_roaming_enabled() &&
+ pol.data_roaming_enabled().data_roaming_enabled());
+
+ // TODO(cmasone): NOTIMPLEMENTED() once http://crosbug.com/13052 is fixed.
+ std::string serialized;
+ if (pol.has_device_proxy_settings() &&
+ pol.device_proxy_settings().SerializeToString(&serialized)) {
+ values_cache_.SetString(kSettingProxyEverywhere, serialized);
+ }
+
+ if (!pol.has_release_channel() ||
+ !pol.release_channel().has_release_channel()) {
+ // Default to an invalid channel (will be ignored).
+ values_cache_.SetString(kReleaseChannel, "");
+ } else {
+ values_cache_.SetString(kReleaseChannel,
+ pol.release_channel().release_channel());
+ }
+
+ if (pol.has_metrics_enabled()) {
+ values_cache_.SetBoolean(kStatsReportingPref,
+ pol.metrics_enabled().metrics_enabled());
+ } else {
+ values_cache_.SetBoolean(kStatsReportingPref, HasOldMetricsFile());
+ }
+
+ base::ListValue* list = new base::ListValue();
+ const em::UserWhitelistProto& whitelist_proto = pol.user_whitelist();
+ const RepeatedPtrField<std::string>& whitelist =
+ whitelist_proto.user_whitelist();
+ for (RepeatedPtrField<std::string>::const_iterator it = whitelist.begin();
+ it != whitelist.end(); ++it) {
+ list->Append(base::Value::CreateStringValue(*it));
+ }
+ values_cache_.SetValue(kAccountsPrefUsers, list);
+}
+
+void DeviceSettingsProvider::ApplyMetricsSetting(bool use_file,
+ bool new_value) const {
+ // TODO(pastarmovj): Remove this once migration is not needed anymore.
+ // If the value is not set we should try to migrate legacy consent file.
+ if (use_file) {
+ new_value = HasOldMetricsFile();
+ // Make sure the values will get eventually written to the policy file.
+ migration_helper_->AddMigrationValue(
+ kStatsReportingPref, base::Value::CreateBooleanValue(new_value));
+ migration_helper_->MigrateValues();
+ LOG(INFO) << "No metrics policy set will revert to checking "
+ << "consent file which is "
+ << (new_value ? "on." : "off.");
+ }
+ VLOG(1) << "Metrics policy is being set to : " << new_value
+ << "(use file : " << use_file << ")";
+ // TODO(pastarmovj): Remove this once we don't need to regenerate the
+ // consent file for the GUID anymore.
+ OptionsUtil::ResolveMetricsReportingEnabled(new_value);
+}
+
+void DeviceSettingsProvider::ApplyRoamingSetting(bool new_value) const {
+ NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary();
+ const NetworkDevice* cellular = cros->FindCellularDevice();
+ if (cellular) {
+ bool device_value = cellular->data_roaming_allowed();
+ if (!device_value && cros->IsCellularAlwaysInRoaming()) {
+ // If operator requires roaming always enabled, ignore supplied value
+ // and set data roaming allowed in true always.
+ cros->SetCellularDataRoamingAllowed(true);
+ } else if (device_value != new_value) {
+ cros->SetCellularDataRoamingAllowed(new_value);
+ }
+ }
+}
+
+void DeviceSettingsProvider::ApplySideEffects() const {
+ const em::PolicyData data = policy();
+ em::ChromeDeviceSettingsProto pol;
+ pol.ParseFromString(data.policy_value());
+ // First migrate metrics settings as needed.
+ if (pol.has_metrics_enabled())
+ ApplyMetricsSetting(false, pol.metrics_enabled().metrics_enabled());
+ else
+ ApplyMetricsSetting(true, false);
+ // Next set the roaming setting as needed.
+ ApplyRoamingSetting(pol.has_data_roaming_enabled() ?
+ pol.data_roaming_enabled().data_roaming_enabled() : false);
+}
+
+const base::Value* DeviceSettingsProvider::Get(const std::string& path) const {
+ if (IsControlledSetting(path)) {
+ const base::Value* value;
+ if (values_cache_.GetValue(path, &value))
+ return value;
+ } else {
+ NOTREACHED() << "Trying to get non cros setting.";
+ }
+
+ return NULL;
+}
+
+bool DeviceSettingsProvider::GetTrusted(const std::string& path,
+ const base::Closure& callback) {
+ if (!IsControlledSetting(path)) {
+ NOTREACHED();
+ return true;
+ }
+
+ if (RequestTrustedEntity()) {
+ return true;
+ } else {
+ if (!callback.is_null())
+ callbacks_.push_back(callback);
+ return false;
+ }
+}
+
+bool DeviceSettingsProvider::HandlesSetting(const std::string& path) const {
+ return IsControlledSetting(path);
+}
+
+bool DeviceSettingsProvider::RequestTrustedEntity() {
+ if (ownership_status_ == OwnershipService::OWNERSHIP_NONE)
+ return true;
+ return trusted_;
+}
+
+void DeviceSettingsProvider::OnStorePolicyCompleted(
+ SignedSettings::ReturnCode code) {
+ // In any case reload the policy cache to now.
+ if (code != SignedSettings::SUCCESS)
+ Reload();
+ else
+ trusted_ = true;
+}
+
+void DeviceSettingsProvider::OnRetrievePolicyCompleted(
+ SignedSettings::ReturnCode code,
+ const em::PolicyFetchResponse& policy_data) {
+ switch (code) {
+ case SignedSettings::SUCCESS: {
+ DCHECK(policy_data.has_policy_data());
+ policy_.ParseFromString(policy_data.policy_data());
+ signed_settings_cache::Store(policy(),
+ g_browser_process->local_state());
+ UpdateValuesCache();
+ trusted_ = true;
+ for (size_t i = 0; i < callbacks_.size(); ++i)
+ callbacks_[i].Run();
+ callbacks_.clear();
+ // TODO(pastarmovj): Make those side effects responsibility of the
+ // respective subsystems.
+ ApplySideEffects();
+ break;
+ }
+ case SignedSettings::NOT_FOUND:
+ case SignedSettings::KEY_UNAVAILABLE: {
+ if (ownership_status_ != OwnershipService::OWNERSHIP_TAKEN)
+ NOTREACHED() << "No policies present yet, will use the temp storage.";
+ break;
+ }
+ case SignedSettings::BAD_SIGNATURE:
+ case SignedSettings::OPERATION_FAILED: {
+ LOG(ERROR) << "Failed to retrieve cros policies. Reason:" << code;
+ if (retries_left_ > 0) {
+ retries_left_ -= 1;
+ Reload();
+ return;
+ }
+ LOG(ERROR) << "No retries left";
+ break;
+ }
+ }
+}
+
+} // namespace chromeos
« no previous file with comments | « chrome/browser/chromeos/device_settings_provider.h ('k') | chrome/browser/chromeos/login/mock_ownership_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698