| Index: chrome/browser/policy/cros_user_policy_cache.cc
|
| diff --git a/chrome/browser/policy/cros_user_policy_cache.cc b/chrome/browser/policy/cros_user_policy_cache.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..19c0ba282bd7a0d426243a080c1e4349bafcf82a
|
| --- /dev/null
|
| +++ b/chrome/browser/policy/cros_user_policy_cache.cc
|
| @@ -0,0 +1,414 @@
|
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/policy/cros_user_policy_cache.h"
|
| +
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/file_path.h"
|
| +#include "base/file_util.h"
|
| +#include "chrome/browser/policy/proto/cloud_policy.pb.h"
|
| +#include "chrome/browser/policy/proto/device_management_backend.pb.h"
|
| +#include "chrome/browser/policy/proto/device_management_local.pb.h"
|
| +#include "chromeos/dbus/session_manager_client.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +
|
| +using content::BrowserThread;
|
| +
|
| +namespace em = enterprise_management;
|
| +
|
| +namespace policy {
|
| +
|
| +// Decodes a CloudPolicySettings object into a policy map. The implementation is
|
| +// generated code in policy/cloud_policy_generated.cc.
|
| +void DecodePolicy(const em::CloudPolicySettings& policy,
|
| + PolicyMap* policies);
|
| +
|
| +// Takes care of sending a new policy blob to session manager and reports back
|
| +// the status through a callback.
|
| +class CrosUserPolicyCache::StorePolicyOperation {
|
| + public:
|
| + typedef base::Callback<void(bool)> StatusCallback;
|
| +
|
| + StorePolicyOperation(
|
| + const em::PolicyFetchResponse& policy,
|
| + chromeos::SessionManagerClient* session_manager_client,
|
| + const StatusCallback& callback);
|
| +
|
| + // Executes the operation.
|
| + void Run();
|
| +
|
| + // Cancels a pending callback.
|
| + void Cancel();
|
| +
|
| + const em::PolicyFetchResponse& policy() { return policy_; }
|
| +
|
| + private:
|
| + // StorePolicyOperation manages its own lifetime.
|
| + ~StorePolicyOperation() {}
|
| +
|
| + // A callback function suitable for passing to session_manager_client.
|
| + void OnPolicyStored(bool result);
|
| +
|
| + em::PolicyFetchResponse policy_;
|
| + chromeos::SessionManagerClient* session_manager_client_;
|
| + StatusCallback callback_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(StorePolicyOperation);
|
| +};
|
| +
|
| +CrosUserPolicyCache::StorePolicyOperation::StorePolicyOperation(
|
| + const em::PolicyFetchResponse& policy,
|
| + chromeos::SessionManagerClient* session_manager_client,
|
| + const StatusCallback& callback)
|
| + : policy_(policy),
|
| + session_manager_client_(session_manager_client),
|
| + callback_(callback) {}
|
| +
|
| +void CrosUserPolicyCache::StorePolicyOperation::Run() {
|
| + std::string serialized;
|
| + if (!policy_.SerializeToString(&serialized)) {
|
| + LOG(ERROR) << "Failed to serialize policy protobuf!";
|
| + callback_.Run(false);
|
| + delete this;
|
| + }
|
| + session_manager_client_->StoreUserPolicy(
|
| + serialized,
|
| + base::Bind(&CrosUserPolicyCache::StorePolicyOperation::OnPolicyStored,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +void CrosUserPolicyCache::StorePolicyOperation::Cancel() {
|
| + callback_.Reset();
|
| +}
|
| +
|
| +void CrosUserPolicyCache::StorePolicyOperation::OnPolicyStored(bool result) {
|
| + if (!callback_.is_null())
|
| + callback_.Run(result);
|
| + delete this;
|
| +}
|
| +
|
| +class CrosUserPolicyCache::RetrievePolicyOperation {
|
| + public:
|
| + typedef base::Callback<void(bool, const em::PolicyFetchResponse&)>
|
| + ResultCallback;
|
| +
|
| + RetrievePolicyOperation(
|
| + chromeos::SessionManagerClient* session_manager_client,
|
| + const ResultCallback& callback);
|
| +
|
| + // Executes the operation.
|
| + void Run();
|
| +
|
| + // Cancels a pending callback.
|
| + void Cancel();
|
| +
|
| + private:
|
| + // RetrievePolicyOperation manages its own lifetime.
|
| + ~RetrievePolicyOperation() {}
|
| +
|
| + // Decodes the policy data and triggers a signature check.
|
| + void OnPolicyRetrieved(const std::string& policy_blob);
|
| +
|
| + chromeos::SessionManagerClient* session_manager_client_;
|
| + ResultCallback callback_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(RetrievePolicyOperation);
|
| +};
|
| +
|
| +CrosUserPolicyCache::RetrievePolicyOperation::RetrievePolicyOperation(
|
| + chromeos::SessionManagerClient* session_manager_client,
|
| + const ResultCallback& callback)
|
| + : session_manager_client_(session_manager_client),
|
| + callback_(callback) {}
|
| +
|
| +void CrosUserPolicyCache::RetrievePolicyOperation::Run() {
|
| + session_manager_client_->RetrieveUserPolicy(
|
| + base::Bind(
|
| + &CrosUserPolicyCache::RetrievePolicyOperation::OnPolicyRetrieved,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +void CrosUserPolicyCache::RetrievePolicyOperation::Cancel() {
|
| + callback_.Reset();
|
| +}
|
| +
|
| +void CrosUserPolicyCache::RetrievePolicyOperation::OnPolicyRetrieved(
|
| + const std::string& policy_blob) {
|
| + bool status = true;
|
| + em::PolicyFetchResponse policy;
|
| + if (!policy.ParseFromString(policy_blob) ||
|
| + !policy.has_policy_data() ||
|
| + !policy.has_policy_data_signature()) {
|
| + LOG(ERROR) << "Failed to decode policy";
|
| + status = false;
|
| + }
|
| +
|
| + if (!callback_.is_null())
|
| + callback_.Run(status, policy);
|
| + delete this;
|
| +}
|
| +
|
| +CrosUserPolicyCache::CrosUserPolicyCache(
|
| + chromeos::SessionManagerClient* session_manager_client,
|
| + CloudPolicyDataStore* data_store,
|
| + bool wait_for_policy_fetch,
|
| + const FilePath& legacy_token_cache_file,
|
| + const FilePath& legacy_policy_cache_file)
|
| + : session_manager_client_(session_manager_client),
|
| + data_store_(data_store),
|
| + pending_policy_fetch_(wait_for_policy_fetch),
|
| + pending_disk_cache_load_(true),
|
| + store_operation_(NULL),
|
| + retrieve_operation_(NULL),
|
| + legacy_cache_dir_(legacy_token_cache_file.DirName()),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(
|
| + legacy_token_cache_delegate_factory_(this)),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(
|
| + legacy_policy_cache_delegate_factory_(this)) {
|
| + DCHECK_EQ(legacy_token_cache_file.DirName().value(),
|
| + legacy_policy_cache_file.DirName().value());
|
| +
|
| + legacy_token_loader_ =
|
| + new UserPolicyTokenLoader(
|
| + legacy_token_cache_delegate_factory_.GetWeakPtr(),
|
| + legacy_token_cache_file);
|
| + legacy_policy_cache_ =
|
| + new UserPolicyDiskCache(
|
| + legacy_policy_cache_delegate_factory_.GetWeakPtr(),
|
| + legacy_policy_cache_file);
|
| +}
|
| +
|
| +CrosUserPolicyCache::~CrosUserPolicyCache() {
|
| + CancelStore();
|
| + CancelRetrieve();
|
| +}
|
| +
|
| +void CrosUserPolicyCache::Load() {
|
| + retrieve_operation_ =
|
| + new RetrievePolicyOperation(
|
| + session_manager_client_,
|
| + base::Bind(&CrosUserPolicyCache::OnPolicyLoadDone,
|
| + base::Unretained(this)));
|
| + retrieve_operation_->Run();
|
| +}
|
| +
|
| +bool CrosUserPolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) {
|
| + CancelStore();
|
| + set_last_policy_refresh_time(base::Time::NowFromSystemTime());
|
| + pending_policy_fetch_ = true;
|
| + store_operation_ =
|
| + new StorePolicyOperation(policy,
|
| + session_manager_client_,
|
| + base::Bind(&CrosUserPolicyCache::OnPolicyStored,
|
| + base::Unretained(this)));
|
| + store_operation_->Run();
|
| + return true;
|
| +}
|
| +
|
| +void CrosUserPolicyCache::SetUnmanaged() {
|
| + base::Time now(base::Time::NowFromSystemTime());
|
| + SetUnmanagedInternal(now);
|
| +
|
| + // Construct a policy blob with unmanaged state.
|
| + em::PolicyData policy_data;
|
| + policy_data.set_policy_type(data_store_->policy_type());
|
| + policy_data.set_timestamp((now - base::Time::UnixEpoch()).InMilliseconds());
|
| + policy_data.set_state(em::PolicyData::UNMANAGED);
|
| +
|
| + em::PolicyFetchResponse policy;
|
| + if (!policy_data.SerializeToString(policy.mutable_policy_data())) {
|
| + LOG(ERROR) << "Failed to serialize policy_data";
|
| + return;
|
| + }
|
| +
|
| + SetPolicy(policy);
|
| +}
|
| +
|
| +void CrosUserPolicyCache::SetFetchingDone() {
|
| + // If there is a pending policy store or reload, wait for that to complete
|
| + // before reporting fetching done.
|
| + if (store_operation_ || retrieve_operation_)
|
| + return;
|
| +
|
| + pending_policy_fetch_ = false;
|
| + CheckIfDone();
|
| +}
|
| +
|
| +bool CrosUserPolicyCache::DecodePolicyData(const em::PolicyData& policy_data,
|
| + PolicyMap* policies) {
|
| + em::CloudPolicySettings policy;
|
| + if (!policy.ParseFromString(policy_data.policy_value())) {
|
| + LOG(WARNING) << "Failed to parse CloudPolicySettings protobuf.";
|
| + return false;
|
| + }
|
| + DecodePolicy(policy, policies);
|
| + return true;
|
| +}
|
| +
|
| +void CrosUserPolicyCache::OnTokenLoaded(const std::string& token,
|
| + const std::string& device_id) {
|
| + if (token.empty())
|
| + LOG(WARNING) << "Failed to load legacy token cache";
|
| +
|
| + data_store_->set_device_id(device_id);
|
| + data_store_->SetDeviceToken(token, true);
|
| +}
|
| +
|
| +void CrosUserPolicyCache::OnDiskCacheLoaded(
|
| + UserPolicyDiskCache::LoadResult result,
|
| + const em::CachedCloudPolicyResponse& policy) {
|
| + if (result == UserPolicyDiskCache::LOAD_RESULT_SUCCESS) {
|
| + if (policy.unmanaged())
|
| + SetUnmanagedInternal(base::Time::FromTimeT(policy.timestamp()));
|
| + else if (policy.has_cloud_policy())
|
| + InstallLegacyPolicy(policy.cloud_policy());
|
| + } else {
|
| + LOG(WARNING) << "Failed to load legacy policy cache: " << result;
|
| + }
|
| +
|
| + pending_disk_cache_load_ = false;
|
| + CheckIfDone();
|
| +}
|
| +
|
| +void CrosUserPolicyCache::OnPolicyStored(bool result) {
|
| + DCHECK(store_operation_);
|
| + CancelStore();
|
| + if (result) {
|
| + // Policy is stored successfully, reload from session_manager and apply.
|
| + // This helps us making sure we only use policy that session_manager has
|
| + // checked and confirmed to be good.
|
| + CancelRetrieve();
|
| + retrieve_operation_ =
|
| + new RetrievePolicyOperation(
|
| + session_manager_client_,
|
| + base::Bind(&CrosUserPolicyCache::OnPolicyReloadDone,
|
| + base::Unretained(this)));
|
| + retrieve_operation_->Run();
|
| +
|
| + // Now that the new policy blob is installed, remove the old cache dir.
|
| + if (!legacy_cache_dir_.empty()) {
|
| + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
|
| + base::Bind(&RemoveLegacyCacheDir,
|
| + legacy_cache_dir_));
|
| + }
|
| + } else {
|
| + LOG(ERROR) << "Failed to store user policy.";
|
| + InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
|
| + CloudPolicySubsystem::POLICY_LOCAL_ERROR);
|
| + pending_policy_fetch_ = false;
|
| + CheckIfDone();
|
| + }
|
| +}
|
| +
|
| +void CrosUserPolicyCache::OnPolicyLoadDone(
|
| + bool result,
|
| + const em::PolicyFetchResponse& policy) {
|
| + DCHECK(retrieve_operation_);
|
| + CancelRetrieve();
|
| + if (!result) {
|
| + LOG(WARNING) << "No user policy present, trying legacy caches.";
|
| + legacy_token_loader_->Load();
|
| + legacy_policy_cache_->Load();
|
| + return;
|
| + }
|
| +
|
| + // We have new-style policy, no need to clean up.
|
| + legacy_cache_dir_.clear();
|
| +
|
| + em::PolicyData policy_data;
|
| + if (!policy_data.ParseFromString(policy.policy_data())) {
|
| + LOG(WARNING) << "Failed to parse PolicyData protobuf.";
|
| + InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
|
| + CloudPolicySubsystem::POLICY_LOCAL_ERROR);
|
| + data_store_->SetDeviceToken(std::string(), true);
|
| + } else if (policy_data.request_token().empty() ||
|
| + policy_data.username().empty() ||
|
| + policy_data.device_id().empty()) {
|
| + LOG(WARNING) << "Policy protobuf is missing credentials";
|
| + InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
|
| + CloudPolicySubsystem::POLICY_LOCAL_ERROR);
|
| + data_store_->SetDeviceToken(std::string(), true);
|
| + } else {
|
| + data_store_->set_device_id(policy_data.device_id());
|
| + data_store_->SetDeviceToken(policy_data.request_token(), true);
|
| + if (SetPolicyInternal(policy, NULL, true))
|
| + set_last_policy_refresh_time(base::Time::NowFromSystemTime());
|
| + }
|
| +
|
| + pending_disk_cache_load_ = false;
|
| + CheckIfDone();
|
| +}
|
| +
|
| +void CrosUserPolicyCache::OnPolicyReloadDone(
|
| + bool result,
|
| + const em::PolicyFetchResponse& policy) {
|
| + DCHECK(retrieve_operation_);
|
| + CancelRetrieve();
|
| + if (result) {
|
| + if (SetPolicyInternal(policy, NULL, false))
|
| + set_last_policy_refresh_time(base::Time::NowFromSystemTime());
|
| + } else {
|
| + InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
|
| + CloudPolicySubsystem::POLICY_LOCAL_ERROR);
|
| + }
|
| + pending_policy_fetch_ = false;
|
| + CheckIfDone();
|
| +}
|
| +
|
| +void CrosUserPolicyCache::CancelStore() {
|
| + if (store_operation_) {
|
| + store_operation_->Cancel();
|
| + store_operation_ = NULL;
|
| + }
|
| +}
|
| +
|
| +void CrosUserPolicyCache::CancelRetrieve() {
|
| + if (retrieve_operation_) {
|
| + retrieve_operation_->Cancel();
|
| + retrieve_operation_ = NULL;
|
| + }
|
| +}
|
| +
|
| +void CrosUserPolicyCache::CheckIfDone() {
|
| + if (!pending_policy_fetch_ && !pending_disk_cache_load_) {
|
| + if (!IsReady())
|
| + SetReady();
|
| + CloudPolicyCacheBase::SetFetchingDone();
|
| + }
|
| +}
|
| +
|
| +void CrosUserPolicyCache::InstallLegacyPolicy(
|
| + const em::PolicyFetchResponse& policy) {
|
| + em::PolicyFetchResponse mutable_policy(policy);
|
| + mutable_policy.clear_policy_data_signature();
|
| + mutable_policy.clear_new_public_key();
|
| + mutable_policy.clear_new_public_key_signature();
|
| + em::PolicyData policy_data;
|
| + if (!policy_data.ParseFromString(mutable_policy.policy_data())) {
|
| + LOG(ERROR) << "Failed to parse policy data.";
|
| + return;
|
| + }
|
| +
|
| + policy_data.clear_public_key_version();
|
| + if (!policy_data.SerializeToString(mutable_policy.mutable_policy_data())) {
|
| + LOG(ERROR) << "Failed to serialize policy data.";
|
| + return;
|
| + }
|
| +
|
| + base::Time timestamp;
|
| + if (SetPolicyInternal(mutable_policy, ×tamp, true))
|
| + set_last_policy_refresh_time(timestamp);
|
| +}
|
| +
|
| +// static
|
| +void CrosUserPolicyCache::RemoveLegacyCacheDir(const FilePath& dir) {
|
| + if (!file_util::Delete(dir, true))
|
| + PLOG(ERROR) << "Failed to remove " << dir.value();
|
| +}
|
| +
|
| +} // namespace policy
|
|
|