Chromium Code Reviews| 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..74e292696e31b1b773956121d2da8b3e4997742d |
| --- /dev/null |
| +++ b/chrome/browser/policy/cros_user_policy_cache.cc |
| @@ -0,0 +1,419 @@ |
| +// 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/chromeos/dbus/session_manager_client.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 "content/public/browser/browser_thread.h" |
| +#include "crypto/signature_verifier.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); |
| +}; |
| + |
| +// static |
|
pastarmovj
2012/04/05 09:56:21
The constructor is static?
Mattias Nissler (ping if slow)
2012/04/05 10:26:04
Done.
|
| +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: |
| + explicit RetrievePolicyOperation(const ResultCallback& callback); |
|
pastarmovj
2012/04/05 09:56:21
Is this one needed/implemented?
Mattias Nissler (ping if slow)
2012/04/05 10:26:04
No. Done.
|
| + |
| + // 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); |
| +}; |
| + |
| +// static |
|
pastarmovj
2012/04/05 09:56:21
Ditto.
Mattias Nissler (ping if slow)
2012/04/05 10:26:04
Done.
|
| +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() || |
|
pastarmovj
2012/04/05 09:56:21
Shouldn't those lines aline with the ( of the if?
Mattias Nissler (ping if slow)
2012/04/05 10:26:04
Absolutely. Done.
|
| + 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 |