blob: 013b9b973a63c8620275d12a86d0b40a647b7ea5 [file] [log] [blame]
// Copyright 2021 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/browser/metrics/per_user_state_manager_chromeos.h"
#include "chrome/browser/ash/login/login_pref_names.h"
#include "chrome/browser/ash/login/startup_utils.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/metrics/profile_pref_names.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/test/base/testing_profile.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/unsent_log_store.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/testing_pref_service.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
namespace {
using ::testing::Eq;
using ::testing::Ne;
// Testing version of PerUserStateManager that decouples external dependencies
// used for unit testing purposes.
class TestPerUserStateManager : public PerUserStateManagerChromeOS {
public:
TestPerUserStateManager(user_manager::UserManager* user_manager,
PrefService* local_state,
const MetricsLogStore::StorageLimits& storage_limits,
const std::string& signing_key)
: PerUserStateManagerChromeOS(/*metrics_service_client=*/nullptr,
user_manager,
local_state,
storage_limits,
signing_key) {}
~TestPerUserStateManager() override = default;
void SetUserLogStore(std::unique_ptr<UnsentLogStore> log_store) override {
is_log_store_set_ = true;
}
void SetIsManaged(bool is_managed) { is_managed_ = is_managed; }
void SetDeviceMetricsConsent(bool metrics_consent) {
device_metrics_consent_ = metrics_consent;
}
void SetIsDeviceOwned(bool is_device_owned) {
is_device_owned_ = is_device_owned;
}
bool is_log_store_set() const { return is_log_store_set_; }
bool is_client_id_reset() const { return is_client_id_reset_; }
protected:
void UnsetUserLogStore() override { is_log_store_set_ = false; }
void ForceClientIdReset() override { is_client_id_reset_ = true; }
bool IsReportingPolicyManaged() const override { return is_managed_; }
bool GetDeviceMetricsConsent() const override {
return device_metrics_consent_;
}
bool HasUserLogStore() const override { return is_log_store_set_; }
bool IsDeviceOwned() const override { return is_device_owned_; }
void WaitForOwnershipStatus() override {
InitializeProfileMetricsState(
is_device_owned_ ? ash::DeviceSettingsService::OWNERSHIP_TAKEN
: ash::DeviceSettingsService::OWNERSHIP_NONE);
}
private:
bool is_log_store_set_ = false;
bool is_client_id_reset_ = false;
bool is_managed_ = false;
bool device_metrics_consent_ = true;
bool is_device_owned_ = true;
};
} // namespace
class PerUserStateManagerChromeOSTest : public testing::Test {
public:
PerUserStateManagerChromeOSTest() : signing_key_("signing_key") {}
// Profiles must be destructed on the UI thread.
~PerUserStateManagerChromeOSTest() override {
// Destruct before user manager because of dependency.
per_user_state_manager_.reset();
// Profiles must be destructed on the UI thread.
profile_.reset();
}
user_manager::User* RegisterUser(const AccountId& account_id) {
// Create profile.
TestingProfile::Builder profile_builder;
sync_preferences::PrefServiceMockFactory factory;
auto registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
RegisterUserProfilePrefs(registry.get());
std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs(
factory.CreateSyncable(registry.get()));
profile_builder.SetPrefService(std::move(prefs));
profile_ = profile_builder.Build();
return test_user_manager_->AddUserWithAffiliationAndTypeAndProfile(
account_id, false, user_manager::USER_TYPE_REGULAR, profile_.get());
}
user_manager::User* RegisterGuestUser() {
// Create profile.
TestingProfile::Builder profile_builder;
sync_preferences::PrefServiceMockFactory factory;
auto registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
RegisterUserProfilePrefs(registry.get());
std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs(
factory.CreateSyncable(registry.get()));
profile_builder.SetPrefService(std::move(prefs));
profile_ = profile_builder.Build();
auto* user = test_user_manager_->AddGuestUser();
ash::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
user, profile_.get());
return user;
}
void LoginRegularUser(user_manager::User* user) {
test_user_manager_->LoginUser(user->GetAccountId());
test_user_manager_->SwitchActiveUser(user->GetAccountId());
test_user_manager_->SimulateUserProfileLoad(user->GetAccountId());
}
void LoginGuestUser(user_manager::User* user) {
test_user_manager_->LoginUser(user->GetAccountId());
test_user_manager_->set_current_user_ephemeral(true);
test_user_manager_->SwitchActiveUser(user->GetAccountId());
test_user_manager_->SimulateUserProfileLoad(user->GetAccountId());
}
void InitializeProfileState(const std::string& user_id,
bool metrics_consent,
bool has_consented_to_metrics) {
profile_->GetPrefs()->SetString(prefs::kMetricsUserId, user_id);
profile_->GetPrefs()->SetBoolean(prefs::kMetricsUserConsent,
metrics_consent);
profile_->GetPrefs()->SetBoolean(
prefs::kMetricsRequiresClientIdResetOnConsent,
has_consented_to_metrics);
profile_->GetPrefs()->SetBoolean(prefs::kMetricsUserInheritOwnerConsent,
false);
}
void SetShouldInheritOwnerConsent(bool should_inherit) {
profile_->GetPrefs()->SetBoolean(prefs::kMetricsUserInheritOwnerConsent,
should_inherit);
}
void SetGuestOobeMetricsConsent(bool metrics_consent) {
GetLocalState()->SetBoolean(ash::prefs::kOobeGuestMetricsEnabled,
metrics_consent);
}
void RunUntilIdle() { task_environment_.RunUntilIdle(); }
PrefService* GetLocalState() { return &pref_service_; }
Profile* GetTestProfile() { return profile_.get(); }
TestPerUserStateManager* GetPerUserStateManager() {
return per_user_state_manager_.get();
}
protected:
void SetUp() override {
storage_limits_.min_ongoing_log_queue_count = 5;
storage_limits_.min_ongoing_log_queue_size = 10000;
storage_limits_.max_ongoing_log_size = 0;
test_user_manager_ = std::make_unique<ash::FakeChromeUserManager>();
per_user_state_manager_ = std::make_unique<TestPerUserStateManager>(
test_user_manager_.get(), &pref_service_, storage_limits_,
signing_key_);
ash::StartupUtils::RegisterPrefs(pref_service_.registry());
PerUserStateManagerChromeOS::RegisterPrefs(pref_service_.registry());
}
std::unique_ptr<TestPerUserStateManager> per_user_state_manager_;
std::unique_ptr<ash::FakeChromeUserManager> test_user_manager_;
std::unique_ptr<TestingProfile> profile_;
TestingPrefServiceSimple pref_service_;
// Profiles must be created in browser threads.
content::BrowserTaskEnvironment task_environment_;
MetricsLogStore::StorageLimits storage_limits_;
std::string signing_key_;
};
TEST_F(PerUserStateManagerChromeOSTest, UserIdErasedWhenConsentTurnedOff) {
auto* test_user =
RegisterUser(AccountId::FromUserEmailGaiaId("test@example.com", "1"));
InitializeProfileState(/*user_id=*/"user_id",
/*metrics_consent=*/true,
/*has_consented_to_metrics=*/true);
GetPerUserStateManager()->SetIsManaged(false);
LoginRegularUser(test_user);
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
GetPerUserStateManager()->SetCurrentUserMetricsConsent(false);
EXPECT_FALSE(
GetTestProfile()->GetPrefs()->GetBoolean(prefs::kMetricsUserConsent));
EXPECT_TRUE(GetTestProfile()->GetPrefs()->GetBoolean(
prefs::kMetricsRequiresClientIdResetOnConsent));
// Client ID should only be reset when going from off->on.
EXPECT_FALSE(GetPerUserStateManager()->is_client_id_reset());
// Ensure that state is clean.
EXPECT_THAT(GetTestProfile()->GetPrefs()->GetString(prefs::kMetricsUserId),
Eq(""));
EXPECT_THAT(GetLocalState()->GetString(prefs::kMetricsCurrentUserId), Eq(""));
}
TEST_F(PerUserStateManagerChromeOSTest,
ClientIdReset_WhenConsentTurnedOn_AndUserSentMetrics) {
auto* test_user =
RegisterUser(AccountId::FromUserEmailGaiaId("test@example.com", "1"));
InitializeProfileState(/*user_id=*/"", /*metrics_consent=*/false,
/*has_consented_to_metrics=*/true);
GetPerUserStateManager()->SetIsManaged(false);
// Simulate user login.
LoginRegularUser(test_user);
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
GetPerUserStateManager()->SetCurrentUserMetricsConsent(true);
EXPECT_TRUE(
GetTestProfile()->GetPrefs()->GetBoolean(prefs::kMetricsUserConsent));
EXPECT_TRUE(GetTestProfile()->GetPrefs()->GetBoolean(
prefs::kMetricsRequiresClientIdResetOnConsent));
// Client ID should be reset when going from off->on and user has sent
// metrics.
EXPECT_TRUE(GetPerUserStateManager()->is_client_id_reset());
}
TEST_F(PerUserStateManagerChromeOSTest,
ClientIdNotResetForUserWhoNeverReportedMetrics) {
auto* test_user =
RegisterUser(AccountId::FromUserEmailGaiaId("test@example.com", "1"));
InitializeProfileState(/*user_id=*/"", /*metrics_consent=*/false,
/*has_consented_to_metrics=*/false);
GetPerUserStateManager()->SetIsManaged(false);
// Simulate user login.
LoginRegularUser(test_user);
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
GetPerUserStateManager()->SetCurrentUserMetricsConsent(true);
EXPECT_TRUE(
GetTestProfile()->GetPrefs()->GetBoolean(prefs::kMetricsUserConsent));
EXPECT_TRUE(GetTestProfile()->GetPrefs()->GetBoolean(
prefs::kMetricsRequiresClientIdResetOnConsent));
// Client ID should not be reset when going from off->on and user had not sent
// metrics.
EXPECT_FALSE(GetPerUserStateManager()->is_client_id_reset());
}
TEST_F(PerUserStateManagerChromeOSTest,
EphemeralLogStoreUsedForGuestSessionWithDisabledPolicy) {
GetPerUserStateManager()->SetIsManaged(false);
GetPerUserStateManager()->SetDeviceMetricsConsent(false);
// Simulate ephemeral user login.
LoginGuestUser(RegisterGuestUser());
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
// Log store should be set to use temporary cryptohome for when device metrics
// consent is off.
EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set());
}
TEST_F(PerUserStateManagerChromeOSTest,
LocalStateLogStoreUsedForGuestWithEnabledPolicy) {
GetPerUserStateManager()->SetIsManaged(false);
GetPerUserStateManager()->SetDeviceMetricsConsent(true);
// Simulate ephemeral user login.
LoginGuestUser(RegisterGuestUser());
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
// Log store should not be loaded yet to store logs in local state for when
// device metrics consent is on.
EXPECT_FALSE(GetPerUserStateManager()->is_log_store_set());
}
TEST_F(PerUserStateManagerChromeOSTest,
GuestWithNoDeviceOwnerLoadsConsentSetOnOobe) {
GetPerUserStateManager()->SetIsManaged(false);
GetPerUserStateManager()->SetIsDeviceOwned(false);
// Guest user went through oobe.
SetGuestOobeMetricsConsent(true);
// Simulate ephemeral user login.
LoginGuestUser(RegisterGuestUser());
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
// Consent set by guest during OOBE.
EXPECT_TRUE(
*GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable());
// Ensure state has been reset.
EXPECT_FALSE(
GetLocalState()->GetBoolean(ash::prefs::kOobeGuestMetricsEnabled));
// Check to ensure that metrics consent is stored in profile pref.
EXPECT_TRUE(
GetTestProfile()->GetPrefs()->GetBoolean(prefs::kMetricsUserConsent));
// Log store should be set to use ephemeral partition in the absence of a
// device owner.
EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set());
}
TEST_F(PerUserStateManagerChromeOSTest, OwnerCannotUsePerUser) {
// Create device owner.
const AccountId account_id =
AccountId::FromUserEmailGaiaId("test@example.com", "1");
auto* test_user = RegisterUser(account_id);
test_user_manager_->SetOwnerId(account_id);
// Simulate user login.
LoginRegularUser(test_user);
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
// Owner should not have a consent.
EXPECT_FALSE(
GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable());
// User logs should still be persisted in the owner's cryptohome.
EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set());
}
TEST_F(PerUserStateManagerChromeOSTest,
NewOrMigratingUserInheritsOwnerConsent) {
auto* test_user =
RegisterUser(AccountId::FromUserEmailGaiaId("test@example.com", "1"));
InitializeProfileState(/*user_id=*/"", /*metrics_consent=*/false,
/*has_consented_to_metrics=*/false);
// User should inherit owner consent if migrating or new user.
SetShouldInheritOwnerConsent(true);
GetPerUserStateManager()->SetIsManaged(false);
GetPerUserStateManager()->SetDeviceMetricsConsent(true);
// Simulate user login.
LoginRegularUser(test_user);
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
// User consent should be set to true since pref is true and device metrics
// consent is also true.
EXPECT_TRUE(
GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable());
EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set());
}
// Multi-user sessions are deprecated, but still need to be supported. This test
// ensures that the primary user (user originally used to login) is used and all
// other users are ignored.
TEST_F(PerUserStateManagerChromeOSTest, MultiUserUsesPrimaryUser) {
auto* test_user1 =
RegisterUser(AccountId::FromUserEmailGaiaId("test1@example.com", "1"));
InitializeProfileState(/*user_id=*/"", /*metrics_consent=*/false,
/*has_consented_to_metrics=*/true);
GetPerUserStateManager()->SetIsManaged(false);
GetPerUserStateManager()->SetDeviceMetricsConsent(true);
// Simulate user login.
LoginRegularUser(test_user1);
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
GetPerUserStateManager()->SetCurrentUserMetricsConsent(true);
// User consent should be set to true since pref is true and device metrics
// consent is also true.
EXPECT_TRUE(
*GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable());
EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set());
// Create secondary user.
TestingProfile::Builder profile_builder;
sync_preferences::PrefServiceMockFactory factory;
auto registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
RegisterUserProfilePrefs(registry.get());
std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs(
factory.CreateSyncable(registry.get()));
profile_builder.SetPrefService(std::move(prefs));
auto test_user2_profile = profile_builder.Build();
AccountId test_user2_account_id =
AccountId::FromUserEmailGaiaId("test2@example.com", "2");
// Add user.
user_manager::User* test_user2 =
test_user_manager_->AddUserWithAffiliationAndTypeAndProfile(
test_user2_account_id, false, user_manager::USER_TYPE_REGULAR,
test_user2_profile.get());
// Explicitly set the user consent to false.
test_user2_profile->GetPrefs()->SetBoolean(prefs::kMetricsUserConsent, false);
// Simulate user login.
LoginRegularUser(test_user2);
// User log store is created async. Ensure that the log store loading
// finishes.
RunUntilIdle();
// User consent should still be true since that's the value of the primary
// user.
EXPECT_TRUE(
*GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable());
EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set());
// Enable user2's metrics consent and disable it.
test_user2_profile->GetPrefs()->SetBoolean(prefs::kMetricsUserConsent, true);
test_user2_profile->GetPrefs()->SetBoolean(prefs::kMetricsUserConsent, false);
// User consent should still be true since that's the value of the primary
// user.
EXPECT_TRUE(
*GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable());
// Profiles must be destructed on the UI thread.
test_user2_profile.reset();
}
} // namespace metrics