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