diff options
author | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-31 19:45:58 +0000 |
---|---|---|
committer | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-31 19:45:58 +0000 |
commit | 1efe63c885b492499347012308392abfc2a3ad09 (patch) | |
tree | 721062dc820f601e5cd665230ebd232f0a9a79bd /chrome/browser/chromeos/settings | |
parent | 9a9e90b57da5f150551373e9c777da72fd414c57 (diff) | |
download | chromium_src-1efe63c885b492499347012308392abfc2a3ad09.zip chromium_src-1efe63c885b492499347012308392abfc2a3ad09.tar.gz chromium_src-1efe63c885b492499347012308392abfc2a3ad09.tar.bz2 |
Revert 154457 - Switch from SignedSettings to DeviceSettingsService.
This broke GoogleUpdateTest.StatsConsent on Linux ChromiumOS bots.
Check the try jobs from the original code review - the same test failed
on the Linux ChromeOS Valgrind bot.
DevicePolicyCache and DeviceSettingsProvider now go through
DeviceSettingsService, which provides a unified and simpler API to
Chrome OS device settings.
BUG=chromium:139126
TEST=Unit tests, everything related to enterprise enrollment and device settings still works.
TBR=ben@chromium.org
Review URL: https://chromiumcodereview.appspot.com/10832035
TBR=mnissler@chromium.org
Review URL: https://chromiumcodereview.appspot.com/10918027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@154483 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos/settings')
19 files changed, 862 insertions, 566 deletions
diff --git a/chrome/browser/chromeos/settings/cros_settings.cc b/chrome/browser/chromeos/settings/cros_settings.cc index f0592f3..a0622b4 100644 --- a/chrome/browser/chromeos/settings/cros_settings.cc +++ b/chrome/browser/chromeos/settings/cros_settings.cc @@ -11,7 +11,7 @@ #include "base/string_util.h" #include "base/values.h" #include "chrome/browser/chromeos/settings/device_settings_provider.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" +#include "chrome/browser/chromeos/settings/signed_settings_helper.h" #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" #include "chrome/browser/chromeos/settings/system_settings_provider.h" #include "chrome/common/chrome_notification_types.h" @@ -35,6 +35,22 @@ bool CrosSettings::IsCrosSettings(const std::string& path) { return StartsWithASCII(path, kCrosSettingsPrefix, true); } +void CrosSettings::FireObservers(const std::string& path) { + DCHECK(CalledOnValidThread()); + SettingsObserverMap::iterator observer_iterator = + settings_observers_.find(path); + if (observer_iterator == settings_observers_.end()) + return; + + NotificationObserverList::Iterator it(*(observer_iterator->second)); + content::NotificationObserver* observer; + while ((observer = it.GetNext()) != NULL) { + observer->Observe(chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED, + content::Source<CrosSettings>(this), + content::Details<const std::string>(&path)); + } +} + void CrosSettings::Set(const std::string& path, const base::Value& in_value) { DCHECK(CalledOnValidThread()); CrosSettingsProvider* provider; @@ -43,27 +59,6 @@ void CrosSettings::Set(const std::string& path, const base::Value& in_value) { provider->Set(path, in_value); } -const base::Value* CrosSettings::GetPref(const std::string& path) const { - DCHECK(CalledOnValidThread()); - CrosSettingsProvider* provider = GetProvider(path); - if (provider) - return provider->Get(path); - NOTREACHED() << path << " preference was not found in the signed settings."; - return NULL; -} - -CrosSettingsProvider::TrustedStatus CrosSettings::PrepareTrustedValues( - const base::Closure& callback) const { - DCHECK(CalledOnValidThread()); - for (size_t i = 0; i < providers_.size(); ++i) { - CrosSettingsProvider::TrustedStatus status = - providers_[i]->PrepareTrustedValues(callback); - if (status != CrosSettingsProvider::TRUSTED) - return status; - } - return CrosSettingsProvider::TRUSTED; -} - void CrosSettings::SetBoolean(const std::string& path, bool in_value) { DCHECK(CalledOnValidThread()); base::FundamentalValue value(in_value); @@ -109,51 +104,6 @@ void CrosSettings::RemoveFromList(const std::string& path, Set(path, *new_value); } -bool CrosSettings::GetBoolean(const std::string& path, - bool* bool_value) const { - DCHECK(CalledOnValidThread()); - const base::Value* value = GetPref(path); - if (value) - return value->GetAsBoolean(bool_value); - return false; -} - -bool CrosSettings::GetInteger(const std::string& path, - int* out_value) const { - DCHECK(CalledOnValidThread()); - const base::Value* value = GetPref(path); - if (value) - return value->GetAsInteger(out_value); - return false; -} - -bool CrosSettings::GetDouble(const std::string& path, - double* out_value) const { - DCHECK(CalledOnValidThread()); - const base::Value* value = GetPref(path); - if (value) - return value->GetAsDouble(out_value); - return false; -} - -bool CrosSettings::GetString(const std::string& path, - std::string* out_value) const { - DCHECK(CalledOnValidThread()); - const base::Value* value = GetPref(path); - if (value) - return value->GetAsString(out_value); - return false; -} - -bool CrosSettings::GetList(const std::string& path, - const base::ListValue** out_value) const { - DCHECK(CalledOnValidThread()); - const base::Value* value = GetPref(path); - if (value) - return value->GetAsList(out_value); - return false; -} - bool CrosSettings::FindEmailInList(const std::string& path, const std::string& email) const { DCHECK(CalledOnValidThread()); @@ -270,6 +220,77 @@ CrosSettingsProvider* CrosSettings::GetProvider( return NULL; } +void CrosSettings::ReloadProviders() { + for (size_t i = 0; i < providers_.size(); ++i) + providers_[i]->Reload(); +} + +const base::Value* CrosSettings::GetPref(const std::string& path) const { + DCHECK(CalledOnValidThread()); + CrosSettingsProvider* provider = GetProvider(path); + if (provider) + return provider->Get(path); + NOTREACHED() << path << " preference was not found in the signed settings."; + return NULL; +} + +CrosSettingsProvider::TrustedStatus CrosSettings::PrepareTrustedValues( + const base::Closure& callback) const { + DCHECK(CalledOnValidThread()); + for (size_t i = 0; i < providers_.size(); ++i) { + CrosSettingsProvider::TrustedStatus status = + providers_[i]->PrepareTrustedValues(callback); + if (status != CrosSettingsProvider::TRUSTED) + return status; + } + return CrosSettingsProvider::TRUSTED; +} + +bool CrosSettings::GetBoolean(const std::string& path, + bool* bool_value) const { + DCHECK(CalledOnValidThread()); + const base::Value* value = GetPref(path); + if (value) + return value->GetAsBoolean(bool_value); + return false; +} + +bool CrosSettings::GetInteger(const std::string& path, + int* out_value) const { + DCHECK(CalledOnValidThread()); + const base::Value* value = GetPref(path); + if (value) + return value->GetAsInteger(out_value); + return false; +} + +bool CrosSettings::GetDouble(const std::string& path, + double* out_value) const { + DCHECK(CalledOnValidThread()); + const base::Value* value = GetPref(path); + if (value) + return value->GetAsDouble(out_value); + return false; +} + +bool CrosSettings::GetString(const std::string& path, + std::string* out_value) const { + DCHECK(CalledOnValidThread()); + const base::Value* value = GetPref(path); + if (value) + return value->GetAsString(out_value); + return false; +} + +bool CrosSettings::GetList(const std::string& path, + const base::ListValue** out_value) const { + DCHECK(CalledOnValidThread()); + const base::Value* value = GetPref(path); + if (value) + return value->GetAsList(out_value); + return false; +} + CrosSettings::CrosSettings() { CrosSettingsProvider::NotifyObserversCallback notify_cb( base::Bind(&CrosSettings::FireObservers, @@ -280,7 +301,7 @@ CrosSettings::CrosSettings() { AddSettingsProvider(new StubCrosSettingsProvider(notify_cb)); } else { AddSettingsProvider( - new DeviceSettingsProvider(notify_cb, DeviceSettingsService::Get())); + new DeviceSettingsProvider(notify_cb, SignedSettingsHelper::Get())); } // System settings are not mocked currently. AddSettingsProvider(new SystemSettingsProvider(notify_cb)); @@ -291,20 +312,4 @@ CrosSettings::~CrosSettings() { STLDeleteValues(&settings_observers_); } -void CrosSettings::FireObservers(const std::string& path) { - DCHECK(CalledOnValidThread()); - SettingsObserverMap::iterator observer_iterator = - settings_observers_.find(path); - if (observer_iterator == settings_observers_.end()) - return; - - NotificationObserverList::Iterator it(*(observer_iterator->second)); - content::NotificationObserver* observer; - while ((observer = it.GetNext()) != NULL) { - observer->Observe(chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED, - content::Source<CrosSettings>(this), - content::Details<const std::string>(&path)); - } -} - } // namespace chromeos diff --git a/chrome/browser/chromeos/settings/cros_settings.h b/chrome/browser/chromeos/settings/cros_settings.h index 4148ee6..128184c 100644 --- a/chrome/browser/chromeos/settings/cros_settings.h +++ b/chrome/browser/chromeos/settings/cros_settings.h @@ -91,16 +91,12 @@ class CrosSettings : public base::NonThreadSafe { // Returns the provider that handles settings with the |path| or prefix. CrosSettingsProvider* GetProvider(const std::string& path) const; + // Forces all providers to reload their caches from the respective backing + // stores if they have any. + void ReloadProviders(); + private: friend struct base::DefaultLazyInstanceTraits<CrosSettings>; - friend class CrosSettingsTest; - - // Public for testing. - CrosSettings(); - ~CrosSettings(); - - // Fires system setting change notification. - void FireObservers(const std::string& path); // List of ChromeOS system settings providers. std::vector<CrosSettingsProvider*> providers_; @@ -113,6 +109,12 @@ class CrosSettings : public base::NonThreadSafe { SettingsObserverMap; SettingsObserverMap settings_observers_; + CrosSettings(); + ~CrosSettings(); + + // Fires system setting change notification. + void FireObservers(const std::string& path); + DISALLOW_COPY_AND_ASSIGN(CrosSettings); }; diff --git a/chrome/browser/chromeos/settings/cros_settings_provider.h b/chrome/browser/chromeos/settings/cros_settings_provider.h index 6196555..2477f34 100644 --- a/chrome/browser/chromeos/settings/cros_settings_provider.h +++ b/chrome/browser/chromeos/settings/cros_settings_provider.h @@ -58,6 +58,9 @@ class CrosSettingsProvider { // Gets the namespace prefix provided by this provider. virtual bool HandlesSetting(const std::string& path) const = 0; + // Reloads the caches if the provider has any. + virtual void Reload() = 0; + void SetNotifyObserversCallback(const NotifyObserversCallback& notify_cb); protected: diff --git a/chrome/browser/chromeos/settings/cros_settings_unittest.cc b/chrome/browser/chromeos/settings/cros_settings_unittest.cc index e3fc928..f5fb133 100644 --- a/chrome/browser/chromeos/settings/cros_settings_unittest.cc +++ b/chrome/browser/chromeos/settings/cros_settings_unittest.cc @@ -2,19 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/chromeos/settings/signed_settings.h" + #include <map> #include <string> #include "base/bind.h" -#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/message_loop.h" #include "base/stl_util.h" #include "base/values.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/login/mock_user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/cros_settings_names.h" -#include "chrome/browser/chromeos/settings/device_settings_test_helper.h" -#include "chrome/browser/policy/cloud_policy_constants.h" +#include "chrome/browser/chromeos/settings/signed_settings_cache.h" #include "chrome/browser/policy/proto/chrome_device_policy.pb.h" #include "chrome/browser/policy/proto/device_management_backend.pb.h" #include "chrome/test/base/testing_browser_process.h" @@ -22,8 +24,10 @@ #include "content/public/test/test_browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" -namespace em = enterprise_management; +using ::testing::AnyNumber; +using ::testing::Return; +namespace em = enterprise_management; namespace chromeos { class CrosSettingsTest : public testing::Test { @@ -31,15 +35,28 @@ class CrosSettingsTest : public testing::Test { CrosSettingsTest() : message_loop_(MessageLoop::TYPE_UI), ui_thread_(content::BrowserThread::UI, &message_loop_), - local_state_(static_cast<TestingBrowserProcess*>(g_browser_process)), - weak_factory_(this) {} + file_thread_(content::BrowserThread::FILE, &message_loop_), + pointer_factory_(this), + local_state_(static_cast<TestingBrowserProcess*>(g_browser_process)) { + } - virtual ~CrosSettingsTest() {} + virtual ~CrosSettingsTest() { + } + + virtual void SetUp() { + EXPECT_CALL(*mock_user_manager_.user_manager(), IsCurrentUserOwner()) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + // Reset the cache between tests. + ApplyEmptyPolicy(); + } - virtual void TearDown() OVERRIDE { + virtual void TearDown() { + message_loop_.RunAllPending(); ASSERT_TRUE(expected_props_.empty()); + // Reset the cache between tests. + ApplyEmptyPolicy(); STLDeleteValues(&expected_props_); - expected_props_.clear(); } void FetchPref(const std::string& pref) { @@ -48,12 +65,12 @@ class CrosSettingsTest : public testing::Test { return; if (CrosSettingsProvider::TRUSTED == - settings_.PrepareTrustedValues( - base::Bind(&CrosSettingsTest::FetchPref, - weak_factory_.GetWeakPtr(), pref))) { + CrosSettings::Get()->PrepareTrustedValues( + base::Bind(&CrosSettingsTest::FetchPref, + pointer_factory_.GetWeakPtr(), pref))) { scoped_ptr<base::Value> expected_value( expected_props_.find(pref)->second); - const base::Value* pref_value = settings_.GetPref(pref); + const base::Value* pref_value = CrosSettings::Get()->GetPref(pref); if (expected_value.get()) { ASSERT_TRUE(pref_value); ASSERT_TRUE(expected_value->Equals(pref_value)); @@ -66,7 +83,7 @@ class CrosSettingsTest : public testing::Test { void SetPref(const std::string& pref_name, const base::Value* value) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - settings_.Set(pref_name, *value); + CrosSettings::Get()->Set(pref_name, *value); } void AddExpectation(const std::string& pref_name, base::Value* value) { @@ -79,7 +96,7 @@ class CrosSettingsTest : public testing::Test { // Prepare some policy blob. em::PolicyFetchResponse response; em::ChromeDeviceSettingsProto pol; - policy->set_policy_type(policy::dm_protocol::kChromeDevicePolicyType); + policy->set_policy_type(chromeos::kDevicePolicyType); policy->set_username("me@owner"); policy->set_policy_value(pol.SerializeAsString()); // Wipe the signed settings store. @@ -87,16 +104,25 @@ class CrosSettingsTest : public testing::Test { response.set_policy_data_signature("false"); } + void ApplyEmptyPolicy() { + em::PolicyData fake_pol; + PrepareEmptyPolicy(&fake_pol); + signed_settings_cache::Store(fake_pol, local_state_.Get()); + CrosSettings::Get()->ReloadProviders(); + } + + std::map<std::string, base::Value*> expected_props_; + MessageLoop message_loop_; content::TestBrowserThread ui_thread_; + content::TestBrowserThread file_thread_; - ScopedTestingLocalState local_state_; - ScopedDeviceSettingsTestHelper device_settings_test_helper_; - CrosSettings settings_; + base::WeakPtrFactory<CrosSettingsTest> pointer_factory_; - base::WeakPtrFactory<CrosSettingsTest> weak_factory_; + ScopedTestingLocalState local_state_; - std::map<std::string, base::Value*> expected_props_; + ScopedMockUserManagerEnabler mock_user_manager_; + ScopedStubCrosEnabler stub_cros_enabler_; }; TEST_F(CrosSettingsTest, SetPref) { @@ -105,6 +131,7 @@ TEST_F(CrosSettingsTest, SetPref) { base::Value::CreateBooleanValue(false)); SetPref(kAccountsPrefAllowGuest, expected_props_[kAccountsPrefAllowGuest]); FetchPref(kAccountsPrefAllowGuest); + message_loop_.RunAllPending(); ASSERT_TRUE(expected_props_.empty()); } @@ -136,7 +163,7 @@ TEST_F(CrosSettingsTest, SetWhitelistWithListOps) { base::Value::CreateBooleanValue(false)); AddExpectation(kAccountsPrefUsers, whitelist); // Add some user to the whitelist. - settings_.AppendToList(kAccountsPrefUsers, &hacky_user); + CrosSettings::Get()->AppendToList(kAccountsPrefUsers, &hacky_user); FetchPref(kAccountsPrefAllowNewUser); FetchPref(kAccountsPrefUsers); } @@ -154,10 +181,11 @@ TEST_F(CrosSettingsTest, SetWhitelistWithListOps2) { SetPref(kAccountsPrefUsers, &whitelist); FetchPref(kAccountsPrefAllowNewUser); FetchPref(kAccountsPrefUsers); + message_loop_.RunAllPending(); ASSERT_TRUE(expected_props_.empty()); // Now try to remove one element from that list. AddExpectation(kAccountsPrefUsers, expected_list); - settings_.RemoveFromList(kAccountsPrefUsers, &lamy_user); + CrosSettings::Get()->RemoveFromList(kAccountsPrefUsers, &lamy_user); FetchPref(kAccountsPrefAllowNewUser); FetchPref(kAccountsPrefUsers); } @@ -199,6 +227,13 @@ TEST_F(CrosSettingsTest, SetAllowNewUsers) { FetchPref(kAccountsPrefAllowNewUser); } +TEST_F(CrosSettingsTest, SetOwner) { + base::StringValue hacky_owner("h@xxor"); + AddExpectation(kDeviceOwner, base::Value::CreateStringValue("h@xxor")); + SetPref(kDeviceOwner, &hacky_owner); + FetchPref(kDeviceOwner); +} + TEST_F(CrosSettingsTest, SetEphemeralUsersEnabled) { base::FundamentalValue ephemeral_users_enabled(true); AddExpectation(kAccountsPrefEphemeralUsersEnabled, @@ -214,7 +249,7 @@ TEST_F(CrosSettingsTest, FindEmailInList) { list.Append(base::Value::CreateStringValue("with.dots@gmail.com")); list.Append(base::Value::CreateStringValue("Upper@example.com")); - CrosSettings* cs = &settings_; + CrosSettings* cs = CrosSettings::Get(); cs->Set(kAccountsPrefUsers, list); EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "user@example.com")); diff --git a/chrome/browser/chromeos/settings/device_settings_cache.cc b/chrome/browser/chromeos/settings/device_settings_cache.cc deleted file mode 100644 index 178cbfb..0000000 --- a/chrome/browser/chromeos/settings/device_settings_cache.cc +++ /dev/null @@ -1,59 +0,0 @@ -// 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/settings/device_settings_cache.h" - -#include <string> - -#include "base/base64.h" -#include "base/bind.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/policy/proto/device_management_backend.pb.h" -#include "chrome/browser/prefs/pref_service.h" -#include "chrome/common/pref_names.h" - -namespace em = enterprise_management; - -namespace chromeos { - -namespace device_settings_cache { - -void RegisterPrefs(PrefService* local_state) { - local_state->RegisterStringPref(prefs::kDeviceSettingsCache, - "invalid", - PrefService::UNSYNCABLE_PREF); -} - -bool Store(const em::PolicyData& policy, PrefService* local_state) { - if (local_state) { - std::string policy_string = policy.SerializeAsString(); - std::string encoded; - if (!base::Base64Encode(policy_string, &encoded)) { - LOG(ERROR) << "Can't encode policy in base64."; - return false; - } - local_state->SetString(prefs::kDeviceSettingsCache, encoded); - return true; - } - return false; -} - -bool Retrieve(em::PolicyData *policy, PrefService* local_state) { - if (local_state) { - std::string encoded = - local_state->GetString(prefs::kDeviceSettingsCache); - std::string policy_string; - if (!base::Base64Decode(encoded, &policy_string)) { - // This is normal and happens on first boot. - VLOG(1) << "Can't decode policy from base64."; - return false; - } - return policy->ParseFromString(policy_string); - } - return false; -} - -} // namespace device_settings_cache - -} // namespace chromeos diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc index 1a6b56b..ed03b55 100644 --- a/chrome/browser/chromeos/settings/device_settings_provider.cc +++ b/chrome/browser/chromeos/settings/device_settings_provider.cc @@ -15,15 +15,19 @@ #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/login/user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/cros_settings_names.h" -#include "chrome/browser/chromeos/settings/device_settings_cache.h" +#include "chrome/browser/chromeos/settings/signed_settings_cache.h" +#include "chrome/browser/chromeos/settings/signed_settings_helper.h" #include "chrome/browser/policy/app_pack_updater.h" #include "chrome/browser/policy/browser_policy_connector.h" #include "chrome/browser/policy/cloud_policy_constants.h" -#include "chrome/browser/policy/proto/device_management_backend.pb.h" +#include "chrome/browser/policy/proto/chrome_device_policy.pb.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/notification_service.h" using google::protobuf::RepeatedPtrField; @@ -60,6 +64,9 @@ const char* kKnownSettings[] = { kSystemTimezonePolicy, }; +// Upper bound for number of retries to fetch a signed setting. +static const int kNumRetriesLimit = 9; + // Legacy policy file location. Used to detect migration from pre v12 ChromeOS. const char kLegacyPolicyFile[] = "/var/lib/whitelist/preferences"; @@ -81,30 +88,43 @@ bool HasOldMetricsFile() { DeviceSettingsProvider::DeviceSettingsProvider( const NotifyObserversCallback& notify_cb, - DeviceSettingsService* device_settings_service) + SignedSettingsHelper* signed_settings_helper) : CrosSettingsProvider(notify_cb), - device_settings_service_(device_settings_service), - trusted_status_(TEMPORARILY_UNTRUSTED), - ownership_status_(device_settings_service_->GetOwnershipStatus()), - ALLOW_THIS_IN_INITIALIZER_LIST(store_callback_factory_(this)) { - device_settings_service_->AddObserver(this); - - if (!UpdateFromService()) { - // Make sure we have at least the cache data immediately. - RetrieveCachedData(); - } + signed_settings_helper_(signed_settings_helper), + ownership_status_(OwnershipService::GetSharedInstance()->GetStatus(true)), + migration_helper_(new SignedSettingsMigrationHelper()), + retries_left_(kNumRetriesLimit), + trusted_status_(TEMPORARILY_UNTRUSTED) { + // 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() { - device_settings_service_->RemoveObserver(this); +} + +void DeviceSettingsProvider::Reload() { + // While fetching we can't trust the cache anymore. + trusted_status_ = TEMPORARILY_UNTRUSTED; + if (ownership_status_ == OwnershipService::OWNERSHIP_NONE) { + RetrieveCachedData(); + } else { + // Retrieve the real data. + signed_settings_helper_->StartRetrievePolicyOp( + base::Bind(&DeviceSettingsProvider::OnRetrievePolicyCompleted, + base::Unretained(this))); + } } void DeviceSettingsProvider::DoSet(const std::string& path, const base::Value& in_value) { - // Make sure that either the current user is the device owner or the - // device doesn't have an owner yet. - if (!(device_settings_service_->HasPrivateOwnerKey() || - ownership_status_ == DeviceSettingsService::OWNERSHIP_NONE)) { + if (!UserManager::Get()->IsCurrentUserOwner() && + ownership_status_ != OwnershipService::OWNERSHIP_NONE) { LOG(WARNING) << "Changing settings from non-owner, setting=" << path; // Revert UI change. @@ -114,60 +134,43 @@ void DeviceSettingsProvider::DoSet(const std::string& path, if (IsControlledSetting(path)) { pending_changes_.push_back(PendingQueueElement(path, in_value.DeepCopy())); - if (!store_callback_factory_.HasWeakPtrs()) + if (pending_changes_.size() == 1) SetInPolicy(); } else { NOTREACHED() << "Try to set unhandled cros setting " << path; } } -void DeviceSettingsProvider::OwnershipStatusChanged() { - DeviceSettingsService::OwnershipStatus new_ownership_status = - device_settings_service_->GetOwnershipStatus(); - - // If the device just became owned, write the settings accumulated in the - // cache to device settings proper. It is important that writing only happens - // in this case, as during normal operation, the contents of the cache should - // never overwrite actual device settings. - if (new_ownership_status == DeviceSettingsService::OWNERSHIP_TAKEN && - ownership_status_ == DeviceSettingsService::OWNERSHIP_NONE && - device_settings_service_->HasPrivateOwnerKey()) { - - // There shouldn't be any pending writes, since the cache writes are all - // immediate. - DCHECK(!store_callback_factory_.HasWeakPtrs()); - - // Apply the locally-accumulated device settings on top of the initial - // settings from the service and write back the result. - if (device_settings_service_->device_settings()) { - em::ChromeDeviceSettingsProto new_settings( - *device_settings_service_->device_settings()); - new_settings.MergeFrom(device_settings_); - device_settings_.Swap(&new_settings); - } - StoreDeviceSettings(); +void DeviceSettingsProvider::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + if (type == chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) { + // Reload the policy blob once the owner key has been loaded or updated. + ownership_status_ = OwnershipService::OWNERSHIP_TAKEN; + Reload(); } - - // The owner key might have become available, allowing migration to happen. - AttemptMigration(); - - ownership_status_ = new_ownership_status; } -void DeviceSettingsProvider::DeviceSettingsUpdated() { - if (!store_callback_factory_.HasWeakPtrs()) - UpdateAndProceedStoring(); +const em::PolicyData DeviceSettingsProvider::policy() const { + return policy_; } void DeviceSettingsProvider::RetrieveCachedData() { - em::PolicyData policy_data; - if (!device_settings_cache::Retrieve(&policy_data, - g_browser_process->local_state()) || - !device_settings_.ParseFromString(policy_data.policy_value())) { - VLOG(1) << "Can't retrieve temp store, possibly not created yet."; + // 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()); } - UpdateValuesCache(policy_data, device_settings_); + policy_ = policy; + UpdateValuesCache(); } void DeviceSettingsProvider::SetInPolicy() { @@ -176,44 +179,62 @@ void DeviceSettingsProvider::SetInPolicy() { return; } - if (RequestTrustedEntity() != TRUSTED) { - // Re-sync device settings before proceeding. - device_settings_service_->Load(); + const std::string& prop = pending_changes_[0].first; + base::Value* value = pending_changes_[0].second; + 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); + // In this case the |value_cache_| takes the ownership of |value|. + values_cache_.SetValue(prop, value); + NotifyObservers(prop); + // We can't trust this value anymore until we reload the real username. + trusted_status_ = TEMPORARILY_UNTRUSTED; + pending_changes_.erase(pending_changes_.begin()); + if (!pending_changes_.empty()) + SetInPolicy(); + } else { + NOTREACHED(); + } return; } - std::string prop(pending_changes_.front().first); - scoped_ptr<base::Value> value(pending_changes_.front().second); - pending_changes_.pop_front(); + if (RequestTrustedEntity() != TRUSTED) { + // Otherwise we should first reload and apply on top of that. + signed_settings_helper_->StartRetrievePolicyOp( + base::Bind(&DeviceSettingsProvider::FinishSetInPolicy, + base::Unretained(this))); + return; + } trusted_status_ = TEMPORARILY_UNTRUSTED; + em::PolicyData data = policy(); + em::ChromeDeviceSettingsProto pol; + pol.ParseFromString(data.policy_value()); if (prop == kAccountsPrefAllowNewUser) { - em::AllowNewUsersProto* allow = - device_settings_.mutable_allow_new_users(); + 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 = - device_settings_.mutable_guest_mode_enabled(); + 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 = - device_settings_.mutable_show_user_names(); + 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 = - device_settings_.mutable_data_roaming_enabled(); + em::DataRoamingEnabledProto* roam = pol.mutable_data_roaming_enabled(); bool roaming_value = false; if (value->GetAsBoolean(&roaming_value)) roam->set_data_roaming_enabled(roaming_value); @@ -225,23 +246,20 @@ void DeviceSettingsProvider::SetInPolicy() { std::string proxy_value; if (value->GetAsString(&proxy_value)) { bool success = - device_settings_.mutable_device_proxy_settings()->ParseFromString( - proxy_value); + pol.mutable_device_proxy_settings()->ParseFromString(proxy_value); DCHECK(success); } else { NOTREACHED(); } } else if (prop == kReleaseChannel) { - em::ReleaseChannelProto* release_channel = - device_settings_.mutable_release_channel(); + 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 = - device_settings_.mutable_metrics_enabled(); + em::MetricsEnabledProto* metrics = pol.mutable_metrics_enabled(); bool metrics_value = false; if (value->GetAsBoolean(&metrics_value)) metrics->set_metrics_enabled(metrics_value); @@ -249,8 +267,7 @@ void DeviceSettingsProvider::SetInPolicy() { NOTREACHED(); ApplyMetricsSetting(false, metrics_value); } else if (prop == kAccountsPrefUsers) { - em::UserWhitelistProto* whitelist_proto = - device_settings_.mutable_user_whitelist(); + em::UserWhitelistProto* whitelist_proto = pol.mutable_user_whitelist(); whitelist_proto->clear_user_whitelist(); base::ListValue& users = static_cast<base::ListValue&>(*value); for (base::ListValue::const_iterator i = users.begin(); @@ -261,19 +278,17 @@ void DeviceSettingsProvider::SetInPolicy() { } } else if (prop == kAccountsPrefEphemeralUsersEnabled) { em::EphemeralUsersEnabledProto* ephemeral_users_enabled = - device_settings_.mutable_ephemeral_users_enabled(); + pol.mutable_ephemeral_users_enabled(); bool ephemeral_users_enabled_value = false; - if (value->GetAsBoolean(&ephemeral_users_enabled_value)) { + if (value->GetAsBoolean(&ephemeral_users_enabled_value)) ephemeral_users_enabled->set_ephemeral_users_enabled( ephemeral_users_enabled_value); - } else { + else NOTREACHED(); - } } else { // The remaining settings don't support Set(), since they are not // intended to be customizable by the user: // kAppPack - // kDeviceOwner // kIdleLogoutTimeout // kIdleLogoutWarningDuration // kReleaseChannelDelegated @@ -286,29 +301,48 @@ void DeviceSettingsProvider::SetInPolicy() { // kStartUpUrls // kSystemTimezonePolicy - LOG(FATAL) << "Device setting " << prop << " is read-only."; + NOTREACHED(); } - - em::PolicyData data; - data.set_username(device_settings_service_->GetUsername()); - CHECK(device_settings_.SerializeToString(data.mutable_policy_value())); - + data.set_policy_value(pol.SerializeAsString()); // Set the cache to the updated value. - UpdateValuesCache(data, device_settings_); + policy_ = data; + UpdateValuesCache(); - if (!device_settings_cache::Store(data, g_browser_process->local_state())) + if (!signed_settings_cache::Store(data, g_browser_process->local_state())) LOG(ERROR) << "Couldn't store to the temp storage."; - if (ownership_status_ == DeviceSettingsService::OWNERSHIP_TAKEN) { - StoreDeviceSettings(); + if (ownership_status_ == OwnershipService::OWNERSHIP_TAKEN) { + em::PolicyFetchResponse policy_envelope; + policy_envelope.set_policy_data(policy_.SerializeAsString()); + signed_settings_helper_->StartStorePolicyOp( + policy_envelope, + base::Bind(&DeviceSettingsProvider::OnStorePolicyCompleted, + base::Unretained(this))); } else { // OnStorePolicyCompleted won't get called in this case so proceed with any // pending operations immediately. + delete pending_changes_[0].second; + pending_changes_.erase(pending_changes_.begin()); if (!pending_changes_.empty()) SetInPolicy(); } } +void DeviceSettingsProvider::FinishSetInPolicy( + SignedSettings::ReturnCode code, + const em::PolicyFetchResponse& policy) { + if (code != SignedSettings::SUCCESS) { + LOG(ERROR) << "Can't serialize to policy error code: " << code; + Reload(); + return; + } + // Update the internal caches and set the trusted flag to true so that we + // can pass the trustedness check in the second call to SetInPolicy. + OnRetrievePolicyCompleted(code, policy); + + SetInPolicy(); +} + void DeviceSettingsProvider::DecodeLoginPolicies( const em::ChromeDeviceSettingsProto& policy, PrefValueMap* new_values_cache) const { @@ -504,19 +538,21 @@ void DeviceSettingsProvider::DecodeGenericPolicies( } } -void DeviceSettingsProvider::UpdateValuesCache( - const em::PolicyData& policy_data, - const em::ChromeDeviceSettingsProto& settings) { +void DeviceSettingsProvider::UpdateValuesCache() { + const em::PolicyData data = policy(); PrefValueMap new_values_cache; - if (policy_data.has_username() && !policy_data.has_request_token()) - new_values_cache.SetString(kDeviceOwner, policy_data.username()); + if (data.has_username() && !data.has_request_token()) + new_values_cache.SetString(kDeviceOwner, data.username()); - DecodeLoginPolicies(settings, &new_values_cache); - DecodeKioskPolicies(settings, &new_values_cache); - DecodeNetworkPolicies(settings, &new_values_cache); - DecodeReportingPolicies(settings, &new_values_cache); - DecodeGenericPolicies(settings, &new_values_cache); + em::ChromeDeviceSettingsProto pol; + pol.ParseFromString(data.policy_value()); + + DecodeLoginPolicies(pol, &new_values_cache); + DecodeKioskPolicies(pol, &new_values_cache); + DecodeNetworkPolicies(pol, &new_values_cache); + DecodeReportingPolicies(pol, &new_values_cache); + DecodeGenericPolicies(pol, &new_values_cache); // Collect all notifications but send them only after we have swapped the // cache so that if somebody actually reads the cache will be already valid. @@ -543,18 +579,18 @@ void DeviceSettingsProvider::UpdateValuesCache( } void DeviceSettingsProvider::ApplyMetricsSetting(bool use_file, - bool new_value) { + 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_values_.SetValue(kStatsReportingPref, - base::Value::CreateBooleanValue(new_value)); - AttemptMigration(); + 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."); + << "consent file which is " + << (new_value ? "on." : "off."); } VLOG(1) << "Metrics policy is being set to : " << new_value << "(use file : " << use_file << ")"; @@ -563,7 +599,7 @@ void DeviceSettingsProvider::ApplyMetricsSetting(bool use_file, OptionsUtil::ResolveMetricsReportingEnabled(new_value); } -void DeviceSettingsProvider::ApplyRoamingSetting(bool new_value) { +void DeviceSettingsProvider::ApplyRoamingSetting(bool new_value) const { NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); const NetworkDevice* cellular = cros->FindCellularDevice(); if (cellular) { @@ -578,26 +614,25 @@ void DeviceSettingsProvider::ApplyRoamingSetting(bool new_value) { } } -void DeviceSettingsProvider::ApplySideEffects( - const em::ChromeDeviceSettingsProto& settings) { +void DeviceSettingsProvider::ApplySideEffects() const { + const em::PolicyData data = policy(); + em::ChromeDeviceSettingsProto pol; + pol.ParseFromString(data.policy_value()); // First migrate metrics settings as needed. - if (settings.has_metrics_enabled()) - ApplyMetricsSetting(false, settings.metrics_enabled().metrics_enabled()); + if (pol.has_metrics_enabled()) + ApplyMetricsSetting(false, pol.metrics_enabled().metrics_enabled()); else ApplyMetricsSetting(true, false); - // Next set the roaming setting as needed. - ApplyRoamingSetting( - settings.has_data_roaming_enabled() ? - settings.data_roaming_enabled().data_roaming_enabled() : - false); + ApplyRoamingSetting(pol.has_data_roaming_enabled() ? + pol.data_roaming_enabled().data_roaming_enabled() : false); } bool DeviceSettingsProvider::MitigateMissingPolicy() { // First check if the device has been owned already and if not exit // immediately. if (g_browser_process->browser_policy_connector()->GetDeviceMode() != - policy::DEVICE_MODE_CONSUMER) { + policy::DEVICE_MODE_CONSUMER) { return false; } @@ -610,15 +645,17 @@ bool DeviceSettingsProvider::MitigateMissingPolicy() { LOG(ERROR) << "Corruption of the policy data has been detected." << "Switching to \"safe-mode\" policies until the owner logs in " << "to regenerate the policy data."; - - device_settings_.Clear(); - device_settings_.mutable_allow_new_users()->set_allow_new_users(true); - device_settings_.mutable_guest_mode_enabled()->set_guest_mode_enabled(true); - em::PolicyData empty_policy_data; - UpdateValuesCache(empty_policy_data, device_settings_); + values_cache_.SetBoolean(kAccountsPrefAllowNewUser, true); + values_cache_.SetBoolean(kAccountsPrefAllowGuest, true); values_cache_.SetBoolean(kPolicyMissingMitigationMode, true); trusted_status_ = TRUSTED; - + // Make sure we will recreate the policy once the owner logs in. + // Any value not in this list will be left to the default which is fine as + // we repopulate the whitelist with the owner and all other existing users + // every time the owner enables whitelist filtering on the UI. + migration_helper_->AddMigrationValue( + kAccountsPrefAllowNewUser, base::Value::CreateBooleanValue(true)); + migration_helper_->MigrateValues(); return true; } @@ -648,93 +685,83 @@ bool DeviceSettingsProvider::HandlesSetting(const std::string& path) const { DeviceSettingsProvider::TrustedStatus DeviceSettingsProvider::RequestTrustedEntity() { - if (ownership_status_ == DeviceSettingsService::OWNERSHIP_NONE) + if (ownership_status_ == OwnershipService::OWNERSHIP_NONE) return TRUSTED; return trusted_status_; } -void DeviceSettingsProvider::UpdateAndProceedStoring() { - // Re-sync the cache from the service. - UpdateFromService(); +void DeviceSettingsProvider::OnStorePolicyCompleted( + SignedSettings::ReturnCode code) { + // In any case reload the policy cache to now. + if (code != SignedSettings::SUCCESS) { + Reload(); + } else { + trusted_status_ = TRUSTED; + // TODO(pastarmovj): Make those side effects responsibility of the + // respective subsystems. + ApplySideEffects(); + // Notify the observers we are done. + std::vector<base::Closure> callbacks; + callbacks.swap(callbacks_); + for (size_t i = 0; i < callbacks.size(); ++i) + callbacks[i].Run(); + } - // Trigger the next change if necessary. - if (trusted_status_ == TRUSTED && - !store_callback_factory_.HasWeakPtrs() && - !pending_changes_.empty()) { + // Clear the finished task and proceed with any other stores that could be + // pending by now. + delete pending_changes_[0].second; + pending_changes_.erase(pending_changes_.begin()); + if (!pending_changes_.empty()) SetInPolicy(); - } } -bool DeviceSettingsProvider::UpdateFromService() { - bool settings_loaded = false; - switch (device_settings_service_->status()) { - case DeviceSettingsService::STORE_SUCCESS: { - const em::PolicyData* policy_data = - device_settings_service_->policy_data(); - const em::ChromeDeviceSettingsProto* device_settings = - device_settings_service_->device_settings(); - if (policy_data && device_settings) { - UpdateValuesCache(*policy_data, *device_settings); - device_settings_ = *device_settings; - trusted_status_ = TRUSTED; - - // TODO(pastarmovj): Make those side effects responsibility of the - // respective subsystems. - ApplySideEffects(*device_settings); - - settings_loaded = true; - } else { - // Initial policy load is still pending. - trusted_status_ = TEMPORARILY_UNTRUSTED; - } +void DeviceSettingsProvider::OnRetrievePolicyCompleted( + SignedSettings::ReturnCode code, + const em::PolicyFetchResponse& policy_data) { + VLOG(1) << "OnRetrievePolicyCompleted. Error code: " << code + << ", trusted status : " << trusted_status_ + << ", ownership status : " << ownership_status_; + 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_status_ = TRUSTED; + // TODO(pastarmovj): Make those side effects responsibility of the + // respective subsystems. + ApplySideEffects(); break; } - case DeviceSettingsService::STORE_NO_POLICY: + case SignedSettings::NOT_FOUND: if (MitigateMissingPolicy()) break; - // fall through. - case DeviceSettingsService::STORE_KEY_UNAVAILABLE: - VLOG(1) << "No policies present yet, will use the temp storage."; + case SignedSettings::KEY_UNAVAILABLE: { + if (ownership_status_ != OwnershipService::OWNERSHIP_TAKEN) + NOTREACHED() << "No policies present yet, will use the temp storage."; trusted_status_ = PERMANENTLY_UNTRUSTED; break; - case DeviceSettingsService::STORE_POLICY_ERROR: - case DeviceSettingsService::STORE_VALIDATION_ERROR: - case DeviceSettingsService::STORE_INVALID_POLICY: - case DeviceSettingsService::STORE_OPERATION_FAILED: - LOG(ERROR) << "Failed to retrieve cros policies. Reason: " - << device_settings_service_->status(); + } + case SignedSettings::BAD_SIGNATURE: + case SignedSettings::OPERATION_FAILED: { + LOG(ERROR) << "Failed to retrieve cros policies. Reason:" << code; + if (retries_left_ > 0) { + trusted_status_ = TEMPORARILY_UNTRUSTED; + retries_left_ -= 1; + Reload(); + return; + } + LOG(ERROR) << "No retries left"; trusted_status_ = PERMANENTLY_UNTRUSTED; break; + } } - // Notify the observers we are done. std::vector<base::Closure> callbacks; callbacks.swap(callbacks_); for (size_t i = 0; i < callbacks.size(); ++i) callbacks[i].Run(); - - return settings_loaded; -} - -void DeviceSettingsProvider::StoreDeviceSettings() { - // Mute all previous callbacks to guarantee the |pending_changes_| queue is - // processed serially. - store_callback_factory_.InvalidateWeakPtrs(); - - device_settings_service_->SignAndStore( - scoped_ptr<em::ChromeDeviceSettingsProto>( - new em::ChromeDeviceSettingsProto(device_settings_)), - base::Bind(&DeviceSettingsProvider::UpdateAndProceedStoring, - store_callback_factory_.GetWeakPtr())); -} - -void DeviceSettingsProvider::AttemptMigration() { - if (device_settings_service_->HasPrivateOwnerKey()) { - PrefValueMap::const_iterator i; - for (i = migration_values_.begin(); i != migration_values_.end(); ++i) - DoSet(i->first, *i->second); - migration_values_.Clear(); - } } } // namespace chromeos diff --git a/chrome/browser/chromeos/settings/device_settings_provider.h b/chrome/browser/chromeos/settings/device_settings_provider.h index cf625df..03735b5 100644 --- a/chrome/browser/chromeos/settings/device_settings_provider.h +++ b/chrome/browser/chromeos/settings/device_settings_provider.h @@ -5,20 +5,18 @@ #ifndef CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_SETTINGS_PROVIDER_H_ #define CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_SETTINGS_PROVIDER_H_ -#include <deque> #include <string> #include <utility> #include <vector> #include "base/basictypes.h" #include "base/callback_forward.h" -#include "base/gtest_prod_util.h" -#include "base/memory/weak_ptr.h" #include "chrome/browser/chromeos/settings/cros_settings_provider.h" -#include "chrome/browser/chromeos/settings/device_settings_service.h" -#include "chrome/browser/policy/proto/chrome_device_policy.pb.h" -#include "chrome/browser/prefs/pref_value_map.h" +#include "chrome/browser/chromeos/settings/ownership_service.h" +#include "chrome/browser/chromeos/settings/signed_settings_migration_helper.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" #include "chrome/browser/prefs/pref_value_map.h" +#include "content/public/browser/notification_registrar.h" namespace base { class Value; @@ -30,12 +28,12 @@ class ChromeDeviceSettingsProto; namespace chromeos { -// CrosSettingsProvider implementation that works with device settings. +// CrosSettingsProvider implementation that works with SignedSettings. class DeviceSettingsProvider : public CrosSettingsProvider, - public DeviceSettingsService::Observer { + public content::NotificationObserver { public: DeviceSettingsProvider(const NotifyObserversCallback& notify_cb, - DeviceSettingsService* device_settings_service); + SignedSettingsHelper* signed_settings_helper); virtual ~DeviceSettingsProvider(); // CrosSettingsProvider implementation. @@ -43,26 +41,35 @@ class DeviceSettingsProvider : public CrosSettingsProvider, virtual TrustedStatus PrepareTrustedValues( const base::Closure& callback) OVERRIDE; virtual bool HandlesSetting(const std::string& path) const OVERRIDE; + virtual void Reload() OVERRIDE; private: // CrosSettingsProvider implementation: virtual void DoSet(const std::string& path, const base::Value& value) OVERRIDE; - // DeviceSettingsService::Observer implementation: - virtual void OwnershipStatusChanged() OVERRIDE; - virtual void DeviceSettingsUpdated() OVERRIDE; + // content::NotificationObserver implementation: + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + const enterprise_management::PolicyData policy() const; // Populates in-memory cache from the local_state cache that is used to store - // device settings before the device is owned and to speed up policy + // signed settings before the device is owned and to speed up policy // availability before the policy blob is fetched on boot. void RetrieveCachedData(); - // Stores a value from the |pending_changes_| queue in the device settings. + // Stores a value from the |pending_changes_| queue in the signed settings. // If the device is not owned yet the data ends up only in the local_state // cache and is serialized once ownership is acquired. void SetInPolicy(); + // Finalizes stores to the policy file if the cache is dirty. + void FinishSetInPolicy( + SignedSettings::ReturnCode code, + const enterprise_management::PolicyFetchResponse& policy); + // Decode the various groups of policies. void DecodeLoginPolicies( const enterprise_management::ChromeDeviceSettingsProto& policy, @@ -80,21 +87,18 @@ class DeviceSettingsProvider : public CrosSettingsProvider, const enterprise_management::ChromeDeviceSettingsProto& policy, PrefValueMap* new_values_cache) const; - // Parses the policy data and fills in |values_cache_|. - void UpdateValuesCache( - const enterprise_management::PolicyData& policy_data, - const enterprise_management::ChromeDeviceSettingsProto& settings); + // Parses the policy cache and fills the cache of base::Value objects. + void UpdateValuesCache(); // Applies the metrics policy and if not set migrates the legacy file. - void ApplyMetricsSetting(bool use_file, bool new_value); + void ApplyMetricsSetting(bool use_file, bool new_value) const; // Applies the data roaming policy. - void ApplyRoamingSetting(bool new_value); + void ApplyRoamingSetting(bool new_value) const; // Applies any changes of the policies that are not handled by the respective // subsystems. - void ApplySideEffects( - const enterprise_management::ChromeDeviceSettingsProto& settings); + void ApplySideEffects() const; // In case of missing policy blob we should verify if this is upgrade of // machine owned from pre version 12 OS and the user never touched the device @@ -102,55 +106,58 @@ class DeviceSettingsProvider : public CrosSettingsProvider, // comes and changes that. bool MitigateMissingPolicy(); + // Called right before boolean property is changed. + void OnBooleanPropertyChange(const std::string& path, bool new_value); + // Checks if the current cache value can be trusted for being representative // for the disk cache. TrustedStatus RequestTrustedEntity(); - // Invokes UpdateFromService() to synchronize with |device_settings_service_|, - // then triggers the next store operation if applicable. - void UpdateAndProceedStoring(); - - // Re-reads state from |device_settings_service_|, adjusts - // |trusted_status_| and calls UpdateValuesCache() if applicable. Returns true - // if new settings have been loaded. - bool UpdateFromService(); - - // Sends |device_settings_| to |device_settings_service_| for signing and - // storage in session_manager. - void StoreDeviceSettings(); - - // Checks the current ownership status to see whether the device owner is - // logged in and writes the data accumulated in |migration_values_| to proper - // device settings. - void AttemptMigration(); + // Called right after signed value was checked. + void OnPropertyRetrieve(const std::string& path, + const base::Value* value, + bool use_default_value); + + // Callback of StorePolicyOp for ordinary policy stores. + void OnStorePolicyCompleted(SignedSettings::ReturnCode code); + + // Callback of RetrievePolicyOp for ordinary policy [re]loads. + void OnRetrievePolicyCompleted( + SignedSettings::ReturnCode code, + const enterprise_management::PolicyFetchResponse& policy); + + // These setters are for test use only. + void set_ownership_status(OwnershipService::Status status) { + ownership_status_ = status; + } + void set_trusted_status(TrustedStatus status) { + trusted_status_ = status; + } + void set_retries_left(int retries) { + retries_left_ = retries; + } // Pending callbacks that need to be invoked after settings verification. std::vector<base::Closure> callbacks_; - DeviceSettingsService* device_settings_service_; - mutable PrefValueMap migration_values_; + SignedSettingsHelper* signed_settings_helper_; + OwnershipService::Status ownership_status_; + mutable scoped_ptr<SignedSettingsMigrationHelper> migration_helper_; + + content::NotificationRegistrar registrar_; + + // In order to guard against occasional failure to fetch a property + // we allow for some number of retries. + int retries_left_; + enterprise_management::PolicyData policy_; TrustedStatus trusted_status_; - DeviceSettingsService::OwnershipStatus ownership_status_; - - // The device settings as currently reported through the CrosSettingsProvider - // interface. This may be different from the actual current device settings - // (which can be obtained from |device_settings_service_|) in case the device - // does not have an owner yet or there are pending changes that have not yet - // been written to session_manager. - enterprise_management::ChromeDeviceSettingsProto device_settings_; - - // A cache of values, indexed by the settings keys served through the - // CrosSettingsProvider interface. This is always kept in sync with the raw - // data found in |device_settings_|. + PrefValueMap values_cache_; // This is a queue for set requests, because those need to be sequential. typedef std::pair<std::string, base::Value*> PendingQueueElement; - std::deque<PendingQueueElement> pending_changes_; - - // Weak pointer factory for creating store operation callbacks. - base::WeakPtrFactory<DeviceSettingsProvider> store_callback_factory_; + std::vector<PendingQueueElement> pending_changes_; friend class DeviceSettingsProviderTest; FRIEND_TEST_ALL_PREFIXES(DeviceSettingsProviderTest, diff --git a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc index 8943eee3e..e05c24c 100644 --- a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc +++ b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc @@ -7,14 +7,13 @@ #include <string> #include "base/bind.h" -#include "base/callback.h" #include "base/message_loop.h" #include "base/values.h" #include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/login/mock_user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings_names.h" -#include "chrome/browser/chromeos/settings/device_settings_test_helper.h" -#include "chrome/browser/chromeos/settings/mock_owner_key_util.h" -#include "chrome/browser/policy/policy_builder.h" +#include "chrome/browser/chromeos/settings/mock_signed_settings_helper.h" +#include "chrome/browser/chromeos/settings/ownership_service.h" #include "chrome/browser/policy/proto/chrome_device_policy.pb.h" #include "chrome/browser/policy/proto/device_management_backend.pb.h" #include "chrome/test/base/testing_browser_process.h" @@ -24,82 +23,96 @@ #include "testing/gtest/include/gtest/gtest.h" namespace em = enterprise_management; - namespace chromeos { using ::testing::AnyNumber; using ::testing::Mock; +using ::testing::Return; +using ::testing::SaveArg; using ::testing::_; class DeviceSettingsProviderTest: public testing::Test { - public: +public: MOCK_METHOD1(SettingChanged, void(const std::string&)); MOCK_METHOD0(GetTrustedCallback, void(void)); - protected: +protected: DeviceSettingsProviderTest() : message_loop_(MessageLoop::TYPE_UI), ui_thread_(content::BrowserThread::UI, &message_loop_), file_thread_(content::BrowserThread::FILE, &message_loop_), - local_state_(static_cast<TestingBrowserProcess*>(g_browser_process)), - owner_key_util_(new MockOwnerKeyUtil()) {} + local_state_(static_cast<TestingBrowserProcess*>(g_browser_process)) { + } + + virtual ~DeviceSettingsProviderTest() { + } virtual void SetUp() OVERRIDE { - policy_.payload().mutable_metrics_enabled()->set_metrics_enabled(false); - policy_.Build(); + PrepareEmptyPolicy(); - device_settings_test_helper_.set_policy_blob(policy_.GetBlob()); + EXPECT_CALL(signed_settings_helper_, StartRetrievePolicyOp(_)) + .WillRepeatedly( + MockSignedSettingsHelperRetrievePolicy(SignedSettings::SUCCESS, + policy_blob_)); + EXPECT_CALL(signed_settings_helper_, StartStorePolicyOp(_,_)) + .WillRepeatedly(DoAll( + SaveArg<0>(&policy_blob_), + MockSignedSettingsHelperStorePolicy(SignedSettings::SUCCESS))); - device_settings_service_.Initialize(&device_settings_test_helper_, - owner_key_util_); + EXPECT_CALL(*mock_user_manager_.user_manager(), IsCurrentUserOwner()) + .WillRepeatedly(Return(true)); EXPECT_CALL(*this, SettingChanged(_)).Times(AnyNumber()); provider_.reset( new DeviceSettingsProvider( base::Bind(&DeviceSettingsProviderTest::SettingChanged, base::Unretained(this)), - &device_settings_service_)); + &signed_settings_helper_)); + provider_->set_ownership_status(OwnershipService::OWNERSHIP_TAKEN); + + // To prevent flooding the logs. + provider_->set_retries_left(1); + provider_->Reload(); Mock::VerifyAndClearExpectations(this); } - virtual void TearDown() OVERRIDE { - device_settings_service_.Shutdown(); + void PrepareEmptyPolicy() { + em::PolicyData policy; + em::ChromeDeviceSettingsProto pol; + // Set metrics to disabled to prevent us from running into code that is not + // mocked. + pol.mutable_metrics_enabled()->set_metrics_enabled(false); + policy.set_policy_type(chromeos::kDevicePolicyType); + policy.set_username("me@owner"); + policy.set_policy_value(pol.SerializeAsString()); + // Wipe the signed settings store. + policy_blob_.set_policy_data(policy.SerializeAsString()); + policy_blob_.set_policy_data_signature("false"); } + em::PolicyFetchResponse policy_blob_; + + scoped_ptr<DeviceSettingsProvider> provider_; + MessageLoop message_loop_; content::TestBrowserThread ui_thread_; content::TestBrowserThread file_thread_; - ScopedStubCrosEnabler stub_cros_enabler_; - ScopedTestingLocalState local_state_; - DeviceSettingsTestHelper device_settings_test_helper_; - scoped_refptr<MockOwnerKeyUtil> owner_key_util_; - - DeviceSettingsService device_settings_service_; - - policy::DevicePolicyBuilder policy_; - - scoped_ptr<DeviceSettingsProvider> provider_; + MockSignedSettingsHelper signed_settings_helper_; - private: - DISALLOW_COPY_AND_ASSIGN(DeviceSettingsProviderTest); + ScopedStubCrosEnabler stub_cros_enabler_; + ScopedMockUserManagerEnabler mock_user_manager_; }; TEST_F(DeviceSettingsProviderTest, InitializationTest) { - owner_key_util_->SetPublicKeyFromPrivateKey(policy_.signing_key()); - - // Have the service load a settings blob. - EXPECT_CALL(*this, SettingChanged(_)).Times(AnyNumber()); - device_settings_service_.Load(); - device_settings_test_helper_.Flush(); - Mock::VerifyAndClearExpectations(this); - // Verify that the policy blob has been correctly parsed and trusted. // The trusted flag should be set before the call to PrepareTrustedValues. EXPECT_EQ(CrosSettingsProvider::TRUSTED, - provider_->PrepareTrustedValues(base::Closure())); + provider_->PrepareTrustedValues( + base::Bind(&DeviceSettingsProviderTest::GetTrustedCallback, + base::Unretained(this)))); const base::Value* value = provider_->Get(kStatsReportingPref); ASSERT_TRUE(value); bool bool_value; @@ -108,13 +121,16 @@ TEST_F(DeviceSettingsProviderTest, InitializationTest) { } TEST_F(DeviceSettingsProviderTest, InitializationTestUnowned) { - // Have the service check the key. - device_settings_service_.Load(); - device_settings_test_helper_.Flush(); + // No calls to the SignedSettingsHelper should occur in this case! + Mock::VerifyAndClear(&signed_settings_helper_); + provider_->set_ownership_status(OwnershipService::OWNERSHIP_NONE); + provider_->Reload(); // The trusted flag should be set before the call to PrepareTrustedValues. EXPECT_EQ(CrosSettingsProvider::TRUSTED, - provider_->PrepareTrustedValues(base::Closure())); + provider_->PrepareTrustedValues( + base::Bind(&DeviceSettingsProviderTest::GetTrustedCallback, + base::Unretained(this)))); const base::Value* value = provider_->Get(kReleaseChannel); ASSERT_TRUE(value); std::string string_value; @@ -122,38 +138,26 @@ TEST_F(DeviceSettingsProviderTest, InitializationTestUnowned) { EXPECT_TRUE(string_value.empty()); // Sets should succeed though and be readable from the cache. - EXPECT_CALL(*this, SettingChanged(_)).Times(AnyNumber()); EXPECT_CALL(*this, SettingChanged(kReleaseChannel)).Times(1); base::StringValue new_value("stable-channel"); provider_->Set(kReleaseChannel, new_value); - Mock::VerifyAndClearExpectations(this); - - // This shouldn't trigger a write. - device_settings_test_helper_.set_policy_blob(std::string()); - device_settings_test_helper_.Flush(); - EXPECT_EQ(std::string(), device_settings_test_helper_.policy_blob()); - - // Verify the change has been applied. + // Do one more reload here to make sure we don't flip randomly between stores. + provider_->Reload(); + // Verify the change has not been applied. const base::Value* saved_value = provider_->Get(kReleaseChannel); ASSERT_TRUE(saved_value); EXPECT_TRUE(saved_value->GetAsString(&string_value)); ASSERT_EQ("stable-channel", string_value); + Mock::VerifyAndClearExpectations(this); } TEST_F(DeviceSettingsProviderTest, SetPrefFailed) { + EXPECT_CALL(*this, SettingChanged(kStatsReportingPref)).Times(1); // If we are not the owner no sets should work. - owner_key_util_->SetPublicKeyFromPrivateKey(policy_.signing_key()); - + EXPECT_CALL(*mock_user_manager_.user_manager(), IsCurrentUserOwner()) + .WillOnce(Return(false)); base::FundamentalValue value(true); - EXPECT_CALL(*this, SettingChanged(kStatsReportingPref)).Times(1); provider_->Set(kStatsReportingPref, value); - Mock::VerifyAndClearExpectations(this); - - // This shouldn't trigger a write. - device_settings_test_helper_.set_policy_blob(std::string()); - device_settings_test_helper_.Flush(); - EXPECT_EQ(std::string(), device_settings_test_helper_.policy_blob()); - // Verify the change has not been applied. const base::Value* saved_value = provider_->Get(kStatsReportingPref); ASSERT_TRUE(saved_value); @@ -163,26 +167,10 @@ TEST_F(DeviceSettingsProviderTest, SetPrefFailed) { } TEST_F(DeviceSettingsProviderTest, SetPrefSucceed) { - owner_key_util_->SetPrivateKey(policy_.signing_key()); - device_settings_service_.SetUsername(policy_.policy_data().username()); - device_settings_test_helper_.Flush(); - - base::FundamentalValue value(true); - EXPECT_CALL(*this, SettingChanged(_)).Times(AnyNumber()); EXPECT_CALL(*this, SettingChanged(kStatsReportingPref)).Times(1); + base::FundamentalValue value(true); provider_->Set(kStatsReportingPref, value); - Mock::VerifyAndClearExpectations(this); - - // Process the store. - device_settings_test_helper_.set_policy_blob(std::string()); - device_settings_test_helper_.Flush(); - - // Verify that the device policy has been adjusted. - ASSERT_TRUE(device_settings_service_.device_settings()); - EXPECT_TRUE(device_settings_service_.device_settings()-> - metrics_enabled().metrics_enabled()); - - // Verify the change has been applied. + // Verify the change has not been applied. const base::Value* saved_value = provider_->Get(kStatsReportingPref); ASSERT_TRUE(saved_value); bool bool_value; @@ -190,64 +178,84 @@ TEST_F(DeviceSettingsProviderTest, SetPrefSucceed) { EXPECT_TRUE(bool_value); } -TEST_F(DeviceSettingsProviderTest, PolicyRetrievalFailedBadSignature) { - owner_key_util_->SetPublicKeyFromPrivateKey(policy_.signing_key()); - policy_.policy().set_policy_data_signature("bad signature"); - device_settings_test_helper_.set_policy_blob(policy_.GetBlob()); - - device_settings_service_.Load(); - device_settings_test_helper_.Flush(); - - // Verify that the cached settings blob is not "trusted". - EXPECT_EQ(DeviceSettingsService::STORE_VALIDATION_ERROR, - device_settings_service_.status()); +TEST_F(DeviceSettingsProviderTest, PolicyRetrievalFailedBadSingature) { + // No calls to the SignedSettingsHelper should occur in this case! + Mock::VerifyAndClear(&signed_settings_helper_); + EXPECT_CALL(signed_settings_helper_, StartRetrievePolicyOp(_)) + .WillRepeatedly( + MockSignedSettingsHelperRetrievePolicy( + SignedSettings::BAD_SIGNATURE, + policy_blob_)); + provider_->Reload(); + // Verify that the cache policy blob is not "trusted". EXPECT_EQ(CrosSettingsProvider::PERMANENTLY_UNTRUSTED, - provider_->PrepareTrustedValues(base::Closure())); + provider_->PrepareTrustedValues( + base::Bind(&DeviceSettingsProviderTest::GetTrustedCallback, + base::Unretained(this)))); } -TEST_F(DeviceSettingsProviderTest, PolicyRetrievalNoPolicy) { - owner_key_util_->SetPublicKeyFromPrivateKey(policy_.signing_key()); - device_settings_test_helper_.set_policy_blob(std::string()); - - device_settings_service_.Load(); - device_settings_test_helper_.Flush(); - - // Verify that the cached settings blob is not "trusted". - EXPECT_EQ(DeviceSettingsService::STORE_NO_POLICY, - device_settings_service_.status()); +TEST_F(DeviceSettingsProviderTest, PolicyRetrievalOperationFailedPermanently) { + // No calls to the SignedSettingsHelper should occur in this case! + Mock::VerifyAndClear(&signed_settings_helper_); + EXPECT_CALL(signed_settings_helper_, StartRetrievePolicyOp(_)) + .WillRepeatedly( + MockSignedSettingsHelperRetrievePolicy( + SignedSettings::OPERATION_FAILED, + policy_blob_)); + provider_->Reload(); + // Verify that the cache policy blob is not "trusted". EXPECT_EQ(CrosSettingsProvider::PERMANENTLY_UNTRUSTED, - provider_->PrepareTrustedValues(base::Closure())); + provider_->PrepareTrustedValues( + base::Bind(&DeviceSettingsProviderTest::GetTrustedCallback, + base::Unretained(this)))); } -TEST_F(DeviceSettingsProviderTest, PolicyFailedPermanentlyNotification) { - owner_key_util_->SetPublicKeyFromPrivateKey(policy_.signing_key()); - device_settings_test_helper_.set_policy_blob(std::string()); +TEST_F(DeviceSettingsProviderTest, PolicyRetrievalOperationFailedOnce) { + // No calls to the SignedSettingsHelper should occur in this case! + Mock::VerifyAndClear(&signed_settings_helper_); + EXPECT_CALL(signed_settings_helper_, StartRetrievePolicyOp(_)) + .WillOnce( + MockSignedSettingsHelperRetrievePolicy( + SignedSettings::OPERATION_FAILED, + policy_blob_)) + .WillRepeatedly( + MockSignedSettingsHelperRetrievePolicy( + SignedSettings::SUCCESS, + policy_blob_)); + // Should be trusted after an automatic reload. + provider_->Reload(); + // Verify that the cache policy blob is not "trusted". + EXPECT_EQ(CrosSettingsProvider::TRUSTED, + provider_->PrepareTrustedValues( + base::Bind(&DeviceSettingsProviderTest::GetTrustedCallback, + base::Unretained(this)))); +} +TEST_F(DeviceSettingsProviderTest, PolicyFailedPermanentlyNotification) { + Mock::VerifyAndClear(&signed_settings_helper_); + EXPECT_CALL(signed_settings_helper_, StartRetrievePolicyOp(_)) + .WillRepeatedly( + MockSignedSettingsHelperRetrievePolicy( + SignedSettings::OPERATION_FAILED, + policy_blob_)); + + provider_->set_trusted_status(CrosSettingsProvider::TEMPORARILY_UNTRUSTED); EXPECT_CALL(*this, GetTrustedCallback()); EXPECT_EQ(CrosSettingsProvider::TEMPORARILY_UNTRUSTED, provider_->PrepareTrustedValues( base::Bind(&DeviceSettingsProviderTest::GetTrustedCallback, base::Unretained(this)))); - - device_settings_service_.Load(); - device_settings_test_helper_.Flush(); - Mock::VerifyAndClearExpectations(this); - - EXPECT_EQ(CrosSettingsProvider::PERMANENTLY_UNTRUSTED, - provider_->PrepareTrustedValues(base::Closure())); + provider_->Reload(); } TEST_F(DeviceSettingsProviderTest, PolicyLoadNotification) { + provider_->set_trusted_status(CrosSettingsProvider::TEMPORARILY_UNTRUSTED); EXPECT_CALL(*this, GetTrustedCallback()); - EXPECT_EQ(CrosSettingsProvider::TEMPORARILY_UNTRUSTED, provider_->PrepareTrustedValues( base::Bind(&DeviceSettingsProviderTest::GetTrustedCallback, base::Unretained(this)))); - - device_settings_service_.Load(); - device_settings_test_helper_.Flush(); - Mock::VerifyAndClearExpectations(this); + provider_->Reload(); } } // namespace chromeos diff --git a/chrome/browser/chromeos/settings/owner_manager.cc b/chrome/browser/chromeos/settings/owner_manager.cc index 60e69a3..385af76 100644 --- a/chrome/browser/chromeos/settings/owner_manager.cc +++ b/chrome/browser/chromeos/settings/owner_manager.cc @@ -12,7 +12,7 @@ #include "base/file_util.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/boot_times_loader.h" -#include "chrome/browser/chromeos/settings/device_settings_cache.h" +#include "chrome/browser/chromeos/settings/signed_settings_cache.h" #include "chrome/common/chrome_notification_types.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" diff --git a/chrome/browser/chromeos/settings/session_manager_observer.cc b/chrome/browser/chromeos/settings/session_manager_observer.cc index fba6894..6bb828c 100644 --- a/chrome/browser/chromeos/settings/session_manager_observer.cc +++ b/chrome/browser/chromeos/settings/session_manager_observer.cc @@ -5,8 +5,8 @@ #include "chrome/browser/chromeos/settings/session_manager_observer.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/chromeos/settings/device_settings_cache.h" #include "chrome/browser/chromeos/settings/signed_settings.h" +#include "chrome/browser/chromeos/settings/signed_settings_cache.h" #include "chrome/common/chrome_notification_types.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/browser/browser_thread.h" @@ -60,7 +60,7 @@ void SessionManagerObserver::OwnerKeySet(bool success) { // Now owner is assigned and key is generated and we should persist // those settings into signed storage. if (success && g_browser_process && g_browser_process->local_state()) - device_settings_cache::Finalize(g_browser_process->local_state()); + signed_settings_cache::Finalize(g_browser_process->local_state()); // Whether we exported the public key or not, send a notification // indicating that we're done with this attempt. diff --git a/chrome/browser/chromeos/settings/signed_settings_cache.cc b/chrome/browser/chromeos/settings/signed_settings_cache.cc new file mode 100644 index 0000000..811f428 --- /dev/null +++ b/chrome/browser/chromeos/settings/signed_settings_cache.cc @@ -0,0 +1,133 @@ +// 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/settings/signed_settings_cache.h" + +#include <string> + +#include "base/base64.h" +#include "base/bind.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/browser/chromeos/settings/ownership_service.h" +#include "chrome/browser/chromeos/settings/signed_settings_helper.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/pref_names.h" + +using content::BrowserThread; + +namespace em = enterprise_management; + +namespace chromeos { + +namespace { + +void OnStorePolicyCompleted(SignedSettings::ReturnCode code) { + if (code != SignedSettings::SUCCESS) + LOG(ERROR) << "Couldn't save temp store to the policy blob. code: " << code; + else + CrosSettings::Get()->ReloadProviders(); +} + +void FinishFinalize(PrefService* local_state, + SignedSettings::ReturnCode code, + const em::PolicyFetchResponse& policy) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (code != SignedSettings::SUCCESS) { + LOG(ERROR) << "Can't finalize temp store error code:" << code; + return; + } + + if (local_state) { + std::string encoded = + local_state->GetString(prefs::kSignedSettingsCache); + std::string policy_string; + if (!base::Base64Decode(encoded, &policy_string)) { + LOG(ERROR) << "Can't decode policy from base64 on finalizing."; + return; + } + + em::PolicyData merging_policy_data; + if (!merging_policy_data.ParseFromString(policy_string)) { + LOG(ERROR) << "Can't decode policy from string on finalizing."; + return; + } + + em::PolicyFetchResponse policy_envelope = policy; + DCHECK(policy_envelope.has_policy_data()); + em::PolicyData base_policy_data; + base_policy_data.ParseFromString(policy_envelope.policy_data()); + // Merge only the policy value as we should never ever rewrite the other + // fields of the PolicyData protobuf. + base_policy_data.set_policy_value(merging_policy_data.policy_value()); + policy_envelope.set_policy_data(base_policy_data.SerializeAsString()); + DCHECK(base_policy_data.has_username()); + policy_envelope.clear_policy_data_signature(); + SignedSettingsHelper::Get()->StartStorePolicyOp( + policy_envelope, base::Bind(&OnStorePolicyCompleted)); + } +} + +// Reload the initial policy blob, and if successful apply the settings from +// temp storage, and write them back the blob in FinishFinalize. +void ReloadSignedSettingsAndFinalize( + PrefService* local_state, + OwnershipService::Status status, + bool current_user_is_owner) { + if (current_user_is_owner) { + SignedSettingsHelper::Get()->StartRetrievePolicyOp( + base::Bind(FinishFinalize, local_state)); + } +} + +} // namespace + +namespace signed_settings_cache { + +void RegisterPrefs(PrefService* local_state) { + local_state->RegisterStringPref(prefs::kSignedSettingsCache, + "invalid", + PrefService::UNSYNCABLE_PREF); +} + +bool Store(const em::PolicyData& policy, PrefService* local_state) { + if (local_state) { + std::string policy_string = policy.SerializeAsString(); + std::string encoded; + if (!base::Base64Encode(policy_string, &encoded)) { + LOG(ERROR) << "Can't encode policy in base64."; + return false; + } + local_state->SetString(prefs::kSignedSettingsCache, encoded); + return true; + } + return false; +} + +bool Retrieve(em::PolicyData *policy, PrefService* local_state) { + if (local_state) { + std::string encoded = + local_state->GetString(prefs::kSignedSettingsCache); + std::string policy_string; + if (!base::Base64Decode(encoded, &policy_string)) { + // This is normal and happens on first boot. + VLOG(1) << "Can't decode policy from base64."; + return false; + } + return policy->ParseFromString(policy_string); + } + return false; +} + +void Finalize(PrefService* local_state) { + // First we have to make sure the owner is really logged in because the key + // notification is generated on every cloud policy key rotation too. + OwnershipService::GetSharedInstance()->GetStatusAsync( + base::Bind(&ReloadSignedSettingsAndFinalize, local_state)); +} + +} // namespace signed_settings_cache + +} // namespace chromeos diff --git a/chrome/browser/chromeos/settings/device_settings_cache.h b/chrome/browser/chromeos/settings/signed_settings_cache.h index 8f6f7cf..ab9da59 100644 --- a/chrome/browser/chromeos/settings/device_settings_cache.h +++ b/chrome/browser/chromeos/settings/signed_settings_cache.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_SETTINGS_CACHE_H_ -#define CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_SETTINGS_CACHE_H_ +#ifndef CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_CACHE_H_ +#define CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_CACHE_H_ namespace enterprise_management { class PolicyData; @@ -13,11 +13,11 @@ class PrefService; namespace chromeos { -// There is need (metrics at OOBE stage) to store settings (that normally would -// go into DeviceSettings storage) before owner has been assigned (hence no key -// is available). This set of functions serves as a transient storage in that -// case. -namespace device_settings_cache { +// There is need (metrics at OOBE stage) to store settings +// (that normally would go into SignedSettings storage) +// before owner has been assigned (hence no key is available). +// This set of functions serves as a transient storage in that case. +namespace signed_settings_cache { // Registers required pref section. void RegisterPrefs(PrefService* local_state); @@ -29,11 +29,11 @@ bool Store(const enterprise_management::PolicyData &policy, bool Retrieve(enterprise_management::PolicyData *policy, PrefService* local_state); -// Call this after owner has been assigned to persist settings into -// DeviceSettings storage. +// Call this after owner has been assigned to persist settings +// into SignedSettings storage. void Finalize(PrefService* local_state); -} // namespace device_settings_cache +} // namespace signed_settings_cache } // namespace chromeos -#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_SETTINGS_CACHE_H_ +#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_CACHE_H_ diff --git a/chrome/browser/chromeos/settings/device_settings_cache_unittest.cc b/chrome/browser/chromeos/settings/signed_settings_cache_unittest.cc index d47021b5..de8bbc5 100644 --- a/chrome/browser/chromeos/settings/device_settings_cache_unittest.cc +++ b/chrome/browser/chromeos/settings/signed_settings_cache_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/chromeos/settings/device_settings_cache.h" +#include "chrome/browser/chromeos/settings/signed_settings_cache.h" #include "chrome/browser/policy/proto/chrome_device_policy.pb.h" #include "chrome/browser/policy/proto/device_management_backend.pb.h" @@ -14,7 +14,7 @@ namespace em = enterprise_management; namespace chromeos { -class DeviceSettingsCacheTest : public testing::Test { +class SignedSettingsCacheTest : public testing::Test { protected: virtual void SetUp() { // prepare some data. @@ -23,18 +23,18 @@ class DeviceSettingsCacheTest : public testing::Test { pol.mutable_allow_new_users()->set_allow_new_users(false); policy_.set_policy_value(pol.SerializeAsString()); - device_settings_cache::RegisterPrefs(&local_state_); + signed_settings_cache::RegisterPrefs(&local_state_); } TestingPrefService local_state_; em::PolicyData policy_; }; -TEST_F(DeviceSettingsCacheTest, Basic) { - EXPECT_TRUE(device_settings_cache::Store(policy_, &local_state_)); +TEST_F(SignedSettingsCacheTest, Basic) { + EXPECT_TRUE(signed_settings_cache::Store(policy_, &local_state_)); em::PolicyData policy_out; - EXPECT_TRUE(device_settings_cache::Retrieve(&policy_out, &local_state_)); + EXPECT_TRUE(signed_settings_cache::Retrieve(&policy_out, &local_state_)); EXPECT_TRUE(policy_out.has_policy_type()); EXPECT_TRUE(policy_out.has_policy_value()); @@ -45,13 +45,13 @@ TEST_F(DeviceSettingsCacheTest, Basic) { EXPECT_FALSE(pol.allow_new_users().allow_new_users()); } -TEST_F(DeviceSettingsCacheTest, CorruptData) { - EXPECT_TRUE(device_settings_cache::Store(policy_, &local_state_)); +TEST_F(SignedSettingsCacheTest, CorruptData) { + EXPECT_TRUE(signed_settings_cache::Store(policy_, &local_state_)); - local_state_.SetString(prefs::kDeviceSettingsCache, "blaaa"); + local_state_.SetString(prefs::kSignedSettingsCache, "blaaa"); em::PolicyData policy_out; - EXPECT_FALSE(device_settings_cache::Retrieve(&policy_out, &local_state_)); + EXPECT_FALSE(signed_settings_cache::Retrieve(&policy_out, &local_state_)); } } // namespace chromeos diff --git a/chrome/browser/chromeos/settings/signed_settings_migration_helper.cc b/chrome/browser/chromeos/settings/signed_settings_migration_helper.cc new file mode 100644 index 0000000..566f2b3 --- /dev/null +++ b/chrome/browser/chromeos/settings/signed_settings_migration_helper.cc @@ -0,0 +1,66 @@ +// 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/settings/signed_settings_migration_helper.h" + +#include "base/bind.h" +#include "base/values.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/common/chrome_notification_types.h" +#include "content/public/browser/notification_service.h" + +namespace chromeos { + +SignedSettingsMigrationHelper::SignedSettingsMigrationHelper() + : ALLOW_THIS_IN_INITIALIZER_LIST(ptr_factory_(this)) { + registrar_.Add(this, chrome::NOTIFICATION_OWNERSHIP_CHECKED, + content::NotificationService::AllSources()); +} + +SignedSettingsMigrationHelper::~SignedSettingsMigrationHelper() { + registrar_.RemoveAll(); + migration_values_.Clear(); +} + +void SignedSettingsMigrationHelper::AddMigrationValue(const std::string& path, + base::Value* value) { + migration_values_.SetValue(path, value); +} + +void SignedSettingsMigrationHelper::MigrateValues(void) { + ptr_factory_.InvalidateWeakPtrs(); + OwnershipService::GetSharedInstance()->GetStatusAsync( + base::Bind(&SignedSettingsMigrationHelper::DoMigrateValues, + ptr_factory_.GetWeakPtr())); +} + +// NotificationObserver overrides: +void SignedSettingsMigrationHelper::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + if (type == chrome::NOTIFICATION_OWNERSHIP_CHECKED) + MigrateValues(); +} + +void SignedSettingsMigrationHelper::DoMigrateValues( + OwnershipService::Status status, + bool current_user_is_owner) { + // We can call StartStorePropertyOp in two cases - either if the owner is + // currently logged in and the policy can be updated immediately or if there + // is no owner yet in which case the value will be temporarily stored in the + // SignedSettingsCache until the device is owned. If none of these + // cases is met then we will wait for user change notification and retry. + if (current_user_is_owner || status != OwnershipService::OWNERSHIP_TAKEN) { + std::map<std::string, base::Value*>::const_iterator i; + for (i = migration_values_.begin(); i != migration_values_.end(); ++i) { + // Queue all values for storing. + CrosSettings::Get()->Set(i->first, *i->second); + } + migration_values_.Clear(); + } +} + +} // namespace chromeos + diff --git a/chrome/browser/chromeos/settings/signed_settings_migration_helper.h b/chrome/browser/chromeos/settings/signed_settings_migration_helper.h new file mode 100644 index 0000000..13d5bfb --- /dev/null +++ b/chrome/browser/chromeos/settings/signed_settings_migration_helper.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_MIGRATION_HELPER_H_ +#define CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_MIGRATION_HELPER_H_ + +#include "base/memory/weak_ptr.h" +#include "chrome/browser/chromeos/settings/ownership_service.h" +#include "chrome/browser/chromeos/settings/signed_settings_helper.h" +#include "chrome/browser/prefs/pref_value_map.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +namespace base { +class Value; +} + +namespace chromeos { + +// This class provides the means to migrate settings to the signed settings +// store. It does one of three things - store the settings in the policy blob +// immediately if the current user is the owner. Uses the +// SignedSettingsCache if there is no owner yet, or waits for an +// OWNERSHIP_CHECKED notification to delay the storing until the owner has +// logged in. +class SignedSettingsMigrationHelper : public content::NotificationObserver { + public: + SignedSettingsMigrationHelper(); + virtual ~SignedSettingsMigrationHelper(); + + // Adds a value to be migrated. The class takes ownership of the |value|. + void AddMigrationValue(const std::string& path, base::Value* value); + + // Initiates values migration. If the device is already owned this will + // happen immediately if not it will wait for ownership login and finish the + // migration then. + void MigrateValues(void); + + // NotificationObserver overrides: + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + private: + // Does the actual migration when ownership has been confirmed. + void DoMigrateValues(OwnershipService::Status status, + bool current_user_is_owner); + + content::NotificationRegistrar registrar_; + base::WeakPtrFactory<SignedSettingsMigrationHelper> ptr_factory_; + PrefValueMap migration_values_; + + DISALLOW_COPY_AND_ASSIGN(SignedSettingsMigrationHelper); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_MIGRATION_HELPER_H_ diff --git a/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc b/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc index 83c9d75..facadc3 100644 --- a/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc +++ b/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc @@ -73,6 +73,9 @@ bool StubCrosSettingsProvider::HandlesSetting(const std::string& path) const { return std::find(kHandledSettings, end, path) != end; } +void StubCrosSettingsProvider::Reload() { +} + void StubCrosSettingsProvider::DoSet(const std::string& path, const base::Value& value) { values_.SetValue(path, value.DeepCopy()); diff --git a/chrome/browser/chromeos/settings/stub_cros_settings_provider.h b/chrome/browser/chromeos/settings/stub_cros_settings_provider.h index 9d88bf7..13a10cc 100644 --- a/chrome/browser/chromeos/settings/stub_cros_settings_provider.h +++ b/chrome/browser/chromeos/settings/stub_cros_settings_provider.h @@ -26,6 +26,7 @@ class StubCrosSettingsProvider : public CrosSettingsProvider { virtual TrustedStatus PrepareTrustedValues( const base::Closure& callback) OVERRIDE; virtual bool HandlesSetting(const std::string& path) const OVERRIDE; + virtual void Reload() OVERRIDE; private: // CrosSettingsProvider implementation: diff --git a/chrome/browser/chromeos/settings/system_settings_provider.cc b/chrome/browser/chromeos/settings/system_settings_provider.cc index 75a8656..5148c74 100644 --- a/chrome/browser/chromeos/settings/system_settings_provider.cc +++ b/chrome/browser/chromeos/settings/system_settings_provider.cc @@ -60,6 +60,11 @@ bool SystemSettingsProvider::HandlesSetting(const std::string& path) const { return path == kSystemTimezone; } +void SystemSettingsProvider::Reload() { + // TODO(pastarmovj): We can actually cache the timezone here to make returning + // it faster. +} + void SystemSettingsProvider::TimezoneChanged(const icu::TimeZone& timezone) { // Fires system setting change notification. timezone_value_.reset(base::Value::CreateStringValue( diff --git a/chrome/browser/chromeos/settings/system_settings_provider.h b/chrome/browser/chromeos/settings/system_settings_provider.h index 578a1c5..49ef2aa 100644 --- a/chrome/browser/chromeos/settings/system_settings_provider.h +++ b/chrome/browser/chromeos/settings/system_settings_provider.h @@ -30,6 +30,7 @@ class SystemSettingsProvider : public CrosSettingsProvider, virtual TrustedStatus PrepareTrustedValues( const base::Closure& callback) OVERRIDE; virtual bool HandlesSetting(const std::string& path) const OVERRIDE; + virtual void Reload() OVERRIDE; // TimezoneSettings::Observer implementation. virtual void TimezoneChanged(const icu::TimeZone& timezone) OVERRIDE; |