Commit 26a6b0bb authored by Toni Barzic's avatar Toni Barzic Committed by Commit Bot

Remove demo mode resources when a user logs in on a managed device

Device getting enterprise enrolled, and then used should be a good
signal that demo mode resources are no longer needed.

An exception is made for demo mode implemented through public sessions
(as it's the case today) - in that case retailer might attempt to swich
to the revamped demo mode sessions, so might need the resources after
all.

BUG=827368

Change-Id: Ia5645b059050f9d7858ba34acd84201969b88b71
Reviewed-on: https://chromium-review.googlesource.com/1176652
Commit-Queue: Toni Baržić <tbarzic@chromium.org>
Reviewed-by: default avatarMichael Giuffrida <michaelpg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#584222}
parent fe045298
......@@ -4,6 +4,7 @@
#include "chrome/browser/chromeos/login/demo_mode/demo_mode_resources_remover.h"
#include <memory>
#include <utility>
#include "base/files/file_path.h"
......@@ -13,10 +14,19 @@
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/post_task.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_type.h"
#include "third_party/re2/src/re2/re2.h"
namespace chromeos {
......@@ -28,6 +38,14 @@ DemoModeResourcesRemover* g_instance = nullptr;
// have been removed from the device.
constexpr char kDemoModeResourcesRemoved[] = "demo_mode_resources_removed";
// Regex matching legacy demo retail mode domains.
constexpr char kLegacyDemoRetailModeDomainRegex[] =
"[[:alpha:]]{2}-retailmode.com";
// An extra legacy demo retail mode domain that does not match
// |kLegacyDemoRetailModeDomainRegex|.
constexpr char kExtraLegacyDemoRetailModeDomain[] = "us2-retailmode.com";
// Deletes directory at |path| from the device.
DemoModeResourcesRemover::RemovalResult RemoveDirectory(
const base::FilePath& path) {
......@@ -40,6 +58,22 @@ DemoModeResourcesRemover::RemovalResult RemoveDirectory(
return DemoModeResourcesRemover::RemovalResult::kSuccess;
}
// Tests whether the session with user |user| is part of legacy demo mode -
// a public session in a legacy demo retail mode domain.
// Note that DemoSession::IsDeviceInDemoMode will return false for these
// sessions.
bool IsLegacyDemoRetailModeSession(const user_manager::User* user) {
if (user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT)
return false;
const std::string enrollment_domain =
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetEnterpriseEnrollmentDomain();
return DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
enrollment_domain);
}
} // namespace
// static
......@@ -64,15 +98,46 @@ DemoModeResourcesRemover* DemoModeResourcesRemover::Get() {
return g_instance;
}
// static
bool DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
const std::string& domain) {
return RE2::FullMatch(domain, kLegacyDemoRetailModeDomainRegex) ||
domain == kExtraLegacyDemoRetailModeDomain;
}
DemoModeResourcesRemover::~DemoModeResourcesRemover() {
CHECK_EQ(g_instance, this);
g_instance = nullptr;
ChromeUserManager::Get()->RemoveSessionStateObserver(this);
}
void DemoModeResourcesRemover::LowDiskSpace(uint64_t free_disk_space) {
AttemptRemoval(RemovalReason::kLowDiskSpace, RemovalCallback());
}
void DemoModeResourcesRemover::ActiveUserChanged(
const user_manager::User* user) {
// Ignore user activity in guest sessions.
if (user->GetType() == user_manager::USER_TYPE_GUEST)
return;
// Do not remove resources if the device is in a legacy derelict demo session,
// which is implemented as kiosk - note that this is different than sessions
// detected by IsLegacyDemoRetailModeSession().
if (DemoAppLauncher::IsDemoAppSession(user->GetAccountId()))
return;
// Attempt resources removal if the device is managed, and not in a retail
// mode domain.
if (g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->IsEnterpriseManaged() &&
!IsLegacyDemoRetailModeSession(user)) {
AttemptRemoval(RemovalReason::kEnterpriseEnrolled, RemovalCallback());
}
}
void DemoModeResourcesRemover::AttemptRemoval(RemovalReason reason,
RemovalCallback callback) {
if (local_state_->GetBoolean(kDemoModeResourcesRemoved)) {
......@@ -115,6 +180,7 @@ DemoModeResourcesRemover::DemoModeResourcesRemover(PrefService* local_state)
g_instance = this;
cryptohome_observer_.Add(DBusThreadManager::Get()->GetCryptohomeClient());
ChromeUserManager::Get()->AddSessionStateObserver(this);
}
void DemoModeResourcesRemover::OnRemovalDone(RemovalResult result) {
......@@ -124,6 +190,7 @@ void DemoModeResourcesRemover::OnRemovalDone(RemovalResult result) {
if (result == RemovalResult::kNotFound || result == RemovalResult::kSuccess) {
local_state_->SetBoolean(kDemoModeResourcesRemoved, true);
cryptohome_observer_.RemoveAll();
ChromeUserManager::Get()->RemoveSessionStateObserver(this);
}
UMA_HISTOGRAM_ENUMERATION("DemoMode.ResourcesRemoval.Result", result);
......
......@@ -6,6 +6,7 @@
#define CHROME_BROWSER_CHROMEOS_LOGIN_DEMO_MODE_DEMO_MODE_RESOURCES_REMOVER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
......@@ -13,6 +14,7 @@
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "components/user_manager/user_manager.h"
class PrefRegistrySimple;
class PrefService;
......@@ -28,10 +30,12 @@ namespace chromeos {
// Demo mode resources are deleted if the device is not in demo mode, and any
// of the following conditions are satisfied:
// * device is running low on disk space
// * TODO(crbug.com/827368): device is enrolled in a non-demo-mode domain
// * device is enrolled in a non-demo-mode domain
// * TODO(crbug.com/827368): enough non-demo-mode user activity has been
// detected on the device
class DemoModeResourcesRemover : public CryptohomeClient::Observer {
class DemoModeResourcesRemover
: public CryptohomeClient::Observer,
public user_manager::UserManager::UserSessionStateObserver {
public:
// The reason a removal was requested.
// DO NOT REORDER - used to report metrics.
......@@ -85,11 +89,21 @@ class DemoModeResourcesRemover : public CryptohomeClient::Observer {
static std::unique_ptr<DemoModeResourcesRemover> CreateIfNeeded(
PrefService* local_state);
// Method used to determine whether a domain is associated with legacy demo
// retail mode, where demo mode sessions are implemented as public sessions.
// Exposed so the matching can be tested.
// TODO(crbug.com/874778): Remove after legacy retail mode domains have been
// disabled.
static bool IsLegacyDemoRetailModeDomain(const std::string& domain);
~DemoModeResourcesRemover() override;
// CryptohomeClient::Observer:
void LowDiskSpace(uint64_t free_disk_space) override;
// user_manager::UserManager::UserSessionStateObserver:
void ActiveUserChanged(const user_manager::User* user) override;
// Requests demo mode resources removal from the disk. If a removal operation
// is already in progress, this method will schedule the callback to be run
// with the result of the operation in progress.
......
......@@ -13,11 +13,16 @@
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/test/scoped_task_environment.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/chromeos/settings/stub_install_attributes.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_cryptohome_client.h"
#include "components/prefs/testing_pref_service.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_names.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
......@@ -40,6 +45,9 @@ class DemoModeResourcesRemoverTest : public testing::Test {
~DemoModeResourcesRemoverTest() override = default;
void SetUp() override {
install_attributes_ = std::make_unique<ScopedStubInstallAttributes>(
CreateInstallAttributes());
auto cryptohome_client = std::make_unique<FakeCryptohomeClient>();
cryptohome_client_ = cryptohome_client.get();
......@@ -52,6 +60,9 @@ class DemoModeResourcesRemoverTest : public testing::Test {
DemoSession::OverridePreInstalledDemoResourcesPathForTesting(
&demo_resources_path_);
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
std::make_unique<FakeChromeUserManager>());
DemoSession::SetDemoModeEnrollmentTypeForTesting(
DemoSession::EnrollmentType::kUnenrolled);
......@@ -67,6 +78,10 @@ class DemoModeResourcesRemoverTest : public testing::Test {
}
protected:
virtual std::unique_ptr<StubInstallAttributes> CreateInstallAttributes() {
return StubInstallAttributes::CreateConsumerOwned();
}
bool CreateDemoModeResources() {
if (!base::CreateDirectory(demo_resources_path_))
return false;
......@@ -96,18 +111,120 @@ class DemoModeResourcesRemoverTest : public testing::Test {
return base::DirectoryExists(demo_resources_path_);
}
enum class TestUserType {
kRegular,
kGuest,
kPublicAccount,
kKiosk,
kDerelictDemoKiosk
};
void AddAndLogInUser(TestUserType type, DemoModeResourcesRemover* remover) {
FakeChromeUserManager* user_manager =
static_cast<FakeChromeUserManager*>(user_manager::UserManager::Get());
const user_manager::User* user = nullptr;
switch (type) {
case TestUserType::kRegular:
user =
user_manager->AddUser(AccountId::FromUserEmail("fake_user@test"));
break;
case TestUserType::kGuest:
user = user_manager->AddGuestUser();
break;
case TestUserType::kPublicAccount:
user = user_manager->AddPublicAccountUser(
AccountId::FromUserEmail("fake_user@test"));
break;
case TestUserType::kKiosk:
user = user_manager->AddKioskAppUser(
AccountId::FromUserEmail("fake_user@test"));
break;
case TestUserType::kDerelictDemoKiosk:
user = user_manager->AddKioskAppUser(user_manager::DemoAccountId());
break;
}
ASSERT_TRUE(user);
user_manager->LoginUser(user->GetAccountId());
user_manager->SwitchActiveUser(user->GetAccountId());
remover->ActiveUserChanged(user);
}
FakeCryptohomeClient* cryptohome_client_ = nullptr;
TestingPrefServiceSimple local_state_;
base::test::ScopedTaskEnvironment task_environment_;
content::TestBrowserThreadBundle thread_bundle_;
private:
std::unique_ptr<ScopedStubInstallAttributes> install_attributes_;
base::ScopedTempDir scoped_temp_dir_;
base::FilePath demo_resources_path_;
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
DISALLOW_COPY_AND_ASSIGN(DemoModeResourcesRemoverTest);
};
class ManagedDemoModeResourcesRemoverTest
: public DemoModeResourcesRemoverTest {
public:
ManagedDemoModeResourcesRemoverTest() = default;
~ManagedDemoModeResourcesRemoverTest() override = default;
std::unique_ptr<StubInstallAttributes> CreateInstallAttributes() override {
return StubInstallAttributes::CreateCloudManaged("test-domain",
"FAKE_DEVICE_ID");
}
private:
DISALLOW_COPY_AND_ASSIGN(ManagedDemoModeResourcesRemoverTest);
};
class DemoModeResourcesRemoverInLegacyDemoRetailModeTest
: public DemoModeResourcesRemoverTest {
public:
DemoModeResourcesRemoverInLegacyDemoRetailModeTest() = default;
~DemoModeResourcesRemoverInLegacyDemoRetailModeTest() override = default;
std::unique_ptr<StubInstallAttributes> CreateInstallAttributes() override {
return StubInstallAttributes::CreateCloudManaged("us-retailmode.com",
"FAKE_DEVICE_ID");
}
private:
DISALLOW_COPY_AND_ASSIGN(DemoModeResourcesRemoverInLegacyDemoRetailModeTest);
};
TEST(LegacyDemoRetailModeDomainMatching, Matching) {
EXPECT_TRUE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"us-retailmode.com"));
EXPECT_TRUE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"us2-retailmode.com"));
EXPECT_TRUE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"hr-retailmode.com"));
EXPECT_TRUE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"uk-retailmode.com"));
EXPECT_FALSE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"u1-retailmode.com"));
EXPECT_FALSE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"uss-retailmode.com"));
EXPECT_FALSE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"us4-retailmode.com"));
EXPECT_FALSE(
DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain("us-retailmode"));
EXPECT_FALSE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"us-retailmode.com.foo"));
EXPECT_FALSE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(""));
EXPECT_FALSE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"fake-domain.com"));
EXPECT_FALSE(DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain(
"us-us-retailmode.com"));
EXPECT_FALSE(
DemoModeResourcesRemover::IsLegacyDemoRetailModeDomain("us.com"));
}
TEST_F(DemoModeResourcesRemoverTest, LowDiskSpace) {
ASSERT_TRUE(CreateDemoModeResources());
......@@ -117,7 +234,7 @@ TEST_F(DemoModeResourcesRemoverTest, LowDiskSpace) {
EXPECT_EQ(DemoModeResourcesRemover::Get(), remover.get());
cryptohome_client_->NotifyLowDiskSpace(1024 * 1024 * 1024);
task_environment_.RunUntilIdle();
thread_bundle_.RunUntilIdle();
EXPECT_FALSE(DemoModeResourcesExist());
}
......@@ -132,7 +249,7 @@ TEST_F(DemoModeResourcesRemoverTest, LowDiskSpaceInDemoSession) {
EXPECT_FALSE(DemoModeResourcesRemover::Get());
cryptohome_client_->NotifyLowDiskSpace(1024 * 1024 * 1024);
task_environment_.RunUntilIdle();
thread_bundle_.RunUntilIdle();
EXPECT_TRUE(DemoModeResourcesExist());
}
......@@ -145,7 +262,7 @@ TEST_F(DemoModeResourcesRemoverTest, NotCreatedAfterResourcesRemoved) {
EXPECT_EQ(DemoModeResourcesRemover::Get(), remover.get());
cryptohome_client_->NotifyLowDiskSpace(1024 * 1024 * 1024);
task_environment_.RunUntilIdle();
thread_bundle_.RunUntilIdle();
EXPECT_FALSE(DemoModeResourcesExist());
// Reset the resources remover - subsequent attempts to create the remover
......@@ -166,7 +283,7 @@ TEST_F(DemoModeResourcesRemoverTest, AttemptRemoval) {
DemoModeResourcesRemover::RemovalReason::kEnterpriseEnrolled,
base::BindOnce(&RecordRemovalResult, &result));
task_environment_.RunUntilIdle();
thread_bundle_.RunUntilIdle();
ASSERT_TRUE(result.has_value());
EXPECT_EQ(DemoModeResourcesRemover::RemovalResult::kSuccess, result.value());
......@@ -184,7 +301,7 @@ TEST_F(DemoModeResourcesRemoverTest, AttemptRemovalResourcesNonExistent) {
DemoModeResourcesRemover::RemovalReason::kLowDiskSpace,
base::BindOnce(&RecordRemovalResult, &result));
task_environment_.RunUntilIdle();
thread_bundle_.RunUntilIdle();
ASSERT_TRUE(result.has_value());
EXPECT_EQ(DemoModeResourcesRemover::RemovalResult::kNotFound, result.value());
......@@ -225,7 +342,7 @@ TEST_F(DemoModeResourcesRemoverTest, ConcurrentRemovalAttempts) {
DemoModeResourcesRemover::RemovalReason::kLowDiskSpace,
base::BindOnce(&RecordRemovalResult, &result_2));
task_environment_.RunUntilIdle();
thread_bundle_.RunUntilIdle();
EXPECT_FALSE(DemoModeResourcesExist());
ASSERT_TRUE(result_1.has_value());
......@@ -245,7 +362,7 @@ TEST_F(DemoModeResourcesRemoverTest, RepeatedRemovalAttempt) {
remover->AttemptRemoval(
DemoModeResourcesRemover::RemovalReason::kLowDiskSpace,
DemoModeResourcesRemover::RemovalCallback());
task_environment_.RunUntilIdle();
thread_bundle_.RunUntilIdle();
EXPECT_FALSE(DemoModeResourcesExist());
......@@ -258,4 +375,119 @@ TEST_F(DemoModeResourcesRemoverTest, RepeatedRemovalAttempt) {
result.value());
}
TEST_F(DemoModeResourcesRemoverTest, NoRemovalOnLogin) {
ASSERT_TRUE(CreateDemoModeResources());
std::unique_ptr<DemoModeResourcesRemover> remover =
DemoModeResourcesRemover::CreateIfNeeded(&local_state_);
ASSERT_TRUE(remover.get());
AddAndLogInUser(TestUserType::kRegular, remover.get());
thread_bundle_.RunUntilIdle();
EXPECT_TRUE(DemoModeResourcesExist());
}
// Tests the kiosk app incarnation of demo mode.
TEST_F(DemoModeResourcesRemoverTest, NoRemovalInKioskDemoMode) {
ASSERT_TRUE(CreateDemoModeResources());
std::unique_ptr<DemoModeResourcesRemover> remover =
DemoModeResourcesRemover::CreateIfNeeded(&local_state_);
ASSERT_TRUE(remover.get());
AddAndLogInUser(TestUserType::kDerelictDemoKiosk, remover.get());
thread_bundle_.RunUntilIdle();
EXPECT_TRUE(DemoModeResourcesExist());
}
TEST_F(ManagedDemoModeResourcesRemoverTest, RemoveOnRegularLogin) {
ASSERT_TRUE(CreateDemoModeResources());
std::unique_ptr<DemoModeResourcesRemover> remover =
DemoModeResourcesRemover::CreateIfNeeded(&local_state_);
ASSERT_TRUE(remover.get());
AddAndLogInUser(TestUserType::kRegular, remover.get());
thread_bundle_.RunUntilIdle();
EXPECT_FALSE(DemoModeResourcesExist());
}
TEST_F(ManagedDemoModeResourcesRemoverTest, NoRemovalGuestLogin) {
ASSERT_TRUE(CreateDemoModeResources());
std::unique_ptr<DemoModeResourcesRemover> remover =
DemoModeResourcesRemover::CreateIfNeeded(&local_state_);
ASSERT_TRUE(remover.get());
AddAndLogInUser(TestUserType::kGuest, remover.get());
thread_bundle_.RunUntilIdle();
EXPECT_TRUE(DemoModeResourcesExist());
}
TEST_F(ManagedDemoModeResourcesRemoverTest, RemoveOnLowDiskInGuest) {
ASSERT_TRUE(CreateDemoModeResources());
std::unique_ptr<DemoModeResourcesRemover> remover =
DemoModeResourcesRemover::CreateIfNeeded(&local_state_);
ASSERT_TRUE(remover.get());
AddAndLogInUser(TestUserType::kGuest, remover.get());
cryptohome_client_->NotifyLowDiskSpace(1024 * 1024 * 1024);
thread_bundle_.RunUntilIdle();
EXPECT_FALSE(DemoModeResourcesExist());
}
TEST_F(ManagedDemoModeResourcesRemoverTest, RemoveOnPublicSessionLogin) {
ASSERT_TRUE(CreateDemoModeResources());
std::unique_ptr<DemoModeResourcesRemover> remover =
DemoModeResourcesRemover::CreateIfNeeded(&local_state_);
ASSERT_TRUE(remover.get());
AddAndLogInUser(TestUserType::kPublicAccount, remover.get());
thread_bundle_.RunUntilIdle();
EXPECT_FALSE(DemoModeResourcesExist());
}
TEST_F(ManagedDemoModeResourcesRemoverTest, RemoveInKioskSession) {
ASSERT_TRUE(CreateDemoModeResources());
std::unique_ptr<DemoModeResourcesRemover> remover =
DemoModeResourcesRemover::CreateIfNeeded(&local_state_);
ASSERT_TRUE(remover.get());
AddAndLogInUser(TestUserType::kKiosk, remover.get());
thread_bundle_.RunUntilIdle();
EXPECT_FALSE(DemoModeResourcesExist());
}
TEST_F(DemoModeResourcesRemoverInLegacyDemoRetailModeTest, NoRemovalOnLogin) {
ASSERT_TRUE(CreateDemoModeResources());
std::unique_ptr<DemoModeResourcesRemover> remover =
DemoModeResourcesRemover::CreateIfNeeded(&local_state_);
ASSERT_TRUE(remover.get());
AddAndLogInUser(TestUserType::kPublicAccount, remover.get());
thread_bundle_.RunUntilIdle();
EXPECT_TRUE(DemoModeResourcesExist());
}
TEST_F(DemoModeResourcesRemoverInLegacyDemoRetailModeTest,
RemoveOnLowDiskSpace) {
ASSERT_TRUE(CreateDemoModeResources());
std::unique_ptr<DemoModeResourcesRemover> remover =
DemoModeResourcesRemover::CreateIfNeeded(&local_state_);
ASSERT_TRUE(remover.get());
AddAndLogInUser(TestUserType::kPublicAccount, remover.get());
cryptohome_client_->NotifyLowDiskSpace(1024 * 1024 * 1024);
thread_bundle_.RunUntilIdle();
EXPECT_FALSE(DemoModeResourcesExist());
}
} // namespace chromeos
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