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 |