device_local_account_policy_service.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
1// Copyright (c) 2012 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/device_local_account_policy_service.h" 6 7#include <vector> 8 9#include "base/bind.h" 10#include "base/file_util.h" 11#include "base/files/file_enumerator.h" 12#include "base/files/file_path.h" 13#include "base/logging.h" 14#include "base/message_loop/message_loop.h" 15#include "base/message_loop/message_loop_proxy.h" 16#include "base/path_service.h" 17#include "base/sequenced_task_runner.h" 18#include "base/strings/string_number_conversions.h" 19#include "chrome/browser/chromeos/policy/device_local_account.h" 20#include "chrome/browser/chromeos/policy/device_local_account_policy_store.h" 21#include "chrome/browser/chromeos/settings/device_settings_service.h" 22#include "chrome/browser/policy/cloud/cloud_policy_client.h" 23#include "chrome/browser/policy/cloud/cloud_policy_constants.h" 24#include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h" 25#include "chrome/browser/policy/cloud/device_management_service.h" 26#include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" 27#include "chromeos/chromeos_paths.h" 28#include "chromeos/dbus/session_manager_client.h" 29#include "chromeos/settings/cros_settings_names.h" 30#include "chromeos/settings/cros_settings_provider.h" 31#include "policy/policy_constants.h" 32 33namespace em = enterprise_management; 34 35namespace policy { 36 37namespace { 38 39// Creates and initializes a cloud policy client. Returns NULL if the device 40// doesn't have credentials in device settings (i.e. is not 41// enterprise-enrolled). 42scoped_ptr<CloudPolicyClient> CreateClient( 43 chromeos::DeviceSettingsService* device_settings_service, 44 DeviceManagementService* device_management_service) { 45 const em::PolicyData* policy_data = device_settings_service->policy_data(); 46 if (!policy_data || 47 !policy_data->has_request_token() || 48 !policy_data->has_device_id() || 49 !device_management_service) { 50 return scoped_ptr<CloudPolicyClient>(); 51 } 52 53 scoped_ptr<CloudPolicyClient> client( 54 new CloudPolicyClient(std::string(), std::string(), 55 USER_AFFILIATION_MANAGED, 56 NULL, device_management_service)); 57 client->SetupRegistration(policy_data->request_token(), 58 policy_data->device_id()); 59 return client.Pass(); 60} 61 62// Get the subdirectory of the cache directory in which force-installed 63// extensions are cached for |account_id|. 64std::string GetCacheSubdirectoryForAccountID(const std::string& account_id) { 65 return base::HexEncode(account_id.c_str(), account_id.size()); 66} 67 68// Cleans up the cache directory by removing subdirectories that are not found 69// in |subdirectories_to_keep|. Only caches whose cache directory is found in 70// |subdirectories_to_keep| may be running while the clean-up is in progress. 71void DeleteOrphanedExtensionCaches( 72 const std::set<std::string>& subdirectories_to_keep) { 73 base::FilePath cache_root_dir; 74 CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE, 75 &cache_root_dir)); 76 base::FileEnumerator enumerator(cache_root_dir, 77 false, 78 base::FileEnumerator::DIRECTORIES); 79 for (base::FilePath path = enumerator.Next(); !path.empty(); 80 path = enumerator.Next()) { 81 const std::string subdirectory(path.BaseName().MaybeAsASCII()); 82 if (subdirectories_to_keep.find(subdirectory) == 83 subdirectories_to_keep.end()) { 84 base::DeleteFile(path, true); 85 } 86 } 87} 88 89// Removes the subdirectory belonging to |account_id_to_delete| from the cache 90// directory. No cache belonging to |account_id_to_delete| may be running while 91// the removal is in progress. 92void DeleteObsoleteExtensionCache(const std::string& account_id_to_delete) { 93 base::FilePath cache_root_dir; 94 CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE, 95 &cache_root_dir)); 96 const base::FilePath path = cache_root_dir 97 .Append(GetCacheSubdirectoryForAccountID(account_id_to_delete)); 98 if (base::DirectoryExists(path)) 99 base::DeleteFile(path, true); 100} 101 102} // namespace 103 104DeviceLocalAccountPolicyBroker::DeviceLocalAccountPolicyBroker( 105 const DeviceLocalAccount& account, 106 scoped_ptr<DeviceLocalAccountPolicyStore> store, 107 const scoped_refptr<base::SequencedTaskRunner>& task_runner) 108 : account_id_(account.account_id), 109 user_id_(account.user_id), 110 store_(store.Pass()), 111 core_(PolicyNamespaceKey(dm_protocol::kChromePublicAccountPolicyType, 112 store_->account_id()), 113 store_.get(), 114 task_runner) { 115 base::FilePath cache_root_dir; 116 CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE, 117 &cache_root_dir)); 118 extension_loader_ = new chromeos::DeviceLocalAccountExternalPolicyLoader( 119 store_.get(), 120 cache_root_dir.Append( 121 GetCacheSubdirectoryForAccountID(account.account_id))); 122} 123 124DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() { 125} 126 127void DeviceLocalAccountPolicyBroker::Initialize() { 128 store_->Load(); 129} 130 131void DeviceLocalAccountPolicyBroker::ConnectIfPossible( 132 chromeos::DeviceSettingsService* device_settings_service, 133 DeviceManagementService* device_management_service) { 134 if (core_.client()) 135 return; 136 137 scoped_ptr<CloudPolicyClient> client(CreateClient(device_settings_service, 138 device_management_service)); 139 if (!client) 140 return; 141 142 core_.Connect(client.Pass()); 143 core_.StartRefreshScheduler(); 144 UpdateRefreshDelay(); 145} 146 147void DeviceLocalAccountPolicyBroker::Disconnect() { 148 core_.Disconnect(); 149} 150 151void DeviceLocalAccountPolicyBroker::UpdateRefreshDelay() { 152 if (core_.refresh_scheduler()) { 153 const Value* policy_value = 154 store_->policy_map().GetValue(key::kPolicyRefreshRate); 155 int delay = 0; 156 if (policy_value && policy_value->GetAsInteger(&delay)) 157 core_.refresh_scheduler()->SetRefreshDelay(delay); 158 } 159} 160 161std::string DeviceLocalAccountPolicyBroker::GetDisplayName() const { 162 std::string display_name; 163 const base::Value* display_name_value = 164 store_->policy_map().GetValue(policy::key::kUserDisplayName); 165 if (display_name_value) 166 display_name_value->GetAsString(&display_name); 167 return display_name; 168} 169 170DeviceLocalAccountPolicyService::DeviceLocalAccountPolicyService( 171 chromeos::SessionManagerClient* session_manager_client, 172 chromeos::DeviceSettingsService* device_settings_service, 173 chromeos::CrosSettings* cros_settings, 174 scoped_refptr<base::SequencedTaskRunner> store_background_task_runner, 175 scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner) 176 : session_manager_client_(session_manager_client), 177 device_settings_service_(device_settings_service), 178 cros_settings_(cros_settings), 179 device_management_service_(NULL), 180 waiting_for_cros_settings_(false), 181 orphan_cache_deletion_state_(NOT_STARTED), 182 store_background_task_runner_(store_background_task_runner), 183 extension_cache_task_runner_(extension_cache_task_runner), 184 local_accounts_subscription_(cros_settings_->AddSettingsObserver( 185 chromeos::kAccountsPrefDeviceLocalAccounts, 186 base::Bind(&DeviceLocalAccountPolicyService:: 187 UpdateAccountListIfNonePending, 188 base::Unretained(this)))), 189 weak_factory_(this) { 190 UpdateAccountList(); 191} 192 193DeviceLocalAccountPolicyService::~DeviceLocalAccountPolicyService() { 194 DeleteBrokers(&policy_brokers_); 195} 196 197void DeviceLocalAccountPolicyService::Connect( 198 DeviceManagementService* device_management_service) { 199 DCHECK(!device_management_service_); 200 device_management_service_ = device_management_service; 201 202 // Connect the brokers. 203 for (PolicyBrokerMap::iterator it(policy_brokers_.begin()); 204 it != policy_brokers_.end(); ++it) { 205 it->second->ConnectIfPossible(device_settings_service_, 206 device_management_service_); 207 } 208} 209 210void DeviceLocalAccountPolicyService::Disconnect() { 211 DCHECK(device_management_service_); 212 device_management_service_ = NULL; 213 214 // Disconnect the brokers. 215 for (PolicyBrokerMap::iterator it(policy_brokers_.begin()); 216 it != policy_brokers_.end(); ++it) { 217 it->second->Disconnect(); 218 } 219} 220 221DeviceLocalAccountPolicyBroker* 222 DeviceLocalAccountPolicyService::GetBrokerForUser( 223 const std::string& user_id) { 224 PolicyBrokerMap::iterator entry = policy_brokers_.find(user_id); 225 if (entry == policy_brokers_.end()) 226 return NULL; 227 228 return entry->second; 229} 230 231bool DeviceLocalAccountPolicyService::IsPolicyAvailableForUser( 232 const std::string& user_id) { 233 DeviceLocalAccountPolicyBroker* broker = GetBrokerForUser(user_id); 234 return broker && broker->core()->store()->is_managed(); 235} 236 237void DeviceLocalAccountPolicyService::AddObserver(Observer* observer) { 238 observers_.AddObserver(observer); 239} 240 241void DeviceLocalAccountPolicyService::RemoveObserver(Observer* observer) { 242 observers_.RemoveObserver(observer); 243} 244 245void DeviceLocalAccountPolicyService::OnStoreLoaded(CloudPolicyStore* store) { 246 DeviceLocalAccountPolicyBroker* broker = GetBrokerForStore(store); 247 DCHECK(broker); 248 if (!broker) 249 return; 250 broker->UpdateRefreshDelay(); 251 FOR_EACH_OBSERVER(Observer, observers_, OnPolicyUpdated(broker->user_id())); 252} 253 254void DeviceLocalAccountPolicyService::OnStoreError(CloudPolicyStore* store) { 255 DeviceLocalAccountPolicyBroker* broker = GetBrokerForStore(store); 256 DCHECK(broker); 257 if (!broker) 258 return; 259 FOR_EACH_OBSERVER(Observer, observers_, OnPolicyUpdated(broker->user_id())); 260} 261 262bool DeviceLocalAccountPolicyService::IsExtensionCacheDirectoryBusy( 263 const std::string& account_id) { 264 return busy_extension_cache_directories_.find(account_id) != 265 busy_extension_cache_directories_.end(); 266} 267 268void DeviceLocalAccountPolicyService::StartExtensionCachesIfPossible() { 269 for (PolicyBrokerMap::iterator it = policy_brokers_.begin(); 270 it != policy_brokers_.end(); ++it) { 271 if (!it->second->extension_loader()->IsCacheRunning() && 272 !IsExtensionCacheDirectoryBusy(it->second->account_id())) { 273 it->second->extension_loader()->StartCache(extension_cache_task_runner_); 274 } 275 } 276} 277 278bool DeviceLocalAccountPolicyService::StartExtensionCacheForAccountIfPresent( 279 const std::string& account_id) { 280 for (PolicyBrokerMap::iterator it = policy_brokers_.begin(); 281 it != policy_brokers_.end(); ++it) { 282 if (it->second->account_id() == account_id) { 283 DCHECK(!it->second->extension_loader()->IsCacheRunning()); 284 it->second->extension_loader()->StartCache(extension_cache_task_runner_); 285 return true; 286 } 287 } 288 return false; 289} 290 291void DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted() { 292 DCHECK_EQ(IN_PROGRESS, orphan_cache_deletion_state_); 293 294 orphan_cache_deletion_state_ = DONE; 295 StartExtensionCachesIfPossible(); 296} 297 298void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown( 299 const std::string& account_id) { 300 DCHECK_NE(NOT_STARTED, orphan_cache_deletion_state_); 301 DCHECK(IsExtensionCacheDirectoryBusy(account_id)); 302 303 // The account with |account_id| was deleted and the broker for it has shut 304 // down completely. 305 306 if (StartExtensionCacheForAccountIfPresent(account_id)) { 307 // If another account with the same ID was created in the meantime, its 308 // extension cache is started, reusing the cache directory. The directory no 309 // longer needs to be marked as busy in this case. 310 busy_extension_cache_directories_.erase(account_id); 311 return; 312 } 313 314 // If no account with |account_id| exists anymore, the cache directory should 315 // be removed. The directory must stay marked as busy while the removal is in 316 // progress. 317 extension_cache_task_runner_->PostTaskAndReply( 318 FROM_HERE, 319 base::Bind(&DeleteObsoleteExtensionCache, account_id), 320 base::Bind(&DeviceLocalAccountPolicyService:: 321 OnObsoleteExtensionCacheDeleted, 322 weak_factory_.GetWeakPtr(), 323 account_id)); 324} 325 326void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheDeleted( 327 const std::string& account_id) { 328 DCHECK_EQ(DONE, orphan_cache_deletion_state_); 329 DCHECK(IsExtensionCacheDirectoryBusy(account_id)); 330 331 // The cache directory for |account_id| has been deleted. The directory no 332 // longer needs to be marked as busy. 333 busy_extension_cache_directories_.erase(account_id); 334 335 // If another account with the same ID was created in the meantime, start its 336 // extension cache, creating a new cache directory. 337 StartExtensionCacheForAccountIfPresent(account_id); 338} 339 340void DeviceLocalAccountPolicyService::UpdateAccountListIfNonePending() { 341 // Avoid unnecessary calls to UpdateAccountList(): If an earlier call is still 342 // pending (because the |cros_settings_| are not trusted yet), the updated 343 // account list will be processed by that call when it eventually runs. 344 if (!waiting_for_cros_settings_) 345 UpdateAccountList(); 346} 347 348void DeviceLocalAccountPolicyService::UpdateAccountList() { 349 chromeos::CrosSettingsProvider::TrustedStatus status = 350 cros_settings_->PrepareTrustedValues( 351 base::Bind(&DeviceLocalAccountPolicyService::UpdateAccountList, 352 weak_factory_.GetWeakPtr())); 353 switch (status) { 354 case chromeos::CrosSettingsProvider::TRUSTED: 355 waiting_for_cros_settings_ = false; 356 break; 357 case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED: 358 waiting_for_cros_settings_ = true; 359 return; 360 case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED: 361 waiting_for_cros_settings_ = false; 362 return; 363 } 364 365 // Update |policy_brokers_|, keeping existing entries. 366 PolicyBrokerMap old_policy_brokers; 367 policy_brokers_.swap(old_policy_brokers); 368 std::set<std::string> subdirectories_to_keep; 369 const std::vector<DeviceLocalAccount> device_local_accounts = 370 GetDeviceLocalAccounts(cros_settings_); 371 for (std::vector<DeviceLocalAccount>::const_iterator it = 372 device_local_accounts.begin(); 373 it != device_local_accounts.end(); ++it) { 374 PolicyBrokerMap::iterator broker_it = old_policy_brokers.find(it->user_id); 375 376 scoped_ptr<DeviceLocalAccountPolicyBroker> broker; 377 bool broker_initialized = false; 378 if (broker_it != old_policy_brokers.end()) { 379 // Reuse the existing broker if present. 380 broker.reset(broker_it->second); 381 old_policy_brokers.erase(broker_it); 382 broker_initialized = true; 383 } else { 384 scoped_ptr<DeviceLocalAccountPolicyStore> store( 385 new DeviceLocalAccountPolicyStore(it->account_id, 386 session_manager_client_, 387 device_settings_service_, 388 store_background_task_runner_)); 389 store->AddObserver(this); 390 broker.reset(new DeviceLocalAccountPolicyBroker( 391 *it, 392 store.Pass(), 393 base::MessageLoopProxy::current())); 394 } 395 396 // Fire up the cloud connection for fetching policy for the account from 397 // the cloud if this is an enterprise-managed device. 398 broker->ConnectIfPossible(device_settings_service_, 399 device_management_service_); 400 401 policy_brokers_[it->user_id] = broker.release(); 402 if (!broker_initialized) { 403 // The broker must be initialized after it has been added to 404 // |policy_brokers_|. 405 policy_brokers_[it->user_id]->Initialize(); 406 } 407 408 if (orphan_cache_deletion_state_ == NOT_STARTED) { 409 subdirectories_to_keep.insert( 410 GetCacheSubdirectoryForAccountID(it->account_id)); 411 } 412 } 413 414 std::set<std::string> obsolete_account_ids; 415 for (PolicyBrokerMap::const_iterator it = old_policy_brokers.begin(); 416 it != old_policy_brokers.end(); ++it) { 417 obsolete_account_ids.insert(it->second->account_id()); 418 } 419 420 if (orphan_cache_deletion_state_ == NOT_STARTED) { 421 DCHECK(old_policy_brokers.empty()); 422 DCHECK(busy_extension_cache_directories_.empty()); 423 424 // If this method is running for the first time, no extension caches have 425 // been started yet. Take this opportunity to do a clean-up by removing 426 // orphaned cache directories not found in |subdirectories_to_keep| from the 427 // cache directory. 428 orphan_cache_deletion_state_ = IN_PROGRESS; 429 extension_cache_task_runner_->PostTaskAndReply( 430 FROM_HERE, 431 base::Bind(&DeleteOrphanedExtensionCaches, subdirectories_to_keep), 432 base::Bind(&DeviceLocalAccountPolicyService:: 433 OnOrphanedExtensionCachesDeleted, 434 weak_factory_.GetWeakPtr())); 435 436 // Start the extension caches for all brokers. These belong to accounts in 437 // |account_ids| and are not affected by the clean-up. 438 StartExtensionCachesIfPossible(); 439 } else { 440 // If this method has run before, obsolete brokers may exist. Shut down 441 // their extension caches and delete the brokers. 442 DeleteBrokers(&old_policy_brokers); 443 444 if (orphan_cache_deletion_state_ == DONE) { 445 // If the initial clean-up of orphaned cache directories has been 446 // complete, start any extension caches that are not running yet but can 447 // be started now because their cache directories are not busy. 448 StartExtensionCachesIfPossible(); 449 } 450 } 451 452 FOR_EACH_OBSERVER(Observer, observers_, OnDeviceLocalAccountsChanged()); 453} 454 455void DeviceLocalAccountPolicyService::DeleteBrokers(PolicyBrokerMap* map) { 456 for (PolicyBrokerMap::iterator it = map->begin(); it != map->end(); ++it) { 457 it->second->core()->store()->RemoveObserver(this); 458 scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader> 459 extension_loader = it->second->extension_loader(); 460 if (extension_loader->IsCacheRunning()) { 461 DCHECK(!IsExtensionCacheDirectoryBusy(it->second->account_id())); 462 busy_extension_cache_directories_.insert(it->second->account_id()); 463 extension_loader->StopCache(base::Bind( 464 &DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown, 465 weak_factory_.GetWeakPtr(), 466 it->second->account_id())); 467 } 468 delete it->second; 469 } 470 map->clear(); 471} 472 473DeviceLocalAccountPolicyBroker* 474 DeviceLocalAccountPolicyService::GetBrokerForStore( 475 CloudPolicyStore* store) { 476 for (PolicyBrokerMap::iterator it(policy_brokers_.begin()); 477 it != policy_brokers_.end(); ++it) { 478 if (it->second->core()->store() == store) 479 return it->second; 480 } 481 return NULL; 482} 483 484} // namespace policy 485