summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chromeos/settings
diff options
context:
space:
mode:
authormnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-01 11:31:31 +0000
committermnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-01 11:31:31 +0000
commitedaf596da7f67917f53891fabbeffad00db5f843 (patch)
tree0b7fd8e3f8a100dfd5daa6a39f17840c589e21e5 /chrome/browser/chromeos/settings
parentd1d877d6ee940ddd2bd062a2126478a013161bba (diff)
downloadchromium_src-edaf596da7f67917f53891fabbeffad00db5f843.zip
chromium_src-edaf596da7f67917f53891fabbeffad00db5f843.tar.gz
chromium_src-edaf596da7f67917f53891fabbeffad00db5f843.tar.bz2
Move Chrome OS device settings stuff to chrome/browser/chromeos/settings.
BUG=chromium:139126 TEST=Still compiles and passes tests, no functional changes. TBR=ben@chromium.org Review URL: https://chromiumcodereview.appspot.com/10824112 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149399 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos/settings')
-rw-r--r--chrome/browser/chromeos/settings/OWNERS2
-rw-r--r--chrome/browser/chromeos/settings/cros_settings.cc315
-rw-r--r--chrome/browser/chromeos/settings/cros_settings.h123
-rw-r--r--chrome/browser/chromeos/settings/cros_settings_names.cc79
-rw-r--r--chrome/browser/chromeos/settings/cros_settings_names.h50
-rw-r--r--chrome/browser/chromeos/settings/cros_settings_provider.cc46
-rw-r--r--chrome/browser/chromeos/settings/cros_settings_provider.h81
-rw-r--r--chrome/browser/chromeos/settings/cros_settings_unittest.cc276
-rw-r--r--chrome/browser/chromeos/settings/device_settings_provider.cc748
-rw-r--r--chrome/browser/chromeos/settings/device_settings_provider.h173
-rw-r--r--chrome/browser/chromeos/settings/device_settings_provider_unittest.cc257
-rw-r--r--chrome/browser/chromeos/settings/mock_owner_key_utils.cc20
-rw-r--r--chrome/browser/chromeos/settings/mock_owner_key_utils.h61
-rw-r--r--chrome/browser/chromeos/settings/mock_ownership_service.cc13
-rw-r--r--chrome/browser/chromeos/settings/mock_ownership_service.h33
-rw-r--r--chrome/browser/chromeos/settings/mock_signed_settings_helper.cc17
-rw-r--r--chrome/browser/chromeos/settings/mock_signed_settings_helper.h39
-rw-r--r--chrome/browser/chromeos/settings/owner_key_utils.cc175
-rw-r--r--chrome/browser/chromeos/settings/owner_key_utils.h90
-rw-r--r--chrome/browser/chromeos/settings/owner_key_utils_unittest.cc84
-rw-r--r--chrome/browser/chromeos/settings/owner_manager.cc147
-rw-r--r--chrome/browser/chromeos/settings/owner_manager.h125
-rw-r--r--chrome/browser/chromeos/settings/owner_manager_unittest.cc347
-rw-r--r--chrome/browser/chromeos/settings/owner_manager_unittest.h93
-rw-r--r--chrome/browser/chromeos/settings/ownership_service.cc259
-rw-r--r--chrome/browser/chromeos/settings/ownership_service.h150
-rw-r--r--chrome/browser/chromeos/settings/ownership_service_unittest.cc238
-rw-r--r--chrome/browser/chromeos/settings/session_manager_observer.cc81
-rw-r--r--chrome/browser/chromeos/settings/session_manager_observer.h32
-rw-r--r--chrome/browser/chromeos/settings/signed_settings.cc290
-rw-r--r--chrome/browser/chromeos/settings/signed_settings.h95
-rw-r--r--chrome/browser/chromeos/settings/signed_settings_cache.cc133
-rw-r--r--chrome/browser/chromeos/settings/signed_settings_cache.h39
-rw-r--r--chrome/browser/chromeos/settings/signed_settings_cache_unittest.cc57
-rw-r--r--chrome/browser/chromeos/settings/signed_settings_helper.cc251
-rw-r--r--chrome/browser/chromeos/settings/signed_settings_helper.h58
-rw-r--r--chrome/browser/chromeos/settings/signed_settings_helper_unittest.cc209
-rw-r--r--chrome/browser/chromeos/settings/signed_settings_migration_helper.cc66
-rw-r--r--chrome/browser/chromeos/settings/signed_settings_migration_helper.h59
-rw-r--r--chrome/browser/chromeos/settings/signed_settings_unittest.cc398
-rw-r--r--chrome/browser/chromeos/settings/stub_cros_settings_provider.cc92
-rw-r--r--chrome/browser/chromeos/settings/stub_cros_settings_provider.h49
-rw-r--r--chrome/browser/chromeos/settings/stub_cros_settings_provider_unittest.cc98
-rw-r--r--chrome/browser/chromeos/settings/system_settings_provider.cc75
-rw-r--r--chrome/browser/chromeos/settings/system_settings_provider.h50
45 files changed, 6173 insertions, 0 deletions
diff --git a/chrome/browser/chromeos/settings/OWNERS b/chrome/browser/chromeos/settings/OWNERS
new file mode 100644
index 0000000..cd1c574
--- /dev/null
+++ b/chrome/browser/chromeos/settings/OWNERS
@@ -0,0 +1,2 @@
+mnissler@chromium.org
+pastarmovj@chromium.org
diff --git a/chrome/browser/chromeos/settings/cros_settings.cc b/chrome/browser/chromeos/settings/cros_settings.cc
new file mode 100644
index 0000000..a0622b4
--- /dev/null
+++ b/chrome/browser/chromeos/settings/cros_settings.cc
@@ -0,0 +1,315 @@
+// 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/cros_settings.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/settings/device_settings_provider.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"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/net/gaia/gaia_auth_util.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+
+namespace chromeos {
+
+static base::LazyInstance<CrosSettings> g_cros_settings =
+ LAZY_INSTANCE_INITIALIZER;
+
+CrosSettings* CrosSettings::Get() {
+ // TODO(xiyaun): Use real stuff when underlying libcros is ready.
+ return g_cros_settings.Pointer();
+}
+
+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;
+ provider = GetProvider(path);
+ if (provider)
+ provider->Set(path, in_value);
+}
+
+void CrosSettings::SetBoolean(const std::string& path, bool in_value) {
+ DCHECK(CalledOnValidThread());
+ base::FundamentalValue value(in_value);
+ Set(path, value);
+}
+
+void CrosSettings::SetInteger(const std::string& path, int in_value) {
+ DCHECK(CalledOnValidThread());
+ base::FundamentalValue value(in_value);
+ Set(path, value);
+}
+
+void CrosSettings::SetDouble(const std::string& path, double in_value) {
+ DCHECK(CalledOnValidThread());
+ base::FundamentalValue value(in_value);
+ Set(path, value);
+}
+
+void CrosSettings::SetString(const std::string& path,
+ const std::string& in_value) {
+ DCHECK(CalledOnValidThread());
+ base::StringValue value(in_value);
+ Set(path, value);
+}
+
+void CrosSettings::AppendToList(const std::string& path,
+ const base::Value* value) {
+ DCHECK(CalledOnValidThread());
+ const base::Value* old_value = GetPref(path);
+ scoped_ptr<base::Value> new_value(
+ old_value ? old_value->DeepCopy() : new base::ListValue());
+ static_cast<base::ListValue*>(new_value.get())->Append(value->DeepCopy());
+ Set(path, *new_value);
+}
+
+void CrosSettings::RemoveFromList(const std::string& path,
+ const base::Value* value) {
+ DCHECK(CalledOnValidThread());
+ const base::Value* old_value = GetPref(path);
+ scoped_ptr<base::Value> new_value(
+ old_value ? old_value->DeepCopy() : new base::ListValue());
+ static_cast<base::ListValue*>(new_value.get())->Remove(*value, NULL);
+ Set(path, *new_value);
+}
+
+bool CrosSettings::FindEmailInList(const std::string& path,
+ const std::string& email) const {
+ DCHECK(CalledOnValidThread());
+ std::string canonicalized_email(
+ gaia::CanonicalizeEmail(gaia::SanitizeEmail(email)));
+ std::string wildcard_email;
+ std::string::size_type at_pos = canonicalized_email.find('@');
+ if (at_pos != std::string::npos) {
+ wildcard_email =
+ std::string("*").append(canonicalized_email.substr(at_pos));
+ }
+
+ const base::ListValue* list;
+ if (!GetList(path, &list))
+ return false;
+ for (base::ListValue::const_iterator entry(list->begin());
+ entry != list->end();
+ ++entry) {
+ std::string entry_string;
+ if (!(*entry)->GetAsString(&entry_string)) {
+ NOTREACHED();
+ continue;
+ }
+ std::string canonicalized_entry(
+ gaia::CanonicalizeEmail(gaia::SanitizeEmail(entry_string)));
+
+ if (canonicalized_entry == canonicalized_email ||
+ canonicalized_entry == wildcard_email) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CrosSettings::AddSettingsProvider(CrosSettingsProvider* provider) {
+ DCHECK(CalledOnValidThread());
+ providers_.push_back(provider);
+
+ // Allow the provider to notify this object when settings have changed.
+ // Providers instantiated inside this class will have the same callback
+ // passed to their constructor, but doing it here allows for providers
+ // to be instantiated outside this class.
+ CrosSettingsProvider::NotifyObserversCallback notify_cb(
+ base::Bind(&CrosSettings::FireObservers, base::Unretained(this)));
+ provider->SetNotifyObserversCallback(notify_cb);
+ return true;
+}
+
+bool CrosSettings::RemoveSettingsProvider(CrosSettingsProvider* provider) {
+ DCHECK(CalledOnValidThread());
+ std::vector<CrosSettingsProvider*>::iterator it =
+ std::find(providers_.begin(), providers_.end(), provider);
+ if (it != providers_.end()) {
+ providers_.erase(it);
+ return true;
+ }
+ return false;
+}
+
+void CrosSettings::AddSettingsObserver(const char* path,
+ content::NotificationObserver* obs) {
+ DCHECK(path);
+ DCHECK(obs);
+ DCHECK(CalledOnValidThread());
+
+ if (!GetProvider(std::string(path))) {
+ NOTREACHED() << "Trying to add an observer for an unregistered setting: "
+ << path;
+ return;
+ }
+
+ // Get the settings observer list associated with the path.
+ NotificationObserverList* observer_list = NULL;
+ SettingsObserverMap::iterator observer_iterator =
+ settings_observers_.find(path);
+ if (observer_iterator == settings_observers_.end()) {
+ observer_list = new NotificationObserverList;
+ settings_observers_[path] = observer_list;
+ } else {
+ observer_list = observer_iterator->second;
+ }
+
+ // Verify that this observer doesn't already exist.
+ NotificationObserverList::Iterator it(*observer_list);
+ content::NotificationObserver* existing_obs;
+ while ((existing_obs = it.GetNext()) != NULL) {
+ if (existing_obs == obs)
+ return;
+ }
+
+ // Ok, safe to add the pref observer.
+ observer_list->AddObserver(obs);
+}
+
+void CrosSettings::RemoveSettingsObserver(const char* path,
+ content::NotificationObserver* obs) {
+ DCHECK(CalledOnValidThread());
+
+ SettingsObserverMap::iterator observer_iterator =
+ settings_observers_.find(path);
+ if (observer_iterator == settings_observers_.end())
+ return;
+
+ NotificationObserverList* observer_list = observer_iterator->second;
+ observer_list->RemoveObserver(obs);
+}
+
+CrosSettingsProvider* CrosSettings::GetProvider(
+ const std::string& path) const {
+ for (size_t i = 0; i < providers_.size(); ++i) {
+ if (providers_[i]->HandlesSetting(path))
+ return providers_[i];
+ }
+ 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,
+ // This is safe since |this| is never deleted.
+ base::Unretained(this)));
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kStubCrosSettings)) {
+ AddSettingsProvider(new StubCrosSettingsProvider(notify_cb));
+ } else {
+ AddSettingsProvider(
+ new DeviceSettingsProvider(notify_cb, SignedSettingsHelper::Get()));
+ }
+ // System settings are not mocked currently.
+ AddSettingsProvider(new SystemSettingsProvider(notify_cb));
+}
+
+CrosSettings::~CrosSettings() {
+ STLDeleteElements(&providers_);
+ STLDeleteValues(&settings_observers_);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/cros_settings.h b/chrome/browser/chromeos/settings/cros_settings.h
new file mode 100644
index 0000000..128184c
--- /dev/null
+++ b/chrome/browser/chromeos/settings/cros_settings.h
@@ -0,0 +1,123 @@
+// 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_CROS_SETTINGS_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_CROS_SETTINGS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/hash_tables.h"
+#include "base/observer_list.h"
+#include "base/threading/non_thread_safe.h"
+#include "chrome/browser/chromeos/settings/cros_settings_names.h"
+#include "chrome/browser/chromeos/settings/cros_settings_provider.h"
+#include "content/public/browser/notification_observer.h"
+
+namespace base {
+template <typename T> struct DefaultLazyInstanceTraits;
+class ListValue;
+class Value;
+}
+
+namespace chromeos {
+
+// This class manages per-device/global settings.
+class CrosSettings : public base::NonThreadSafe {
+ public:
+ // Class factory.
+ static CrosSettings* Get();
+
+ // Helper function to test if the given |path| is a valid cros setting.
+ static bool IsCrosSettings(const std::string& path);
+
+ // Sets |in_value| to given |path| in cros settings.
+ void Set(const std::string& path, const base::Value& in_value);
+
+ // Returns setting value for the given |path|.
+ const base::Value* GetPref(const std::string& path) const;
+
+ // Requests all providers to fetch their values from a trusted store, if they
+ // haven't done so yet. Returns true if the cros settings returned by |this|
+ // are trusted during the current loop cycle; otherwise returns false, and
+ // |callback| will be invoked later when trusted values become available.
+ // PrepareTrustedValues() should be tried again in that case.
+ virtual CrosSettingsProvider::TrustedStatus PrepareTrustedValues(
+ const base::Closure& callback) const;
+
+ // Convenience forms of Set(). These methods will replace any existing
+ // value at that |path|, even if it has a different type.
+ void SetBoolean(const std::string& path, bool in_value);
+ void SetInteger(const std::string& path, int in_value);
+ void SetDouble(const std::string& path, double in_value);
+ void SetString(const std::string& path, const std::string& in_value);
+
+ // Convenience functions for manipulating lists. Note that the following
+ // functions employs a read, modify and write pattern. If underlying settings
+ // provider updates its value asynchronously such as DeviceSettingsProvider,
+ // value cache they read from might not be fresh and multiple calls to those
+ // function would lose data. See http://crbug.com/127215
+ void AppendToList(const std::string& path, const base::Value* value);
+ void RemoveFromList(const std::string& path, const base::Value* value);
+
+ // These are convenience forms of Get(). The value will be retrieved
+ // and the return value will be true if the |path| is valid and the value at
+ // the end of the path can be returned in the form specified.
+ bool GetBoolean(const std::string& path, bool* out_value) const;
+ bool GetInteger(const std::string& path, int* out_value) const;
+ bool GetDouble(const std::string& path, double* out_value) const;
+ bool GetString(const std::string& path, std::string* out_value) const;
+ bool GetList(const std::string& path,
+ const base::ListValue** out_value) const;
+
+ // Helper function for the whitelist op. Implemented here because we will need
+ // this in a few places. The functions searches for |email| in the pref |path|
+ // It respects whitelists so foo@bar.baz will match *@bar.baz too.
+ bool FindEmailInList(const std::string& path, const std::string& email) const;
+
+ // Adding/removing of providers.
+ bool AddSettingsProvider(CrosSettingsProvider* provider);
+ bool RemoveSettingsProvider(CrosSettingsProvider* provider);
+
+ // If the pref at the given |path| changes, we call the observer's Observe
+ // method with NOTIFICATION_SYSTEM_SETTING_CHANGED.
+ void AddSettingsObserver(const char* path,
+ content::NotificationObserver* obs);
+ void RemoveSettingsObserver(const char* path,
+ content::NotificationObserver* obs);
+
+ // 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>;
+
+ // List of ChromeOS system settings providers.
+ std::vector<CrosSettingsProvider*> providers_;
+
+ // A map from settings names to a list of observers. Observers get fired in
+ // the order they are added.
+ typedef ObserverList<content::NotificationObserver, true>
+ NotificationObserverList;
+ typedef base::hash_map<std::string, NotificationObserverList*>
+ SettingsObserverMap;
+ SettingsObserverMap settings_observers_;
+
+ CrosSettings();
+ ~CrosSettings();
+
+ // Fires system setting change notification.
+ void FireObservers(const std::string& path);
+
+ DISALLOW_COPY_AND_ASSIGN(CrosSettings);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_CROS_SETTINGS_H_
diff --git a/chrome/browser/chromeos/settings/cros_settings_names.cc b/chrome/browser/chromeos/settings/cros_settings_names.cc
new file mode 100644
index 0000000..0cb3aad
--- /dev/null
+++ b/chrome/browser/chromeos/settings/cros_settings_names.cc
@@ -0,0 +1,79 @@
+// 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/cros_settings_names.h"
+
+namespace chromeos {
+
+const char kCrosSettingsPrefix[] = "cros.";
+
+// All cros.accounts.* settings are stored in SignedSettings.
+const char kAccountsPrefAllowGuest[] = "cros.accounts.allowBWSI";
+const char kAccountsPrefAllowNewUser[] = "cros.accounts.allowGuest";
+const char kAccountsPrefShowUserNamesOnSignIn[]
+ = "cros.accounts.showUserNamesOnSignIn";
+const char kAccountsPrefUsers[] = "cros.accounts.users";
+const char kAccountsPrefEphemeralUsersEnabled[] =
+ "cros.accounts.ephemeralUsersEnabled";
+
+// Name of signed setting persisted on device, writeable only by owner.
+const char kSettingProxyEverywhere[] = "cros.proxy.everywhere";
+
+// All cros.signed.* settings are stored in SignedSettings.
+const char kSignedDataRoamingEnabled[] = "cros.signed.data_roaming_enabled";
+
+const char kSystemTimezone[] = "cros.system.timezone";
+
+const char kDeviceOwner[] = "cros.device.owner";
+
+const char kStatsReportingPref[] = "cros.metrics.reportingEnabled";
+
+const char kReleaseChannel[] = "cros.system.releaseChannel";
+const char kReleaseChannelDelegated[] = "cros.system.releaseChannelDelegated";
+
+// A boolean pref that indicates whether OS & firmware version info should be
+// reported along with device policy requests.
+const char kReportDeviceVersionInfo[] =
+ "cros.device_status.report_version_info";
+
+// A boolean pref that indicates whether device activity times should be
+// recorded and reported along with device policy requests.
+const char kReportDeviceActivityTimes[] =
+ "cros.device_status.report_activity_times";
+
+// A boolean pref that indicates whether the state of the dev mode switch at
+// boot should be reported along with device policy requests.
+const char kReportDeviceBootMode[] = "cros.device_status.report_boot_mode";
+
+// A boolean pref that indicates whether the current location should be reported
+// along with device policy requests.
+const char kReportDeviceLocation[] = "cros.device_status.report_location";
+
+// A list of dictionaries, each detailing one extension to install as part of
+// the AppPack and including the following fields:
+// "extension-id": ID of the extension to install
+// "update-url": URL to check the extension's version and download location
+// "key-checksum": checksum of the extension's CRX public key, encoded in hex.
+const char kAppPack[] = "cros.app_pack";
+
+// Values from the ScreenSaver proto. Defines the extension ID of the screen
+// saver extension and the timeout before the screen saver should be started.
+const char kScreenSaverExtensionId[] = "cros.screen_saver.extension_id";
+const char kScreenSaverTimeout[] = "cros.screen_saver.timeout";
+
+// Values from the ForcedLogoutTimeouts proto. Defines the timeouts before a
+// user is logged out after some period of inactivity as well as the duration of
+// a warning message informing the user about the pending logout.
+const char kIdleLogoutTimeout[] = "cros.idle_logout.timeout";
+const char kIdleLogoutWarningDuration[] = "cros.idle_logout.warning_duration";
+
+// Defines the set of URLs to be opened on login to the anonymous account used
+// if the device is in KIOSK mode.
+const char kStartUpUrls[] = "cros.start_up_urls";
+
+// This policy should not appear in the protobuf ever but is used internally to
+// signal that we are running in a "safe-mode" for policy recovery.
+const char kPolicyMissingMitigationMode[] =
+ "cros.internal.policy_mitigation_mode";
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/cros_settings_names.h b/chrome/browser/chromeos/settings/cros_settings_names.h
new file mode 100644
index 0000000..c75c834a
--- /dev/null
+++ b/chrome/browser/chromeos/settings/cros_settings_names.h
@@ -0,0 +1,50 @@
+// 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_CROS_SETTINGS_NAMES_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_CROS_SETTINGS_NAMES_H_
+
+namespace chromeos {
+
+extern const char kCrosSettingsPrefix[];
+
+extern const char kAccountsPrefAllowGuest[];
+extern const char kAccountsPrefAllowNewUser[];
+extern const char kAccountsPrefShowUserNamesOnSignIn[];
+extern const char kAccountsPrefUsers[];
+extern const char kAccountsPrefEphemeralUsersEnabled[];
+
+extern const char kSettingProxyEverywhere[];
+
+extern const char kSignedDataRoamingEnabled[];
+
+extern const char kSystemTimezone[];
+
+extern const char kDeviceOwner[];
+
+extern const char kStatsReportingPref[];
+
+extern const char kReleaseChannel[];
+extern const char kReleaseChannelDelegated[];
+
+extern const char kReportDeviceVersionInfo[];
+extern const char kReportDeviceActivityTimes[];
+extern const char kReportDeviceBootMode[];
+extern const char kReportDeviceLocation[];
+
+extern const char kAppPack[];
+
+extern const char kScreenSaverExtensionId[];
+extern const char kScreenSaverTimeout[];
+
+extern const char kIdleLogoutTimeout[];
+extern const char kIdleLogoutWarningDuration[];
+
+extern const char kStartUpUrls[];
+
+extern const char kPolicyMissingMitigationMode[];
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_CROS_SETTINGS_NAMES_H_
diff --git a/chrome/browser/chromeos/settings/cros_settings_provider.cc b/chrome/browser/chromeos/settings/cros_settings_provider.cc
new file mode 100644
index 0000000..e5810c3
--- /dev/null
+++ b/chrome/browser/chromeos/settings/cros_settings_provider.cc
@@ -0,0 +1,46 @@
+// 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/cros_settings_provider.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/common/chrome_switches.h"
+
+namespace chromeos {
+
+CrosSettingsProvider::CrosSettingsProvider(
+ const NotifyObserversCallback& notify_cb)
+ : notify_cb_(notify_cb) {
+}
+
+CrosSettingsProvider::~CrosSettingsProvider() {
+}
+
+void CrosSettingsProvider::Set(const std::string& path,
+ const base::Value& value) {
+ // We don't allow changing any of the cros settings without prefix
+ // "cros.session." in the guest mode.
+ // It should not reach here from UI in the guest mode, but just in case.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession) &&
+ !::StartsWithASCII(path, "cros.session.", true)) {
+ LOG(ERROR) << "Ignoring the guest request to change: " << path;
+ return;
+ }
+ DoSet(path, value);
+}
+
+void CrosSettingsProvider::NotifyObservers(const std::string& path) {
+ if (!notify_cb_.is_null())
+ notify_cb_.Run(path);
+}
+
+void CrosSettingsProvider::SetNotifyObserversCallback(
+ const NotifyObserversCallback& notify_cb) {
+ notify_cb_ = notify_cb;
+}
+
+}; // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/cros_settings_provider.h b/chrome/browser/chromeos/settings/cros_settings_provider.h
new file mode 100644
index 0000000..2477f34
--- /dev/null
+++ b/chrome/browser/chromeos/settings/cros_settings_provider.h
@@ -0,0 +1,81 @@
+// 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_CROS_SETTINGS_PROVIDER_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_CROS_SETTINGS_PROVIDER_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+namespace base {
+class Value;
+}
+
+namespace chromeos {
+
+class CrosSettingsProvider {
+ public:
+ // The callback type that is called to notify the CrosSettings observers
+ // about a setting change.
+ typedef base::Callback<void(const std::string&)> NotifyObserversCallback;
+
+ // Possible results of a trusted check.
+ enum TrustedStatus {
+ // The trusted values were populated in the cache and can be accessed
+ // until the next iteration of the message loop.
+ TRUSTED,
+ // Either a store or a load operation is in progress. The provided
+ // callback will be invoked once the verification has finished.
+ TEMPORARILY_UNTRUSTED,
+ // The verification of the trusted store has failed permanently. The
+ // client should assume this state final and further checks for
+ // trustedness will fail at least until the browser restarts.
+ PERMANENTLY_UNTRUSTED,
+ };
+
+ // Creates a new provider instance. |notify_cb| will be used to notify
+ // about setting changes.
+ explicit CrosSettingsProvider(const NotifyObserversCallback& notify_cb);
+ virtual ~CrosSettingsProvider();
+
+ // Sets |in_value| to given |path| in cros settings.
+ void Set(const std::string& path, const base::Value& in_value);
+
+ // Gets settings value of given |path| to |out_value|.
+ virtual const base::Value* Get(const std::string& path) const = 0;
+
+ // Requests the provider to fetch its values from a trusted store, if it
+ // hasn't done so yet. Returns TRUSTED if the values returned by this provider
+ // are trusted during the current loop cycle. Otherwise returns
+ // TEMPORARILY_UNTRUSTED, and |callback| will be invoked later when trusted
+ // values become available, PrepareTrustedValues() should be tried again in
+ // that case. Returns PERMANENTLY_UNTRUSTED if a permanent error has occurred.
+ virtual TrustedStatus PrepareTrustedValues(
+ const base::Closure& callback) = 0;
+
+ // 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:
+ // Notifies the observers about a setting change.
+ void NotifyObservers(const std::string& path);
+
+ private:
+ // Does the real job for Set().
+ virtual void DoSet(const std::string& path,
+ const base::Value& in_value) = 0;
+
+ // Callback used to notify about setting changes.
+ NotifyObserversCallback notify_cb_;
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_CROS_SETTINGS_PROVIDER_H_
diff --git a/chrome/browser/chromeos/settings/cros_settings_unittest.cc b/chrome/browser/chromeos/settings/cros_settings_unittest.cc
new file mode 100644
index 0000000..f5fb133
--- /dev/null
+++ b/chrome/browser/chromeos/settings/cros_settings_unittest.cc
@@ -0,0 +1,276 @@
+// 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.h"
+
+#include <map>
+#include <string>
+
+#include "base/bind.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/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"
+#include "chrome/test/base/testing_pref_service.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AnyNumber;
+using ::testing::Return;
+
+namespace em = enterprise_management;
+namespace chromeos {
+
+class CrosSettingsTest : public testing::Test {
+ protected:
+ CrosSettingsTest()
+ : message_loop_(MessageLoop::TYPE_UI),
+ ui_thread_(content::BrowserThread::UI, &message_loop_),
+ file_thread_(content::BrowserThread::FILE, &message_loop_),
+ pointer_factory_(this),
+ local_state_(static_cast<TestingBrowserProcess*>(g_browser_process)) {
+ }
+
+ 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() {
+ message_loop_.RunAllPending();
+ ASSERT_TRUE(expected_props_.empty());
+ // Reset the cache between tests.
+ ApplyEmptyPolicy();
+ STLDeleteValues(&expected_props_);
+ }
+
+ void FetchPref(const std::string& pref) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ if (expected_props_.find(pref) == expected_props_.end())
+ return;
+
+ if (CrosSettingsProvider::TRUSTED ==
+ 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 = CrosSettings::Get()->GetPref(pref);
+ if (expected_value.get()) {
+ ASSERT_TRUE(pref_value);
+ ASSERT_TRUE(expected_value->Equals(pref_value));
+ } else {
+ ASSERT_FALSE(pref_value);
+ }
+ expected_props_.erase(pref);
+ }
+ }
+
+ void SetPref(const std::string& pref_name, const base::Value* value) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ CrosSettings::Get()->Set(pref_name, *value);
+ }
+
+ void AddExpectation(const std::string& pref_name, base::Value* value) {
+ base::Value*& entry = expected_props_[pref_name];
+ delete entry;
+ entry = value;
+ }
+
+ void PrepareEmptyPolicy(em::PolicyData* policy) {
+ // Prepare some policy blob.
+ em::PolicyFetchResponse response;
+ em::ChromeDeviceSettingsProto pol;
+ policy->set_policy_type(chromeos::kDevicePolicyType);
+ policy->set_username("me@owner");
+ policy->set_policy_value(pol.SerializeAsString());
+ // Wipe the signed settings store.
+ response.set_policy_data(policy->SerializeAsString());
+ 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_;
+
+ base::WeakPtrFactory<CrosSettingsTest> pointer_factory_;
+
+ ScopedTestingLocalState local_state_;
+
+ ScopedMockUserManagerEnabler mock_user_manager_;
+ ScopedStubCrosEnabler stub_cros_enabler_;
+};
+
+TEST_F(CrosSettingsTest, SetPref) {
+ // Change to something that is not the default.
+ AddExpectation(kAccountsPrefAllowGuest,
+ base::Value::CreateBooleanValue(false));
+ SetPref(kAccountsPrefAllowGuest, expected_props_[kAccountsPrefAllowGuest]);
+ FetchPref(kAccountsPrefAllowGuest);
+ message_loop_.RunAllPending();
+ ASSERT_TRUE(expected_props_.empty());
+}
+
+TEST_F(CrosSettingsTest, GetPref) {
+ // We didn't change the default so look for it.
+ AddExpectation(kAccountsPrefAllowGuest,
+ base::Value::CreateBooleanValue(true));
+ FetchPref(kAccountsPrefAllowGuest);
+}
+
+TEST_F(CrosSettingsTest, SetWhitelist) {
+ // Setting the whitelist should also switch the value of
+ // kAccountsPrefAllowNewUser to false.
+ base::ListValue whitelist;
+ whitelist.Append(base::Value::CreateStringValue("me@owner"));
+ AddExpectation(kAccountsPrefAllowNewUser,
+ base::Value::CreateBooleanValue(false));
+ AddExpectation(kAccountsPrefUsers, whitelist.DeepCopy());
+ SetPref(kAccountsPrefUsers, &whitelist);
+ FetchPref(kAccountsPrefAllowNewUser);
+ FetchPref(kAccountsPrefUsers);
+}
+
+TEST_F(CrosSettingsTest, SetWhitelistWithListOps) {
+ base::ListValue* whitelist = new base::ListValue();
+ base::StringValue hacky_user("h@xxor");
+ whitelist->Append(hacky_user.DeepCopy());
+ AddExpectation(kAccountsPrefAllowNewUser,
+ base::Value::CreateBooleanValue(false));
+ AddExpectation(kAccountsPrefUsers, whitelist);
+ // Add some user to the whitelist.
+ CrosSettings::Get()->AppendToList(kAccountsPrefUsers, &hacky_user);
+ FetchPref(kAccountsPrefAllowNewUser);
+ FetchPref(kAccountsPrefUsers);
+}
+
+TEST_F(CrosSettingsTest, SetWhitelistWithListOps2) {
+ base::ListValue whitelist;
+ base::StringValue hacky_user("h@xxor");
+ base::StringValue lamy_user("l@mer");
+ whitelist.Append(hacky_user.DeepCopy());
+ base::ListValue* expected_list = whitelist.DeepCopy();
+ whitelist.Append(lamy_user.DeepCopy());
+ AddExpectation(kAccountsPrefAllowNewUser,
+ base::Value::CreateBooleanValue(false));
+ AddExpectation(kAccountsPrefUsers, whitelist.DeepCopy());
+ 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);
+ CrosSettings::Get()->RemoveFromList(kAccountsPrefUsers, &lamy_user);
+ FetchPref(kAccountsPrefAllowNewUser);
+ FetchPref(kAccountsPrefUsers);
+}
+
+TEST_F(CrosSettingsTest, SetEmptyWhitelist) {
+ // Setting the whitelist empty should switch the value of
+ // kAccountsPrefAllowNewUser to true.
+ base::ListValue whitelist;
+ base::FundamentalValue disallow_new(false);
+ AddExpectation(kAccountsPrefAllowNewUser,
+ base::Value::CreateBooleanValue(true));
+ SetPref(kAccountsPrefUsers, &whitelist);
+ SetPref(kAccountsPrefAllowNewUser, &disallow_new);
+ FetchPref(kAccountsPrefAllowNewUser);
+ FetchPref(kAccountsPrefUsers);
+}
+
+TEST_F(CrosSettingsTest, SetWhitelistAndNoNewUsers) {
+ // Setting the whitelist should allow us to set kAccountsPrefAllowNewUser to
+ // false (which is the implicit value too).
+ base::ListValue whitelist;
+ whitelist.Append(base::Value::CreateStringValue("me@owner"));
+ AddExpectation(kAccountsPrefUsers, whitelist.DeepCopy());
+ AddExpectation(kAccountsPrefAllowNewUser,
+ base::Value::CreateBooleanValue(false));
+ SetPref(kAccountsPrefUsers, &whitelist);
+ SetPref(kAccountsPrefAllowNewUser,
+ expected_props_[kAccountsPrefAllowNewUser]);
+ FetchPref(kAccountsPrefAllowNewUser);
+ FetchPref(kAccountsPrefUsers);
+}
+
+TEST_F(CrosSettingsTest, SetAllowNewUsers) {
+ // Setting kAccountsPrefAllowNewUser to true with no whitelist should be ok.
+ AddExpectation(kAccountsPrefAllowNewUser,
+ base::Value::CreateBooleanValue(true));
+ SetPref(kAccountsPrefAllowNewUser,
+ expected_props_[kAccountsPrefAllowNewUser]);
+ 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,
+ base::Value::CreateBooleanValue(true));
+ SetPref(kAccountsPrefEphemeralUsersEnabled, &ephemeral_users_enabled);
+ FetchPref(kAccountsPrefEphemeralUsersEnabled);
+}
+
+TEST_F(CrosSettingsTest, FindEmailInList) {
+ base::ListValue list;
+ list.Append(base::Value::CreateStringValue("user@example.com"));
+ list.Append(base::Value::CreateStringValue("nodomain"));
+ list.Append(base::Value::CreateStringValue("with.dots@gmail.com"));
+ list.Append(base::Value::CreateStringValue("Upper@example.com"));
+
+ CrosSettings* cs = CrosSettings::Get();
+ cs->Set(kAccountsPrefUsers, list);
+
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "user@example.com"));
+ EXPECT_FALSE(cs->FindEmailInList(kAccountsPrefUsers, "us.er@example.com"));
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "USER@example.com"));
+ EXPECT_FALSE(cs->FindEmailInList(kAccountsPrefUsers, "user"));
+
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "nodomain"));
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "nodomain@gmail.com"));
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "no.domain@gmail.com"));
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "NO.DOMAIN"));
+
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "with.dots@gmail.com"));
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "withdots@gmail.com"));
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "WITH.DOTS@gmail.com"));
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "WITHDOTS"));
+
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "Upper@example.com"));
+ EXPECT_FALSE(cs->FindEmailInList(kAccountsPrefUsers, "U.pper@example.com"));
+ EXPECT_FALSE(cs->FindEmailInList(kAccountsPrefUsers, "Upper"));
+ EXPECT_TRUE(cs->FindEmailInList(kAccountsPrefUsers, "upper@example.com"));
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc
new file mode 100644
index 0000000..0c1432e
--- /dev/null
+++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -0,0 +1,748 @@
+// 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_provider.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/threading/thread_restrictions.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/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/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/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;
+
+namespace em = enterprise_management;
+
+namespace chromeos {
+
+namespace {
+
+// List of settings handled by the DeviceSettingsProvider.
+const char* kKnownSettings[] = {
+ kAccountsPrefAllowGuest,
+ kAccountsPrefAllowNewUser,
+ kAccountsPrefEphemeralUsersEnabled,
+ kAccountsPrefShowUserNamesOnSignIn,
+ kAccountsPrefUsers,
+ kAppPack,
+ kDeviceOwner,
+ kIdleLogoutTimeout,
+ kIdleLogoutWarningDuration,
+ kPolicyMissingMitigationMode,
+ kReleaseChannel,
+ kReleaseChannelDelegated,
+ kReportDeviceActivityTimes,
+ kReportDeviceBootMode,
+ kReportDeviceLocation,
+ kReportDeviceVersionInfo,
+ kScreenSaverExtensionId,
+ kScreenSaverTimeout,
+ kSettingProxyEverywhere,
+ kSignedDataRoamingEnabled,
+ kStartUpUrls,
+ kStatsReportingPref,
+};
+
+// 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";
+
+bool IsControlledSetting(const std::string& pref_path) {
+ const char** end = kKnownSettings + arraysize(kKnownSettings);
+ return std::find(kKnownSettings, end, pref_path) != end;
+}
+
+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(
+ const NotifyObserversCallback& notify_cb,
+ SignedSettingsHelper* signed_settings_helper)
+ : CrosSettingsProvider(notify_cb),
+ 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() {
+}
+
+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) {
+ if (!UserManager::Get()->IsCurrentUserOwner() &&
+ ownership_status_ != OwnershipService::OWNERSHIP_NONE) {
+ LOG(WARNING) << "Changing settings from non-owner, setting=" << path;
+
+ // Revert UI change.
+ NotifyObservers(path);
+ return;
+ }
+
+ if (IsControlledSetting(path)) {
+ pending_changes_.push_back(PendingQueueElement(path, in_value.DeepCopy()));
+ if (pending_changes_.size() == 1)
+ SetInPolicy();
+ } 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) {
+ // Reload the policy blob once the owner key has been loaded or updated.
+ 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() {
+ if (pending_changes_.empty()) {
+ NOTREACHED();
+ return;
+ }
+
+ 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;
+ }
+
+ 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 = 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();
+ base::ListValue& users = static_cast<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 if (prop == kAccountsPrefEphemeralUsersEnabled) {
+ em::EphemeralUsersEnabledProto* ephemeral_users_enabled =
+ pol.mutable_ephemeral_users_enabled();
+ bool ephemeral_users_enabled_value = false;
+ if (value->GetAsBoolean(&ephemeral_users_enabled_value))
+ ephemeral_users_enabled->set_ephemeral_users_enabled(
+ ephemeral_users_enabled_value);
+ else
+ NOTREACHED();
+ } else {
+ // The remaining settings don't support Set(), since they are not
+ // intended to be customizable by the user:
+ // kAppPack
+ // kIdleLogoutTimeout
+ // kIdleLogoutWarningDuration
+ // kReleaseChannelDelegated
+ // kReportDeviceVersionInfo
+ // kReportDeviceActivityTimes
+ // kReportDeviceBootMode
+ // kReportDeviceLocation
+ // kScreenSaverExtensionId
+ // kScreenSaverTimeout
+ // kStartUpUrls
+
+ NOTREACHED();
+ }
+ data.set_policy_value(pol.SerializeAsString());
+ // Set the cache to the updated value.
+ policy_ = data;
+ UpdateValuesCache();
+
+ 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());
+ 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 {
+ // For all our boolean settings the following is applicable:
+ // true is default permissive value and false is safe prohibitive value.
+ // Exceptions:
+ // kSignedDataRoamingEnabled has a default value of false.
+ // kAccountsPrefEphemeralUsersEnabled has a default value of false.
+ if (policy.has_allow_new_users() &&
+ policy.allow_new_users().has_allow_new_users() &&
+ policy.allow_new_users().allow_new_users()) {
+ // New users allowed, user_whitelist() ignored.
+ new_values_cache->SetBoolean(kAccountsPrefAllowNewUser, true);
+ } else if (!policy.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.
+ new_values_cache->SetBoolean(kAccountsPrefAllowNewUser, true);
+ } else {
+ new_values_cache->SetBoolean(
+ kAccountsPrefAllowNewUser,
+ policy.user_whitelist().user_whitelist_size() == 0);
+ }
+
+ new_values_cache->SetBoolean(
+ kAccountsPrefAllowGuest,
+ !policy.has_guest_mode_enabled() ||
+ !policy.guest_mode_enabled().has_guest_mode_enabled() ||
+ policy.guest_mode_enabled().guest_mode_enabled());
+
+ new_values_cache->SetBoolean(
+ kAccountsPrefShowUserNamesOnSignIn,
+ !policy.has_show_user_names() ||
+ !policy.show_user_names().has_show_user_names() ||
+ policy.show_user_names().show_user_names());
+
+ new_values_cache->SetBoolean(
+ kAccountsPrefEphemeralUsersEnabled,
+ policy.has_ephemeral_users_enabled() &&
+ policy.ephemeral_users_enabled().has_ephemeral_users_enabled() &&
+ policy.ephemeral_users_enabled().ephemeral_users_enabled());
+
+ base::ListValue* list = new base::ListValue();
+ const em::UserWhitelistProto& whitelist_proto = policy.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));
+ }
+ new_values_cache->SetValue(kAccountsPrefUsers, list);
+}
+
+void DeviceSettingsProvider::DecodeKioskPolicies(
+ const em::ChromeDeviceSettingsProto& policy,
+ PrefValueMap* new_values_cache) const {
+ if (policy.has_forced_logout_timeouts()) {
+ if (policy.forced_logout_timeouts().has_idle_logout_timeout()) {
+ new_values_cache->SetInteger(
+ kIdleLogoutTimeout,
+ policy.forced_logout_timeouts().idle_logout_timeout());
+ }
+
+ if (policy.forced_logout_timeouts().has_idle_logout_warning_duration()) {
+ new_values_cache->SetInteger(
+ kIdleLogoutWarningDuration,
+ policy.forced_logout_timeouts().idle_logout_warning_duration());
+ }
+ }
+
+ if (policy.has_login_screen_saver()) {
+ if (policy.login_screen_saver().has_screen_saver_timeout()) {
+ new_values_cache->SetInteger(
+ kScreenSaverTimeout,
+ policy.login_screen_saver().screen_saver_timeout());
+ }
+
+ if (policy.login_screen_saver().has_screen_saver_extension_id()) {
+ new_values_cache->SetString(
+ kScreenSaverExtensionId,
+ policy.login_screen_saver().screen_saver_extension_id());
+ }
+ }
+
+ if (policy.has_app_pack()) {
+ typedef RepeatedPtrField<em::AppPackEntryProto> proto_type;
+ base::ListValue* list = new base::ListValue;
+ const proto_type& app_pack = policy.app_pack().app_pack();
+ for (proto_type::const_iterator it = app_pack.begin();
+ it != app_pack.end(); ++it) {
+ base::DictionaryValue* entry = new base::DictionaryValue;
+ if (it->has_extension_id()) {
+ entry->SetString(policy::AppPackUpdater::kExtensionId,
+ it->extension_id());
+ }
+ if (it->has_update_url())
+ entry->SetString(policy::AppPackUpdater::kUpdateUrl, it->update_url());
+ list->Append(entry);
+ }
+ new_values_cache->SetValue(kAppPack, list);
+ }
+
+ if (policy.has_start_up_urls()) {
+ base::ListValue* list = new base::ListValue();
+ const em::StartUpUrlsProto& urls_proto = policy.start_up_urls();
+ const RepeatedPtrField<std::string>& urls = urls_proto.start_up_urls();
+ for (RepeatedPtrField<std::string>::const_iterator it = urls.begin();
+ it != urls.end(); ++it) {
+ list->Append(base::Value::CreateStringValue(*it));
+ }
+ new_values_cache->SetValue(kStartUpUrls, list);
+ }
+}
+
+void DeviceSettingsProvider::DecodeNetworkPolicies(
+ const em::ChromeDeviceSettingsProto& policy,
+ PrefValueMap* new_values_cache) const {
+ new_values_cache->SetBoolean(
+ kSignedDataRoamingEnabled,
+ policy.has_data_roaming_enabled() &&
+ policy.data_roaming_enabled().has_data_roaming_enabled() &&
+ policy.data_roaming_enabled().data_roaming_enabled());
+
+ // TODO(cmasone): NOTIMPLEMENTED() once http://crosbug.com/13052 is fixed.
+ std::string serialized;
+ if (policy.has_device_proxy_settings() &&
+ policy.device_proxy_settings().SerializeToString(&serialized)) {
+ new_values_cache->SetString(kSettingProxyEverywhere, serialized);
+ }
+}
+
+void DeviceSettingsProvider::DecodeReportingPolicies(
+ const em::ChromeDeviceSettingsProto& policy,
+ PrefValueMap* new_values_cache) const {
+ if (policy.has_device_reporting()) {
+ if (policy.device_reporting().has_report_version_info()) {
+ new_values_cache->SetBoolean(
+ kReportDeviceVersionInfo,
+ policy.device_reporting().report_version_info());
+ }
+ if (policy.device_reporting().has_report_activity_times()) {
+ new_values_cache->SetBoolean(
+ kReportDeviceActivityTimes,
+ policy.device_reporting().report_activity_times());
+ }
+ if (policy.device_reporting().has_report_boot_mode()) {
+ new_values_cache->SetBoolean(
+ kReportDeviceBootMode,
+ policy.device_reporting().report_boot_mode());
+ }
+ // Device location reporting needs to pass privacy review before it can be
+ // enabled. crosbug.com/24681
+ // if (policy.device_reporting().has_report_location()) {
+ // new_values_cache->SetBoolean(
+ // kReportDeviceLocation,
+ // policy.device_reporting().report_location());
+ // }
+ }
+}
+
+void DeviceSettingsProvider::DecodeGenericPolicies(
+ const em::ChromeDeviceSettingsProto& policy,
+ PrefValueMap* new_values_cache) const {
+ if (policy.has_metrics_enabled()) {
+ new_values_cache->SetBoolean(kStatsReportingPref,
+ policy.metrics_enabled().metrics_enabled());
+ } else {
+ new_values_cache->SetBoolean(kStatsReportingPref, HasOldMetricsFile());
+ }
+
+ if (!policy.has_release_channel() ||
+ !policy.release_channel().has_release_channel()) {
+ // Default to an invalid channel (will be ignored).
+ new_values_cache->SetString(kReleaseChannel, "");
+ } else {
+ new_values_cache->SetString(kReleaseChannel,
+ policy.release_channel().release_channel());
+ }
+
+ new_values_cache->SetBoolean(
+ kReleaseChannelDelegated,
+ policy.has_release_channel() &&
+ policy.release_channel().has_release_channel_delegated() &&
+ policy.release_channel().release_channel_delegated());
+}
+
+void DeviceSettingsProvider::UpdateValuesCache() {
+ const em::PolicyData data = policy();
+ PrefValueMap new_values_cache;
+
+ if (data.has_username() && !data.has_request_token())
+ new_values_cache.SetString(kDeviceOwner, data.username());
+
+ 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.
+ std::vector<std::string> notifications;
+ // Go through the new values and verify in the old ones.
+ PrefValueMap::iterator iter = new_values_cache.begin();
+ for (; iter != new_values_cache.end(); ++iter) {
+ const base::Value* old_value;
+ if (!values_cache_.GetValue(iter->first, &old_value) ||
+ !old_value->Equals(iter->second)) {
+ notifications.push_back(iter->first);
+ }
+ }
+ // Now check for values that have been removed from the policy blob.
+ for (iter = values_cache_.begin(); iter != values_cache_.end(); ++iter) {
+ const base::Value* value;
+ if (!new_values_cache.GetValue(iter->first, &value))
+ notifications.push_back(iter->first);
+ }
+ // Swap and notify.
+ values_cache_.Swap(&new_values_cache);
+ for (size_t i = 0; i < notifications.size(); ++i)
+ NotifyObservers(notifications[i]);
+}
+
+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);
+}
+
+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) {
+ return false;
+ }
+
+ // If we are here the policy file were corrupted or missing. This can happen
+ // because we are migrating Pre R11 device to the new secure policies or there
+ // was an attempt to circumvent policy system. In this case we should populate
+ // the policy cache with "safe-mode" defaults which should allow the owner to
+ // log in but lock the device for anyone else until the policy blob has been
+ // recreated by the session manager.
+ 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.";
+ 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;
+}
+
+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;
+}
+
+DeviceSettingsProvider::TrustedStatus
+ DeviceSettingsProvider::PrepareTrustedValues(const base::Closure& cb) {
+ TrustedStatus status = RequestTrustedEntity();
+ if (status == TEMPORARILY_UNTRUSTED && !cb.is_null())
+ callbacks_.push_back(cb);
+ return status;
+}
+
+bool DeviceSettingsProvider::HandlesSetting(const std::string& path) const {
+ return IsControlledSetting(path);
+}
+
+DeviceSettingsProvider::TrustedStatus
+ DeviceSettingsProvider::RequestTrustedEntity() {
+ if (ownership_status_ == OwnershipService::OWNERSHIP_NONE)
+ return TRUSTED;
+ return trusted_status_;
+}
+
+void DeviceSettingsProvider::OnStorePolicyCompleted(
+ SignedSettings::ReturnCode code) {
+ // In any case reload the policy cache to now.
+ if (code != SignedSettings::SUCCESS)
+ Reload();
+ else
+ trusted_status_ = TRUSTED;
+
+ // 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();
+}
+
+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 SignedSettings::NOT_FOUND:
+ if (MitigateMissingPolicy())
+ break;
+ 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 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();
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.h b/chrome/browser/chromeos/settings/device_settings_provider.h
new file mode 100644
index 0000000..03735b5
--- /dev/null
+++ b/chrome/browser/chromeos/settings/device_settings_provider.h
@@ -0,0 +1,173 @@
+// 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_DEVICE_SETTINGS_PROVIDER_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_SETTINGS_PROVIDER_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "chrome/browser/chromeos/settings/cros_settings_provider.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;
+}
+
+namespace enterprise_management {
+class ChromeDeviceSettingsProto;
+} // namespace enterprise_management
+
+namespace chromeos {
+
+// CrosSettingsProvider implementation that works with SignedSettings.
+class DeviceSettingsProvider : public CrosSettingsProvider,
+ public content::NotificationObserver {
+ public:
+ DeviceSettingsProvider(const NotifyObserversCallback& notify_cb,
+ SignedSettingsHelper* signed_settings_helper);
+ virtual ~DeviceSettingsProvider();
+
+ // CrosSettingsProvider implementation.
+ virtual const base::Value* Get(const std::string& path) const OVERRIDE;
+ 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;
+
+ // 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
+ // 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 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,
+ PrefValueMap* new_values_cache) const;
+ void DecodeKioskPolicies(
+ const enterprise_management::ChromeDeviceSettingsProto& policy,
+ PrefValueMap* new_values_cache) const;
+ void DecodeNetworkPolicies(
+ const enterprise_management::ChromeDeviceSettingsProto& policy,
+ PrefValueMap* new_values_cache) const;
+ void DecodeReportingPolicies(
+ const enterprise_management::ChromeDeviceSettingsProto& policy,
+ PrefValueMap* new_values_cache) const;
+ void DecodeGenericPolicies(
+ const enterprise_management::ChromeDeviceSettingsProto& policy,
+ PrefValueMap* new_values_cache) const;
+
+ // 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) const;
+
+ // Applies the data roaming policy.
+ void ApplyRoamingSetting(bool new_value) const;
+
+ // Applies any changes of the policies that are not handled by the respective
+ // subsystems.
+ 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
+ // settings. In this case revert to defaults and let people in until the owner
+ // 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();
+
+ // 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_;
+
+ 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_;
+
+ 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::vector<PendingQueueElement> pending_changes_;
+
+ friend class DeviceSettingsProviderTest;
+ FRIEND_TEST_ALL_PREFIXES(DeviceSettingsProviderTest,
+ InitializationTestUnowned);
+ FRIEND_TEST_ALL_PREFIXES(DeviceSettingsProviderTest,
+ PolicyFailedPermanentlyNotification);
+ FRIEND_TEST_ALL_PREFIXES(DeviceSettingsProviderTest, PolicyLoadNotification);
+ DISALLOW_COPY_AND_ASSIGN(DeviceSettingsProvider);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_SETTINGS_PROVIDER_H_
diff --git a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
new file mode 100644
index 0000000..f8abd165
--- /dev/null
+++ b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
@@ -0,0 +1,257 @@
+// 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_provider.h"
+
+#include <string>
+
+#include "base/bind.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/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"
+#include "chrome/test/base/testing_pref_service.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+namespace chromeos {
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::SaveArg;
+
+class DeviceSettingsProviderTest: public testing::Test {
+public:
+ MOCK_METHOD1(SettingChanged, void(const std::string&));
+ MOCK_METHOD0(GetTrustedCallback, void(void));
+
+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)) {
+ }
+
+ virtual ~DeviceSettingsProviderTest() {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ PrepareEmptyPolicy();
+
+ EXPECT_CALL(*this, SettingChanged(_))
+ .Times(AnyNumber());
+
+ 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)));
+
+ EXPECT_CALL(*mock_user_manager_.user_manager(), IsCurrentUserOwner())
+ .WillRepeatedly(Return(true));
+
+ provider_.reset(
+ new DeviceSettingsProvider(
+ base::Bind(&DeviceSettingsProviderTest::SettingChanged,
+ base::Unretained(this)),
+ &signed_settings_helper_));
+ provider_->set_ownership_status(OwnershipService::OWNERSHIP_TAKEN);
+ // To prevent flooding the logs.
+ provider_->set_retries_left(1);
+ provider_->Reload();
+ }
+
+ 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_;
+
+ ScopedTestingLocalState local_state_;
+
+ MockSignedSettingsHelper signed_settings_helper_;
+
+ ScopedStubCrosEnabler stub_cros_enabler_;
+ ScopedMockUserManagerEnabler mock_user_manager_;
+};
+
+TEST_F(DeviceSettingsProviderTest, InitializationTest) {
+ // 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::Bind(&DeviceSettingsProviderTest::GetTrustedCallback,
+ base::Unretained(this))));
+ const base::Value* value = provider_->Get(kStatsReportingPref);
+ ASSERT_TRUE(value);
+ bool bool_value;
+ EXPECT_TRUE(value->GetAsBoolean(&bool_value));
+ EXPECT_FALSE(bool_value);
+}
+
+TEST_F(DeviceSettingsProviderTest, InitializationTestUnowned) {
+ // 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::Bind(&DeviceSettingsProviderTest::GetTrustedCallback,
+ base::Unretained(this))));
+ const base::Value* value = provider_->Get(kReleaseChannel);
+ ASSERT_TRUE(value);
+ std::string string_value;
+ EXPECT_TRUE(value->GetAsString(&string_value));
+ EXPECT_TRUE(string_value.empty());
+
+ // Sets should succeed though and be readable from the cache.
+ base::StringValue new_value("stable-channel");
+ provider_->Set(kReleaseChannel, new_value);
+ // 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);
+}
+
+TEST_F(DeviceSettingsProviderTest, SetPrefFailed) {
+ // If we are not the owner no sets should work.
+ EXPECT_CALL(*mock_user_manager_.user_manager(), IsCurrentUserOwner())
+ .WillOnce(Return(false));
+ base::FundamentalValue value(true);
+ provider_->Set(kStatsReportingPref, value);
+ // Verify the change has not been applied.
+ const base::Value* saved_value = provider_->Get(kStatsReportingPref);
+ ASSERT_TRUE(saved_value);
+ bool bool_value;
+ EXPECT_TRUE(saved_value->GetAsBoolean(&bool_value));
+ EXPECT_FALSE(bool_value);
+}
+
+TEST_F(DeviceSettingsProviderTest, SetPrefSucceed) {
+ base::FundamentalValue value(true);
+ provider_->Set(kStatsReportingPref, value);
+ // Verify the change has not been applied.
+ const base::Value* saved_value = provider_->Get(kStatsReportingPref);
+ ASSERT_TRUE(saved_value);
+ bool bool_value;
+ EXPECT_TRUE(saved_value->GetAsBoolean(&bool_value));
+ EXPECT_TRUE(bool_value);
+}
+
+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::Bind(&DeviceSettingsProviderTest::GetTrustedCallback,
+ base::Unretained(this))));
+}
+
+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::Bind(&DeviceSettingsProviderTest::GetTrustedCallback,
+ base::Unretained(this))));
+}
+
+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))));
+ 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))));
+ provider_->Reload();
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/mock_owner_key_utils.cc b/chrome/browser/chromeos/settings/mock_owner_key_utils.cc
new file mode 100644
index 0000000..55ed5d4
--- /dev/null
+++ b/chrome/browser/chromeos/settings/mock_owner_key_utils.cc
@@ -0,0 +1,20 @@
+// 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/mock_owner_key_utils.h"
+
+namespace chromeos {
+
+MockKeyUtils::MockKeyUtils() {}
+
+MockKeyUtils::~MockKeyUtils() {}
+
+MockInjector::MockInjector(MockKeyUtils* mock) : transient_(mock) {}
+
+MockInjector::~MockInjector() {}
+
+OwnerKeyUtils* MockInjector::CreateOwnerKeyUtils() {
+ return transient_.get();
+}
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/mock_owner_key_utils.h b/chrome/browser/chromeos/settings/mock_owner_key_utils.h
new file mode 100644
index 0000000..1a55179
--- /dev/null
+++ b/chrome/browser/chromeos/settings/mock_owner_key_utils.h
@@ -0,0 +1,61 @@
+// 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_MOCK_OWNER_KEY_UTILS_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_MOCK_OWNER_KEY_UTILS_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "chrome/browser/chromeos/settings/owner_key_utils.h"
+#include "crypto/rsa_private_key.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+class MockKeyUtils : public OwnerKeyUtils {
+ public:
+ MockKeyUtils();
+
+ MOCK_METHOD2(ImportPublicKey, bool(const FilePath& key_file,
+ std::vector<uint8>* output));
+ MOCK_METHOD3(Verify, bool(const std::string& data,
+ const std::vector<uint8> signature,
+ const std::vector<uint8> public_key));
+ MOCK_METHOD3(Sign, bool(const std::string& data,
+ std::vector<uint8>* OUT_signature,
+ crypto::RSAPrivateKey* key));
+ MOCK_METHOD1(FindPrivateKey,
+ crypto::RSAPrivateKey*(const std::vector<uint8>& key));
+ MOCK_METHOD0(GetOwnerKeyFilePath, FilePath());
+ MOCK_METHOD2(ExportPublicKeyToFile, bool(crypto::RSAPrivateKey* pair,
+ const FilePath& key_file));
+ protected:
+ virtual ~MockKeyUtils();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockKeyUtils);
+};
+
+class MockInjector : public OwnerKeyUtils::Factory {
+ public:
+ // Takes ownership of |mock|.
+ explicit MockInjector(MockKeyUtils* mock);
+ virtual ~MockInjector();
+
+ // If this is called, its caller takes ownership of |transient_|.
+ // If it's never called, |transient_| remains our problem.
+ virtual OwnerKeyUtils* CreateOwnerKeyUtils() OVERRIDE;
+
+ private:
+ scoped_refptr<MockKeyUtils> transient_;
+ DISALLOW_COPY_AND_ASSIGN(MockInjector);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_MOCK_OWNER_KEY_UTILS_H_
diff --git a/chrome/browser/chromeos/settings/mock_ownership_service.cc b/chrome/browser/chromeos/settings/mock_ownership_service.cc
new file mode 100644
index 0000000..6af5061
--- /dev/null
+++ b/chrome/browser/chromeos/settings/mock_ownership_service.cc
@@ -0,0 +1,13 @@
+// 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/mock_ownership_service.h"
+
+namespace chromeos {
+
+MockOwnershipService::MockOwnershipService() {}
+
+MockOwnershipService::~MockOwnershipService() {}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/mock_ownership_service.h b/chrome/browser/chromeos/settings/mock_ownership_service.h
new file mode 100644
index 0000000..df8c17f
--- /dev/null
+++ b/chrome/browser/chromeos/settings/mock_ownership_service.h
@@ -0,0 +1,33 @@
+// 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_MOCK_OWNERSHIP_SERVICE_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_MOCK_OWNERSHIP_SERVICE_H_
+
+#include <string>
+
+#include "chrome/browser/chromeos/settings/ownership_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace chromeos {
+
+class MockOwnershipService : public OwnershipService {
+ public:
+ MockOwnershipService();
+ virtual ~MockOwnershipService();
+
+ MOCK_METHOD0(IsAlreadyOwned, bool(void));
+ MOCK_METHOD1(GetStatus, OwnershipService::Status(bool));
+ MOCK_METHOD0(StartLoadOwnerKeyAttempt, void(void));
+ MOCK_METHOD2(StartSigningAttempt, void(const std::string&,
+ OwnerManager::Delegate*));
+ MOCK_METHOD3(StartVerifyAttempt, void(const std::string&,
+ const std::vector<uint8>&,
+ OwnerManager::Delegate*));
+ MOCK_METHOD0(CurrentUserIsOwner, bool(void));
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_MOCK_OWNERSHIP_SERVICE_H_
diff --git a/chrome/browser/chromeos/settings/mock_signed_settings_helper.cc b/chrome/browser/chromeos/settings/mock_signed_settings_helper.cc
new file mode 100644
index 0000000..2386b6d
--- /dev/null
+++ b/chrome/browser/chromeos/settings/mock_signed_settings_helper.cc
@@ -0,0 +1,17 @@
+// 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/mock_signed_settings_helper.h"
+
+#include "chrome/browser/policy/proto/device_management_backend.pb.h"
+
+namespace chromeos {
+
+MockSignedSettingsHelper::MockSignedSettingsHelper() {
+}
+
+MockSignedSettingsHelper::~MockSignedSettingsHelper() {
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/mock_signed_settings_helper.h b/chrome/browser/chromeos/settings/mock_signed_settings_helper.h
new file mode 100644
index 0000000..aad2f5a
--- /dev/null
+++ b/chrome/browser/chromeos/settings/mock_signed_settings_helper.h
@@ -0,0 +1,39 @@
+// 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_MOCK_SIGNED_SETTINGS_HELPER_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_MOCK_SIGNED_SETTINGS_HELPER_H_
+
+#include "base/basictypes.h"
+#include "chrome/browser/chromeos/settings/signed_settings_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace chromeos {
+
+class MockSignedSettingsHelper : public chromeos::SignedSettingsHelper {
+ public:
+ MockSignedSettingsHelper();
+ virtual ~MockSignedSettingsHelper();
+
+ MOCK_METHOD2(StartStorePolicyOp,
+ void(const enterprise_management::PolicyFetchResponse&,
+ SignedSettingsHelper::StorePolicyCallback));
+ MOCK_METHOD1(StartRetrievePolicyOp,
+ void(SignedSettingsHelper::RetrievePolicyCallback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockSignedSettingsHelper);
+};
+
+ACTION_P(MockSignedSettingsHelperStorePolicy, status_code) {
+ arg1.Run(status_code);
+}
+
+ACTION_P2(MockSignedSettingsHelperRetrievePolicy, status_code, policy) {
+ arg0.Run(status_code, policy);
+}
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_MOCK_SIGNED_SETTINGS_HELPER_H_
diff --git a/chrome/browser/chromeos/settings/owner_key_utils.cc b/chrome/browser/chromeos/settings/owner_key_utils.cc
new file mode 100644
index 0000000..e211877
--- /dev/null
+++ b/chrome/browser/chromeos/settings/owner_key_utils.cc
@@ -0,0 +1,175 @@
+// 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/owner_key_utils.h"
+
+#include <limits>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/browser/chromeos/cros/cros_library.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/signature_creator.h"
+#include "crypto/signature_verifier.h"
+
+using extension_misc::kSignatureAlgorithm;
+
+namespace chromeos {
+
+///////////////////////////////////////////////////////////////////////////
+// OwnerKeyUtils
+
+// static
+OwnerKeyUtils::Factory* OwnerKeyUtils::factory_ = NULL;
+
+OwnerKeyUtils::OwnerKeyUtils() {}
+
+OwnerKeyUtils::~OwnerKeyUtils() {}
+
+///////////////////////////////////////////////////////////////////////////
+// OwnerKeyUtilsImpl
+
+class OwnerKeyUtilsImpl : public OwnerKeyUtils {
+ public:
+ OwnerKeyUtilsImpl();
+
+ bool ImportPublicKey(const FilePath& key_file,
+ std::vector<uint8>* output);
+
+ bool Verify(const std::string& data,
+ const std::vector<uint8> signature,
+ const std::vector<uint8> public_key);
+
+ bool Sign(const std::string& data,
+ std::vector<uint8>* OUT_signature,
+ crypto::RSAPrivateKey* key);
+
+ crypto::RSAPrivateKey* FindPrivateKey(const std::vector<uint8>& key);
+
+ FilePath GetOwnerKeyFilePath();
+
+ protected:
+ virtual ~OwnerKeyUtilsImpl();
+
+ bool ExportPublicKeyToFile(crypto::RSAPrivateKey* pair,
+ const FilePath& key_file);
+
+ private:
+ // The file outside the owner's encrypted home directory where her
+ // key will live.
+ static const char kOwnerKeyFile[];
+
+ DISALLOW_COPY_AND_ASSIGN(OwnerKeyUtilsImpl);
+};
+
+// Defined here, instead of up above, because we need OwnerKeyUtilsImpl.
+OwnerKeyUtils* OwnerKeyUtils::Create() {
+ if (!factory_)
+ return new OwnerKeyUtilsImpl();
+ else
+ return factory_->CreateOwnerKeyUtils();
+}
+
+// static
+const char OwnerKeyUtilsImpl::kOwnerKeyFile[] = "/var/lib/whitelist/owner.key";
+
+OwnerKeyUtilsImpl::OwnerKeyUtilsImpl() {}
+
+OwnerKeyUtilsImpl::~OwnerKeyUtilsImpl() {}
+
+bool OwnerKeyUtilsImpl::ExportPublicKeyToFile(crypto::RSAPrivateKey* pair,
+ const FilePath& key_file) {
+ DCHECK(pair);
+ bool ok = false;
+ int safe_file_size = 0;
+
+ std::vector<uint8> to_export;
+ if (!pair->ExportPublicKey(&to_export)) {
+ LOG(ERROR) << "Formatting key for export failed!";
+ return false;
+ }
+
+ if (to_export.size() > static_cast<uint>(INT_MAX)) {
+ LOG(ERROR) << "key is too big! " << to_export.size();
+ } else {
+ safe_file_size = static_cast<int>(to_export.size());
+
+ ok = (safe_file_size ==
+ file_util::WriteFile(key_file,
+ reinterpret_cast<char*>(&to_export.front()),
+ safe_file_size));
+ }
+ return ok;
+}
+
+bool OwnerKeyUtilsImpl::ImportPublicKey(const FilePath& key_file,
+ std::vector<uint8>* output) {
+ // Get the file size (must fit in a 32 bit int for NSS).
+ int64 file_size;
+ if (!file_util::GetFileSize(key_file, &file_size)) {
+ LOG(ERROR) << "Could not get size of " << key_file.value();
+ return false;
+ }
+ if (file_size > static_cast<int64>(std::numeric_limits<int>::max())) {
+ LOG(ERROR) << key_file.value() << "is "
+ << file_size << "bytes!!! Too big!";
+ return false;
+ }
+ int32 safe_file_size = static_cast<int32>(file_size);
+
+ output->resize(safe_file_size);
+
+ if (safe_file_size == 0) {
+ LOG(WARNING) << "Public key file is empty. This seems wrong.";
+ return false;
+ }
+
+ // Get the key data off of disk
+ int data_read = file_util::ReadFile(key_file,
+ reinterpret_cast<char*>(&(output->at(0))),
+ safe_file_size);
+ return data_read == safe_file_size;
+}
+
+bool OwnerKeyUtilsImpl::Verify(const std::string& data,
+ const std::vector<uint8> signature,
+ const std::vector<uint8> public_key) {
+ crypto::SignatureVerifier verifier;
+ if (!verifier.VerifyInit(kSignatureAlgorithm, sizeof(kSignatureAlgorithm),
+ &signature[0], signature.size(),
+ &public_key[0], public_key.size())) {
+ return false;
+ }
+
+ verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
+ data.length());
+ return (verifier.VerifyFinal());
+}
+
+bool OwnerKeyUtilsImpl::Sign(const std::string& data,
+ std::vector<uint8>* OUT_signature,
+ crypto::RSAPrivateKey* key) {
+ scoped_ptr<crypto::SignatureCreator> signer(
+ crypto::SignatureCreator::Create(key));
+ if (!signer->Update(reinterpret_cast<const uint8*>(data.c_str()),
+ data.length())) {
+ return false;
+ }
+ return signer->Final(OUT_signature);
+}
+
+crypto::RSAPrivateKey* OwnerKeyUtilsImpl::FindPrivateKey(
+ const std::vector<uint8>& key) {
+ return crypto::RSAPrivateKey::FindFromPublicKeyInfo(key);
+}
+
+FilePath OwnerKeyUtilsImpl::GetOwnerKeyFilePath() {
+ return FilePath(OwnerKeyUtilsImpl::kOwnerKeyFile);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/owner_key_utils.h b/chrome/browser/chromeos/settings/owner_key_utils.h
new file mode 100644
index 0000000..1c9dde5
--- /dev/null
+++ b/chrome/browser/chromeos/settings/owner_key_utils.h
@@ -0,0 +1,90 @@
+// 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_OWNER_KEY_UTILS_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_OWNER_KEY_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+
+class FilePath;
+
+namespace crypto {
+class RSAPrivateKey;
+}
+
+namespace chromeos {
+
+class OwnerKeyUtilsTest;
+
+class OwnerKeyUtils : public base::RefCounted<OwnerKeyUtils> {
+ public:
+ class Factory {
+ public:
+ virtual OwnerKeyUtils* CreateOwnerKeyUtils() = 0;
+ };
+
+ OwnerKeyUtils();
+
+ // Sets the factory used by the static method Create to create an
+ // OwnerKeyUtils. OwnerKeyUtils does not take ownership of
+ // |factory|. A value of NULL results in an OwnerKeyUtils being
+ // created directly.
+#if defined(UNIT_TEST)
+ static void set_factory(Factory* factory) { factory_ = factory; }
+#endif
+
+ // Creates an OwnerKeyUtils, ownership returns to the caller. If there is no
+ // Factory (the default) this creates and returns a new OwnerKeyUtils.
+ static OwnerKeyUtils* Create();
+
+ // Assumes that the file at |key_file| exists.
+ // Upon success, returns true and populates |output|. False on failure.
+ virtual bool ImportPublicKey(const FilePath& key_file,
+ std::vector<uint8>* output) = 0;
+
+ // Verfiy that |signature| is a Sha1-with-RSA signature over |data| with
+ // |public_key|
+ // Returns true if so, false on bad signature or other error.
+ virtual bool Verify(const std::string& data,
+ const std::vector<uint8> signature,
+ const std::vector<uint8> public_key) = 0;
+
+ // Sign |data| with |key| using Sha1 with RSA. If successful, return true
+ // and populate |OUT_signature|.
+ virtual bool Sign(const std::string& data,
+ std::vector<uint8>* OUT_signature,
+ crypto::RSAPrivateKey* key) = 0;
+
+ // Looks for the private key associated with |key| in the default slot,
+ // and returns it if it can be found. Returns NULL otherwise.
+ // Caller takes ownership.
+ virtual crypto::RSAPrivateKey* FindPrivateKey(
+ const std::vector<uint8>& key) = 0;
+
+ virtual FilePath GetOwnerKeyFilePath() = 0;
+
+ protected:
+ virtual ~OwnerKeyUtils();
+
+ // DER encodes public half of |pair| and writes it out to |key_file|.
+ // The blob on disk is a DER-encoded X509 SubjectPublicKeyInfo object.
+ // Returns false on error.
+ virtual bool ExportPublicKeyToFile(crypto::RSAPrivateKey* pair,
+ const FilePath& key_file) = 0;
+
+ private:
+ friend class base::RefCounted<OwnerKeyUtils>;
+ static Factory* factory_;
+
+ FRIEND_TEST_ALL_PREFIXES(OwnerKeyUtilsTest, ExportImportPublicKey);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_OWNER_KEY_UTILS_H_
diff --git a/chrome/browser/chromeos/settings/owner_key_utils_unittest.cc b/chrome/browser/chromeos/settings/owner_key_utils_unittest.cc
new file mode 100644
index 0000000..0632b6b
--- /dev/null
+++ b/chrome/browser/chromeos/settings/owner_key_utils_unittest.cc
@@ -0,0 +1,84 @@
+// 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/owner_key_utils.h"
+
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/scoped_temp_dir.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "crypto/rsa_private_key.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+class OwnerKeyUtilsTest : public ::testing::Test {
+ public:
+ OwnerKeyUtilsTest() : utils_(OwnerKeyUtils::Create()) {}
+ virtual ~OwnerKeyUtilsTest() {}
+
+ virtual void SetUp() {
+ crypto::OpenPersistentNSSDB();
+ }
+
+ // Key generation parameters.
+ static const uint16 kKeySizeInBits;
+
+ scoped_refptr<OwnerKeyUtils> utils_;
+};
+
+// We're generating and using 2048-bit RSA keys.
+// static
+const uint16 OwnerKeyUtilsTest::kKeySizeInBits = 2048;
+
+TEST_F(OwnerKeyUtilsTest, ExportImportPublicKey) {
+ scoped_ptr<crypto::RSAPrivateKey> pair(
+ crypto::RSAPrivateKey::CreateSensitive(kKeySizeInBits));
+ ASSERT_NE(pair.get(), reinterpret_cast<crypto::RSAPrivateKey*>(NULL));
+
+ // Export public key to file.
+ ScopedTempDir tmpdir;
+ FilePath tmpfile;
+ ASSERT_TRUE(tmpdir.CreateUniqueTempDir());
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(tmpdir.path(), &tmpfile));
+ ASSERT_TRUE(utils_->ExportPublicKeyToFile(pair.get(), tmpfile));
+
+ // Export public key, so that we can compare it to the one we get off disk.
+ std::vector<uint8> public_key;
+ ASSERT_TRUE(pair->ExportPublicKey(&public_key));
+ std::vector<uint8> from_disk;
+ ASSERT_TRUE(utils_->ImportPublicKey(tmpfile, &from_disk));
+
+ std::vector<uint8>::iterator pubkey_it;
+ std::vector<uint8>::iterator disk_it;
+ for (pubkey_it = public_key.begin(), disk_it = from_disk.begin();
+ pubkey_it < public_key.end();
+ pubkey_it++, disk_it++) {
+ EXPECT_EQ(*pubkey_it, *disk_it);
+ }
+}
+
+TEST_F(OwnerKeyUtilsTest, ImportPublicKeyFailed) {
+ ScopedTempDir tmpdir;
+ FilePath tmpfile;
+ ASSERT_TRUE(tmpdir.CreateUniqueTempDir());
+
+ // First test the case where the file is missing which should fail.
+ std::vector<uint8> from_disk;
+ ASSERT_FALSE(utils_->ImportPublicKey(tmpfile, &from_disk));
+
+ // Next try empty file. This should fail and the array should be empty.
+ from_disk.resize(10);
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(tmpdir.path(), &tmpfile));
+ ASSERT_FALSE(utils_->ImportPublicKey(tmpfile, &from_disk));
+ ASSERT_FALSE(from_disk.size());
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/owner_manager.cc b/chrome/browser/chromeos/settings/owner_manager.cc
new file mode 100644
index 0000000..385af76
--- /dev/null
+++ b/chrome/browser/chromeos/settings/owner_manager.cc
@@ -0,0 +1,147 @@
+// 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/owner_manager.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/boot_times_loader.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"
+
+using content::BrowserThread;
+
+namespace chromeos {
+
+OwnerManager::OwnerManager()
+ : private_key_(NULL),
+ public_key_(0),
+ utils_(OwnerKeyUtils::Create()) {
+}
+
+void OwnerManager::UpdateOwnerKey(const BrowserThread::ID thread_id,
+ const std::vector<uint8>& key,
+ KeyUpdateDelegate* d) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ public_key_ = key;
+
+ BrowserThread::PostTask(
+ thread_id, FROM_HERE,
+ base::Bind(&OwnerManager::CallKeyUpdateDelegate, this, d));
+}
+
+void OwnerManager::LoadOwnerKey() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ VLOG(1) << "Loading owner key";
+ int result = chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED;
+
+ // If |public_key_| isn't empty, we already have the key, so don't
+ // try to import again.
+ if (public_key_.empty() &&
+ !utils_->ImportPublicKey(utils_->GetOwnerKeyFilePath(), &public_key_)) {
+ result = chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_FAILED;
+ }
+
+ // Whether we loaded the public key or not, send a notification indicating
+ // that we're done with this attempt.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&OwnerManager::SendNotification, this, result,
+ content::NotificationService::NoDetails()));
+}
+
+bool OwnerManager::EnsurePublicKey() {
+ if (public_key_.empty())
+ LoadOwnerKey();
+
+ return !public_key_.empty();
+}
+
+bool OwnerManager::EnsurePrivateKey() {
+ if (!EnsurePublicKey())
+ return false;
+
+ if (!private_key_.get())
+ private_key_.reset(utils_->FindPrivateKey(public_key_));
+
+ return private_key_.get() != NULL;
+}
+
+void OwnerManager::Sign(const BrowserThread::ID thread_id,
+ const std::string& data,
+ Delegate* d) {
+ BootTimesLoader::Get()->AddLoginTimeMarker("SignStart", false);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ // If it's not the case that we can get both keys...
+ if (!(EnsurePublicKey() && EnsurePrivateKey())) {
+ BrowserThread::PostTask(
+ thread_id, FROM_HERE,
+ base::Bind(&OwnerManager::CallDelegate, this, d, KEY_UNAVAILABLE,
+ std::vector<uint8>()));
+ BootTimesLoader::Get()->AddLoginTimeMarker("SignEnd", false);
+ return;
+ }
+
+ VLOG(1) << "Starting signing attempt";
+ KeyOpCode return_code = SUCCESS;
+ std::vector<uint8> signature;
+ if (!utils_->Sign(data, &signature, private_key_.get())) {
+ return_code = OPERATION_FAILED;
+ }
+
+ BrowserThread::PostTask(
+ thread_id, FROM_HERE,
+ base::Bind(&OwnerManager::CallDelegate, this, d, return_code, signature));
+ BootTimesLoader::Get()->AddLoginTimeMarker("SignEnd", false);
+}
+
+void OwnerManager::Verify(const BrowserThread::ID thread_id,
+ const std::string& data,
+ const std::vector<uint8>& signature,
+ Delegate* d) {
+ BootTimesLoader::Get()->AddLoginTimeMarker("VerifyStart", false);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ if (!EnsurePublicKey()) {
+ BrowserThread::PostTask(
+ thread_id, FROM_HERE,
+ base::Bind(&OwnerManager::CallDelegate, this, d, KEY_UNAVAILABLE,
+ std::vector<uint8>()));
+ BootTimesLoader::Get()->AddLoginTimeMarker("VerifyEnd", false);
+ return;
+ }
+
+ VLOG(1) << "Starting verify attempt";
+ KeyOpCode return_code = SUCCESS;
+ if (!utils_->Verify(data, signature, public_key_)) {
+ return_code = OPERATION_FAILED;
+ }
+ BrowserThread::PostTask(
+ thread_id, FROM_HERE,
+ base::Bind(&OwnerManager::CallDelegate, this, d, return_code,
+ std::vector<uint8>()));
+ BootTimesLoader::Get()->AddLoginTimeMarker("VerifyEnd", false);
+}
+
+OwnerManager::~OwnerManager() {}
+
+void OwnerManager::SendNotification(
+ int type,
+ const content::NotificationDetails& details) {
+ content::NotificationService::current()->Notify(
+ type,
+ content::NotificationService::AllSources(),
+ details);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/owner_manager.h b/chrome/browser/chromeos/settings/owner_manager.h
new file mode 100644
index 0000000..d3c0ad5
--- /dev/null
+++ b/chrome/browser/chromeos/settings/owner_manager.h
@@ -0,0 +1,125 @@
+// 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_OWNER_MANAGER_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_OWNER_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/chromeos/settings/owner_key_utils.h"
+#include "content/public/browser/browser_thread.h"
+#include "crypto/rsa_private_key.h"
+
+namespace content {
+class NotificationDetails;
+}
+
+namespace chromeos {
+
+// This class allows the registration of an Owner of a Chromium OS device.
+// It handles generating the appropriate keys and storing them in the
+// appropriate locations.
+class OwnerManager : public base::RefCountedThreadSafe<OwnerManager> {
+ public:
+ // Return codes for public/private key operations.
+ enum KeyOpCode {
+ SUCCESS,
+ KEY_UNAVAILABLE, // The necessary key isn't available yet.
+ OPERATION_FAILED // The crypto operation failed.
+ };
+
+ class Delegate {
+ public:
+ // Upon completion of a key operation, this method will be called.
+ // |return_code| indicates what happened, |payload| will be used to pass
+ // back any artifacts of the operation. For example, if the operation
+ // was a signature attempt, the signature blob would come back in |payload|.
+ virtual void OnKeyOpComplete(const KeyOpCode return_code,
+ const std::vector<uint8>& payload) = 0;
+ };
+
+ class KeyUpdateDelegate {
+ public:
+ // Called upon completion of a key update operation.
+ virtual void OnKeyUpdated() = 0;
+ };
+
+ OwnerManager();
+
+ // Sets a new owner key from a provided memory buffer.
+ void UpdateOwnerKey(const content::BrowserThread::ID thread_id,
+ const std::vector<uint8>& key,
+ KeyUpdateDelegate* d);
+
+ // Pulls the owner's public key off disk and into memory.
+ //
+ // Call this on the FILE thread.
+ void LoadOwnerKey();
+
+ bool EnsurePublicKey();
+ bool EnsurePrivateKey();
+
+ // Do the actual work of signing |data| with |private_key_|. First,
+ // ensures that we have the keys we need. Then, computes the signature.
+ //
+ // On success, calls d->OnKeyOpComplete() on |thread_id| with a
+ // successful return code, passing the signaure blob in |payload|.
+ // On failure, calls d->OnKeyOpComplete() on |thread_id| with an appropriate
+ // error and passes an empty string for |payload|.
+ void Sign(const content::BrowserThread::ID thread_id,
+ const std::string& data,
+ Delegate* d);
+
+ // Do the actual work of verifying that |signature| is valid over
+ // |data| with |public_key_|. First, ensures we have the key we
+ // need, then does the verify.
+ //
+ // On success, calls d->OnKeyOpComplete() on |thread_id| with a
+ // successful return code, passing an empty string for |payload|.
+ // On failure, calls d->OnKeyOpComplete() on |thread_id| with an appropriate
+ // error code, passing an empty string for |payload|.
+ void Verify(const content::BrowserThread::ID thread_id,
+ const std::string& data,
+ const std::vector<uint8>& signature,
+ Delegate* d);
+
+ protected:
+ virtual ~OwnerManager();
+
+ private:
+ friend class base::RefCountedThreadSafe<OwnerManager>;
+
+ // A helper method to send a notification on another thread.
+ void SendNotification(int type,
+ const content::NotificationDetails& details);
+
+ // Calls back a key update delegate on a given thread.
+ void CallKeyUpdateDelegate(KeyUpdateDelegate* d) {
+ d->OnKeyUpdated();
+ }
+
+ // A helper method to call back a delegte on another thread.
+ void CallDelegate(Delegate* d,
+ const KeyOpCode return_code,
+ const std::vector<uint8>& payload) {
+ d->OnKeyOpComplete(return_code, payload);
+ }
+
+ scoped_ptr<crypto::RSAPrivateKey> private_key_;
+ std::vector<uint8> public_key_;
+
+ scoped_refptr<OwnerKeyUtils> utils_;
+
+ friend class OwnerManagerTest;
+
+ DISALLOW_COPY_AND_ASSIGN(OwnerManager);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_OWNER_MANAGER_H_
diff --git a/chrome/browser/chromeos/settings/owner_manager_unittest.cc b/chrome/browser/chromeos/settings/owner_manager_unittest.cc
new file mode 100644
index 0000000..d1893fd
--- /dev/null
+++ b/chrome/browser/chromeos/settings/owner_manager_unittest.cc
@@ -0,0 +1,347 @@
+// 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/owner_manager.h"
+#include "chrome/browser/chromeos/settings/owner_manager_unittest.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/scoped_temp_dir.h"
+#include "chrome/browser/chromeos/settings/mock_owner_key_utils.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/test_browser_thread.h"
+#include "crypto/nss_util.h"
+#include "crypto/rsa_private_key.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+using ::crypto::RSAPrivateKey;
+using ::testing::DoAll;
+using ::testing::Eq;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::_;
+
+namespace chromeos {
+
+////////////////////////////////////////////////////////////////////////////////
+// MockKeyLoadObserver
+MockKeyLoadObserver::MockKeyLoadObserver(base::WaitableEvent* e)
+ : success_expected_(false),
+ event_(e),
+ observed_(false) {
+ registrar_.Add(
+ this,
+ chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_FAILED,
+ content::NotificationService::AllSources());
+ registrar_.Add(
+ this,
+ chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED,
+ content::NotificationService::AllSources());
+}
+
+MockKeyLoadObserver::~MockKeyLoadObserver() {
+ DCHECK(observed_);
+}
+
+void MockKeyLoadObserver::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ LOG(INFO) << "Observed key fetch event";
+ if (type == chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) {
+ DCHECK(success_expected_);
+ observed_ = true;
+ if (event_)
+ event_->Signal();
+ } else if (type == chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_FAILED) {
+ DCHECK(!success_expected_);
+ observed_ = true;
+ if (event_)
+ event_->Signal();
+ }
+}
+
+void MockKeyLoadObserver::ExpectKeyFetchSuccess(bool should_succeed) {
+ success_expected_ = should_succeed;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// MockKeyUser
+MockKeyUser::MockKeyUser(const OwnerManager::KeyOpCode expected,
+ base::WaitableEvent* e)
+ : expected_(expected),
+ event_(e) {
+}
+
+void MockKeyUser::OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
+ const std::vector<uint8>& payload) {
+ DCHECK_EQ(expected_, return_code);
+ if (event_)
+ event_->Signal();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// MockKeyUpdateUser
+
+void MockKeyUpdateUser::OnKeyUpdated() {
+ if (event_)
+ event_->Signal();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// MockSigner
+
+MockSigner::MockSigner(const OwnerManager::KeyOpCode expected,
+ const std::vector<uint8>& sig,
+ base::WaitableEvent* e)
+ : expected_code_(expected),
+ expected_sig_(sig),
+ event_(e) {
+}
+
+MockSigner::~MockSigner() {}
+
+void MockSigner::OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
+ const std::vector<uint8>& payload) {
+ DCHECK_EQ(expected_code_, return_code);
+ for (uint32 i = 0; i < payload.size(); ++i)
+ DCHECK_EQ(expected_sig_[i], payload[i]);
+ if (event_)
+ event_->Signal();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// OwnerManagerTest
+
+class OwnerManagerTest : public testing::Test {
+ public:
+ OwnerManagerTest()
+ : message_loop_(MessageLoop::TYPE_UI),
+ ui_thread_(BrowserThread::UI, &message_loop_),
+ file_thread_(BrowserThread::FILE),
+ mock_(new MockKeyUtils),
+ injector_(mock_) /* injector_ takes ownership of mock_ */ {
+ }
+ virtual ~OwnerManagerTest() {}
+
+ virtual void SetUp() {
+ crypto::OpenPersistentNSSDB(); // TODO(cmasone): use test DB instead
+ fake_private_key_.reset(RSAPrivateKey::Create(256));
+ ASSERT_TRUE(fake_private_key_->ExportPublicKey(&fake_public_key_));
+
+ // Mimic ownership.
+ ASSERT_TRUE(tmpdir_.CreateUniqueTempDir());
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(tmpdir_.path(), &tmpfile_));
+
+ file_thread_.Start();
+ OwnerKeyUtils::set_factory(&injector_);
+ }
+
+ virtual void TearDown() {
+ OwnerKeyUtils::set_factory(NULL);
+ }
+
+ void StartUnowned() {
+ file_util::Delete(tmpfile_, false);
+ }
+
+ void InjectKeys(OwnerManager* manager) {
+ manager->public_key_ = fake_public_key_;
+ manager->private_key_.reset(fake_private_key_.release());
+ }
+
+ ScopedTempDir tmpdir_;
+ FilePath tmpfile_;
+
+ MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread file_thread_;
+
+ std::vector<uint8> fake_public_key_;
+ scoped_ptr<RSAPrivateKey> fake_private_key_;
+
+ MockKeyUtils* mock_;
+ MockInjector injector_;
+};
+
+TEST_F(OwnerManagerTest, UpdateOwnerKey) {
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+
+ base::WaitableEvent event(true, false);
+ MockKeyUpdateUser delegate(&event);
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnerManager::UpdateOwnerKey, manager.get(),
+ BrowserThread::UI, std::vector<uint8>(), &delegate));
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnerManagerTest, LoadOwnerKeyFail) {
+ StartUnowned();
+ base::WaitableEvent event(true, false);
+ MockKeyLoadObserver loader(&event);
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_, _))
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnerManager::LoadOwnerKey, manager.get()));
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnerManagerTest, AlreadyLoadedOwnerKey) {
+ base::WaitableEvent event(true, false);
+ MockKeyLoadObserver loader(&event);
+ loader.ExpectKeyFetchSuccess(true);
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+
+ InjectKeys(manager.get());
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnerManager::LoadOwnerKey, manager.get()));
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnerManagerTest, LoadOwnerKey) {
+ base::WaitableEvent event(true, false);
+ MockKeyLoadObserver loader(&event);
+ loader.ExpectKeyFetchSuccess(true);
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(fake_public_key_),
+ Return(true)))
+ .RetiresOnSaturation();
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnerManager::LoadOwnerKey, manager.get()));
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnerManagerTest, GetKeyFailDuringVerify) {
+ StartUnowned();
+ MockKeyLoadObserver loader(NULL);
+ loader.ExpectKeyFetchSuccess(false);
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_, _))
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+
+ base::WaitableEvent event(true, false);
+ MockKeyUser delegate(OwnerManager::KEY_UNAVAILABLE, &event);
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnerManager::Verify, manager.get(), BrowserThread::UI,
+ std::string(), std::vector<uint8>(), &delegate));
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnerManagerTest, AlreadyHaveKeysVerify) {
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+
+ std::string data;
+ std::vector<uint8> sig(0, 2);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, Verify(Eq(data), Eq(sig), Eq(fake_public_key_)))
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+
+ InjectKeys(manager.get());
+ base::WaitableEvent event(true, false);
+ MockKeyUser delegate(OwnerManager::SUCCESS, &event);
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnerManager::Verify, manager.get(), BrowserThread::UI, data,
+ sig, &delegate));
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnerManagerTest, GetKeyAndVerify) {
+ MockKeyLoadObserver loader(NULL);
+ loader.ExpectKeyFetchSuccess(true);
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+
+ std::string data;
+ std::vector<uint8> sig(0, 2);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(fake_public_key_),
+ Return(true)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_, Verify(Eq(data), Eq(sig), Eq(fake_public_key_)))
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+
+ base::WaitableEvent event(true, false);
+ MockKeyUser delegate(OwnerManager::SUCCESS, &event);
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnerManager::Verify, manager.get(), BrowserThread::UI, data,
+ sig, &delegate));
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnerManagerTest, AlreadyHaveKeysSign) {
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+
+ std::string data;
+ std::vector<uint8> sig(0, 2);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, Sign(Eq(data), _, Eq(fake_private_key_.get())))
+ .WillOnce(DoAll(SetArgumentPointee<1>(sig),
+ Return(true)))
+ .RetiresOnSaturation();
+
+ InjectKeys(manager.get());
+ base::WaitableEvent event(true, false);
+ MockSigner delegate(OwnerManager::SUCCESS, sig, &event);
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnerManager::Sign, manager.get(), BrowserThread::UI, data,
+ &delegate));
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/owner_manager_unittest.h b/chrome/browser/chromeos/settings/owner_manager_unittest.h
new file mode 100644
index 0000000..7377a76
--- /dev/null
+++ b/chrome/browser/chromeos/settings/owner_manager_unittest.h
@@ -0,0 +1,93 @@
+// 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_OWNER_MANAGER_UNITTEST_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_OWNER_MANAGER_UNITTEST_H_
+
+#include "chrome/browser/chromeos/settings/owner_manager.h"
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+namespace chromeos {
+class MockKeyLoadObserver : public content::NotificationObserver {
+ public:
+ explicit MockKeyLoadObserver(base::WaitableEvent* e);
+ virtual ~MockKeyLoadObserver();
+
+ // content::NotificationObserver implementation.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ void ExpectKeyFetchSuccess(bool should_succeed);
+
+ private:
+ content::NotificationRegistrar registrar_;
+ bool success_expected_;
+ base::WaitableEvent* event_;
+ bool observed_;
+ DISALLOW_COPY_AND_ASSIGN(MockKeyLoadObserver);
+};
+
+class MockKeyUser : public OwnerManager::Delegate {
+ public:
+ MockKeyUser(const OwnerManager::KeyOpCode expected, base::WaitableEvent* e);
+ virtual ~MockKeyUser() {}
+
+ virtual void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
+ const std::vector<uint8>& payload) OVERRIDE;
+
+ const OwnerManager::KeyOpCode expected_;
+ private:
+ base::WaitableEvent* event_;
+ DISALLOW_COPY_AND_ASSIGN(MockKeyUser);
+};
+
+class MockKeyUpdateUser : public OwnerManager::KeyUpdateDelegate {
+ public:
+ explicit MockKeyUpdateUser(base::WaitableEvent* e) : event_(e) {}
+ virtual ~MockKeyUpdateUser() {}
+
+ virtual void OnKeyUpdated() OVERRIDE;
+
+ private:
+ base::WaitableEvent* event_;
+ DISALLOW_COPY_AND_ASSIGN(MockKeyUpdateUser);
+};
+
+
+class MockSigner : public OwnerManager::Delegate {
+ public:
+ MockSigner(const OwnerManager::KeyOpCode expected,
+ const std::vector<uint8>& sig,
+ base::WaitableEvent* e);
+ virtual ~MockSigner();
+
+ virtual void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
+ const std::vector<uint8>& payload) OVERRIDE;
+
+ const OwnerManager::KeyOpCode expected_code_;
+ const std::vector<uint8> expected_sig_;
+
+ private:
+ base::WaitableEvent* event_;
+ DISALLOW_COPY_AND_ASSIGN(MockSigner);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_OWNER_MANAGER_UNITTEST_H_
diff --git a/chrome/browser/chromeos/settings/ownership_service.cc b/chrome/browser/chromeos/settings/ownership_service.cc
new file mode 100644
index 0000000..6343fa3
--- /dev/null
+++ b/chrome/browser/chromeos/settings/ownership_service.cc
@@ -0,0 +1,259 @@
+// 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/ownership_service.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/chrome_switches.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+
+using content::BrowserThread;
+
+namespace chromeos {
+
+namespace {
+
+typedef std::pair<OwnershipService::Status, bool> OwnershipStatusReturnType;
+
+// Makes the check for ownership on the FILE thread and stores the result in the
+// provided pointers.
+OwnershipStatusReturnType CheckStatusOnFileThread() {
+ bool current_user_is_owner;
+ OwnershipService::Status status;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ status = OwnershipService::GetSharedInstance()->IsAlreadyOwned() ?
+ OwnershipService::OWNERSHIP_TAKEN : OwnershipService::OWNERSHIP_NONE;
+ current_user_is_owner =
+ OwnershipService::GetSharedInstance()->IsCurrentUserOwner();
+ return OwnershipStatusReturnType(status, current_user_is_owner);
+}
+
+} // namespace
+
+static base::LazyInstance<OwnershipService> g_ownership_service =
+ LAZY_INSTANCE_INITIALIZER;
+
+// static
+OwnershipService* OwnershipService::GetSharedInstance() {
+ return g_ownership_service.Pointer();
+}
+
+OwnershipService::OwnershipService()
+ : manager_(new OwnerManager),
+ utils_(OwnerKeyUtils::Create()),
+ ownership_status_(OWNERSHIP_UNKNOWN),
+ force_ownership_(CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kStubCrosSettings)) {
+ notification_registrar_.Add(
+ this,
+ chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED,
+ content::NotificationService::AllSources());
+}
+
+OwnershipService::~OwnershipService() {}
+
+void OwnershipService::Prewarm() {
+ // Note that we cannot prewarm in constructor because in current codebase
+ // object is created before spawning threads.
+ if (g_ownership_service == this) {
+ // Start getting ownership status.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnershipService::FetchStatus, base::Unretained(this)));
+ } else {
+ // This can happen only for particular test: OwnershipServiceTest. It uses
+ // mocks and for that uses OwnershipService not as a regular singleton but
+ // as a resurrecting object. This behaviour conflicts with
+ // base::Unretained(). So avoid posting task in those circumstances
+ // in order to avoid accessing already deleted object.
+ }
+}
+
+bool OwnershipService::IsAlreadyOwned() {
+ return file_util::PathExists(utils_->GetOwnerKeyFilePath());
+}
+
+OwnershipService::Status OwnershipService::GetStatus(bool blocking) {
+ if (force_ownership_)
+ return OWNERSHIP_TAKEN;
+ Status status = OWNERSHIP_UNKNOWN;
+ bool is_owned = false;
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ ownership_status_lock_.Acquire();
+ status = ownership_status_;
+ ownership_status_lock_.Release();
+ if (status != OWNERSHIP_UNKNOWN || !blocking)
+ return status;
+ // Under common usage there is very short lapse of time when ownership
+ // status is still unknown after constructing OwnershipService.
+ LOG(ERROR) << "Blocking on UI thread in OwnershipService::GetStatus";
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ is_owned = IsAlreadyOwned();
+ } else {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ is_owned = IsAlreadyOwned();
+ }
+ status = is_owned ? OWNERSHIP_TAKEN : OWNERSHIP_NONE;
+ SetStatus(status);
+ return status;
+}
+
+void OwnershipService::StartLoadOwnerKeyAttempt() {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&TryLoadOwnerKeyAttempt, base::Unretained(this)));
+}
+
+void OwnershipService::StartUpdateOwnerKey(const std::vector<uint8>& new_key,
+ OwnerManager::KeyUpdateDelegate* d) {
+ BrowserThread::ID thread_id;
+ if (!BrowserThread::GetCurrentThreadIdentifier(&thread_id))
+ thread_id = BrowserThread::UI;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnershipService::UpdateOwnerKey, base::Unretained(this),
+ thread_id, new_key, d));
+ return;
+}
+
+void OwnershipService::StartSigningAttempt(const std::string& data,
+ OwnerManager::Delegate* d) {
+ BrowserThread::ID thread_id;
+ if (!BrowserThread::GetCurrentThreadIdentifier(&thread_id))
+ thread_id = BrowserThread::UI;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnershipService::TrySigningAttempt, base::Unretained(this),
+ thread_id, data, d));
+ return;
+}
+
+void OwnershipService::StartVerifyAttempt(const std::string& data,
+ const std::vector<uint8>& signature,
+ OwnerManager::Delegate* d) {
+ BrowserThread::ID thread_id;
+ if (!BrowserThread::GetCurrentThreadIdentifier(&thread_id))
+ thread_id = BrowserThread::UI;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OwnershipService::TryVerifyAttempt, base::Unretained(this),
+ thread_id, data, signature, d));
+ return;
+}
+
+void OwnershipService::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (type == chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) {
+ SetStatus(OWNERSHIP_TAKEN);
+ notification_registrar_.RemoveAll();
+ } else {
+ NOTREACHED();
+ }
+}
+
+bool OwnershipService::IsCurrentUserOwner() {
+ if (force_ownership_)
+ return true;
+ // If this user has the private key associated with the owner's
+ // public key, this user is the owner.
+ return IsAlreadyOwned() && manager_->EnsurePrivateKey();
+}
+
+void OwnershipService::GetStatusAsync(const Callback& callback) {
+ PostTaskAndReplyWithResult(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
+ FROM_HERE,
+ base::Bind(&CheckStatusOnFileThread),
+ base::Bind(&OwnershipService::ReturnStatus,
+ callback));
+}
+
+
+// static
+void OwnershipService::UpdateOwnerKey(OwnershipService* service,
+ const BrowserThread::ID thread_id,
+ const std::vector<uint8>& new_key,
+ OwnerManager::KeyUpdateDelegate* d) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ service->manager()->UpdateOwnerKey(thread_id, new_key, d);
+}
+
+// static
+void OwnershipService::TryLoadOwnerKeyAttempt(OwnershipService* service) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (!service->IsAlreadyOwned()) {
+ VLOG(1) << "Device not yet owned";
+ return;
+ }
+ service->manager()->LoadOwnerKey();
+}
+
+// static
+void OwnershipService::TrySigningAttempt(OwnershipService* service,
+ const BrowserThread::ID thread_id,
+ const std::string& data,
+ OwnerManager::Delegate* d) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (!service->IsAlreadyOwned()) {
+ LOG(ERROR) << "Device not yet owned";
+ BrowserThread::PostTask(
+ thread_id, FROM_HERE,
+ base::Bind(&OwnershipService::FailAttempt, d));
+ return;
+ }
+ service->manager()->Sign(thread_id, data, d);
+}
+
+// static
+void OwnershipService::TryVerifyAttempt(OwnershipService* service,
+ const BrowserThread::ID thread_id,
+ const std::string& data,
+ const std::vector<uint8>& signature,
+ OwnerManager::Delegate* d) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (!service->IsAlreadyOwned()) {
+ LOG(ERROR) << "Device not yet owned";
+ BrowserThread::PostTask(
+ thread_id, FROM_HERE,
+ base::Bind(&OwnershipService::FailAttempt, d));
+ return;
+ }
+ service->manager()->Verify(thread_id, data, signature, d);
+}
+
+// static
+void OwnershipService::FailAttempt(OwnerManager::Delegate* d) {
+ d->OnKeyOpComplete(OwnerManager::KEY_UNAVAILABLE, std::vector<uint8>());
+}
+
+void OwnershipService::FetchStatus() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ Status status = IsAlreadyOwned() ? OWNERSHIP_TAKEN : OWNERSHIP_NONE;
+ SetStatus(status);
+}
+
+void OwnershipService::SetStatus(Status new_status) {
+ DCHECK(new_status == OWNERSHIP_TAKEN || new_status == OWNERSHIP_NONE);
+ base::AutoLock lk(ownership_status_lock_);
+ ownership_status_ = new_status;
+}
+
+// static
+void OwnershipService::ReturnStatus(const Callback& callback,
+ OwnershipStatusReturnType status) {
+ callback.Run(status.first, status.second);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/ownership_service.h b/chrome/browser/chromeos/settings/ownership_service.h
new file mode 100644
index 0000000..13a68d5
--- /dev/null
+++ b/chrome/browser/chromeos/settings/ownership_service.h
@@ -0,0 +1,150 @@
+// 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_OWNERSHIP_SERVICE_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_OWNERSHIP_SERVICE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/chromeos/settings/owner_key_utils.h"
+#include "chrome/browser/chromeos/settings/owner_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+namespace base {
+template <typename T> struct DefaultLazyInstanceTraits;
+}
+
+namespace chromeos {
+
+class OwnershipService : public content::NotificationObserver {
+ public:
+ enum Status {
+ // Listed in upgrade order.
+ OWNERSHIP_UNKNOWN = 0,
+ OWNERSHIP_NONE,
+ OWNERSHIP_TAKEN
+ };
+
+ // Callback function type. The status code is guaranteed to be different from
+ // OWNERSHIP_UNKNOWN. The bool parameter is true iff the currently logged in
+ // user is the owner.
+ typedef base::Callback<void(OwnershipService::Status, bool)> Callback;
+
+ // Returns the singleton instance of the OwnershipService.
+ static OwnershipService* GetSharedInstance();
+ virtual ~OwnershipService();
+
+ // Called after FILE thread is created to prefetch ownership status and avoid
+ // blocking on UI thread.
+ void Prewarm();
+
+ // Sets a new owner key. This will _not_ load the key material from disk, but
+ // rather update Chrome's in-memory copy of the key. |callback| will be
+ // invoked once the operation completes.
+ virtual void StartUpdateOwnerKey(const std::vector<uint8>& new_key,
+ OwnerManager::KeyUpdateDelegate* d);
+
+ // If the device has been owned already, posts a task to the FILE thread to
+ // fetch the public key off disk.
+ //
+ // Sends out a OWNER_KEY_FETCH_ATTEMPT_SUCCESS notification on success,
+ // OWNER_KEY_FETCH_ATTEMPT_FAILED on failure.
+ virtual void StartLoadOwnerKeyAttempt();
+
+ // Initiate an attempt to sign |data| with |private_key_|. Will call
+ // d->OnKeyOpComplete() when done. Upon success, the signature will be passed
+ // as the |payload| argument to d->OnKeyOpComplete().
+ //
+ // If you call this on a well-known thread, you'll be called back on that
+ // thread. Otherwise, you'll get called back on the UI thread.
+ virtual void StartSigningAttempt(const std::string& data,
+ OwnerManager::Delegate* d);
+
+ // Initiate an attempt to verify that |signature| is valid over |data| with
+ // |public_key_|. When the attempt is completed, an appropriate KeyOpCode
+ // will be passed to d->OnKeyOpComplete().
+ //
+ // If you call this on a well-known thread, you'll be called back on that
+ // thread. Otherwise, you'll get called back on the UI thread.
+ virtual void StartVerifyAttempt(const std::string& data,
+ const std::vector<uint8>& signature,
+ OwnerManager::Delegate* d);
+
+ // This method must be run on the FILE thread.
+ virtual bool IsCurrentUserOwner();
+
+ // This method should be run on FILE thread.
+ // Note: not static, for better mocking.
+ virtual bool IsAlreadyOwned();
+
+ // This method can be run either on FILE or UI threads. If |blocking| flag
+ // is specified then it is guaranteed to return either OWNERSHIP_NONE or
+ // OWNERSHIP_TAKEN (and not OWNERSHIP_UNKNOWN), however in this case it may
+ // occasionally block doing i/o.
+ virtual Status GetStatus(bool blocking);
+
+ // Determines the ownership status on the FILE thread and calls the |callback|
+ // with the result.
+ virtual void GetStatusAsync(const Callback& callback);
+
+ protected:
+ OwnershipService();
+
+ // content::NotificationObserver implementation.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<OwnershipService>;
+ friend class OwnershipServiceTest;
+
+ // Task posted on FILE thread on startup to prefetch ownership status.
+ void FetchStatus();
+
+ // Sets ownership status. May be called on either thread.
+ void SetStatus(Status new_status);
+
+ // Used by |CheckOwnershipAsync| to call the callback with the result.
+ static void ReturnStatus(const Callback& callback,
+ std::pair<OwnershipService::Status, bool> status);
+
+ static void UpdateOwnerKey(OwnershipService* service,
+ const content::BrowserThread::ID thread_id,
+ const std::vector<uint8>& new_key,
+ OwnerManager::KeyUpdateDelegate* d);
+ static void TryLoadOwnerKeyAttempt(OwnershipService* service);
+ static void TrySigningAttempt(OwnershipService* service,
+ const content::BrowserThread::ID thread_id,
+ const std::string& data,
+ OwnerManager::Delegate* d);
+ static void TryVerifyAttempt(OwnershipService* service,
+ const content::BrowserThread::ID thread_id,
+ const std::string& data,
+ const std::vector<uint8>& signature,
+ OwnerManager::Delegate* d);
+ static void FailAttempt(OwnerManager::Delegate* d);
+
+ OwnerManager* manager() { return manager_.get(); }
+
+ scoped_refptr<OwnerManager> manager_;
+ scoped_refptr<OwnerKeyUtils> utils_;
+ content::NotificationRegistrar notification_registrar_;
+ volatile Status ownership_status_;
+ base::Lock ownership_status_lock_;
+
+ // If true, current user is regarded as owner (for testing only).
+ bool force_ownership_;
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_OWNERSHIP_SERVICE_H_
diff --git a/chrome/browser/chromeos/settings/ownership_service_unittest.cc b/chrome/browser/chromeos/settings/ownership_service_unittest.cc
new file mode 100644
index 0000000..6a88d9f
--- /dev/null
+++ b/chrome/browser/chromeos/settings/ownership_service_unittest.cc
@@ -0,0 +1,238 @@
+// 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/ownership_service.h"
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/scoped_temp_dir.h"
+#include "chrome/browser/chromeos/settings/mock_owner_key_utils.h"
+#include "chrome/browser/chromeos/settings/owner_manager_unittest.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/test_browser_thread.h"
+#include "crypto/nss_util.h"
+#include "crypto/rsa_private_key.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+using ::crypto::RSAPrivateKey;
+using ::testing::DoAll;
+using ::testing::Eq;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::_;
+
+
+namespace chromeos {
+
+class OwnershipServiceTest : public testing::Test {
+ public:
+ OwnershipServiceTest()
+ : message_loop_(MessageLoop::TYPE_UI),
+ ui_thread_(BrowserThread::UI, &message_loop_),
+ file_thread_(BrowserThread::FILE),
+ mock_(new MockKeyUtils),
+ injector_(mock_) /* injector_ takes ownership of mock_ */ {
+ }
+ virtual ~OwnershipServiceTest() {}
+
+ virtual void SetUp() {
+ crypto::OpenPersistentNSSDB(); // TODO(cmasone): use test DB instead
+ fake_private_key_.reset(RSAPrivateKey::Create(256));
+ ASSERT_TRUE(fake_private_key_->ExportPublicKey(&fake_public_key_));
+
+ // Mimic ownership.
+ ASSERT_TRUE(tmpdir_.CreateUniqueTempDir());
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(tmpdir_.path(), &tmpfile_));
+
+ file_thread_.Start();
+ OwnerKeyUtils::set_factory(&injector_);
+ service_.reset(new OwnershipService); // must happen AFTER set_factory().
+ service_->Prewarm();
+ }
+
+ virtual void TearDown() {
+ OwnerKeyUtils::set_factory(NULL);
+ service_.reset(NULL);
+ }
+
+ void StartUnowned() {
+ file_util::Delete(tmpfile_, false);
+ }
+
+ ScopedTempDir tmpdir_;
+ FilePath tmpfile_;
+
+ MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread file_thread_;
+
+ std::vector<uint8> fake_public_key_;
+ scoped_ptr<RSAPrivateKey> fake_private_key_;
+
+ MockKeyUtils* mock_;
+ MockInjector injector_;
+ scoped_ptr<OwnershipService> service_;
+};
+
+TEST_F(OwnershipServiceTest, IsOwned) {
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_TRUE(service_->IsAlreadyOwned());
+}
+
+TEST_F(OwnershipServiceTest, IsOwnershipTaken) {
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_TRUE(service_->GetStatus(true) == OwnershipService::OWNERSHIP_TAKEN);
+}
+
+TEST_F(OwnershipServiceTest, IsUnowned) {
+ StartUnowned();
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_FALSE(service_->IsAlreadyOwned());
+}
+
+TEST_F(OwnershipServiceTest, IsOwnershipNone) {
+ StartUnowned();
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_TRUE(service_->GetStatus(true) == OwnershipService::OWNERSHIP_NONE);
+}
+
+TEST_F(OwnershipServiceTest, LoadOwnerKeyFail) {
+ base::WaitableEvent event(true, false);
+ MockKeyLoadObserver loader(&event);
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_, _))
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+
+ service_->StartLoadOwnerKeyAttempt();
+
+ // Run remaining events, until ExportPublicKeyViaDbus().
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnershipServiceTest, UpdateOwnerKey) {
+ base::WaitableEvent event(true, false);
+ MockKeyUpdateUser delegate(&event);
+ service_->StartUpdateOwnerKey(std::vector<uint8>(), &delegate);
+
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnershipServiceTest, LoadOwnerKey) {
+ base::WaitableEvent event(true, false);
+ MockKeyLoadObserver loader(&event);
+ loader.ExpectKeyFetchSuccess(true);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(fake_public_key_),
+ Return(true)))
+ .RetiresOnSaturation();
+ service_->StartLoadOwnerKeyAttempt();
+
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnershipServiceTest, NotYetOwnedVerify) {
+ StartUnowned();
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+
+ base::WaitableEvent event(true, false);
+ MockKeyUser delegate(OwnerManager::KEY_UNAVAILABLE, &event);
+ service_->StartVerifyAttempt("", std::vector<uint8>(), &delegate);
+
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnershipServiceTest, GetKeyFailDuringVerify) {
+ MockKeyLoadObserver loader(NULL);
+ loader.ExpectKeyFetchSuccess(false);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_, _))
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+
+ base::WaitableEvent event(true, false);
+ MockKeyUser delegate(OwnerManager::KEY_UNAVAILABLE, &event);
+ service_->StartVerifyAttempt("", std::vector<uint8>(), &delegate);
+
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnershipServiceTest, GetKeyAndVerify) {
+ MockKeyLoadObserver loader(NULL);
+ loader.ExpectKeyFetchSuccess(true);
+
+ std::string data;
+ std::vector<uint8> sig(0, 2);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(fake_public_key_),
+ Return(true)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_, Verify(Eq(data), Eq(sig), Eq(fake_public_key_)))
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+
+ base::WaitableEvent event(true, false);
+ MockKeyUser delegate(OwnerManager::SUCCESS, &event);
+ service_->StartVerifyAttempt(data, sig, &delegate);
+
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OwnershipServiceTest, GetKeyAndFailVerify) {
+ MockKeyLoadObserver loader(NULL);
+ loader.ExpectKeyFetchSuccess(true);
+
+ std::string data;
+ std::vector<uint8> sig(0, 2);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(fake_public_key_),
+ Return(true)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_, Verify(Eq(data), Eq(sig), Eq(fake_public_key_)))
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+
+ base::WaitableEvent event(true, false);
+ MockKeyUser delegate(OwnerManager::OPERATION_FAILED, &event);
+ service_->StartVerifyAttempt(data, sig, &delegate);
+
+ while (!event.IsSignaled())
+ message_loop_.RunAllPending();
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/session_manager_observer.cc b/chrome/browser/chromeos/settings/session_manager_observer.cc
new file mode 100644
index 0000000..6bb828c
--- /dev/null
+++ b/chrome/browser/chromeos/settings/session_manager_observer.cc
@@ -0,0 +1,81 @@
+// 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/session_manager_observer.h"
+
+#include "chrome/browser/browser_process.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"
+#include "content/public/browser/notification_service.h"
+
+namespace chromeos {
+
+namespace {
+
+class StubDelegate
+ : public SignedSettings::Delegate<
+ const enterprise_management::PolicyFetchResponse&> {
+ public:
+ StubDelegate() : policy_fetcher_(NULL) {}
+ virtual ~StubDelegate() {}
+
+ void set_fetcher(SignedSettings* fetcher) { policy_fetcher_ = fetcher; }
+ SignedSettings* fetcher() { return policy_fetcher_.get(); }
+
+ // Implementation of SignedSettings::Delegate
+ virtual void OnSettingsOpCompleted(
+ SignedSettings::ReturnCode code,
+ const enterprise_management::PolicyFetchResponse& value) {
+ VLOG(1) << "Done Fetching Policy";
+ delete this;
+ }
+
+ private:
+ scoped_refptr<SignedSettings> policy_fetcher_;
+ DISALLOW_COPY_AND_ASSIGN(StubDelegate);
+};
+
+} // namespace
+
+SessionManagerObserver::SessionManagerObserver() {
+ DBusThreadManager::Get()->GetSessionManagerClient()->AddObserver(this);
+}
+
+SessionManagerObserver::~SessionManagerObserver() {
+ DBusThreadManager::Get()->GetSessionManagerClient()->RemoveObserver(this);
+}
+
+void SessionManagerObserver::OwnerKeySet(bool success) {
+ VLOG(1) << "Owner key generation: " << (success ? "success" : "fail");
+ int result =
+ chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED;
+ if (!success)
+ result = chrome::NOTIFICATION_OWNER_KEY_FETCH_ATTEMPT_FAILED;
+
+ // We stored some settings in transient storage before owner was assigned.
+ // 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())
+ 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.
+ content::NotificationService::current()->Notify(
+ result,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+}
+
+void SessionManagerObserver::PropertyChangeComplete(bool success) {
+ if (success) {
+ StubDelegate* stub = new StubDelegate(); // Manages its own lifetime.
+ stub->set_fetcher(SignedSettings::CreateRetrievePolicyOp(stub));
+ stub->fetcher()->Execute();
+ }
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/session_manager_observer.h b/chrome/browser/chromeos/settings/session_manager_observer.h
new file mode 100644
index 0000000..b1e3adb
--- /dev/null
+++ b/chrome/browser/chromeos/settings/session_manager_observer.h
@@ -0,0 +1,32 @@
+// 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_SESSION_MANAGER_OBSERVER_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_SESSION_MANAGER_OBSERVER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chromeos/dbus/session_manager_client.h"
+
+namespace chromeos {
+
+// SessionManagerObserver is used to take actions per signals sent from the
+// session manager.
+class SessionManagerObserver : public SessionManagerClient::Observer {
+ public:
+ SessionManagerObserver();
+ virtual ~SessionManagerObserver();
+
+ private:
+ // SessionManagerClient::Observer override.
+ virtual void OwnerKeySet(bool success) OVERRIDE;
+ // SessionManagerClient::Observer override.
+ virtual void PropertyChangeComplete(bool success) OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionManagerObserver);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_SESSION_MANAGER_OBSERVER_H_
diff --git a/chrome/browser/chromeos/settings/signed_settings.cc b/chrome/browser/chromeos/settings/signed_settings.cc
new file mode 100644
index 0000000..9cdb826
--- /dev/null
+++ b/chrome/browser/chromeos/settings/signed_settings.cc
@@ -0,0 +1,290 @@
+// 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.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/cros/cros_library.h"
+#include "chrome/browser/chromeos/login/authenticator.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 "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/session_manager_client.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace em = enterprise_management;
+
+namespace chromeos {
+using content::BrowserThread;
+
+const char kDevicePolicyType[] = "google/chromeos/device";
+
+SignedSettings::SignedSettings()
+ : service_(OwnershipService::GetSharedInstance()) {
+}
+
+SignedSettings::~SignedSettings() {}
+
+// static
+bool SignedSettings::PolicyIsSane(const em::PolicyFetchResponse& value,
+ em::PolicyData* poldata) {
+ if (value.has_policy_data()) {
+ poldata->ParseFromString(value.policy_data());
+ if (poldata->has_policy_type() &&
+ poldata->policy_type() == kDevicePolicyType &&
+ poldata->has_policy_value()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// static
+SignedSettings::ReturnCode SignedSettings::MapKeyOpCode(
+ OwnerManager::KeyOpCode return_code) {
+ return (return_code == OwnerManager::KEY_UNAVAILABLE ?
+ KEY_UNAVAILABLE : BAD_SIGNATURE);
+}
+
+class StorePolicyOp : public SignedSettings {
+ public:
+ StorePolicyOp(em::PolicyFetchResponse* policy,
+ SignedSettings::Delegate<bool>* d);
+ void Succeed(bool value);
+ // Implementation of OwnerManager::Delegate
+ virtual void Execute() OVERRIDE;
+ virtual void Fail(SignedSettings::ReturnCode code) OVERRIDE;
+ virtual void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
+ const std::vector<uint8>& payload) OVERRIDE;
+
+ protected:
+ virtual ~StorePolicyOp();
+
+ private:
+ void RequestStorePolicy();
+
+ void OnBoolComplete(bool success);
+ // Always call d_->OnSettingOpCompleted() via this call.
+ // It guarantees that the callback will not be triggered until _after_
+ // Execute() returns, which is implicitly assumed by SignedSettingsHelper
+ // in some cases.
+ void PerformCallback(SignedSettings::ReturnCode code, bool value);
+
+ em::PolicyFetchResponse* policy_;
+ SignedSettings::Delegate<bool>* d_;
+};
+
+class RetrievePolicyOp : public SignedSettings {
+ public:
+ explicit RetrievePolicyOp(
+ SignedSettings::Delegate<const em::PolicyFetchResponse&>* d);
+ void Succeed(const em::PolicyFetchResponse& value);
+ // Implementation of OwnerManager::Delegate
+ virtual void Execute() OVERRIDE;
+ virtual void Fail(SignedSettings::ReturnCode code) OVERRIDE;
+ virtual void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
+ const std::vector<uint8>& payload) OVERRIDE;
+
+ protected:
+ virtual ~RetrievePolicyOp();
+
+ private:
+ void OnStringComplete(const std::string& serialized_proto);
+ // Always call d_->OnSettingOpCompleted() via this call.
+ // It guarantees that the callback will not be triggered until _after_
+ // Execute() returns, which is implicitly assumed by SignedSettingsHelper
+ // in some cases.
+ void PerformCallback(SignedSettings::ReturnCode code,
+ const em::PolicyFetchResponse& value);
+
+ void ProcessPolicy(const std::string& serialized_proto);
+
+ em::PolicyFetchResponse policy_;
+ SignedSettings::Delegate<const em::PolicyFetchResponse&>* d_;
+};
+
+// static
+SignedSettings* SignedSettings::CreateStorePolicyOp(
+ em::PolicyFetchResponse* policy,
+ SignedSettings::Delegate<bool>* d) {
+ DCHECK(d != NULL);
+ DCHECK(policy != NULL);
+ return new StorePolicyOp(policy, d);
+}
+
+// static
+SignedSettings* SignedSettings::CreateRetrievePolicyOp(
+ SignedSettings::Delegate<const em::PolicyFetchResponse&>* d) {
+ DCHECK(d != NULL);
+ return new RetrievePolicyOp(d);
+}
+
+
+StorePolicyOp::StorePolicyOp(em::PolicyFetchResponse* policy,
+ SignedSettings::Delegate<bool>* d)
+ : policy_(policy),
+ d_(d) {
+}
+
+void StorePolicyOp::Succeed(bool ignored) {
+ SignedSettings::ReturnCode code = SUCCESS;
+ bool to_ret = true;
+ em::PolicyData poldata;
+ if (SignedSettings::PolicyIsSane(*policy_, &poldata)) {
+ } else {
+ code = NOT_FOUND;
+ to_ret = false;
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&StorePolicyOp::PerformCallback, this, code, to_ret));
+}
+
+void StorePolicyOp::Execute() {
+ // get protobuf contents to sign
+ if (!policy_->has_policy_data())
+ Fail(OPERATION_FAILED);
+ else if (!policy_->has_policy_data_signature())
+ service_->StartSigningAttempt(policy_->policy_data(), this);
+ else
+ RequestStorePolicy();
+}
+
+void StorePolicyOp::Fail(SignedSettings::ReturnCode code) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&StorePolicyOp::PerformCallback, this, code, false));
+}
+
+void StorePolicyOp::OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
+ const std::vector<uint8>& payload) {
+ // Ensure we're on the UI thread, due to the need to send DBus traffic.
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&StorePolicyOp::OnKeyOpComplete, this, return_code,
+ payload));
+ return;
+ }
+ VLOG(2) << "StorePolicyOp::OnKeyOpComplete return_code = " << return_code;
+ // Now, sure we're on the UI thread.
+ if (return_code == OwnerManager::SUCCESS) {
+ policy_->set_policy_data_signature(std::string(payload.begin(),
+ payload.end()));
+ RequestStorePolicy();
+ return;
+ }
+ Fail(SignedSettings::MapKeyOpCode(return_code));
+}
+
+StorePolicyOp::~StorePolicyOp() {}
+
+void StorePolicyOp::RequestStorePolicy() {
+ std::string serialized;
+ if (policy_->SerializeToString(&serialized)) {
+ DBusThreadManager::Get()->GetSessionManagerClient()->StoreDevicePolicy(
+ serialized,
+ base::Bind(&StorePolicyOp::OnBoolComplete, this));
+ } else {
+ Fail(OPERATION_FAILED);
+ }
+}
+
+void StorePolicyOp::OnBoolComplete(bool success) {
+ if (success)
+ Succeed(true);
+ else
+ Fail(NOT_FOUND);
+}
+
+void StorePolicyOp::PerformCallback(SignedSettings::ReturnCode code,
+ bool value) {
+ d_->OnSettingsOpCompleted(code, value);
+}
+
+RetrievePolicyOp::RetrievePolicyOp(
+ SignedSettings::Delegate<const em::PolicyFetchResponse&>* d)
+ : d_(d) {
+}
+
+void RetrievePolicyOp::Succeed(const em::PolicyFetchResponse& value) {
+ em::PolicyData poldata;
+ if (SignedSettings::PolicyIsSane(value, &poldata)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&RetrievePolicyOp::PerformCallback, this, SUCCESS, value));
+ } else {
+ Fail(NOT_FOUND);
+ }
+}
+
+void RetrievePolicyOp::Execute() {
+ DBusThreadManager::Get()->GetSessionManagerClient()->RetrieveDevicePolicy(
+ base::Bind(&RetrievePolicyOp::OnStringComplete, this));
+}
+
+void RetrievePolicyOp::Fail(SignedSettings::ReturnCode code) {
+ VLOG(2) << "RetrievePolicyOp::Execute() failed with " << code;
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&RetrievePolicyOp::PerformCallback, this, code,
+ em::PolicyFetchResponse()));
+}
+
+void RetrievePolicyOp::OnKeyOpComplete(
+ const OwnerManager::KeyOpCode return_code,
+ const std::vector<uint8>& payload) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&RetrievePolicyOp::OnKeyOpComplete, this, return_code,
+ payload));
+ return;
+ }
+ // Now, sure we're on the UI thread.
+ if (return_code == OwnerManager::SUCCESS)
+ Succeed(policy_);
+ else
+ Fail(SignedSettings::MapKeyOpCode(return_code));
+}
+
+RetrievePolicyOp::~RetrievePolicyOp() {}
+
+void RetrievePolicyOp::OnStringComplete(const std::string& serialized_proto) {
+ ProcessPolicy(serialized_proto);
+}
+
+void RetrievePolicyOp::ProcessPolicy(const std::string& serialized_proto) {
+ if (serialized_proto.empty() || !policy_.ParseFromString(serialized_proto) ||
+ (!policy_.has_policy_data() && !policy_.has_policy_data_signature())) {
+ Fail(NOT_FOUND);
+ return;
+ }
+ if (!policy_.has_policy_data()) {
+ Fail(OPERATION_FAILED);
+ return;
+ }
+ if (!policy_.has_policy_data_signature()) {
+ Fail(BAD_SIGNATURE);
+ return;
+ }
+ std::vector<uint8> sig;
+ const char* sig_ptr = policy_.policy_data_signature().c_str();
+ sig.assign(sig_ptr, sig_ptr + policy_.policy_data_signature().length());
+ service_->StartVerifyAttempt(policy_.policy_data(), sig, this);
+}
+
+void RetrievePolicyOp::PerformCallback(SignedSettings::ReturnCode code,
+ const em::PolicyFetchResponse& value) {
+ d_->OnSettingsOpCompleted(code, value);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/signed_settings.h b/chrome/browser/chromeos/settings/signed_settings.h
new file mode 100644
index 0000000..17822e5
--- /dev/null
+++ b/chrome/browser/chromeos/settings/signed_settings.h
@@ -0,0 +1,95 @@
+// 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_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/chromeos/settings/owner_manager.h"
+
+// There are two operations that can be performed on the Chrome OS owner-signed
+// settings store: Storing and Retrieving the policy blob.
+//
+// The pattern of use here is that the caller instantiates some
+// subclass of SignedSettings by calling one of the create
+// methods. Then, call Execute() on this object from the UI
+// thread. It'll go off and do work (on the FILE thread and over DBus),
+// and then call the appropriate method of the Delegate you passed in
+// -- again, on the UI thread.
+
+namespace enterprise_management {
+class PolicyData;
+class PolicyFetchResponse;
+} // namespace enterprise_management
+
+namespace chromeos {
+class OwnershipService;
+
+extern const char kDevicePolicyType[];
+
+class SignedSettings : public base::RefCountedThreadSafe<SignedSettings>,
+ public OwnerManager::Delegate {
+ public:
+ enum ReturnCode {
+ SUCCESS,
+ NOT_FOUND, // Email address or property name not found.
+ KEY_UNAVAILABLE, // Owner key not yet configured.
+ OPERATION_FAILED, // IPC to signed settings daemon failed.
+ BAD_SIGNATURE // Signature verification failed.
+ };
+
+ template <class T>
+ class Delegate {
+ public:
+ // This method will be called on the UI thread.
+ virtual void OnSettingsOpCompleted(ReturnCode code, T value) {}
+ };
+
+ SignedSettings();
+
+ // These are both "policy" operations, and only one instance of
+ // one type can be in flight at a time.
+ static SignedSettings* CreateStorePolicyOp(
+ enterprise_management::PolicyFetchResponse* policy,
+ SignedSettings::Delegate<bool>* d);
+
+ static SignedSettings* CreateRetrievePolicyOp(
+ SignedSettings::Delegate<
+ const enterprise_management::PolicyFetchResponse&>* d);
+
+ static ReturnCode MapKeyOpCode(OwnerManager::KeyOpCode code);
+
+ virtual void Execute() = 0;
+
+ virtual void Fail(ReturnCode code) = 0;
+
+ // Implementation of OwnerManager::Delegate
+ virtual void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
+ const std::vector<uint8>& payload) = 0;
+
+ protected:
+ virtual ~SignedSettings();
+
+ static bool PolicyIsSane(
+ const enterprise_management::PolicyFetchResponse& value,
+ enterprise_management::PolicyData* poldata);
+
+ void set_service(OwnershipService* service) { service_ = service; }
+
+ OwnershipService* service_;
+
+ private:
+ friend class base::RefCountedThreadSafe<SignedSettings>;
+ friend class SignedSettingsTest;
+ friend class SignedSettingsHelperTest;
+
+ DISALLOW_COPY_AND_ASSIGN(SignedSettings);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_H_
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/signed_settings_cache.h b/chrome/browser/chromeos/settings/signed_settings_cache.h
new file mode 100644
index 0000000..ab9da59
--- /dev/null
+++ b/chrome/browser/chromeos/settings/signed_settings_cache.h
@@ -0,0 +1,39 @@
+// 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_CACHE_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_CACHE_H_
+
+namespace enterprise_management {
+class PolicyData;
+}
+
+class PrefService;
+
+namespace chromeos {
+
+// 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);
+
+// Stores a new policy blob inside the cache stored in |local_state|.
+bool Store(const enterprise_management::PolicyData &policy,
+ PrefService* local_state);
+
+// Retrieves the policy blob from the cache stored in |local_state|.
+bool Retrieve(enterprise_management::PolicyData *policy,
+ PrefService* local_state);
+
+// Call this after owner has been assigned to persist settings
+// into SignedSettings storage.
+void Finalize(PrefService* local_state);
+} // namespace signed_settings_cache
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_CACHE_H_
diff --git a/chrome/browser/chromeos/settings/signed_settings_cache_unittest.cc b/chrome/browser/chromeos/settings/signed_settings_cache_unittest.cc
new file mode 100644
index 0000000..de8bbc5
--- /dev/null
+++ b/chrome/browser/chromeos/settings/signed_settings_cache_unittest.cc
@@ -0,0 +1,57 @@
+// 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 "chrome/browser/policy/proto/chrome_device_policy.pb.h"
+#include "chrome/browser/policy/proto/device_management_backend.pb.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+namespace chromeos {
+
+class SignedSettingsCacheTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // prepare some data.
+ policy_.set_policy_type("google/chromeos/device");
+ em::ChromeDeviceSettingsProto pol;
+ pol.mutable_allow_new_users()->set_allow_new_users(false);
+ policy_.set_policy_value(pol.SerializeAsString());
+
+ signed_settings_cache::RegisterPrefs(&local_state_);
+ }
+
+ TestingPrefService local_state_;
+ em::PolicyData policy_;
+};
+
+TEST_F(SignedSettingsCacheTest, Basic) {
+ EXPECT_TRUE(signed_settings_cache::Store(policy_, &local_state_));
+
+ em::PolicyData policy_out;
+ EXPECT_TRUE(signed_settings_cache::Retrieve(&policy_out, &local_state_));
+
+ EXPECT_TRUE(policy_out.has_policy_type());
+ EXPECT_TRUE(policy_out.has_policy_value());
+
+ em::ChromeDeviceSettingsProto pol;
+ pol.ParseFromString(policy_out.policy_value());
+ EXPECT_TRUE(pol.has_allow_new_users());
+ EXPECT_FALSE(pol.allow_new_users().allow_new_users());
+}
+
+TEST_F(SignedSettingsCacheTest, CorruptData) {
+ EXPECT_TRUE(signed_settings_cache::Store(policy_, &local_state_));
+
+ local_state_.SetString(prefs::kSignedSettingsCache, "blaaa");
+
+ em::PolicyData policy_out;
+ EXPECT_FALSE(signed_settings_cache::Retrieve(&policy_out, &local_state_));
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/signed_settings_helper.cc b/chrome/browser/chromeos/settings/signed_settings_helper.cc
new file mode 100644
index 0000000..56486fa
--- /dev/null
+++ b/chrome/browser/chromeos/settings/signed_settings_helper.cc
@@ -0,0 +1,251 @@
+// 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_helper.h"
+
+#include <vector>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/policy/proto/device_management_backend.pb.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+namespace em = enterprise_management;
+
+namespace chromeos {
+
+namespace {
+
+class OpContext {
+ public:
+ class Delegate {
+ public:
+ virtual void OnOpCreated(OpContext* context) = 0;
+ virtual void OnOpStarted(OpContext* context) = 0;
+ virtual void OnOpCompleted(OpContext* context) = 0;
+ };
+
+ virtual ~OpContext() {}
+
+ // Creates and execute op.
+ void Execute() {
+ CreateOp();
+ CHECK(op_.get());
+ if (delegate_)
+ delegate_->OnOpCreated(this);
+
+ // Note that the context could be released when op_->Execute() returns.
+ // So keep a local copy of delegate and executing flag to use after
+ // the call.
+ Delegate* delegate = delegate_;
+ executing_ = true;
+ op_->Execute();
+ if (delegate)
+ delegate->OnOpStarted(this);
+ }
+
+ // Cancels the callback and cancels the op if it is not executing.
+ void Cancel() {
+ if (!executing_)
+ OnOpCompleted();
+ }
+
+ // Accessors.
+ SignedSettings* op() const {
+ return op_.get();
+ }
+
+ void set_delegate(Delegate* delegate) {
+ delegate_ = delegate;
+ }
+
+ protected:
+ explicit OpContext(Delegate* delegate)
+ : executing_(false),
+ delegate_(delegate) {
+ }
+
+ // Creates the op to execute.
+ virtual void CreateOp() = 0;
+
+ // Callback on op completion.
+ virtual void OnOpCompleted() {
+ if (delegate_)
+ delegate_->OnOpCompleted(this);
+
+ delete this;
+ }
+
+ bool executing_;
+ Delegate* delegate_;
+
+ scoped_refptr<SignedSettings> op_;
+};
+
+class StorePolicyOpContext
+ : public SignedSettings::Delegate<bool>,
+ public OpContext {
+ public:
+ StorePolicyOpContext(const em::PolicyFetchResponse& policy,
+ SignedSettingsHelper::StorePolicyCallback callback,
+ OpContext::Delegate* delegate)
+ : OpContext(delegate),
+ callback_(callback),
+ policy_(policy) {
+ }
+
+ // chromeos::SignedSettings::Delegate implementation
+ virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
+ bool unused) OVERRIDE {
+ VLOG(2) << "OnSettingsOpCompleted, code = " << code;
+ callback_.Run(code);
+ OnOpCompleted();
+ }
+
+ protected:
+ // OpContext implementation
+ virtual void CreateOp() OVERRIDE {
+ op_ = SignedSettings::CreateStorePolicyOp(&policy_, this);
+ }
+
+ private:
+ SignedSettingsHelper::StorePolicyCallback callback_;
+ em::PolicyFetchResponse policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(StorePolicyOpContext);
+};
+
+class RetrievePolicyOpContext
+ : public SignedSettings::Delegate<const em::PolicyFetchResponse&>,
+ public OpContext {
+ public:
+ RetrievePolicyOpContext(SignedSettingsHelper::RetrievePolicyCallback callback,
+ OpContext::Delegate* delegate)
+ : OpContext(delegate),
+ callback_(callback){
+ }
+
+ // chromeos::SignedSettings::Delegate implementation
+ virtual void OnSettingsOpCompleted(
+ SignedSettings::ReturnCode code,
+ const em::PolicyFetchResponse& policy) OVERRIDE {
+ callback_.Run(code, policy);
+ OnOpCompleted();
+ }
+
+ protected:
+ // OpContext implementation
+ virtual void CreateOp() OVERRIDE {
+ op_ = SignedSettings::CreateRetrievePolicyOp(this);
+ }
+
+ private:
+ SignedSettingsHelper::RetrievePolicyCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(RetrievePolicyOpContext);
+};
+
+} // namespace
+
+
+class SignedSettingsHelperImpl : public SignedSettingsHelper,
+ public OpContext::Delegate {
+ public:
+ // SignedSettingsHelper implementation
+ virtual void StartStorePolicyOp(const em::PolicyFetchResponse& policy,
+ StorePolicyCallback callback) OVERRIDE;
+ virtual void StartRetrievePolicyOp(RetrievePolicyCallback callback) OVERRIDE;
+
+ // OpContext::Delegate implementation
+ virtual void OnOpCreated(OpContext* context);
+ virtual void OnOpStarted(OpContext* context);
+ virtual void OnOpCompleted(OpContext* context);
+
+ private:
+ SignedSettingsHelperImpl();
+ virtual ~SignedSettingsHelperImpl();
+
+ void AddOpContext(OpContext* context);
+ void ClearAll();
+
+ std::vector<OpContext*> pending_contexts_;
+
+ friend struct base::DefaultLazyInstanceTraits<SignedSettingsHelperImpl>;
+ DISALLOW_COPY_AND_ASSIGN(SignedSettingsHelperImpl);
+};
+
+static base::LazyInstance<SignedSettingsHelperImpl>
+ g_signed_settings_helper_impl = LAZY_INSTANCE_INITIALIZER;
+
+SignedSettingsHelperImpl::SignedSettingsHelperImpl() {
+}
+
+SignedSettingsHelperImpl::~SignedSettingsHelperImpl() {
+ if (!pending_contexts_.empty()) {
+ LOG(WARNING) << "SignedSettingsHelperImpl shutdown with pending ops, "
+ << "changes will be lost.";
+ ClearAll();
+ }
+}
+
+void SignedSettingsHelperImpl::StartStorePolicyOp(
+ const em::PolicyFetchResponse& policy,
+ StorePolicyCallback callback) {
+ AddOpContext(new StorePolicyOpContext(policy, callback, this));
+}
+
+void SignedSettingsHelperImpl::StartRetrievePolicyOp(
+ RetrievePolicyCallback callback) {
+ AddOpContext(new RetrievePolicyOpContext(callback, this));
+}
+
+void SignedSettingsHelperImpl::AddOpContext(OpContext* context) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ CHECK(context);
+
+ pending_contexts_.push_back(context);
+ if (pending_contexts_.size() == 1)
+ context->Execute();
+}
+
+void SignedSettingsHelperImpl::ClearAll() {
+ for (size_t i = 0; i < pending_contexts_.size(); ++i) {
+ pending_contexts_[i]->set_delegate(NULL);
+ pending_contexts_[i]->Cancel();
+ }
+ pending_contexts_.clear();
+}
+
+void SignedSettingsHelperImpl::OnOpCreated(OpContext* context) {
+ if (test_delegate_)
+ test_delegate_->OnOpCreated(context->op());
+}
+
+void SignedSettingsHelperImpl::OnOpStarted(OpContext* context) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (test_delegate_)
+ test_delegate_->OnOpStarted(context->op());
+}
+
+void SignedSettingsHelperImpl::OnOpCompleted(OpContext* context) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(pending_contexts_.front() == context);
+
+ pending_contexts_.erase(pending_contexts_.begin());
+ if (!pending_contexts_.empty())
+ pending_contexts_.front()->Execute();
+
+ if (test_delegate_)
+ test_delegate_->OnOpCompleted(context->op());
+}
+
+SignedSettingsHelper* SignedSettingsHelper::Get() {
+ return g_signed_settings_helper_impl.Pointer();
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/signed_settings_helper.h b/chrome/browser/chromeos/settings/signed_settings_helper.h
new file mode 100644
index 0000000..466f45a
--- /dev/null
+++ b/chrome/browser/chromeos/settings/signed_settings_helper.h
@@ -0,0 +1,58 @@
+// 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_HELPER_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_HELPER_H_
+
+#include "chrome/browser/chromeos/settings/signed_settings.h"
+
+namespace enterprise_management {
+class PolicyFetchResponse;
+} // namespace enterprise_management
+
+namespace chromeos {
+
+// Helper to serialize signed settings ops, provide unified callback interface,
+// and handle callbacks destruction before ops completion.
+class SignedSettingsHelper {
+ public:
+ typedef base::Callback<void(SignedSettings::ReturnCode)> StorePolicyCallback;
+ typedef
+ base::Callback<void(SignedSettings::ReturnCode,
+ const enterprise_management::PolicyFetchResponse&)>
+ RetrievePolicyCallback;
+
+ // Class factory
+ static SignedSettingsHelper* Get();
+
+ // Functions to start signed settings ops.
+ virtual void StartStorePolicyOp(
+ const enterprise_management::PolicyFetchResponse& policy,
+ StorePolicyCallback callback) = 0;
+ virtual void StartRetrievePolicyOp(
+ RetrievePolicyCallback callback) = 0;
+
+ class TestDelegate {
+ public:
+ virtual void OnOpCreated(SignedSettings* op) = 0;
+ virtual void OnOpStarted(SignedSettings* op) = 0;
+ virtual void OnOpCompleted(SignedSettings* op) = 0;
+ };
+
+#if defined(UNIT_TEST)
+ void set_test_delegate(TestDelegate* test_delegate) {
+ test_delegate_ = test_delegate;
+ }
+#endif // defined(UNIT_TEST)
+
+ protected:
+ SignedSettingsHelper() : test_delegate_(NULL) {
+ }
+
+ TestDelegate* test_delegate_;
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_SIGNED_SETTINGS_HELPER_H_
diff --git a/chrome/browser/chromeos/settings/signed_settings_helper_unittest.cc b/chrome/browser/chromeos/settings/signed_settings_helper_unittest.cc
new file mode 100644
index 0000000..5a01f0d
--- /dev/null
+++ b/chrome/browser/chromeos/settings/signed_settings_helper_unittest.cc
@@ -0,0 +1,209 @@
+// 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_helper.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "chrome/browser/chromeos/cros/cros_library.h"
+#include "chrome/browser/chromeos/settings/cros_settings_names.h"
+#include "chrome/browser/chromeos/settings/mock_owner_key_utils.h"
+#include "chrome/browser/chromeos/settings/mock_ownership_service.h"
+#include "chrome/browser/chromeos/settings/owner_manager.h"
+#include "chrome/browser/chromeos/settings/signed_settings.h"
+#include "chrome/browser/policy/proto/chrome_device_policy.pb.h"
+#include "chrome/browser/policy/proto/device_management_backend.pb.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/mock_dbus_thread_manager.h"
+#include "chromeos/dbus/mock_session_manager_client.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::A;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::WithArg;
+
+namespace em = enterprise_management;
+namespace chromeos {
+
+ACTION_P(Retrieve, policy_blob) { arg0.Run(policy_blob); }
+ACTION_P(Store, success) { arg1.Run(success); }
+
+class SignedSettingsHelperTest : public testing::Test,
+ public SignedSettingsHelper::TestDelegate {
+ public:
+ SignedSettingsHelperTest()
+ : message_loop_(MessageLoop::TYPE_UI),
+ ui_thread_(content::BrowserThread::UI, &message_loop_),
+ file_thread_(content::BrowserThread::FILE, &message_loop_),
+ pending_ops_(0),
+ mock_dbus_thread_manager_(new MockDBusThreadManager) {
+ }
+
+ virtual void SetUp() {
+ SignedSettingsHelper::Get()->set_test_delegate(this);
+ DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager_);
+
+ fake_policy_data_ = BuildPolicyData();
+ std::string data_serialized = fake_policy_data_.SerializeAsString();
+ std::string serialized_policy_;
+ fake_policy_ = BuildProto(data_serialized,
+ std::string("false"),
+ &serialized_policy_);
+
+ MockSessionManagerClient* client =
+ mock_dbus_thread_manager_->mock_session_manager_client();
+ // Make sure the mocked out class calls back to notify success on store and
+ // retrieve ops.
+ EXPECT_CALL(*client, StoreDevicePolicy(_, _))
+ .WillRepeatedly(Store(true));
+ EXPECT_CALL(*client, RetrieveDevicePolicy(_))
+ .WillRepeatedly(Retrieve(serialized_policy_));
+
+ EXPECT_CALL(m_, StartSigningAttempt(_, A<OwnerManager::Delegate*>()))
+ .WillRepeatedly(WithArg<1>(
+ Invoke(&SignedSettingsHelperTest::OnKeyOpComplete)));
+ EXPECT_CALL(m_, StartVerifyAttempt(_, _, A<OwnerManager::Delegate*>()))
+ .WillRepeatedly(WithArg<2>(
+ Invoke(&SignedSettingsHelperTest::OnKeyOpComplete)));
+ }
+
+ virtual void TearDown() {
+ DBusThreadManager::Shutdown();
+ SignedSettingsHelper::Get()->set_test_delegate(NULL);
+ }
+
+ virtual void OnOpCreated(SignedSettings* op) {
+ // Use MockOwnershipService for all SignedSettings op.
+ op->set_service(&m_);
+ }
+
+ virtual void OnOpStarted(SignedSettings* op) {
+ }
+
+ virtual void OnOpCompleted(SignedSettings* op) {
+ --pending_ops_;
+ }
+
+ static void OnKeyOpComplete(OwnerManager::Delegate* op) {
+ op->OnKeyOpComplete(OwnerManager::SUCCESS, std::vector<uint8>());
+ }
+
+ em::PolicyData BuildPolicyData() {
+ em::PolicyData to_return;
+ em::ChromeDeviceSettingsProto pol;
+ to_return.set_policy_type(chromeos::kDevicePolicyType);
+ to_return.set_policy_value(pol.SerializeAsString());
+ return to_return;
+ }
+
+ em::PolicyFetchResponse BuildProto(const std::string& data,
+ const std::string& sig,
+ std::string* out_serialized) {
+ em::PolicyFetchResponse fake_policy;
+ if (!data.empty())
+ fake_policy.set_policy_data(data);
+ if (!sig.empty())
+ fake_policy.set_policy_data_signature(sig);
+ EXPECT_TRUE(fake_policy.SerializeToString(out_serialized));
+ return fake_policy;
+ }
+
+ em::PolicyData fake_policy_data_;
+ em::PolicyFetchResponse fake_policy_;
+ std::string serialized_policy_;
+ MockOwnershipService m_;
+
+ MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread file_thread_;
+
+ int pending_ops_;
+
+ MockDBusThreadManager* mock_dbus_thread_manager_;
+
+ ScopedStubCrosEnabler stub_cros_enabler_;
+};
+
+class SignedSettingsCallbacks {
+ public:
+ virtual ~SignedSettingsCallbacks() {}
+ // Callback of StorePolicyOp.
+ virtual void OnStorePolicyCompleted(SignedSettings::ReturnCode code) = 0;
+ // Callback of RetrievePolicyOp.
+ virtual void OnRetrievePolicyCompleted(
+ SignedSettings::ReturnCode code,
+ const em::PolicyFetchResponse& policy) = 0;
+};
+
+class MockSignedSettingsCallbacks
+ : public SignedSettingsCallbacks,
+ public base::SupportsWeakPtr<MockSignedSettingsCallbacks> {
+public:
+ virtual ~MockSignedSettingsCallbacks() {}
+
+ MOCK_METHOD1(OnStorePolicyCompleted, void(SignedSettings::ReturnCode));
+ MOCK_METHOD2(OnRetrievePolicyCompleted, void(SignedSettings::ReturnCode,
+ const em::PolicyFetchResponse&));
+};
+
+TEST_F(SignedSettingsHelperTest, SerializedOps) {
+ MockSignedSettingsCallbacks cb;
+
+ InSequence s;
+ EXPECT_CALL(cb, OnStorePolicyCompleted(SignedSettings::SUCCESS))
+ .Times(1);
+ EXPECT_CALL(cb, OnRetrievePolicyCompleted(SignedSettings::SUCCESS, _))
+ .Times(1);
+
+
+ pending_ops_ = 2;
+ SignedSettingsHelper::Get()->StartStorePolicyOp(
+ fake_policy_,
+ base::Bind(&MockSignedSettingsCallbacks::OnStorePolicyCompleted,
+ base::Unretained(&cb)));
+ SignedSettingsHelper::Get()->StartRetrievePolicyOp(
+ base::Bind(&MockSignedSettingsCallbacks::OnRetrievePolicyCompleted,
+ base::Unretained(&cb)));
+
+ message_loop_.RunAllPending();
+ ASSERT_EQ(0, pending_ops_);
+}
+
+TEST_F(SignedSettingsHelperTest, CanceledOps) {
+ MockSignedSettingsCallbacks cb;
+
+ InSequence s;
+ EXPECT_CALL(cb, OnStorePolicyCompleted(SignedSettings::SUCCESS))
+ .Times(1);
+ EXPECT_CALL(cb, OnRetrievePolicyCompleted(SignedSettings::SUCCESS, _))
+ .Times(1);
+
+ pending_ops_ = 3;
+
+ {
+ // This op will be deleted and never will be executed (expect only one call
+ // to OnRetrievePolicyCompleted above). However the OpComplete callback will
+ // be still called, therefore we expect three pending ops.
+ MockSignedSettingsCallbacks cb_to_be_deleted;
+ SignedSettingsHelper::Get()->StartRetrievePolicyOp(
+ base::Bind(&MockSignedSettingsCallbacks::OnRetrievePolicyCompleted,
+ cb_to_be_deleted.AsWeakPtr()));
+ }
+ SignedSettingsHelper::Get()->StartStorePolicyOp(
+ fake_policy_,
+ base::Bind(&MockSignedSettingsCallbacks::OnStorePolicyCompleted,
+ base::Unretained(&cb)));
+ SignedSettingsHelper::Get()->StartRetrievePolicyOp(
+ base::Bind(&MockSignedSettingsCallbacks::OnRetrievePolicyCompleted,
+ base::Unretained(&cb)));
+
+ message_loop_.RunAllPending();
+ ASSERT_EQ(0, pending_ops_);
+}
+
+} // 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/signed_settings_unittest.cc b/chrome/browser/chromeos/settings/signed_settings_unittest.cc
new file mode 100644
index 0000000..0765f91
--- /dev/null
+++ b/chrome/browser/chromeos/settings/signed_settings_unittest.cc
@@ -0,0 +1,398 @@
+// 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.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/cros/cros_library.h"
+#include "chrome/browser/chromeos/settings/cros_settings_names.h"
+#include "chrome/browser/chromeos/settings/mock_owner_key_utils.h"
+#include "chrome/browser/chromeos/settings/mock_ownership_service.h"
+#include "chrome/browser/chromeos/settings/owner_manager_unittest.h"
+#include "chrome/browser/policy/proto/chrome_device_policy.pb.h"
+#include "chrome/browser/policy/proto/device_management_backend.pb.h"
+#include "chromeos/dbus/mock_dbus_thread_manager.h"
+#include "chromeos/dbus/mock_session_manager_client.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::A;
+using ::testing::AnyNumber;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SaveArg;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::_;
+using content::BrowserThread;
+using google::protobuf::RepeatedPtrField;
+
+namespace em = enterprise_management;
+namespace chromeos {
+
+namespace {
+template <class T>
+class DummyDelegate : public SignedSettings::Delegate<T> {
+ public:
+ explicit DummyDelegate(T to_expect)
+ : expect_success_(false),
+ expected_failure_(SignedSettings::SUCCESS),
+ expected_(to_expect),
+ run_(false) {}
+ virtual ~DummyDelegate() { EXPECT_TRUE(run_); }
+ virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
+ T value) {
+ run_ = true;
+ if (expect_success_)
+ compare_expected(value);
+ EXPECT_EQ(expected_failure_, code);
+ }
+ virtual void expect_success() {
+ expect_success_ = true;
+ expected_failure_ = SignedSettings::SUCCESS;
+ }
+ virtual void expect_failure(SignedSettings::ReturnCode code) {
+ expect_success_ = false;
+ expected_failure_ = code;
+ }
+
+ protected:
+ bool expect_success_;
+ SignedSettings::ReturnCode expected_failure_;
+ T expected_;
+ bool run_;
+ virtual void compare_expected(T to_compare) = 0;
+};
+
+template <class T>
+class NormalDelegate : public DummyDelegate<T> {
+ public:
+ explicit NormalDelegate(T to_expect) : DummyDelegate<T>(to_expect) {}
+ virtual ~NormalDelegate() {}
+ protected:
+ virtual void compare_expected(T to_compare) {
+ // without this-> this won't build.
+ EXPECT_EQ(this->expected_, to_compare);
+ }
+};
+
+class ProtoDelegate : public DummyDelegate<const em::PolicyFetchResponse&> {
+ public:
+ explicit ProtoDelegate(const em::PolicyFetchResponse& e)
+ : DummyDelegate<const em::PolicyFetchResponse&>(e) {
+ }
+ virtual ~ProtoDelegate() {}
+ protected:
+ virtual void compare_expected(const em::PolicyFetchResponse& to_compare) {
+ std::string ex_string, comp_string;
+ EXPECT_TRUE(expected_.SerializeToString(&ex_string));
+ EXPECT_TRUE(to_compare.SerializeToString(&comp_string));
+ EXPECT_EQ(ex_string, comp_string);
+ }
+};
+
+} // anonymous namespace
+
+class SignedSettingsTest : public testing::Test {
+ public:
+ SignedSettingsTest()
+ : fake_prop_(kAccountsPrefAllowGuest),
+ fake_signature_("false"),
+ fake_value_(false),
+ fake_value_signature_(
+ fake_signature_.c_str(),
+ fake_signature_.c_str() + fake_signature_.length()),
+ message_loop_(MessageLoop::TYPE_UI),
+ ui_thread_(BrowserThread::UI, &message_loop_),
+ file_thread_(BrowserThread::FILE),
+ mock_(new MockKeyUtils),
+ injector_(mock_) /* injector_ takes ownership of mock_ */,
+ mock_dbus_thread_manager_(new MockDBusThreadManager) {
+ }
+
+ virtual ~SignedSettingsTest() {}
+
+ virtual void SetUp() {
+ file_thread_.Start();
+ DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager_);
+ }
+
+ virtual void TearDown() {
+ OwnerKeyUtils::set_factory(NULL);
+ DBusThreadManager::Shutdown();
+ }
+
+ void mock_service(SignedSettings* s, MockOwnershipService* m) {
+ s->set_service(m);
+ }
+
+ void FailingStorePolicyOp(const OwnerManager::KeyOpCode return_code) {
+ NormalDelegate<bool> d(false);
+ d.expect_failure(SignedSettings::MapKeyOpCode(return_code));
+
+ em::PolicyFetchResponse fake_policy;
+ fake_policy.set_policy_data(fake_prop_);
+ std::string serialized;
+ ASSERT_TRUE(fake_policy.SerializeToString(&serialized));
+
+ scoped_refptr<SignedSettings> s(
+ SignedSettings::CreateStorePolicyOp(&fake_policy, &d));
+
+ mock_service(s.get(), &m_);
+ EXPECT_CALL(m_, StartSigningAttempt(StrEq(fake_prop_), _))
+ .Times(1);
+
+ s->Execute();
+ s->OnKeyOpComplete(return_code, std::vector<uint8>());
+ message_loop_.RunAllPending();
+ }
+
+ em::PolicyData BuildPolicyData(std::vector<std::string> whitelist) {
+ em::PolicyData to_return;
+ em::ChromeDeviceSettingsProto pol;
+ em::GuestModeEnabledProto* allow = pol.mutable_guest_mode_enabled();
+ allow->set_guest_mode_enabled(false);
+ pol.mutable_device_proxy_settings()->set_proxy_mode("direct");
+
+ if (!whitelist.empty()) {
+ em::UserWhitelistProto* whitelist_proto = pol.mutable_user_whitelist();
+ for (std::vector<std::string>::const_iterator it = whitelist.begin();
+ it != whitelist.end();
+ ++it) {
+ whitelist_proto->add_user_whitelist(*it);
+ }
+ }
+
+ to_return.set_policy_type(chromeos::kDevicePolicyType);
+ to_return.set_policy_value(pol.SerializeAsString());
+ return to_return;
+ }
+
+ em::PolicyFetchResponse BuildProto(const std::string& data,
+ const std::string& sig,
+ std::string* out_serialized) {
+ em::PolicyFetchResponse fake_policy;
+ if (!data.empty())
+ fake_policy.set_policy_data(data);
+ if (!sig.empty())
+ fake_policy.set_policy_data_signature(sig);
+ EXPECT_TRUE(fake_policy.SerializeToString(out_serialized));
+ return fake_policy;
+ }
+
+ const std::string fake_prop_;
+ const std::string fake_signature_;
+ const base::FundamentalValue fake_value_;
+ const std::vector<uint8> fake_value_signature_;
+ MockOwnershipService m_;
+
+ MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread file_thread_;
+
+ MockKeyUtils* mock_;
+ MockInjector injector_;
+ MockDBusThreadManager* mock_dbus_thread_manager_;
+
+ ScopedStubCrosEnabler stub_cros_enabler_;
+};
+
+ACTION_P(Retrieve, policy_blob) { arg0.Run(policy_blob); }
+ACTION_P(Store, success) { arg1.Run(success); }
+
+TEST_F(SignedSettingsTest, SignAndStorePolicy) {
+ NormalDelegate<bool> d(true);
+ d.expect_success();
+
+ em::PolicyData in_pol = BuildPolicyData(std::vector<std::string>());
+ std::string data_serialized = in_pol.SerializeAsString();
+ std::string serialized;
+ em::PolicyFetchResponse fake_policy = BuildProto(data_serialized,
+ std::string(),
+ &serialized);
+ scoped_refptr<SignedSettings> s(
+ SignedSettings::CreateStorePolicyOp(&fake_policy, &d));
+
+ mock_service(s.get(), &m_);
+ EXPECT_CALL(m_, StartSigningAttempt(StrEq(data_serialized), _))
+ .Times(1);
+ em::PolicyData out_pol;
+
+ // Ask for signature over unsigned policy.
+ s->Execute();
+ message_loop_.RunAllPending();
+
+ // Fake out a successful signing.
+ std::string signed_serialized;
+ em::PolicyFetchResponse signed_policy = BuildProto(data_serialized,
+ fake_signature_,
+ &signed_serialized);
+ MockSessionManagerClient* client =
+ mock_dbus_thread_manager_->mock_session_manager_client();
+ EXPECT_CALL(*client, StoreDevicePolicy(signed_serialized, _))
+ .WillOnce(Store(true))
+ .RetiresOnSaturation();
+ s->OnKeyOpComplete(OwnerManager::SUCCESS, fake_value_signature_);
+ message_loop_.RunAllPending();
+}
+
+TEST_F(SignedSettingsTest, StoreSignedPolicy) {
+ NormalDelegate<bool> d(true);
+ d.expect_success();
+
+ em::PolicyData in_pol = BuildPolicyData(std::vector<std::string>());
+ std::string serialized = in_pol.SerializeAsString();
+ std::string signed_serialized;
+ em::PolicyFetchResponse signed_policy = BuildProto(serialized,
+ fake_signature_,
+ &signed_serialized);
+ scoped_refptr<SignedSettings> s(
+ SignedSettings::CreateStorePolicyOp(&signed_policy, &d));
+ MockSessionManagerClient* client =
+ mock_dbus_thread_manager_->mock_session_manager_client();
+ EXPECT_CALL(*client, StoreDevicePolicy(signed_serialized, _))
+ .WillOnce(Store(true))
+ .RetiresOnSaturation();
+
+ mock_service(s.get(), &m_);
+ em::PolicyData out_pol;
+
+ s->Execute();
+ message_loop_.RunAllPending();
+}
+
+TEST_F(SignedSettingsTest, StorePolicyNoKey) {
+ FailingStorePolicyOp(OwnerManager::KEY_UNAVAILABLE);
+}
+
+TEST_F(SignedSettingsTest, StorePolicyFailed) {
+ FailingStorePolicyOp(OwnerManager::OPERATION_FAILED);
+}
+
+TEST_F(SignedSettingsTest, StorePolicyNoPolicyData) {
+ NormalDelegate<bool> d(false);
+ d.expect_failure(SignedSettings::OPERATION_FAILED);
+
+ std::string serialized;
+ em::PolicyFetchResponse fake_policy = BuildProto(std::string(),
+ std::string(),
+ &serialized);
+ scoped_refptr<SignedSettings> s(
+ SignedSettings::CreateStorePolicyOp(&fake_policy, &d));
+
+ s->Execute();
+ message_loop_.RunAllPending();
+}
+
+TEST_F(SignedSettingsTest, RetrievePolicy) {
+ em::PolicyData in_pol = BuildPolicyData(std::vector<std::string>());
+ std::string serialized = in_pol.SerializeAsString();
+ std::string signed_serialized;
+ em::PolicyFetchResponse signed_policy = BuildProto(serialized,
+ fake_signature_,
+ &signed_serialized);
+ ProtoDelegate d(signed_policy);
+ d.expect_success();
+ scoped_refptr<SignedSettings> s(SignedSettings::CreateRetrievePolicyOp(&d));
+
+ MockSessionManagerClient* client =
+ mock_dbus_thread_manager_->mock_session_manager_client();
+ EXPECT_CALL(*client, RetrieveDevicePolicy(_))
+ .WillOnce(Retrieve(signed_serialized))
+ .RetiresOnSaturation();
+
+ mock_service(s.get(), &m_);
+ EXPECT_CALL(m_, StartVerifyAttempt(serialized, fake_value_signature_, _))
+ .Times(1);
+ em::PolicyData out_pol;
+
+ s->Execute();
+ message_loop_.RunAllPending();
+
+ s->OnKeyOpComplete(OwnerManager::SUCCESS, std::vector<uint8>());
+ message_loop_.RunAllPending();
+}
+
+TEST_F(SignedSettingsTest, RetrieveNullPolicy) {
+ em::PolicyFetchResponse policy;
+ ProtoDelegate d(policy);
+ d.expect_failure(SignedSettings::NOT_FOUND);
+ scoped_refptr<SignedSettings> s(SignedSettings::CreateRetrievePolicyOp(&d));
+
+ MockSessionManagerClient* client =
+ mock_dbus_thread_manager_->mock_session_manager_client();
+ EXPECT_CALL(*client, RetrieveDevicePolicy(_))
+ .WillOnce(Retrieve(""))
+ .RetiresOnSaturation();
+
+ s->Execute();
+ message_loop_.RunAllPending();
+}
+
+TEST_F(SignedSettingsTest, RetrieveEmptyPolicy) {
+ std::string serialized;
+ em::PolicyFetchResponse policy = BuildProto("", "", &serialized);
+ ProtoDelegate d(policy);
+ d.expect_failure(SignedSettings::NOT_FOUND);
+ scoped_refptr<SignedSettings> s(SignedSettings::CreateRetrievePolicyOp(&d));
+
+ MockSessionManagerClient* client =
+ mock_dbus_thread_manager_->mock_session_manager_client();
+ EXPECT_CALL(*client, RetrieveDevicePolicy(_))
+ .WillOnce(Retrieve(""))
+ .RetiresOnSaturation();
+
+ s->Execute();
+ message_loop_.RunAllPending();
+}
+
+TEST_F(SignedSettingsTest, RetrieveUnsignedPolicy) {
+ std::string serialized;
+ em::PolicyFetchResponse policy = BuildProto(fake_prop_,
+ std::string(),
+ &serialized);
+ ProtoDelegate d(policy);
+ d.expect_failure(SignedSettings::BAD_SIGNATURE);
+ scoped_refptr<SignedSettings> s(SignedSettings::CreateRetrievePolicyOp(&d));
+
+ MockSessionManagerClient* client =
+ mock_dbus_thread_manager_->mock_session_manager_client();
+ EXPECT_CALL(*client, RetrieveDevicePolicy(_))
+ .WillOnce(Retrieve(serialized))
+ .RetiresOnSaturation();
+
+ s->Execute();
+ message_loop_.RunAllPending();
+}
+
+TEST_F(SignedSettingsTest, RetrieveMalsignedPolicy) {
+ std::string signed_serialized;
+ em::PolicyFetchResponse signed_policy = BuildProto(fake_prop_,
+ fake_signature_,
+ &signed_serialized);
+ ProtoDelegate d(signed_policy);
+ d.expect_failure(SignedSettings::BAD_SIGNATURE);
+ scoped_refptr<SignedSettings> s(SignedSettings::CreateRetrievePolicyOp(&d));
+
+ MockSessionManagerClient* client =
+ mock_dbus_thread_manager_->mock_session_manager_client();
+ EXPECT_CALL(*client, RetrieveDevicePolicy(_))
+ .WillOnce(Retrieve(signed_serialized))
+ .RetiresOnSaturation();
+
+ mock_service(s.get(), &m_);
+ EXPECT_CALL(m_, StartVerifyAttempt(fake_prop_, fake_value_signature_, _))
+ .Times(1);
+
+ s->Execute();
+ message_loop_.RunAllPending();
+
+ s->OnKeyOpComplete(OwnerManager::OPERATION_FAILED, std::vector<uint8>());
+ message_loop_.RunAllPending();
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc b/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc
new file mode 100644
index 0000000..facadc3
--- /dev/null
+++ b/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc
@@ -0,0 +1,92 @@
+// 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/stub_cros_settings_provider.h"
+
+#include "base/logging.h"
+#include "base/values.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"
+
+namespace chromeos {
+
+namespace {
+
+const char* kHandledSettings[] = {
+ kAccountsPrefAllowGuest,
+ kAccountsPrefAllowNewUser,
+ kAccountsPrefShowUserNamesOnSignIn,
+ kAccountsPrefUsers,
+ kAccountsPrefEphemeralUsersEnabled,
+ kDeviceOwner,
+ kPolicyMissingMitigationMode,
+ kReleaseChannel,
+ kReportDeviceVersionInfo,
+ kReportDeviceActivityTimes,
+ kReportDeviceBootMode,
+ kReportDeviceLocation,
+ kSettingProxyEverywhere,
+ kSignedDataRoamingEnabled,
+ kStatsReportingPref,
+ // Kiosk mode settings.
+ kIdleLogoutTimeout,
+ kIdleLogoutWarningDuration,
+ kScreenSaverExtensionId,
+ kScreenSaverTimeout
+};
+
+} // namespace
+
+StubCrosSettingsProvider::StubCrosSettingsProvider(
+ const NotifyObserversCallback& notify_cb)
+ : CrosSettingsProvider(notify_cb) {
+ SetDefaults();
+}
+
+StubCrosSettingsProvider::StubCrosSettingsProvider()
+ : CrosSettingsProvider(CrosSettingsProvider::NotifyObserversCallback()) {
+ SetDefaults();
+}
+
+StubCrosSettingsProvider::~StubCrosSettingsProvider() {
+}
+
+const base::Value* StubCrosSettingsProvider::Get(
+ const std::string& path) const {
+ DCHECK(HandlesSetting(path));
+ const base::Value* value;
+ if (values_.GetValue(path, &value))
+ return value;
+ return NULL;
+}
+
+CrosSettingsProvider::TrustedStatus
+ StubCrosSettingsProvider::PrepareTrustedValues(const base::Closure& cb) {
+ // We don't have a trusted store so all values are available immediately.
+ return TRUSTED;
+}
+
+bool StubCrosSettingsProvider::HandlesSetting(const std::string& path) const {
+ const char** end = kHandledSettings + arraysize(kHandledSettings);
+ 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());
+ NotifyObservers(path);
+}
+
+void StubCrosSettingsProvider::SetDefaults() {
+ values_.SetBoolean(kAccountsPrefAllowGuest, true);
+ values_.SetBoolean(kAccountsPrefAllowNewUser, true);
+ values_.SetBoolean(kAccountsPrefShowUserNamesOnSignIn, true);
+ // |kDeviceOwner| will be set to the logged-in user by |UserManager|.
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/stub_cros_settings_provider.h b/chrome/browser/chromeos/settings/stub_cros_settings_provider.h
new file mode 100644
index 0000000..13a10cc
--- /dev/null
+++ b/chrome/browser/chromeos/settings/stub_cros_settings_provider.h
@@ -0,0 +1,49 @@
+// 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_STUB_CROS_SETTINGS_PROVIDER_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_STUB_CROS_SETTINGS_PROVIDER_H_
+
+#include <string>
+
+#include "chrome/browser/chromeos/settings/cros_settings_provider.h"
+#include "chrome/browser/prefs/pref_value_map.h"
+
+namespace chromeos {
+
+class CrosSettings;
+
+// CrosSettingsProvider implementation that stores settings in memory unsigned.
+class StubCrosSettingsProvider : public CrosSettingsProvider {
+ public:
+ explicit StubCrosSettingsProvider(const NotifyObserversCallback& notify_cb);
+ StubCrosSettingsProvider();
+ virtual ~StubCrosSettingsProvider();
+
+ // CrosSettingsProvider implementation.
+ virtual const base::Value* Get(const std::string& path) const OVERRIDE;
+ 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;
+
+ // Initializes settings to their defaults.
+ void SetDefaults();
+
+ // In-memory settings storage.
+ PrefValueMap values_;
+
+ CrosSettings* cros_settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(StubCrosSettingsProvider);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_STUB_CROS_SETTINGS_PROVIDER_H_
diff --git a/chrome/browser/chromeos/settings/stub_cros_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/stub_cros_settings_provider_unittest.cc
new file mode 100644
index 0000000..cd7b00b
--- /dev/null
+++ b/chrome/browser/chromeos/settings/stub_cros_settings_provider_unittest.cc
@@ -0,0 +1,98 @@
+// 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/stub_cros_settings_provider.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/settings/cros_settings_names.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace {
+
+void Fail() {
+ // Should never be called.
+ FAIL();
+}
+
+} // namespace
+
+class StubCrosSettingsProviderTest : public testing::Test {
+ protected:
+ StubCrosSettingsProviderTest()
+ : provider_(new StubCrosSettingsProvider(
+ base::Bind(&StubCrosSettingsProviderTest::FireObservers,
+ base::Unretained(this)))) {
+ }
+
+ virtual ~StubCrosSettingsProviderTest() {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ // Reset the observer notification count.
+ observer_count_.clear();
+ }
+
+ void AssertPref(const std::string& prefName, const Value* value) {
+ const Value* pref = provider_->Get(prefName);
+ ASSERT_TRUE(pref);
+ ASSERT_TRUE(pref->Equals(value));
+ }
+
+ void ExpectObservers(const std::string& prefName, int count) {
+ EXPECT_EQ(observer_count_[prefName], count);
+ }
+
+ void FireObservers(const std::string& path) {
+ observer_count_[path]++;
+ }
+
+ scoped_ptr<StubCrosSettingsProvider> provider_;
+ std::map<std::string, int> observer_count_;
+};
+
+TEST_F(StubCrosSettingsProviderTest, HandlesSettings) {
+ // HandlesSettings should return false for unknown settings.
+ ASSERT_TRUE(provider_->HandlesSetting(kDeviceOwner));
+ ASSERT_FALSE(provider_->HandlesSetting("no.such.setting"));
+}
+
+TEST_F(StubCrosSettingsProviderTest, Defaults) {
+ // Verify default values.
+ const base::FundamentalValue kTrueValue(true);
+ AssertPref(kAccountsPrefAllowGuest, &kTrueValue);
+ AssertPref(kAccountsPrefAllowNewUser, &kTrueValue);
+ AssertPref(kAccountsPrefShowUserNamesOnSignIn, &kTrueValue);
+}
+
+TEST_F(StubCrosSettingsProviderTest, Set) {
+ // Setting value and reading it afterwards returns the same value.
+ base::StringValue owner_value("me@owner");
+ provider_->Set(kDeviceOwner, owner_value);
+ AssertPref(kDeviceOwner, &owner_value);
+ ExpectObservers(kDeviceOwner, 1);
+}
+
+TEST_F(StubCrosSettingsProviderTest, SetMissing) {
+ // Setting is missing initially but is added by |Set|.
+ base::StringValue pref_value("testing");
+ ASSERT_FALSE(provider_->Get(kReleaseChannel));
+ provider_->Set(kReleaseChannel, pref_value);
+ AssertPref(kReleaseChannel, &pref_value);
+ ExpectObservers(kReleaseChannel, 1);
+}
+
+TEST_F(StubCrosSettingsProviderTest, PrepareTrustedValues) {
+ // Should return immediately without invoking the callback.
+ CrosSettingsProvider::TrustedStatus trusted =
+ provider_->PrepareTrustedValues(base::Bind(&Fail));
+ EXPECT_EQ(CrosSettingsProvider::TRUSTED, trusted);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/system_settings_provider.cc b/chrome/browser/chromeos/settings/system_settings_provider.cc
new file mode 100644
index 0000000..5148c74
--- /dev/null
+++ b/chrome/browser/chromeos/settings/system_settings_provider.cc
@@ -0,0 +1,75 @@
+// 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/system_settings_provider.h"
+
+#include "base/string16.h"
+#include "base/time.h"
+#include "base/values.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 "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+
+SystemSettingsProvider::SystemSettingsProvider(
+ const NotifyObserversCallback& notify_cb)
+ : CrosSettingsProvider(notify_cb) {
+ system::TimezoneSettings *timezone_settings =
+ system::TimezoneSettings::GetInstance();
+ timezone_settings->AddObserver(this);
+ timezone_value_.reset(base::Value::CreateStringValue(
+ timezone_settings->GetCurrentTimezoneID()));
+}
+
+SystemSettingsProvider::~SystemSettingsProvider() {
+ system::TimezoneSettings::GetInstance()->RemoveObserver(this);
+}
+
+void SystemSettingsProvider::DoSet(const std::string& path,
+ const base::Value& in_value) {
+ // Non-guest users can change the time zone.
+ if (UserManager::Get()->IsLoggedInAsGuest())
+ return;
+
+ if (path == kSystemTimezone) {
+ string16 timezone_id;
+ if (!in_value.GetAsString(&timezone_id))
+ return;
+ // This will call TimezoneChanged.
+ system::TimezoneSettings::GetInstance()->SetTimezoneFromID(timezone_id);
+ }
+}
+
+const base::Value* SystemSettingsProvider::Get(const std::string& path) const {
+ if (path == kSystemTimezone)
+ return timezone_value_.get();
+ return NULL;
+}
+
+// The timezone is always trusted.
+CrosSettingsProvider::TrustedStatus
+ SystemSettingsProvider::PrepareTrustedValues(const base::Closure& cb) {
+ return TRUSTED;
+}
+
+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(
+ system::TimezoneSettings::GetTimezoneID(timezone)));
+ NotifyObservers(kSystemTimezone);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/system_settings_provider.h b/chrome/browser/chromeos/settings/system_settings_provider.h
new file mode 100644
index 0000000..49ef2aa
--- /dev/null
+++ b/chrome/browser/chromeos/settings/system_settings_provider.h
@@ -0,0 +1,50 @@
+// 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_SYSTEM_SETTINGS_PROVIDER_H_
+#define CHROME_BROWSER_CHROMEOS_SETTINGS_SYSTEM_SETTINGS_PROVIDER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/chromeos/settings/cros_settings_provider.h"
+#include "chrome/browser/chromeos/system/timezone_settings.h"
+#include "third_party/icu/public/i18n/unicode/timezone.h"
+
+namespace base {
+class Value;
+}
+
+namespace chromeos {
+
+class SystemSettingsProvider : public CrosSettingsProvider,
+ public system::TimezoneSettings::Observer {
+ public:
+ explicit SystemSettingsProvider(const NotifyObserversCallback& notify_cb);
+ virtual ~SystemSettingsProvider();
+
+ // CrosSettingsProvider implementation.
+ virtual const base::Value* Get(const std::string& path) const OVERRIDE;
+ 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;
+
+ private:
+ // CrosSettingsProvider implementation.
+ virtual void DoSet(const std::string& path,
+ const base::Value& in_value) OVERRIDE;
+
+ scoped_ptr<base::Value> timezone_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemSettingsProvider);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_SYSTEM_SETTINGS_PROVIDER_H_