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