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 |