device_local_account_policy_service.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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/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
46namespace em = enterprise_management;
47
48namespace policy {
49
50namespace {
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).
55scoped_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|.
83std::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.
90void 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.
107void 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
119DeviceLocalAccountPolicyBroker::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
155DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() {
156  store_->RemoveObserver(this);
157  external_data_manager_->SetPolicyStore(NULL);
158  external_data_manager_->Disconnect();
159}
160
161void DeviceLocalAccountPolicyBroker::Initialize() {
162  store_->Load();
163}
164
165void 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
185void 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
195std::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
204void DeviceLocalAccountPolicyBroker::OnStoreLoaded(CloudPolicyStore* store) {
205  UpdateRefreshDelay();
206  policy_update_callback_.Run();
207}
208
209void DeviceLocalAccountPolicyBroker::OnStoreError(CloudPolicyStore* store) {
210  policy_update_callback_.Run();
211}
212
213void DeviceLocalAccountPolicyBroker::OnComponentCloudPolicyUpdated() {
214  policy_update_callback_.Run();
215}
216
217void 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
242DeviceLocalAccountPolicyService::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
276DeviceLocalAccountPolicyService::~DeviceLocalAccountPolicyService() {
277  DCHECK(!request_context_);
278  DCHECK(policy_brokers_.empty());
279}
280
281void DeviceLocalAccountPolicyService::Shutdown() {
282  device_management_service_ = NULL;
283  request_context_ = NULL;
284  DeleteBrokers(&policy_brokers_);
285}
286
287void 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
301DeviceLocalAccountPolicyBroker*
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
311bool DeviceLocalAccountPolicyService::IsPolicyAvailableForUser(
312    const std::string& user_id) {
313  DeviceLocalAccountPolicyBroker* broker = GetBrokerForUser(user_id);
314  return broker && broker->core()->store()->is_managed();
315}
316
317void DeviceLocalAccountPolicyService::AddObserver(Observer* observer) {
318  observers_.AddObserver(observer);
319}
320
321void DeviceLocalAccountPolicyService::RemoveObserver(Observer* observer) {
322  observers_.RemoveObserver(observer);
323}
324
325bool 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
331void 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
341bool 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
354void DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted() {
355  DCHECK_EQ(IN_PROGRESS, orphan_extension_cache_deletion_state_);
356
357  orphan_extension_cache_deletion_state_ = DONE;
358  StartExtensionCachesIfPossible();
359}
360
361void 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
389void 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
403void 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
411void 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
535void 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
553DeviceLocalAccountPolicyBroker*
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
564void DeviceLocalAccountPolicyService::NotifyPolicyUpdated(
565    const std::string& user_id) {
566  FOR_EACH_OBSERVER(Observer, observers_, OnPolicyUpdated(user_id));
567}
568
569}  // namespace policy
570