diff --git a/base/hash_tables.h b/base/hash_tables.h index 250b27d9f886d..e320670f9c736 100644 --- a/base/hash_tables.h +++ b/base/hash_tables.h @@ -58,7 +58,7 @@ using __gnu_cxx::hash_set; namespace __gnu_cxx { -// The GNU C++ library provides identiy hash functions for many integral types, +// The GNU C++ library provides identity hash functions for many integral types, // but not for |long long|. This hash function will truncate if |size_t| is // narrower than |long long|. This is probably good enough for what we will // use it for. diff --git a/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc b/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc index e461989150213..8cc42600752a9 100644 --- a/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc +++ b/chrome/browser/chromeos/dom_ui/accounts_options_handler.cc @@ -17,7 +17,7 @@ namespace chromeos { AccountsOptionsHandler::AccountsOptionsHandler() - : CrosOptionsPageUIHandler(new UserCrosSettingsProvider()) { + : CrosOptionsPageUIHandler(new UserCrosSettingsProvider) { } AccountsOptionsHandler::~AccountsOptionsHandler() { diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index 0492d397da303..8ce8df89add1f 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc @@ -126,7 +126,8 @@ ExistingUserController::ExistingUserController( selected_view_index_(kNotSelected), num_login_attempts_(0), bubble_(NULL), - user_settings_(new UserCrosSettingsProvider()) { + user_settings_(new UserCrosSettingsProvider), + method_factory_(this) { if (delete_scheduled_instance_) delete_scheduled_instance_->Delete(); @@ -302,8 +303,19 @@ void ExistingUserController::WhiteListCheckFailed(const std::string& email) { void ExistingUserController::LoginOffTheRecord() { // Check allow_guest in case this call is fired from key accelerator. - if (!UserCrosSettingsProvider::cached_allow_guest()) + // Must not proceed without signature verification. + bool trusted_setting_available = user_settings_->RequestTrustedAllowGuest( + method_factory_.NewRunnableMethod( + &ExistingUserController::LoginOffTheRecord)); + if (!trusted_setting_available) { + // Value of AllowGuest setting is still not verified. + // Another attempt will be invoked again after verification completion. return; + } + if (!UserCrosSettingsProvider::cached_allow_guest()) { + // Disallowed. + return; + } // Disable clicking on other windows. SendSetLoginState(false); @@ -356,11 +368,10 @@ void ExistingUserController::ActivateWizard(const std::string& screen_name) { void ExistingUserController::RemoveUser(UserController* source) { ClearErrors(); - // TODO(xiyuan): Wait for the cached settings update before using them. - if (UserCrosSettingsProvider::cached_owner() == source->user().email()) { - // Owner is not allowed to be removed from the device. - return; - } + // Owner is not allowed to be removed from the device. + // It must be enforced at upper levels. + DCHECK(user_settings_->RequestTrustedOwner(NULL)); + DCHECK(source->user().email() != UserCrosSettingsProvider::cached_owner()); UserManager::Get()->RemoveUser(source->user().email()); @@ -548,9 +559,18 @@ void ExistingUserController::OnPasswordChangeDetected( return; } + // Must not proceed without signature verification. + bool trusted_setting_available = user_settings_->RequestTrustedOwner( + method_factory_.NewRunnableMethod( + &ExistingUserController::OnPasswordChangeDetected, + credentials)); + if (!trusted_setting_available) { + // Value of owner email is still not verified. + // Another attempt will be invoked after verification completion. + return; + } // TODO(altimofeev): remove this constrain when full sync for the owner will // be correctly handled. - // TODO(xiyuan): Wait for the cached settings update before using them. bool full_sync_disabled = (UserCrosSettingsProvider::cached_owner() == controllers_[selected_view_index_]->user().email()); diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h index e150a31fb3155..61e941a26acae 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h @@ -166,9 +166,12 @@ class ExistingUserController : public WmMessageListener::Observer, // Help application used for help dialogs. scoped_ptr<HelpAppLauncher> help_app_; - // A user settings provider instance to trigger settings cache update. + // Triggers prefetching of user settings. scoped_ptr<UserCrosSettingsProvider> user_settings_; + // Factory of callbacks. + ScopedRunnableMethodFactory<ExistingUserController> method_factory_; + DISALLOW_COPY_AND_ASSIGN(ExistingUserController); }; diff --git a/chrome/browser/chromeos/login/login_performer.cc b/chrome/browser/chromeos/login/login_performer.cc index eb5304d2e41f9..1f10bfb7a02b7 100644 --- a/chrome/browser/chromeos/login/login_performer.cc +++ b/chrome/browser/chromeos/login/login_performer.cc @@ -21,7 +21,8 @@ namespace { LoginPerformer::LoginPerformer(Delegate* delegate) : last_login_failure_(LoginFailure::None()), - delegate_(delegate) {} + delegate_(delegate), + method_factory_(this) {} //////////////////////////////////////////////////////////////////////////////// // LoginPerformer, LoginStatusConsumer implementation: @@ -100,6 +101,18 @@ void LoginPerformer::Login(const std::string& username, const std::string& password) { username_ = username; password_ = password; + // Must not proceed without signature verification. + UserCrosSettingsProvider user_settings; + bool trusted_setting_available = user_settings.RequestTrustedAllowNewUser( + method_factory_.NewRunnableMethod( + &LoginPerformer::Login, + username, + password)); + if (!trusted_setting_available) { + // Value of AllowNewUser setting is still not verified. + // Another attempt will be invoked after verification completion. + return; + } if (UserCrosSettingsProvider::cached_allow_new_user()) { // Starts authentication if guest login is allowed. StartAuthentication(); diff --git a/chrome/browser/chromeos/login/login_performer.h b/chrome/browser/chromeos/login/login_performer.h index e594723c0dffc..7edf3cfee46e1 100644 --- a/chrome/browser/chromeos/login/login_performer.h +++ b/chrome/browser/chromeos/login/login_performer.h @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "base/ref_counted.h" +#include "base/task.h" #include "chrome/browser/chromeos/login/authenticator.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/browser/chromeos/login/signed_settings_helper.h" @@ -107,6 +108,8 @@ class LoginPerformer : public LoginStatusConsumer, // Notifications receiver. Delegate* delegate_; + ScopedRunnableMethodFactory<LoginPerformer> method_factory_; + DISALLOW_COPY_AND_ASSIGN(LoginPerformer); }; diff --git a/chrome/browser/chromeos/login/owner_manager.cc b/chrome/browser/chromeos/login/owner_manager.cc index 71b15a83e0bbf..389d0a82698fc 100644 --- a/chrome/browser/chromeos/login/owner_manager.cc +++ b/chrome/browser/chromeos/login/owner_manager.cc @@ -73,8 +73,16 @@ void OwnerManager::GenerateKeysAndExportPublic() { } void OwnerManager::ExportKey() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); VLOG(1) << "Exporting public key"; - if (!utils_->ExportPublicKeyViaDbus(private_key_.get(), this)) { + if (utils_->ExportPublicKeyViaDbus(private_key_.get(), this)) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &OwnerManager::SendNotification, + NotificationType::OWNERSHIP_TAKEN, + NotificationService::NoDetails())); + } else { private_key_.reset(NULL); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, diff --git a/chrome/browser/chromeos/login/user_controller.cc b/chrome/browser/chromeos/login/user_controller.cc index 1a526c24717f1..639909cc74cbb 100644 --- a/chrome/browser/chromeos/login/user_controller.cc +++ b/chrome/browser/chromeos/login/user_controller.cc @@ -148,7 +148,8 @@ UserController::UserController(Delegate* delegate, bool is_guest) existing_user_view_(NULL), guest_user_view_(NULL), label_view_(NULL), - unselected_label_view_(NULL) { + unselected_label_view_(NULL), + method_factory_(this) { registrar_.Add( this, NotificationType::LOGIN_USER_IMAGE_CHANGED, @@ -163,9 +164,7 @@ UserController::UserController(Delegate* delegate, is_guest_(false), // Empty 'cached_owner()' means that owner hasn't been cached yet, not // that owner has an empty email. - is_owner_( - !user.email().empty() && - UserCrosSettingsProvider::cached_owner() == user.email()), + is_owner_(user.email() == UserCrosSettingsProvider::cached_owner()), show_name_tooltip_(false), user_(user), delegate_(delegate), @@ -179,7 +178,9 @@ UserController::UserController(Delegate* delegate, existing_user_view_(NULL), guest_user_view_(NULL), label_view_(NULL), - unselected_label_view_(NULL) { + unselected_label_view_(NULL), + method_factory_(this) { + DCHECK(!user.email().empty()); registrar_.Add( this, NotificationType::LOGIN_USER_IMAGE_CHANGED, @@ -552,6 +553,19 @@ void UserController::NavigateAway() { } void UserController::OnRemoveUser() { + // Must not proceed without signature verification. + UserCrosSettingsProvider user_settings; + bool trusted_owner_available = user_settings.RequestTrustedOwner( + method_factory_.NewRunnableMethod(&UserController::OnRemoveUser)); + if (!trusted_owner_available) { + // Value of owner email is still not verified. + // Another attempt will be invoked after verification completion. + return; + } + if (user().email() == UserCrosSettingsProvider::cached_owner()) { + // Owner is not allowed to be removed from the device. + return; + } delegate_->RemoveUser(this); } diff --git a/chrome/browser/chromeos/login/user_controller.h b/chrome/browser/chromeos/login/user_controller.h index 62608e6879b3d..0de0e4bb194b1 100644 --- a/chrome/browser/chromeos/login/user_controller.h +++ b/chrome/browser/chromeos/login/user_controller.h @@ -9,6 +9,7 @@ #include <string> #include "base/string16.h" +#include "base/task.h" #include "chrome/browser/chromeos/login/new_user_view.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/login/user_view.h" @@ -19,7 +20,6 @@ #include "views/controls/button/button.h" #include "views/controls/textfield/textfield.h" #include "views/widget/widget_delegate.h" - namespace views { class WidgetGtk; } @@ -231,6 +231,8 @@ class UserController : public views::ButtonListener, NotificationRegistrar registrar_; + ScopedRunnableMethodFactory<UserController> method_factory_; + DISALLOW_COPY_AND_ASSIGN(UserController); }; diff --git a/chrome/browser/chromeos/user_cros_settings_provider.cc b/chrome/browser/chromeos/user_cros_settings_provider.cc index 1cdfd3a447580..4f90c203ed6ce 100644 --- a/chrome/browser/chromeos/user_cros_settings_provider.cc +++ b/chrome/browser/chromeos/user_cros_settings_provider.cc @@ -4,21 +4,87 @@ #include "chrome/browser/chromeos/user_cros_settings_provider.h" +#include <map> +#include <set> + +#include "base/hash_tables.h" #include "base/logging.h" +#include "base/singleton.h" #include "base/string_util.h" +#include "base/task.h" #include "base/values.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_thread.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/login_library.h" #include "chrome/browser/chromeos/cros_settings.h" #include "chrome/browser/chromeos/cros_settings_names.h" +#include "chrome/browser/chromeos/login/ownership_service.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" namespace chromeos { namespace { +const char kTrueIncantation[] = "true"; +const char kFalseIncantation[] = "false"; +const char kTrustedSuffix[] = "/trusted"; + +// For all our boolean settings following is applicable: +// true is default permissive value and false is safe prohibitic value. +const char* kBooleanSettings[] = { + kAccountsPrefAllowNewUser, + kAccountsPrefAllowGuest, + kAccountsPrefShowUserNamesOnSignIn +}; + +const char* kStringSettings[] = { + kDeviceOwner +}; + +const char* kListSettings[] = { + kAccountsPrefUsers +}; + +bool IsControlledBooleanSetting(const std::string& pref_path) { + return std::find(kBooleanSettings, + kBooleanSettings + arraysize(kBooleanSettings), + pref_path) != + kBooleanSettings + arraysize(kBooleanSettings); +} + +bool IsControlledStringSetting(const std::string& pref_path) { + return std::find(kStringSettings, + kStringSettings + arraysize(kStringSettings), + pref_path) != + kStringSettings + arraysize(kStringSettings); +} + +bool IsControlledListSetting(const std::string& pref_path) { + return std::find(kListSettings, + kListSettings + arraysize(kListSettings), + pref_path) != + kListSettings + arraysize(kListSettings); +} + +void RegisterSetting(PrefService* local_state, const std::string& pref_path) { + local_state->RegisterBooleanPref((pref_path + kTrustedSuffix).c_str(), + false); + if (IsControlledBooleanSetting(pref_path)) { + local_state->RegisterBooleanPref(pref_path.c_str(), true); + } else if (IsControlledStringSetting(pref_path)) { + local_state->RegisterStringPref(pref_path.c_str(), ""); + } else { + DCHECK(IsControlledListSetting(pref_path)); + local_state->RegisterListPref(pref_path.c_str()); + } +} + Value* CreateSettingsBooleanValue(bool value, bool managed) { DictionaryValue* dict = new DictionaryValue; dict->Set("value", Value::CreateBooleanValue(value)); @@ -26,16 +92,30 @@ Value* CreateSettingsBooleanValue(bool value, bool managed) { return dict; } -// static -void UpdateCacheBool(const char* name, bool value) { +enum UseValue { + USE_VALUE_SUPPLIED, + USE_VALUE_DEFAULT +}; + +void UpdateCacheBool(const std::string& name, + bool value, + UseValue use_value) { PrefService* prefs = g_browser_process->local_state(); - prefs->SetBoolean(name, value); + if (use_value == USE_VALUE_DEFAULT) + prefs->ClearPref(name.c_str()); + else + prefs->SetBoolean(name.c_str(), value); prefs->ScheduleSavePersistentPrefs(); } -void UpdateCacheString(const char* name, const std::string& value) { +void UpdateCacheString(const std::string& name, + const std::string& value, + UseValue use_value) { PrefService* prefs = g_browser_process->local_state(); - prefs->SetString(name, value); + if (use_value == USE_VALUE_DEFAULT) + prefs->ClearPref(name.c_str()); + else + prefs->SetString(name.c_str(), value); prefs->ScheduleSavePersistentPrefs(); } @@ -74,43 +154,333 @@ bool GetUserWhitelist(ListValue* user_list) { return true; } +class UserCrosSettingsTrust : public SignedSettingsHelper::Callback, + public NotificationObserver { + public: + static UserCrosSettingsTrust* GetSharedInstance() { + return Singleton<UserCrosSettingsTrust>::get(); + } + + // Working horse for UserCrosSettingsProvider::RequestTrusted* family. + bool RequestTrustedEntity(const std::string& name, Task* callback) { + if (GetOwnershipStatus() == OWNERSHIP_NONE) + return true; + if (GetOwnershipStatus() == OWNERSHIP_TAKEN) { + DCHECK(g_browser_process); + PrefService* prefs = g_browser_process->local_state(); + DCHECK(prefs); + if (prefs->GetBoolean((name + kTrustedSuffix).c_str())) + return true; + } + if (callback) + callbacks_[name].push_back(callback); + return false; + } + + void Set(const std::string& path, Value* in_value) { + if (!UserManager::Get()->current_user_is_owner()) { + LOG(WARNING) << "Changing settings from non-owner, setting=" << path; + + // Revert UI change. + CrosSettings::Get()->FireObservers(path.c_str()); + return; + } + + if (IsControlledBooleanSetting(path)) { + bool bool_value = false; + if (in_value->GetAsBoolean(&bool_value)) { + std::string value = bool_value ? kTrueIncantation : kFalseIncantation; + SignedSettingsHelper::Get()->StartStorePropertyOp(path, value, this); + UpdateCacheBool(path, bool_value, USE_VALUE_SUPPLIED); + + VLOG(1) << "Set cros setting " << path << "=" << value; + } + } else if (path == kDeviceOwner) { + VLOG(1) << "Setting owner is not supported. Please use " + "'UpdateCachedOwner' instead."; + } else if (path == kAccountsPrefUsers) { + VLOG(1) << "Setting user whitelist is not implemented. Please use " + "whitelist/unwhitelist instead."; + } else { + LOG(WARNING) << "Try to set unhandled cros setting " << path; + } + } + + private: + // Listed in upgrade order. + enum OwnershipStatus { + OWNERSHIP_UNKNOWN = 0, + OWNERSHIP_NONE, + OWNERSHIP_TAKEN + }; + + // Used to discriminate different sources of ownership status info. + enum OwnershipSource { + SOURCE_FETCH, // Info comes from FetchOwnershipStatus method. + SOURCE_OBSERVE // Info comes from Observe method. + }; + + // upper bound for number of retries to fetch a signed setting. + static const int kNumRetriesLimit = 9; + + UserCrosSettingsTrust() : ownership_status_(OWNERSHIP_UNKNOWN), + retries_left_(kNumRetriesLimit) { + notification_registrar_.Add(this, + NotificationType::OWNERSHIP_TAKEN, + NotificationService::AllSources()); + // Start getting ownership status. + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + NewRunnableMethod(this, &UserCrosSettingsTrust::FetchOwnershipStatus)); + } + + ~UserCrosSettingsTrust() { + if (BrowserThread::CurrentlyOn(BrowserThread::UI) && + CrosLibrary::Get()->EnsureLoaded()) { + // Cancels all pending callbacks from us. + SignedSettingsHelper::Get()->CancelCallback(this); + } + } + + void FetchOwnershipStatus() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + OwnershipStatus status = + OwnershipService::GetSharedInstance()->IsAlreadyOwned() ? + OWNERSHIP_TAKEN : OWNERSHIP_NONE; + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &UserCrosSettingsTrust::SetOwnershipStatus, + status, SOURCE_FETCH)); + } + + void SetOwnershipStatus(OwnershipStatus new_status, + OwnershipSource source) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(new_status == OWNERSHIP_TAKEN || new_status == OWNERSHIP_NONE); + if (source == SOURCE_FETCH) { + DCHECK(ownership_status_ != OWNERSHIP_NONE); + if (ownership_status_ == OWNERSHIP_TAKEN) { + // OWNERSHIP_TAKEN notification was observed earlier. + return; + } + } + ownership_status_ = new_status; + if (source == SOURCE_FETCH) { + // Start prefetching Boolean and String preferences. + for (size_t i = 0; i < arraysize(kBooleanSettings); ++i) + StartFetchingSetting(kBooleanSettings[i]); + for (size_t i = 0; i < arraysize(kStringSettings); ++i) + StartFetchingSetting(kStringSettings[i]); + } else if (source == SOURCE_OBSERVE) { + DCHECK(new_status == OWNERSHIP_TAKEN); + if (CrosLibrary::Get()->EnsureLoaded()) { + // TODO(dilmah,cmasone): We would not need following piece of code as + // long as failure callback from SignedSettings will allow us to + // discriminate missing settings from failed signature verification. + // Otherwise we must set default values for boolean settings. + UserManager::Get()->set_current_user_is_owner(true); + for (size_t i = 0; i < arraysize(kBooleanSettings); ++i) + Set(kBooleanSettings[i], Value::CreateBooleanValue(true)); + } + } + } + + // Returns ownership status. + // Called on UI thread unlike OwnershipService::IsAlreadyOwned. + OwnershipStatus GetOwnershipStatus() { + return ownership_status_; + } + + void StartFetchingSetting(const std::string& name) { + DCHECK(g_browser_process); + PrefService* prefs = g_browser_process->local_state(); + if (!prefs) + return; + // Do not trust before fetching complete. + prefs->ClearPref((name + kTrustedSuffix).c_str()); + prefs->ScheduleSavePersistentPrefs(); + if (CrosLibrary::Get()->EnsureLoaded()) { + SignedSettingsHelper::Get()->StartRetrieveProperty(name, this); + } + } + + // Implementation of SignedSettingsHelper::Callback. + virtual void OnRetrievePropertyCompleted(bool success, + const std::string& name, + const std::string& value) { + if (!IsControlledBooleanSetting(name) && !IsControlledStringSetting(name)) { + NOTREACHED(); + return; + } + DCHECK(GetOwnershipStatus() != OWNERSHIP_UNKNOWN); + + PrefService* prefs = g_browser_process->local_state(); + if (!success && GetOwnershipStatus() == OWNERSHIP_TAKEN) { + LOG(ERROR) << "On owned device: failed to retrieve cros " + "setting, name=" << name; + if (retries_left_ > 0) { + retries_left_ -= 1; + StartFetchingSetting(name); + return; + } + LOG(ERROR) << "No retries left"; + if (IsControlledBooleanSetting(name)) { + // For boolean settings we can just set safe (false) values + // and continue as trusted. + UpdateCacheBool(name, false, USE_VALUE_SUPPLIED); + } else { + prefs->ClearPref((name + kTrustedSuffix).c_str()); + return; + } + } else { + DCHECK(success || GetOwnershipStatus() == OWNERSHIP_NONE); + if (success) + VLOG(1) << "Retrieved cros setting " << name << "=" << value; + else + VLOG(1) << "We are disowned. Going default for cros setting " << name; + if (IsControlledBooleanSetting(name)) { + // Our boolean settings are true by default (if not explicitly present). + UpdateCacheBool(name, (value == kTrueIncantation), + success ? USE_VALUE_SUPPLIED : USE_VALUE_DEFAULT); + } else if (IsControlledStringSetting(name)) { + UpdateCacheString(name, value, + success ? USE_VALUE_SUPPLIED : USE_VALUE_DEFAULT); + } + } + prefs->SetBoolean((name + kTrustedSuffix).c_str(), true); + { + DCHECK(GetOwnershipStatus() != OWNERSHIP_UNKNOWN); + std::vector<Task*>& callbacks_vector = callbacks_[name]; + for (size_t i = 0; i < callbacks_vector.size(); ++i) + MessageLoop::current()->PostTask(FROM_HERE, callbacks_vector[i]); + callbacks_vector.clear(); + } + if (success) + CrosSettings::Get()->FireObservers(name.c_str()); + } + + // Implementation of SignedSettingsHelper::Callback. + virtual void OnStorePropertyCompleted(bool success, + const std::string& name, + const std::string& value) { + VLOG(1) << "Store cros setting " << name << "=" << value << ", success=" + << success; + + // Reload the setting if store op fails. + if (!success) + SignedSettingsHelper::Get()->StartRetrieveProperty(name, this); + } + + // Implementation of SignedSettingsHelper::Callback. + virtual void OnWhitelistCompleted(bool success, const std::string& email) { + VLOG(1) << "Add " << email << " to whitelist, success=" << success; + + // Reload the whitelist on settings op failure. + if (!success) + CrosSettings::Get()->FireObservers(kAccountsPrefUsers); + } + + // Implementation of SignedSettingsHelper::Callback. + virtual void OnUnwhitelistCompleted(bool success, const std::string& email) { + VLOG(1) << "Remove " << email << " from whitelist, success=" << success; + + // Reload the whitelist on settings op failure. + if (!success) + CrosSettings::Get()->FireObservers(kAccountsPrefUsers); + } + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type.value == NotificationType::OWNERSHIP_TAKEN) { + SetOwnershipStatus(OWNERSHIP_TAKEN, SOURCE_OBSERVE); + notification_registrar_.RemoveAll(); + } else { + NOTREACHED(); + } + } + + // Pending callbacks that need to be invoked after settings verification. + base::hash_map< std::string, std::vector< Task* > > callbacks_; + + NotificationRegistrar notification_registrar_; + OwnershipStatus ownership_status_; + + // In order to guard against occasional failure to fetch a property + // we allow for some number of retries. + int retries_left_; + + friend class SignedSettingsHelper; + friend struct DefaultSingletonTraits<UserCrosSettingsTrust>; + + DISALLOW_COPY_AND_ASSIGN(UserCrosSettingsTrust); +}; + } // namespace -UserCrosSettingsProvider::UserCrosSettingsProvider() { - StartFetchingBoolSetting(kAccountsPrefAllowGuest); - StartFetchingBoolSetting(kAccountsPrefAllowNewUser); - StartFetchingBoolSetting(kAccountsPrefShowUserNamesOnSignIn); - StartFetchingStringSetting(kDeviceOwner); -} +} // namespace chromeos -UserCrosSettingsProvider::~UserCrosSettingsProvider() { - // Cancels all pending callbacks from us. - SignedSettingsHelper::Get()->CancelCallback(this); +// We want to use NewRunnableMethod with this class but need to disable +// reference counting since it is singleton. +DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::UserCrosSettingsTrust); + +namespace chromeos { + +UserCrosSettingsProvider::UserCrosSettingsProvider() { + // Trigger prefetching of settings. + UserCrosSettingsTrust::GetSharedInstance(); } // static void UserCrosSettingsProvider::RegisterPrefs(PrefService* local_state) { - // Cached signed settings values - local_state->RegisterBooleanPref(kAccountsPrefAllowGuest, true); - local_state->RegisterBooleanPref(kAccountsPrefAllowNewUser, true); - local_state->RegisterBooleanPref(kAccountsPrefShowUserNamesOnSignIn, true); - local_state->RegisterListPref(kAccountsPrefUsers); - local_state->RegisterStringPref(kDeviceOwner, ""); + for (size_t i = 0; i < arraysize(kBooleanSettings); ++i) + RegisterSetting(local_state, kBooleanSettings[i]); + for (size_t i = 0; i < arraysize(kStringSettings); ++i) + RegisterSetting(local_state, kStringSettings[i]); + for (size_t i = 0; i < arraysize(kListSettings); ++i) + RegisterSetting(local_state, kListSettings[i]); +} + +bool UserCrosSettingsProvider::RequestTrustedAllowGuest(Task* callback) { + return UserCrosSettingsTrust::GetSharedInstance()->RequestTrustedEntity( + kAccountsPrefAllowGuest, callback); +} + +bool UserCrosSettingsProvider::RequestTrustedAllowNewUser(Task* callback) { + return UserCrosSettingsTrust::GetSharedInstance()->RequestTrustedEntity( + kAccountsPrefAllowNewUser, callback); +} + +bool UserCrosSettingsProvider::RequestTrustedShowUsersOnSignin(Task* callback) { + return UserCrosSettingsTrust::GetSharedInstance()->RequestTrustedEntity( + kAccountsPrefShowUserNamesOnSignIn, callback); +} + +bool UserCrosSettingsProvider::RequestTrustedOwner(Task* callback) { + return UserCrosSettingsTrust::GetSharedInstance()->RequestTrustedEntity( + kDeviceOwner, callback); } // static bool UserCrosSettingsProvider::cached_allow_guest() { + // Trigger prefetching if singleton object still does not exist. + UserCrosSettingsTrust::GetSharedInstance(); return g_browser_process->local_state()->GetBoolean(kAccountsPrefAllowGuest); } // static bool UserCrosSettingsProvider::cached_allow_new_user() { + // Trigger prefetching if singleton object still does not exist. + UserCrosSettingsTrust::GetSharedInstance(); return g_browser_process->local_state()->GetBoolean( kAccountsPrefAllowNewUser); } // static bool UserCrosSettingsProvider::cached_show_users_on_signin() { + // Trigger prefetching if singleton object still does not exist. + UserCrosSettingsTrust::GetSharedInstance(); return g_browser_process->local_state()->GetBoolean( kAccountsPrefShowUserNamesOnSignIn); } @@ -132,6 +502,8 @@ const ListValue* UserCrosSettingsProvider::cached_whitelist() { // static std::string UserCrosSettingsProvider::cached_owner() { + // Trigger prefetching if singleton object still does not exist. + UserCrosSettingsTrust::GetSharedInstance(); if (!g_browser_process || !g_browser_process->local_state()) return std::string(); return g_browser_process->local_state()->GetString(kDeviceOwner); @@ -154,41 +526,12 @@ bool UserCrosSettingsProvider::IsEmailInCachedWhitelist( void UserCrosSettingsProvider::DoSet(const std::string& path, Value* in_value) { - if (!UserManager::Get()->current_user_is_owner()) { - LOG(WARNING) << "Changing settings from non-owner, setting=" << path; - - // Revert UI change. - CrosSettings::Get()->FireObservers(path.c_str()); - return; - } - - if (path == kAccountsPrefAllowGuest || - path == kAccountsPrefAllowNewUser || - path == kAccountsPrefShowUserNamesOnSignIn) { - bool bool_value = false; - if (in_value->GetAsBoolean(&bool_value)) { - std::string value = bool_value ? "true" : "false"; - SignedSettingsHelper::Get()->StartStorePropertyOp(path, value, this); - UpdateCacheBool(path.c_str(), bool_value); - - VLOG(1) << "Set cros setting " << path << "=" << value; - } - } else if (path == kDeviceOwner) { - VLOG(1) << "Setting owner is not supported. Please use 'UpdateCachedOwner' " - "instead."; - } else if (path == kAccountsPrefUsers) { - VLOG(1) << "Setting user whitelist is not implemented. Please use " - "whitelist/unwhitelist instead."; - } else { - LOG(WARNING) << "Try to set unhandled cros setting " << path; - } + UserCrosSettingsTrust::GetSharedInstance()->Set(path, in_value); } bool UserCrosSettingsProvider::Get(const std::string& path, Value** out_value) const { - if (path == kAccountsPrefAllowGuest || - path == kAccountsPrefAllowNewUser || - path == kAccountsPrefShowUserNamesOnSignIn) { + if (IsControlledBooleanSetting(path)) { *out_value = CreateSettingsBooleanValue( g_browser_process->local_state()->GetBoolean(path.c_str()), !UserManager::Get()->current_user_is_owner()); @@ -207,56 +550,9 @@ bool UserCrosSettingsProvider::HandlesSetting(const std::string& path) { return ::StartsWithASCII(path, "cros.accounts.", true); } -void UserCrosSettingsProvider::OnWhitelistCompleted(bool success, - const std::string& email) { - VLOG(1) << "Add " << email << " to whitelist, success=" << success; - - // Reload the whitelist on settings op failure. - if (!success) - CrosSettings::Get()->FireObservers(kAccountsPrefUsers); -} - -void UserCrosSettingsProvider::OnUnwhitelistCompleted(bool success, - const std::string& email) { - VLOG(1) << "Remove " << email << " from whitelist, success=" << success; - - // Reload the whitelist on settings op failure. - if (!success) - CrosSettings::Get()->FireObservers(kAccountsPrefUsers); -} - -void UserCrosSettingsProvider::OnStorePropertyCompleted( - bool success, const std::string& name, const std::string& value) { - VLOG(1) << "Store cros setting " << name << "=" << value << ", success=" - << success; - - // Reload the setting if store op fails. - if (!success) - SignedSettingsHelper::Get()->StartRetrieveProperty(name, this); -} - -void UserCrosSettingsProvider::OnRetrievePropertyCompleted( - bool success, const std::string& name, const std::string& value) { - if (!success) { - LOG(WARNING) << "Failed to retrieve cros setting, name=" << name; - return; - } - - VLOG(1) << "Retrieved cros setting " << name << "=" << value; - - if (bool_settings_.count(name)) { - UpdateCacheBool(name.c_str(), value == "true" ? true : false); - } - - if (string_settings_.count(name)) { - UpdateCacheString(name.c_str(), value); - } - - CrosSettings::Get()->FireObservers(name.c_str()); -} - void UserCrosSettingsProvider::WhitelistUser(const std::string& email) { - SignedSettingsHelper::Get()->StartWhitelistOp(email, true, this); + SignedSettingsHelper::Get()->StartWhitelistOp( + email, true, UserCrosSettingsTrust::GetSharedInstance()); PrefService* prefs = g_browser_process->local_state(); ListValue* cached_whitelist = prefs->GetMutableList(kAccountsPrefUsers); @@ -265,7 +561,8 @@ void UserCrosSettingsProvider::WhitelistUser(const std::string& email) { } void UserCrosSettingsProvider::UnwhitelistUser(const std::string& email) { - SignedSettingsHelper::Get()->StartWhitelistOp(email, false, this); + SignedSettingsHelper::Get()->StartWhitelistOp( + email, false, UserCrosSettingsTrust::GetSharedInstance()); PrefService* prefs = g_browser_process->local_state(); ListValue* cached_whitelist = prefs->GetMutableList(kAccountsPrefUsers); @@ -276,25 +573,7 @@ void UserCrosSettingsProvider::UnwhitelistUser(const std::string& email) { // static void UserCrosSettingsProvider::UpdateCachedOwner(const std::string& email) { - UpdateCacheString(kDeviceOwner, email); -} - -void UserCrosSettingsProvider::StartFetchingBoolSetting( - const std::string& name) { - bool_settings_.insert(name); - StartFetchingSetting(name); -} - -void UserCrosSettingsProvider::StartFetchingStringSetting( - const std::string& name) { - string_settings_.insert(name); - StartFetchingSetting(name); -} - -void UserCrosSettingsProvider::StartFetchingSetting( - const std::string& name) { - if (CrosLibrary::Get()->EnsureLoaded()) - SignedSettingsHelper::Get()->StartRetrieveProperty(name, this); + UpdateCacheString(kDeviceOwner, email, USE_VALUE_SUPPLIED); } } // namespace chromeos diff --git a/chrome/browser/chromeos/user_cros_settings_provider.h b/chrome/browser/chromeos/user_cros_settings_provider.h index aa7db0f2047cd..7b000ca5b0156 100644 --- a/chrome/browser/chromeos/user_cros_settings_provider.h +++ b/chrome/browser/chromeos/user_cros_settings_provider.h @@ -9,24 +9,33 @@ #include <string> #include "base/basictypes.h" -#include "base/hash_tables.h" #include "chrome/browser/chromeos/cros_settings_provider.h" #include "chrome/browser/chromeos/login/signed_settings_helper.h" class ListValue; class PrefService; +class Task; namespace chromeos { -class UserCrosSettingsProvider : public CrosSettingsProvider, - public SignedSettingsHelper::Callback { +class UserCrosSettingsProvider : public CrosSettingsProvider { public: UserCrosSettingsProvider(); - virtual ~UserCrosSettingsProvider(); + virtual ~UserCrosSettingsProvider() {} // Registers cached users settings in preferences. static void RegisterPrefs(PrefService* local_state); + // Methods to use when trusted (signature verified) values are required. + // Return true if subsequent call to corresponding cached_* getter shall + // return trusted value. + // Return false if trusted values are unavailable at a moment. + // In latter case passed task will be posted when ready. + bool RequestTrustedAllowGuest(Task* callback); + bool RequestTrustedAllowNewUser(Task* callback); + bool RequestTrustedShowUsersOnSignin(Task* callback); + bool RequestTrustedOwner(Task* callback); + // Helper functions to access cached settings. static bool cached_allow_guest(); static bool cached_allow_new_user(); @@ -43,14 +52,6 @@ class UserCrosSettingsProvider : public CrosSettingsProvider, virtual bool Get(const std::string& path, Value** out_value) const; virtual bool HandlesSetting(const std::string& path); - // SignedSettingsHelper::Callback overrides. - virtual void OnWhitelistCompleted(bool success, const std::string& email); - virtual void OnUnwhitelistCompleted(bool success, const std::string& email); - virtual void OnStorePropertyCompleted( - bool success, const std::string& name, const std::string& value); - virtual void OnRetrievePropertyCompleted( - bool success, const std::string& name, const std::string& value); - void WhitelistUser(const std::string& email); void UnwhitelistUser(const std::string& email); @@ -61,13 +62,6 @@ class UserCrosSettingsProvider : public CrosSettingsProvider, // CrosSettingsProvider implementation. virtual void DoSet(const std::string& path, Value* value); - void StartFetchingBoolSetting(const std::string& name); - void StartFetchingStringSetting(const std::string& name); - void StartFetchingSetting(const std::string& name); - - base::hash_set<std::string> bool_settings_; - base::hash_set<std::string> string_settings_; - DISALLOW_COPY_AND_ASSIGN(UserCrosSettingsProvider); }; diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h index eba0332358617..3adc3ff0641b0 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -1243,6 +1243,9 @@ class NotificationType { // os device has failed. OWNER_KEY_FETCH_ATTEMPT_FAILED, + // Sent after device was successfully owned. + OWNERSHIP_TAKEN, + // This is sent to a ChromeOS settings observer when a system setting is // changed. The source is the CrosSettings and the details a std::string of // the changed setting.