blob: cff3896c20d65aac739111a0fe26cd5b15dfcc97 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/enterprise_companion/dm_client.h"
#include <memory>
#include <string>
#include <utility>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "chrome/enterprise_companion/device_management_storage/dm_storage.h"
#include "chrome/enterprise_companion/enterprise_companion_status.h"
#include "chrome/enterprise_companion/event_logger.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/cloud_policy_validator.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/policy/core/common/cloud/mock_device_management_service.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace enterprise_companion {
namespace {
constexpr char kFakeDeviceId[] = "test-device-id";
constexpr char kFakeEnrollmentToken[] = "TestEnrollmentToken";
constexpr char kFakeDMToken[] = "test-dm-token";
constexpr char kPolicyType1[] = "google/test-policy-type-1";
constexpr char kPolicyValue1[] = "test policy value 1";
constexpr char kPolicyType2[] = "google/test-policy-type-2";
constexpr char kPolicyValue2[] = "test policy value 2";
constexpr char kPublicKey1[] = "new public key 1";
constexpr char kPolicyValue3[] = "test policy value 3";
constexpr int kPublicKey1Version = 100;
constexpr int kTimestamp1 = 42;
constexpr int kTimestamp2 = 84;
using ::testing::ElementsAre;
// Wraps a real DM storage instance allowing behavior to be augmented by tests.
class TestDMStorage final : public device_management_storage::DMStorage {
public:
TestDMStorage(
const base::FilePath& policy_cache_root,
std::unique_ptr<device_management_storage::TokenServiceInterface>
token_service)
: storage_(device_management_storage::CreateDMStorage(
policy_cache_root,
std::move(token_service))) {}
void SetCanPersistPolicies(bool can_persist_policies) {
can_persist_policies_ = can_persist_policies;
}
void SetWillPersistPolicies(bool will_persist_policies) {
will_persist_policies_ = will_persist_policies;
}
// Overrides for DMStorage.
bool CanPersistPolicies() const override { return can_persist_policies_; }
bool PersistPolicies(
const device_management_storage::DMPolicyMap& policy_map) const override {
return will_persist_policies_ ? storage_->PersistPolicies(policy_map)
: false;
}
std::string GetDeviceID() const override { return storage_->GetDeviceID(); }
bool IsEnrollmentMandatory() const override {
return storage_->IsEnrollmentMandatory();
}
bool StoreEnrollmentToken(const std::string& enrollment_token) override {
return storage_->StoreEnrollmentToken(enrollment_token);
}
bool DeleteEnrollmentToken() override {
return storage_->DeleteEnrollmentToken();
}
std::string GetEnrollmentToken() const override {
return storage_->GetEnrollmentToken();
}
bool StoreDmToken(const std::string& dm_token) override {
return storage_->StoreDmToken(dm_token);
}
std::string GetDmToken() const override { return storage_->GetDmToken(); }
bool InvalidateDMToken() override { return storage_->InvalidateDMToken(); }
bool DeleteDMToken() override { return storage_->DeleteDMToken(); }
bool IsValidDMToken() const override { return storage_->IsValidDMToken(); }
bool IsDeviceDeregistered() const override {
return storage_->IsDeviceDeregistered();
}
bool RemoveAllPolicies() const override {
return storage_->RemoveAllPolicies();
}
std::unique_ptr<device_management_storage::CachedPolicyInfo>
GetCachedPolicyInfo() const override {
return storage_->GetCachedPolicyInfo();
}
std::optional<enterprise_management::PolicyData> ReadPolicyData(
const std::string& policy_type) override {
return storage_->ReadPolicyData(policy_type);
}
base::FilePath policy_cache_folder() const override {
return storage_->policy_cache_folder();
}
private:
scoped_refptr<device_management_storage::DMStorage> storage_;
bool can_persist_policies_ = true;
bool will_persist_policies_ = true;
~TestDMStorage() override = default;
};
class MockCloudPolicyClient : public policy::MockCloudPolicyClient {
public:
explicit MockCloudPolicyClient(policy::DeviceManagementService* service)
: policy::MockCloudPolicyClient(service) {}
MOCK_METHOD(void,
RegisterBrowserWithEnrollmentToken,
(const std::string& token,
const std::string& client_id,
const policy::ClientDataDelegate& client_data_delegate,
bool is_mandatory),
(override));
};
class TestTokenService
: public device_management_storage::TokenServiceInterface {
public:
~TestTokenService() override = default;
// Overrides for TokenServiceInterface.
std::string GetDeviceID() const override { return kFakeDeviceId; }
bool IsEnrollmentMandatory() const override { return false; }
bool StoreEnrollmentToken(const std::string& enrollment_token) override {
enrollment_token_ = enrollment_token;
return true;
}
bool DeleteEnrollmentToken() override { return StoreEnrollmentToken(""); }
std::string GetEnrollmentToken() const override { return enrollment_token_; }
bool StoreDmToken(const std::string& dm_token) override {
dm_token_ = dm_token;
return true;
}
bool DeleteDmToken() override {
dm_token_.clear();
return true;
}
std::string GetDmToken() const override { return dm_token_; }
private:
std::string enrollment_token_;
std::string dm_token_;
};
class TestEventLogger : public EventLogger {
public:
const std::vector<EnterpriseCompanionStatus>& registration_events() {
return registration_events_;
}
const std::vector<EnterpriseCompanionStatus>& policy_fetch_events() {
return policy_fetch_events_;
}
// Overrides for EventLogger.
void Flush() override {}
OnEnrollmentFinishCallback OnEnrollmentStart() override {
return base::BindOnce(
[](scoped_refptr<TestEventLogger> logger,
const EnterpriseCompanionStatus& status) {
logger->registration_events_.push_back(status);
},
base::WrapRefCounted(this));
}
OnPolicyFetchFinishCallback OnPolicyFetchStart() override {
return base::BindOnce(
[](scoped_refptr<TestEventLogger> logger,
const EnterpriseCompanionStatus& status) {
logger->policy_fetch_events_.push_back(status);
},
base::WrapRefCounted(this));
}
private:
std::vector<EnterpriseCompanionStatus> registration_events_;
std::vector<EnterpriseCompanionStatus> policy_fetch_events_;
~TestEventLogger() override = default;
};
} // namespace
class DMClientTest : public ::testing::Test {
public:
void SetUp() override {
ASSERT_TRUE(storage_dir_.CreateUniqueTempDir());
std::unique_ptr<TestTokenService> test_token_service =
std::make_unique<TestTokenService>();
test_token_service_ = test_token_service.get();
mock_cloud_policy_client_ =
new MockCloudPolicyClient(&fake_device_management_service_);
dm_storage_ = base::MakeRefCounted<TestDMStorage>(
storage_dir_.GetPath(), std::move(test_token_service));
dm_client_ = CreateDMClient(
base::BindLambdaForTesting([&](policy::DeviceManagementService*) {
return base::WrapUnique(static_cast<policy::CloudPolicyClient*>(
mock_cloud_policy_client_));
}),
dm_storage_, mock_policy_fetch_response_validator_.Get());
}
protected:
base::test::TaskEnvironment environment_;
base::ScopedTempDir storage_dir_;
policy::MockJobCreationHandler mock_job_creation_handler_;
policy::FakeDeviceManagementService fake_device_management_service_ =
policy::FakeDeviceManagementService(&mock_job_creation_handler_);
scoped_refptr<TestDMStorage> dm_storage_;
// |test_token_service_| and |mock_cloud_policy_client_| are pointers to
// objects owned by |dm_client_|. They must be destructed before the client to
// avoid raw_ptr from complaining about dangling pointers.
std::unique_ptr<DMClient> dm_client_;
raw_ptr<TestTokenService> test_token_service_ = nullptr;
raw_ptr<MockCloudPolicyClient> mock_cloud_policy_client_ = nullptr;
base::MockCallback<PolicyFetchResponseValidator>
mock_policy_fetch_response_validator_;
scoped_refptr<TestEventLogger> test_event_logger_ =
base::MakeRefCounted<TestEventLogger>();
// Ensure a valid registration state by storing an enrollment token, fake
// DM token, and configuring the cloud policy client.
void EnsureRegistered() {
test_token_service_->StoreEnrollmentToken(kFakeEnrollmentToken);
test_token_service_->StoreDmToken(kFakeDMToken);
mock_cloud_policy_client_->dm_token_ = kFakeDMToken;
ASSERT_TRUE(mock_cloud_policy_client_->is_registered());
}
void SetMockPolicyFetchResponseValidatorResult(
policy::CloudPolicyValidatorBase::Status status) {
EXPECT_CALL(mock_policy_fetch_response_validator_, Run)
.WillRepeatedly(
[status](const std::string&, const std::string&, const std::string&,
int64_t,
const enterprise_management::PolicyFetchResponse&) {
auto result = std::make_unique<
policy::CloudPolicyValidatorBase::ValidationResult>();
result->status = status;
return result;
});
}
};
TEST_F(DMClientTest, RegisterDeviceSuccess) {
test_token_service_->StoreEnrollmentToken(kFakeEnrollmentToken);
EXPECT_CALL(*mock_cloud_policy_client_,
RegisterBrowserWithEnrollmentToken(
kFakeEnrollmentToken, kFakeDeviceId, testing::_, false))
.Times(1);
base::RunLoop run_loop;
dm_client_->RegisterBrowser(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.ok());
}).Then(run_loop.QuitClosure()));
mock_cloud_policy_client_->SetDMToken(kFakeDMToken);
mock_cloud_policy_client_->NotifyRegistrationStateChanged();
run_loop.Run();
EXPECT_EQ(test_token_service_->GetDmToken(), kFakeDMToken);
EXPECT_THAT(test_event_logger_->registration_events(),
ElementsAre(EnterpriseCompanionStatus::Success()));
EXPECT_TRUE(test_event_logger_->policy_fetch_events().empty());
}
TEST_F(DMClientTest, RegisterDeviceFailure) {
test_token_service_->StoreEnrollmentToken(kFakeEnrollmentToken);
EXPECT_CALL(*mock_cloud_policy_client_,
RegisterBrowserWithEnrollmentToken(
kFakeEnrollmentToken, kFakeDeviceId, testing::_, false))
.Times(1);
base::RunLoop run_loop;
dm_client_->RegisterBrowser(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.EqualsDeviceManagementStatus(
policy::DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER));
}).Then(run_loop.QuitClosure()));
mock_cloud_policy_client_->SetStatus(
policy::DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER);
mock_cloud_policy_client_->NotifyClientError();
run_loop.Run();
EXPECT_TRUE(test_token_service_->GetDmToken().empty());
EXPECT_THAT(test_event_logger_->registration_events(),
ElementsAre(EnterpriseCompanionStatus::FromDeviceManagementStatus(
policy::DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER)));
EXPECT_TRUE(test_event_logger_->policy_fetch_events().empty());
}
TEST_F(DMClientTest, RegistrationRemovesPolicies) {
// Store some policies, a DM token must be preset to serialize the data.
test_token_service_->StoreDmToken(kFakeDMToken);
::enterprise_management::PolicyFetchResponse fake_response;
::enterprise_management::PolicyData fake_policy_data;
fake_policy_data.set_policy_value(kPolicyValue1);
fake_response.set_policy_data(fake_policy_data.SerializeAsString());
ASSERT_TRUE(dm_storage_->CanPersistPolicies());
ASSERT_TRUE(dm_storage_->PersistPolicies(
{{kPolicyType1, fake_response.SerializeAsString()}}));
ASSERT_TRUE(dm_storage_->ReadPolicyData(kPolicyType1));
// Delete the DM token to ensure registration is not skipped.
test_token_service_->DeleteDmToken();
test_token_service_->StoreEnrollmentToken(kFakeEnrollmentToken);
EXPECT_CALL(*mock_cloud_policy_client_,
RegisterBrowserWithEnrollmentToken(
kFakeEnrollmentToken, kFakeDeviceId, testing::_, false))
.Times(1);
// Register the device. All policies should be removed as a side effect.
base::RunLoop run_loop;
dm_client_->RegisterBrowser(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.ok());
}).Then(run_loop.QuitClosure()));
mock_cloud_policy_client_->SetDMToken(kFakeDMToken);
mock_cloud_policy_client_->NotifyRegistrationStateChanged();
run_loop.Run();
EXPECT_FALSE(dm_storage_->ReadPolicyData(kPolicyType1));
EXPECT_THAT(test_event_logger_->registration_events(),
ElementsAre(EnterpriseCompanionStatus::Success()));
EXPECT_TRUE(test_event_logger_->policy_fetch_events().empty());
}
TEST_F(DMClientTest, RegistrationSkippedNoEnrollmentToken) {
EXPECT_CALL(*mock_cloud_policy_client_, RegisterBrowserWithEnrollmentToken)
.Times(0);
base::RunLoop run_loop;
dm_client_->RegisterBrowser(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.ok());
}).Then(run_loop.QuitClosure()));
run_loop.Run();
EXPECT_TRUE(test_token_service_->GetDmToken().empty());
}
TEST_F(DMClientTest, RegistrationSkippedAlreadyManaged) {
EnsureRegistered();
EXPECT_CALL(*mock_cloud_policy_client_, RegisterBrowserWithEnrollmentToken)
.Times(0);
base::RunLoop run_loop;
dm_client_->RegisterBrowser(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.ok());
}).Then(run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(test_token_service_->GetDmToken(), kFakeDMToken);
// Skipping registration should result in no logged events.
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_TRUE(test_event_logger_->policy_fetch_events().empty());
}
TEST_F(DMClientTest, PoliciesPersistedThroughSkippedRegistration) {
EnsureRegistered();
::enterprise_management::PolicyFetchResponse fake_response;
::enterprise_management::PolicyData fake_policy_data;
fake_policy_data.set_policy_value(kPolicyValue1);
fake_response.set_policy_data(fake_policy_data.SerializeAsString());
ASSERT_TRUE(dm_storage_->CanPersistPolicies());
ASSERT_TRUE(dm_storage_->PersistPolicies(
{{kPolicyType1, fake_response.SerializeAsString()}}));
ASSERT_TRUE(dm_storage_->ReadPolicyData(kPolicyType1));
// Delete the DM token to ensure registration is not skipped.
EXPECT_CALL(*mock_cloud_policy_client_, RegisterBrowserWithEnrollmentToken)
.Times(0);
// Registration should be skipped as DM token is still present.
base::RunLoop run_loop;
dm_client_->RegisterBrowser(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.ok());
}).Then(run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(test_token_service_->GetDmToken(), kFakeDMToken);
EXPECT_TRUE(dm_storage_->ReadPolicyData(kPolicyType1));
// Skipping registration should result in no logged events.
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_TRUE(test_event_logger_->policy_fetch_events().empty());
}
TEST_F(DMClientTest, FetchPoliciesFailsIfNotRegistered) {
base::RunLoop run_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.EqualsApplicationError(
ApplicationError::kRegistrationPreconditionFailed));
}).Then(run_loop.QuitClosure()));
run_loop.Run();
std::unique_ptr<device_management_storage::CachedPolicyInfo>
cached_policy_info = dm_storage_->GetCachedPolicyInfo();
EXPECT_TRUE(cached_policy_info->public_key().empty());
EXPECT_FALSE(cached_policy_info->has_key_version());
EXPECT_EQ(cached_policy_info->timestamp(), 0);
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_THAT(test_event_logger_->policy_fetch_events(),
ElementsAre(EnterpriseCompanionStatus(
ApplicationError::kRegistrationPreconditionFailed)));
}
TEST_F(DMClientTest, FetchPoliciesFailsIfDMStorageCannotPersist) {
EnsureRegistered();
dm_storage_->SetCanPersistPolicies(false);
base::RunLoop run_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.EqualsApplicationError(
ApplicationError::kPolicyPersistenceImpossible));
}).Then(run_loop.QuitClosure()));
run_loop.Run();
std::unique_ptr<device_management_storage::CachedPolicyInfo>
cached_policy_info = dm_storage_->GetCachedPolicyInfo();
EXPECT_TRUE(cached_policy_info->public_key().empty());
EXPECT_FALSE(cached_policy_info->has_key_version());
EXPECT_EQ(cached_policy_info->timestamp(), 0);
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_THAT(test_event_logger_->policy_fetch_events(),
ElementsAre(EnterpriseCompanionStatus(
ApplicationError::kPolicyPersistenceImpossible)));
}
TEST_F(DMClientTest, FetchPoliciesFailsIfCloudPolicyClientFails) {
EnsureRegistered();
EXPECT_CALL(*mock_cloud_policy_client_, FetchPolicy).Times(1);
base::RunLoop run_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.EqualsDeviceManagementStatus(
policy::DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID));
}).Then(run_loop.QuitClosure()));
mock_cloud_policy_client_->SetStatus(
policy::DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID);
mock_cloud_policy_client_->NotifyPolicyFetched();
run_loop.Run();
std::unique_ptr<device_management_storage::CachedPolicyInfo>
cached_policy_info = dm_storage_->GetCachedPolicyInfo();
EXPECT_TRUE(cached_policy_info->public_key().empty());
EXPECT_FALSE(cached_policy_info->has_key_version());
EXPECT_EQ(cached_policy_info->timestamp(), 0);
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_THAT(test_event_logger_->policy_fetch_events(),
ElementsAre(EnterpriseCompanionStatus::FromDeviceManagementStatus(
policy::DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID)));
}
TEST_F(DMClientTest, FetchPoliciesFailsIfFetchResultInvalid) {
EnsureRegistered();
EXPECT_CALL(*mock_cloud_policy_client_, FetchPolicy).Times(1);
SetMockPolicyFetchResponseValidatorResult(
policy::CloudPolicyValidatorBase::VALIDATION_POLICY_PARSE_ERROR);
base::RunLoop run_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.EqualsCloudPolicyValidationResult(
policy::CloudPolicyValidatorBase::VALIDATION_POLICY_PARSE_ERROR));
}).Then(run_loop.QuitClosure()));
mock_cloud_policy_client_->SetPolicy(
kPolicyType1, /*settings_entity_id=*/"",
enterprise_management::PolicyFetchResponse());
mock_cloud_policy_client_->NotifyPolicyFetched();
run_loop.Run();
std::unique_ptr<device_management_storage::CachedPolicyInfo>
cached_policy_info = dm_storage_->GetCachedPolicyInfo();
EXPECT_TRUE(cached_policy_info->public_key().empty());
EXPECT_FALSE(cached_policy_info->has_key_version());
EXPECT_EQ(cached_policy_info->timestamp(), 0);
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_THAT(
test_event_logger_->policy_fetch_events(),
ElementsAre(EnterpriseCompanionStatus::FromCloudPolicyValidationResult(
policy::CloudPolicyValidatorBase::VALIDATION_POLICY_PARSE_ERROR)));
}
TEST_F(DMClientTest, FetchPoliciesFailsIfResultCannotBePersisted) {
EnsureRegistered();
dm_storage_->SetWillPersistPolicies(false);
EXPECT_CALL(*mock_cloud_policy_client_, FetchPolicy).Times(1);
SetMockPolicyFetchResponseValidatorResult(
policy::CloudPolicyValidatorBase::VALIDATION_OK);
base::RunLoop run_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.EqualsApplicationError(
ApplicationError::kPolicyPersistenceFailed));
}).Then(run_loop.QuitClosure()));
mock_cloud_policy_client_->SetPolicy(
kPolicyType1, /*settings_entity_id=*/"",
enterprise_management::PolicyFetchResponse());
mock_cloud_policy_client_->NotifyPolicyFetched();
run_loop.Run();
std::unique_ptr<device_management_storage::CachedPolicyInfo>
cached_policy_info = dm_storage_->GetCachedPolicyInfo();
EXPECT_TRUE(cached_policy_info->public_key().empty());
EXPECT_FALSE(cached_policy_info->has_key_version());
EXPECT_EQ(cached_policy_info->timestamp(), 0);
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_THAT(test_event_logger_->policy_fetch_events(),
ElementsAre(EnterpriseCompanionStatus(
ApplicationError::kPolicyPersistenceFailed)));
}
TEST_F(DMClientTest, FetchPoliciesSuccess) {
EnsureRegistered();
EXPECT_CALL(*mock_cloud_policy_client_, FetchPolicy).Times(1);
SetMockPolicyFetchResponseValidatorResult(
policy::CloudPolicyValidatorBase::VALIDATION_OK);
enterprise_management::PublicKeyVerificationData key_verification_data;
key_verification_data.set_new_public_key(kPublicKey1);
key_verification_data.set_new_public_key_version(kPublicKey1Version);
enterprise_management::PolicyData data1;
data1.set_timestamp(kTimestamp1);
data1.set_policy_type(kPolicyType1);
data1.set_policy_value(kPolicyValue1);
enterprise_management::PolicyFetchResponse response1;
response1.set_policy_data(data1.SerializeAsString());
response1.set_new_public_key_verification_data(
key_verification_data.SerializeAsString());
enterprise_management::PolicyData data2;
data2.set_timestamp(kTimestamp1);
data2.set_policy_type(kPolicyType2);
data2.set_policy_value(kPolicyValue2);
enterprise_management::PolicyFetchResponse response2;
response2.set_policy_data(data2.SerializeAsString());
base::RunLoop run_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.ok());
}).Then(run_loop.QuitClosure()));
mock_cloud_policy_client_->SetPolicy(kPolicyType1, "", response1);
mock_cloud_policy_client_->SetPolicy(kPolicyType2, "", response2);
mock_cloud_policy_client_->NotifyPolicyFetched();
run_loop.Run();
std::unique_ptr<device_management_storage::CachedPolicyInfo>
cached_policy_info = dm_storage_->GetCachedPolicyInfo();
EXPECT_EQ(cached_policy_info->public_key(), kPublicKey1);
EXPECT_EQ(cached_policy_info->key_version(), kPublicKey1Version);
EXPECT_EQ(cached_policy_info->timestamp(), kTimestamp1);
std::optional<enterprise_management::PolicyData> read_data1 =
dm_storage_->ReadPolicyData(kPolicyType1);
ASSERT_TRUE(read_data1);
EXPECT_EQ(read_data1->SerializeAsString(), data1.SerializeAsString());
std::optional<enterprise_management::PolicyData> read_data2 =
dm_storage_->ReadPolicyData(kPolicyType2);
ASSERT_TRUE(read_data2);
EXPECT_EQ(read_data2->SerializeAsString(), data2.SerializeAsString());
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_THAT(test_event_logger_->policy_fetch_events(),
ElementsAre(EnterpriseCompanionStatus::Success()));
}
TEST_F(DMClientTest, FetchPoliciesOverwrite) {
EnsureRegistered();
EXPECT_CALL(*mock_cloud_policy_client_, FetchPolicy).Times(2);
SetMockPolicyFetchResponseValidatorResult(
policy::CloudPolicyValidatorBase::VALIDATION_OK);
// Perform a policy a policy fetch which populates the cached info.
enterprise_management::PublicKeyVerificationData key_verification_data;
key_verification_data.set_new_public_key(kPublicKey1);
key_verification_data.set_new_public_key_version(kPublicKey1Version);
enterprise_management::PolicyData data1;
data1.set_timestamp(kTimestamp1);
data1.set_policy_type(kPolicyType1);
data1.set_policy_value(kPolicyValue1);
enterprise_management::PolicyFetchResponse response1;
response1.set_policy_data(data1.SerializeAsString());
response1.set_new_public_key_verification_data(
key_verification_data.SerializeAsString());
enterprise_management::PolicyData data2;
data2.set_timestamp(kTimestamp1);
data2.set_policy_type(kPolicyType2);
data2.set_policy_value(kPolicyValue2);
enterprise_management::PolicyFetchResponse response2;
response2.set_policy_data(data2.SerializeAsString());
base::RunLoop first_fetch_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.ok());
}).Then(first_fetch_loop.QuitClosure()));
mock_cloud_policy_client_->SetPolicy(kPolicyType1, "", response1);
mock_cloud_policy_client_->SetPolicy(kPolicyType2, "", response2);
mock_cloud_policy_client_->NotifyPolicyFetched();
first_fetch_loop.Run();
std::unique_ptr<device_management_storage::CachedPolicyInfo>
cached_policy_info = dm_storage_->GetCachedPolicyInfo();
EXPECT_EQ(cached_policy_info->public_key(), kPublicKey1);
EXPECT_EQ(cached_policy_info->key_version(), kPublicKey1Version);
EXPECT_EQ(cached_policy_info->timestamp(), kTimestamp1);
// Perform a subsequent policy fetch whose response does not contain a new
// public key. The cached information should not change.
enterprise_management::PolicyData data3;
data3.set_timestamp(kTimestamp2);
data3.set_policy_type(kPolicyType1);
data3.set_policy_value(kPolicyValue3);
enterprise_management::PolicyFetchResponse response3;
response3.set_policy_data(data3.SerializeAsString());
base::RunLoop second_fetch_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.ok());
}).Then(second_fetch_loop.QuitClosure()));
mock_cloud_policy_client_->SetPolicy(kPolicyType1, "", response3);
mock_cloud_policy_client_->NotifyPolicyFetched();
second_fetch_loop.Run();
cached_policy_info = dm_storage_->GetCachedPolicyInfo();
EXPECT_EQ(cached_policy_info->public_key(), kPublicKey1);
EXPECT_EQ(cached_policy_info->key_version(), kPublicKey1Version);
EXPECT_EQ(cached_policy_info->timestamp(), kTimestamp1);
std::optional<enterprise_management::PolicyData> read_data1 =
dm_storage_->ReadPolicyData(kPolicyType1);
ASSERT_TRUE(read_data1);
EXPECT_EQ(read_data1->SerializeAsString(), data3.SerializeAsString());
std::optional<enterprise_management::PolicyData> read_data2 =
dm_storage_->ReadPolicyData(kPolicyType2);
ASSERT_TRUE(read_data2);
EXPECT_EQ(read_data2->SerializeAsString(), data2.SerializeAsString());
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_THAT(test_event_logger_->policy_fetch_events(),
ElementsAre(EnterpriseCompanionStatus::Success(),
EnterpriseCompanionStatus::Success()));
}
// Tests that the DM token is removed if the server requests the device to be
// reset.
TEST_F(DMClientTest, FetchPoliciesReset) {
EnsureRegistered();
EXPECT_CALL(*mock_cloud_policy_client_, FetchPolicy).Times(1);
base::RunLoop first_fetch_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.EqualsDeviceManagementStatus(
policy::DM_STATUS_SERVICE_DEVICE_NEEDS_RESET));
}).Then(first_fetch_loop.QuitClosure()));
mock_cloud_policy_client_->SetStatus(
policy::DM_STATUS_SERVICE_DEVICE_NEEDS_RESET);
mock_cloud_policy_client_->dm_token_.clear();
mock_cloud_policy_client_->NotifyClientError();
mock_cloud_policy_client_->NotifyRegistrationStateChanged();
first_fetch_loop.Run();
EXPECT_TRUE(test_token_service_->GetDmToken().empty());
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_THAT(test_event_logger_->policy_fetch_events(),
ElementsAre(EnterpriseCompanionStatus::FromDeviceManagementStatus(
policy::DM_STATUS_SERVICE_DEVICE_NEEDS_RESET)));
}
// Tests that the DM token is replaced with the invalid token if the server
// indicates that the device is not found.
TEST_F(DMClientTest, FetchPoliciesInvalidation) {
EnsureRegistered();
EXPECT_CALL(*mock_cloud_policy_client_, FetchPolicy).Times(1);
base::RunLoop first_fetch_loop;
dm_client_->FetchPolicies(
test_event_logger_,
base::BindOnce([](const EnterpriseCompanionStatus& status) {
EXPECT_TRUE(status.EqualsDeviceManagementStatus(
policy::DM_STATUS_SERVICE_DEVICE_NOT_FOUND));
}).Then(first_fetch_loop.QuitClosure()));
mock_cloud_policy_client_->SetStatus(
policy::DM_STATUS_SERVICE_DEVICE_NOT_FOUND);
mock_cloud_policy_client_->dm_token_.clear();
mock_cloud_policy_client_->NotifyClientError();
mock_cloud_policy_client_->NotifyRegistrationStateChanged();
first_fetch_loop.Run();
EXPECT_TRUE(dm_storage_->IsDeviceDeregistered());
EXPECT_TRUE(test_event_logger_->registration_events().empty());
EXPECT_THAT(test_event_logger_->policy_fetch_events(),
ElementsAre(EnterpriseCompanionStatus::FromDeviceManagementStatus(
policy::DM_STATUS_SERVICE_DEVICE_NOT_FOUND)));
}
} // namespace enterprise_companion