| // Copyright 2018 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/chromeos/policy/remote_commands/device_command_start_crd_session_job.h" |
| |
| #include <map> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/json/json_writer.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/test/test_mock_time_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "components/policy/proto/device_management_backend.pb.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace policy { |
| |
| namespace em = enterprise_management; |
| |
| namespace { |
| |
| constexpr char kResultCodeFieldName[] = "resultCode"; |
| constexpr char kResultMessageFieldName[] = "message"; |
| constexpr char kResultAuthCodeFieldName[] = "authCode"; |
| constexpr char kResultLastActivityFieldName[] = "lastActivitySec"; |
| |
| constexpr RemoteCommandJob::UniqueIDType kUniqueID = 123456789; |
| |
| constexpr char kTestDirectoryBotJID[] = "remote@bot.jabber.nowhere.org"; |
| |
| constexpr char kTestOAuthToken[] = "test-oauth-token"; |
| constexpr char kTestAuthCode[] = "1111-2222-3333"; |
| constexpr char kTestNoOAuthTokenReason[] = "oops-no-oauth-token"; |
| constexpr char kTestNoICEConfigReason[] = "oops-no-ice-config"; |
| |
| constexpr char kIdlenessCutoffFieldName[] = "idlenessCutoffSec"; |
| constexpr char kDirectoryBotJIDFieldName[] = "directoryBotJID"; |
| |
| em::RemoteCommand GenerateCommandProto(RemoteCommandJob::UniqueIDType unique_id, |
| base::TimeDelta age_of_command, |
| base::TimeDelta idleness_cutoff, |
| const std::string& directory_bot_jid) { |
| em::RemoteCommand command_proto; |
| command_proto.set_type( |
| enterprise_management::RemoteCommand_Type_DEVICE_START_CRD_SESSION); |
| command_proto.set_command_id(unique_id); |
| command_proto.set_age_of_command(age_of_command.InMilliseconds()); |
| |
| std::string payload; |
| base::Value root_dict(base::Value::Type::DICTIONARY); |
| root_dict.SetKey(kIdlenessCutoffFieldName, |
| base::Value((int)idleness_cutoff.InSeconds())); |
| root_dict.SetKey(kDirectoryBotJIDFieldName, base::Value(directory_bot_jid)); |
| base::JSONWriter::Write(root_dict, &payload); |
| command_proto.set_payload(payload); |
| return command_proto; |
| } |
| |
| class StubCRDHostDelegate : public DeviceCommandStartCRDSessionJob::Delegate { |
| public: |
| StubCRDHostDelegate(bool has_active_session, |
| bool are_services_ready, |
| bool is_running_kiosk, |
| base::TimeDelta idleness_period, |
| bool oauth_token_success, |
| bool ice_config_success, |
| bool auth_code_success); |
| ~StubCRDHostDelegate() override; |
| |
| bool HasActiveSession() const override; |
| void TerminateSession(base::OnceClosure callback) override; |
| |
| bool AreServicesReady() const override; |
| bool IsRunningKiosk() const override; |
| base::TimeDelta GetIdlenessPeriod() const override; |
| |
| void FetchOAuthToken( |
| DeviceCommandStartCRDSessionJob::OAuthTokenCallback success_callback, |
| DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) override; |
| |
| void FetchICEConfig( |
| const std::string& oauth_token, |
| DeviceCommandStartCRDSessionJob::ICEConfigCallback success_callback, |
| DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) override; |
| |
| void StartCRDHostAndGetCode( |
| const std::string& directory_bot_jid, |
| const std::string& oauth_token, |
| base::Value ice_config, |
| DeviceCommandStartCRDSessionJob::AuthCodeCallback success_callback, |
| DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) override; |
| |
| private: |
| bool has_active_session_; |
| bool are_services_ready_; |
| bool is_running_kiosk_; |
| base::TimeDelta idleness_period_; |
| bool oauth_token_success_; |
| bool ice_config_success_; |
| bool auth_code_success_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StubCRDHostDelegate); |
| }; |
| |
| StubCRDHostDelegate::StubCRDHostDelegate(bool has_active_session, |
| bool are_services_ready, |
| bool is_running_kiosk, |
| base::TimeDelta idleness_period, |
| bool oauth_token_success, |
| bool ice_config_success, |
| bool auth_code_success) |
| : has_active_session_(has_active_session), |
| are_services_ready_(are_services_ready), |
| is_running_kiosk_(is_running_kiosk), |
| idleness_period_(idleness_period), |
| oauth_token_success_(oauth_token_success), |
| ice_config_success_(ice_config_success), |
| auth_code_success_(auth_code_success) {} |
| |
| StubCRDHostDelegate::~StubCRDHostDelegate() {} |
| |
| bool StubCRDHostDelegate::HasActiveSession() const { |
| return has_active_session_; |
| } |
| |
| void StubCRDHostDelegate::TerminateSession(base::OnceClosure callback) { |
| has_active_session_ = false; |
| std::move(callback).Run(); |
| } |
| |
| bool StubCRDHostDelegate::AreServicesReady() const { |
| return are_services_ready_; |
| } |
| |
| bool StubCRDHostDelegate::IsRunningKiosk() const { |
| return is_running_kiosk_; |
| } |
| |
| base::TimeDelta StubCRDHostDelegate::GetIdlenessPeriod() const { |
| return idleness_period_; |
| } |
| |
| void StubCRDHostDelegate::FetchOAuthToken( |
| DeviceCommandStartCRDSessionJob::OAuthTokenCallback success_callback, |
| DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) { |
| if (oauth_token_success_) { |
| std::move(success_callback).Run(kTestOAuthToken); |
| } else { |
| std::move(error_callback) |
| .Run(DeviceCommandStartCRDSessionJob::FAILURE_NO_OAUTH_TOKEN, |
| kTestNoOAuthTokenReason); |
| } |
| } |
| |
| void StubCRDHostDelegate::FetchICEConfig( |
| const std::string& oauth_token, |
| DeviceCommandStartCRDSessionJob::ICEConfigCallback success_callback, |
| DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) { |
| if (ice_config_success_) { |
| base::Value ice_config(base::Value::Type::DICTIONARY); |
| std::move(success_callback).Run(std::move(ice_config)); |
| } else { |
| std::move(error_callback) |
| .Run(DeviceCommandStartCRDSessionJob::FAILURE_NO_ICE_CONFIG, |
| kTestNoICEConfigReason); |
| } |
| } |
| |
| void StubCRDHostDelegate::StartCRDHostAndGetCode( |
| const std::string& directory_bot_jid, |
| const std::string& oauth_token, |
| base::Value ice_config, |
| DeviceCommandStartCRDSessionJob::AuthCodeCallback success_callback, |
| DeviceCommandStartCRDSessionJob::ErrorCallback error_callback) { |
| if (auth_code_success_) { |
| std::move(success_callback).Run(kTestAuthCode); |
| } else { |
| std::move(error_callback) |
| .Run(DeviceCommandStartCRDSessionJob::FAILURE_CRD_HOST_ERROR, |
| std::string()); |
| } |
| } |
| |
| } // namespace |
| |
| class DeviceCommandStartCRDSessionJobTest : public testing::Test { |
| public: |
| void VerifyResults(RemoteCommandJob* job, |
| RemoteCommandJob::Status expected_status, |
| std::string expected_payload); |
| |
| protected: |
| DeviceCommandStartCRDSessionJobTest(); |
| |
| // testing::Test: |
| void SetUp() override; |
| |
| void InitializeJob(RemoteCommandJob* job, |
| RemoteCommandJob::UniqueIDType unique_id, |
| base::TimeTicks issued_time, |
| base::TimeDelta idleness_cutoff, |
| const std::string& directory_bot_jid); |
| |
| std::string CreateSuccessPayload(const std::string& auth_code); |
| std::string CreateErrorPayload( |
| DeviceCommandStartCRDSessionJob::ResultCode result_code, |
| const std::string& error_message); |
| std::string CreateNotIdlePayload(base::TimeDelta idleness); |
| |
| base::TimeTicks test_start_time_; |
| |
| content::TestBrowserThreadBundle thread_bundle_; |
| base::RunLoop run_loop_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(DeviceCommandStartCRDSessionJobTest); |
| }; |
| |
| DeviceCommandStartCRDSessionJobTest::DeviceCommandStartCRDSessionJobTest() {} |
| |
| void DeviceCommandStartCRDSessionJobTest::SetUp() { |
| test_start_time_ = base::TimeTicks::Now(); |
| } |
| |
| void DeviceCommandStartCRDSessionJobTest::InitializeJob( |
| RemoteCommandJob* job, |
| RemoteCommandJob::UniqueIDType unique_id, |
| base::TimeTicks issued_time, |
| base::TimeDelta idleness_cutoff, |
| const std::string& directory_bot_jid) { |
| EXPECT_TRUE(job->Init( |
| base::TimeTicks::Now(), |
| GenerateCommandProto(unique_id, base::TimeTicks::Now() - issued_time, |
| idleness_cutoff, directory_bot_jid))); |
| |
| EXPECT_EQ(unique_id, job->unique_id()); |
| EXPECT_EQ(RemoteCommandJob::NOT_STARTED, job->status()); |
| } |
| |
| std::string DeviceCommandStartCRDSessionJobTest::CreateSuccessPayload( |
| const std::string& auth_code) { |
| std::string payload; |
| base::Value root(base::Value::Type::DICTIONARY); |
| root.SetKey(kResultCodeFieldName, |
| base::Value(DeviceCommandStartCRDSessionJob::SUCCESS)); |
| root.SetKey(kResultAuthCodeFieldName, base::Value(auth_code)); |
| base::JSONWriter::Write(root, &payload); |
| return payload; |
| } |
| |
| std::string DeviceCommandStartCRDSessionJobTest::CreateErrorPayload( |
| DeviceCommandStartCRDSessionJob::ResultCode result_code, |
| const std::string& error_message) { |
| std::string payload; |
| base::Value root(base::Value::Type::DICTIONARY); |
| root.SetKey(kResultCodeFieldName, base::Value(result_code)); |
| if (!error_message.empty()) |
| root.SetKey(kResultMessageFieldName, base::Value(error_message)); |
| base::JSONWriter::Write(root, &payload); |
| return payload; |
| } |
| |
| std::string DeviceCommandStartCRDSessionJobTest::CreateNotIdlePayload( |
| base::TimeDelta idleness) { |
| std::string payload; |
| base::Value root(base::Value::Type::DICTIONARY); |
| root.SetKey(kResultCodeFieldName, |
| base::Value(DeviceCommandStartCRDSessionJob::FAILURE_NOT_IDLE)); |
| root.SetKey(kResultLastActivityFieldName, |
| base::Value(static_cast<int>(idleness.InSeconds()))); |
| base::JSONWriter::Write(root, &payload); |
| return payload; |
| } |
| |
| void DeviceCommandStartCRDSessionJobTest::VerifyResults( |
| RemoteCommandJob* job, |
| RemoteCommandJob::Status expected_status, |
| std::string expected_payload) { |
| EXPECT_EQ(expected_status, job->status()); |
| std::unique_ptr<std::string> payload = job->GetResultPayload(); |
| EXPECT_TRUE(payload); |
| EXPECT_EQ(expected_payload, *payload); |
| run_loop_.Quit(); |
| } |
| |
| TEST_F(DeviceCommandStartCRDSessionJobTest, Success) { |
| StubCRDHostDelegate delegate( |
| false /* has_active_session */, true /* are_services_ready */, |
| true /* is_running_kiosk */, |
| base::TimeDelta::FromHours(1) /* idleness_period */, |
| true /* oauth_token_success */, true /* ice_config_success */, |
| true /* auth_code_success */); |
| |
| std::unique_ptr<RemoteCommandJob> job = |
| std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate); |
| InitializeJob(job.get(), kUniqueID, test_start_time_, |
| base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID); |
| bool success = job->Run( |
| base::TimeTicks::Now(), |
| base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults, |
| base::Unretained(this), base::Unretained(job.get()), |
| RemoteCommandJob::SUCCEEDED, |
| CreateSuccessPayload(kTestAuthCode))); |
| EXPECT_TRUE(success); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(DeviceCommandStartCRDSessionJobTest, SuccessOldSessionWasRunning) { |
| StubCRDHostDelegate delegate( |
| true /* has_active_session */, true /* are_services_ready */, |
| true /* is_running_kiosk */, |
| base::TimeDelta::FromHours(1) /* idleness_period */, |
| true /* oauth_token_success */, true /* ice_config_success */, |
| true /* auth_code_success */); |
| |
| std::unique_ptr<RemoteCommandJob> job = |
| std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate); |
| InitializeJob(job.get(), kUniqueID, test_start_time_, |
| base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID); |
| bool success = job->Run( |
| base::TimeTicks::Now(), |
| base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults, |
| base::Unretained(this), base::Unretained(job.get()), |
| RemoteCommandJob::SUCCEEDED, |
| CreateSuccessPayload(kTestAuthCode))); |
| EXPECT_TRUE(success); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(DeviceCommandStartCRDSessionJobTest, FailureServicesAreNotReady) { |
| StubCRDHostDelegate delegate( |
| false /* has_active_session */, false /* are_services_ready */, |
| true /* is_running_kiosk */, |
| base::TimeDelta::FromHours(1) /* idleness_period */, |
| true /* oauth_token_success */, true /* ice_config_success */, |
| true /* auth_code_success */); |
| |
| std::unique_ptr<RemoteCommandJob> job = |
| std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate); |
| InitializeJob(job.get(), kUniqueID, test_start_time_, |
| base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID); |
| bool success = job->Run( |
| base::TimeTicks::Now(), |
| base::BindOnce( |
| &DeviceCommandStartCRDSessionJobTest::VerifyResults, |
| base::Unretained(this), base::Unretained(job.get()), |
| RemoteCommandJob::FAILED, |
| CreateErrorPayload( |
| DeviceCommandStartCRDSessionJob::FAILURE_SERVICES_NOT_READY, |
| std::string()))); |
| EXPECT_TRUE(success); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(DeviceCommandStartCRDSessionJobTest, FailureNotAKiosk) { |
| StubCRDHostDelegate delegate( |
| false /* has_active_session */, true /* are_services_ready */, |
| false /* is_running_kiosk */, |
| base::TimeDelta::FromHours(1) /* idleness_period */, |
| true /* oauth_token_success */, true /* ice_config_success */, |
| true /* auth_code_success */); |
| |
| std::unique_ptr<RemoteCommandJob> job = |
| std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate); |
| InitializeJob(job.get(), kUniqueID, test_start_time_, |
| base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID); |
| bool success = job->Run( |
| base::TimeTicks::Now(), |
| base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults, |
| base::Unretained(this), base::Unretained(job.get()), |
| RemoteCommandJob::FAILED, |
| CreateErrorPayload( |
| DeviceCommandStartCRDSessionJob::FAILURE_NOT_A_KIOSK, |
| std::string()))); |
| EXPECT_TRUE(success); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(DeviceCommandStartCRDSessionJobTest, FailureNotIdle) { |
| StubCRDHostDelegate delegate( |
| false /* has_active_session */, true /* are_services_ready */, |
| true /* is_running_kiosk */, |
| base::TimeDelta::FromSeconds(1) /* idleness_period */, |
| true /* oauth_token_success */, true /* ice_config_success */, |
| true /* auth_code_success */); |
| |
| std::unique_ptr<RemoteCommandJob> job = |
| std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate); |
| InitializeJob(job.get(), kUniqueID, test_start_time_, |
| base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID); |
| bool success = job->Run( |
| base::TimeTicks::Now(), |
| base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults, |
| base::Unretained(this), base::Unretained(job.get()), |
| RemoteCommandJob::FAILED, |
| CreateNotIdlePayload(base::TimeDelta::FromSeconds(1)))); |
| EXPECT_TRUE(success); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(DeviceCommandStartCRDSessionJobTest, TestNoOauthToken) { |
| StubCRDHostDelegate delegate( |
| false /* has_active_session */, true /* are_services_ready */, |
| true /* is_running_kiosk */, |
| base::TimeDelta::FromHours(1) /* idleness_period */, |
| false /* oauth_token_success */, true /* ice_config_success */, |
| true /* auth_code_success */); |
| |
| std::unique_ptr<RemoteCommandJob> job = |
| std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate); |
| InitializeJob(job.get(), kUniqueID, test_start_time_, |
| base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID); |
| bool success = |
| job->Run(base::TimeTicks::Now(), |
| base::BindOnce( |
| &DeviceCommandStartCRDSessionJobTest::VerifyResults, |
| base::Unretained(this), base::Unretained(job.get()), |
| RemoteCommandJob::FAILED, |
| CreateErrorPayload( |
| DeviceCommandStartCRDSessionJob::FAILURE_NO_OAUTH_TOKEN, |
| kTestNoOAuthTokenReason))); |
| EXPECT_TRUE(success); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(DeviceCommandStartCRDSessionJobTest, TestNoICEConfig) { |
| StubCRDHostDelegate delegate( |
| false /* has_active_session */, true /* are_services_ready */, |
| true /* is_running_kiosk */, |
| base::TimeDelta::FromHours(1) /* idleness_period */, |
| true /* oauth_token_success */, false /* ice_config_success */, |
| true /* auth_code_success */); |
| |
| std::unique_ptr<RemoteCommandJob> job = |
| std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate); |
| InitializeJob(job.get(), kUniqueID, test_start_time_, |
| base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID); |
| bool success = job->Run( |
| base::TimeTicks::Now(), |
| base::BindOnce(&DeviceCommandStartCRDSessionJobTest::VerifyResults, |
| base::Unretained(this), base::Unretained(job.get()), |
| RemoteCommandJob::FAILED, |
| CreateErrorPayload( |
| DeviceCommandStartCRDSessionJob::FAILURE_NO_ICE_CONFIG, |
| kTestNoICEConfigReason))); |
| EXPECT_TRUE(success); |
| run_loop_.Run(); |
| } |
| |
| TEST_F(DeviceCommandStartCRDSessionJobTest, TestErrorRunningCRDHost) { |
| StubCRDHostDelegate delegate( |
| false /* has_active_session */, true /* are_services_ready */, |
| true /* is_running_kiosk */, |
| base::TimeDelta::FromHours(1) /* idleness_period */, |
| true /* oauth_token_success */, true /* ice_config_success */, |
| false /* auth_code_success */); |
| |
| std::unique_ptr<RemoteCommandJob> job = |
| std::make_unique<DeviceCommandStartCRDSessionJob>(&delegate); |
| InitializeJob(job.get(), kUniqueID, test_start_time_, |
| base::TimeDelta::FromSeconds(30), kTestDirectoryBotJID); |
| bool success = |
| job->Run(base::TimeTicks::Now(), |
| base::BindOnce( |
| &DeviceCommandStartCRDSessionJobTest::VerifyResults, |
| base::Unretained(this), base::Unretained(job.get()), |
| RemoteCommandJob::FAILED, |
| CreateErrorPayload( |
| DeviceCommandStartCRDSessionJob::FAILURE_CRD_HOST_ERROR, |
| std::string()))); |
| EXPECT_TRUE(success); |
| run_loop_.Run(); |
| } |
| |
| } // namespace policy |