// Copyright (c) 2011 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/cloud_policy_controller.h"
#include "base/memory/scoped_temp_dir.h"
#include "base/message_loop.h"
#include "chrome/browser/policy/device_management_service.h"
#include "chrome/browser/policy/device_token_fetcher.h"
#include "chrome/browser/policy/mock_configuration_policy_store.h"
#include "chrome/browser/policy/mock_device_management_backend.h"
#include "chrome/browser/policy/mock_device_management_service.h"
#include "chrome/browser/policy/policy_notifier.h"
#include "chrome/browser/policy/proto/device_management_backend.pb.h"
#include "chrome/browser/policy/user_policy_cache.h"
#include "content/browser/browser_thread.h"
#include "policy/policy_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
const char kTestToken[] = "cloud_policy_controller_test_auth_token";
namespace policy {
namespace em = enterprise_management;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::InSequence;
using ::testing::Mock;
using ::testing::Return;
class MockCloudPolicyIdentityStrategy : public CloudPolicyIdentityStrategy {
public:
MockCloudPolicyIdentityStrategy() {}
virtual ~MockCloudPolicyIdentityStrategy() {}
MOCK_METHOD0(GetDeviceToken, std::string());
MOCK_METHOD0(GetDeviceID, std::string());
MOCK_METHOD0(GetMachineID, std::string());
MOCK_METHOD0(GetMachineModel, std::string());
MOCK_METHOD0(GetPolicyType, std::string());
MOCK_METHOD0(GetPolicyRegisterType, em::DeviceRegisterRequest_Type());
MOCK_METHOD2(GetCredentials, bool(std::string*, std::string*));
virtual void OnDeviceTokenAvailable(const std::string&) {}
private:
DISALLOW_COPY_AND_ASSIGN(MockCloudPolicyIdentityStrategy);
};
ACTION_P2(MockCloudPolicyIdentityStrategyGetCredentials, username, auth_token) {
*arg0 = username;
*arg1 = auth_token;
return true;
}
class MockDeviceTokenFetcher : public DeviceTokenFetcher {
public:
explicit MockDeviceTokenFetcher(CloudPolicyCacheBase* cache)
: DeviceTokenFetcher(NULL, cache, NULL) {}
virtual ~MockDeviceTokenFetcher() {}
MOCK_METHOD0(GetDeviceToken, const std::string&());
MOCK_METHOD5(FetchToken,
void(const std::string&, const std::string&,
em::DeviceRegisterRequest_Type,
const std::string&, const std::string&));
MOCK_METHOD0(SetUnmanagedState, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockDeviceTokenFetcher);
};
class CloudPolicyControllerTest : public testing::Test {
public:
CloudPolicyControllerTest()
: ui_thread_(BrowserThread::UI, &loop_),
file_thread_(BrowserThread::FILE, &loop_) {}
virtual ~CloudPolicyControllerTest() {}
virtual void SetUp() {
ASSERT_TRUE(temp_user_data_dir_.CreateUniqueTempDir());
cache_.reset(new UserPolicyCache(
temp_user_data_dir_.path().AppendASCII("CloudPolicyControllerTest")));
token_fetcher_.reset(new MockDeviceTokenFetcher(cache_.get()));
service_.set_backend(&backend_);
}
virtual void TearDown() {
controller_.reset(); // Unregisters observers.
}
// Takes ownership of |backend|.
void CreateNewController() {
controller_.reset(new CloudPolicyController(
&service_, cache_.get(), token_fetcher_.get(), &identity_strategy_,
¬ifier_));
}
void CreateNewController(int64 policy_refresh_rate_ms,
int policy_refresh_deviation_factor_percent,
int64 policy_refresh_deviation_max_ms,
int64 policy_refresh_error_delay_ms) {
controller_.reset(new CloudPolicyController(
&service_, cache_.get(), token_fetcher_.get(), &identity_strategy_,
¬ifier_,
policy_refresh_rate_ms,
policy_refresh_deviation_factor_percent,
policy_refresh_deviation_max_ms,
policy_refresh_error_delay_ms));
}
void ExpectHasSpdyPolicy() {
MockConfigurationPolicyStore store;
EXPECT_CALL(store, Apply(_, _)).Times(AtLeast(1));
cache_->GetManagedPolicyProvider()->Provide(&store);
FundamentalValue expected(true);
ASSERT_TRUE(store.Get(kPolicyDisableSpdy) != NULL);
EXPECT_TRUE(store.Get(kPolicyDisableSpdy)->Equals(&expected));
}
void SetupIdentityStrategy(
const std::string& device_token,
const std::string& device_id,
const std::string& machine_id,
const std::string& machine_model,
const std::string& policy_type,
const em::DeviceRegisterRequest_Type& policy_register_type,
const std::string& user_name,
const std::string& auth_token) {
EXPECT_CALL(identity_strategy_, GetDeviceToken()).WillRepeatedly(
Return(device_token));
EXPECT_CALL(identity_strategy_, GetDeviceID()).WillRepeatedly(
Return(device_id));
EXPECT_CALL(identity_strategy_, GetMachineID()).WillRepeatedly(
Return(machine_id));
EXPECT_CALL(identity_strategy_, GetMachineModel()).WillRepeatedly(
Return(machine_model));
EXPECT_CALL(identity_strategy_, GetPolicyType()).WillRepeatedly(
Return(policy_type));
EXPECT_CALL(identity_strategy_, GetPolicyRegisterType()).WillRepeatedly(
Return(policy_register_type));
if (!user_name.empty()) {
EXPECT_CALL(identity_strategy_, GetCredentials(_, _)).WillRepeatedly(
MockCloudPolicyIdentityStrategyGetCredentials(user_name, auth_token));
}
}
protected:
scoped_ptr<CloudPolicyCacheBase> cache_;
scoped_ptr<CloudPolicyController> controller_;
scoped_ptr<MockDeviceTokenFetcher> token_fetcher_;
MockCloudPolicyIdentityStrategy identity_strategy_;
MockDeviceManagementBackend backend_;
MockDeviceManagementService service_;
PolicyNotifier notifier_;
ScopedTempDir temp_user_data_dir_;
MessageLoop loop_;
private:
BrowserThread ui_thread_;
BrowserThread file_thread_;
DISALLOW_COPY_AND_ASSIGN(CloudPolicyControllerTest);
};
// If a device token is present when the controller starts up, it should
// fetch and apply policy.
TEST_F(CloudPolicyControllerTest, StartupWithDeviceToken) {
SetupIdentityStrategy("fake_device_token", "device_id", "machine_id",
"machine_model", "google/chromeos/user",
em::DeviceRegisterRequest::USER, "", "");
EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _)).WillOnce(
MockDeviceManagementBackendSucceedSpdyCloudPolicy());
CreateNewController();
loop_.RunAllPending();
ExpectHasSpdyPolicy();
}
// If no device token is present when the controller starts up, it should
// instruct the token_fetcher_ to fetch one.
TEST_F(CloudPolicyControllerTest, StartupWithoutDeviceToken) {
SetupIdentityStrategy("", "device_id", "machine_id", "machine_model",
"google/chromeos/user", em::DeviceRegisterRequest::USER,
"a@b.com", "auth_token");
EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _, _, _, _)).Times(1);
CreateNewController();
loop_.RunAllPending();
}
// If the current user belongs to a known non-managed domain, no token fetch
// should be initiated.
TEST_F(CloudPolicyControllerTest, StartupUnmanagedUser) {
SetupIdentityStrategy("", "device_id", "machine_id", "machine_mode",
"google/chromeos/user", em::DeviceRegisterRequest::USER,
"DannoHelper@gmail.com", "auth_token");
EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _, _, _, _)).Times(0);
CreateNewController();
loop_.RunAllPending();
}
// After policy has been fetched successfully, a new fetch should be triggered
// after the refresh interval has timed out.
TEST_F(CloudPolicyControllerTest, RefreshAfterSuccessfulPolicy) {
SetupIdentityStrategy("device_token", "device_id", "machine_id",
"machine_model", "google/chromeos/user",
em::DeviceRegisterRequest::USER,
"DannoHelperDelegate@b.com", "auth_token");
EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _)).WillOnce(
MockDeviceManagementBackendSucceedSpdyCloudPolicy()).WillOnce(
MockDeviceManagementBackendFailPolicy(
DeviceManagementBackend::kErrorRequestFailed));
CreateNewController(0, 0, 0, 1000 * 1000);
loop_.RunAllPending();
ExpectHasSpdyPolicy();
}
// If policy fetching failed, it should be retried.
TEST_F(CloudPolicyControllerTest, RefreshAfterError) {
SetupIdentityStrategy("device_token", "device_id", "machine_id",
"machine_model", "google/chromeos/user",
em::DeviceRegisterRequest::USER,
"DannoHelperDelegateImpl@b.com", "auth_token");
EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _)).WillOnce(
MockDeviceManagementBackendFailPolicy(
DeviceManagementBackend::kErrorRequestFailed)).WillOnce(
MockDeviceManagementBackendSucceedSpdyCloudPolicy());
CreateNewController(1000 * 1000, 0, 0, 0);
loop_.RunAllPending();
ExpectHasSpdyPolicy();
}
// If the backend reports that the device token was invalid, the controller
// should instruct the token fetcher to fetch a new token.
TEST_F(CloudPolicyControllerTest, InvalidToken) {
SetupIdentityStrategy("device_token", "device_id", "machine_id",
"machine_model", "google/chromeos/user",
em::DeviceRegisterRequest::USER,
"standup@ten.am", "auth");
EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _)).WillOnce(
MockDeviceManagementBackendFailPolicy(
DeviceManagementBackend::kErrorServiceManagementTokenInvalid));
EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _, _, _, _)).Times(1);
CreateNewController(1000 * 1000, 0, 0, 0);
loop_.RunAllPending();
}
// If the backend reports that the device is unknown to the server, the
// controller should instruct the token fetcher to fetch a new token.
TEST_F(CloudPolicyControllerTest, DeviceNotFound) {
SetupIdentityStrategy("device_token", "device_id", "machine_id",
"machine_model", "google/chromeos/user",
em::DeviceRegisterRequest::USER,
"me@you.com", "auth");
EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _)).WillOnce(
MockDeviceManagementBackendFailPolicy(
DeviceManagementBackend::kErrorServiceDeviceNotFound));
EXPECT_CALL(*token_fetcher_.get(), FetchToken(_, _, _, _, _)).Times(1);
CreateNewController(1000 * 1000, 0, 0, 0);
loop_.RunAllPending();
}
// If the backend reports that the device is no longer managed, the controller
// should instruct the token fetcher to fetch a new token (which will in turn
// set and persist the correct 'unmanaged' state).
TEST_F(CloudPolicyControllerTest, NoLongerManaged) {
SetupIdentityStrategy("device_token", "device_id", "machine_id",
"machine_model", "google/chromeos/user",
em::DeviceRegisterRequest::USER,
"who@what.com", "auth");
EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _)).WillOnce(
MockDeviceManagementBackendFailPolicy(
DeviceManagementBackend::kErrorServiceManagementNotSupported));
EXPECT_CALL(*token_fetcher_.get(), SetUnmanagedState()).Times(1);
CreateNewController(0, 0, 0, 1000 * 1000);
loop_.RunAllPending();
}
} // namespace policy