forked from chromium/chromium
-
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remote powerwash on Chromad via policy
Implement remote powerwash on Chromad, using the recently added ChromadToCloudMigrationEnabled policy. The powerwash will be used to start the migration of AD managed devices into cloud management. Besides having the new policy enabled, the device needs to be on the login screen and the enrollment ID must have already been uploaded to DMServer. Bug: 1209246 Change-Id: I6a67b8a8a43c28bc5f03f27e96d0415f69b6bc83 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3141873 Reviewed-by: Maksim Ivanov <emaxx@chromium.org> Reviewed-by: Roman Sorokin <rsorokin@chromium.org> Reviewed-by: Colin Blundell <blundell@chromium.org> Commit-Queue: Felipe Andrade <fsandrade@chromium.org> Cr-Commit-Position: refs/heads/main@{#959123}
- Loading branch information
Felipe Andrade
authored and
Chromium LUCI CQ
committed
Jan 14, 2022
1 parent
2c36908
commit 6ff1599
Showing
12 changed files
with
668 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
194 changes: 194 additions & 0 deletions
194
chrome/browser/ash/policy/active_directory/active_directory_migration_manager.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
// Copyright 2022 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/ash/policy/active_directory/active_directory_migration_manager.h" | ||
|
||
#include <utility> | ||
|
||
#include "ash/constants/ash_pref_names.h" | ||
#include "base/bind.h" | ||
#include "base/location.h" | ||
#include "base/time/time.h" | ||
#include "chrome/common/pref_names.h" | ||
#include "chromeos/dbus/session_manager/session_manager_client.h" | ||
#include "components/policy/proto/device_management_backend.pb.h" | ||
#include "components/prefs/pref_change_registrar.h" | ||
#include "components/prefs/pref_registry_simple.h" | ||
#include "components/prefs/pref_service.h" | ||
#include "components/session_manager/core/session_manager.h" | ||
#include "content/public/browser/browser_task_traits.h" | ||
#include "content/public/browser/browser_thread.h" | ||
|
||
namespace em = enterprise_management; | ||
|
||
namespace policy { | ||
|
||
namespace { | ||
|
||
// The amount of time we wait before actively checking the preconditions to | ||
// start the migration again. | ||
constexpr base::TimeDelta kRetryDelay = base::Hours(1); | ||
|
||
// The amount of time we wait before triggering a new powerwash, in case the | ||
// last request has failed for any reason. | ||
constexpr base::TimeDelta kPowerwashBackoffTime = base::Days(1); | ||
|
||
// Returns true if any user is logged in (session is started). | ||
bool IsUserLoggedIn() { | ||
auto* session_manager = session_manager::SessionManager::Get(); | ||
return session_manager && session_manager->IsSessionStarted(); | ||
} | ||
|
||
} // namespace | ||
|
||
ActiveDirectoryMigrationManager::ActiveDirectoryMigrationManager( | ||
PrefService* local_state) | ||
: local_state_(local_state) { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
DCHECK(local_state_); | ||
|
||
// Listen to user session state changes. | ||
auto* session_manager = session_manager::SessionManager::Get(); | ||
if (session_manager) { | ||
session_manager->AddObserver(this); | ||
} | ||
} | ||
|
||
ActiveDirectoryMigrationManager::~ActiveDirectoryMigrationManager() { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
// Stop listening to user session state changes. | ||
auto* session_manager = session_manager::SessionManager::Get(); | ||
if (session_manager) { | ||
session_manager->RemoveObserver(this); | ||
} | ||
} | ||
|
||
// static | ||
void ActiveDirectoryMigrationManager::RegisterLocalStatePrefs( | ||
PrefRegistrySimple* registry) { | ||
registry->RegisterTimePref(prefs::kLastChromadMigrationAttemptTime, | ||
/*default_value=*/base::Time()); | ||
} | ||
|
||
void ActiveDirectoryMigrationManager::Init() { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
// Listen to pref changes. | ||
pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); | ||
pref_change_registrar_->Init(local_state_); | ||
pref_change_registrar_->Add( | ||
prefs::kEnrollmentIdUploadedOnChromad, | ||
base::BindRepeating( | ||
&ActiveDirectoryMigrationManager::OnEnrollmentIdUploadedPrefChanged, | ||
weak_ptr_factory_.GetWeakPtr())); | ||
pref_change_registrar_->Add( | ||
ash::prefs::kChromadToCloudMigrationEnabled, | ||
base::BindRepeating(&ActiveDirectoryMigrationManager:: | ||
OnChromadMigrationEnabledPrefChanged, | ||
weak_ptr_factory_.GetWeakPtr())); | ||
|
||
// Check the conditions here as well, because this manager might be | ||
// initialized while the pre-conditions are already satisfied. | ||
TryToStartMigration(); | ||
} | ||
|
||
void ActiveDirectoryMigrationManager::SetStatusCallbackForTesting( | ||
StatusCallback callback) { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
status_callback_for_testing_ = std::move(callback); | ||
} | ||
|
||
bool ActiveDirectoryMigrationManager::HasUploadedEnrollmentId() const { | ||
return local_state_->GetBoolean(prefs::kEnrollmentIdUploadedOnChromad); | ||
} | ||
|
||
bool ActiveDirectoryMigrationManager::IsChromadMigrationEnabled() const { | ||
return local_state_->GetBoolean(ash::prefs::kChromadToCloudMigrationEnabled); | ||
} | ||
|
||
bool ActiveDirectoryMigrationManager::HasBackoffTimePassed() const { | ||
base::Time last_migration_attempt_time = | ||
local_state_->GetTime(prefs::kLastChromadMigrationAttemptTime); | ||
base::Time now = base::Time::Now(); | ||
|
||
return now - last_migration_attempt_time > kPowerwashBackoffTime; | ||
} | ||
|
||
void ActiveDirectoryMigrationManager::OnEnrollmentIdUploadedPrefChanged() { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
TryToStartMigration(); | ||
} | ||
|
||
void ActiveDirectoryMigrationManager::OnChromadMigrationEnabledPrefChanged() { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
TryToStartMigration(); | ||
} | ||
|
||
void ActiveDirectoryMigrationManager::OnLoginOrLockScreenVisible() { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
TryToStartMigration(); | ||
} | ||
|
||
void ActiveDirectoryMigrationManager::TryToStartMigration() { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
bool is_on_login_screen = !IsUserLoggedIn(); | ||
|
||
if (is_on_login_screen && HasUploadedEnrollmentId() && | ||
IsChromadMigrationEnabled() && HasBackoffTimePassed()) { | ||
StartPowerwash(); | ||
MaybeRunStatusCallback(/*started=*/true, /*rescheduled=*/false); | ||
return; | ||
} | ||
|
||
// Theoretically, the following reschedule logic is not necessary. However, it | ||
// was added as a fallback, in case any of the signals this class listens is | ||
// not triggered as expected. Ultimatelly, we want to avoid inactive devices | ||
// getting stuck and not migrating. | ||
if (is_on_login_screen && !retry_already_scheduled_) { | ||
retry_already_scheduled_ = true; | ||
content::GetUIThreadTaskRunner({})->PostDelayedTask( | ||
FROM_HERE, | ||
base::BindOnce(&ActiveDirectoryMigrationManager::RetryToStartMigration, | ||
weak_ptr_factory_.GetWeakPtr()), | ||
kRetryDelay); | ||
|
||
MaybeRunStatusCallback(/*started=*/false, /*rescheduled=*/true); | ||
return; | ||
} | ||
|
||
MaybeRunStatusCallback(/*started=*/false, /*rescheduled=*/false); | ||
} | ||
|
||
void ActiveDirectoryMigrationManager::RetryToStartMigration() { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
retry_already_scheduled_ = false; | ||
TryToStartMigration(); | ||
} | ||
|
||
void ActiveDirectoryMigrationManager::StartPowerwash() { | ||
local_state_->SetTime(prefs::kLastChromadMigrationAttemptTime, | ||
base::Time::Now()); | ||
|
||
// Unsigned remote powerwash requests are allowed in AD mode. | ||
chromeos::SessionManagerClient::Get()->StartRemoteDeviceWipe( | ||
em::SignedData()); | ||
} | ||
|
||
void ActiveDirectoryMigrationManager::MaybeRunStatusCallback(bool started, | ||
bool rescheduled) { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
if (status_callback_for_testing_) { | ||
std::move(status_callback_for_testing_).Run(started, rescheduled); | ||
} | ||
} | ||
|
||
} // namespace policy |
107 changes: 107 additions & 0 deletions
107
chrome/browser/ash/policy/active_directory/active_directory_migration_manager.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright 2022 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_ASH_POLICY_ACTIVE_DIRECTORY_ACTIVE_DIRECTORY_MIGRATION_MANAGER_H_ | ||
#define CHROME_BROWSER_ASH_POLICY_ACTIVE_DIRECTORY_ACTIVE_DIRECTORY_MIGRATION_MANAGER_H_ | ||
|
||
#include <memory> | ||
|
||
#include "base/callback.h" | ||
#include "base/memory/raw_ptr.h" | ||
#include "base/memory/weak_ptr.h" | ||
#include "components/session_manager/core/session_manager_observer.h" | ||
|
||
class PrefChangeRegistrar; | ||
class PrefRegistrySimple; | ||
class PrefService; | ||
|
||
namespace policy { | ||
|
||
// Manages the migration of AD managed devices into cloud management. The goal | ||
// is to start the migration when (a) the device is on the login screen, (b) the | ||
// enrollment ID has already been uploaded to DMServer and (c) the | ||
// `ChromadToCloudMigrationEnabled` policy is enabled. After being constructed, | ||
// this class listens to changes from the `SessionManager` and from the | ||
// `kEnrollmentIdUploadedOnChromad` and `kChromadToCloudMigrationEnabled` local | ||
// state prefs. Additionally, these checks are periodically executed while the | ||
// device is on the login screen. | ||
class ActiveDirectoryMigrationManager | ||
: public session_manager::SessionManagerObserver { | ||
public: | ||
explicit ActiveDirectoryMigrationManager(PrefService* local_state); | ||
|
||
~ActiveDirectoryMigrationManager() override; | ||
|
||
// Disallow copy and assignment. | ||
ActiveDirectoryMigrationManager(const ActiveDirectoryMigrationManager&) = | ||
delete; | ||
ActiveDirectoryMigrationManager& operator=( | ||
const ActiveDirectoryMigrationManager&) = delete; | ||
|
||
static void RegisterLocalStatePrefs(PrefRegistrySimple* registry); | ||
|
||
// Registers to prefs change and tries to start the migration. | ||
void Init(); | ||
|
||
// Callback called when the `TryToStartMigration` method is executed. Returns | ||
// whether the migrations started or not, and whether a retry was scheduled or | ||
// not. | ||
using StatusCallback = | ||
base::OnceCallback<void(bool started, bool rescheduled)>; | ||
|
||
// Only used for testing. | ||
void SetStatusCallbackForTesting(StatusCallback callback); | ||
|
||
private: | ||
// Returns true if the enrollment ID has already been uploaded. | ||
bool HasUploadedEnrollmentId() const; | ||
|
||
// Returns true if the migration of Chromad devices to cloud management is | ||
// enabled. | ||
bool IsChromadMigrationEnabled() const; | ||
|
||
// Returns true if the last powerwash attempt happened more than | ||
// `kPowerwashBackoffTime` ago. | ||
bool HasBackoffTimePassed() const; | ||
|
||
// Pref change handlers. | ||
void OnEnrollmentIdUploadedPrefChanged(); | ||
void OnChromadMigrationEnabledPrefChanged(); | ||
|
||
// session_manager::SessionManagerObserver: | ||
void OnLoginOrLockScreenVisible() override; | ||
|
||
// Triggers a device powerwash, if the pre-requisites are satisfied. Called | ||
// every time one of the three events of interest happens. Also called | ||
// periodically while the device is on the login screen. | ||
void TryToStartMigration(); | ||
|
||
// Executes the same steps as `TryToStartMigration`, but also updates the | ||
// value of `retry_already_scheduled_` accordingly. | ||
void RetryToStartMigration(); | ||
|
||
// Sends a device powerwash request through D-Bus. | ||
void StartPowerwash(); | ||
|
||
// Runs the `status_callback_for_testing_`, if it's not empty. Passes the | ||
// received boolean values to the callback. Only used for testing. | ||
void MaybeRunStatusCallback(bool started, bool rescheduled); | ||
|
||
// Local state prefs, not owned. | ||
raw_ptr<PrefService> local_state_; | ||
|
||
// Observer for Chromad migration related prefs. | ||
std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_; | ||
|
||
bool retry_already_scheduled_ = false; | ||
|
||
StatusCallback status_callback_for_testing_; | ||
|
||
// Must be the last member. | ||
base::WeakPtrFactory<ActiveDirectoryMigrationManager> weak_ptr_factory_{this}; | ||
}; | ||
|
||
} // namespace policy | ||
|
||
#endif // CHROME_BROWSER_ASH_POLICY_ACTIVE_DIRECTORY_ACTIVE_DIRECTORY_MIGRATION_MANAGER_H_ |
Oops, something went wrong.