diff options
author | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-05 12:52:20 +0000 |
---|---|---|
committer | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-05 12:52:20 +0000 |
commit | 15a194d1dd4137f3463df80db34ef8964e85cf9d (patch) | |
tree | 6f99a61c8921a9c3f77d9b6c9f949e1ea74eb090 /chrome/browser/policy | |
parent | 3c2dfca55048041ab27f2575fc533d568df90419 (diff) | |
download | chromium_src-15a194d1dd4137f3463df80db34ef8964e85cf9d.zip chromium_src-15a194d1dd4137f3463df80db34ef8964e85cf9d.tar.gz chromium_src-15a194d1dd4137f3463df80db34ef8964e85cf9d.tar.bz2 |
Enable user policy handling through the new cloud policy stack.
Implement UserCloudPolicyManager, which wraps an instance of the new
cloud policy stack and implements the ConfigurationPolicyProvider
interface. Hook it up in BrowserPolicyConnector to be used for user
policy instead of the old code if enabled through a command line switch.
BUG=chromium:108929
TEST=With --enable-cloud-policy-service, user policy on Chrome OS still works as before.
Review URL: https://chromiumcodereview.appspot.com/10449071
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@140519 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/policy')
19 files changed, 1100 insertions, 138 deletions
diff --git a/chrome/browser/policy/browser_policy_connector.cc b/chrome/browser/policy/browser_policy_connector.cc index 4c1967b..bc2258d 100644 --- a/chrome/browser/policy/browser_policy_connector.cc +++ b/chrome/browser/policy/browser_policy_connector.cc @@ -10,10 +10,14 @@ #include "base/file_path.h" #include "base/path_service.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/policy/cloud_policy_client.h" #include "chrome/browser/policy/cloud_policy_provider.h" +#include "chrome/browser/policy/cloud_policy_service.h" #include "chrome/browser/policy/cloud_policy_subsystem.h" #include "chrome/browser/policy/configuration_policy_provider.h" +#include "chrome/browser/policy/device_management_service.h" #include "chrome/browser/policy/policy_service_impl.h" +#include "chrome/browser/policy/user_cloud_policy_manager.h" #include "chrome/browser/policy/user_policy_cache.h" #include "chrome/browser/policy/user_policy_token_cache.h" #include "chrome/browser/signin/token_service.h" @@ -106,13 +110,11 @@ BrowserPolicyConnector::~BrowserPolicyConnector() { user_policy_token_cache_.reset(); user_data_store_.reset(); - // Delete the providers while the |handler_list_| is still valid, since - // OnProviderGoingAway() can trigger refreshes at the - // ConfigurationPolicyPrefStore. - recommended_cloud_provider_.reset(); - managed_cloud_provider_.reset(); - recommended_platform_provider_.reset(); - managed_platform_provider_.reset(); + if (user_cloud_policy_manager_.get()) + user_cloud_policy_manager_->Shutdown(); + user_cloud_policy_manager_.reset(); + + device_management_service_.reset(); } void BrowserPolicyConnector::Init() { @@ -128,14 +130,19 @@ void BrowserPolicyConnector::Init() { // but for now it just isn't created. CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kDeviceManagementUrl)) { - managed_cloud_provider_.reset(new CloudPolicyProvider( - this, - GetChromePolicyDefinitionList(), - POLICY_LEVEL_MANDATORY)); - recommended_cloud_provider_.reset(new CloudPolicyProvider( - this, - GetChromePolicyDefinitionList(), - POLICY_LEVEL_RECOMMENDED)); + device_management_service_.reset( + new DeviceManagementService( + command_line->GetSwitchValueASCII(switches::kDeviceManagementUrl))); + if (!command_line->HasSwitch(switches::kEnableCloudPolicyService)) { + managed_cloud_provider_.reset(new CloudPolicyProvider( + this, + GetChromePolicyDefinitionList(), + POLICY_LEVEL_MANDATORY)); + recommended_cloud_provider_.reset(new CloudPolicyProvider( + this, + GetChromePolicyDefinitionList(), + POLICY_LEVEL_RECOMMENDED)); + } } InitializeDevicePolicy(); @@ -151,7 +158,7 @@ void BrowserPolicyConnector::Init() { } PolicyService* BrowserPolicyConnector::CreatePolicyService( - Profile* profile) const { + Profile* profile) { // |providers| in decreasing order of priority. PolicyServiceImpl::Providers providers; if (managed_platform_provider_.get()) @@ -162,6 +169,18 @@ PolicyService* BrowserPolicyConnector::CreatePolicyService( providers.push_back(recommended_platform_provider_.get()); if (recommended_cloud_provider_.get()) providers.push_back(recommended_cloud_provider_.get()); + + // The global policy service uses the proxy provider to allow for swapping in + // user policy after startup, while profiles use |user_cloud_policy_manager_| + // directly as their provider, which may also block initialization on a policy + // fetch at login time. + if (profile) { + if (user_cloud_policy_manager_.get()) + providers.push_back(user_cloud_policy_manager_.get()); + } else { + providers.push_back(&user_cloud_policy_provider_); + } + return new PolicyServiceImpl(providers); } @@ -260,6 +279,8 @@ void BrowserPolicyConnector::FetchCloudPolicy() { void BrowserPolicyConnector::ScheduleServiceInitialization( int64 delay_milliseconds) { + if (device_management_service_.get()) + device_management_service_->ScheduleInitialization(delay_milliseconds); if (user_cloud_policy_subsystem_.get()) { user_cloud_policy_subsystem_-> ScheduleServiceInitialization(delay_milliseconds); @@ -275,6 +296,8 @@ void BrowserPolicyConnector::InitializeUserPolicy( const std::string& user_name, bool wait_for_policy_fetch) { // Throw away the old backend. + user_cloud_policy_manager_.reset(); + user_cloud_policy_subsystem_.reset(); user_policy_token_cache_.reset(); user_data_store_.reset(); @@ -284,51 +307,65 @@ void BrowserPolicyConnector::InitializeUserPolicy( CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kDeviceManagementUrl)) { - user_data_store_.reset(CloudPolicyDataStore::CreateForUserPolicies()); + int64 startup_delay = + wait_for_policy_fetch ? 0 : kServiceInitializationStartupDelay; - FilePath profile_dir; - PathService::Get(chrome::DIR_USER_DATA, &profile_dir); + if (command_line->HasSwitch(switches::kEnableCloudPolicyService)) { #if defined(OS_CHROMEOS) - profile_dir = profile_dir.Append( - command_line->GetSwitchValuePath(switches::kLoginProfile)); + user_cloud_policy_manager_ = + UserCloudPolicyManager::Create(wait_for_policy_fetch); + user_cloud_policy_manager_->Initialize(g_browser_process->local_state(), + device_management_service_.get(), + GetUserAffiliation(user_name)); + user_cloud_policy_provider_.SetDelegate(user_cloud_policy_manager_.get()); + + device_management_service_->ScheduleInitialization(startup_delay); #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; + } else { + FilePath profile_dir; + PathService::Get(chrome::DIR_USER_DATA, &profile_dir); +#if defined(OS_CHROMEOS) + 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; + 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); + 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(), token_cache_file)); + user_policy_cache = new UserPolicyCache(policy_cache_file, + wait_for_policy_fetch); + user_policy_token_cache_.reset( + new UserPolicyTokenCache(user_data_store_.get(), token_cache_file)); - // Initiate the DM-Token load. - user_policy_token_cache_->Load(); + // 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); - user_cloud_policy_subsystem_.reset(new CloudPolicySubsystem( - user_data_store_.get(), - user_policy_cache)); + user_cloud_policy_subsystem_.reset(new CloudPolicySubsystem( + user_data_store_.get(), + user_policy_cache)); - user_data_store_->set_user_name(user_name); - user_data_store_->set_user_affiliation(GetUserAffiliation(user_name)); + user_data_store_->set_user_name(user_name); + user_data_store_->set_user_affiliation(GetUserAffiliation(user_name)); - int64 delay = - wait_for_policy_fetch ? 0 : kServiceInitializationStartupDelay; - user_cloud_policy_subsystem_->CompleteInitialization( - prefs::kUserPolicyRefreshRate, - delay); + user_cloud_policy_subsystem_->CompleteInitialization( + prefs::kUserPolicyRefreshRate, + startup_delay); + + managed_cloud_provider_->SetUserPolicyCache(user_policy_cache); + recommended_cloud_provider_->SetUserPolicyCache(user_policy_cache); + } } } @@ -360,6 +397,17 @@ void BrowserPolicyConnector::RegisterForUserPolicy( if (user_data_store_.get()) user_data_store_->SetOAuthToken(oauth_token); } + if (user_cloud_policy_manager_.get()) { + CloudPolicyService* service = + user_cloud_policy_manager_->cloud_policy_service(); + if (service->client() && + !service->client()->is_registered() && + !oauth_token.empty()) { + service->client()->Register(oauth_token); + } else { + user_cloud_policy_manager_->CancelWaitForPolicyFetch(); + } + } } CloudPolicyDataStore* BrowserPolicyConnector::GetDeviceCloudPolicyDataStore() { @@ -439,26 +487,31 @@ void BrowserPolicyConnector::InitializeDevicePolicy() { CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kEnableDevicePolicy)) { - device_data_store_.reset(CloudPolicyDataStore::CreateForDevicePolicies()); - chromeos::CryptohomeLibrary* cryptohome = - chromeos::CrosLibrary::Get()->GetCryptohomeLibrary(); - install_attributes_.reset(new EnterpriseInstallAttributes(cryptohome)); - DevicePolicyCache* device_policy_cache = - new DevicePolicyCache(device_data_store_.get(), - install_attributes_.get()); - - managed_cloud_provider_->SetDevicePolicyCache(device_policy_cache); - recommended_cloud_provider_->SetDevicePolicyCache(device_policy_cache); - - device_cloud_policy_subsystem_.reset(new CloudPolicySubsystem( - device_data_store_.get(), - device_policy_cache)); - - // Initialize the subsystem once the message loops are spinning. - MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&BrowserPolicyConnector::CompleteInitialization, - weak_ptr_factory_.GetWeakPtr())); + if (command_line->HasSwitch(switches::kEnableCloudPolicyService)) { + // TODO(mnissler): Initialize new-style device policy here once it's + // implemented. + } else { + device_data_store_.reset(CloudPolicyDataStore::CreateForDevicePolicies()); + chromeos::CryptohomeLibrary* cryptohome = + chromeos::CrosLibrary::Get()->GetCryptohomeLibrary(); + install_attributes_.reset(new EnterpriseInstallAttributes(cryptohome)); + DevicePolicyCache* device_policy_cache = + new DevicePolicyCache(device_data_store_.get(), + install_attributes_.get()); + + managed_cloud_provider_->SetDevicePolicyCache(device_policy_cache); + recommended_cloud_provider_->SetDevicePolicyCache(device_policy_cache); + + device_cloud_policy_subsystem_.reset(new CloudPolicySubsystem( + device_data_store_.get(), + device_policy_cache)); + + // Initialize the subsystem once the message loops are spinning. + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&BrowserPolicyConnector::CompleteInitialization, + weak_ptr_factory_.GetWeakPtr())); + } } #endif } diff --git a/chrome/browser/policy/browser_policy_connector.h b/chrome/browser/policy/browser_policy_connector.h index 2c52472..bb71c15 100644 --- a/chrome/browser/policy/browser_policy_connector.h +++ b/chrome/browser/policy/browser_policy_connector.h @@ -14,6 +14,7 @@ #include "chrome/browser/policy/cloud_policy_constants.h" #include "chrome/browser/policy/configuration_policy_handler_list.h" #include "chrome/browser/policy/enterprise_install_attributes.h" +#include "chrome/browser/policy/proxy_policy_provider.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" @@ -27,7 +28,9 @@ class CloudPolicyDataStore; class CloudPolicyProvider; class CloudPolicySubsystem; class ConfigurationPolicyProvider; +class DeviceManagementService; class PolicyService; +class UserCloudPolicyManager; class UserPolicyTokenCache; // Manages the lifecycle of browser-global policy infrastructure, such as the @@ -48,7 +51,7 @@ class BrowserPolicyConnector : public content::NotificationObserver { // Creates a new policy service for the given profile, or a global one if // it is NULL. Ownership is transferred to the caller. - PolicyService* CreatePolicyService(Profile* profile) const; + PolicyService* CreatePolicyService(Profile* profile); // Returns a weak pointer to the CloudPolicySubsystem corresponding to the // device policy managed by this policy connector, or NULL if no such @@ -153,6 +156,12 @@ class BrowserPolicyConnector : public content::NotificationObserver { static ConfigurationPolicyProvider* CreateManagedPlatformProvider(); static ConfigurationPolicyProvider* CreateRecommendedPlatformProvider(); + // Used to convert policies to preferences. The providers declared below + // trigger policy updates during destruction via OnProviderGoingAway(), which + // will result in |handler_list_| being consulted for policy translation. + // Therefore, it's important to destroy |handler_list_| after the providers. + ConfigurationPolicyHandlerList handler_list_; + scoped_ptr<ConfigurationPolicyProvider> managed_platform_provider_; scoped_ptr<ConfigurationPolicyProvider> recommended_platform_provider_; @@ -169,6 +178,14 @@ class BrowserPolicyConnector : public content::NotificationObserver { scoped_ptr<CloudPolicyDataStore> user_data_store_; scoped_ptr<CloudPolicySubsystem> user_cloud_policy_subsystem_; + // Components of the new-style cloud policy implementation. + // TODO(mnissler): Remove the old-style components above once we have + // completed the switch to the new cloud policy implementation. + scoped_ptr<DeviceManagementService> device_management_service_; + + ProxyPolicyProvider user_cloud_policy_provider_; + scoped_ptr<UserCloudPolicyManager> user_cloud_policy_manager_; + // Used to initialize the device policy subsystem once the message loops // are spinning. base::WeakPtrFactory<BrowserPolicyConnector> weak_ptr_factory_; @@ -180,9 +197,6 @@ class BrowserPolicyConnector : public content::NotificationObserver { // policy authentication tokens. TokenService* token_service_; - // Used to convert policies to preferences. - ConfigurationPolicyHandlerList handler_list_; - #if defined(OS_CHROMEOS) scoped_ptr<AppPackUpdater> app_pack_updater_; #endif diff --git a/chrome/browser/policy/cloud_policy_service.cc b/chrome/browser/policy/cloud_policy_service.cc index d97ff25..8784e88 100644 --- a/chrome/browser/policy/cloud_policy_service.cc +++ b/chrome/browser/policy/cloud_policy_service.cc @@ -12,12 +12,16 @@ namespace em = enterprise_management; namespace policy { CloudPolicyService::CloudPolicyService(scoped_ptr<CloudPolicyClient> client, - scoped_ptr<CloudPolicyStore> store) + CloudPolicyStore* store) : client_(client.Pass()), - store_(store.Pass()), + store_(store), refresh_state_(REFRESH_NONE) { client_->AddObserver(this); store_->AddObserver(this); + + // Make sure we initialize |client_| from the policy data that might be + // already present in |store_|. + OnStoreLoaded(store_); } CloudPolicyService::~CloudPolicyService() { diff --git a/chrome/browser/policy/cloud_policy_service.h b/chrome/browser/policy/cloud_policy_service.h index fe5cc34..6288e794 100644 --- a/chrome/browser/policy/cloud_policy_service.h +++ b/chrome/browser/policy/cloud_policy_service.h @@ -26,7 +26,7 @@ class CloudPolicyService : public CloudPolicyClient::Observer, public CloudPolicyStore::Observer { public: CloudPolicyService(scoped_ptr<CloudPolicyClient> client, - scoped_ptr<CloudPolicyStore> store); + CloudPolicyStore* store); virtual ~CloudPolicyService(); // Returns the domain that manages this user/device, according to the current @@ -38,7 +38,7 @@ class CloudPolicyService : public CloudPolicyClient::Observer, void RefreshPolicy(const base::Closure& callback); CloudPolicyClient* client() { return client_.get(); } - CloudPolicyStore* store() { return store_.get(); } + CloudPolicyStore* store() { return store_; } // CloudPolicyClient::Observer: virtual void OnPolicyFetched(CloudPolicyClient* client) OVERRIDE; @@ -57,7 +57,7 @@ class CloudPolicyService : public CloudPolicyClient::Observer, scoped_ptr<CloudPolicyClient> client_; // Takes care of persisting and decoding cloud policy. - scoped_ptr<CloudPolicyStore> store_; + CloudPolicyStore* store_; // Tracks the state of a pending refresh operation, if any. enum { diff --git a/chrome/browser/policy/cloud_policy_service_unittest.cc b/chrome/browser/policy/cloud_policy_service_unittest.cc index 14e53df..e4f8965 100644 --- a/chrome/browser/policy/cloud_policy_service_unittest.cc +++ b/chrome/browser/policy/cloud_policy_service_unittest.cc @@ -22,15 +22,13 @@ class CloudPolicyServiceTest : public testing::Test { public: CloudPolicyServiceTest() : client_(new MockCloudPolicyClient), - store_(new MockCloudPolicyStore), - service_(scoped_ptr<CloudPolicyClient>(client_), - scoped_ptr<CloudPolicyStore>(store_)) {} + service_(scoped_ptr<CloudPolicyClient>(client_), &store_) {} MOCK_METHOD0(OnPolicyRefresh, void(void)); protected: MockCloudPolicyClient* client_; - MockCloudPolicyStore* store_; + MockCloudPolicyStore store_; CloudPolicyService service_; }; @@ -43,8 +41,8 @@ TEST_F(CloudPolicyServiceTest, ManagedByEmptyPolicy) { } TEST_F(CloudPolicyServiceTest, ManagedByValidPolicy) { - store_->policy_.reset(new em::PolicyData()); - store_->policy_->set_username("user@example.com"); + store_.policy_.reset(new em::PolicyData()); + store_.policy_->set_username("user@example.com"); EXPECT_EQ("example.com", service_.ManagedBy()); } @@ -52,21 +50,21 @@ TEST_F(CloudPolicyServiceTest, PolicyUpdateSuccess) { em::PolicyFetchResponse policy; policy.set_policy_data("fake policy"); client_->SetPolicy(policy); - EXPECT_CALL(*store_, Store(ProtoMatches(policy))).Times(1); + EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1); client_->NotifyPolicyFetched(); // After |store_| initializes, credentials and other meta data should be // transferred to |client_|. - store_->policy_.reset(new em::PolicyData()); - store_->policy_->set_request_token("fake token"); - store_->policy_->set_device_id("fake client id"); - store_->policy_->set_timestamp(32); - store_->policy_->set_valid_serial_number_missing(true); - store_->policy_->set_public_key_version(17); + store_.policy_.reset(new em::PolicyData()); + store_.policy_->set_request_token("fake token"); + store_.policy_->set_device_id("fake client id"); + store_.policy_->set_timestamp(32); + store_.policy_->set_valid_serial_number_missing(true); + store_.policy_->set_public_key_version(17); EXPECT_CALL(*client_, - SetupRegistration(store_->policy_->request_token(), - store_->policy_->device_id())).Times(1); - store_->NotifyStoreLoaded(); + SetupRegistration(store_.policy_->request_token(), + store_.policy_->device_id())).Times(1); + store_.NotifyStoreLoaded(); EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(32), client_->last_policy_timestamp_); EXPECT_TRUE(client_->submit_machine_id_); @@ -76,7 +74,7 @@ TEST_F(CloudPolicyServiceTest, PolicyUpdateSuccess) { TEST_F(CloudPolicyServiceTest, PolicyUpdateClientFailure) { client_->SetStatus(DM_STATUS_REQUEST_FAILED); - EXPECT_CALL(*store_, Store(_)).Times(0); + EXPECT_CALL(store_, Store(_)).Times(0); client_->NotifyPolicyFetched(); } @@ -95,15 +93,15 @@ TEST_F(CloudPolicyServiceTest, RefreshPolicySuccess) { em::PolicyFetchResponse policy; policy.set_policy_data("fake policy"); client_->SetPolicy(policy); - EXPECT_CALL(*store_, Store(ProtoMatches(policy))).Times(1); + EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1); client_->NotifyPolicyFetched(); // Store reloads policy, callback gets triggered. - store_->policy_.reset(new em::PolicyData()); - store_->policy_->set_request_token("token"); - store_->policy_->set_device_id("device-id"); + store_.policy_.reset(new em::PolicyData()); + store_.policy_->set_request_token("token"); + store_.policy_->set_device_id("device-id"); EXPECT_CALL(*this, OnPolicyRefresh()).Times(1); - store_->NotifyStoreLoaded(); + store_.NotifyStoreLoaded(); } TEST_F(CloudPolicyServiceTest, RefreshPolicyNotRegistered) { @@ -148,12 +146,12 @@ TEST_F(CloudPolicyServiceTest, RefreshPolicyStoreError) { em::PolicyFetchResponse policy; policy.set_policy_data("fake policy"); client_->SetPolicy(policy); - EXPECT_CALL(*store_, Store(ProtoMatches(policy))).Times(1); + EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1); client_->NotifyPolicyFetched(); // Store fails, which should trigger the callback. EXPECT_CALL(*this, OnPolicyRefresh()).Times(1); - store_->NotifyStoreError(); + store_.NotifyStoreError(); } TEST_F(CloudPolicyServiceTest, RefreshPolicyConcurrent) { @@ -176,7 +174,7 @@ TEST_F(CloudPolicyServiceTest, RefreshPolicyConcurrent) { em::PolicyFetchResponse policy; policy.set_policy_data("fake policy"); client_->SetPolicy(policy); - EXPECT_CALL(*store_, Store(ProtoMatches(policy))).Times(1); + EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1); client_->NotifyPolicyFetched(); // Trigger another policy fetch. @@ -186,15 +184,15 @@ TEST_F(CloudPolicyServiceTest, RefreshPolicyConcurrent) { // The store finishing the first load should not generate callbacks. EXPECT_CALL(*this, OnPolicyRefresh()).Times(0); - store_->NotifyStoreLoaded(); + store_.NotifyStoreLoaded(); // Second policy fetch finishes. - EXPECT_CALL(*store_, Store(ProtoMatches(policy))).Times(1); + EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1); client_->NotifyPolicyFetched(); // Corresponding store operation finishes, all _three_ callbacks fire. EXPECT_CALL(*this, OnPolicyRefresh()).Times(3); - store_->NotifyStoreLoaded(); + store_.NotifyStoreLoaded(); } } // namespace policy diff --git a/chrome/browser/policy/config_dir_policy_provider_unittest.cc b/chrome/browser/policy/config_dir_policy_provider_unittest.cc index 087878b..84ce467 100644 --- a/chrome/browser/policy/config_dir_policy_provider_unittest.cc +++ b/chrome/browser/policy/config_dir_policy_provider_unittest.cc @@ -25,7 +25,7 @@ class TestHarness : public PolicyProviderTestHarness { virtual void SetUp() OVERRIDE; - virtual AsynchronousPolicyProvider* CreateProvider( + virtual ConfigurationPolicyProvider* CreateProvider( const PolicyDefinitionList* policy_definition_list) OVERRIDE; virtual void InstallEmptyPolicy() OVERRIDE; @@ -65,7 +65,7 @@ void TestHarness::SetUp() { ASSERT_TRUE(test_dir_.CreateUniqueTempDir()); } -AsynchronousPolicyProvider* TestHarness::CreateProvider( +ConfigurationPolicyProvider* TestHarness::CreateProvider( const PolicyDefinitionList* policy_definition_list) { return new ConfigDirPolicyProvider(policy_definition_list, POLICY_LEVEL_MANDATORY, diff --git a/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc b/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc index 0ff0bb3..9414f22 100644 --- a/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc +++ b/chrome/browser/policy/configuration_policy_provider_mac_unittest.cc @@ -123,7 +123,7 @@ class TestHarness : public PolicyProviderTestHarness { virtual void SetUp() OVERRIDE; - virtual AsynchronousPolicyProvider* CreateProvider( + virtual ConfigurationPolicyProvider* CreateProvider( const PolicyDefinitionList* policy_definition_list) OVERRIDE; virtual void InstallEmptyPolicy() OVERRIDE; @@ -155,7 +155,7 @@ TestHarness::~TestHarness() {} void TestHarness::SetUp() {} -AsynchronousPolicyProvider* TestHarness::CreateProvider( +ConfigurationPolicyProvider* TestHarness::CreateProvider( const PolicyDefinitionList* policy_definition_list) { prefs_ = new MockPreferences(); return new ConfigurationPolicyProviderMac(policy_definition_list, prefs_); diff --git a/chrome/browser/policy/configuration_policy_provider_test.cc b/chrome/browser/policy/configuration_policy_provider_test.cc index 3f1084f..64a83c0 100644 --- a/chrome/browser/policy/configuration_policy_provider_test.cc +++ b/chrome/browser/policy/configuration_policy_provider_test.cc @@ -7,8 +7,6 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" -#include "chrome/browser/policy/asynchronous_policy_loader.h" -#include "chrome/browser/policy/asynchronous_policy_provider.h" #include "chrome/browser/policy/configuration_policy_provider.h" #include "chrome/browser/policy/mock_configuration_policy_provider.h" #include "chrome/browser/policy/policy_bundle.h" diff --git a/chrome/browser/policy/configuration_policy_provider_test.h b/chrome/browser/policy/configuration_policy_provider_test.h index 006ef4d..126ca5e 100644 --- a/chrome/browser/policy/configuration_policy_provider_test.h +++ b/chrome/browser/policy/configuration_policy_provider_test.h @@ -24,7 +24,7 @@ class Value; namespace policy { -class AsynchronousPolicyProvider; +class ConfigurationPolicyProvider; struct PolicyDefinitionList; // A stripped-down policy definition list that contains entries for the @@ -57,7 +57,7 @@ class PolicyProviderTestHarness { virtual void SetUp() = 0; // Create a new policy provider. - virtual AsynchronousPolicyProvider* CreateProvider( + virtual ConfigurationPolicyProvider* CreateProvider( const PolicyDefinitionList* policy_definition_list) = 0; // Returns the policy level and scope set by the policy provider. @@ -108,7 +108,7 @@ class ConfigurationPolicyProviderTest base::Closure install_value); scoped_ptr<PolicyProviderTestHarness> test_harness_; - scoped_ptr<AsynchronousPolicyProvider> provider_; + scoped_ptr<ConfigurationPolicyProvider> provider_; private: DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProviderTest); diff --git a/chrome/browser/policy/configuration_policy_provider_win_unittest.cc b/chrome/browser/policy/configuration_policy_provider_win_unittest.cc index e2caa33..d8d9142 100644 --- a/chrome/browser/policy/configuration_policy_provider_win_unittest.cc +++ b/chrome/browser/policy/configuration_policy_provider_win_unittest.cc @@ -64,7 +64,7 @@ class TestHarness : public PolicyProviderTestHarness { virtual void SetUp() OVERRIDE; - virtual AsynchronousPolicyProvider* CreateProvider( + virtual ConfigurationPolicyProvider* CreateProvider( const PolicyDefinitionList* policy_definition_list) OVERRIDE; virtual void InstallEmptyPolicy() OVERRIDE; @@ -139,7 +139,7 @@ TestHarness::~TestHarness() {} void TestHarness::SetUp() {} -AsynchronousPolicyProvider* TestHarness::CreateProvider( +ConfigurationPolicyProvider* TestHarness::CreateProvider( const PolicyDefinitionList* policy_definition_list) { return new ConfigurationPolicyProviderWin(policy_definition_list); } diff --git a/chrome/browser/policy/mock_device_management_service.cc b/chrome/browser/policy/mock_device_management_service.cc index 106e454..28fe0f3 100644 --- a/chrome/browser/policy/mock_device_management_service.cc +++ b/chrome/browser/policy/mock_device_management_service.cc @@ -11,19 +11,16 @@ using testing::Action; namespace em = enterprise_management; namespace policy { +namespace { -class MockDeviceManagementRequestJob : public DeviceManagementRequestJob { +// Common mock request job functionality. +class MockRequestJobBase : public DeviceManagementRequestJob { public: - MockDeviceManagementRequestJob( - JobType type, - MockDeviceManagementService* service, - DeviceManagementStatus status, - const enterprise_management::DeviceManagementResponse& response) + MockRequestJobBase(JobType type, + MockDeviceManagementService* service) : DeviceManagementRequestJob(type), - service_(service), - status_(status), - response_(response) {} - virtual ~MockDeviceManagementRequestJob() {} + service_(service) {} + virtual ~MockRequestJobBase() {} protected: virtual void Run() OVERRIDE { @@ -34,7 +31,6 @@ class MockDeviceManagementRequestJob : public DeviceManagementRequestJob { ExtractParameter(dm_protocol::kParamUserAffiliation), ExtractParameter(dm_protocol::kParamDeviceID), request_); - callback_.Run(status_, response_); } private: @@ -51,16 +47,68 @@ class MockDeviceManagementRequestJob : public DeviceManagementRequestJob { } MockDeviceManagementService* service_; + + DISALLOW_COPY_AND_ASSIGN(MockRequestJobBase); +}; + +// Synchronous mock request job that immediately completes on calling Run(). +class SyncRequestJob : public MockRequestJobBase { + public: + SyncRequestJob(JobType type, + MockDeviceManagementService* service, + DeviceManagementStatus status, + const em::DeviceManagementResponse& response) + : MockRequestJobBase(type, service), + status_(status), + response_(response) {} + virtual ~SyncRequestJob() {} + + protected: + virtual void Run() OVERRIDE { + MockRequestJobBase::Run(); + callback_.Run(status_, response_); + } + + private: DeviceManagementStatus status_; - enterprise_management::DeviceManagementResponse response_; + em::DeviceManagementResponse response_; - DISALLOW_COPY_AND_ASSIGN(MockDeviceManagementRequestJob); + DISALLOW_COPY_AND_ASSIGN(SyncRequestJob); }; -ACTION_P3(CreateMockDeviceManagementRequestJob, service, status, response) { - return new MockDeviceManagementRequestJob(arg0, service, status, response); +// Asynchronous job that allows the test to delay job completion. +class AsyncRequestJob : public MockRequestJobBase, + public MockDeviceManagementJob { + public: + AsyncRequestJob(JobType type, MockDeviceManagementService* service) + : MockRequestJobBase(type, service) {} + virtual ~AsyncRequestJob() {} + + protected: + virtual void SendResponse( + DeviceManagementStatus status, + const em::DeviceManagementResponse& response) OVERRIDE { + callback_.Run(status, response); + } + + private: + DISALLOW_COPY_AND_ASSIGN(AsyncRequestJob); +}; + +} // namespace + +ACTION_P3(CreateSyncMockDeviceManagementJob, service, status, response) { + return new SyncRequestJob(arg0, service, status, response); +} + +ACTION_P2(CreateAsyncMockDeviceManagementJob, service, mock_job) { + AsyncRequestJob* job = new AsyncRequestJob(arg0, service); + *mock_job = job; + return job; } +MockDeviceManagementJob::~MockDeviceManagementJob() {} + MockDeviceManagementService::MockDeviceManagementService() : DeviceManagementService("") {} @@ -68,15 +116,19 @@ MockDeviceManagementService::~MockDeviceManagementService() {} Action<MockDeviceManagementService::CreateJobFunction> MockDeviceManagementService::SucceedJob( - const enterprise_management::DeviceManagementResponse& response) { - return CreateMockDeviceManagementRequestJob(this, DM_STATUS_SUCCESS, - response); + const em::DeviceManagementResponse& response) { + return CreateSyncMockDeviceManagementJob(this, DM_STATUS_SUCCESS, response); } Action<MockDeviceManagementService::CreateJobFunction> MockDeviceManagementService::FailJob(DeviceManagementStatus status) { - const enterprise_management::DeviceManagementResponse dummy_response; - return CreateMockDeviceManagementRequestJob(this, status, dummy_response); + const em::DeviceManagementResponse dummy_response; + return CreateSyncMockDeviceManagementJob(this, status, dummy_response); +} + +Action<MockDeviceManagementService::CreateJobFunction> + MockDeviceManagementService::CreateAsyncJob(MockDeviceManagementJob** job) { + return CreateAsyncMockDeviceManagementJob(this, job); } } // namespace policy diff --git a/chrome/browser/policy/mock_device_management_service.h b/chrome/browser/policy/mock_device_management_service.h index 51593bf..c8c5828 100644 --- a/chrome/browser/policy/mock_device_management_service.h +++ b/chrome/browser/policy/mock_device_management_service.h @@ -10,10 +10,19 @@ #include "base/basictypes.h" #include "chrome/browser/policy/device_management_service.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" #include "testing/gmock/include/gmock/gmock.h" namespace policy { +class MockDeviceManagementJob { + public: + virtual ~MockDeviceManagementJob(); + virtual void SendResponse( + DeviceManagementStatus status, + const enterprise_management::DeviceManagementResponse& response) = 0; +}; + class MockDeviceManagementService : public DeviceManagementService { public: MockDeviceManagementService(); @@ -36,9 +45,15 @@ class MockDeviceManagementService : public DeviceManagementService { // Creates a gmock action that will make the job succeed. testing::Action<CreateJobFunction> SucceedJob( const enterprise_management::DeviceManagementResponse& response); + // Creates a gmock action which will fail the job with the given error. testing::Action<CreateJobFunction> FailJob(DeviceManagementStatus status); + // Creates a gmock action which will capture the job so the test code can + // delay job completion. + testing::Action<CreateJobFunction> CreateAsyncJob( + MockDeviceManagementJob** job); + private: DISALLOW_COPY_AND_ASSIGN(MockDeviceManagementService); }; diff --git a/chrome/browser/policy/proxy_policy_provider.cc b/chrome/browser/policy/proxy_policy_provider.cc new file mode 100644 index 0000000..a019b08 --- /dev/null +++ b/chrome/browser/policy/proxy_policy_provider.cc @@ -0,0 +1,48 @@ +// 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/proxy_policy_provider.h" + +#include "policy/policy_constants.h" + +namespace policy { + +ProxyPolicyProvider::ProxyPolicyProvider() + : ConfigurationPolicyProvider(GetChromePolicyDefinitionList()) {} + +ProxyPolicyProvider::~ProxyPolicyProvider() {} + +void ProxyPolicyProvider::SetDelegate(ConfigurationPolicyProvider* delegate) { + if (delegate) { + registrar_.reset(new ConfigurationPolicyObserverRegistrar()); + registrar_->Init(delegate, this); + OnUpdatePolicy(delegate); + } else { + registrar_.reset(); + UpdatePolicy(scoped_ptr<PolicyBundle>(new PolicyBundle())); + } +} + +void ProxyPolicyProvider::RefreshPolicies() { + if (registrar_.get()) + registrar_->provider()->RefreshPolicies(); + else + UpdatePolicy(scoped_ptr<PolicyBundle>(new PolicyBundle())); +} + +void ProxyPolicyProvider::OnUpdatePolicy( + ConfigurationPolicyProvider* provider) { + DCHECK_EQ(registrar_->provider(), provider); + scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); + bundle->CopyFrom(registrar_->provider()->policies()); + UpdatePolicy(bundle.Pass()); +} + +void ProxyPolicyProvider::OnProviderGoingAway( + ConfigurationPolicyProvider* provider) { + registrar_.reset(); + UpdatePolicy(scoped_ptr<PolicyBundle>(new PolicyBundle())); +} + +} // namespace policy diff --git a/chrome/browser/policy/proxy_policy_provider.h b/chrome/browser/policy/proxy_policy_provider.h new file mode 100644 index 0000000..f3794f2 --- /dev/null +++ b/chrome/browser/policy/proxy_policy_provider.h @@ -0,0 +1,42 @@ +// 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_PROXY_POLICY_PROVIDER_H_ +#define CHROME_BROWSER_POLICY_PROXY_POLICY_PROVIDER_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/policy/configuration_policy_provider.h" + +namespace policy { + +// A policy provider implementation that acts as a proxy for another policy +// provider, swappable at any point. +class ProxyPolicyProvider : public ConfigurationPolicyProvider, + public ConfigurationPolicyProvider::Observer { + public: + ProxyPolicyProvider(); + virtual ~ProxyPolicyProvider(); + + // Updates the provider this proxy delegates to. + void SetDelegate(ConfigurationPolicyProvider* delegate); + + // ConfigurationPolicyProvider: + virtual void RefreshPolicies() OVERRIDE; + + // ConfigurationPolicyProvider::Observer: + virtual void OnUpdatePolicy(ConfigurationPolicyProvider* provider) OVERRIDE; + virtual void OnProviderGoingAway( + ConfigurationPolicyProvider* provider) OVERRIDE; + + private: + scoped_ptr<ConfigurationPolicyObserverRegistrar> registrar_; + + DISALLOW_COPY_AND_ASSIGN(ProxyPolicyProvider); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_PROXY_POLICY_PROVIDER_H_ diff --git a/chrome/browser/policy/proxy_policy_provider_unittest.cc b/chrome/browser/policy/proxy_policy_provider_unittest.cc new file mode 100644 index 0000000..7e226ef --- /dev/null +++ b/chrome/browser/policy/proxy_policy_provider_unittest.cc @@ -0,0 +1,85 @@ +// 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/basictypes.h" +#include "chrome/browser/policy/mock_configuration_policy_provider.h" +#include "chrome/browser/policy/proxy_policy_provider.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Mock; + +namespace policy { + +class ProxyPolicyProviderTest : public testing::Test { + protected: + ProxyPolicyProviderTest() { + registrar_.Init(&proxy_provider_, &observer_); + } + + MockConfigurationPolicyObserver observer_; + MockConfigurationPolicyProvider mock_provider_; + ProxyPolicyProvider proxy_provider_; + ConfigurationPolicyObserverRegistrar registrar_; + + static scoped_ptr<PolicyBundle> CopyBundle(const PolicyBundle& bundle) { + scoped_ptr<PolicyBundle> copy(new PolicyBundle()); + copy->CopyFrom(bundle); + return copy.Pass(); + } + + DISALLOW_COPY_AND_ASSIGN(ProxyPolicyProviderTest); +}; + +TEST_F(ProxyPolicyProviderTest, Init) { + EXPECT_TRUE(proxy_provider_.IsInitializationComplete()); + EXPECT_TRUE(PolicyBundle().Equals(proxy_provider_.policies())); +} + +TEST_F(ProxyPolicyProviderTest, Delegate) { + PolicyBundle bundle; + bundle.Get(POLICY_DOMAIN_CHROME, std::string()) + .Set("policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + Value::CreateStringValue("value")); + mock_provider_.UpdatePolicy(CopyBundle(bundle)); + + EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_)); + proxy_provider_.SetDelegate(&mock_provider_); + Mock::VerifyAndClearExpectations(&observer_); + EXPECT_TRUE(bundle.Equals(proxy_provider_.policies())); + + EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_)); + bundle.Get(POLICY_DOMAIN_CHROME, std::string()) + .Set("policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + Value::CreateStringValue("new value")); + mock_provider_.UpdatePolicy(CopyBundle(bundle)); + Mock::VerifyAndClearExpectations(&observer_); + EXPECT_TRUE(bundle.Equals(proxy_provider_.policies())); + + EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_)); + proxy_provider_.SetDelegate(NULL); + EXPECT_TRUE(PolicyBundle().Equals(proxy_provider_.policies())); +} + +TEST_F(ProxyPolicyProviderTest, RefreshPolicies) { + EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_)); + proxy_provider_.RefreshPolicies(); + Mock::VerifyAndClearExpectations(&observer_); + + EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_)); + proxy_provider_.SetDelegate(&mock_provider_); + Mock::VerifyAndClearExpectations(&observer_); + + EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_)).Times(0); + EXPECT_CALL(mock_provider_, RefreshPolicies()); + proxy_provider_.RefreshPolicies(); + Mock::VerifyAndClearExpectations(&observer_); + Mock::VerifyAndClearExpectations(&mock_provider_); + + EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_)); + mock_provider_.UpdatePolicy(scoped_ptr<PolicyBundle>(new PolicyBundle())); + Mock::VerifyAndClearExpectations(&observer_); +} + +} // namespace policy diff --git a/chrome/browser/policy/user_cloud_policy_manager.cc b/chrome/browser/policy/user_cloud_policy_manager.cc new file mode 100644 index 0000000..693f036 --- /dev/null +++ b/chrome/browser/policy/user_cloud_policy_manager.cc @@ -0,0 +1,202 @@ +// 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/user_cloud_policy_manager.h" + +#include <string> + +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "chrome/browser/policy/cloud_policy_refresh_scheduler.h" +#include "chrome/browser/policy/cloud_policy_service.h" +#include "chrome/browser/policy/policy_constants.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/pref_names.h" +#include "policy/policy_constants.h" + +#if defined(OS_CHROMEOS) +#include "chrome/browser/policy/user_cloud_policy_store_chromeos.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/session_manager_client.h" +#endif + +namespace policy { + +namespace { + +#if defined(OS_CHROMEOS) +// Paths for the legacy policy caches in the profile directory. +// TODO(mnissler): Remove once the number of pre-M20 clients becomes negligible. + +// Subdirectory in the user's profile for storing user policies. +const FilePath::CharType kPolicyDir[] = FILE_PATH_LITERAL("Device Management"); +// File in the above directory for stroing user policy dmtokens. +const FilePath::CharType kTokenCacheFile[] = FILE_PATH_LITERAL("Token"); +// File in the above directory for storing user policy data. +const FilePath::CharType kPolicyCacheFile[] = FILE_PATH_LITERAL("Policy"); +#endif + +} // namespace + +UserCloudPolicyManager::UserCloudPolicyManager( + const PolicyDefinitionList* policy_list, + scoped_ptr<CloudPolicyStore> store, + bool wait_for_policy_fetch) + : ConfigurationPolicyProvider(policy_list), + wait_for_policy_fetch_(wait_for_policy_fetch), + wait_for_policy_refresh_(false), + store_(store.Pass()) { + store_->Load(); + store_->AddObserver(this); +} + +UserCloudPolicyManager::~UserCloudPolicyManager() { + Shutdown(); + store_->RemoveObserver(this); +} + +#if defined(OS_CHROMEOS) +// static +scoped_ptr<UserCloudPolicyManager> UserCloudPolicyManager::Create( + bool wait_for_policy_fetch) { + FilePath profile_dir; + CHECK(PathService::Get(chrome::DIR_USER_DATA, &profile_dir)); + CommandLine* command_line = CommandLine::ForCurrentProcess(); + const FilePath policy_dir = + profile_dir + .Append(command_line->GetSwitchValuePath(switches::kLoginProfile)) + .Append(kPolicyDir); + const FilePath policy_cache_file = policy_dir.Append(kPolicyCacheFile); + const FilePath token_cache_file = policy_dir.Append(kTokenCacheFile); + + scoped_ptr<CloudPolicyStore> store( + new UserCloudPolicyStoreChromeOS( + chromeos::DBusThreadManager::Get()->GetSessionManagerClient(), + token_cache_file, policy_cache_file)); + return scoped_ptr<UserCloudPolicyManager>( + new UserCloudPolicyManager(GetChromePolicyDefinitionList(), + store.Pass(), wait_for_policy_fetch)); +} +#endif + +void UserCloudPolicyManager::Initialize(PrefService* prefs, + DeviceManagementService* service, + UserAffiliation user_affiliation) { + DCHECK(!service_.get()); + prefs_ = prefs; + scoped_ptr<CloudPolicyClient> client( + new CloudPolicyClient(std::string(), std::string(), user_affiliation, + POLICY_SCOPE_USER, NULL, service)); + service_.reset(new CloudPolicyService(client.Pass(), store_.get())); + service_->client()->AddObserver(this); + + if (wait_for_policy_fetch_) { + // If we are supposed to wait for a policy fetch, we trigger an explicit + // policy refresh at startup that allows us to unblock initialization once + // done. The refresh scheduler only gets started once that refresh + // completes. Note that we might have to wait for registration to happen, + // see OnRegistrationStateChanged() below. + if (service_->client()->is_registered()) { + service_->RefreshPolicy( + base::Bind(&UserCloudPolicyManager::OnInitialPolicyFetchComplete, + base::Unretained(this))); + } + } else { + CancelWaitForPolicyFetch(); + } +} + +void UserCloudPolicyManager::Shutdown() { + refresh_scheduler_.reset(); + if (service_.get()) + service_->client()->RemoveObserver(this); + service_.reset(); + prefs_ = NULL; +} + +void UserCloudPolicyManager::CancelWaitForPolicyFetch() { + wait_for_policy_fetch_ = false; + CheckAndPublishPolicy(); + + // Now that |wait_for_policy_fetch_| is guaranteed to be false, the scheduler + // can be started. + if (service_.get() && !refresh_scheduler_.get() && prefs_) { + refresh_scheduler_.reset( + new CloudPolicyRefreshScheduler( + service_->client(), store_.get(), prefs_, + prefs::kUserPolicyRefreshRate, + MessageLoop::current()->message_loop_proxy())); + } +} + +bool UserCloudPolicyManager::IsInitializationComplete() const { + return store_->is_initialized() && !wait_for_policy_fetch_; +} + +void UserCloudPolicyManager::RefreshPolicies() { + if (service_.get()) { + wait_for_policy_refresh_ = true; + service_->RefreshPolicy( + base::Bind(&UserCloudPolicyManager::OnRefreshComplete, + base::Unretained(this))); + } else { + OnRefreshComplete(); + } +} + +void UserCloudPolicyManager::OnPolicyFetched(CloudPolicyClient* client) { + // No action required. If we're blocked on a policy fetch, we'll learn about + // completion of it through OnInitialPolicyFetchComplete(). +} + +void UserCloudPolicyManager::OnRegistrationStateChanged( + CloudPolicyClient* client) { + if (wait_for_policy_fetch_) { + // If we're blocked on the policy fetch, now is a good time to issue it. + if (service_->client()->is_registered()) { + service_->RefreshPolicy( + base::Bind(&UserCloudPolicyManager::OnInitialPolicyFetchComplete, + base::Unretained(this))); + } else { + // If the client has switched to not registered, we bail out as this + // indicates the cloud policy setup flow has been aborted. + CancelWaitForPolicyFetch(); + } + } +} + +void UserCloudPolicyManager::OnClientError(CloudPolicyClient* client) { + CancelWaitForPolicyFetch(); +} + +void UserCloudPolicyManager::CheckAndPublishPolicy() { + if (IsInitializationComplete() && !wait_for_policy_refresh_) { + scoped_ptr<PolicyBundle> bundle(new PolicyBundle()); + bundle->Get(POLICY_DOMAIN_CHROME, std::string()).CopyFrom( + store_->policy_map()); + UpdatePolicy(bundle.Pass()); + } +} + +void UserCloudPolicyManager::OnStoreLoaded(CloudPolicyStore* store) { + CheckAndPublishPolicy(); +} + +void UserCloudPolicyManager::OnStoreError(CloudPolicyStore* store) { + // No action required, the old policy is still valid. +} + +void UserCloudPolicyManager::OnInitialPolicyFetchComplete() { + CancelWaitForPolicyFetch(); +} + +void UserCloudPolicyManager::OnRefreshComplete() { + wait_for_policy_refresh_ = false; + CheckAndPublishPolicy(); +} + +} // namespace policy diff --git a/chrome/browser/policy/user_cloud_policy_manager.h b/chrome/browser/policy/user_cloud_policy_manager.h new file mode 100644 index 0000000..4c0b55f --- /dev/null +++ b/chrome/browser/policy/user_cloud_policy_manager.h @@ -0,0 +1,102 @@ +// 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_USER_CLOUD_POLICY_MANAGER_H_ +#define CHROME_BROWSER_POLICY_USER_CLOUD_POLICY_MANAGER_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/policy/cloud_policy_client.h" +#include "chrome/browser/policy/cloud_policy_constants.h" +#include "chrome/browser/policy/cloud_policy_store.h" +#include "chrome/browser/policy/configuration_policy_provider.h" + +class PrefService; + +namespace policy { + +class CloudPolicyRefreshScheduler; +class CloudPolicyService; +class DeviceManagementService; + +// UserCloudPolicyManager keeps track of all things user policy, drives the +// corresponding cloud policy service and publishes policy through the +// ConfigurationPolicyProvider interface. +class UserCloudPolicyManager : public ConfigurationPolicyProvider, + public CloudPolicyClient::Observer, + public CloudPolicyStore::Observer { + public: + // If |wait_for_policy| fetch is true, IsInitializationComplete() will return + // false as long as there hasn't been a successful policy fetch. + UserCloudPolicyManager(const PolicyDefinitionList* policy_list, + scoped_ptr<CloudPolicyStore> store, + bool wait_for_policy_fetch); + virtual ~UserCloudPolicyManager(); + +#if defined(OS_CHROMEOS) + // Creates a UserCloudPolicyService instance for the Chrome OS platform. + static scoped_ptr<UserCloudPolicyManager> Create(bool wait_for_policy_fetch); +#endif + + // Initializes the cloud connection. |prefs| and |service| must stay valid + // until Shutdown() gets called. + void Initialize(PrefService* prefs, + DeviceManagementService* service, + UserAffiliation user_affiliation); + void Shutdown(); + + // Cancels waiting for the policy fetch and flags the + // ConfigurationPolicyProvider ready (assuming all other initialization tasks + // have completed). + void CancelWaitForPolicyFetch(); + + CloudPolicyService* cloud_policy_service() { return service_.get(); } + + // ConfigurationPolicyProvider: + virtual bool IsInitializationComplete() const OVERRIDE; + virtual void RefreshPolicies() OVERRIDE; + + // CloudPolicyClient::Observer: + virtual void OnPolicyFetched(CloudPolicyClient* client) OVERRIDE; + virtual void OnRegistrationStateChanged(CloudPolicyClient* client) OVERRIDE; + virtual void OnClientError(CloudPolicyClient* client) OVERRIDE; + + // CloudPolicyStore::Observer: + virtual void OnStoreLoaded(CloudPolicyStore* store) OVERRIDE; + virtual void OnStoreError(CloudPolicyStore* store) OVERRIDE; + + private: + // Check whether fully initialized and if so, publish policy by calling + // ConfigurationPolicyStore::UpdatePolicy(). + void CheckAndPublishPolicy(); + + // Completion handler for the explicit policy fetch triggered on startup in + // case |wait_for_policy_fetch_| is true. + void OnInitialPolicyFetchComplete(); + + // Completion handler for policy refresh operations. + void OnRefreshComplete(); + + // Whether to wait for a policy fetch to complete before reporting + // IsInitializationComplete(). + bool wait_for_policy_fetch_; + + // Whether there's a policy refresh operation pending, in which case all + // policy update notifications are deferred until after it completes. + bool wait_for_policy_refresh_; + + scoped_ptr<CloudPolicyStore> store_; + scoped_ptr<CloudPolicyService> service_; + scoped_ptr<CloudPolicyRefreshScheduler> refresh_scheduler_; + + // The pref service to pass to the refresh scheduler on initialization. + PrefService* prefs_; + + DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyManager); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_USER_CLOUD_POLICY_MANAGER_H_ diff --git a/chrome/browser/policy/user_cloud_policy_manager_unittest.cc b/chrome/browser/policy/user_cloud_policy_manager_unittest.cc new file mode 100644 index 0000000..498192f --- /dev/null +++ b/chrome/browser/policy/user_cloud_policy_manager_unittest.cc @@ -0,0 +1,347 @@ +// 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/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "chrome/browser/policy/cloud_policy_service.h" +#include "chrome/browser/policy/configuration_policy_provider_test.h" +#include "chrome/browser/policy/mock_cloud_policy_store.h" +#include "chrome/browser/policy/mock_configuration_policy_provider.h" +#include "chrome/browser/policy/mock_device_management_service.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" +#include "chrome/browser/policy/user_cloud_policy_manager.h" +#include "chrome/browser/prefs/browser_prefs.h" +#include "chrome/test/base/testing_pref_service.h" +#include "policy/policy_constants.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace em = enterprise_management; + +using testing::AnyNumber; +using testing::AtLeast; +using testing::Mock; +using testing::_; + +namespace policy { +namespace { + +class TestHarness : public PolicyProviderTestHarness { + public: + explicit TestHarness(PolicyLevel level); + virtual ~TestHarness(); + + virtual void SetUp() OVERRIDE; + + virtual ConfigurationPolicyProvider* CreateProvider( + const PolicyDefinitionList* policy_definition_list) OVERRIDE; + + virtual void InstallEmptyPolicy() OVERRIDE; + virtual void InstallStringPolicy(const std::string& policy_name, + const std::string& policy_value) OVERRIDE; + virtual void InstallIntegerPolicy(const std::string& policy_name, + int policy_value) OVERRIDE; + virtual void InstallBooleanPolicy(const std::string& policy_name, + bool policy_value) OVERRIDE; + virtual void InstallStringListPolicy( + const std::string& policy_name, + const base::ListValue* policy_value) OVERRIDE; + virtual void InstallDictionaryPolicy( + const std::string& policy_name, + const base::DictionaryValue* policy_value) OVERRIDE; + + // Creates harnesses for mandatory and recommended levels, respectively. + static PolicyProviderTestHarness* CreateMandatory(); + static PolicyProviderTestHarness* CreateRecommended(); + + private: + MockCloudPolicyStore* store_; + + DISALLOW_COPY_AND_ASSIGN(TestHarness); +}; + +TestHarness::TestHarness(PolicyLevel level) + : PolicyProviderTestHarness(level, POLICY_SCOPE_USER) {} + +TestHarness::~TestHarness() {} + +void TestHarness::SetUp() {} + +ConfigurationPolicyProvider* TestHarness::CreateProvider( + const PolicyDefinitionList* policy_definition_list) { + // Create and initialize the store. + store_ = new MockCloudPolicyStore(); + store_->NotifyStoreLoaded(); + EXPECT_CALL(*store_, Load()); + return new UserCloudPolicyManager(policy_definition_list, + scoped_ptr<CloudPolicyStore>(store_).Pass(), + false); +} + +void TestHarness::InstallEmptyPolicy() {} + +void TestHarness::InstallStringPolicy(const std::string& policy_name, + const std::string& policy_value) { + store_->policy_map_.Set(policy_name, policy_level(), policy_scope(), + base::Value::CreateStringValue(policy_value)); +} + +void TestHarness::InstallIntegerPolicy(const std::string& policy_name, + int policy_value) { + store_->policy_map_.Set(policy_name, policy_level(), policy_scope(), + base::Value::CreateIntegerValue(policy_value)); +} + +void TestHarness::InstallBooleanPolicy(const std::string& policy_name, + bool policy_value) { + store_->policy_map_.Set(policy_name, policy_level(), policy_scope(), + base::Value::CreateBooleanValue(policy_value)); +} + +void TestHarness::InstallStringListPolicy(const std::string& policy_name, + const base::ListValue* policy_value) { + store_->policy_map_.Set(policy_name, policy_level(), policy_scope(), + policy_value->DeepCopy()); +} + +void TestHarness::InstallDictionaryPolicy( + const std::string& policy_name, + const base::DictionaryValue* policy_value) { + store_->policy_map_.Set(policy_name, policy_level(), policy_scope(), + policy_value->DeepCopy()); +} + +// static +PolicyProviderTestHarness* TestHarness::CreateMandatory() { + return new TestHarness(POLICY_LEVEL_MANDATORY); +} + +// static +PolicyProviderTestHarness* TestHarness::CreateRecommended() { + return new TestHarness(POLICY_LEVEL_RECOMMENDED); +} + +// Instantiate abstract test case for basic policy reading tests. +INSTANTIATE_TEST_CASE_P( + UserCloudPolicyManagerProviderTest, + ConfigurationPolicyProviderTest, + testing::Values(TestHarness::CreateMandatory, + TestHarness::CreateRecommended)); + +class UserCloudPolicyManagerTest : public testing::Test { + protected: + UserCloudPolicyManagerTest() + : store_(NULL) {} + + virtual void SetUp() OVERRIDE { + browser::RegisterLocalState(&prefs_); + + // Set up a policy map for testing. + policy_map_.Set("key", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, + base::Value::CreateStringValue("value")); + expected_bundle_.Get(POLICY_DOMAIN_CHROME, std::string()).CopyFrom( + policy_map_); + + // Create a fake policy blob to deliver to the client. + em::PolicyData policy_data; + em::PolicyFetchResponse* policy_response = + policy_blob_.mutable_policy_response()->add_response(); + ASSERT_TRUE(policy_data.SerializeToString( + policy_response->mutable_policy_data())); + + EXPECT_CALL(device_management_service_, StartJob(_, _, _, _, _, _, _)) + .Times(AnyNumber()); + } + + void CreateManager(bool wait_for_policy_fetch) { + store_ = new MockCloudPolicyStore(); + EXPECT_CALL(*store_, Load()); + manager_.reset( + new UserCloudPolicyManager(policy::GetChromePolicyDefinitionList(), + scoped_ptr<CloudPolicyStore>(store_).Pass(), + wait_for_policy_fetch)); + registrar_.Init(manager_.get(), &observer_); + } + + void CreateManagerWithPendingFetch() { + CreateManager(true); + manager_->Initialize(&prefs_, &device_management_service_, + USER_AFFILIATION_NONE); + EXPECT_FALSE(manager_->IsInitializationComplete()); + + // Finishing the Load() operation shouldn't set the initialized flag. + EXPECT_CALL(observer_, OnUpdatePolicy(_)).Times(0); + store_->NotifyStoreLoaded(); + EXPECT_FALSE(manager_->IsInitializationComplete()); + Mock::VerifyAndClearExpectations(&observer_); + } + + // Required by the refresh scheduler that's created by the manager. + MessageLoop loop_; + + // Convenience policy objects. + em::DeviceManagementResponse policy_blob_; + PolicyMap policy_map_; + PolicyBundle expected_bundle_; + + // Policy infrastructure. + TestingPrefService prefs_; + MockConfigurationPolicyObserver observer_; + MockDeviceManagementService device_management_service_; + MockCloudPolicyStore* store_; + scoped_ptr<UserCloudPolicyManager> manager_; + ConfigurationPolicyObserverRegistrar registrar_; + + private: + DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyManagerTest); +}; + +TEST_F(UserCloudPolicyManagerTest, Init) { + CreateManager(false); + PolicyBundle empty_bundle; + EXPECT_TRUE(empty_bundle.Equals(manager_->policies())); + EXPECT_FALSE(manager_->IsInitializationComplete()); + + store_->policy_map_.CopyFrom(policy_map_); + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())); + store_->NotifyStoreLoaded(); + EXPECT_TRUE(expected_bundle_.Equals(manager_->policies())); + EXPECT_TRUE(manager_->IsInitializationComplete()); +} + +TEST_F(UserCloudPolicyManagerTest, Update) { + CreateManager(false); + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())); + store_->NotifyStoreLoaded(); + EXPECT_TRUE(manager_->IsInitializationComplete()); + PolicyBundle empty_bundle; + EXPECT_TRUE(empty_bundle.Equals(manager_->policies())); + + store_->policy_map_.CopyFrom(policy_map_); + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())); + store_->NotifyStoreLoaded(); + EXPECT_TRUE(expected_bundle_.Equals(manager_->policies())); + EXPECT_TRUE(manager_->IsInitializationComplete()); +} + +TEST_F(UserCloudPolicyManagerTest, RefreshNotRegistered) { + CreateManager(false); + manager_->Initialize(&prefs_, &device_management_service_, + USER_AFFILIATION_NONE); + + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())); + store_->NotifyStoreLoaded(); + Mock::VerifyAndClearExpectations(&observer_); + + // A refresh on a non-registered store should not block. + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())); + manager_->RefreshPolicies(); + Mock::VerifyAndClearExpectations(&observer_); +} + +TEST_F(UserCloudPolicyManagerTest, RefreshSuccessful) { + CreateManager(false); + manager_->Initialize(&prefs_, &device_management_service_, + USER_AFFILIATION_NONE); + manager_->cloud_policy_service()->client()->SetupRegistration("dm_token", + "client_id"); + + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())); + store_->NotifyStoreLoaded(); + Mock::VerifyAndClearExpectations(&observer_); + + // Start a refresh. + EXPECT_CALL(observer_, OnUpdatePolicy(_)).Times(0); + MockDeviceManagementJob* request_job; + EXPECT_CALL(device_management_service_, + CreateJob(DeviceManagementRequestJob::TYPE_POLICY_FETCH)) + .WillOnce(device_management_service_.CreateAsyncJob(&request_job)); + manager_->RefreshPolicies(); + ASSERT_TRUE(request_job); + store_->policy_map_.CopyFrom(policy_map_); + Mock::VerifyAndClearExpectations(&observer_); + + // A stray reload should be suppressed until the refresh completes. + EXPECT_CALL(observer_, OnUpdatePolicy(_)).Times(0); + store_->NotifyStoreLoaded(); + Mock::VerifyAndClearExpectations(&observer_); + + // Respond to the policy fetch, which should trigger a write to |store_|. + EXPECT_CALL(observer_, OnUpdatePolicy(_)).Times(0); + EXPECT_CALL(*store_, Store(_)); + request_job->SendResponse(DM_STATUS_SUCCESS, policy_blob_); + Mock::VerifyAndClearExpectations(&observer_); + + // The load notification from |store_| should trigger the policy update. + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())); + store_->NotifyStoreLoaded(); + EXPECT_TRUE(expected_bundle_.Equals(manager_->policies())); + Mock::VerifyAndClearExpectations(&observer_); +} + +TEST_F(UserCloudPolicyManagerTest, WaitForPolicyFetch) { + CreateManagerWithPendingFetch(); + + // Setting the token should trigger the policy fetch. + EXPECT_CALL(observer_, OnUpdatePolicy(_)).Times(0); + MockDeviceManagementJob* fetch_request = NULL; + EXPECT_CALL(device_management_service_, + CreateJob(DeviceManagementRequestJob::TYPE_POLICY_FETCH)) + .WillOnce(device_management_service_.CreateAsyncJob(&fetch_request)); + manager_->cloud_policy_service()->client()->SetupRegistration("dm_token", + "client_id"); + ASSERT_TRUE(fetch_request); + EXPECT_FALSE(manager_->IsInitializationComplete()); + Mock::VerifyAndClearExpectations(&observer_); + + // Respond to the policy fetch, which should trigger a write to |store_|. + EXPECT_CALL(observer_, OnUpdatePolicy(_)).Times(0); + EXPECT_CALL(*store_, Store(_)); + fetch_request->SendResponse(DM_STATUS_SUCCESS, policy_blob_); + EXPECT_FALSE(manager_->IsInitializationComplete()); + Mock::VerifyAndClearExpectations(&observer_); + + // The load notification from |store_| should trigger the policy update and + // flip the initialized bit. + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())); + store_->NotifyStoreLoaded(); + EXPECT_TRUE(manager_->IsInitializationComplete()); + Mock::VerifyAndClearExpectations(&observer_); +} + +TEST_F(UserCloudPolicyManagerTest, WaitForPolicyFetchError) { + CreateManagerWithPendingFetch(); + + // Setting the token should trigger the policy fetch. + EXPECT_CALL(observer_, OnUpdatePolicy(_)).Times(0); + MockDeviceManagementJob* fetch_request = NULL; + EXPECT_CALL(device_management_service_, + CreateJob(DeviceManagementRequestJob::TYPE_POLICY_FETCH)) + .WillOnce(device_management_service_.CreateAsyncJob(&fetch_request)); + manager_->cloud_policy_service()->client()->SetupRegistration("dm_token", + "client_id"); + ASSERT_TRUE(fetch_request); + EXPECT_FALSE(manager_->IsInitializationComplete()); + Mock::VerifyAndClearExpectations(&observer_); + + // Make the policy fetch fail, at which point the manager should bail out. + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())).Times(AtLeast(1)); + fetch_request->SendResponse(DM_STATUS_REQUEST_FAILED, policy_blob_); + EXPECT_TRUE(manager_->IsInitializationComplete()); + Mock::VerifyAndClearExpectations(&observer_); +} + +TEST_F(UserCloudPolicyManagerTest, WaitForPolicyFetchCancel) { + CreateManagerWithPendingFetch(); + + // Cancelling the initial fetch should flip the flag. + EXPECT_CALL(observer_, OnUpdatePolicy(manager_.get())); + manager_->CancelWaitForPolicyFetch(); + EXPECT_TRUE(manager_->IsInitializationComplete()); + Mock::VerifyAndClearExpectations(&observer_); +} + +} // namespace +} // namespace policy diff --git a/chrome/browser/policy/user_cloud_policy_store_chromeos_unittest.cc b/chrome/browser/policy/user_cloud_policy_store_chromeos_unittest.cc index 0cda54a..e171e5c 100644 --- a/chrome/browser/policy/user_cloud_policy_store_chromeos_unittest.cc +++ b/chrome/browser/policy/user_cloud_policy_store_chromeos_unittest.cc @@ -15,6 +15,8 @@ #include "chromeos/dbus/mock_session_manager_client.h" #include "content/public/test/test_browser_thread.h" #include "policy/policy_constants.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" namespace em = enterprise_management; |