root/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. policy_service_
  2. PolicyServiceObserver
  3. OnPolicyUpdated
  4. OnExternalDataSet
  5. OnExternalDataCleared
  6. OnExternalDataFetched
  7. weak_factory_
  8. Init
  9. OnPolicyUpdated
  10. OnDeviceLocalAccountsChanged
  11. RetrieveDeviceLocalAccounts
  12. HandleExternalDataPolicyUpdate
  13. OnExternalDataFetched

// 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/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/cloud/cloud_policy_core.h"
#include "components/policy/core/common/cloud/cloud_policy_store.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_service.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) {
    // The order in which |this| and the |device_local_account_policy_service_|
    // find out that a new device-local account has been added is undefined. If
    // no |broker| exists yet, the |device_local_account_policy_service_| must
    // not have seen the new |user_id| yet. OnPolicyUpdated() will be invoked
    // again by the |device_local_account_policy_service_| in this case when it
    // finds out about |user_id| and creates a |broker| for it.
    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);
  delegate_->OnExternalDataFetched(policy_, user_id, data.Pass());
}

}  // namespace policy

/* [<][>][^][v][top][bottom][index][help] */