| Index: chrome/browser/chromeos/policy/device_local_account_policy_service.cc
|
| diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
|
| index 825d39c38d3c23699cc1c7cee20c46aea90f8a08..3579bec7a2466f76f4e58505d2c0bca3e509d302 100644
|
| --- a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
|
| +++ b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
|
| @@ -7,18 +7,24 @@
|
| #include <vector>
|
|
|
| #include "base/bind.h"
|
| +#include "base/file_util.h"
|
| +#include "base/files/file_enumerator.h"
|
| +#include "base/files/file_path.h"
|
| #include "base/logging.h"
|
| #include "base/message_loop/message_loop.h"
|
| #include "base/message_loop/message_loop_proxy.h"
|
| +#include "base/path_service.h"
|
| +#include "base/sequenced_task_runner.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| #include "chrome/browser/chromeos/policy/device_local_account.h"
|
| #include "chrome/browser/chromeos/policy/device_local_account_policy_store.h"
|
| -#include "chrome/browser/chromeos/settings/cros_settings.h"
|
| #include "chrome/browser/chromeos/settings/device_settings_service.h"
|
| #include "chrome/browser/policy/cloud/cloud_policy_client.h"
|
| #include "chrome/browser/policy/cloud/cloud_policy_constants.h"
|
| #include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h"
|
| #include "chrome/browser/policy/cloud/device_management_service.h"
|
| #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
|
| +#include "chromeos/chromeos_paths.h"
|
| #include "chromeos/dbus/session_manager_client.h"
|
| #include "chromeos/settings/cros_settings_names.h"
|
| #include "chromeos/settings/cros_settings_provider.h"
|
| @@ -53,23 +59,86 @@ scoped_ptr<CloudPolicyClient> CreateClient(
|
| return client.Pass();
|
| }
|
|
|
| +// Get the subdirectory of the cache directory in which force-installed
|
| +// extensions are cached for |account_id|.
|
| +std::string GetCacheSubdirectoryForAccountID(const std::string& account_id) {
|
| + return base::HexEncode(account_id.c_str(), account_id.size());
|
| +}
|
| +
|
| +// Cleans up the cache directory by removing subdirectories that are not found
|
| +// in |subdirectories_to_keep|. Only caches whose cache directory is found in
|
| +// |subdirectories_to_keep| may be running while the clean-up is in progress.
|
| +void DeleteOrphanedExtensionCaches(
|
| + const std::set<std::string>& subdirectories_to_keep) {
|
| + base::FilePath cache_root_dir;
|
| + CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE,
|
| + &cache_root_dir));
|
| + base::FileEnumerator enumerator(cache_root_dir,
|
| + false,
|
| + base::FileEnumerator::DIRECTORIES);
|
| + for (base::FilePath path = enumerator.Next(); !path.empty();
|
| + path = enumerator.Next()) {
|
| + const std::string subdirectory(path.BaseName().MaybeAsASCII());
|
| + if (subdirectories_to_keep.find(subdirectory) ==
|
| + subdirectories_to_keep.end()) {
|
| + base::DeleteFile(path, true);
|
| + }
|
| + }
|
| +}
|
| +
|
| +// Removes the subdirectory belonging to |account_id_to_delete| from the cache
|
| +// directory. No cache belonging to |account_id_to_delete| may be running while
|
| +// the removal is in progress.
|
| +void DeleteObsoleteExtensionCache(const std::string& account_id_to_delete) {
|
| + base::FilePath cache_root_dir;
|
| + CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE,
|
| + &cache_root_dir));
|
| + const base::FilePath path = cache_root_dir
|
| + .Append(GetCacheSubdirectoryForAccountID(account_id_to_delete));
|
| + if (base::DirectoryExists(path))
|
| + base::DeleteFile(path, true);
|
| +}
|
| +
|
| } // namespace
|
|
|
| DeviceLocalAccountPolicyBroker::DeviceLocalAccountPolicyBroker(
|
| - const std::string& user_id,
|
| + const DeviceLocalAccount& account,
|
| scoped_ptr<DeviceLocalAccountPolicyStore> store,
|
| const scoped_refptr<base::SequencedTaskRunner>& task_runner)
|
| - : user_id_(user_id),
|
| + : account_id_(account.account_id),
|
| + user_id_(account.user_id),
|
| store_(store.Pass()),
|
| core_(PolicyNamespaceKey(dm_protocol::kChromePublicAccountPolicyType,
|
| store_->account_id()),
|
| store_.get(),
|
| - task_runner) {}
|
| + task_runner) {
|
| + base::FilePath cache_root_dir;
|
| + CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_CACHE,
|
| + &cache_root_dir));
|
| + extension_loader_ = new chromeos::DeviceLocalAccountExternalPolicyLoader(
|
| + store_.get(),
|
| + cache_root_dir.Append(
|
| + GetCacheSubdirectoryForAccountID(account.account_id)));
|
| +}
|
|
|
| -DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() {}
|
| +DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() {
|
| +}
|
| +
|
| +void DeviceLocalAccountPolicyBroker::Initialize() {
|
| + store_->Load();
|
| +}
|
| +
|
| +void DeviceLocalAccountPolicyBroker::ConnectIfPossible(
|
| + chromeos::DeviceSettingsService* device_settings_service,
|
| + DeviceManagementService* device_management_service) {
|
| + if (core_.client())
|
| + return;
|
| +
|
| + scoped_ptr<CloudPolicyClient> client(CreateClient(device_settings_service,
|
| + device_management_service));
|
| + if (!client)
|
| + return;
|
|
|
| -void DeviceLocalAccountPolicyBroker::Connect(
|
| - scoped_ptr<CloudPolicyClient> client) {
|
| core_.Connect(client.Pass());
|
| core_.StartRefreshScheduler();
|
| UpdateRefreshDelay();
|
| @@ -98,66 +167,26 @@ std::string DeviceLocalAccountPolicyBroker::GetDisplayName() const {
|
| return display_name;
|
| }
|
|
|
| -DeviceLocalAccountPolicyService::PolicyBrokerWrapper::PolicyBrokerWrapper()
|
| - : parent(NULL), broker(NULL) {}
|
| -
|
| -DeviceLocalAccountPolicyService::PolicyBrokerWrapper::~PolicyBrokerWrapper() {}
|
| -
|
| -DeviceLocalAccountPolicyBroker*
|
| - DeviceLocalAccountPolicyService::PolicyBrokerWrapper::GetBroker() {
|
| - if (!broker) {
|
| - scoped_ptr<DeviceLocalAccountPolicyStore> store(
|
| - new DeviceLocalAccountPolicyStore(account_id,
|
| - parent->session_manager_client_,
|
| - parent->device_settings_service_,
|
| - parent->background_task_runner_));
|
| - broker = new DeviceLocalAccountPolicyBroker(
|
| - user_id, store.Pass(), base::MessageLoopProxy::current());
|
| - broker->core()->store()->AddObserver(parent);
|
| - broker->core()->store()->Load();
|
| - }
|
| - return broker;
|
| -}
|
| -
|
| -void DeviceLocalAccountPolicyService::PolicyBrokerWrapper::ConnectIfPossible() {
|
| - if (broker && broker->core()->client())
|
| - return;
|
| - scoped_ptr<CloudPolicyClient> client(CreateClient(
|
| - parent->device_settings_service_,
|
| - parent->device_management_service_));
|
| - if (client)
|
| - GetBroker()->Connect(client.Pass());
|
| -}
|
| -
|
| -void DeviceLocalAccountPolicyService::PolicyBrokerWrapper::Disconnect() {
|
| - if (broker)
|
| - broker->Disconnect();
|
| -}
|
| -
|
| -void DeviceLocalAccountPolicyService::PolicyBrokerWrapper::DeleteBroker() {
|
| - if (!broker)
|
| - return;
|
| - broker->core()->store()->RemoveObserver(parent);
|
| - delete broker;
|
| - broker = NULL;
|
| -}
|
| -
|
| DeviceLocalAccountPolicyService::DeviceLocalAccountPolicyService(
|
| chromeos::SessionManagerClient* session_manager_client,
|
| chromeos::DeviceSettingsService* device_settings_service,
|
| chromeos::CrosSettings* cros_settings,
|
| - scoped_refptr<base::SequencedTaskRunner> background_task_runner)
|
| + scoped_refptr<base::SequencedTaskRunner> store_background_task_runner,
|
| + scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner)
|
| : session_manager_client_(session_manager_client),
|
| device_settings_service_(device_settings_service),
|
| cros_settings_(cros_settings),
|
| device_management_service_(NULL),
|
| - background_task_runner_(background_task_runner),
|
| - cros_settings_callback_factory_(this) {
|
| - local_accounts_subscription_ = cros_settings_->AddSettingsObserver(
|
| - chromeos::kAccountsPrefDeviceLocalAccounts,
|
| - base::Bind(&DeviceLocalAccountPolicyService::
|
| - UpdateAccountListIfNonePending,
|
| - base::Unretained(this)));
|
| + waiting_for_cros_settings_(false),
|
| + orphan_cache_deletion_state_(NOT_STARTED),
|
| + store_background_task_runner_(store_background_task_runner),
|
| + extension_cache_task_runner_(extension_cache_task_runner),
|
| + local_accounts_subscription_(cros_settings_->AddSettingsObserver(
|
| + chromeos::kAccountsPrefDeviceLocalAccounts,
|
| + base::Bind(&DeviceLocalAccountPolicyService::
|
| + UpdateAccountListIfNonePending,
|
| + base::Unretained(this)))),
|
| + weak_factory_(this) {
|
| UpdateAccountList();
|
| }
|
|
|
| @@ -173,7 +202,8 @@ void DeviceLocalAccountPolicyService::Connect(
|
| // Connect the brokers.
|
| for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
|
| it != policy_brokers_.end(); ++it) {
|
| - it->second.ConnectIfPossible();
|
| + it->second->ConnectIfPossible(device_settings_service_,
|
| + device_management_service_);
|
| }
|
| }
|
|
|
| @@ -184,7 +214,7 @@ void DeviceLocalAccountPolicyService::Disconnect() {
|
| // Disconnect the brokers.
|
| for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
|
| it != policy_brokers_.end(); ++it) {
|
| - it->second.Disconnect();
|
| + it->second->Disconnect();
|
| }
|
| }
|
|
|
| @@ -195,7 +225,7 @@ DeviceLocalAccountPolicyBroker*
|
| if (entry == policy_brokers_.end())
|
| return NULL;
|
|
|
| - return entry->second.GetBroker();
|
| + return entry->second;
|
| }
|
|
|
| bool DeviceLocalAccountPolicyService::IsPolicyAvailableForUser(
|
| @@ -229,52 +259,214 @@ void DeviceLocalAccountPolicyService::OnStoreError(CloudPolicyStore* store) {
|
| FOR_EACH_OBSERVER(Observer, observers_, OnPolicyUpdated(broker->user_id()));
|
| }
|
|
|
| +bool DeviceLocalAccountPolicyService::IsExtensionCacheDirectoryBusy(
|
| + const std::string& account_id) {
|
| + return busy_extension_cache_directories_.find(account_id) !=
|
| + busy_extension_cache_directories_.end();
|
| +}
|
| +
|
| +void DeviceLocalAccountPolicyService::StartExtensionCachesIfPossible() {
|
| + for (PolicyBrokerMap::iterator it = policy_brokers_.begin();
|
| + it != policy_brokers_.end(); ++it) {
|
| + if (!it->second->extension_loader()->IsCacheRunning() &&
|
| + !IsExtensionCacheDirectoryBusy(it->second->account_id())) {
|
| + it->second->extension_loader()->StartCache(extension_cache_task_runner_);
|
| + }
|
| + }
|
| +}
|
| +
|
| +bool DeviceLocalAccountPolicyService::StartExtensionCacheForAccountIfPresent(
|
| + const std::string& account_id) {
|
| + for (PolicyBrokerMap::iterator it = policy_brokers_.begin();
|
| + it != policy_brokers_.end(); ++it) {
|
| + if (it->second->account_id() == account_id) {
|
| + DCHECK(!it->second->extension_loader()->IsCacheRunning());
|
| + it->second->extension_loader()->StartCache(extension_cache_task_runner_);
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted() {
|
| + DCHECK_EQ(IN_PROGRESS, orphan_cache_deletion_state_);
|
| +
|
| + orphan_cache_deletion_state_ = DONE;
|
| + StartExtensionCachesIfPossible();
|
| +}
|
| +
|
| +void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown(
|
| + const std::string& account_id) {
|
| + DCHECK_NE(NOT_STARTED, orphan_cache_deletion_state_);
|
| + DCHECK(IsExtensionCacheDirectoryBusy(account_id));
|
| +
|
| + // The account with |account_id| was deleted and the broker for it has shut
|
| + // down completely.
|
| +
|
| + if (StartExtensionCacheForAccountIfPresent(account_id)) {
|
| + // If another account with the same ID was created in the meantime, its
|
| + // extension cache is started, reusing the cache directory. The directory no
|
| + // longer needs to be marked as busy in this case.
|
| + busy_extension_cache_directories_.erase(account_id);
|
| + return;
|
| + }
|
| +
|
| + // If no account with |account_id| exists anymore, the cache directory should
|
| + // be removed. The directory must stay marked as busy while the removal is in
|
| + // progress.
|
| + extension_cache_task_runner_->PostTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&DeleteObsoleteExtensionCache, account_id),
|
| + base::Bind(&DeviceLocalAccountPolicyService::
|
| + OnObsoleteExtensionCacheDeleted,
|
| + weak_factory_.GetWeakPtr(),
|
| + account_id));
|
| +}
|
| +
|
| +void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheDeleted(
|
| + const std::string& account_id) {
|
| + DCHECK_EQ(DONE, orphan_cache_deletion_state_);
|
| + DCHECK(IsExtensionCacheDirectoryBusy(account_id));
|
| +
|
| + // The cache directory for |account_id| has been deleted. The directory no
|
| + // longer needs to be marked as busy.
|
| + busy_extension_cache_directories_.erase(account_id);
|
| +
|
| + // If another account with the same ID was created in the meantime, start its
|
| + // extension cache, creating a new cache directory.
|
| + StartExtensionCacheForAccountIfPresent(account_id);
|
| +}
|
| +
|
| void DeviceLocalAccountPolicyService::UpdateAccountListIfNonePending() {
|
| // Avoid unnecessary calls to UpdateAccountList(): If an earlier call is still
|
| // pending (because the |cros_settings_| are not trusted yet), the updated
|
| // account list will be processed by that call when it eventually runs.
|
| - if (!cros_settings_callback_factory_.HasWeakPtrs())
|
| + if (!waiting_for_cros_settings_)
|
| UpdateAccountList();
|
| }
|
|
|
| void DeviceLocalAccountPolicyService::UpdateAccountList() {
|
| - if (chromeos::CrosSettingsProvider::TRUSTED !=
|
| - cros_settings_->PrepareTrustedValues(
|
| - base::Bind(&DeviceLocalAccountPolicyService::UpdateAccountList,
|
| - cros_settings_callback_factory_.GetWeakPtr()))) {
|
| - return;
|
| + chromeos::CrosSettingsProvider::TrustedStatus status =
|
| + cros_settings_->PrepareTrustedValues(
|
| + base::Bind(&DeviceLocalAccountPolicyService::UpdateAccountList,
|
| + weak_factory_.GetWeakPtr()));
|
| + switch (status) {
|
| + case chromeos::CrosSettingsProvider::TRUSTED:
|
| + waiting_for_cros_settings_ = false;
|
| + break;
|
| + case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
|
| + waiting_for_cros_settings_ = true;
|
| + return;
|
| + case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
|
| + waiting_for_cros_settings_ = false;
|
| + return;
|
| }
|
|
|
| // Update |policy_brokers_|, keeping existing entries.
|
| PolicyBrokerMap old_policy_brokers;
|
| policy_brokers_.swap(old_policy_brokers);
|
| + std::set<std::string> subdirectories_to_keep;
|
| const std::vector<DeviceLocalAccount> device_local_accounts =
|
| GetDeviceLocalAccounts(cros_settings_);
|
| for (std::vector<DeviceLocalAccount>::const_iterator it =
|
| device_local_accounts.begin();
|
| it != device_local_accounts.end(); ++it) {
|
| - PolicyBrokerWrapper& wrapper = policy_brokers_[it->user_id];
|
| - wrapper.user_id = it->user_id;
|
| - wrapper.account_id = it->account_id;
|
| - wrapper.parent = this;
|
| -
|
| - // Reuse the existing broker if present.
|
| - PolicyBrokerWrapper& existing_wrapper = old_policy_brokers[it->user_id];
|
| - wrapper.broker = existing_wrapper.broker;
|
| - existing_wrapper.broker = NULL;
|
| + PolicyBrokerMap::iterator broker_it = old_policy_brokers.find(it->user_id);
|
| +
|
| + scoped_ptr<DeviceLocalAccountPolicyBroker> broker;
|
| + bool broker_initialized = false;
|
| + if (broker_it != old_policy_brokers.end()) {
|
| + // Reuse the existing broker if present.
|
| + broker.reset(broker_it->second);
|
| + old_policy_brokers.erase(broker_it);
|
| + broker_initialized = true;
|
| + } else {
|
| + scoped_ptr<DeviceLocalAccountPolicyStore> store(
|
| + new DeviceLocalAccountPolicyStore(it->account_id,
|
| + session_manager_client_,
|
| + device_settings_service_,
|
| + store_background_task_runner_));
|
| + store->AddObserver(this);
|
| + broker.reset(new DeviceLocalAccountPolicyBroker(
|
| + *it,
|
| + store.Pass(),
|
| + base::MessageLoopProxy::current()));
|
| + }
|
|
|
| // Fire up the cloud connection for fetching policy for the account from
|
| // the cloud if this is an enterprise-managed device.
|
| - wrapper.ConnectIfPossible();
|
| + broker->ConnectIfPossible(device_settings_service_,
|
| + device_management_service_);
|
| +
|
| + policy_brokers_[it->user_id] = broker.release();
|
| + if (!broker_initialized) {
|
| + // The broker must be initialized after it has been added to
|
| + // |policy_brokers_|.
|
| + policy_brokers_[it->user_id]->Initialize();
|
| + }
|
| +
|
| + if (orphan_cache_deletion_state_ == NOT_STARTED) {
|
| + subdirectories_to_keep.insert(
|
| + GetCacheSubdirectoryForAccountID(it->account_id));
|
| + }
|
| + }
|
| +
|
| + std::set<std::string> obsolete_account_ids;
|
| + for (PolicyBrokerMap::const_iterator it = old_policy_brokers.begin();
|
| + it != old_policy_brokers.end(); ++it) {
|
| + obsolete_account_ids.insert(it->second->account_id());
|
| + }
|
| +
|
| + if (orphan_cache_deletion_state_ == NOT_STARTED) {
|
| + DCHECK(old_policy_brokers.empty());
|
| + DCHECK(busy_extension_cache_directories_.empty());
|
| +
|
| + // If this method is running for the first time, no extension caches have
|
| + // been started yet. Take this opportunity to do a clean-up by removing
|
| + // orphaned cache directories not found in |subdirectories_to_keep| from the
|
| + // cache directory.
|
| + orphan_cache_deletion_state_ = IN_PROGRESS;
|
| + extension_cache_task_runner_->PostTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&DeleteOrphanedExtensionCaches, subdirectories_to_keep),
|
| + base::Bind(&DeviceLocalAccountPolicyService::
|
| + OnOrphanedExtensionCachesDeleted,
|
| + weak_factory_.GetWeakPtr()));
|
| +
|
| + // Start the extension caches for all brokers. These belong to accounts in
|
| + // |account_ids| and are not affected by the clean-up.
|
| + StartExtensionCachesIfPossible();
|
| + } else {
|
| + // If this method has run before, obsolete brokers may exist. Shut down
|
| + // their extension caches and delete the brokers.
|
| + DeleteBrokers(&old_policy_brokers);
|
| +
|
| + if (orphan_cache_deletion_state_ == DONE) {
|
| + // If the initial clean-up of orphaned cache directories has been
|
| + // complete, start any extension caches that are not running yet but can
|
| + // be started now because their cache directories are not busy.
|
| + StartExtensionCachesIfPossible();
|
| + }
|
| }
|
| - DeleteBrokers(&old_policy_brokers);
|
|
|
| FOR_EACH_OBSERVER(Observer, observers_, OnDeviceLocalAccountsChanged());
|
| }
|
|
|
| void DeviceLocalAccountPolicyService::DeleteBrokers(PolicyBrokerMap* map) {
|
| - for (PolicyBrokerMap::iterator it = map->begin(); it != map->end(); ++it)
|
| - it->second.DeleteBroker();
|
| + for (PolicyBrokerMap::iterator it = map->begin(); it != map->end(); ++it) {
|
| + it->second->core()->store()->RemoveObserver(this);
|
| + scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader>
|
| + extension_loader = it->second->extension_loader();
|
| + if (extension_loader->IsCacheRunning()) {
|
| + DCHECK(!IsExtensionCacheDirectoryBusy(it->second->account_id()));
|
| + busy_extension_cache_directories_.insert(it->second->account_id());
|
| + extension_loader->StopCache(base::Bind(
|
| + &DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown,
|
| + weak_factory_.GetWeakPtr(),
|
| + it->second->account_id()));
|
| + }
|
| + delete it->second;
|
| + }
|
| map->clear();
|
| }
|
|
|
| @@ -283,8 +475,8 @@ DeviceLocalAccountPolicyBroker*
|
| CloudPolicyStore* store) {
|
| for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
|
| it != policy_brokers_.end(); ++it) {
|
| - if (it->second.broker && it->second.broker->core()->store() == store)
|
| - return it->second.broker;
|
| + if (it->second->core()->store() == store)
|
| + return it->second;
|
| }
|
| return NULL;
|
| }
|
|
|