Home | History | Annotate | Download | only in policy
      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/bind_helpers.h"
     11 #include "base/command_line.h"
     12 #include "base/files/file_enumerator.h"
     13 #include "base/files/file_util.h"
     14 #include "base/logging.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/message_loop/message_loop_proxy.h"
     17 #include "base/path_service.h"
     18 #include "base/sequenced_task_runner.h"
     19 #include "base/stl_util.h"
     20 #include "base/strings/string_number_conversions.h"
     21 #include "chrome/browser/browser_process.h"
     22 #include "chrome/browser/chromeos/policy/device_local_account.h"
     23 #include "chrome/browser/chromeos/policy/device_local_account_external_data_service.h"
     24 #include "chrome/browser/chromeos/policy/device_local_account_policy_store.h"
     25 #include "chrome/browser/chromeos/settings/device_settings_service.h"
     26 #include "chrome/common/chrome_content_client.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 "components/policy/core/browser/browser_policy_connector.h"
     32 #include "components/policy/core/common/cloud/cloud_policy_client.h"
     33 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
     34 #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
     35 #include "components/policy/core/common/cloud/device_management_service.h"
     36 #include "components/policy/core/common/cloud/resource_cache.h"
     37 #include "components/policy/core/common/cloud/system_policy_request_context.h"
     38 #include "components/policy/core/common/policy_namespace.h"
     39 #include "components/policy/core/common/policy_switches.h"
     40 #include "content/public/browser/browser_thread.h"
     41 #include "net/url_request/url_request_context_getter.h"
     42 #include "policy/policy_constants.h"
     43 #include "policy/proto/device_management_backend.pb.h"
     44 #include "url/gurl.h"
     45 
     46 namespace em = enterprise_management;
     47 
     48 namespace policy {
     49 
     50 namespace {
     51 
     52 // Creates and initializes a cloud policy client. Returns NULL if the device
     53 // doesn't have credentials in device settings (i.e. is not
     54 // enterprise-enrolled).
     55 scoped_ptr<CloudPolicyClient> CreateClient(
     56     chromeos::DeviceSettingsService* device_settings_service,
     57     DeviceManagementService* device_management_service,
     58     scoped_refptr<net::URLRequestContextGetter> system_request_context) {
     59   const em::PolicyData* policy_data = device_settings_service->policy_data();
     60   if (!policy_data ||
     61       !policy_data->has_request_token() ||
     62       !policy_data->has_device_id() ||
     63       !device_management_service) {
     64     return scoped_ptr<CloudPolicyClient>();
     65   }
     66 
     67   scoped_refptr<net::URLRequestContextGetter> request_context =
     68       new SystemPolicyRequestContext(
     69           system_request_context, GetUserAgent());
     70 
     71   scoped_ptr<CloudPolicyClient> client(
     72       new CloudPolicyClient(std::string(), std::string(),
     73                             kPolicyVerificationKeyHash,
     74                             USER_AFFILIATION_MANAGED,
     75                             NULL, device_management_service, request_context));
     76   client->SetupRegistration(policy_data->request_token(),
     77                             policy_data->device_id());
     78   return client.Pass();
     79 }
     80 
     81 // Get the subdirectory of the force-installed extension cache and the component
     82 // policy cache used for |account_id|.
     83 std::string GetCacheSubdirectoryForAccountID(const std::string& account_id) {
     84   return base::HexEncode(account_id.c_str(), account_id.size());
     85 }
     86 
     87 // Cleans up the cache directory by removing subdirectories that are not found
     88 // in |subdirectories_to_keep|. Only caches whose cache directory is found in
     89 // |subdirectories_to_keep| may be running while the clean-up is in progress.
     90 void DeleteOrphanedCaches(
     91     const base::FilePath& cache_root_dir,
     92     const std::set<std::string>& subdirectories_to_keep) {
     93   base::FileEnumerator enumerator(cache_root_dir,
     94                                   false,
     95                                   base::FileEnumerator::DIRECTORIES);
     96   for (base::FilePath path = enumerator.Next(); !path.empty();
     97        path = enumerator.Next()) {
     98     const std::string subdirectory(path.BaseName().MaybeAsASCII());
     99     if (!ContainsKey(subdirectories_to_keep, subdirectory))
    100       base::DeleteFile(path, true);
    101   }
    102 }
    103 
    104 // Removes the subdirectory belonging to |account_id_to_delete| from the cache
    105 // directory. No cache belonging to |account_id_to_delete| may be running while
    106 // the removal is in progress.
    107 void DeleteObsoleteExtensionCache(const std::string& account_id_to_delete) {
    108   base::FilePath cache_root_dir;
    109   CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS,
    110                          &cache_root_dir));
    111   const base::FilePath path = cache_root_dir.Append(
    112       GetCacheSubdirectoryForAccountID(account_id_to_delete));
    113   if (base::DirectoryExists(path))
    114     base::DeleteFile(path, true);
    115 }
    116 
    117 }  // namespace
    118 
    119 DeviceLocalAccountPolicyBroker::DeviceLocalAccountPolicyBroker(
    120     const DeviceLocalAccount& account,
    121     const base::FilePath& component_policy_cache_path,
    122     scoped_ptr<DeviceLocalAccountPolicyStore> store,
    123     scoped_refptr<DeviceLocalAccountExternalDataManager> external_data_manager,
    124     const base::Closure& policy_update_callback,
    125     const scoped_refptr<base::SequencedTaskRunner>& task_runner)
    126     : account_id_(account.account_id),
    127       user_id_(account.user_id),
    128       component_policy_cache_path_(component_policy_cache_path),
    129       store_(store.Pass()),
    130       extension_tracker_(account, store_.get(), &schema_registry_),
    131       external_data_manager_(external_data_manager),
    132       core_(PolicyNamespaceKey(dm_protocol::kChromePublicAccountPolicyType,
    133                                store_->account_id()),
    134             store_.get(),
    135             task_runner),
    136       policy_update_callback_(policy_update_callback) {
    137   base::FilePath cache_root_dir;
    138   CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS,
    139                          &cache_root_dir));
    140   extension_loader_ = new chromeos::DeviceLocalAccountExternalPolicyLoader(
    141       store_.get(),
    142       cache_root_dir.Append(
    143           GetCacheSubdirectoryForAccountID(account.account_id)));
    144   store_->AddObserver(this);
    145 
    146   // Unblock the |schema_registry_| so that the |component_policy_service_|
    147   // starts using it.
    148   schema_registry_.RegisterComponent(
    149       PolicyNamespace(POLICY_DOMAIN_CHROME, ""),
    150       g_browser_process->browser_policy_connector()->GetChromeSchema());
    151   schema_registry_.SetReady(POLICY_DOMAIN_CHROME);
    152   schema_registry_.SetReady(POLICY_DOMAIN_EXTENSIONS);
    153 }
    154 
    155 DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() {
    156   store_->RemoveObserver(this);
    157   external_data_manager_->SetPolicyStore(NULL);
    158   external_data_manager_->Disconnect();
    159 }
    160 
    161 void DeviceLocalAccountPolicyBroker::Initialize() {
    162   store_->Load();
    163 }
    164 
    165 void DeviceLocalAccountPolicyBroker::ConnectIfPossible(
    166     chromeos::DeviceSettingsService* device_settings_service,
    167     DeviceManagementService* device_management_service,
    168     scoped_refptr<net::URLRequestContextGetter> request_context) {
    169   if (core_.client())
    170     return;
    171 
    172   scoped_ptr<CloudPolicyClient> client(CreateClient(device_settings_service,
    173                                                     device_management_service,
    174                                                     request_context));
    175   if (!client)
    176     return;
    177 
    178   core_.Connect(client.Pass());
    179   external_data_manager_->Connect(request_context);
    180   core_.StartRefreshScheduler();
    181   UpdateRefreshDelay();
    182   CreateComponentCloudPolicyService(request_context);
    183 }
    184 
    185 void DeviceLocalAccountPolicyBroker::UpdateRefreshDelay() {
    186   if (core_.refresh_scheduler()) {
    187     const base::Value* policy_value =
    188         store_->policy_map().GetValue(key::kPolicyRefreshRate);
    189     int delay = 0;
    190     if (policy_value && policy_value->GetAsInteger(&delay))
    191       core_.refresh_scheduler()->SetRefreshDelay(delay);
    192   }
    193 }
    194 
    195 std::string DeviceLocalAccountPolicyBroker::GetDisplayName() const {
    196   std::string display_name;
    197   const base::Value* display_name_value =
    198       store_->policy_map().GetValue(policy::key::kUserDisplayName);
    199   if (display_name_value)
    200     display_name_value->GetAsString(&display_name);
    201   return display_name;
    202 }
    203 
    204 void DeviceLocalAccountPolicyBroker::OnStoreLoaded(CloudPolicyStore* store) {
    205   UpdateRefreshDelay();
    206   policy_update_callback_.Run();
    207 }
    208 
    209 void DeviceLocalAccountPolicyBroker::OnStoreError(CloudPolicyStore* store) {
    210   policy_update_callback_.Run();
    211 }
    212 
    213 void DeviceLocalAccountPolicyBroker::OnComponentCloudPolicyUpdated() {
    214   policy_update_callback_.Run();
    215 }
    216 
    217 void DeviceLocalAccountPolicyBroker::CreateComponentCloudPolicyService(
    218     const scoped_refptr<net::URLRequestContextGetter>& request_context) {
    219   if (CommandLine::ForCurrentProcess()->HasSwitch(
    220           switches::kDisableComponentCloudPolicy)) {
    221     // Disabled via the command line.
    222     return;
    223   }
    224 
    225   scoped_ptr<ResourceCache> resource_cache(
    226       new ResourceCache(component_policy_cache_path_,
    227                         content::BrowserThread::GetMessageLoopProxyForThread(
    228                             content::BrowserThread::FILE)));
    229 
    230   component_policy_service_.reset(new ComponentCloudPolicyService(
    231       this,
    232       &schema_registry_,
    233       core(),
    234       resource_cache.Pass(),
    235       request_context,
    236       content::BrowserThread::GetMessageLoopProxyForThread(
    237           content::BrowserThread::FILE),
    238       content::BrowserThread::GetMessageLoopProxyForThread(
    239           content::BrowserThread::IO)));
    240 }
    241 
    242 DeviceLocalAccountPolicyService::DeviceLocalAccountPolicyService(
    243     chromeos::SessionManagerClient* session_manager_client,
    244     chromeos::DeviceSettingsService* device_settings_service,
    245     chromeos::CrosSettings* cros_settings,
    246     scoped_refptr<base::SequencedTaskRunner> store_background_task_runner,
    247     scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner,
    248     scoped_refptr<base::SequencedTaskRunner>
    249         external_data_service_backend_task_runner,
    250     scoped_refptr<base::SequencedTaskRunner> io_task_runner,
    251     scoped_refptr<net::URLRequestContextGetter> request_context)
    252     : session_manager_client_(session_manager_client),
    253       device_settings_service_(device_settings_service),
    254       cros_settings_(cros_settings),
    255       device_management_service_(NULL),
    256       waiting_for_cros_settings_(false),
    257       orphan_extension_cache_deletion_state_(NOT_STARTED),
    258       store_background_task_runner_(store_background_task_runner),
    259       extension_cache_task_runner_(extension_cache_task_runner),
    260       request_context_(request_context),
    261       local_accounts_subscription_(cros_settings_->AddSettingsObserver(
    262           chromeos::kAccountsPrefDeviceLocalAccounts,
    263           base::Bind(&DeviceLocalAccountPolicyService::
    264                          UpdateAccountListIfNonePending,
    265                      base::Unretained(this)))),
    266       weak_factory_(this) {
    267   CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_COMPONENT_POLICY,
    268                          &component_policy_cache_root_));
    269   external_data_service_.reset(new DeviceLocalAccountExternalDataService(
    270       this,
    271       external_data_service_backend_task_runner,
    272       io_task_runner));
    273   UpdateAccountList();
    274 }
    275 
    276 DeviceLocalAccountPolicyService::~DeviceLocalAccountPolicyService() {
    277   DCHECK(!request_context_.get());
    278   DCHECK(policy_brokers_.empty());
    279 }
    280 
    281 void DeviceLocalAccountPolicyService::Shutdown() {
    282   device_management_service_ = NULL;
    283   request_context_ = NULL;
    284   DeleteBrokers(&policy_brokers_);
    285 }
    286 
    287 void DeviceLocalAccountPolicyService::Connect(
    288     DeviceManagementService* device_management_service) {
    289   DCHECK(!device_management_service_);
    290   device_management_service_ = device_management_service;
    291 
    292   // Connect the brokers.
    293   for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
    294        it != policy_brokers_.end(); ++it) {
    295     it->second->ConnectIfPossible(device_settings_service_,
    296                                   device_management_service_,
    297                                   request_context_);
    298   }
    299 }
    300 
    301 DeviceLocalAccountPolicyBroker*
    302     DeviceLocalAccountPolicyService::GetBrokerForUser(
    303         const std::string& user_id) {
    304   PolicyBrokerMap::iterator entry = policy_brokers_.find(user_id);
    305   if (entry == policy_brokers_.end())
    306     return NULL;
    307 
    308   return entry->second;
    309 }
    310 
    311 bool DeviceLocalAccountPolicyService::IsPolicyAvailableForUser(
    312     const std::string& user_id) {
    313   DeviceLocalAccountPolicyBroker* broker = GetBrokerForUser(user_id);
    314   return broker && broker->core()->store()->is_managed();
    315 }
    316 
    317 void DeviceLocalAccountPolicyService::AddObserver(Observer* observer) {
    318   observers_.AddObserver(observer);
    319 }
    320 
    321 void DeviceLocalAccountPolicyService::RemoveObserver(Observer* observer) {
    322   observers_.RemoveObserver(observer);
    323 }
    324 
    325 bool DeviceLocalAccountPolicyService::IsExtensionCacheDirectoryBusy(
    326     const std::string& account_id) {
    327   return busy_extension_cache_directories_.find(account_id) !=
    328             busy_extension_cache_directories_.end();
    329 }
    330 
    331 void DeviceLocalAccountPolicyService::StartExtensionCachesIfPossible() {
    332   for (PolicyBrokerMap::iterator it = policy_brokers_.begin();
    333        it != policy_brokers_.end(); ++it) {
    334     if (!it->second->extension_loader()->IsCacheRunning() &&
    335         !IsExtensionCacheDirectoryBusy(it->second->account_id())) {
    336       it->second->extension_loader()->StartCache(extension_cache_task_runner_);
    337     }
    338   }
    339 }
    340 
    341 bool DeviceLocalAccountPolicyService::StartExtensionCacheForAccountIfPresent(
    342     const std::string& account_id) {
    343   for (PolicyBrokerMap::iterator it = policy_brokers_.begin();
    344        it != policy_brokers_.end(); ++it) {
    345     if (it->second->account_id() == account_id) {
    346       DCHECK(!it->second->extension_loader()->IsCacheRunning());
    347       it->second->extension_loader()->StartCache(extension_cache_task_runner_);
    348       return true;
    349     }
    350   }
    351   return false;
    352 }
    353 
    354 void DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted() {
    355   DCHECK_EQ(IN_PROGRESS, orphan_extension_cache_deletion_state_);
    356 
    357   orphan_extension_cache_deletion_state_ = DONE;
    358   StartExtensionCachesIfPossible();
    359 }
    360 
    361 void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown(
    362     const std::string& account_id) {
    363   DCHECK_NE(NOT_STARTED, orphan_extension_cache_deletion_state_);
    364   DCHECK(IsExtensionCacheDirectoryBusy(account_id));
    365 
    366   // The account with |account_id| was deleted and the broker for it has shut
    367   // down completely.
    368 
    369   if (StartExtensionCacheForAccountIfPresent(account_id)) {
    370     // If another account with the same ID was created in the meantime, its
    371     // extension cache is started, reusing the cache directory. The directory no
    372     // longer needs to be marked as busy in this case.
    373     busy_extension_cache_directories_.erase(account_id);
    374     return;
    375   }
    376 
    377   // If no account with |account_id| exists anymore, the cache directory should
    378   // be removed. The directory must stay marked as busy while the removal is in
    379   // progress.
    380   extension_cache_task_runner_->PostTaskAndReply(
    381       FROM_HERE,
    382       base::Bind(&DeleteObsoleteExtensionCache, account_id),
    383       base::Bind(&DeviceLocalAccountPolicyService::
    384                      OnObsoleteExtensionCacheDeleted,
    385                  weak_factory_.GetWeakPtr(),
    386                  account_id));
    387 }
    388 
    389 void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheDeleted(
    390     const std::string& account_id) {
    391   DCHECK_EQ(DONE, orphan_extension_cache_deletion_state_);
    392   DCHECK(IsExtensionCacheDirectoryBusy(account_id));
    393 
    394   // The cache directory for |account_id| has been deleted. The directory no
    395   // longer needs to be marked as busy.
    396   busy_extension_cache_directories_.erase(account_id);
    397 
    398   // If another account with the same ID was created in the meantime, start its
    399   // extension cache, creating a new cache directory.
    400   StartExtensionCacheForAccountIfPresent(account_id);
    401 }
    402 
    403 void DeviceLocalAccountPolicyService::UpdateAccountListIfNonePending() {
    404   // Avoid unnecessary calls to UpdateAccountList(): If an earlier call is still
    405   // pending (because the |cros_settings_| are not trusted yet), the updated
    406   // account list will be processed by that call when it eventually runs.
    407   if (!waiting_for_cros_settings_)
    408     UpdateAccountList();
    409 }
    410 
    411 void DeviceLocalAccountPolicyService::UpdateAccountList() {
    412   chromeos::CrosSettingsProvider::TrustedStatus status =
    413       cros_settings_->PrepareTrustedValues(
    414           base::Bind(&DeviceLocalAccountPolicyService::UpdateAccountList,
    415                      weak_factory_.GetWeakPtr()));
    416   switch (status) {
    417     case chromeos::CrosSettingsProvider::TRUSTED:
    418       waiting_for_cros_settings_ = false;
    419       break;
    420     case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
    421       waiting_for_cros_settings_ = true;
    422       return;
    423     case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
    424       waiting_for_cros_settings_ = false;
    425       return;
    426   }
    427 
    428   // Update |policy_brokers_|, keeping existing entries.
    429   PolicyBrokerMap old_policy_brokers;
    430   policy_brokers_.swap(old_policy_brokers);
    431   std::set<std::string> subdirectories_to_keep;
    432   const std::vector<DeviceLocalAccount> device_local_accounts =
    433       GetDeviceLocalAccounts(cros_settings_);
    434   for (std::vector<DeviceLocalAccount>::const_iterator it =
    435            device_local_accounts.begin();
    436        it != device_local_accounts.end(); ++it) {
    437     PolicyBrokerMap::iterator broker_it = old_policy_brokers.find(it->user_id);
    438 
    439     scoped_ptr<DeviceLocalAccountPolicyBroker> broker;
    440     bool broker_initialized = false;
    441     if (broker_it != old_policy_brokers.end()) {
    442       // Reuse the existing broker if present.
    443       broker.reset(broker_it->second);
    444       old_policy_brokers.erase(broker_it);
    445       broker_initialized = true;
    446     } else {
    447       scoped_ptr<DeviceLocalAccountPolicyStore> store(
    448           new DeviceLocalAccountPolicyStore(it->account_id,
    449                                             session_manager_client_,
    450                                             device_settings_service_,
    451                                             store_background_task_runner_));
    452       scoped_refptr<DeviceLocalAccountExternalDataManager>
    453           external_data_manager =
    454               external_data_service_->GetExternalDataManager(it->account_id,
    455                                                              store.get());
    456       broker.reset(new DeviceLocalAccountPolicyBroker(
    457           *it,
    458           component_policy_cache_root_.Append(
    459               GetCacheSubdirectoryForAccountID(it->account_id)),
    460           store.Pass(),
    461           external_data_manager,
    462           base::Bind(&DeviceLocalAccountPolicyService::NotifyPolicyUpdated,
    463                      base::Unretained(this),
    464                      it->user_id),
    465           base::MessageLoopProxy::current()));
    466     }
    467 
    468     // Fire up the cloud connection for fetching policy for the account from
    469     // the cloud if this is an enterprise-managed device.
    470     broker->ConnectIfPossible(device_settings_service_,
    471                               device_management_service_,
    472                               request_context_);
    473 
    474     policy_brokers_[it->user_id] = broker.release();
    475     if (!broker_initialized) {
    476       // The broker must be initialized after it has been added to
    477       // |policy_brokers_|.
    478       policy_brokers_[it->user_id]->Initialize();
    479     }
    480 
    481     subdirectories_to_keep.insert(
    482         GetCacheSubdirectoryForAccountID(it->account_id));
    483   }
    484 
    485   if (orphan_extension_cache_deletion_state_ == NOT_STARTED) {
    486     DCHECK(old_policy_brokers.empty());
    487     DCHECK(busy_extension_cache_directories_.empty());
    488 
    489     // If this method is running for the first time, no extension caches have
    490     // been started yet. Take this opportunity to do a clean-up by removing
    491     // orphaned cache directories not found in |subdirectories_to_keep| from the
    492     // cache directory.
    493     orphan_extension_cache_deletion_state_ = IN_PROGRESS;
    494 
    495     base::FilePath cache_root_dir;
    496     CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS,
    497                            &cache_root_dir));
    498     extension_cache_task_runner_->PostTaskAndReply(
    499         FROM_HERE,
    500         base::Bind(
    501             &DeleteOrphanedCaches, cache_root_dir, subdirectories_to_keep),
    502         base::Bind(
    503             &DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted,
    504             weak_factory_.GetWeakPtr()));
    505 
    506     // Start the extension caches for all brokers. These belong to accounts in
    507     // |account_ids| and are not affected by the clean-up.
    508     StartExtensionCachesIfPossible();
    509   } else {
    510     // If this method has run before, obsolete brokers may exist. Shut down
    511     // their extension caches and delete the brokers.
    512     DeleteBrokers(&old_policy_brokers);
    513 
    514     if (orphan_extension_cache_deletion_state_ == DONE) {
    515       // If the initial clean-up of orphaned cache directories has been
    516       // complete, start any extension caches that are not running yet but can
    517       // be started now because their cache directories are not busy.
    518       StartExtensionCachesIfPossible();
    519     }
    520   }
    521 
    522   // Purge the component policy caches of any accounts that have been removed.
    523   // Do this only after any obsolete brokers have been destroyed.
    524   // TODO(joaodasilva): for now this must be posted to the FILE thread,
    525   // to avoid racing with the ComponentCloudPolicyStore. Use a task runner
    526   // once that class supports another background thread too.
    527   content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
    528                                    base::Bind(&DeleteOrphanedCaches,
    529                                               component_policy_cache_root_,
    530                                               subdirectories_to_keep));
    531 
    532   FOR_EACH_OBSERVER(Observer, observers_, OnDeviceLocalAccountsChanged());
    533 }
    534 
    535 void DeviceLocalAccountPolicyService::DeleteBrokers(PolicyBrokerMap* map) {
    536   for (PolicyBrokerMap::iterator it = map->begin(); it != map->end(); ++it) {
    537     scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader>
    538         extension_loader = it->second->extension_loader();
    539     if (extension_loader->IsCacheRunning()) {
    540       DCHECK(!IsExtensionCacheDirectoryBusy(it->second->account_id()));
    541       busy_extension_cache_directories_.insert(it->second->account_id());
    542       extension_loader->StopCache(base::Bind(
    543           &DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown,
    544           weak_factory_.GetWeakPtr(),
    545           it->second->account_id()));
    546     }
    547 
    548     delete it->second;
    549   }
    550   map->clear();
    551 }
    552 
    553 DeviceLocalAccountPolicyBroker*
    554     DeviceLocalAccountPolicyService::GetBrokerForStore(
    555         CloudPolicyStore* store) {
    556   for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
    557        it != policy_brokers_.end(); ++it) {
    558     if (it->second->core()->store() == store)
    559       return it->second;
    560   }
    561   return NULL;
    562 }
    563 
    564 void DeviceLocalAccountPolicyService::NotifyPolicyUpdated(
    565     const std::string& user_id) {
    566   FOR_EACH_OBSERVER(Observer, observers_, OnPolicyUpdated(user_id));
    567 }
    568 
    569 }  // namespace policy
    570