diff options
author | jkummerow@chromium.org <jkummerow@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-23 12:56:17 +0000 |
---|---|---|
committer | jkummerow@chromium.org <jkummerow@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-23 12:56:17 +0000 |
commit | f5e942e0b0e7c5fcd84711567356280a5409a547 (patch) | |
tree | 2bc2bc899c99354f8c08e937e0bab0fc70cfdd1f /chrome/browser/policy | |
parent | ba0cb0e9bb82616fe96d16fbf65ee38aa437a633 (diff) | |
download | chromium_src-f5e942e0b0e7c5fcd84711567356280a5409a547.zip chromium_src-f5e942e0b0e7c5fcd84711567356280a5409a547.tar.gz chromium_src-f5e942e0b0e7c5fcd84711567356280a5409a547.tar.bz2 |
Refresh policies from DM server periodically
Also, this takes care of retrying when policy fetching fails.
BUG=62487
TEST=unit tests: DeviceManagementPolicyProviderTest.*:DeviceTokenFetcherTest.*
Review URL: http://codereview.chromium.org/5219006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67091 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/policy')
11 files changed, 345 insertions, 88 deletions
diff --git a/chrome/browser/policy/device_management_policy_cache.cc b/chrome/browser/policy/device_management_policy_cache.cc index d9ed7a6..534794d 100644 --- a/chrome/browser/policy/device_management_policy_cache.cc +++ b/chrome/browser/policy/device_management_policy_cache.cc @@ -105,9 +105,10 @@ void DeviceManagementPolicyCache::LoadPolicyFromFile() { } } -void DeviceManagementPolicyCache::SetPolicy( +bool DeviceManagementPolicyCache::SetPolicy( const em::DevicePolicyResponse& policy) { DictionaryValue* value = DeviceManagementPolicyCache::DecodePolicy(policy); + const bool new_policy_differs = !(value->Equals(policy_.get())); base::Time now(base::Time::Now()); { AutoLock lock(lock_); @@ -123,6 +124,7 @@ void DeviceManagementPolicyCache::SetPolicy( FROM_HERE, new PersistPolicyTask(backing_file_path_, policy_copy, base::Time::NowFromSystemTime())); + return new_policy_differs; } DictionaryValue* DeviceManagementPolicyCache::GetPolicy() { diff --git a/chrome/browser/policy/device_management_policy_cache.h b/chrome/browser/policy/device_management_policy_cache.h index 5136059..ffe63d4 100644 --- a/chrome/browser/policy/device_management_policy_cache.h +++ b/chrome/browser/policy/device_management_policy_cache.h @@ -33,8 +33,9 @@ class DeviceManagementPolicyCache { // cache files are ignored. void LoadPolicyFromFile(); - // Resets the policy information. - void SetPolicy(const em::DevicePolicyResponse& policy); + // Resets the policy information. Returns true if the new policy is different + // from the previously stored policy. + bool SetPolicy(const em::DevicePolicyResponse& policy); // Gets the policy information. Ownership of the return value is transferred // to the caller. diff --git a/chrome/browser/policy/device_management_policy_cache_unittest.cc b/chrome/browser/policy/device_management_policy_cache_unittest.cc index 6654d38..45e78fc 100644 --- a/chrome/browser/policy/device_management_policy_cache_unittest.cc +++ b/chrome/browser/policy/device_management_policy_cache_unittest.cc @@ -134,7 +134,10 @@ TEST_F(DeviceManagementPolicyCacheTest, SetPolicy) { DeviceManagementPolicyCache cache(test_file()); em::DevicePolicyResponse policy; AddStringPolicy(&policy, "HomepageLocation", "http://www.example.com"); - cache.SetPolicy(policy); + EXPECT_TRUE(cache.SetPolicy(policy)); + em::DevicePolicyResponse policy2; + AddStringPolicy(&policy2, "HomepageLocation", "http://www.example.com"); + EXPECT_FALSE(cache.SetPolicy(policy2)); DictionaryValue expected; expected.Set("HomepageLocation", Value::CreateStringValue("http://www.example.com")); @@ -147,14 +150,14 @@ TEST_F(DeviceManagementPolicyCacheTest, ResetPolicy) { em::DevicePolicyResponse policy; AddStringPolicy(&policy, "HomepageLocation", "http://www.example.com"); - cache.SetPolicy(policy); + EXPECT_TRUE(cache.SetPolicy(policy)); DictionaryValue expected; expected.Set("HomepageLocation", Value::CreateStringValue("http://www.example.com")); scoped_ptr<Value> policy_value(cache.GetPolicy()); EXPECT_TRUE(expected.Equals(policy_value.get())); - cache.SetPolicy(em::DevicePolicyResponse()); + EXPECT_TRUE(cache.SetPolicy(em::DevicePolicyResponse())); policy_value.reset(cache.GetPolicy()); DictionaryValue empty; EXPECT_TRUE(empty.Equals(policy_value.get())); @@ -189,7 +192,7 @@ TEST_F(DeviceManagementPolicyCacheTest, FreshPolicyOverride) { em::DevicePolicyResponse updated_policy; AddStringPolicy(&updated_policy, "HomepageLocation", "http://www.chromium.org"); - cache.SetPolicy(updated_policy); + EXPECT_TRUE(cache.SetPolicy(updated_policy)); cache.LoadPolicyFromFile(); DictionaryValue expected; diff --git a/chrome/browser/policy/device_management_policy_provider.cc b/chrome/browser/policy/device_management_policy_provider.cc index 25cfc32..f191564 100644 --- a/chrome/browser/policy/device_management_policy_provider.cc +++ b/chrome/browser/policy/device_management_policy_provider.cc @@ -7,12 +7,11 @@ #include "base/command_line.h" #include "base/file_util.h" #include "base/path_service.h" +#include "base/rand_util.h" #include "base/task.h" -#include "base/time.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/policy/device_management_backend.h" #include "chrome/browser/policy/device_management_policy_cache.h" -#include "chrome/browser/policy/device_token_fetcher.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/notification_service.h" @@ -22,7 +21,12 @@ namespace policy { const char kChromePolicyScope[] = "chromeos/device"; const char kChromeDevicePolicySettingKey[] = "chrome-policy"; -const int64 kPolicyRefreshRateInMinutes = 3 * 60; // 3 hours +const int64 kPolicyRefreshRateInMilliseconds = 3 * 60 * 60 * 1000; // 3 hours +const int64 kPolicyRefreshMaxEarlierInMilliseconds = 20 * 60 * 1000; // 20 mins +// These are the base values for delays before retrying after an error. They +// will be doubled each time they are used. +const int64 kPolicyRefreshErrorDelayInMilliseconds = 3 * 1000; // 3 seconds +const int64 kDeviceTokenRefreshErrorDelayInMilliseconds = 3 * 1000; // Ensures that the portion of the policy provider implementation that requires // the IOThread is deferred until the IOThread is fully initialized. The policy @@ -48,6 +52,22 @@ class DeviceManagementPolicyProvider::InitializeAfterIOThreadExistsTask base::WeakPtr<DeviceManagementPolicyProvider> provider_; }; +class DeviceManagementPolicyProvider::RefreshTask : public Task { + public: + explicit RefreshTask(base::WeakPtr<DeviceManagementPolicyProvider> provider) + : provider_(provider) {} + + // Task implementation: + virtual void Run() { + DeviceManagementPolicyProvider* provider = provider_.get(); + if (provider) + provider->RefreshTaskExecute(); + } + + private: + base::WeakPtr<DeviceManagementPolicyProvider> provider_; +}; + DeviceManagementPolicyProvider::DeviceManagementPolicyProvider( const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list, DeviceManagementBackend* backend, @@ -57,7 +77,12 @@ DeviceManagementPolicyProvider::DeviceManagementPolicyProvider( backend_(backend), token_service_(token_service), storage_dir_(GetOrCreateDeviceManagementDir(storage_dir)), - policy_request_pending_(false) { + policy_request_pending_(false), + refresh_task_pending_(false), + policy_refresh_rate_ms_(kPolicyRefreshRateInMilliseconds), + policy_refresh_max_earlier_ms_(kPolicyRefreshMaxEarlierInMilliseconds), + policy_refresh_error_delay_ms_(kPolicyRefreshErrorDelayInMilliseconds), + token_fetch_error_delay_ms_(kDeviceTokenRefreshErrorDelayInMilliseconds) { Initialize(); } @@ -70,34 +95,46 @@ bool DeviceManagementPolicyProvider::Provide( return true; } -void DeviceManagementPolicyProvider::Observe( - NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - if (type == NotificationType::DEVICE_TOKEN_AVAILABLE) { - if (token_fetcher_.get() == Source<DeviceTokenFetcher>(source).ptr() && - !policy_request_pending_ && - IsPolicyStale()) { - SendPolicyRequest(); - } - } else { - NOTREACHED(); - } -} - void DeviceManagementPolicyProvider::HandlePolicyResponse( const em::DevicePolicyResponse& response) { - cache_->SetPolicy(response); - NotifyStoreOfPolicyChange(); + if (cache_->SetPolicy(response)) + NotifyStoreOfPolicyChange(); policy_request_pending_ = false; + // Reset the error delay since policy fetching succeeded this time. + policy_refresh_error_delay_ms_ = kPolicyRefreshErrorDelayInMilliseconds; + ScheduleRefreshTask(GetRefreshTaskDelay()); } void DeviceManagementPolicyProvider::OnError( DeviceManagementBackend::ErrorCode code) { - LOG(WARNING) << "could not provide policy from the device manager (error = " - << code << ")"; policy_request_pending_ = false; - // TODO(danno): do something sensible in the error case, perhaps retry later? + LOG(WARNING) << "Could not provide policy from the device manager (error = " + << code << "), will retry in " + << (policy_refresh_error_delay_ms_/1000) << " seconds."; + ScheduleRefreshTask(policy_refresh_error_delay_ms_); + policy_refresh_error_delay_ms_ *= 2; + if (policy_refresh_rate_ms_ && + policy_refresh_rate_ms_ < policy_refresh_error_delay_ms_) { + policy_refresh_error_delay_ms_ = policy_refresh_rate_ms_; + } +} + +void DeviceManagementPolicyProvider::OnTokenSuccess() { + if (policy_request_pending_) + return; + SendPolicyRequest(); +} + +void DeviceManagementPolicyProvider::OnTokenError() { + LOG(WARNING) << "Could not retrieve device token."; + ScheduleRefreshTask(token_fetch_error_delay_ms_); + token_fetch_error_delay_ms_ *= 2; + if (token_fetch_error_delay_ms_ > policy_refresh_rate_ms_) + token_fetch_error_delay_ms_ = policy_refresh_rate_ms_; +} + +void DeviceManagementPolicyProvider::OnNotManaged() { + VLOG(1) << "This device is not managed."; } void DeviceManagementPolicyProvider::Shutdown() { @@ -107,10 +144,6 @@ void DeviceManagementPolicyProvider::Shutdown() { } void DeviceManagementPolicyProvider::Initialize() { - registrar_.Add(this, - NotificationType::DEVICE_TOKEN_AVAILABLE, - NotificationService::AllSources()); - const FilePath policy_path = storage_dir_.Append( FILE_PATH_LITERAL("Policy")); cache_.reset(new DeviceManagementPolicyCache(policy_path)); @@ -128,12 +161,15 @@ void DeviceManagementPolicyProvider::InitializeAfterIOThreadExists() { if (token_service_) { token_fetcher_ = new DeviceTokenFetcher(backend_.get(), token_service_, token_path); + registrar_.Init(token_fetcher_); + registrar_.AddObserver(this); token_fetcher_->StartFetching(); } } void DeviceManagementPolicyProvider::SendPolicyRequest() { if (!policy_request_pending_) { + policy_request_pending_ = true; em::DevicePolicyRequest policy_request; policy_request.set_policy_scope(kChromePolicyScope); em::DevicePolicySettingRequest* setting = @@ -142,18 +178,44 @@ void DeviceManagementPolicyProvider::SendPolicyRequest() { backend_->ProcessPolicyRequest(token_fetcher_->GetDeviceToken(), token_fetcher_->GetDeviceID(), policy_request, this); - policy_request_pending_ = true; } } -bool DeviceManagementPolicyProvider::IsPolicyStale() const { - base::Time now(base::Time::NowFromSystemTime()); - base::Time last_policy_refresh_time = - cache_->last_policy_refresh_time(); - base::Time policy_expiration_time = - last_policy_refresh_time + base::TimeDelta::FromMinutes( - kPolicyRefreshRateInMinutes); - return (now > policy_expiration_time); +void DeviceManagementPolicyProvider::RefreshTaskExecute() { + DCHECK(refresh_task_pending_); + refresh_task_pending_ = false; + // If there is no valid device token, the token_fetcher_ apparently failed, + // so it must be restarted. + if (!token_fetcher_->IsTokenValid()) { + if (token_fetcher_->IsTokenPending()) { + NOTREACHED(); + return; + } + token_fetcher_->Restart(); + return; + } + // If there is a device token, just refresh policies. + SendPolicyRequest(); +} + +void DeviceManagementPolicyProvider::ScheduleRefreshTask( + int64 delay_in_milliseconds) { + // This check is simply a safeguard, the situation currently cannot happen. + if (refresh_task_pending_) { + NOTREACHED(); + return; + } + refresh_task_pending_ = true; + BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE, + new RefreshTask(AsWeakPtr()), + delay_in_milliseconds); +} + +int64 DeviceManagementPolicyProvider::GetRefreshTaskDelay() { + int64 delay = policy_refresh_rate_ms_; + if (policy_refresh_max_earlier_ms_) + delay -= base::RandGenerator(policy_refresh_max_earlier_ms_); + return delay; } // static diff --git a/chrome/browser/policy/device_management_policy_provider.h b/chrome/browser/policy/device_management_policy_provider.h index d91e22b..89ef850 100644 --- a/chrome/browser/policy/device_management_policy_provider.h +++ b/chrome/browser/policy/device_management_policy_provider.h @@ -10,13 +10,11 @@ #include "base/file_path.h" #include "base/scoped_ptr.h" +#include "base/time.h" #include "base/weak_ptr.h" #include "chrome/browser/policy/configuration_policy_provider.h" #include "chrome/browser/policy/device_management_backend.h" -#include "chrome/common/notification_details.h" -#include "chrome/common/notification_observer.h" -#include "chrome/common/notification_registrar.h" -#include "chrome/common/notification_source.h" +#include "chrome/browser/policy/device_token_fetcher.h" class TokenService; @@ -24,16 +22,15 @@ namespace policy { class DeviceManagementBackend; class DeviceManagementPolicyCache; -class DeviceTokenFetcher; // Provides policy fetched from the device management server. With the exception // of the Provide method, which can be called on the FILE thread, all public // methods must be called on the UI thread. class DeviceManagementPolicyProvider : public ConfigurationPolicyProvider, - public NotificationObserver, public DeviceManagementBackend::DevicePolicyResponseDelegate, - public base::SupportsWeakPtr<DeviceManagementPolicyProvider> { + public base::SupportsWeakPtr<DeviceManagementPolicyProvider>, + public DeviceTokenFetcher::Observer { public: DeviceManagementPolicyProvider(const PolicyDefinitionList* policy_list, DeviceManagementBackend* backend, @@ -45,16 +42,16 @@ class DeviceManagementPolicyProvider // ConfigurationPolicyProvider implementation: virtual bool Provide(ConfigurationPolicyStoreInterface* store); - // NotificationObserver implementation: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - // DevicePolicyResponseDelegate implementation: virtual void HandlePolicyResponse( const em::DevicePolicyResponse& response); virtual void OnError(DeviceManagementBackend::ErrorCode code); + // DeviceTokenFetcher::Observer implementation: + void OnTokenSuccess(); + void OnTokenError(); + void OnNotManaged(); + // True if a policy request has been sent to the device management backend // server and no response or error has yet been received. bool IsPolicyRequestPending() const { return policy_request_pending_; } @@ -65,6 +62,9 @@ class DeviceManagementPolicyProvider private: class InitializeAfterIOThreadExistsTask; + class RefreshTask; + + friend class DeviceManagementPolicyProviderTest; // Called by constructors to perform shared initialization. Initialization // requiring the IOThread must not be performed directly in this method, @@ -80,9 +80,15 @@ class DeviceManagementPolicyProvider // already outstanding. void SendPolicyRequest(); - // True if policy must be re-fetched because the cached policy is too old or - // its time stamp is invalid. - bool IsPolicyStale() const; + // Triggers policy refresh, re-requesting device token and policy information + // as necessary. + void RefreshTaskExecute(); + + // Schedules a new RefreshTask. + void ScheduleRefreshTask(int64 delay_in_milliseconds); + + // Calculates when the next RefreshTask shall be executed. + int64 GetRefreshTaskDelay(); // Provides the URL at which requests are sent to from the device management // backend. @@ -93,13 +99,32 @@ class DeviceManagementPolicyProvider static FilePath GetOrCreateDeviceManagementDir( const FilePath& user_data_dir); + // Give unit tests the ability to override timeout settings. + void set_policy_refresh_rate_ms(int64 policy_refresh_rate_ms) { + policy_refresh_rate_ms_ = policy_refresh_rate_ms; + } + void set_policy_refresh_max_earlier_ms(int64 policy_refresh_max_earlier_ms) { + policy_refresh_max_earlier_ms_ = policy_refresh_max_earlier_ms; + } + void set_policy_refresh_error_delay_ms(int64 policy_refresh_error_delay_ms) { + policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms; + } + void set_token_fetch_error_delay_ms(int64 token_fetch_error_delay_ms) { + token_fetch_error_delay_ms_ = token_fetch_error_delay_ms; + } + scoped_ptr<DeviceManagementBackend> backend_; - TokenService* token_service_; // weak + TokenService* token_service_; // weak scoped_ptr<DeviceManagementPolicyCache> cache_; scoped_refptr<DeviceTokenFetcher> token_fetcher_; - NotificationRegistrar registrar_; + DeviceTokenFetcher::ObserverRegistrar registrar_; FilePath storage_dir_; bool policy_request_pending_; + bool refresh_task_pending_; + int64 policy_refresh_rate_ms_; + int64 policy_refresh_max_earlier_ms_; + int64 policy_refresh_error_delay_ms_; + int64 token_fetch_error_delay_ms_; DISALLOW_COPY_AND_ASSIGN(DeviceManagementPolicyProvider); }; diff --git a/chrome/browser/policy/device_management_policy_provider_unittest.cc b/chrome/browser/policy/device_management_policy_provider_unittest.cc index 5717286..705b228 100644 --- a/chrome/browser/policy/device_management_policy_provider_unittest.cc +++ b/chrome/browser/policy/device_management_policy_provider_unittest.cc @@ -76,10 +76,21 @@ class DeviceManagementPolicyProviderTest : public testing::Test { loop_.RunAllPending(); } - protected: MockDeviceManagementBackend* backend_; // weak scoped_ptr<DeviceManagementPolicyProvider> provider_; + protected: + void SetRefreshDelays(DeviceManagementPolicyProvider* provider, + int64 policy_refresh_rate_ms, + int64 policy_refresh_max_earlier_ms, + int64 policy_refresh_error_delay_ms, + int64 token_fetch_error_delay_ms) { + provider->set_policy_refresh_rate_ms(policy_refresh_rate_ms); + provider->set_policy_refresh_max_earlier_ms(policy_refresh_max_earlier_ms); + provider->set_policy_refresh_error_delay_ms(policy_refresh_error_delay_ms); + provider->set_token_fetch_error_delay_ms(token_fetch_error_delay_ms); + } + private: MessageLoop loop_; BrowserThread ui_thread_; @@ -127,13 +138,23 @@ TEST_F(DeviceManagementPolicyProviderTest, SecondProvide) { SimulateSuccessfulInitialPolicyFetch(); // Simulate a app relaunch by constructing a new provider. Policy should be - // immediately provided and no refresh should be triggered. + // refreshed (since that might be the purpose of the app relaunch). CreateNewBackend(); - EXPECT_CALL(*backend_, ProcessPolicyRequest(_, _, _, _)).Times(0); + EXPECT_CALL(*backend_, ProcessPolicyRequest(_, _, _, _)).Times(1); CreateNewProvider(); + Mock::VerifyAndClearExpectations(backend_); + + // Simulate another app relaunch, this time against a failing backend. + // Cached policy should still be available. + CreateNewBackend(); + backend_->AllShouldFail(); MockConfigurationPolicyStore store; + EXPECT_CALL(*backend_, ProcessPolicyRequest(_, _, _, _)).Times(1); + CreateNewProvider(); + SimulateSuccessfulLoginAndRunPending(); EXPECT_CALL(store, Apply(kPolicyDisableSpdy, _)).Times(1); provider_->Provide(&store); + ASSERT_EQ(1U, store.policy_map().size()); } // When policy is successfully fetched from the device management server, it @@ -144,9 +165,24 @@ TEST_F(DeviceManagementPolicyProviderTest, FetchTriggersRefresh) { registrar.Add(&observer, NotificationType::POLICY_CHANGED, NotificationService::AllSources()); - EXPECT_CALL(observer, - Observe(_, _, _)).Times(1); + EXPECT_CALL(observer, Observe(_, _, _)).Times(1); SimulateSuccessfulInitialPolicyFetch(); } +TEST_F(DeviceManagementPolicyProviderTest, ErrorCausesNewRequest) { + backend_->RegisterFailsOncePolicyFailsTwice(); + SetRefreshDelays(provider_.get(), 1000 * 1000, 0, 0, 0); + EXPECT_CALL(*backend_, ProcessRegisterRequest(_, _, _, _)).Times(2); + EXPECT_CALL(*backend_, ProcessPolicyRequest(_, _, _, _)).Times(3); + SimulateSuccessfulLoginAndRunPending(); +} + +TEST_F(DeviceManagementPolicyProviderTest, RefreshPolicies) { + backend_->AllWorksFirstPolicyFailsLater(); + SetRefreshDelays(provider_.get(), 0, 0, 1000 * 1000, 1000); + EXPECT_CALL(*backend_, ProcessRegisterRequest(_, _, _, _)).Times(1); + EXPECT_CALL(*backend_, ProcessPolicyRequest(_, _, _, _)).Times(4); + SimulateSuccessfulLoginAndRunPending(); } + +} // namespace policy diff --git a/chrome/browser/policy/device_token_fetcher.cc b/chrome/browser/policy/device_token_fetcher.cc index 1952747..602550f 100644 --- a/chrome/browser/policy/device_token_fetcher.cc +++ b/chrome/browser/policy/device_token_fetcher.cc @@ -87,11 +87,25 @@ void DeviceTokenFetcher::OnError(DeviceManagementBackend::ErrorCode code) { if (code == DeviceManagementBackend::kErrorServiceManagementNotSupported) { device_token_ = std::string(); device_id_ = std::string(); - file_util::Delete(token_path_, false); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + // The Windows compiler needs explicit template instantiation. + NewRunnableFunction<bool(*)(const FilePath&, bool), FilePath, bool>( + &file_util::Delete, token_path_, false)); + SetState(kStateNotManaged); + return; } SetState(kStateFailure); } +void DeviceTokenFetcher::Restart() { + DCHECK(!IsTokenPending()); + device_token_.clear(); + device_token_load_complete_event_.Reset(); + MakeReadyToRequestDeviceToken(); +} + void DeviceTokenFetcher::StartFetching() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (state_ == kStateNotStarted) { @@ -186,12 +200,13 @@ void DeviceTokenFetcher::SetState(FetcherState state) { state_ = state; if (state == kStateFailure) { device_token_load_complete_event_.Signal(); + NotifyTokenError(); + } else if (state == kStateNotManaged) { + device_token_load_complete_event_.Signal(); + NotifyNotManaged(); } else if (state == kStateHasDeviceToken) { device_token_load_complete_event_.Signal(); - NotificationService::current()->Notify( - NotificationType::DEVICE_TOKEN_AVAILABLE, - Source<DeviceTokenFetcher>(this), - NotificationService::NoDetails()); + NotifyTokenSuccess(); } } diff --git a/chrome/browser/policy/device_token_fetcher.h b/chrome/browser/policy/device_token_fetcher.h index 7704d35..1bfff13 100644 --- a/chrome/browser/policy/device_token_fetcher.h +++ b/chrome/browser/policy/device_token_fetcher.h @@ -7,8 +7,10 @@ #pragma once #include <string> +#include <vector> #include "base/file_path.h" +#include "base/observer_list.h" #include "base/ref_counted.h" #include "base/waitable_event.h" #include "chrome/browser/policy/device_management_backend.h" @@ -31,6 +33,38 @@ class DeviceTokenFetcher public DeviceManagementBackend::DeviceRegisterResponseDelegate, public base::RefCountedThreadSafe<DeviceTokenFetcher> { public: + class Observer { + public: + virtual void OnTokenSuccess() = 0; + virtual void OnTokenError() = 0; + virtual void OnNotManaged() = 0; + virtual ~Observer() {} + }; + + class ObserverRegistrar { + public: + void Init(DeviceTokenFetcher* token_fetcher) { + token_fetcher_ = token_fetcher; + } + ~ObserverRegistrar() { + RemoveAll(); + } + void AddObserver(DeviceTokenFetcher::Observer* observer) { + observers_.push_back(observer); + token_fetcher_->AddObserver(observer); + } + void RemoveAll() { + for (std::vector<DeviceTokenFetcher::Observer*>::iterator it = + observers_.begin(); it != observers_.end(); ++it) { + token_fetcher_->RemoveObserver(*it); + } + observers_.clear(); + } + private: + DeviceTokenFetcher* token_fetcher_; + std::vector<DeviceTokenFetcher::Observer*> observers_; + }; + // Requests to the device management server are sent through |backend|. It // obtains the authentication token from |token_service|. The fetcher stores // the device token to |token_path| once it's retrieved from the server. @@ -49,6 +83,9 @@ class DeviceTokenFetcher const em::DeviceRegisterResponse& response); virtual void OnError(DeviceManagementBackend::ErrorCode code); + // Re-initializes this DeviceTokenFetcher + void Restart(); + // Called by subscribers of the device management token to indicate that they // will need the token in the future. Must be called on the UI thread. void StartFetching(); @@ -87,7 +124,8 @@ class DeviceTokenFetcher kStateReadyToRequestDeviceTokenFromServer, kStateRequestingDeviceTokenFromServer, kStateHasDeviceToken, - kStateFailure + kStateFailure, + kStateNotManaged, }; // Moves the fetcher into a new state. If the fetcher has the device token @@ -114,6 +152,26 @@ class DeviceTokenFetcher // available. void SendServerRequestIfPossible(); + void AddObserver(Observer* obs) { + observer_list_.AddObserver(obs); + } + + void RemoveObserver(Observer* obs) { + observer_list_.RemoveObserver(obs); + } + + void NotifyTokenSuccess() { + FOR_EACH_OBSERVER(Observer, observer_list_, OnTokenSuccess()); + } + + void NotifyTokenError() { + FOR_EACH_OBSERVER(Observer, observer_list_, OnTokenError()); + } + + void NotifyNotManaged() { + FOR_EACH_OBSERVER(Observer, observer_list_, OnNotManaged()); + } + // Saves the device management token to disk once it has been retrieved from // the server. Must be called on the FILE thread. static void WriteDeviceTokenToDisk(const FilePath& path, @@ -124,6 +182,7 @@ class DeviceTokenFetcher // management server and generate the device token. static std::string GenerateNewDeviceID(); + ObserverList<Observer, true> observer_list_; FilePath token_path_; DeviceManagementBackend* backend_; // weak TokenService* token_service_; diff --git a/chrome/browser/policy/device_token_fetcher_unittest.cc b/chrome/browser/policy/device_token_fetcher_unittest.cc index 7546286..393195c 100644 --- a/chrome/browser/policy/device_token_fetcher_unittest.cc +++ b/chrome/browser/policy/device_token_fetcher_unittest.cc @@ -23,15 +23,14 @@ const char kTestToken[] = "device_token_fetcher_test_auth_token"; using testing::_; using testing::Mock; -class MockTokenAvailableObserver : public NotificationObserver { +class MockTokenAvailableObserver : public DeviceTokenFetcher::Observer { public: MockTokenAvailableObserver() {} virtual ~MockTokenAvailableObserver() {} - MOCK_METHOD3(Observe, void( - NotificationType type, - const NotificationSource& source, - const NotificationDetails& details)); + MOCK_METHOD0(OnTokenSuccess, void()); + MOCK_METHOD0(OnTokenError, void()); + MOCK_METHOD0(OnNotManaged, void()); private: DISALLOW_COPY_AND_ASSIGN(MockTokenAvailableObserver); @@ -142,12 +141,12 @@ TEST_F(DeviceTokenFetcherTest, SimpleFetchDoubleLogin) { } TEST_F(DeviceTokenFetcherTest, FetchBetweenBrowserLaunchAndNotify) { - NotificationRegistrar registrar; MockTokenAvailableObserver observer; - registrar.Add(&observer, - NotificationType::DEVICE_TOKEN_AVAILABLE, - NotificationService::AllSources()); - EXPECT_CALL(observer, Observe(_, _, _)).Times(1); + DeviceTokenFetcher::ObserverRegistrar registrar; + registrar.Init(fetcher_); + registrar.AddObserver(&observer); + EXPECT_CALL(observer, OnTokenSuccess()).Times(1); + EXPECT_CALL(observer, OnTokenError()).Times(0); backend_->AllShouldSucceed(); EXPECT_CALL(*backend_, ProcessRegisterRequest(_, _, _, _)).Times(1); SimulateSuccessfulLoginAndRunPending(); @@ -158,8 +157,9 @@ TEST_F(DeviceTokenFetcherTest, FetchBetweenBrowserLaunchAndNotify) { // Swap out the fetchers, including copying the device management token on // disk to where the new fetcher expects it. - fetcher_ = NewTestFetcher( - temp_user_data_dir_.path()); + registrar.RemoveAll(); + fetcher_ = NewTestFetcher(temp_user_data_dir_.path()); + registrar.Init(fetcher_); fetcher_->StartFetching(); ASSERT_TRUE(fetcher_->IsTokenPending()); loop_.RunAllPending(); @@ -170,6 +170,12 @@ TEST_F(DeviceTokenFetcherTest, FetchBetweenBrowserLaunchAndNotify) { } TEST_F(DeviceTokenFetcherTest, FailedServerRequest) { + MockTokenAvailableObserver observer; + DeviceTokenFetcher::ObserverRegistrar registrar; + registrar.Init(fetcher_); + registrar.AddObserver(&observer); + EXPECT_CALL(observer, OnTokenSuccess()).Times(0); + EXPECT_CALL(observer, OnTokenError()).Times(1); backend_->AllShouldFail(); EXPECT_CALL(*backend_, ProcessRegisterRequest(_, _, _, _)).Times(1); SimulateSuccessfulLoginAndRunPending(); @@ -179,14 +185,16 @@ TEST_F(DeviceTokenFetcherTest, FailedServerRequest) { } TEST_F(DeviceTokenFetcherTest, UnmanagedDevice) { + FilePath token_path; + GetDeviceTokenPath(fetcher_, &token_path); + file_util::WriteFile(token_path, "foo", 3); + ASSERT_TRUE(file_util::PathExists(token_path)); backend_->UnmanagedDevice(); EXPECT_CALL(*backend_, ProcessRegisterRequest(_, _, _, _)).Times(1); SimulateSuccessfulLoginAndRunPending(); ASSERT_FALSE(fetcher_->IsTokenPending()); ASSERT_EQ("", fetcher_->GetDeviceToken()); ASSERT_EQ("", device_id(fetcher_)); - FilePath token_path; - GetDeviceTokenPath(fetcher_, &token_path); ASSERT_FALSE(file_util::PathExists(token_path)); } diff --git a/chrome/browser/policy/mock_device_management_backend.cc b/chrome/browser/policy/mock_device_management_backend.cc index 05d2c3c..ca61feb 100644 --- a/chrome/browser/policy/mock_device_management_backend.cc +++ b/chrome/browser/policy/mock_device_management_backend.cc @@ -28,7 +28,10 @@ using em::DeviceUnregisterResponse; using em::DevicePolicyResponse; MockDeviceManagementBackend::MockDeviceManagementBackend() - : DeviceManagementBackend() { + : DeviceManagementBackend(), + policy_remaining_fail_count_(0), + register_remaining_fail_count_(0), + policy_remaining_success_count_(0) { policy_setting_ = policy_response_.add_setting(); policy_setting_->set_policy_key("chrome-policy"); policy_setting_->set_watermark("fresh"); @@ -66,6 +69,17 @@ void MockDeviceManagementBackend::UnmanagedDevice() { &MockDeviceManagementBackend::SimulateUnmanagedRegisterRequest)); } +void MockDeviceManagementBackend::RegisterFailsOncePolicyFailsTwice() { + register_remaining_fail_count_ = 1; + policy_remaining_fail_count_ = 2; + AllShouldFail(); +} + +void MockDeviceManagementBackend::AllWorksFirstPolicyFailsLater() { + policy_remaining_success_count_ = 3; + AllShouldSucceed(); +} + void MockDeviceManagementBackend::AddBooleanPolicy(const char* policy_name, bool value) { em::GenericSetting* policy_value = policy_setting_->mutable_policy_value(); @@ -93,6 +107,15 @@ void MockDeviceManagementBackend::SimulateSuccessfulPolicyRequest( const std::string& device_id, const em::DevicePolicyRequest& request, DevicePolicyResponseDelegate* delegate) { + if (policy_remaining_success_count_) { + policy_remaining_success_count_--; + if (!policy_remaining_success_count_) { + ON_CALL(*this, ProcessPolicyRequest(_, _, _, _)). + WillByDefault(Invoke( + this, + &MockDeviceManagementBackend::SimulateFailedPolicyRequest)); + } + } delegate->HandlePolicyResponse(policy_response_); } @@ -101,6 +124,14 @@ void MockDeviceManagementBackend::SimulateFailedRegisterRequest( const std::string& device_id, const em::DeviceRegisterRequest& request, DeviceRegisterResponseDelegate* delegate) { + if (register_remaining_fail_count_) { + register_remaining_fail_count_--; + if (!register_remaining_fail_count_) { + ON_CALL(*this, ProcessRegisterRequest(_, _, _, _)).WillByDefault(Invoke( + this, + &MockDeviceManagementBackend::SimulateSuccessfulRegisterRequest)); + } + } delegate->OnError(kErrorRequestFailed); } @@ -109,6 +140,15 @@ void MockDeviceManagementBackend::SimulateFailedPolicyRequest( const std::string& device_id, const em::DevicePolicyRequest& request, DevicePolicyResponseDelegate* delegate) { + if (policy_remaining_fail_count_) { + policy_remaining_fail_count_--; + if (!policy_remaining_fail_count_) { + ON_CALL(*this, ProcessPolicyRequest(_, _, _, _)). + WillByDefault(Invoke( + this, + &MockDeviceManagementBackend::SimulateSuccessfulPolicyRequest)); + } + } delegate->OnError(kErrorRequestFailed); } diff --git a/chrome/browser/policy/mock_device_management_backend.h b/chrome/browser/policy/mock_device_management_backend.h index 6b3258c..9371dc0 100644 --- a/chrome/browser/policy/mock_device_management_backend.h +++ b/chrome/browser/policy/mock_device_management_backend.h @@ -11,8 +11,8 @@ #include "base/values.h" #include "chrome/browser/policy/device_management_backend.h" -#include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" namespace policy { @@ -49,6 +49,8 @@ class MockDeviceManagementBackend void AllShouldSucceed(); void AllShouldFail(); void UnmanagedDevice(); + void RegisterFailsOncePolicyFailsTwice(); + void AllWorksFirstPolicyFailsLater(); void SimulateSuccessfulRegisterRequest( const std::string& auth_token, @@ -86,6 +88,10 @@ class MockDeviceManagementBackend em::DevicePolicyResponse policy_response_; em::DevicePolicySetting* policy_setting_; + int policy_remaining_fail_count_; + int register_remaining_fail_count_; + int policy_remaining_success_count_; + DISALLOW_COPY_AND_ASSIGN(MockDeviceManagementBackend); }; |