Commit 61c9e430 authored by Ivan Sandrk's avatar Ivan Sandrk Committed by Commit Bot

Add "Remove Users" remote command

Currently managed Chrome OS devices support remote commands issued by the admin
on CPanel. Add a command that wipes all the users off of the device when run
(equivalent to removing all the user PODs manually from the login screen).

Most changes are glue or wiring things through, main files are:
- device_command_wipe_users_job
- user_removal_manager

More details at the design doc @ go/remove-users-command

Bug: chromium:827467
Change-Id: I77a130bf4dda0757a60a141713baae27d2b5b5a2
Reviewed-on: https://chromium-review.googlesource.com/1107803Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarJulian Pastarmov <pastarmovj@chromium.org>
Commit-Queue: Ivan Šandrk <isandrk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#570804}
parent 827bd421
......@@ -1412,6 +1412,8 @@ source_set("chromeos") {
"policy/remote_commands/device_command_screenshot_job.h",
"policy/remote_commands/device_command_set_volume_job.cc",
"policy/remote_commands/device_command_set_volume_job.h",
"policy/remote_commands/device_command_wipe_users_job.cc",
"policy/remote_commands/device_command_wipe_users_job.h",
"policy/remote_commands/device_commands_factory_chromeos.cc",
"policy/remote_commands/device_commands_factory_chromeos.h",
"policy/remote_commands/screenshot_delegate.cc",
......@@ -1653,6 +1655,8 @@ source_set("chromeos") {
"system/timezone_resolver_manager.h",
"system/timezone_util.cc",
"system/timezone_util.h",
"system/user_removal_manager.cc",
"system/user_removal_manager.h",
"system_logs/command_line_log_source.cc",
"system_logs/command_line_log_source.h",
"system_logs/dbus_log_source.cc",
......@@ -2089,6 +2093,7 @@ source_set("unit_tests") {
"policy/pre_signin_policy_fetcher_unittest.cc",
"policy/remote_commands/device_command_screenshot_job_unittest.cc",
"policy/remote_commands/device_command_set_volume_job_unittest.cc",
"policy/remote_commands/device_command_wipe_users_job_unittest.cc",
"policy/remote_commands/user_command_arc_job_unittest.cc",
"policy/secondary_google_account_signin_policy_handler_unittest.cc",
"policy/server_backed_state_keys_broker_unittest.cc",
......@@ -2152,6 +2157,7 @@ source_set("unit_tests") {
"smb_client/temp_file_manager_unittest.cc",
"system/automatic_reboot_manager_unittest.cc",
"system/device_disabling_manager_unittest.cc",
"system/user_removal_manager_unittest.cc",
"system_logs/single_debug_daemon_log_source_unittest.cc",
"system_logs/single_log_file_log_source_unittest.cc",
"tether/tether_service_unittest.cc",
......
......@@ -90,6 +90,7 @@
#include "chrome/browser/chromeos/settings/device_settings_service.h"
#include "chrome/browser/chromeos/settings/shutdown_policy_forwarder.h"
#include "chrome/browser/chromeos/system/input_device_settings.h"
#include "chrome/browser/chromeos/system/user_removal_manager.h"
#include "chrome/browser/chromeos/ui/low_disk_notification.h"
#include "chrome/browser/chromeos/upgrade_detector_chromeos.h"
#include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
......@@ -956,6 +957,7 @@ void ChromeBrowserMainPartsChromeos::PostProfileInit() {
g_browser_process->platform_part()->InitializeAutomaticRebootManager();
g_browser_process->platform_part()->InitializeDeviceDisablingManager();
user_removal_manager::RemoveUsersIfNeeded();
// This observer cannot be created earlier because it requires the shell to be
// available.
......
......@@ -209,6 +209,7 @@ void DeviceCloudPolicyManagerChromeOS::RegisterPrefs(
registry->RegisterBooleanPref(prefs::kDeviceEnrollmentAutoStart, false);
registry->RegisterBooleanPref(prefs::kDeviceEnrollmentCanExit, true);
registry->RegisterDictionaryPref(prefs::kServerBackedDeviceState);
registry->RegisterBooleanPref(prefs::kRemoveUsersRemoteCommand, false);
}
// static
......
// 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_wipe_users_job.h"
#include "base/bind.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/system/user_removal_manager.h"
#include "components/policy/core/common/remote_commands/remote_commands_service.h"
#include "components/policy/proto/device_management_backend.pb.h"
namespace policy {
namespace {
// This command has an expiration time this high because the canonical use case
// would be in schools where the admin issues the command at the end of one
// school year, and then in the next school year when a student picks up a
// device from the lot and turns it on for the first time it gets wiped clean
// and ready to be used again. Most schools use the cart model of deployment
// where a given device might not be used in quite some time (eg. device at the
// bottom of the pool would take quite some time to be turned on).
constexpr base::TimeDelta kWipeUsersCommandExpirationTime =
base::TimeDelta::FromDays(180);
} // namespace
DeviceCommandWipeUsersJob::DeviceCommandWipeUsersJob(
RemoteCommandsService* service)
: service_(service) {}
DeviceCommandWipeUsersJob::~DeviceCommandWipeUsersJob() = default;
enterprise_management::RemoteCommand_Type DeviceCommandWipeUsersJob::GetType()
const {
return enterprise_management::RemoteCommand_Type_DEVICE_WIPE_USERS;
}
bool DeviceCommandWipeUsersJob::IsExpired(base::TimeTicks now) {
return now > issued_time() + kWipeUsersCommandExpirationTime;
}
void DeviceCommandWipeUsersJob::RunImpl(
const CallbackWithResult& succeeded_callback,
const CallbackWithResult& failed_callback) {
// Set callback which gets called after command is ACKd to the server. We want
// to log out only after the server got the ACK, otherwise we could log out
// before ACKing and then the server would never get the ACK.
service_->SetOnCommandAckedCallback(
base::BindOnce(&chromeos::user_removal_manager::LogOut));
// Initiate the user removal process. Once the first part is done, the passed
// callback gets called and signals that the command was successfully received
// and will be executed.
chromeos::user_removal_manager::InitiateUserRemoval(
base::BindOnce(succeeded_callback, nullptr));
}
} // namespace policy
// 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.
#ifndef CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_WIPE_USERS_JOB_H_
#define CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_WIPE_USERS_JOB_H_
#include "base/macros.h"
#include "components/policy/core/common/remote_commands/remote_command_job.h"
namespace policy {
class RemoteCommandsService;
class DeviceCommandWipeUsersJob : public RemoteCommandJob {
public:
explicit DeviceCommandWipeUsersJob(RemoteCommandsService* service);
~DeviceCommandWipeUsersJob() override;
// RemoteCommandJob:
enterprise_management::RemoteCommand_Type GetType() const override;
protected:
// RemoteCommandJob:
bool IsExpired(base::TimeTicks now) override;
void RunImpl(const CallbackWithResult& succeeded_callback,
const CallbackWithResult& failed_callback) override;
private:
RemoteCommandsService* const service_;
DISALLOW_COPY_AND_ASSIGN(DeviceCommandWipeUsersJob);
};
} // namespace policy
#endif // CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_WIPE_USERS_JOB_H_
// 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_wipe_users_job.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_forward.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_commands_factory_chromeos.h"
#include "chrome/browser/chromeos/system/user_removal_manager.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/policy/core/common/remote_commands/remote_command_job.h"
#include "components/policy/core/common/remote_commands/remote_commands_service.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace policy {
constexpr base::TimeDelta kCommandAge = base::TimeDelta::FromMinutes(10);
constexpr base::TimeDelta kVeryoldCommandAge = base::TimeDelta::FromDays(175);
class TestingRemoteCommandsService : public RemoteCommandsService {
public:
explicit TestingRemoteCommandsService(MockCloudPolicyClient* client)
: RemoteCommandsService(std::make_unique<DeviceCommandsFactoryChromeOS>(),
client) {}
// RemoteCommandsService:
void SetOnCommandAckedCallback(base::OnceClosure callback) override {
on_command_acked_callback_ = std::move(callback);
}
base::OnceClosure OnCommandAckedCallback() {
return std::move(on_command_acked_callback_);
}
protected:
base::OnceClosure on_command_acked_callback_;
private:
DISALLOW_COPY_AND_ASSIGN(TestingRemoteCommandsService);
};
std::unique_ptr<policy::RemoteCommandJob> CreateWipeUsersJob(
base::TimeDelta age_of_command,
RemoteCommandsService* service) {
// Create the job proto.
enterprise_management::RemoteCommand command_proto;
command_proto.set_type(
enterprise_management::RemoteCommand_Type_DEVICE_WIPE_USERS);
constexpr policy::RemoteCommandJob::UniqueIDType kUniqueID = 123456789;
command_proto.set_command_id(kUniqueID);
command_proto.set_age_of_command(age_of_command.InMilliseconds());
// Create the job and validate.
auto job = std::make_unique<policy::DeviceCommandWipeUsersJob>(service);
EXPECT_TRUE(job->Init(base::TimeTicks::Now(), command_proto));
EXPECT_EQ(kUniqueID, job->unique_id());
EXPECT_EQ(policy::RemoteCommandJob::NOT_STARTED, job->status());
return job;
}
class DeviceCommandWipeUsersJobTest : public testing::Test {
protected:
DeviceCommandWipeUsersJobTest();
~DeviceCommandWipeUsersJobTest() override;
content::TestBrowserThreadBundle thread_bundle_;
base::RunLoop run_loop_;
ScopedTestingLocalState local_state_;
const std::unique_ptr<MockCloudPolicyClient> client_;
const std::unique_ptr<TestingRemoteCommandsService> service_;
private:
DISALLOW_COPY_AND_ASSIGN(DeviceCommandWipeUsersJobTest);
};
DeviceCommandWipeUsersJobTest::DeviceCommandWipeUsersJobTest()
: local_state_(TestingBrowserProcess::GetGlobal()),
client_(std::make_unique<MockCloudPolicyClient>()),
service_(std::make_unique<TestingRemoteCommandsService>(client_.get())) {}
DeviceCommandWipeUsersJobTest::~DeviceCommandWipeUsersJobTest() {}
// Make sure that the command is still valid 175 days after being issued.
TEST_F(DeviceCommandWipeUsersJobTest, TestCommandLifetime) {
std::unique_ptr<policy::RemoteCommandJob> job =
CreateWipeUsersJob(kVeryoldCommandAge, service_.get());
EXPECT_TRUE(job->Run(base::TimeTicks::Now(), base::Closure()));
}
// Make sure that the command's succeeded_callback is being invoked.
TEST_F(DeviceCommandWipeUsersJobTest, TestCommandSucceededCallback) {
std::unique_ptr<policy::RemoteCommandJob> job =
CreateWipeUsersJob(kCommandAge, service_.get());
auto check_result_callback = base::Bind(
[](base::RunLoop* run_loop, policy::RemoteCommandJob* job) {
EXPECT_EQ(policy::RemoteCommandJob::SUCCEEDED, job->status());
run_loop->Quit();
},
&run_loop_, job.get());
EXPECT_TRUE(job->Run(base::TimeTicks::Now(), check_result_callback));
// This call processes the CommitPendingWrite which persists the pref to disk,
// and runs the passed callback which is the succeeded_callback.
run_loop_.Run();
}
// Make sure that LogOut is being called after the commands gets ACK'd to the
// server.
TEST_F(DeviceCommandWipeUsersJobTest, TestLogOutCalled) {
std::unique_ptr<policy::RemoteCommandJob> job =
CreateWipeUsersJob(kCommandAge, service_.get());
EXPECT_TRUE(job->Run(base::TimeTicks::Now(), run_loop_.QuitClosure()));
// At this point the job is run, and the succeeded_callback is waiting to be
// invoked.
run_loop_.Run();
// Now the succeeded_callback has been invoked, and normally it would cause an
// ACK to be sent to the server, and upon receiving a response back from the
// server LogOut would get called.
// Simulate a response from the server by posting a task and waiting for
// LogOut to be called.
bool log_out_called = false;
base::RunLoop run_loop2;
auto log_out_callback = base::BindOnce(
[](base::RunLoop* run_loop, bool* log_out_called) {
*log_out_called = true;
run_loop->Quit();
},
&run_loop2, &log_out_called);
chromeos::user_removal_manager::OverrideLogOutForTesting(
std::move(log_out_callback));
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, service_->OnCommandAckedCallback());
run_loop2.Run();
EXPECT_TRUE(log_out_called);
}
} // namespace policy
......@@ -9,6 +9,7 @@
#include "chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_set_volume_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/screenshot_delegate.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "components/policy/core/common/remote_commands/remote_command_job.h"
......@@ -23,7 +24,8 @@ DeviceCommandsFactoryChromeOS::DeviceCommandsFactoryChromeOS() = default;
DeviceCommandsFactoryChromeOS::~DeviceCommandsFactoryChromeOS() = default;
std::unique_ptr<RemoteCommandJob>
DeviceCommandsFactoryChromeOS::BuildJobForType(em::RemoteCommand_Type type) {
DeviceCommandsFactoryChromeOS::BuildJobForType(em::RemoteCommand_Type type,
RemoteCommandsService* service) {
switch (type) {
case em::RemoteCommand_Type_DEVICE_REBOOT:
return std::make_unique<DeviceCommandRebootJob>(
......@@ -35,6 +37,8 @@ DeviceCommandsFactoryChromeOS::BuildJobForType(em::RemoteCommand_Type type) {
return std::make_unique<DeviceCommandSetVolumeJob>();
case em::RemoteCommand_Type_DEVICE_FETCH_STATUS:
return std::make_unique<DeviceCommandFetchStatusJob>();
case em::RemoteCommand_Type_DEVICE_WIPE_USERS:
return std::make_unique<DeviceCommandWipeUsersJob>(service);
default:
// Other types of commands should be sent to UserCommandsFactoryChromeOS
// instead of here.
......
......@@ -19,7 +19,8 @@ class DeviceCommandsFactoryChromeOS : public RemoteCommandsFactory {
// RemoteCommandsFactory:
std::unique_ptr<RemoteCommandJob> BuildJobForType(
enterprise_management::RemoteCommand_Type type) override;
enterprise_management::RemoteCommand_Type type,
RemoteCommandsService* service) override;
private:
DISALLOW_COPY_AND_ASSIGN(DeviceCommandsFactoryChromeOS);
......
......@@ -19,7 +19,8 @@ UserCommandsFactoryChromeOS::UserCommandsFactoryChromeOS(Profile* profile)
UserCommandsFactoryChromeOS::~UserCommandsFactoryChromeOS() = default;
std::unique_ptr<RemoteCommandJob> UserCommandsFactoryChromeOS::BuildJobForType(
em::RemoteCommand_Type type) {
em::RemoteCommand_Type type,
RemoteCommandsService* service) {
switch (type) {
case em::RemoteCommand_Type_USER_ARC_COMMAND:
return std::make_unique<UserCommandArcJob>(profile_);
......
......@@ -21,7 +21,8 @@ class UserCommandsFactoryChromeOS : public RemoteCommandsFactory {
// RemoteCommandsFactory:
std::unique_ptr<RemoteCommandJob> BuildJobForType(
enterprise_management::RemoteCommand_Type type) override;
enterprise_management::RemoteCommand_Type type,
RemoteCommandsService* service) override;
private:
Profile* const profile_;
......
// Copyright 2014 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/system/user_removal_manager.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/persistent_pref_store.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
namespace chromeos {
namespace user_removal_manager {
namespace {
// The time that InitiateUserRemoval waits on the passed callback to do a log
// out, otherwise it does the log out itself.
constexpr base::TimeDelta kFailsafeTimerTimeout =
base::TimeDelta::FromSeconds(60);
// Override for the LogOut function inside of tests.
base::OnceClosure g_log_out_override_callback;
} // namespace
bool RemoveUsersIfNeeded() {
PrefService* local_state = g_browser_process->local_state();
const bool should_remove_users =
local_state->GetBoolean(prefs::kRemoveUsersRemoteCommand);
if (!should_remove_users)
return false;
local_state->SetBoolean(prefs::kRemoveUsersRemoteCommand, false);
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
// Make a copy of the list since we'll be removing users (and the list would
// change underneath us if we used a reference).
const user_manager::UserList user_list = user_manager->GetUsers();
for (user_manager::User* user : user_list)
user_manager->RemoveUser(user->GetAccountId(), nullptr);
return true;
}
void LogOut() {
if (!g_log_out_override_callback.is_null()) {
std::move(g_log_out_override_callback).Run();
return;
}
chrome::AttemptUserExit();
}
void OverrideLogOutForTesting(base::OnceClosure callback) {
g_log_out_override_callback = std::move(callback);
}
void InitiateUserRemoval(base::OnceClosure on_pref_persisted_callback) {
PrefService* local_state = g_browser_process->local_state();
local_state->SetBoolean(prefs::kRemoveUsersRemoteCommand, true);
local_state->CommitPendingWrite(base::BindOnce(
[](base::OnceClosure on_pref_persisted_callback) {
// Start the failsafe timer.
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::BindOnce(&LogOut), kFailsafeTimerTimeout);
if (!on_pref_persisted_callback.is_null())
std::move(on_pref_persisted_callback).Run();
},
std::move(on_pref_persisted_callback)));
}
} // namespace user_removal_manager
} // namespace chromeos
// 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.
#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_USER_REMOVAL_MANAGER_H_
#define CHROME_BROWSER_CHROMEOS_SYSTEM_USER_REMOVAL_MANAGER_H_
#include "base/callback_forward.h"
// This file contains a collection of functions that are used to support
// removing users from the device.
//
// InitiateUserRemoval starts the process and restarts the Chrome session back
// to the login screen, while RemoveUsersIfNeeded does the actual removal at the
// login screen.
namespace chromeos {
namespace user_removal_manager {
// This function is called early in the startup sequence on the login screen. It
// removes users from the device in case the magic pref is set to true, and
// otherwise it does nothing. Returns true if users were removed, false
// otherwise.
bool RemoveUsersIfNeeded();
// Performs a log out. Can be overridden for testing.
void LogOut();
// Overrides LogOut (AttemptUserExit) inside tests with a custom callback that
// gets run instead.
void OverrideLogOutForTesting(base::OnceClosure callback);
// Write the magic local_state pref and run the callback once the pref is
// persisted to disk (the callback is expected to restart the Chrome session
// back to the login screen). Also start a fail-safe timer that shuts down
// Chrome in case the callback doesn't do its job in time (eg. callback wants to
// do some network communication, but the connection is lost).
void InitiateUserRemoval(base::OnceClosure on_pref_persisted_callback);
} // namespace user_removal_manager
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_SYSTEM_USER_REMOVAL_MANAGER_H_
// 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/system/user_removal_manager.h"
#include <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/test_mock_time_task_runner.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace {
class FakeChromeUserRemovalManager : public FakeChromeUserManager {
public:
FakeChromeUserRemovalManager() = default;
void RemoveUser(const AccountId& account_id,
user_manager::RemoveUserDelegate* delegate) override {
RemoveUserFromList(account_id);
}
private:
DISALLOW_COPY_AND_ASSIGN(FakeChromeUserRemovalManager);
};
class UserRemovalManagerTest : public testing::Test {
protected:
UserRemovalManagerTest();
~UserRemovalManagerTest() override;
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
const ScopedTestingLocalState local_state_;
const user_manager::ScopedUserManager scoped_user_manager_;
private:
DISALLOW_COPY_AND_ASSIGN(UserRemovalManagerTest);
};
UserRemovalManagerTest::UserRemovalManagerTest()
: task_runner_(base::MakeRefCounted<base::TestMockTimeTaskRunner>(
base::TestMockTimeTaskRunner::Type::kBoundToThread)),
local_state_(TestingBrowserProcess::GetGlobal()),
scoped_user_manager_(std::make_unique<FakeChromeUserRemovalManager>()) {}
UserRemovalManagerTest::~UserRemovalManagerTest() = default;
} // namespace
// Test that the InitiateUserRemoval/RemoveUsersIfNeeded sequence results in
// users being removed from the device.
TEST_F(UserRemovalManagerTest, TestUserRemovingWorks) {
FakeChromeUserManager* fake_user_manager =
static_cast<FakeChromeUserManager*>(user_manager::UserManager::Get());
fake_user_manager->AddUser(AccountId::FromUserEmailGaiaId("user1", "1"));
fake_user_manager->AddUser(AccountId::FromUserEmailGaiaId("user2", "2"));
fake_user_manager->AddUser(AccountId::FromUserEmailGaiaId("user3", "3"));
fake_user_manager->AddPublicAccountUser(
AccountId::FromUserEmailGaiaId("public1", "4"));
user_removal_manager::InitiateUserRemoval(base::OnceClosure());
EXPECT_TRUE(user_removal_manager::RemoveUsersIfNeeded());
EXPECT_TRUE(fake_user_manager->GetUsers().empty());
}
// Test that the failsafe timer runs LogOut after 60 seconds.
TEST_F(UserRemovalManagerTest, TestFailsafeTimer) {
bool log_out_called = false;
user_removal_manager::OverrideLogOutForTesting(base::BindOnce(
[](bool* log_out_called) { *log_out_called = true; }, &log_out_called));
// This call starts the timer.
user_removal_manager::InitiateUserRemoval(base::Closure());
// After 55s the timer is not run yet.
task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(55));
EXPECT_FALSE(log_out_called);
// After 60s the timer is run.
task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(5));
EXPECT_TRUE(log_out_called);
}
} // namespace chromeos
......@@ -1904,6 +1904,10 @@ const char kReportingUsers[] = "reporting_users";
const char kArcAppInstallEventLoggingEnabled[] =
"arc.app_install_event_logging_enabled";
// Whether we received the remove users remote command, and hence should proceed
// with removing the users while at the login screen.
const char kRemoveUsersRemoteCommand[] = "remove_users_remote_command";
#endif // defined(OS_CHROMEOS)
// Whether there is a Flash version installed that supports clearing LSO data.
......
......@@ -644,6 +644,7 @@ extern const char kPowerMetricsIdleSuspendCount[];
extern const char kPowerMetricsLidClosedSuspendCount[];
extern const char kReportingUsers[];
extern const char kArcAppInstallEventLoggingEnabled[];
extern const char kRemoveUsersRemoteCommand[];
#endif // defined(OS_CHROMEOS)
extern const char kClearPluginLSODataEnabled[];
......
......@@ -14,6 +14,7 @@
namespace policy {
class RemoteCommandJob;
class RemoteCommandsService;
// An interface class for creating remote commands based on command type.
class POLICY_EXPORT RemoteCommandsFactory {
......@@ -21,7 +22,8 @@ class POLICY_EXPORT RemoteCommandsFactory {
virtual ~RemoteCommandsFactory();
virtual std::unique_ptr<RemoteCommandJob> BuildJobForType(
enterprise_management::RemoteCommand_Type type) = 0;
enterprise_management::RemoteCommand_Type type,
RemoteCommandsService* service) = 0;
private:
DISALLOW_ASSIGN(RemoteCommandsFactory);
......
......@@ -9,6 +9,7 @@
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/syslog_logging.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
......@@ -80,6 +81,11 @@ void RemoteCommandsService::SetClockForTesting(const base::TickClock* clock) {
queue_.SetClockForTesting(clock);
}
void RemoteCommandsService::SetOnCommandAckedCallback(
base::OnceClosure callback) {
on_command_acked_callback_ = std::move(callback);
}
void RemoteCommandsService::EnqueueCommand(
const enterprise_management::RemoteCommand& command) {
if (!command.has_type() || !command.has_command_id()) {
......@@ -96,7 +102,7 @@ void RemoteCommandsService::EnqueueCommand(
fetched_command_ids_.push_back(command.command_id());
std::unique_ptr<RemoteCommandJob> job =
factory_->BuildJobForType(command.type());
factory_->BuildJobForType(command.type(), this);
if (!job || !job->Init(queue_.GetNowTicks(), command)) {
SYSLOG(ERROR) << "Initialization of remote command failed.";
......@@ -160,6 +166,9 @@ void RemoteCommandsService::OnRemoteCommandsFetched(
SYSLOG(INFO) << "Remote commands fetched.";
command_fetch_in_progress_ = false;
if (!on_command_acked_callback_.is_null())
std::move(on_command_acked_callback_).Run();
// TODO(binjin): Add retrying on errors. See http://crbug.com/466572.
if (status == DM_STATUS_SUCCESS) {
for (const auto& command : commands)
......
......@@ -8,6 +8,7 @@
#include <memory>
#include <vector>
#include "base/callback_forward.h"
#include "base/containers/circular_deque.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
......@@ -53,6 +54,8 @@ class POLICY_EXPORT RemoteCommandsService
// Set an alternative clock for testing.
void SetClockForTesting(const base::TickClock* clock);
virtual void SetOnCommandAckedCallback(base::OnceClosure callback);
private:
// Helper function to enqueue a command which we get from server.
void EnqueueCommand(const enterprise_management::RemoteCommand& command);
......@@ -96,6 +99,10 @@ class POLICY_EXPORT RemoteCommandsService
std::unique_ptr<RemoteCommandsFactory> factory_;
CloudPolicyClient* const client_;
// Callback which gets called after the last command got ACK'd to the server
// as executed.
base::OnceClosure on_command_acked_callback_;
base::WeakPtrFactory<RemoteCommandsService> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(RemoteCommandsService);
......
......@@ -68,7 +68,8 @@ class MockTestRemoteCommandFactory : public RemoteCommandsFactory {
private:
// RemoteCommandJobsFactory:
std::unique_ptr<RemoteCommandJob> BuildJobForType(
em::RemoteCommand_Type type) override {
em::RemoteCommand_Type type,
RemoteCommandsService* service) override {
if (type != em::RemoteCommand_Type_COMMAND_ECHO_TEST) {
ADD_FAILURE();
return nullptr;
......@@ -335,4 +336,36 @@ TEST_F(RemoteCommandsServiceTest, NewCommandFollwingFetch) {
EXPECT_EQ(0u, server_->NumberOfCommandsPendingResult());
}
// Tests that on_command_acked_callback_ gets called after the commands get
// acked/fetched (one function handles both).
TEST_F(RemoteCommandsServiceTest, AckedCallback) {
std::unique_ptr<MockTestRemoteCommandFactory> factory(
new MockTestRemoteCommandFactory());
EXPECT_CALL(*factory, BuildTestCommand()).Times(1);
StartService(std::move(factory));
bool on_command_acked_callback_called = false;
remote_commands_service_->SetOnCommandAckedCallback(base::BindOnce(
[](bool* on_command_acked_callback_called) {
*on_command_acked_callback_called = true;
},
&on_command_acked_callback_called));
// Set up expectations on fetch commands calls. The first request will fetch
// one command, and the second will fetch none but provide result for the
// previous command instead.
cloud_policy_client_->ExpectFetchCommands(0u, 1u, base::Closure());
cloud_policy_client_->ExpectFetchCommands(1u, 0u, base::Closure());
// Issue a command and manually start a command fetch.
server_->IssueCommand(em::RemoteCommand_Type_COMMAND_ECHO_TEST, kTestPayload,
base::Bind(&ExpectSucceededJob, kTestPayload), false);
EXPECT_TRUE(remote_commands_service_->FetchRemoteCommands());
FlushAllTasks();
EXPECT_TRUE(on_command_acked_callback_called);
}
} // namespace policy
......@@ -1411,6 +1411,9 @@ message RemoteCommand {
// Forwards a user command received from the management server to the ARC++
// side. The payload is opaque to Chrome OS.
USER_ARC_COMMAND = 4;
// Wipe all the users off of the device.
DEVICE_WIPE_USERS = 5;
}
// The command type.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment