| Index: chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
 | 
| diff --git a/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..9977b5b1c03aca872f09622e857c452fd3bdb73c
 | 
| --- /dev/null
 | 
| +++ b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
 | 
| @@ -0,0 +1,313 @@
 | 
| +// Copyright 2013 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/policy/cloud_external_data_policy_observer.h"
 | 
| +
 | 
| +#include <set>
 | 
| +#include <vector>
 | 
| +
 | 
| +#include "base/bind.h"
 | 
| +#include "base/bind_helpers.h"
 | 
| +#include "base/logging.h"
 | 
| +#include "base/stl_util.h"
 | 
| +#include "base/values.h"
 | 
| +#include "chrome/browser/chrome_notification_types.h"
 | 
| +#include "chrome/browser/chromeos/login/user.h"
 | 
| +#include "chrome/browser/chromeos/login/user_manager.h"
 | 
| +#include "chrome/browser/chromeos/policy/device_local_account.h"
 | 
| +#include "chrome/browser/policy/cloud/cloud_policy_core.h"
 | 
| +#include "chrome/browser/policy/cloud/cloud_policy_store.h"
 | 
| +#include "chrome/browser/policy/policy_service.h"
 | 
| +#include "chrome/browser/policy/profile_policy_connector.h"
 | 
| +#include "chrome/browser/policy/profile_policy_connector_factory.h"
 | 
| +#include "chrome/browser/profiles/profile.h"
 | 
| +#include "chromeos/settings/cros_settings_names.h"
 | 
| +#include "chromeos/settings/cros_settings_provider.h"
 | 
| +#include "components/policy/core/common/external_data_fetcher.h"
 | 
| +#include "components/policy/core/common/policy_namespace.h"
 | 
| +#include "content/public/browser/notification_details.h"
 | 
| +#include "content/public/browser/notification_service.h"
 | 
| +#include "content/public/browser/notification_source.h"
 | 
| +
 | 
| +namespace policy {
 | 
| +
 | 
| +// Helper class that observes a policy for a logged-in user, notifying the
 | 
| +// |parent_| whenever the external data reference for this user changes.
 | 
| +class CloudExternalDataPolicyObserver::PolicyServiceObserver
 | 
| +    : public PolicyService::Observer {
 | 
| + public:
 | 
| +  PolicyServiceObserver(CloudExternalDataPolicyObserver* parent,
 | 
| +                        const std::string& user_id,
 | 
| +                        PolicyService* policy_service);
 | 
| +  virtual ~PolicyServiceObserver();
 | 
| +
 | 
| +  // PolicyService::Observer:
 | 
| +  virtual void OnPolicyUpdated(const PolicyNamespace& ns,
 | 
| +                               const PolicyMap& previous,
 | 
| +                               const PolicyMap& current) OVERRIDE;
 | 
| +
 | 
| + private:
 | 
| +  CloudExternalDataPolicyObserver* parent_;
 | 
| +  const std::string user_id_;
 | 
| +  PolicyService* policy_service_;
 | 
| +
 | 
| +  DISALLOW_COPY_AND_ASSIGN(PolicyServiceObserver);
 | 
| +};
 | 
| +
 | 
| +CloudExternalDataPolicyObserver::PolicyServiceObserver::PolicyServiceObserver(
 | 
| +    CloudExternalDataPolicyObserver* parent,
 | 
| +    const std::string& user_id,
 | 
| +    PolicyService* policy_service)
 | 
| +    : parent_(parent),
 | 
| +      user_id_(user_id),
 | 
| +      policy_service_(policy_service) {
 | 
| +  policy_service_->AddObserver(POLICY_DOMAIN_CHROME, this);
 | 
| +
 | 
| +  if (!IsDeviceLocalAccountUser(user_id, NULL)) {
 | 
| +    // Notify |parent_| if the external data reference for |user_id_| is set
 | 
| +    // during login. This is omitted for device-local accounts because their
 | 
| +    // policy is available before login and the external data reference will
 | 
| +    // have been seen by the |parent_| already.
 | 
| +    const PolicyMap::Entry* entry = policy_service_->GetPolicies(
 | 
| +        PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
 | 
| +            .Get(parent_->policy_);
 | 
| +    if (entry)
 | 
| +      parent_->HandleExternalDataPolicyUpdate(user_id_, entry);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +CloudExternalDataPolicyObserver::PolicyServiceObserver::
 | 
| +    ~PolicyServiceObserver() {
 | 
| +  policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::PolicyServiceObserver::OnPolicyUpdated(
 | 
| +    const PolicyNamespace& ns,
 | 
| +    const PolicyMap& previous,
 | 
| +    const PolicyMap& current) {
 | 
| +  DCHECK(ns == PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
 | 
| +
 | 
| +  const PolicyMap::Entry* previous_entry = previous.Get(parent_->policy_);
 | 
| +  const PolicyMap::Entry* current_entry = current.Get(parent_->policy_);
 | 
| +  if ((!previous_entry && current_entry) ||
 | 
| +      (previous_entry && !current_entry) ||
 | 
| +      (previous_entry && current_entry &&
 | 
| +           !previous_entry->Equals(*current_entry))) {
 | 
| +    // Notify |parent_| if the external data reference for |user_id_| has
 | 
| +    // changed.
 | 
| +    parent_->HandleExternalDataPolicyUpdate(user_id_, current_entry);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::Delegate::OnExternalDataSet(
 | 
| +    const std::string& policy,
 | 
| +    const std::string& user_id) {
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::Delegate::OnExternalDataCleared(
 | 
| +    const std::string& policy,
 | 
| +    const std::string& user_id) {
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::Delegate::OnExternalDataFetched(
 | 
| +    const std::string& policy,
 | 
| +    const std::string& user_id,
 | 
| +    scoped_ptr<std::string> data) {
 | 
| +}
 | 
| +
 | 
| +CloudExternalDataPolicyObserver::Delegate::~Delegate() {
 | 
| +}
 | 
| +
 | 
| +CloudExternalDataPolicyObserver::CloudExternalDataPolicyObserver(
 | 
| +    chromeos::CrosSettings* cros_settings,
 | 
| +    chromeos::UserManager* user_manager,
 | 
| +    DeviceLocalAccountPolicyService* device_local_account_policy_service,
 | 
| +    const std::string& policy,
 | 
| +    Delegate* delegate)
 | 
| +    : cros_settings_(cros_settings),
 | 
| +      user_manager_(user_manager),
 | 
| +      device_local_account_policy_service_(device_local_account_policy_service),
 | 
| +      policy_(policy),
 | 
| +      delegate_(delegate),
 | 
| +      weak_factory_(this) {
 | 
| +  notification_registrar_.Add(
 | 
| +      this,
 | 
| +      chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
 | 
| +      content::NotificationService::AllSources());
 | 
| +
 | 
| +  if (device_local_account_policy_service_)
 | 
| +    device_local_account_policy_service_->AddObserver(this);
 | 
| +
 | 
| +  device_local_accounts_subscription_ = cros_settings_->AddSettingsObserver(
 | 
| +      chromeos::kAccountsPrefDeviceLocalAccounts,
 | 
| +      base::Bind(&CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts,
 | 
| +                 base::Unretained(this)));
 | 
| +}
 | 
| +
 | 
| +CloudExternalDataPolicyObserver::~CloudExternalDataPolicyObserver() {
 | 
| +  if (device_local_account_policy_service_)
 | 
| +    device_local_account_policy_service_->RemoveObserver(this);
 | 
| +  for (DeviceLocalAccountEntryMap::iterator it =
 | 
| +           device_local_account_entries_.begin();
 | 
| +       it != device_local_account_entries_.end(); ++it) {
 | 
| +    it->second.DeleteOwnedMembers();
 | 
| +  }
 | 
| +  device_local_account_entries_.clear();
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::Init() {
 | 
| +  RetrieveDeviceLocalAccounts();
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::Observe(
 | 
| +    int type,
 | 
| +    const content::NotificationSource& source,
 | 
| +    const content::NotificationDetails& details) {
 | 
| +  if (type != chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) {
 | 
| +    NOTREACHED();
 | 
| +    return;
 | 
| +  }
 | 
| +  Profile* profile = content::Details<Profile>(details).ptr();
 | 
| +
 | 
| +  const chromeos::User* user = user_manager_->GetUserByProfile(profile);
 | 
| +  if (!user) {
 | 
| +    NOTREACHED();
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  const std::string& user_id = user->email();
 | 
| +  if (ContainsKey(logged_in_user_observers_, user_id)) {
 | 
| +    NOTREACHED();
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  ProfilePolicyConnector* policy_connector =
 | 
| +      ProfilePolicyConnectorFactory::GetForProfile(profile);
 | 
| +  logged_in_user_observers_[user_id] = make_linked_ptr(
 | 
| +      new PolicyServiceObserver(this,
 | 
| +                                user_id,
 | 
| +                                policy_connector->policy_service()));
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::OnPolicyUpdated(
 | 
| +    const std::string& user_id) {
 | 
| +  if (ContainsKey(logged_in_user_observers_, user_id)) {
 | 
| +    // When a device-local account is logged in, a policy change triggers both
 | 
| +    // OnPolicyUpdated() and PolicyServiceObserver::OnPolicyUpdated(). Ignore
 | 
| +    // the former so that the policy change is handled only once.
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  if (!device_local_account_policy_service_) {
 | 
| +    NOTREACHED();
 | 
| +    return;
 | 
| +  }
 | 
| +  DeviceLocalAccountPolicyBroker* broker =
 | 
| +      device_local_account_policy_service_->GetBrokerForUser(user_id);
 | 
| +  if (!broker) {
 | 
| +    NOTREACHED();
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  const PolicyMap::Entry* entry =
 | 
| +      broker->core()->store()->policy_map().Get(policy_);
 | 
| +  if (!entry) {
 | 
| +    DeviceLocalAccountEntryMap::iterator it =
 | 
| +        device_local_account_entries_.find(user_id);
 | 
| +    if (it != device_local_account_entries_.end()) {
 | 
| +      it->second.DeleteOwnedMembers();
 | 
| +      device_local_account_entries_.erase(it);
 | 
| +      HandleExternalDataPolicyUpdate(user_id, NULL);
 | 
| +    }
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  PolicyMap::Entry& map_entry = device_local_account_entries_[user_id];
 | 
| +  if (map_entry.Equals(*entry))
 | 
| +    return;
 | 
| +
 | 
| +  map_entry.DeleteOwnedMembers();
 | 
| +  map_entry = *entry->DeepCopy();
 | 
| +  HandleExternalDataPolicyUpdate(user_id, entry);
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::OnDeviceLocalAccountsChanged() {
 | 
| +  // No action needed here, changes to the list of device-local accounts get
 | 
| +  // handled via the kAccountsPrefDeviceLocalAccounts device setting observer.
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts() {
 | 
| +  // Schedule a callback if device policy has not yet been verified.
 | 
| +  if (chromeos::CrosSettingsProvider::TRUSTED !=
 | 
| +      cros_settings_->PrepareTrustedValues(base::Bind(
 | 
| +          &CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts,
 | 
| +          weak_factory_.GetWeakPtr()))) {
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  std::vector<DeviceLocalAccount> device_local_account_list =
 | 
| +      policy::GetDeviceLocalAccounts(cros_settings_);
 | 
| +  std::set<std::string> device_local_accounts;
 | 
| +  for (std::vector<DeviceLocalAccount>::const_iterator it =
 | 
| +           device_local_account_list.begin();
 | 
| +       it != device_local_account_list.end(); ++it) {
 | 
| +    device_local_accounts.insert(it->user_id);
 | 
| +  }
 | 
| +
 | 
| +  for (DeviceLocalAccountEntryMap::iterator it =
 | 
| +           device_local_account_entries_.begin();
 | 
| +       it != device_local_account_entries_.end(); ) {
 | 
| +    if (!ContainsKey(device_local_accounts, it->first)) {
 | 
| +      const std::string user_id = it->first;
 | 
| +      it->second.DeleteOwnedMembers();
 | 
| +      device_local_account_entries_.erase(it++);
 | 
| +      // When a device-local account whose external data reference was set is
 | 
| +      // removed, emit a notification that the external data reference has been
 | 
| +      // cleared.
 | 
| +      HandleExternalDataPolicyUpdate(user_id, NULL);
 | 
| +    } else {
 | 
| +      ++it;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  for (std::set<std::string>::const_iterator it = device_local_accounts.begin();
 | 
| +       it != device_local_accounts.end(); ++it) {
 | 
| +    OnPolicyUpdated(*it);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::HandleExternalDataPolicyUpdate(
 | 
| +    const std::string& user_id,
 | 
| +    const PolicyMap::Entry* entry) {
 | 
| +  if (!entry) {
 | 
| +    delegate_->OnExternalDataCleared(policy_, user_id);
 | 
| +    fetch_weak_ptrs_.erase(user_id);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  delegate_->OnExternalDataSet(policy_, user_id);
 | 
| +
 | 
| +  linked_ptr<WeakPtrFactory>& weak_ptr_factory = fetch_weak_ptrs_[user_id];
 | 
| +  weak_ptr_factory.reset(new WeakPtrFactory(this));
 | 
| +  if (entry->external_data_fetcher) {
 | 
| +    entry->external_data_fetcher->Fetch(base::Bind(
 | 
| +        &CloudExternalDataPolicyObserver::OnExternalDataFetched,
 | 
| +        weak_ptr_factory->GetWeakPtr(),
 | 
| +        user_id));
 | 
| +  } else {
 | 
| +    NOTREACHED();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void CloudExternalDataPolicyObserver::OnExternalDataFetched(
 | 
| +    const std::string& user_id,
 | 
| +    scoped_ptr<std::string> data) {
 | 
| +  FetchWeakPtrMap::iterator it = fetch_weak_ptrs_.find(user_id);
 | 
| +  DCHECK(it != fetch_weak_ptrs_.end());
 | 
| +  fetch_weak_ptrs_.erase(it);
 | 
| +  DCHECK(data);
 | 
| +  delegate_->OnExternalDataFetched(policy_, user_id, data.Pass());
 | 
| +}
 | 
| +
 | 
| +}  // namespace policy
 | 
| 
 |