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/login/user.h" 17 #include "chrome/browser/chromeos/login/user_manager.h" 18 #include "chrome/browser/chromeos/policy/device_local_account.h" 19 #include "chrome/browser/policy/profile_policy_connector.h" 20 #include "chrome/browser/policy/profile_policy_connector_factory.h" 21 #include "chrome/browser/profiles/profile.h" 22 #include "chromeos/settings/cros_settings_names.h" 23 #include "chromeos/settings/cros_settings_provider.h" 24 #include "components/policy/core/common/cloud/cloud_policy_core.h" 25 #include "components/policy/core/common/cloud/cloud_policy_store.h" 26 #include "components/policy/core/common/external_data_fetcher.h" 27 #include "components/policy/core/common/policy_namespace.h" 28 #include "components/policy/core/common/policy_service.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 33 namespace 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. 37 class 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 58 CloudExternalDataPolicyObserver::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 80 CloudExternalDataPolicyObserver::PolicyServiceObserver:: 81 ~PolicyServiceObserver() { 82 policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this); 83 } 84 85 void 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 103 void CloudExternalDataPolicyObserver::Delegate::OnExternalDataSet( 104 const std::string& policy, 105 const std::string& user_id) { 106 } 107 108 void CloudExternalDataPolicyObserver::Delegate::OnExternalDataCleared( 109 const std::string& policy, 110 const std::string& user_id) { 111 } 112 113 void CloudExternalDataPolicyObserver::Delegate::OnExternalDataFetched( 114 const std::string& policy, 115 const std::string& user_id, 116 scoped_ptr<std::string> data) { 117 } 118 119 CloudExternalDataPolicyObserver::Delegate::~Delegate() { 120 } 121 122 CloudExternalDataPolicyObserver::CloudExternalDataPolicyObserver( 123 chromeos::CrosSettings* cros_settings, 124 chromeos::UserManager* user_manager, 125 DeviceLocalAccountPolicyService* device_local_account_policy_service, 126 const std::string& policy, 127 Delegate* delegate) 128 : cros_settings_(cros_settings), 129 user_manager_(user_manager), 130 device_local_account_policy_service_(device_local_account_policy_service), 131 policy_(policy), 132 delegate_(delegate), 133 weak_factory_(this) { 134 notification_registrar_.Add( 135 this, 136 chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, 137 content::NotificationService::AllSources()); 138 139 if (device_local_account_policy_service_) 140 device_local_account_policy_service_->AddObserver(this); 141 142 device_local_accounts_subscription_ = cros_settings_->AddSettingsObserver( 143 chromeos::kAccountsPrefDeviceLocalAccounts, 144 base::Bind(&CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts, 145 base::Unretained(this))); 146 } 147 148 CloudExternalDataPolicyObserver::~CloudExternalDataPolicyObserver() { 149 if (device_local_account_policy_service_) 150 device_local_account_policy_service_->RemoveObserver(this); 151 for (DeviceLocalAccountEntryMap::iterator it = 152 device_local_account_entries_.begin(); 153 it != device_local_account_entries_.end(); ++it) { 154 it->second.DeleteOwnedMembers(); 155 } 156 device_local_account_entries_.clear(); 157 } 158 159 void CloudExternalDataPolicyObserver::Init() { 160 RetrieveDeviceLocalAccounts(); 161 } 162 163 void CloudExternalDataPolicyObserver::Observe( 164 int type, 165 const content::NotificationSource& source, 166 const content::NotificationDetails& details) { 167 if (type != chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) { 168 NOTREACHED(); 169 return; 170 } 171 Profile* profile = content::Details<Profile>(details).ptr(); 172 173 const chromeos::User* user = user_manager_->GetUserByProfile(profile); 174 if (!user) { 175 NOTREACHED(); 176 return; 177 } 178 179 const std::string& user_id = user->email(); 180 if (ContainsKey(logged_in_user_observers_, user_id)) { 181 NOTREACHED(); 182 return; 183 } 184 185 ProfilePolicyConnector* policy_connector = 186 ProfilePolicyConnectorFactory::GetForProfile(profile); 187 logged_in_user_observers_[user_id] = make_linked_ptr( 188 new PolicyServiceObserver(this, 189 user_id, 190 policy_connector->policy_service())); 191 } 192 193 void CloudExternalDataPolicyObserver::OnPolicyUpdated( 194 const std::string& user_id) { 195 if (ContainsKey(logged_in_user_observers_, user_id)) { 196 // When a device-local account is logged in, a policy change triggers both 197 // OnPolicyUpdated() and PolicyServiceObserver::OnPolicyUpdated(). Ignore 198 // the former so that the policy change is handled only once. 199 return; 200 } 201 202 if (!device_local_account_policy_service_) { 203 NOTREACHED(); 204 return; 205 } 206 DeviceLocalAccountPolicyBroker* broker = 207 device_local_account_policy_service_->GetBrokerForUser(user_id); 208 if (!broker) { 209 // The order in which |this| and the |device_local_account_policy_service_| 210 // find out that a new device-local account has been added is undefined. If 211 // no |broker| exists yet, the |device_local_account_policy_service_| must 212 // not have seen the new |user_id| yet. OnPolicyUpdated() will be invoked 213 // again by the |device_local_account_policy_service_| in this case when it 214 // finds out about |user_id| and creates a |broker| for it. 215 return; 216 } 217 218 const PolicyMap::Entry* entry = 219 broker->core()->store()->policy_map().Get(policy_); 220 if (!entry) { 221 DeviceLocalAccountEntryMap::iterator it = 222 device_local_account_entries_.find(user_id); 223 if (it != device_local_account_entries_.end()) { 224 it->second.DeleteOwnedMembers(); 225 device_local_account_entries_.erase(it); 226 HandleExternalDataPolicyUpdate(user_id, NULL); 227 } 228 return; 229 } 230 231 PolicyMap::Entry& map_entry = device_local_account_entries_[user_id]; 232 if (map_entry.Equals(*entry)) 233 return; 234 235 map_entry.DeleteOwnedMembers(); 236 map_entry = *entry->DeepCopy(); 237 HandleExternalDataPolicyUpdate(user_id, entry); 238 } 239 240 void CloudExternalDataPolicyObserver::OnDeviceLocalAccountsChanged() { 241 // No action needed here, changes to the list of device-local accounts get 242 // handled via the kAccountsPrefDeviceLocalAccounts device setting observer. 243 } 244 245 void CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts() { 246 // Schedule a callback if device policy has not yet been verified. 247 if (chromeos::CrosSettingsProvider::TRUSTED != 248 cros_settings_->PrepareTrustedValues(base::Bind( 249 &CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts, 250 weak_factory_.GetWeakPtr()))) { 251 return; 252 } 253 254 std::vector<DeviceLocalAccount> device_local_account_list = 255 policy::GetDeviceLocalAccounts(cros_settings_); 256 std::set<std::string> device_local_accounts; 257 for (std::vector<DeviceLocalAccount>::const_iterator it = 258 device_local_account_list.begin(); 259 it != device_local_account_list.end(); ++it) { 260 device_local_accounts.insert(it->user_id); 261 } 262 263 for (DeviceLocalAccountEntryMap::iterator it = 264 device_local_account_entries_.begin(); 265 it != device_local_account_entries_.end(); ) { 266 if (!ContainsKey(device_local_accounts, it->first)) { 267 const std::string user_id = it->first; 268 it->second.DeleteOwnedMembers(); 269 device_local_account_entries_.erase(it++); 270 // When a device-local account whose external data reference was set is 271 // removed, emit a notification that the external data reference has been 272 // cleared. 273 HandleExternalDataPolicyUpdate(user_id, NULL); 274 } else { 275 ++it; 276 } 277 } 278 279 for (std::set<std::string>::const_iterator it = device_local_accounts.begin(); 280 it != device_local_accounts.end(); ++it) { 281 OnPolicyUpdated(*it); 282 } 283 } 284 285 void CloudExternalDataPolicyObserver::HandleExternalDataPolicyUpdate( 286 const std::string& user_id, 287 const PolicyMap::Entry* entry) { 288 if (!entry) { 289 delegate_->OnExternalDataCleared(policy_, user_id); 290 fetch_weak_ptrs_.erase(user_id); 291 return; 292 } 293 294 delegate_->OnExternalDataSet(policy_, user_id); 295 296 linked_ptr<WeakPtrFactory>& weak_ptr_factory = fetch_weak_ptrs_[user_id]; 297 weak_ptr_factory.reset(new WeakPtrFactory(this)); 298 if (entry->external_data_fetcher) { 299 entry->external_data_fetcher->Fetch(base::Bind( 300 &CloudExternalDataPolicyObserver::OnExternalDataFetched, 301 weak_ptr_factory->GetWeakPtr(), 302 user_id)); 303 } else { 304 NOTREACHED(); 305 } 306 } 307 308 void CloudExternalDataPolicyObserver::OnExternalDataFetched( 309 const std::string& user_id, 310 scoped_ptr<std::string> data) { 311 FetchWeakPtrMap::iterator it = fetch_weak_ptrs_.find(user_id); 312 DCHECK(it != fetch_weak_ptrs_.end()); 313 fetch_weak_ptrs_.erase(it); 314 delegate_->OnExternalDataFetched(policy_, user_id, data.Pass()); 315 } 316 317 } // namespace policy 318