1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/chromeos/policy/cloud_external_data_policy_observer.h" 6 7#include <set> 8#include <vector> 9 10#include "base/bind.h" 11#include "base/bind_helpers.h" 12#include "base/logging.h" 13#include "base/stl_util.h" 14#include "base/values.h" 15#include "chrome/browser/chrome_notification_types.h" 16#include "chrome/browser/chromeos/policy/device_local_account.h" 17#include "chrome/browser/chromeos/profiles/profile_helper.h" 18#include "chrome/browser/policy/profile_policy_connector.h" 19#include "chrome/browser/policy/profile_policy_connector_factory.h" 20#include "chrome/browser/profiles/profile.h" 21#include "chromeos/settings/cros_settings_names.h" 22#include "chromeos/settings/cros_settings_provider.h" 23#include "components/policy/core/common/cloud/cloud_policy_core.h" 24#include "components/policy/core/common/cloud/cloud_policy_store.h" 25#include "components/policy/core/common/external_data_fetcher.h" 26#include "components/policy/core/common/policy_namespace.h" 27#include "components/policy/core/common/policy_service.h" 28#include "components/user_manager/user.h" 29#include "content/public/browser/notification_details.h" 30#include "content/public/browser/notification_service.h" 31#include "content/public/browser/notification_source.h" 32 33namespace policy { 34 35// Helper class that observes a policy for a logged-in user, notifying the 36// |parent_| whenever the external data reference for this user changes. 37class CloudExternalDataPolicyObserver::PolicyServiceObserver 38 : public PolicyService::Observer { 39 public: 40 PolicyServiceObserver(CloudExternalDataPolicyObserver* parent, 41 const std::string& user_id, 42 PolicyService* policy_service); 43 virtual ~PolicyServiceObserver(); 44 45 // PolicyService::Observer: 46 virtual void OnPolicyUpdated(const PolicyNamespace& ns, 47 const PolicyMap& previous, 48 const PolicyMap& current) OVERRIDE; 49 50 private: 51 CloudExternalDataPolicyObserver* parent_; 52 const std::string user_id_; 53 PolicyService* policy_service_; 54 55 DISALLOW_COPY_AND_ASSIGN(PolicyServiceObserver); 56}; 57 58CloudExternalDataPolicyObserver::PolicyServiceObserver::PolicyServiceObserver( 59 CloudExternalDataPolicyObserver* parent, 60 const std::string& user_id, 61 PolicyService* policy_service) 62 : parent_(parent), 63 user_id_(user_id), 64 policy_service_(policy_service) { 65 policy_service_->AddObserver(POLICY_DOMAIN_CHROME, this); 66 67 if (!IsDeviceLocalAccountUser(user_id, NULL)) { 68 // Notify |parent_| if the external data reference for |user_id_| is set 69 // during login. This is omitted for device-local accounts because their 70 // policy is available before login and the external data reference will 71 // have been seen by the |parent_| already. 72 const PolicyMap::Entry* entry = policy_service_->GetPolicies( 73 PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())) 74 .Get(parent_->policy_); 75 if (entry) 76 parent_->HandleExternalDataPolicyUpdate(user_id_, entry); 77 } 78} 79 80CloudExternalDataPolicyObserver::PolicyServiceObserver:: 81 ~PolicyServiceObserver() { 82 policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this); 83} 84 85void CloudExternalDataPolicyObserver::PolicyServiceObserver::OnPolicyUpdated( 86 const PolicyNamespace& ns, 87 const PolicyMap& previous, 88 const PolicyMap& current) { 89 DCHECK(ns == PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())); 90 91 const PolicyMap::Entry* previous_entry = previous.Get(parent_->policy_); 92 const PolicyMap::Entry* current_entry = current.Get(parent_->policy_); 93 if ((!previous_entry && current_entry) || 94 (previous_entry && !current_entry) || 95 (previous_entry && current_entry && 96 !previous_entry->Equals(*current_entry))) { 97 // Notify |parent_| if the external data reference for |user_id_| has 98 // changed. 99 parent_->HandleExternalDataPolicyUpdate(user_id_, current_entry); 100 } 101} 102 103void CloudExternalDataPolicyObserver::Delegate::OnExternalDataSet( 104 const std::string& policy, 105 const std::string& user_id) { 106} 107 108void CloudExternalDataPolicyObserver::Delegate::OnExternalDataCleared( 109 const std::string& policy, 110 const std::string& user_id) { 111} 112 113void CloudExternalDataPolicyObserver::Delegate::OnExternalDataFetched( 114 const std::string& policy, 115 const std::string& user_id, 116 scoped_ptr<std::string> data) { 117} 118 119CloudExternalDataPolicyObserver::Delegate::~Delegate() { 120} 121 122CloudExternalDataPolicyObserver::CloudExternalDataPolicyObserver( 123 chromeos::CrosSettings* cros_settings, 124 DeviceLocalAccountPolicyService* device_local_account_policy_service, 125 const std::string& policy, 126 Delegate* delegate) 127 : cros_settings_(cros_settings), 128 device_local_account_policy_service_(device_local_account_policy_service), 129 policy_(policy), 130 delegate_(delegate), 131 weak_factory_(this) { 132 notification_registrar_.Add( 133 this, 134 chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, 135 content::NotificationService::AllSources()); 136 137 if (device_local_account_policy_service_) 138 device_local_account_policy_service_->AddObserver(this); 139 140 device_local_accounts_subscription_ = cros_settings_->AddSettingsObserver( 141 chromeos::kAccountsPrefDeviceLocalAccounts, 142 base::Bind(&CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts, 143 base::Unretained(this))); 144} 145 146CloudExternalDataPolicyObserver::~CloudExternalDataPolicyObserver() { 147 if (device_local_account_policy_service_) 148 device_local_account_policy_service_->RemoveObserver(this); 149 for (DeviceLocalAccountEntryMap::iterator it = 150 device_local_account_entries_.begin(); 151 it != device_local_account_entries_.end(); ++it) { 152 it->second.DeleteOwnedMembers(); 153 } 154 device_local_account_entries_.clear(); 155} 156 157void CloudExternalDataPolicyObserver::Init() { 158 RetrieveDeviceLocalAccounts(); 159} 160 161void CloudExternalDataPolicyObserver::Observe( 162 int type, 163 const content::NotificationSource& source, 164 const content::NotificationDetails& details) { 165 if (type != chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) { 166 NOTREACHED(); 167 return; 168 } 169 Profile* profile = content::Details<Profile>(details).ptr(); 170 171 const user_manager::User* user = 172 chromeos::ProfileHelper::Get()->GetUserByProfile(profile); 173 if (!user) { 174 NOTREACHED(); 175 return; 176 } 177 178 const std::string& user_id = user->email(); 179 if (ContainsKey(logged_in_user_observers_, user_id)) { 180 NOTREACHED(); 181 return; 182 } 183 184 ProfilePolicyConnector* policy_connector = 185 ProfilePolicyConnectorFactory::GetForProfile(profile); 186 logged_in_user_observers_[user_id] = make_linked_ptr( 187 new PolicyServiceObserver(this, 188 user_id, 189 policy_connector->policy_service())); 190} 191 192void CloudExternalDataPolicyObserver::OnPolicyUpdated( 193 const std::string& user_id) { 194 if (ContainsKey(logged_in_user_observers_, user_id)) { 195 // When a device-local account is logged in, a policy change triggers both 196 // OnPolicyUpdated() and PolicyServiceObserver::OnPolicyUpdated(). Ignore 197 // the former so that the policy change is handled only once. 198 return; 199 } 200 201 if (!device_local_account_policy_service_) { 202 NOTREACHED(); 203 return; 204 } 205 DeviceLocalAccountPolicyBroker* broker = 206 device_local_account_policy_service_->GetBrokerForUser(user_id); 207 if (!broker) { 208 // The order in which |this| and the |device_local_account_policy_service_| 209 // find out that a new device-local account has been added is undefined. If 210 // no |broker| exists yet, the |device_local_account_policy_service_| must 211 // not have seen the new |user_id| yet. OnPolicyUpdated() will be invoked 212 // again by the |device_local_account_policy_service_| in this case when it 213 // finds out about |user_id| and creates a |broker| for it. 214 return; 215 } 216 217 const PolicyMap::Entry* entry = 218 broker->core()->store()->policy_map().Get(policy_); 219 if (!entry) { 220 DeviceLocalAccountEntryMap::iterator it = 221 device_local_account_entries_.find(user_id); 222 if (it != device_local_account_entries_.end()) { 223 it->second.DeleteOwnedMembers(); 224 device_local_account_entries_.erase(it); 225 HandleExternalDataPolicyUpdate(user_id, NULL); 226 } 227 return; 228 } 229 230 PolicyMap::Entry& map_entry = device_local_account_entries_[user_id]; 231 if (map_entry.Equals(*entry)) 232 return; 233 234 map_entry.DeleteOwnedMembers(); 235 map_entry = *entry->DeepCopy(); 236 HandleExternalDataPolicyUpdate(user_id, entry); 237} 238 239void CloudExternalDataPolicyObserver::OnDeviceLocalAccountsChanged() { 240 // No action needed here, changes to the list of device-local accounts get 241 // handled via the kAccountsPrefDeviceLocalAccounts device setting observer. 242} 243 244void CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts() { 245 // Schedule a callback if device policy has not yet been verified. 246 if (chromeos::CrosSettingsProvider::TRUSTED != 247 cros_settings_->PrepareTrustedValues(base::Bind( 248 &CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts, 249 weak_factory_.GetWeakPtr()))) { 250 return; 251 } 252 253 std::vector<DeviceLocalAccount> device_local_account_list = 254 policy::GetDeviceLocalAccounts(cros_settings_); 255 std::set<std::string> device_local_accounts; 256 for (std::vector<DeviceLocalAccount>::const_iterator it = 257 device_local_account_list.begin(); 258 it != device_local_account_list.end(); ++it) { 259 device_local_accounts.insert(it->user_id); 260 } 261 262 for (DeviceLocalAccountEntryMap::iterator it = 263 device_local_account_entries_.begin(); 264 it != device_local_account_entries_.end(); ) { 265 if (!ContainsKey(device_local_accounts, it->first)) { 266 const std::string user_id = it->first; 267 it->second.DeleteOwnedMembers(); 268 device_local_account_entries_.erase(it++); 269 // When a device-local account whose external data reference was set is 270 // removed, emit a notification that the external data reference has been 271 // cleared. 272 HandleExternalDataPolicyUpdate(user_id, NULL); 273 } else { 274 ++it; 275 } 276 } 277 278 for (std::set<std::string>::const_iterator it = device_local_accounts.begin(); 279 it != device_local_accounts.end(); ++it) { 280 OnPolicyUpdated(*it); 281 } 282} 283 284void CloudExternalDataPolicyObserver::HandleExternalDataPolicyUpdate( 285 const std::string& user_id, 286 const PolicyMap::Entry* entry) { 287 if (!entry) { 288 delegate_->OnExternalDataCleared(policy_, user_id); 289 fetch_weak_ptrs_.erase(user_id); 290 return; 291 } 292 293 delegate_->OnExternalDataSet(policy_, user_id); 294 295 linked_ptr<WeakPtrFactory>& weak_ptr_factory = fetch_weak_ptrs_[user_id]; 296 weak_ptr_factory.reset(new WeakPtrFactory(this)); 297 if (entry->external_data_fetcher) { 298 entry->external_data_fetcher->Fetch(base::Bind( 299 &CloudExternalDataPolicyObserver::OnExternalDataFetched, 300 weak_ptr_factory->GetWeakPtr(), 301 user_id)); 302 } else { 303 NOTREACHED(); 304 } 305} 306 307void CloudExternalDataPolicyObserver::OnExternalDataFetched( 308 const std::string& user_id, 309 scoped_ptr<std::string> data) { 310 FetchWeakPtrMap::iterator it = fetch_weak_ptrs_.find(user_id); 311 DCHECK(it != fetch_weak_ptrs_.end()); 312 fetch_weak_ptrs_.erase(it); 313 delegate_->OnExternalDataFetched(policy_, user_id, data.Pass()); 314} 315 316} // namespace policy 317