普通文本  |  603行  |  20.86 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/user_cros_settings_provider.h"

#include <map>
#include <set>

#include "base/hash_tables.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/string_util.h"
#include "base/task.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/login_library.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/chromeos/cros_settings.h"
#include "chrome/browser/chromeos/cros_settings_names.h"
#include "chrome/browser/chromeos/login/ownership_service.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "content/browser/browser_thread.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.
// Exception: kSignedDataRoamingEnabled which has default value of false.
const char* kBooleanSettings[] = {
  kAccountsPrefAllowNewUser,
  kAccountsPrefAllowGuest,
  kAccountsPrefShowUserNamesOnSignIn,
  kSignedDataRoamingEnabled,
};

const char* kStringSettings[] = {
  kDeviceOwner
};

const char* kListSettings[] = {
  kAccountsPrefUsers
};

bool IsControlledBooleanSetting(const std::string& pref_path) {
  // TODO(nkostylev): Using std::find for 4 value array generates this warning
  // in chroot stl_algo.h:231: error: array subscript is above array bounds.
  // GCC 4.4.3
  return (pref_path == kAccountsPrefAllowNewUser) ||
         (pref_path == kAccountsPrefAllowGuest) ||
         (pref_path == kAccountsPrefShowUserNamesOnSignIn) ||
         (pref_path == kSignedDataRoamingEnabled);
}

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)) {
    if (pref_path == kSignedDataRoamingEnabled)
      local_state->RegisterBooleanPref(pref_path.c_str(), false);
    else
      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());
  }
}

// Create a settings boolean value with "managed" and "disabled" property.
// "managed" property is true if the setting is managed by administrator.
// "disabled" property is true if the UI for the setting should be disabled.
Value* CreateSettingsBooleanValue(bool value, bool managed, bool disabled) {
  DictionaryValue* dict = new DictionaryValue;
  dict->Set("value", Value::CreateBooleanValue(value));
  dict->Set("managed", Value::CreateBooleanValue(managed));
  dict->Set("disabled", Value::CreateBooleanValue(disabled));
  return dict;
}

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();
  if (use_value == USE_VALUE_DEFAULT)
    prefs->ClearPref(name.c_str());
  else
    prefs->SetBoolean(name.c_str(), value);
  prefs->ScheduleSavePersistentPrefs();
}

void UpdateCacheString(const std::string& name,
                       const std::string& value,
                       UseValue use_value) {
  PrefService* prefs = g_browser_process->local_state();
  if (use_value == USE_VALUE_DEFAULT)
    prefs->ClearPref(name.c_str());
  else
    prefs->SetString(name.c_str(), value);
  prefs->ScheduleSavePersistentPrefs();
}

bool GetUserWhitelist(ListValue* user_list) {
  PrefService* prefs = g_browser_process->local_state();
  DCHECK(!prefs->IsManagedPreference(kAccountsPrefUsers));

  std::vector<std::string> whitelist;
  if (!SignedSettings::EnumerateWhitelist(&whitelist)) {
    LOG(WARNING) << "Failed to retrieve user whitelist.";
    return false;
  }

  ListPrefUpdate cached_whitelist_update(prefs, kAccountsPrefUsers);
  cached_whitelist_update->Clear();

  const UserManager::User& self = UserManager::Get()->logged_in_user();
  bool is_owner = UserManager::Get()->current_user_is_owner();

  for (size_t i = 0; i < whitelist.size(); ++i) {
    const std::string& email = whitelist[i];

    if (user_list) {
      DictionaryValue* user = new DictionaryValue;
      user->SetString("email", email);
      user->SetString("name", "");
      user->SetBoolean("owner", is_owner && email == self.email());
      user_list->Append(user);
    }

    cached_whitelist_update->Append(Value::CreateStringValue(email));
  }

  prefs->ScheduleSavePersistentPrefs();
  return true;
}

class UserCrosSettingsTrust : public SignedSettingsHelper::Callback {
 public:
  static UserCrosSettingsTrust* GetInstance() {
    return Singleton<UserCrosSettingsTrust>::get();
  }

  // Working horse for UserCrosSettingsProvider::RequestTrusted* family.
  bool RequestTrustedEntity(const std::string& name) {
    OwnershipService::Status ownership_status =
        ownership_service_->GetStatus(false);
    if (ownership_status == OwnershipService::OWNERSHIP_NONE)
      return true;
    PrefService* prefs = g_browser_process->local_state();
    if (prefs->IsManagedPreference(name.c_str()))
      return true;
    if (ownership_status == OwnershipService::OWNERSHIP_TAKEN) {
      DCHECK(g_browser_process);
      PrefService* prefs = g_browser_process->local_state();
      DCHECK(prefs);
      if (prefs->GetBoolean((name + kTrustedSuffix).c_str()))
        return true;
    }
    return false;
  }

  bool RequestTrustedEntity(const std::string& name, Task* callback) {
    if (RequestTrustedEntity(name)) {
      delete callback;
      return true;
    } else {
      if (callback)
        callbacks_[name].push_back(callback);
      return false;
    }
  }

  void Reload() {
    for (size_t i = 0; i < arraysize(kBooleanSettings); ++i)
      StartFetchingSetting(kBooleanSettings[i]);
    for (size_t i = 0; i < arraysize(kStringSettings); ++i)
      StartFetchingSetting(kStringSettings[i]);
  }

  void Set(const std::string& path, Value* in_value) {
    PrefService* prefs = g_browser_process->local_state();
    DCHECK(!prefs->IsManagedPreference(path.c_str()));

    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)) {
        OnBooleanPropertyChange(path, 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:
  // upper bound for number of retries to fetch a signed setting.
  static const int kNumRetriesLimit = 9;

  UserCrosSettingsTrust()
      : ownership_service_(OwnershipService::GetSharedInstance()),
        retries_left_(kNumRetriesLimit) {
    // Start prefetching Boolean and String preferences.
    Reload();
  }

  ~UserCrosSettingsTrust() {
    if (BrowserThread::CurrentlyOn(BrowserThread::UI) &&
        CrosLibrary::Get()->EnsureLoaded()) {
      // Cancels all pending callbacks from us.
      SignedSettingsHelper::Get()->CancelCallback(this);
    }
  }

  // Called right before boolean property is changed.
  void OnBooleanPropertyChange(const std::string& path, bool new_value) {
    if (path == kSignedDataRoamingEnabled) {
      if (!CrosLibrary::Get()->EnsureLoaded())
        return;

      NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary();
      cros->SetCellularDataRoamingAllowed(new_value);
    }
  }

  // Called right after signed value was checked.
  void OnBooleanPropertyRetrieve(const std::string& path,
                                 bool value,
                                 UseValue use_value) {
    if (path == kSignedDataRoamingEnabled) {
      if (!CrosLibrary::Get()->EnsureLoaded())
        return;

      NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary();
      const NetworkDevice* cellular = cros->FindCellularDevice();
      if (cellular) {
        bool device_value = cellular->data_roaming_allowed();
        bool new_value = (use_value == USE_VALUE_SUPPLIED) ? value : false;
        if (device_value != new_value)
          cros->SetCellularDataRoamingAllowed(new_value);
      }
    }
  }

  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(SignedSettings::ReturnCode code,
                                           const std::string& name,
                                           const std::string& value) {
    if (!IsControlledBooleanSetting(name) && !IsControlledStringSetting(name)) {
      NOTREACHED();
      return;
    }

    bool is_owned = ownership_service_->GetStatus(true) ==
        OwnershipService::OWNERSHIP_TAKEN;
    PrefService* prefs = g_browser_process->local_state();
    switch (code) {
      case SignedSettings::SUCCESS:
      case SignedSettings::NOT_FOUND:
      case SignedSettings::KEY_UNAVAILABLE: {
        bool fallback_to_default = !is_owned
            || (code == SignedSettings::NOT_FOUND);
        DCHECK(fallback_to_default || code == SignedSettings::SUCCESS);
        if (fallback_to_default)
          VLOG(1) << "Going default for cros setting " << name;
        else
          VLOG(1) << "Retrieved cros setting " << name << "=" << value;
        if (IsControlledBooleanSetting(name)) {
          OnBooleanPropertyRetrieve(name, (value == kTrueIncantation),
              fallback_to_default ? USE_VALUE_DEFAULT : USE_VALUE_SUPPLIED);
          UpdateCacheBool(name, (value == kTrueIncantation),
              fallback_to_default ? USE_VALUE_DEFAULT : USE_VALUE_SUPPLIED);
        } else if (IsControlledStringSetting(name)) {
          UpdateCacheString(name, value,
              fallback_to_default ? USE_VALUE_DEFAULT : USE_VALUE_SUPPLIED);
        }
        break;
      }
      case SignedSettings::OPERATION_FAILED:
      default: {
        DCHECK(code == SignedSettings::OPERATION_FAILED);
        DCHECK(is_owned);
        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.
          OnBooleanPropertyRetrieve(name, false, USE_VALUE_SUPPLIED);
          UpdateCacheBool(name, false, USE_VALUE_SUPPLIED);
        } else {
          prefs->ClearPref((name + kTrustedSuffix).c_str());
          return;
        }
        break;
      }
    }
    prefs->SetBoolean((name + kTrustedSuffix).c_str(), true);
    {
      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 (code == SignedSettings::SUCCESS)
      CrosSettings::Get()->FireObservers(name.c_str());
  }

  // Implementation of SignedSettingsHelper::Callback.
  virtual void OnStorePropertyCompleted(SignedSettings::ReturnCode code,
                                        const std::string& name,
                                        const std::string& value) {
    VLOG(1) << "Store cros setting " << name << "=" << value << ", code="
            << code;

    // Reload the setting if store op fails.
    if (code != SignedSettings::SUCCESS)
      SignedSettingsHelper::Get()->StartRetrieveProperty(name, this);
  }

  // Implementation of SignedSettingsHelper::Callback.
  virtual void OnWhitelistCompleted(SignedSettings::ReturnCode code,
                                    const std::string& email) {
    VLOG(1) << "Add " << email << " to whitelist, code=" << code;

    // Reload the whitelist on settings op failure.
    if (code != SignedSettings::SUCCESS)
      CrosSettings::Get()->FireObservers(kAccountsPrefUsers);
  }

  // Implementation of SignedSettingsHelper::Callback.
  virtual void OnUnwhitelistCompleted(SignedSettings::ReturnCode code,
                                      const std::string& email) {
    VLOG(1) << "Remove " << email << " from whitelist, code=" << code;

    // Reload the whitelist on settings op failure.
    if (code != SignedSettings::SUCCESS)
      CrosSettings::Get()->FireObservers(kAccountsPrefUsers);
  }

  // Pending callbacks that need to be invoked after settings verification.
  base::hash_map< std::string, std::vector< Task* > > callbacks_;

  OwnershipService* ownership_service_;

  // 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

}  // namespace chromeos

// 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::GetInstance();
}

// static
void UserCrosSettingsProvider::RegisterPrefs(PrefService* local_state) {
  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::GetInstance()->RequestTrustedEntity(
      kAccountsPrefAllowGuest, callback);
}

bool UserCrosSettingsProvider::RequestTrustedAllowNewUser(Task* callback) {
  return UserCrosSettingsTrust::GetInstance()->RequestTrustedEntity(
      kAccountsPrefAllowNewUser, callback);
}

bool UserCrosSettingsProvider::RequestTrustedShowUsersOnSignin(Task* callback) {
  return UserCrosSettingsTrust::GetInstance()->RequestTrustedEntity(
      kAccountsPrefShowUserNamesOnSignIn, callback);
}

bool UserCrosSettingsProvider::RequestTrustedDataRoamingEnabled(
    Task* callback) {
  return UserCrosSettingsTrust::GetInstance()->RequestTrustedEntity(
      kSignedDataRoamingEnabled, callback);
}

bool UserCrosSettingsProvider::RequestTrustedOwner(Task* callback) {
  return UserCrosSettingsTrust::GetInstance()->RequestTrustedEntity(
      kDeviceOwner, callback);
}

void UserCrosSettingsProvider::Reload() {
  UserCrosSettingsTrust::GetInstance()->Reload();
}

// static
bool UserCrosSettingsProvider::cached_allow_guest() {
  // Trigger prefetching if singleton object still does not exist.
  UserCrosSettingsTrust::GetInstance();
  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::GetInstance();
  return g_browser_process->local_state()->GetBoolean(
      kAccountsPrefAllowNewUser);
}

// static
bool UserCrosSettingsProvider::cached_data_roaming_enabled() {
  // Trigger prefetching if singleton object still does not exist.
  UserCrosSettingsTrust::GetInstance();
  return g_browser_process->local_state()->GetBoolean(
      kSignedDataRoamingEnabled);
}

// static
bool UserCrosSettingsProvider::cached_show_users_on_signin() {
  // Trigger prefetching if singleton object still does not exist.
  UserCrosSettingsTrust::GetInstance();
  return g_browser_process->local_state()->GetBoolean(
      kAccountsPrefShowUserNamesOnSignIn);
}

// static
const ListValue* UserCrosSettingsProvider::cached_whitelist() {
  PrefService* prefs = g_browser_process->local_state();
  const ListValue* cached_users = prefs->GetList(kAccountsPrefUsers);
  if (!prefs->IsManagedPreference(kAccountsPrefUsers)) {
    if (cached_users == NULL) {
      // Update whitelist cache.
      GetUserWhitelist(NULL);
      cached_users = prefs->GetList(kAccountsPrefUsers);
    }
  }
  if (cached_users == NULL) {
    NOTREACHED();
    cached_users = new ListValue;
  }
  return cached_users;
}

// static
std::string UserCrosSettingsProvider::cached_owner() {
  // Trigger prefetching if singleton object still does not exist.
  UserCrosSettingsTrust::GetInstance();
  if (!g_browser_process || !g_browser_process->local_state())
    return std::string();
  return g_browser_process->local_state()->GetString(kDeviceOwner);
}

// static
bool UserCrosSettingsProvider::IsEmailInCachedWhitelist(
    const std::string& email) {
  const ListValue* whitelist = cached_whitelist();
  if (whitelist) {
    StringValue email_value(email);
    for (ListValue::const_iterator i(whitelist->begin());
        i != whitelist->end(); ++i) {
      if ((*i)->Equals(&email_value))
        return true;
    }
  }
  return false;
}

void UserCrosSettingsProvider::DoSet(const std::string& path,
                                     Value* in_value) {
  UserCrosSettingsTrust::GetInstance()->Set(path, in_value);
}

bool UserCrosSettingsProvider::Get(const std::string& path,
                                   Value** out_value) const {
  if (IsControlledBooleanSetting(path)) {
    PrefService* prefs = g_browser_process->local_state();
    *out_value = CreateSettingsBooleanValue(
        prefs->GetBoolean(path.c_str()),
        prefs->IsManagedPreference(path.c_str()),
        !UserManager::Get()->current_user_is_owner());
    return true;
  } else if (path == kAccountsPrefUsers) {
    ListValue* user_list = new ListValue;
    GetUserWhitelist(user_list);
    *out_value = user_list;
    return true;
  }

  return false;
}

bool UserCrosSettingsProvider::HandlesSetting(const std::string& path) {
  return ::StartsWithASCII(path, "cros.accounts.", true) ||
      ::StartsWithASCII(path, "cros.signed.", true);
}

void UserCrosSettingsProvider::WhitelistUser(const std::string& email) {
  SignedSettingsHelper::Get()->StartWhitelistOp(
      email, true, UserCrosSettingsTrust::GetInstance());
  PrefService* prefs = g_browser_process->local_state();
  ListPrefUpdate cached_whitelist_update(prefs, kAccountsPrefUsers);
  cached_whitelist_update->Append(Value::CreateStringValue(email));
  prefs->ScheduleSavePersistentPrefs();
}

void UserCrosSettingsProvider::UnwhitelistUser(const std::string& email) {
  SignedSettingsHelper::Get()->StartWhitelistOp(
      email, false, UserCrosSettingsTrust::GetInstance());

  PrefService* prefs = g_browser_process->local_state();
  ListPrefUpdate cached_whitelist_update(prefs, kAccountsPrefUsers);
  StringValue email_value(email);
  if (cached_whitelist_update->Remove(email_value) != -1)
    prefs->ScheduleSavePersistentPrefs();
}

// static
void UserCrosSettingsProvider::UpdateCachedOwner(const std::string& email) {
  UpdateCacheString(kDeviceOwner, email, USE_VALUE_SUPPLIED);
}

}  // namespace chromeos