diff options
-rw-r--r-- | chrome/browser/chromeos/login/login_utils_browsertest.cc | 25 | ||||
-rw-r--r-- | chrome/browser/policy/browser_policy_connector.cc | 45 | ||||
-rw-r--r-- | chrome/browser/policy/cros_user_policy_cache.cc | 414 | ||||
-rw-r--r-- | chrome/browser/policy/cros_user_policy_cache.h | 126 | ||||
-rw-r--r-- | chrome/browser/policy/cros_user_policy_cache_unittest.cc | 190 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 4 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 2 | ||||
-rw-r--r-- | chrome/common/chrome_paths.cc | 3 | ||||
-rw-r--r-- | chrome/common/chrome_paths.h | 2 |
9 files changed, 789 insertions, 22 deletions
diff --git a/chrome/browser/chromeos/login/login_utils_browsertest.cc b/chrome/browser/chromeos/login/login_utils_browsertest.cc index 4137ce4..d5c8cdb 100644 --- a/chrome/browser/chromeos/login/login_utils_browsertest.cc +++ b/chrome/browser/chromeos/login/login_utils_browsertest.cc @@ -49,6 +49,7 @@ namespace em = enterprise_management; using ::testing::DoAll; using ::testing::Return; +using ::testing::SaveArg; using ::testing::SetArgPointee; using ::testing::_; using content::BrowserThread; @@ -79,8 +80,12 @@ const char kDMPolicyRequest[] = const char kDMToken[] = "1234"; -ACTION_P(MockSessionManagerClientPolicyCallback, policy) { - arg0.Run(policy); +ACTION_P(MockSessionManagerClientRetrievePolicyCallback, policy) { + arg0.Run(*policy); +} + +ACTION_P(MockSessionManagerClientStorePolicyCallback, success) { + arg1.Run(success); } template<typename TESTBASE> @@ -111,9 +116,6 @@ class LoginUtilsTestBase : public TESTBASE, virtual void SetUp() OVERRIDE { ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); - // BrowserPolicyConnector makes the UserPolicyCache read relative to this - // path. Make sure it's in a clean state. - PathService::Override(chrome::DIR_USER_DATA, scoped_temp_dir_.path()); CommandLine* command_line = CommandLine::ForCurrentProcess(); command_line->AppendSwitch(switches::kEnableDevicePolicy); @@ -132,7 +134,15 @@ class LoginUtilsTestBase : public TESTBASE, MockSessionManagerClient* session_managed_client = mock_dbus_thread_manager_.mock_session_manager_client(); EXPECT_CALL(*session_managed_client, RetrieveDevicePolicy(_)) - .WillRepeatedly(MockSessionManagerClientPolicyCallback("")); + .WillRepeatedly( + MockSessionManagerClientRetrievePolicyCallback(&device_policy_)); + EXPECT_CALL(*session_managed_client, RetrieveUserPolicy(_)) + .WillRepeatedly( + MockSessionManagerClientRetrievePolicyCallback(&user_policy_)); + EXPECT_CALL(*session_managed_client, StoreUserPolicy(_, _)) + .WillRepeatedly( + DoAll(SaveArg<0>(&user_policy_), + MockSessionManagerClientStorePolicyCallback(true))); mock_async_method_caller_ = new cryptohome::MockAsyncMethodCaller; cryptohome::AsyncMethodCaller::InitializeForTesting( @@ -364,6 +374,9 @@ class LoginUtilsTestBase : public TESTBASE, private: ScopedTempDir scoped_temp_dir_; + std::string device_policy_; + std::string user_policy_; + DISALLOW_COPY_AND_ASSIGN(LoginUtilsTestBase); }; diff --git a/chrome/browser/policy/browser_policy_connector.cc b/chrome/browser/policy/browser_policy_connector.cc index a29c610..1bb6bda 100644 --- a/chrome/browser/policy/browser_policy_connector.cc +++ b/chrome/browser/policy/browser_policy_connector.cc @@ -22,6 +22,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/pref_names.h" +#include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" #include "grit/generated_resources.h" @@ -39,6 +40,7 @@ #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/system/statistics_provider.h" #include "chrome/browser/policy/app_pack_updater.h" +#include "chrome/browser/policy/cros_user_policy_cache.h" #include "chrome/browser/policy/device_policy_cache.h" #include "chrome/browser/policy/network_configuration_updater.h" #endif @@ -311,23 +313,37 @@ void BrowserPolicyConnector::InitializeUserPolicy( CommandLine* command_line = CommandLine::ForCurrentProcess(); - FilePath policy_dir; - PathService::Get(chrome::DIR_USER_DATA, &policy_dir); + if (command_line->HasSwitch(switches::kDeviceManagementUrl)) { + user_data_store_.reset(CloudPolicyDataStore::CreateForUserPolicies()); + + FilePath profile_dir; + PathService::Get(chrome::DIR_USER_DATA, &profile_dir); #if defined(OS_CHROMEOS) - policy_dir = policy_dir.Append( - command_line->GetSwitchValuePath(switches::kLoginProfile)); + profile_dir = profile_dir.Append( + command_line->GetSwitchValuePath(switches::kLoginProfile)); #endif + const FilePath policy_dir = profile_dir.Append(kPolicyDir); + const FilePath policy_cache_file = policy_dir.Append(kPolicyCacheFile); + const FilePath token_cache_file = policy_dir.Append(kTokenCacheFile); + CloudPolicyCacheBase* user_policy_cache = NULL; - if (command_line->HasSwitch(switches::kDeviceManagementUrl)) { - FilePath policy_cache_dir = policy_dir.Append(kPolicyDir); - - UserPolicyCache* user_policy_cache = - new UserPolicyCache(policy_cache_dir.Append(kPolicyCacheFile), - wait_for_policy_fetch); - user_data_store_.reset(CloudPolicyDataStore::CreateForUserPolicies()); +#if defined(OS_CHROMEOS) + user_policy_cache = + new CrosUserPolicyCache( + chromeos::DBusThreadManager::Get()->GetSessionManagerClient(), + user_data_store_.get(), + wait_for_policy_fetch, + token_cache_file, + policy_cache_file); +#else + user_policy_cache = new UserPolicyCache(policy_cache_file, + wait_for_policy_fetch); user_policy_token_cache_.reset( - new UserPolicyTokenCache(user_data_store_.get(), - policy_cache_dir.Append(kTokenCacheFile))); + new UserPolicyTokenCache(user_data_store_.get(), token_cache_file)); + + // Initiate the DM-Token load. + user_policy_token_cache_->Load(); +#endif managed_cloud_provider_->SetUserPolicyCache(user_policy_cache); recommended_cloud_provider_->SetUserPolicyCache(user_policy_cache); @@ -335,9 +351,6 @@ void BrowserPolicyConnector::InitializeUserPolicy( user_data_store_.get(), user_policy_cache)); - // Initiate the DM-Token load. - user_policy_token_cache_->Load(); - user_data_store_->set_user_name(user_name); user_data_store_->set_user_affiliation(GetUserAffiliation(user_name)); 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 0000000..19c0ba2 --- /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 diff --git a/chrome/browser/policy/cros_user_policy_cache.h b/chrome/browser/policy/cros_user_policy_cache.h new file mode 100644 index 0000000..c31c45e --- /dev/null +++ b/chrome/browser/policy/cros_user_policy_cache.h @@ -0,0 +1,126 @@ +// 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. + +#ifndef CHROME_BROWSER_POLICY_CROS_USER_POLICY_CACHE_H_ +#define CHROME_BROWSER_POLICY_CROS_USER_POLICY_CACHE_H_ +#pragma once + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/file_path.h" +#include "base/memory/ref_counted.h" +#include "chrome/browser/policy/cloud_policy_cache_base.h" +#include "chrome/browser/policy/user_policy_disk_cache.h" +#include "chrome/browser/policy/user_policy_token_cache.h" + +namespace chromeos { +class SessionManagerClient; +} + +namespace policy { + +class CloudPolicyDataStore; + +// User policy cache that talks to the ChromeOS login library in order to store +// and fetch policy data. +class CrosUserPolicyCache : public CloudPolicyCacheBase, + public UserPolicyTokenCache::Delegate, + public UserPolicyDiskCache::Delegate { + public: + 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); + virtual ~CrosUserPolicyCache(); + + // CloudPolicyCacheBase implementation. + virtual void Load() OVERRIDE; + virtual bool SetPolicy( + const enterprise_management::PolicyFetchResponse& policy) OVERRIDE; + virtual void SetUnmanaged() OVERRIDE; + virtual void SetFetchingDone() OVERRIDE; + + protected: + virtual bool DecodePolicyData( + const enterprise_management::PolicyData& policy_data, + PolicyMap* policies) OVERRIDE; + + private: + class StorePolicyOperation; + class RetrievePolicyOperation; + + // UserPolicyTokenLoader::Delegate: + virtual void OnTokenLoaded(const std::string& token, + const std::string& device_id) OVERRIDE; + + // UserPolicyDiskCache::Delegate: + virtual void OnDiskCacheLoaded( + UserPolicyDiskCache::LoadResult result, + const enterprise_management::CachedCloudPolicyResponse& policy) OVERRIDE; + + // Used as a callback for the policy store operation. + void OnPolicyStored(bool result); + + // Callback for the initial policy load. Installs the policy and passes the + // loaded token and device ID to the data store. + void OnPolicyLoadDone( + bool result, + const enterprise_management::PolicyFetchResponse& policy); + + // Callback for the policy retrieval operation run to reload the policy after + // new policy has been successfully stored. Installs the new policy in the + // cache and publishes it if successful. + void OnPolicyReloadDone( + bool result, + const enterprise_management::PolicyFetchResponse& policy); + + void CancelStore(); + void CancelRetrieve(); + + // Checks whether the disk cache and (if requested) the policy fetch + // (including the DBus roundtrips) has completed and generates ready or + // fetching done notifications if this is the case. + void CheckIfDone(); + + // Installs legacy policy, mangling it to remove any public keys, public key + // versions and signatures. This is done so on the next policy fetch the + // server ships down a new policy to be sent down to session_manager. + void InstallLegacyPolicy( + const enterprise_management::PolicyFetchResponse& policy); + + // Removes the legacy cache dir. + static void RemoveLegacyCacheDir(const FilePath& dir); + + chromeos::SessionManagerClient* session_manager_client_; + CloudPolicyDataStore* data_store_; + + // Whether a policy fetch is pending before readiness is asserted. + bool pending_policy_fetch_; + bool pending_disk_cache_load_; + + // Storage and retrieval operations that are currently in flight. + StorePolicyOperation* store_operation_; + RetrievePolicyOperation* retrieve_operation_; + + // TODO(mnissler): Remove all the legacy policy support members below after + // the number of pre-M20 clients drops back to zero. + FilePath legacy_cache_dir_; + + base::WeakPtrFactory<UserPolicyTokenCache::Delegate> + legacy_token_cache_delegate_factory_; + scoped_refptr<UserPolicyTokenLoader> legacy_token_loader_; + + base::WeakPtrFactory<UserPolicyDiskCache::Delegate> + legacy_policy_cache_delegate_factory_; + scoped_refptr<UserPolicyDiskCache> legacy_policy_cache_; + + DISALLOW_COPY_AND_ASSIGN(CrosUserPolicyCache); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_CROS_USER_POLICY_CACHE_H_ diff --git a/chrome/browser/policy/cros_user_policy_cache_unittest.cc b/chrome/browser/policy/cros_user_policy_cache_unittest.cc new file mode 100644 index 0000000..afff499 --- /dev/null +++ b/chrome/browser/policy/cros_user_policy_cache_unittest.cc @@ -0,0 +1,190 @@ +// 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 "base/bind.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/scoped_temp_dir.h" +#include "base/time.h" +#include "chrome/browser/policy/cloud_policy_data_store.h" +#include "chrome/browser/policy/cros_user_policy_cache.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/mock_session_manager_client.h" +#include "content/test/test_browser_thread.h" +#include "policy/policy_constants.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::InvokeArgument; +using testing::_; + +namespace em = enterprise_management; + +// These should probably be moved to a more central place. +ACTION_TEMPLATE(InvokeCallbackArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(p1)) { + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(::std::tr1::get<k>(args), p1)); +} +ACTION_TEMPLATE(InvokeCallbackArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_2_VALUE_PARAMS(p1, p2)) { + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(::std::tr1::get<k>(args), p1, p2)); +} + +namespace policy { +namespace { + +const char kFakeToken[] = "token"; +const char kFakeSignature[] = "signature"; +const char kFakeMachineName[] = "machine-name"; +const char kFakeUsername[] = "username"; +const int kFakePublicKeyVersion = 17; +const char kFakeDeviceId[] = "device-id"; + +class CrosUserPolicyCacheTest : public testing::Test { + protected: + CrosUserPolicyCacheTest() + : loop_(MessageLoop::TYPE_UI), + data_store_(CloudPolicyDataStore::CreateForUserPolicies()), + ui_thread_(content::BrowserThread::UI, &loop_), + file_thread_(content::BrowserThread::FILE, &loop_) {} + + virtual void SetUp() { + ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir()); + } + + void CreatePolicyResponse(em::PolicyFetchResponse* response) { + em::CloudPolicySettings policy_settings; + policy_settings.mutable_showhomebutton()->set_showhomebutton(true); + + em::PolicyData policy_data; + policy_data.set_policy_type(dm_protocol::kChromeUserPolicyType); + policy_data.set_timestamp((base::Time::NowFromSystemTime() - + base::Time::UnixEpoch()).InMilliseconds()); + policy_data.set_request_token(kFakeToken); + ASSERT_TRUE( + policy_settings.SerializeToString(policy_data.mutable_policy_value())); + policy_data.set_machine_name(kFakeMachineName); + policy_data.set_public_key_version(kFakePublicKeyVersion); + policy_data.set_username(kFakeUsername); + policy_data.set_device_id(kFakeDeviceId); + policy_data.set_state(em::PolicyData::ACTIVE); + + ASSERT_TRUE(policy_data.SerializeToString(response->mutable_policy_data())); + response->set_policy_data_signature(kFakeSignature); + } + + FilePath token_file() { + return tmp_dir_.path().Append(FILE_PATH_LITERAL("token")); + } + + FilePath policy_file() { + return tmp_dir_.path().Append(FILE_PATH_LITERAL("policy")); + } + + MessageLoop loop_; + + chromeos::MockSessionManagerClient session_manager_client_; + scoped_ptr<CloudPolicyDataStore> data_store_; + + private: + content::TestBrowserThread ui_thread_; + content::TestBrowserThread file_thread_; + ScopedTempDir tmp_dir_; + + DISALLOW_COPY_AND_ASSIGN(CrosUserPolicyCacheTest); +}; + +TEST_F(CrosUserPolicyCacheTest, InitAndSetPolicy) { + CrosUserPolicyCache cache(&session_manager_client_, + data_store_.get(), + false, + token_file(), + policy_file()); + EXPECT_FALSE(cache.IsReady()); + EXPECT_FALSE(data_store_->token_cache_loaded()); + + // Initialize the cache. + EXPECT_CALL(session_manager_client_, RetrieveUserPolicy(_)) + .WillOnce(InvokeCallbackArgument<0>(std::string())); + + cache.Load(); + loop_.RunAllPending(); + + EXPECT_TRUE(cache.IsReady()); + EXPECT_TRUE(data_store_->token_cache_loaded()); + EXPECT_EQ(0U, cache.policy()->size()); + + // Set policy. + em::PolicyFetchResponse response; + CreatePolicyResponse(&response); + std::string serialized_response; + ASSERT_TRUE(response.SerializeToString(&serialized_response)); + testing::Sequence seq; + EXPECT_CALL(session_manager_client_, StoreUserPolicy(serialized_response, _)) + .InSequence(seq) + .WillOnce(InvokeCallbackArgument<1>(true)); + EXPECT_CALL(session_manager_client_, RetrieveUserPolicy(_)) + .InSequence(seq) + .WillOnce(InvokeCallbackArgument<0>(serialized_response)); + + EXPECT_TRUE(cache.SetPolicy(response)); + loop_.RunAllPending(); + + EXPECT_EQ(1U, cache.policy()->size()); + const PolicyMap::Entry* entry = cache.policy()->Get(key::kShowHomeButton); + ASSERT_TRUE(entry); + EXPECT_TRUE(base::FundamentalValue(true).Equals(entry->value)); +}; + +TEST_F(CrosUserPolicyCacheTest, Migration) { + std::string data; + + em::DeviceCredentials credentials; + credentials.set_device_token(kFakeToken); + credentials.set_device_id(kFakeDeviceId); + ASSERT_TRUE(credentials.SerializeToString(&data)); + ASSERT_NE(-1, file_util::WriteFile(token_file(), data.c_str(), data.size())); + + em::CachedCloudPolicyResponse policy; + CreatePolicyResponse(policy.mutable_cloud_policy()); + ASSERT_TRUE(policy.SerializeToString(&data)); + ASSERT_NE(-1, file_util::WriteFile(policy_file(), data.c_str(), data.size())); + + CrosUserPolicyCache cache(&session_manager_client_, + data_store_.get(), + false, + token_file(), + policy_file()); + EXPECT_FALSE(cache.IsReady()); + EXPECT_FALSE(data_store_->token_cache_loaded()); + + // Initialize the cache. + EXPECT_CALL(session_manager_client_, RetrieveUserPolicy(_)) + .WillOnce(InvokeCallbackArgument<0>(std::string())); + + cache.Load(); + loop_.RunAllPending(); + + EXPECT_TRUE(cache.IsReady()); + EXPECT_TRUE(data_store_->token_cache_loaded()); + EXPECT_EQ(kFakeDeviceId, data_store_->device_id()); + EXPECT_EQ(kFakeToken, data_store_->device_token()); + EXPECT_EQ(1U, cache.policy()->size()); + const PolicyMap::Entry* entry = cache.policy()->Get(key::kShowHomeButton); + ASSERT_TRUE(entry); + EXPECT_TRUE(base::FundamentalValue(true).Equals(entry->value)); +}; + +} // namespace +} // namespace policy diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index b9e3d3e..1b8f255 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1562,6 +1562,8 @@ 'browser/policy/configuration_policy_reader.h', 'browser/policy/delayed_work_scheduler.cc', 'browser/policy/delayed_work_scheduler.h', + 'browser/policy/cros_user_policy_cache.cc', + 'browser/policy/cros_user_policy_cache.h', 'browser/policy/device_management_service.cc', 'browser/policy/device_management_service.h', 'browser/policy/device_policy_cache.cc', @@ -4105,6 +4107,8 @@ ['exclude', 'browser/policy/app_pack_updater.h'], ['exclude', 'browser/policy/auto_enrollment_client.cc'], ['exclude', 'browser/policy/auto_enrollment_client.h'], + ['exclude', 'browser/policy/cros_user_policy_cache.cc'], + ['exclude', 'browser/policy/cros_user_policy_cache.h'], ['exclude', 'browser/policy/device_policy_cache.cc'], ['exclude', 'browser/policy/device_policy_cache.h'], ['exclude', 'browser/policy/device_status_collector.cc'], diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 4f51726..18b7d7c 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1577,6 +1577,7 @@ 'browser/policy/testing_policy_url_fetcher_factory.h', 'browser/policy/url_blacklist_manager_unittest.cc', 'browser/policy/user_policy_cache_unittest.cc', + 'browser/policy/cros_user_policy_cache_unittest.cc', 'browser/preferences_mock_mac.cc', 'browser/preferences_mock_mac.h', 'browser/prefs/command_line_pref_store_unittest.cc', @@ -2246,6 +2247,7 @@ ['exclude', '^browser/oom_priority_manager_unittest.cc'], ['exclude', '^browser/policy/auto_enrollment_client_unittest.cc' ], ['exclude', '^browser/policy/configuration_policy_handler_chromeos_unittest.cc' ], + ['exclude', '^browser/policy/cros_user_policy_cache_unittest.cc'], ['exclude', '^browser/policy/device_policy_cache_unittest.cc'], ['exclude', '^browser/policy/device_status_collector_unittest.cc'], ['exclude', '^browser/policy/enterprise_install_attributes_unittest.cc' ], diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc index 6d4849e..675a357 100644 --- a/chrome/common/chrome_paths.cc +++ b/chrome/common/chrome_paths.cc @@ -300,6 +300,9 @@ bool PathProvider(int key, FilePath* result) { cur = cur.Append(FILE_PATH_LITERAL("chromeos")); cur = cur.Append(FILE_PATH_LITERAL("libcros.so")); break; + case chrome::DIR_USER_POLICY: + cur = FilePath(FILE_PATH_LITERAL("/home/chronos/root/enterprise")); + break; #endif // The following are only valid in the development environment, and // will fail if executed from an installed executable (because the diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h index 09988fc..738a61f 100644 --- a/chrome/common/chrome_paths.h +++ b/chrome/common/chrome_paths.h @@ -52,6 +52,8 @@ enum { // on Chrome Mac. On Chrome OS, this path is // used for OEM customization. // Getting this path does not create it. + DIR_USER_POLICY, // Root-owned per-user private enterprise + // directory. #endif DIR_EXTERNAL_EXTENSIONS, // Directory where installer places .crx files. |