普通文本  |  1019行  |  39.7 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/policy/configuration_policy_pref_store.h"

#include <map>
#include <set>
#include <string>
#include <vector>

#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/stl_util-inl.h"
#include "base/string16.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/policy/browser_policy_connector.h"
#include "chrome/browser/policy/configuration_policy_provider.h"
#include "chrome/browser/policy/policy_path_parser.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/prefs/pref_value_map.h"
#include "chrome/browser/prefs/proxy_config_dictionary.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/search_terms_data.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/common/pref_names.h"
#include "content/common/notification_service.h"
#include "policy/policy_constants.h"

namespace policy {

// Accepts policy settings from a ConfigurationPolicyProvider, converts them
// to preferences and caches the result.
class ConfigurationPolicyPrefKeeper
    : private ConfigurationPolicyStoreInterface {
 public:
  explicit ConfigurationPolicyPrefKeeper(ConfigurationPolicyProvider* provider);
  virtual ~ConfigurationPolicyPrefKeeper();

  // Get a preference value.
  PrefStore::ReadResult GetValue(const std::string& key,
                                 const Value** result) const;

  // Compute the set of preference names that are different in |keeper|. This
  // includes preferences that are missing in either one.
  void GetDifferingPrefPaths(const ConfigurationPolicyPrefKeeper* other,
                             std::vector<std::string>* differing_prefs) const;

 private:
  // ConfigurationPolicyStore methods:
  virtual void Apply(ConfigurationPolicyType setting, Value* value);

  // Policies that map to a single preference are handled
  // by an automated converter. Each one of these policies
  // has an entry in |simple_policy_map_| with the following type.
  struct PolicyToPreferenceMapEntry {
    Value::ValueType value_type;
    ConfigurationPolicyType policy_type;
    const char* preference_path;  // A DictionaryValue path, not a file path.
  };

  // Remove the preferences found in the map from |prefs_|. Returns true if any
  // such preferences were found and removed.
  bool RemovePreferencesOfMap(const PolicyToPreferenceMapEntry* map,
                              int table_size);

  bool ApplyPolicyFromMap(ConfigurationPolicyType policy,
                          Value* value,
                          const PolicyToPreferenceMapEntry* map,
                          int size);

  // Processes proxy-specific policies. Returns true if the specified policy
  // is a proxy-related policy. ApplyProxyPolicy assumes the ownership
  // of |value| in the case that the policy is proxy-specific.
  bool ApplyProxyPolicy(ConfigurationPolicyType policy, Value* value);

  // Handles sync-related policies. Returns true if the policy was handled.
  // Assumes ownership of |value| in that case.
  bool ApplySyncPolicy(ConfigurationPolicyType policy, Value* value);

  // Handles policies that affect Autofill. Returns true if the policy was
  // handled and assumes ownership of |value| in that case.
  bool ApplyAutofillPolicy(ConfigurationPolicyType policy, Value* value);

  // Processes download directory policy. Returns true if the specified policy
  // is the download directory policy. ApplyDownloadDirPolicy assumes the
  // ownership of |value| in the case that the policy is recognized.
  bool ApplyDownloadDirPolicy(ConfigurationPolicyType policy, Value* value);

  // Make sure that the |path| if present in |prefs_|.  If not, set it to
  // a blank string.
  void EnsureStringPrefExists(const std::string& path);

  // If the required entries for default search are specified and valid,
  // finalizes the policy-specified configuration by initializing the
  // unspecified map entries.  Otherwise wipes all default search related
  // map entries from |prefs_|.
  void FinalizeDefaultSearchPolicySettings();

  // If the required entries for the proxy settings are specified and valid,
  // finalizes the policy-specified configuration by initializing the
  // respective values in |prefs_|.
  void FinalizeProxyPolicySettings();

  // Returns true if the policy values stored in proxy_* represent a valid proxy
  // configuration, including the case in which there is no configuration at
  // all.
  bool CheckProxySettings();

  // Assumes CheckProxySettings returns true and applies the values stored
  // in proxy_*.
  void ApplyProxySettings();

  bool HasProxyPolicy(ConfigurationPolicyType policy) const;

  // Temporary cache that stores values until FinalizeProxyPolicySettings()
  // is called.
  std::map<ConfigurationPolicyType, Value*> proxy_policies_;

  PrefValueMap prefs_;

  static const PolicyToPreferenceMapEntry kSimplePolicyMap[];
  static const PolicyToPreferenceMapEntry kDefaultSearchPolicyMap[];

  DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyPrefKeeper);
};

const ConfigurationPolicyPrefKeeper::PolicyToPreferenceMapEntry
    ConfigurationPolicyPrefKeeper::kSimplePolicyMap[] = {
  { Value::TYPE_STRING, kPolicyHomepageLocation,  prefs::kHomePage },
  { Value::TYPE_BOOLEAN, kPolicyHomepageIsNewTabPage,
    prefs::kHomePageIsNewTabPage },
  { Value::TYPE_INTEGER, kPolicyRestoreOnStartup,
    prefs::kRestoreOnStartup},
  { Value::TYPE_LIST, kPolicyRestoreOnStartupURLs,
    prefs::kURLsToRestoreOnStartup },
  { Value::TYPE_BOOLEAN, kPolicyAlternateErrorPagesEnabled,
    prefs::kAlternateErrorPagesEnabled },
  { Value::TYPE_BOOLEAN, kPolicySearchSuggestEnabled,
    prefs::kSearchSuggestEnabled },
  { Value::TYPE_BOOLEAN, kPolicyDnsPrefetchingEnabled,
    prefs::kNetworkPredictionEnabled },
  { Value::TYPE_BOOLEAN, kPolicyDisableSpdy,
    prefs::kDisableSpdy },
  { Value::TYPE_LIST, kPolicyDisabledSchemes,
    prefs::kDisabledSchemes },
  { Value::TYPE_BOOLEAN, kPolicySafeBrowsingEnabled,
    prefs::kSafeBrowsingEnabled },
  { Value::TYPE_BOOLEAN, kPolicyPasswordManagerEnabled,
    prefs::kPasswordManagerEnabled },
  { Value::TYPE_BOOLEAN, kPolicyPasswordManagerAllowShowPasswords,
    prefs::kPasswordManagerAllowShowPasswords },
  { Value::TYPE_BOOLEAN, kPolicyPrintingEnabled,
    prefs::kPrintingEnabled },
  { Value::TYPE_BOOLEAN, kPolicyMetricsReportingEnabled,
    prefs::kMetricsReportingEnabled },
  { Value::TYPE_STRING, kPolicyApplicationLocaleValue,
    prefs::kApplicationLocale},
  { Value::TYPE_LIST, kPolicyExtensionInstallWhitelist,
    prefs::kExtensionInstallAllowList},
  { Value::TYPE_LIST, kPolicyExtensionInstallBlacklist,
    prefs::kExtensionInstallDenyList},
  { Value::TYPE_LIST, kPolicyExtensionInstallForcelist,
    prefs::kExtensionInstallForceList},
  { Value::TYPE_LIST, kPolicyDisabledPlugins,
    prefs::kPluginsDisabledPlugins},
  { Value::TYPE_LIST, kPolicyDisabledPluginsExceptions,
    prefs::kPluginsDisabledPluginsExceptions},
  { Value::TYPE_LIST, kPolicyEnabledPlugins,
    prefs::kPluginsEnabledPlugins},
  { Value::TYPE_BOOLEAN, kPolicyShowHomeButton,
    prefs::kShowHomeButton },
  { Value::TYPE_BOOLEAN, kPolicyJavascriptEnabled,
    prefs::kWebKitJavascriptEnabled },
  { Value::TYPE_BOOLEAN, kPolicyIncognitoEnabled,
    prefs::kIncognitoEnabled },
  { Value::TYPE_BOOLEAN, kPolicySavingBrowserHistoryDisabled,
    prefs::kSavingBrowserHistoryDisabled },
  { Value::TYPE_BOOLEAN, kPolicyClearSiteDataOnExit,
    prefs::kClearSiteDataOnExit },
  { Value::TYPE_BOOLEAN, kPolicyDeveloperToolsDisabled,
    prefs::kDevToolsDisabled },
  { Value::TYPE_BOOLEAN, kPolicyBlockThirdPartyCookies,
    prefs::kBlockThirdPartyCookies },
  { Value::TYPE_INTEGER, kPolicyDefaultCookiesSetting,
    prefs::kManagedDefaultCookiesSetting },
  { Value::TYPE_INTEGER, kPolicyDefaultImagesSetting,
    prefs::kManagedDefaultImagesSetting },
  { Value::TYPE_INTEGER, kPolicyDefaultJavaScriptSetting,
    prefs::kManagedDefaultJavaScriptSetting },
  { Value::TYPE_INTEGER, kPolicyDefaultPluginsSetting,
    prefs::kManagedDefaultPluginsSetting },
  { Value::TYPE_INTEGER, kPolicyDefaultPopupsSetting,
    prefs::kManagedDefaultPopupsSetting },
  { Value::TYPE_LIST, kPolicyCookiesAllowedForUrls,
    prefs::kManagedCookiesAllowedForUrls },
  { Value::TYPE_LIST, kPolicyCookiesBlockedForUrls,
    prefs::kManagedCookiesBlockedForUrls },
  { Value::TYPE_LIST, kPolicyCookiesSessionOnlyForUrls,
    prefs::kManagedCookiesSessionOnlyForUrls },
  { Value::TYPE_LIST, kPolicyImagesAllowedForUrls,
    prefs::kManagedImagesAllowedForUrls },
  { Value::TYPE_LIST, kPolicyImagesBlockedForUrls,
    prefs::kManagedImagesBlockedForUrls },
  { Value::TYPE_LIST, kPolicyJavaScriptAllowedForUrls,
    prefs::kManagedJavaScriptAllowedForUrls },
  { Value::TYPE_LIST, kPolicyJavaScriptBlockedForUrls,
    prefs::kManagedJavaScriptBlockedForUrls },
  { Value::TYPE_LIST, kPolicyPluginsAllowedForUrls,
    prefs::kManagedPluginsAllowedForUrls },
  { Value::TYPE_LIST, kPolicyPluginsBlockedForUrls,
    prefs::kManagedPluginsBlockedForUrls },
  { Value::TYPE_LIST, kPolicyPopupsAllowedForUrls,
    prefs::kManagedPopupsAllowedForUrls },
  { Value::TYPE_LIST, kPolicyPopupsBlockedForUrls,
    prefs::kManagedPopupsBlockedForUrls },
  { Value::TYPE_INTEGER, kPolicyDefaultNotificationSetting,
    prefs::kDesktopNotificationDefaultContentSetting },
  { Value::TYPE_INTEGER, kPolicyDefaultGeolocationSetting,
    prefs::kGeolocationDefaultContentSetting },
  { Value::TYPE_STRING, kPolicyAuthSchemes,
    prefs::kAuthSchemes },
  { Value::TYPE_BOOLEAN, kPolicyDisableAuthNegotiateCnameLookup,
    prefs::kDisableAuthNegotiateCnameLookup },
  { Value::TYPE_BOOLEAN, kPolicyEnableAuthNegotiatePort,
    prefs::kEnableAuthNegotiatePort },
  { Value::TYPE_STRING, kPolicyAuthServerWhitelist,
    prefs::kAuthServerWhitelist },
  { Value::TYPE_STRING, kPolicyAuthNegotiateDelegateWhitelist,
    prefs::kAuthNegotiateDelegateWhitelist },
  { Value::TYPE_STRING, kPolicyGSSAPILibraryName,
    prefs::kGSSAPILibraryName },
  { Value::TYPE_BOOLEAN, kPolicyDisable3DAPIs,
    prefs::kDisable3DAPIs },
  { Value::TYPE_BOOLEAN, kPolicyDisablePluginFinder,
    prefs::kDisablePluginFinder },
  { Value::TYPE_INTEGER, kPolicyPolicyRefreshRate,
    prefs::kPolicyRefreshRate },
  { Value::TYPE_BOOLEAN, kPolicyInstantEnabled, prefs::kInstantEnabled },
  { Value::TYPE_BOOLEAN, kPolicyDefaultBrowserSettingEnabled,
    prefs::kDefaultBrowserSettingEnabled },
  { Value::TYPE_BOOLEAN, kPolicyCloudPrintProxyEnabled,
    prefs::kCloudPrintProxyEnabled },
  { Value::TYPE_BOOLEAN, kPolicyTranslateEnabled, prefs::kEnableTranslate },
  { Value::TYPE_BOOLEAN, kPolicyBookmarkBarEnabled, prefs::kEnableBookmarkBar },
  { Value::TYPE_BOOLEAN, kPolicyAllowOutdatedPlugins,
    prefs::kPluginsAllowOutdated },
  { Value::TYPE_BOOLEAN, kPolicyEditBookmarksEnabled,
    prefs::kEditBookmarksEnabled },
  { Value::TYPE_BOOLEAN, kPolicyAllowFileSelectionDialogs,
    prefs::kAllowFileSelectionDialogs },

#if defined(OS_CHROMEOS)
  { Value::TYPE_BOOLEAN, kPolicyChromeOsLockOnIdleSuspend,
    prefs::kEnableScreenLock },
#endif
};

const ConfigurationPolicyPrefKeeper::PolicyToPreferenceMapEntry
    ConfigurationPolicyPrefKeeper::kDefaultSearchPolicyMap[] = {
  { Value::TYPE_BOOLEAN, kPolicyDefaultSearchProviderEnabled,
    prefs::kDefaultSearchProviderEnabled },
  { Value::TYPE_STRING, kPolicyDefaultSearchProviderName,
    prefs::kDefaultSearchProviderName },
  { Value::TYPE_STRING, kPolicyDefaultSearchProviderKeyword,
    prefs::kDefaultSearchProviderKeyword },
  { Value::TYPE_STRING, kPolicyDefaultSearchProviderSearchURL,
    prefs::kDefaultSearchProviderSearchURL },
  { Value::TYPE_STRING, kPolicyDefaultSearchProviderSuggestURL,
    prefs::kDefaultSearchProviderSuggestURL },
  { Value::TYPE_STRING, kPolicyDefaultSearchProviderIconURL,
    prefs::kDefaultSearchProviderIconURL },
  { Value::TYPE_STRING, kPolicyDefaultSearchProviderEncodings,
    prefs::kDefaultSearchProviderEncodings },
};

ConfigurationPolicyPrefKeeper::ConfigurationPolicyPrefKeeper(
    ConfigurationPolicyProvider* provider) {
  if (!provider->Provide(this))
    LOG(WARNING) << "Failed to get policy from provider.";
  FinalizeProxyPolicySettings();
  FinalizeDefaultSearchPolicySettings();
}

ConfigurationPolicyPrefKeeper::~ConfigurationPolicyPrefKeeper() {
  DCHECK(proxy_policies_.empty());
}

PrefStore::ReadResult
ConfigurationPolicyPrefKeeper::GetValue(const std::string& key,
                                        const Value** result) const {
  const Value* stored_value = NULL;
  if (!prefs_.GetValue(key, &stored_value))
    return PrefStore::READ_NO_VALUE;

  // Check whether there's a default value, which indicates READ_USE_DEFAULT
  // should be returned.
  if (stored_value->IsType(Value::TYPE_NULL))
    return PrefStore::READ_USE_DEFAULT;

  *result = stored_value;
  return PrefStore::READ_OK;
}

void ConfigurationPolicyPrefKeeper::GetDifferingPrefPaths(
    const ConfigurationPolicyPrefKeeper* other,
    std::vector<std::string>* differing_prefs) const {
  prefs_.GetDifferingKeys(&other->prefs_, differing_prefs);
}

void ConfigurationPolicyPrefKeeper::Apply(ConfigurationPolicyType policy,
                                          Value* value) {
  if (ApplyProxyPolicy(policy, value))
    return;

  if (ApplySyncPolicy(policy, value))
    return;

  if (ApplyAutofillPolicy(policy, value))
    return;

  if (ApplyDownloadDirPolicy(policy, value))
    return;

  if (ApplyPolicyFromMap(policy, value, kDefaultSearchPolicyMap,
                         arraysize(kDefaultSearchPolicyMap)))
    return;

  if (ApplyPolicyFromMap(policy, value, kSimplePolicyMap,
                         arraysize(kSimplePolicyMap)))
    return;

  // Other policy implementations go here.
  NOTIMPLEMENTED();
  delete value;
}

bool ConfigurationPolicyPrefKeeper::RemovePreferencesOfMap(
    const PolicyToPreferenceMapEntry* map, int table_size) {
  bool found_any = false;
  for (int i = 0; i < table_size; ++i) {
    if (prefs_.RemoveValue(map[i].preference_path))
      found_any = true;
  }
  return found_any;
}

bool ConfigurationPolicyPrefKeeper::ApplyPolicyFromMap(
    ConfigurationPolicyType policy,
    Value* value,
    const PolicyToPreferenceMapEntry* map,
    int size) {
  for (int current = 0; current < size; ++current) {
    if (map[current].policy_type == policy) {
      DCHECK_EQ(map[current].value_type, value->GetType())
          << "mismatch in provided and expected policy value for preferences"
          << map[current].preference_path << ". expected = "
          << map[current].value_type << ", actual = "<< value->GetType();
      prefs_.SetValue(map[current].preference_path, value);
      return true;
    }
  }
  return false;
}

bool ConfigurationPolicyPrefKeeper::ApplyProxyPolicy(
    ConfigurationPolicyType policy,
    Value* value) {
  // We only collect the values until we have sufficient information when
  // FinalizeProxyPolicySettings() is called to determine whether the presented
  // values were correct and apply them in that case.
  if (policy == kPolicyProxyMode ||
      policy == kPolicyProxyServerMode ||
      policy == kPolicyProxyServer ||
      policy == kPolicyProxyPacUrl ||
      policy == kPolicyProxyBypassList) {
    delete proxy_policies_[policy];
    proxy_policies_[policy] = value;
    return true;
  }
  // We are not interested in this policy.
  return false;
}

bool ConfigurationPolicyPrefKeeper::ApplySyncPolicy(
    ConfigurationPolicyType policy, Value* value) {
  if (policy == kPolicySyncDisabled) {
    bool disable_sync;
    if (value->GetAsBoolean(&disable_sync) && disable_sync)
      prefs_.SetValue(prefs::kSyncManaged, value);
    else
      delete value;
    return true;
  }
  return false;
}

bool ConfigurationPolicyPrefKeeper::ApplyAutofillPolicy(
    ConfigurationPolicyType policy, Value* value) {
  if (policy == kPolicyAutoFillEnabled) {
    bool auto_fill_enabled;
    if (value->GetAsBoolean(&auto_fill_enabled) && !auto_fill_enabled)
      prefs_.SetValue(prefs::kAutofillEnabled,
                       Value::CreateBooleanValue(false));
    delete value;
    return true;
  }
  return false;
}

bool ConfigurationPolicyPrefKeeper::ApplyDownloadDirPolicy(
    ConfigurationPolicyType policy,
    Value* value) {
  // Replace the policy string which might contain some user variables to an
  // expanded string.
  if (policy == kPolicyDownloadDirectory) {
    FilePath::StringType string_value;
    bool result = value->GetAsString(&string_value);
    DCHECK(result);
    FilePath::StringType expanded_value =
        policy::path_parser::ExpandPathVariables(string_value);
    prefs_.SetValue(prefs::kDownloadDefaultDirectory,
                    Value::CreateStringValue(expanded_value));
    prefs_.SetValue(prefs::kPromptForDownload,
                    Value::CreateBooleanValue(false));
    delete value;
    return true;
  }
  // We are not interested in this policy.
  return false;
}

void ConfigurationPolicyPrefKeeper::EnsureStringPrefExists(
    const std::string& path) {
  std::string value;
  if (!prefs_.GetString(path, &value))
    prefs_.SetString(path, value);
}

namespace {

// Implementation of SearchTermsData just for validation.
class SearchTermsDataForValidation : public SearchTermsData {
 public:
  SearchTermsDataForValidation() {}

  // Implementation of SearchTermsData.
  virtual std::string GoogleBaseURLValue() const {
    return "http://www.google.com/";
  }
  virtual std::string GetApplicationLocale() const {
    return "en";
  }
#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
  virtual string16 GetRlzParameterValue() const {
    return string16();
  }
#endif
 private:
  DISALLOW_COPY_AND_ASSIGN(SearchTermsDataForValidation);
};

}  // namespace

void ConfigurationPolicyPrefKeeper::FinalizeDefaultSearchPolicySettings() {
  bool enabled = true;
  if (prefs_.GetBoolean(prefs::kDefaultSearchProviderEnabled, &enabled) &&
      !enabled) {
    // If default search is disabled, we ignore the other fields.
    prefs_.SetString(prefs::kDefaultSearchProviderName, std::string());
    prefs_.SetString(prefs::kDefaultSearchProviderSearchURL, std::string());
    prefs_.SetString(prefs::kDefaultSearchProviderSuggestURL, std::string());
    prefs_.SetString(prefs::kDefaultSearchProviderIconURL, std::string());
    prefs_.SetString(prefs::kDefaultSearchProviderEncodings, std::string());
    prefs_.SetString(prefs::kDefaultSearchProviderKeyword, std::string());
    prefs_.SetString(prefs::kDefaultSearchProviderInstantURL, std::string());
    return;
  }
  std::string search_url;
  // The search URL is required.
  if (prefs_.GetString(prefs::kDefaultSearchProviderSearchURL, &search_url) &&
      !search_url.empty()) {
    SearchTermsDataForValidation search_terms_data;
    const TemplateURLRef search_url_ref(search_url, 0, 0);
    // It must support replacement (which implies it is valid).
    if (search_url_ref.SupportsReplacementUsingTermsData(search_terms_data)) {
      // The other entries are optional.  Just make sure that they are all
      // specified via policy, so that we don't use regular prefs.
      EnsureStringPrefExists(prefs::kDefaultSearchProviderSuggestURL);
      EnsureStringPrefExists(prefs::kDefaultSearchProviderIconURL);
      EnsureStringPrefExists(prefs::kDefaultSearchProviderEncodings);
      EnsureStringPrefExists(prefs::kDefaultSearchProviderKeyword);
      EnsureStringPrefExists(prefs::kDefaultSearchProviderInstantURL);

      // For the name, default to the host if not specified.
      std::string name;
      if (!prefs_.GetString(prefs::kDefaultSearchProviderName, &name) ||
          name.empty())
        prefs_.SetString(prefs::kDefaultSearchProviderName,
                          GURL(search_url).host());

      // And clear the IDs since these are not specified via policy.
      prefs_.SetString(prefs::kDefaultSearchProviderID, std::string());
      prefs_.SetString(prefs::kDefaultSearchProviderPrepopulateID,
                        std::string());
      return;
    }
  }
  // Required entries are not there.  Remove any related entries.
  RemovePreferencesOfMap(kDefaultSearchPolicyMap,
                         arraysize(kDefaultSearchPolicyMap));
}

void ConfigurationPolicyPrefKeeper::FinalizeProxyPolicySettings() {
  if (CheckProxySettings())
    ApplyProxySettings();

  STLDeleteContainerPairSecondPointers(proxy_policies_.begin(),
                                       proxy_policies_.end());
  proxy_policies_.clear();
}

bool ConfigurationPolicyPrefKeeper::CheckProxySettings() {
  bool mode = HasProxyPolicy(kPolicyProxyMode);
  bool server_mode = HasProxyPolicy(kPolicyProxyServerMode);  // deprecated
  bool server = HasProxyPolicy(kPolicyProxyServer);
  bool pac_url = HasProxyPolicy(kPolicyProxyPacUrl);
  bool bypass_list = HasProxyPolicy(kPolicyProxyBypassList);

  if ((server || pac_url || bypass_list) && !(mode || server_mode)) {
    LOG(WARNING) << "A centrally-administered policy defines proxy setting"
                 << " details without setting a proxy mode.";
    return false;
  }

  // If there's a server mode, convert it into a mode.
  std::string mode_value;
  if (mode) {
    if (server_mode)
      LOG(WARNING) << "Both ProxyMode and ProxyServerMode policies defined, "
                   << "ignoring ProxyMode.";
    if (!proxy_policies_[kPolicyProxyMode]->GetAsString(&mode_value)) {
      LOG(WARNING) << "Invalid ProxyMode value.";
      return false;
    }
  } else if (server_mode) {
    int server_mode_value;
    if (!proxy_policies_[kPolicyProxyServerMode]->GetAsInteger(
        &server_mode_value)) {
      LOG(WARNING) << "Invalid ProxyServerMode value.";
      return false;
    }

    switch (server_mode_value) {
      case kPolicyNoProxyServerMode:
        mode_value = ProxyPrefs::kDirectProxyModeName;
        break;
      case kPolicyAutoDetectProxyServerMode:
        mode_value = ProxyPrefs::kAutoDetectProxyModeName;
        break;
      case kPolicyManuallyConfiguredProxyServerMode:
        if (server && pac_url) {
          LOG(WARNING) << "A centrally-administered policy dictates that"
                       << " both fixed proxy servers and a .pac url. should"
                       << " be used for proxy configuration.";
          return false;
        }
        if (!server && !pac_url) {
          LOG(WARNING) << "A centrally-administered policy dictates that the"
                       << " proxy settings should use either fixed proxy"
                       << " servers or a .pac url, but specifies neither.";
          return false;
        }
        if (pac_url)
          mode_value = ProxyPrefs::kPacScriptProxyModeName;
        else
          mode_value = ProxyPrefs::kFixedServersProxyModeName;
        break;
      case kPolicyUseSystemProxyServerMode:
        mode_value = ProxyPrefs::kSystemProxyModeName;
        break;
      default:
        LOG(WARNING) << "Invalid proxy mode " << server_mode_value;
        return false;
    }
  }

  // If neither ProxyMode nor ProxyServerMode are specified, mode_value will be
  // empty and the proxy shouldn't be configured at all.
  if (mode_value.empty())
    return true;

  if (mode_value == ProxyPrefs::kDirectProxyModeName) {
    if (server || pac_url || bypass_list) {
      LOG(WARNING) << "A centrally-administered policy disables the use of"
                   << " a proxy but also specifies an explicit proxy"
                   << " configuration.";
      return false;
    }
  } else if (mode_value == ProxyPrefs::kAutoDetectProxyModeName) {
    if (server || bypass_list || pac_url) {
      LOG(WARNING) << "A centrally-administered policy dictates that a proxy"
                   << " shall be auto configured but specifies fixed proxy"
                   << " servers, a by-pass list or a .pac script URL.";
      return false;
    }
  } else if (mode_value == ProxyPrefs::kPacScriptProxyModeName) {
    if (server || bypass_list) {
      LOG(WARNING) << "A centrally-administered policy dictates that a .pac"
                   << " script URL should be used for proxy configuration but"
                   << " also specifies policies required only for fixed"
                   << " proxy servers.";
      return false;
    }
  } else if (mode_value == ProxyPrefs::kFixedServersProxyModeName) {
    if (pac_url) {
      LOG(WARNING) << "A centrally-administered policy dictates that"
                   << " fixed proxy servers should be used but also"
                   << " specifies a .pac script URL.";
      return false;
    }
  } else if (mode_value == ProxyPrefs::kSystemProxyModeName) {
    if (server || pac_url || bypass_list) {
      LOG(WARNING) << "A centrally-administered policy dictates that the"
                   << " system proxy settings should be used but also "
                   << " specifies an explicit proxy configuration.";
      return false;
    }
  } else {
    LOG(WARNING) << "Invalid proxy mode " << mode_value;
    return false;
  }
  return true;
}

void ConfigurationPolicyPrefKeeper::ApplyProxySettings() {
  ProxyPrefs::ProxyMode mode;
  if (HasProxyPolicy(kPolicyProxyMode)) {
    std::string string_mode;
    CHECK(proxy_policies_[kPolicyProxyMode]->GetAsString(&string_mode));
    if (!ProxyPrefs::StringToProxyMode(string_mode, &mode)) {
      LOG(WARNING) << "A centrally-administered policy specifies a value for"
                   << "the ProxyMode policy that isn't recognized.";
      return;
    }
  } else if (HasProxyPolicy(kPolicyProxyServerMode)) {
    int int_mode = 0;
    CHECK(proxy_policies_[kPolicyProxyServerMode]->GetAsInteger(&int_mode));
    switch (int_mode) {
      case kPolicyNoProxyServerMode:
        mode = ProxyPrefs::MODE_DIRECT;
        break;
      case kPolicyAutoDetectProxyServerMode:
        mode = ProxyPrefs::MODE_AUTO_DETECT;
        break;
      case kPolicyManuallyConfiguredProxyServerMode:
        mode = ProxyPrefs::MODE_FIXED_SERVERS;
        if (HasProxyPolicy(kPolicyProxyPacUrl))
          mode = ProxyPrefs::MODE_PAC_SCRIPT;
        break;
      case kPolicyUseSystemProxyServerMode:
        mode = ProxyPrefs::MODE_SYSTEM;
        break;
      default:
        mode = ProxyPrefs::MODE_DIRECT;
        NOTREACHED();
    }
  } else {
    return;
  }
  switch (mode) {
    case ProxyPrefs::MODE_DIRECT:
      prefs_.SetValue(prefs::kProxy, ProxyConfigDictionary::CreateDirect());
      break;
    case ProxyPrefs::MODE_AUTO_DETECT:
      prefs_.SetValue(prefs::kProxy, ProxyConfigDictionary::CreateAutoDetect());
      break;
    case ProxyPrefs::MODE_PAC_SCRIPT: {
      if (!HasProxyPolicy(kPolicyProxyPacUrl)) {
        LOG(WARNING) << "A centrally-administered policy specifies to use a "
                     << "PAC script, but doesn't supply the PAC script URL.";
        return;
      }
      std::string pac_url;
      proxy_policies_[kPolicyProxyPacUrl]->GetAsString(&pac_url);
      prefs_.SetValue(prefs::kProxy,
                      ProxyConfigDictionary::CreatePacScript(pac_url));
      break;
    }
    case ProxyPrefs::MODE_FIXED_SERVERS: {
      if (!HasProxyPolicy(kPolicyProxyServer)) {
        LOG(WARNING) << "A centrally-administered policy specifies to use a "
                     << "fixed server, but doesn't supply the server address.";
        return;
      }
      std::string proxy_server;
      proxy_policies_[kPolicyProxyServer]->GetAsString(&proxy_server);
      std::string bypass_list;
      if (HasProxyPolicy(kPolicyProxyBypassList))
        proxy_policies_[kPolicyProxyBypassList]->GetAsString(&bypass_list);
      prefs_.SetValue(prefs::kProxy,
                      ProxyConfigDictionary::CreateFixedServers(proxy_server,
                                                                bypass_list));
      break;
    }
    case ProxyPrefs::MODE_SYSTEM:
      prefs_.SetValue(prefs::kProxy,
                      ProxyConfigDictionary::CreateSystem());
      break;
    case ProxyPrefs::kModeCount:
      NOTREACHED();
  }
}

bool ConfigurationPolicyPrefKeeper::HasProxyPolicy(
    ConfigurationPolicyType policy) const {
  std::map<ConfigurationPolicyType, Value*>::const_iterator iter;
  iter = proxy_policies_.find(policy);
  std::string tmp;
  if (iter == proxy_policies_.end() ||
      !iter->second ||
      iter->second->IsType(Value::TYPE_NULL) ||
      (iter->second->IsType(Value::TYPE_STRING) &&
       iter->second->GetAsString(&tmp) &&
       tmp.empty())) {
    return false;
  }
  return true;
}

ConfigurationPolicyPrefStore::ConfigurationPolicyPrefStore(
    ConfigurationPolicyProvider* provider)
    : provider_(provider),
      initialization_complete_(false) {
  if (provider_) {
    // Read initial policy.
    policy_keeper_.reset(new ConfigurationPolicyPrefKeeper(provider));
    registrar_.Init(provider_, this);
    initialization_complete_ = provider->IsInitializationComplete();
  } else {
    initialization_complete_ = true;
  }
}

ConfigurationPolicyPrefStore::~ConfigurationPolicyPrefStore() {
}

void ConfigurationPolicyPrefStore::AddObserver(PrefStore::Observer* observer) {
  observers_.AddObserver(observer);
}

void ConfigurationPolicyPrefStore::RemoveObserver(
    PrefStore::Observer* observer) {
  observers_.RemoveObserver(observer);
}

bool ConfigurationPolicyPrefStore::IsInitializationComplete() const {
  return initialization_complete_;
}

PrefStore::ReadResult
ConfigurationPolicyPrefStore::GetValue(const std::string& key,
                                       const Value** value) const {
  if (policy_keeper_.get())
    return policy_keeper_->GetValue(key, value);

  return PrefStore::READ_NO_VALUE;
}

void ConfigurationPolicyPrefStore::OnUpdatePolicy() {
  Refresh();
}

void ConfigurationPolicyPrefStore::OnProviderGoingAway() {
  provider_ = NULL;
}

// static
ConfigurationPolicyPrefStore*
ConfigurationPolicyPrefStore::CreateManagedPlatformPolicyPrefStore() {
  BrowserPolicyConnector* connector =
      g_browser_process->browser_policy_connector();
  return new ConfigurationPolicyPrefStore(
      connector->GetManagedPlatformProvider());
}

// static
ConfigurationPolicyPrefStore*
ConfigurationPolicyPrefStore::CreateManagedCloudPolicyPrefStore(
    Profile* profile) {
  ConfigurationPolicyProvider* provider = NULL;
  if (profile) {
    // For user policy, return the profile's policy provider.
    provider = profile->GetPolicyConnector()->GetManagedCloudProvider();
  } else {
    // For device policy, return the provider of the browser process.
    BrowserPolicyConnector* connector =
        g_browser_process->browser_policy_connector();
    provider = connector->GetManagedCloudProvider();
  }
  return new ConfigurationPolicyPrefStore(provider);
}

// static
ConfigurationPolicyPrefStore*
ConfigurationPolicyPrefStore::CreateRecommendedPlatformPolicyPrefStore() {
  BrowserPolicyConnector* connector =
      g_browser_process->browser_policy_connector();
  return new ConfigurationPolicyPrefStore(
      connector->GetRecommendedPlatformProvider());
}

// static
ConfigurationPolicyPrefStore*
ConfigurationPolicyPrefStore::CreateRecommendedCloudPolicyPrefStore(
    Profile* profile) {
  ConfigurationPolicyProvider* provider = NULL;
  if (profile) {
    // For user policy, return the profile's policy provider.
    provider = profile->GetPolicyConnector()->GetRecommendedCloudProvider();
  } else {
    // For device policy, return the provider of the browser process.
    BrowserPolicyConnector* connector =
        g_browser_process->browser_policy_connector();
    provider = connector->GetRecommendedCloudProvider();
  }
  return new ConfigurationPolicyPrefStore(provider);
}

/* static */
const ConfigurationPolicyProvider::PolicyDefinitionList*
ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList() {
  static ConfigurationPolicyProvider::PolicyDefinitionList::Entry entries[] = {
    { kPolicyHomepageLocation, Value::TYPE_STRING, key::kHomepageLocation },
    { kPolicyHomepageIsNewTabPage, Value::TYPE_BOOLEAN,
      key::kHomepageIsNewTabPage },
    { kPolicyRestoreOnStartup, Value::TYPE_INTEGER, key::kRestoreOnStartup },
    { kPolicyRestoreOnStartupURLs, Value::TYPE_LIST,
      key::kRestoreOnStartupURLs },
    { kPolicyDefaultSearchProviderEnabled, Value::TYPE_BOOLEAN,
      key::kDefaultSearchProviderEnabled },
    { kPolicyDefaultSearchProviderName, Value::TYPE_STRING,
      key::kDefaultSearchProviderName },
    { kPolicyDefaultSearchProviderKeyword, Value::TYPE_STRING,
      key::kDefaultSearchProviderKeyword },
    { kPolicyDefaultSearchProviderSearchURL, Value::TYPE_STRING,
      key::kDefaultSearchProviderSearchURL },
    { kPolicyDefaultSearchProviderSuggestURL, Value::TYPE_STRING,
      key::kDefaultSearchProviderSuggestURL },
    { kPolicyDefaultSearchProviderInstantURL, Value::TYPE_STRING,
      key::kDefaultSearchProviderInstantURL },
    { kPolicyDefaultSearchProviderIconURL, Value::TYPE_STRING,
      key::kDefaultSearchProviderIconURL },
    { kPolicyDefaultSearchProviderEncodings, Value::TYPE_STRING,
      key::kDefaultSearchProviderEncodings },
    { kPolicyProxyMode, Value::TYPE_STRING, key::kProxyMode },
    { kPolicyProxyServerMode, Value::TYPE_INTEGER, key::kProxyServerMode },
    { kPolicyProxyServer, Value::TYPE_STRING, key::kProxyServer },
    { kPolicyProxyPacUrl, Value::TYPE_STRING, key::kProxyPacUrl },
    { kPolicyProxyBypassList, Value::TYPE_STRING, key::kProxyBypassList },
    { kPolicyAlternateErrorPagesEnabled, Value::TYPE_BOOLEAN,
      key::kAlternateErrorPagesEnabled },
    { kPolicySearchSuggestEnabled, Value::TYPE_BOOLEAN,
      key::kSearchSuggestEnabled },
    { kPolicyDnsPrefetchingEnabled, Value::TYPE_BOOLEAN,
      key::kDnsPrefetchingEnabled },
    { kPolicyDisableSpdy, Value::TYPE_BOOLEAN, key::kDisableSpdy },
    { kPolicyDisabledSchemes, Value::TYPE_LIST, key::kDisabledSchemes },
    { kPolicySafeBrowsingEnabled, Value::TYPE_BOOLEAN,
      key::kSafeBrowsingEnabled },
    { kPolicyMetricsReportingEnabled, Value::TYPE_BOOLEAN,
      key::kMetricsReportingEnabled },
    { kPolicyPasswordManagerEnabled, Value::TYPE_BOOLEAN,
      key::kPasswordManagerEnabled },
    { kPolicyPasswordManagerAllowShowPasswords, Value::TYPE_BOOLEAN,
      key::kPasswordManagerAllowShowPasswords },
    { kPolicyAutoFillEnabled, Value::TYPE_BOOLEAN, key::kAutoFillEnabled },
    { kPolicyDisabledPlugins, Value::TYPE_LIST, key::kDisabledPlugins },
    { kPolicyDisabledPluginsExceptions, Value::TYPE_LIST,
      key::kDisabledPluginsExceptions },
    { kPolicyEnabledPlugins, Value::TYPE_LIST, key::kEnabledPlugins },
    { kPolicyApplicationLocaleValue, Value::TYPE_STRING,
      key::kApplicationLocaleValue },
    { kPolicySyncDisabled, Value::TYPE_BOOLEAN, key::kSyncDisabled },
    { kPolicyExtensionInstallWhitelist, Value::TYPE_LIST,
      key::kExtensionInstallWhitelist },
    { kPolicyExtensionInstallBlacklist, Value::TYPE_LIST,
      key::kExtensionInstallBlacklist },
    { kPolicyExtensionInstallForcelist, Value::TYPE_LIST,
      key::kExtensionInstallForcelist },
    { kPolicyShowHomeButton, Value::TYPE_BOOLEAN, key::kShowHomeButton },
    { kPolicyPrintingEnabled, Value::TYPE_BOOLEAN, key::kPrintingEnabled },
    { kPolicyJavascriptEnabled, Value::TYPE_BOOLEAN, key::kJavascriptEnabled },
    { kPolicyIncognitoEnabled, Value::TYPE_BOOLEAN, key::kIncognitoEnabled },
    { kPolicySavingBrowserHistoryDisabled, Value::TYPE_BOOLEAN,
      key::kSavingBrowserHistoryDisabled },
    { kPolicyClearSiteDataOnExit, Value::TYPE_BOOLEAN,
      key::kClearSiteDataOnExit },
    { kPolicyDeveloperToolsDisabled, Value::TYPE_BOOLEAN,
      key::kDeveloperToolsDisabled },
    { kPolicyBlockThirdPartyCookies, Value::TYPE_BOOLEAN,
      key::kBlockThirdPartyCookies },
    { kPolicyDefaultCookiesSetting, Value::TYPE_INTEGER,
      key::kDefaultCookiesSetting },
    { kPolicyDefaultImagesSetting, Value::TYPE_INTEGER,
      key::kDefaultImagesSetting },
    { kPolicyDefaultJavaScriptSetting, Value::TYPE_INTEGER,
      key::kDefaultJavaScriptSetting },
    { kPolicyDefaultPluginsSetting, Value::TYPE_INTEGER,
      key::kDefaultPluginsSetting },
    { kPolicyDefaultPopupsSetting, Value::TYPE_INTEGER,
      key::kDefaultPopupsSetting },
    { kPolicyDefaultNotificationSetting, Value::TYPE_INTEGER,
      key::kDefaultNotificationSetting },
    { kPolicyDefaultGeolocationSetting, Value::TYPE_INTEGER,
      key::kDefaultGeolocationSetting },
    { kPolicyCookiesAllowedForUrls, Value::TYPE_LIST,
      key::kCookiesAllowedForUrls },
    { kPolicyCookiesBlockedForUrls, Value::TYPE_LIST,
      key::kCookiesBlockedForUrls },
    { kPolicyCookiesSessionOnlyForUrls, Value::TYPE_LIST,
      key::kCookiesSessionOnlyForUrls },
    { kPolicyImagesAllowedForUrls, Value::TYPE_LIST,
      key::kImagesAllowedForUrls },
    { kPolicyImagesBlockedForUrls, Value::TYPE_LIST,
      key::kImagesBlockedForUrls },
    { kPolicyJavaScriptAllowedForUrls, Value::TYPE_LIST,
      key::kJavaScriptAllowedForUrls },
    { kPolicyJavaScriptBlockedForUrls, Value::TYPE_LIST,
      key::kJavaScriptBlockedForUrls },
    { kPolicyPluginsAllowedForUrls, Value::TYPE_LIST,
      key::kPluginsAllowedForUrls },
    { kPolicyPluginsBlockedForUrls, Value::TYPE_LIST,
      key::kPluginsBlockedForUrls },
    { kPolicyPopupsAllowedForUrls, Value::TYPE_LIST,
      key::kPopupsAllowedForUrls },
    { kPolicyPopupsBlockedForUrls, Value::TYPE_LIST,
      key::kPopupsBlockedForUrls },
    { kPolicyAuthSchemes, Value::TYPE_STRING, key::kAuthSchemes },
    { kPolicyDisableAuthNegotiateCnameLookup, Value::TYPE_BOOLEAN,
      key::kDisableAuthNegotiateCnameLookup },
    { kPolicyEnableAuthNegotiatePort, Value::TYPE_BOOLEAN,
      key::kEnableAuthNegotiatePort },
    { kPolicyAuthServerWhitelist, Value::TYPE_STRING,
      key::kAuthServerWhitelist },
    { kPolicyAuthNegotiateDelegateWhitelist, Value::TYPE_STRING,
      key::kAuthNegotiateDelegateWhitelist },
    { kPolicyGSSAPILibraryName, Value::TYPE_STRING,
      key::kGSSAPILibraryName },
    { kPolicyDisable3DAPIs, Value::TYPE_BOOLEAN,
      key::kDisable3DAPIs },
    { kPolicyDisablePluginFinder, Value::TYPE_BOOLEAN,
      key::kDisablePluginFinder },
    { kPolicyPolicyRefreshRate, Value::TYPE_INTEGER,
      key::kPolicyRefreshRate },
    { kPolicyInstantEnabled, Value::TYPE_BOOLEAN, key::kInstantEnabled },
    { kPolicyDefaultBrowserSettingEnabled, Value::TYPE_BOOLEAN,
      key::kDefaultBrowserSettingEnabled },
    { kPolicyCloudPrintProxyEnabled, Value::TYPE_BOOLEAN,
      key::kCloudPrintProxyEnabled },
    { kPolicyDownloadDirectory, Value::TYPE_STRING,
      key::kDownloadDirectory },
    { kPolicyTranslateEnabled, Value::TYPE_BOOLEAN, key::kTranslateEnabled },
    { kPolicyAllowOutdatedPlugins, Value::TYPE_BOOLEAN,
      key::kAllowOutdatedPlugins },
    { kPolicyBookmarkBarEnabled, Value::TYPE_BOOLEAN,
      key::kBookmarkBarEnabled },
    { kPolicyEditBookmarksEnabled, Value::TYPE_BOOLEAN,
      key::kEditBookmarksEnabled },
    { kPolicyAllowFileSelectionDialogs, Value::TYPE_BOOLEAN,
      key::kAllowFileSelectionDialogs },

#if defined(OS_CHROMEOS)
    { kPolicyChromeOsLockOnIdleSuspend, Value::TYPE_BOOLEAN,
      key::kChromeOsLockOnIdleSuspend },
#endif
  };

  static ConfigurationPolicyProvider::PolicyDefinitionList policy_list = {
    entries,
    entries + arraysize(entries),
  };
  return &policy_list;
}

void ConfigurationPolicyPrefStore::Refresh() {
  if (!provider_)
    return;

  // Construct a new keeper, determine what changed and swap the keeper in.
  scoped_ptr<ConfigurationPolicyPrefKeeper> new_keeper(
      new ConfigurationPolicyPrefKeeper(provider_));
  std::vector<std::string> changed_prefs;
  new_keeper->GetDifferingPrefPaths(policy_keeper_.get(), &changed_prefs);
  policy_keeper_.reset(new_keeper.release());

  // Send out change notifications.
  for (std::vector<std::string>::const_iterator pref(changed_prefs.begin());
       pref != changed_prefs.end();
       ++pref) {
    FOR_EACH_OBSERVER(PrefStore::Observer, observers_,
                      OnPrefValueChanged(*pref));
  }

  // Update the initialization flag.
  if (!initialization_complete_ &&
      provider_->IsInitializationComplete()) {
    initialization_complete_ = true;
    FOR_EACH_OBSERVER(PrefStore::Observer, observers_,
                      OnInitializationCompleted());
  }
}

}  // namespace policy