blob: 604ef0780269e39b590b2db5b215cabda7f16f35 [file]
// Copyright 2020 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/signin/dice_signed_in_profile_creator.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_manager_observer.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kProfileTestName[] = "profile_test_name";
std::unique_ptr<TestingProfile> BuildTestingProfile(const base::FilePath& path,
Profile::Delegate* delegate,
bool tokens_loaded) {
TestingProfile::Builder profile_builder;
profile_builder.SetDelegate(delegate);
profile_builder.SetPath(path);
std::unique_ptr<TestingProfile> profile =
IdentityTestEnvironmentProfileAdaptor::
CreateProfileForIdentityTestEnvironment(profile_builder);
if (!tokens_loaded) {
IdentityTestEnvironmentProfileAdaptor adaptor(profile.get());
adaptor.identity_test_env()->ResetToAccountsNotYetLoadedFromDiskState();
}
if (profile->GetPath() == ProfileManager::GetGuestProfilePath())
profile->SetGuestSession(true);
return profile;
}
// Simple ProfileManager creating testing profiles.
class UnittestProfileManager : public ProfileManagerWithoutInit {
public:
explicit UnittestProfileManager(const base::FilePath& user_data_dir)
: ProfileManagerWithoutInit(user_data_dir) {}
void set_tokens_loaded_at_creation(bool loaded) {
tokens_loaded_at_creation_ = loaded;
}
protected:
std::unique_ptr<Profile> CreateProfileHelper(
const base::FilePath& path) override {
if (!base::PathExists(path) && !base::CreateDirectory(path))
return nullptr;
return BuildTestingProfile(path, /*delegate=*/nullptr,
tokens_loaded_at_creation_);
}
std::unique_ptr<Profile> CreateProfileAsyncHelper(
const base::FilePath& path,
Delegate* delegate) override {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(base::IgnoreResult(&base::CreateDirectory), path));
return BuildTestingProfile(path, this, tokens_loaded_at_creation_);
}
bool tokens_loaded_at_creation_ = true;
};
} // namespace
class DiceSignedInProfileCreatorTest
: public testing::Test,
public ProfileManagerObserver,
public testing::WithParamInterface<bool> {
public:
DiceSignedInProfileCreatorTest()
: local_state_(TestingBrowserProcess::GetGlobal()),
use_guest_profile_(GetParam()) {
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
profile_manager_ = new UnittestProfileManager(temp_dir_.GetPath());
TestingBrowserProcess::GetGlobal()->SetProfileManager(profile_manager_);
profile_ = BuildTestingProfile(base::FilePath(), /*delegate=*/nullptr,
/*tokens_loaded=*/true);
identity_test_env_profile_adaptor_ =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile());
profile_manager()->AddObserver(this);
// Update |use_guest_profile_| if ephemeral Guest profiles is not supported.
use_guest_profile_ &=
TestingProfile::SetScopedFeatureListForEphemeralGuestProfiles(
scoped_feature_list_, use_guest_profile_);
}
~DiceSignedInProfileCreatorTest() override { DeleteProfiles(); }
UnittestProfileManager* profile_manager() { return profile_manager_; }
// Test environment attached to profile().
signin::IdentityTestEnvironment* identity_test_env() {
return identity_test_env_profile_adaptor_->identity_test_env();
}
// Source profile (the one which we are extracting credentials from).
Profile* profile() { return profile_.get(); }
// Profile created by the DiceSignedInProfileCreator.
Profile* signed_in_profile() { return signed_in_profile_; }
// Profile added to the ProfileManager. In general this should be the same as
// signed_in_profile() except in error cases.
Profile* added_profile() { return added_profile_; }
bool creator_callback_called() { return creator_callback_called_; }
void set_profile_added_closure(base::OnceClosure closure) {
profile_added_closure_ = std::move(closure);
}
bool use_guest_profile() const { return use_guest_profile_; }
void DeleteProfiles() {
identity_test_env_profile_adaptor_.reset();
if (profile_manager_) {
profile_manager()->RemoveObserver(this);
TestingBrowserProcess::GetGlobal()->SetProfileManager(nullptr);
profile_manager_ = nullptr;
}
}
// Callback for the DiceSignedInProfileCreator.
void OnProfileCreated(base::OnceClosure quit_closure, Profile* profile) {
creator_callback_called_ = true;
signed_in_profile_ = profile;
if (quit_closure)
std::move(quit_closure).Run();
}
// ProfileManagerObserver:
void OnProfileAdded(Profile* profile) override {
added_profile_ = profile;
if (profile_added_closure_)
std::move(profile_added_closure_).Run();
}
private:
content::BrowserTaskEnvironment task_environment_;
base::ScopedTempDir temp_dir_;
ScopedTestingLocalState local_state_;
UnittestProfileManager* profile_manager_ = nullptr;
std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
identity_test_env_profile_adaptor_;
std::unique_ptr<TestingProfile> profile_;
Profile* signed_in_profile_ = nullptr;
Profile* added_profile_ = nullptr;
base::OnceClosure profile_added_closure_;
bool creator_callback_called_ = false;
base::test::ScopedFeatureList scoped_feature_list_;
bool use_guest_profile_;
};
TEST_P(DiceSignedInProfileCreatorTest, CreateWithTokensLoaded) {
AccountInfo account_info =
identity_test_env()->MakeAccountAvailable("bob@example.com");
size_t kTestIcon = profiles::GetModernAvatarIconStartIndex();
base::string16 kProfileTestName16 = base::UTF8ToUTF16(kProfileTestName);
base::RunLoop loop;
std::unique_ptr<DiceSignedInProfileCreator> creator =
std::make_unique<DiceSignedInProfileCreator>(
profile(), account_info.account_id, kProfileTestName16, kTestIcon,
use_guest_profile(),
base::BindOnce(&DiceSignedInProfileCreatorTest::OnProfileCreated,
base::Unretained(this), loop.QuitClosure()));
loop.Run();
// Check that the account was moved.
EXPECT_TRUE(creator_callback_called());
EXPECT_TRUE(signed_in_profile());
EXPECT_NE(profile(), signed_in_profile());
EXPECT_EQ(signed_in_profile(), added_profile());
EXPECT_FALSE(IdentityManagerFactory::GetForProfile(profile())
->HasAccountWithRefreshToken(account_info.account_id));
EXPECT_EQ(1u, IdentityManagerFactory::GetForProfile(signed_in_profile())
->GetAccountsWithRefreshTokens()
.size());
EXPECT_TRUE(IdentityManagerFactory::GetForProfile(signed_in_profile())
->HasAccountWithRefreshToken(account_info.account_id));
// Check profile type
ASSERT_EQ(use_guest_profile(),
signed_in_profile()->IsEphemeralGuestProfile());
// Check the profile name and icon.
ProfileAttributesEntry* entry = nullptr;
ProfileAttributesStorage& storage =
profile_manager()->GetProfileAttributesStorage();
ASSERT_TRUE(storage.GetProfileAttributesWithPath(
signed_in_profile()->GetPath(), &entry));
ASSERT_TRUE(entry);
ASSERT_EQ(entry->IsGuest(), use_guest_profile());
if (!use_guest_profile()) {
EXPECT_EQ(kProfileTestName16, entry->GetLocalProfileName());
EXPECT_EQ(kTestIcon, entry->GetAvatarIconIndex());
}
}
TEST_P(DiceSignedInProfileCreatorTest, CreateWithTokensNotLoaded) {
AccountInfo account_info =
identity_test_env()->MakeAccountAvailable("bob@example.com");
profile_manager()->set_tokens_loaded_at_creation(false);
base::RunLoop creator_loop;
base::RunLoop profile_added_loop;
set_profile_added_closure(profile_added_loop.QuitClosure());
std::unique_ptr<DiceSignedInProfileCreator> creator =
std::make_unique<DiceSignedInProfileCreator>(
profile(), account_info.account_id, base::string16(), base::nullopt,
use_guest_profile(),
base::BindOnce(&DiceSignedInProfileCreatorTest::OnProfileCreated,
base::Unretained(this), creator_loop.QuitClosure()));
profile_added_loop.Run();
base::RunLoop().RunUntilIdle();
// The profile was created, but tokens not loaded. The callback has not been
// called yet.
EXPECT_FALSE(creator_callback_called());
EXPECT_TRUE(added_profile());
EXPECT_NE(profile(), added_profile());
// Load the tokens.
IdentityTestEnvironmentProfileAdaptor adaptor(added_profile());
adaptor.identity_test_env()->ReloadAccountsFromDisk();
creator_loop.Run();
// Check that the account was moved.
EXPECT_EQ(signed_in_profile(), added_profile());
EXPECT_TRUE(creator_callback_called());
EXPECT_FALSE(IdentityManagerFactory::GetForProfile(profile())
->HasAccountWithRefreshToken(account_info.account_id));
EXPECT_EQ(1u, IdentityManagerFactory::GetForProfile(signed_in_profile())
->GetAccountsWithRefreshTokens()
.size());
EXPECT_TRUE(IdentityManagerFactory::GetForProfile(signed_in_profile())
->HasAccountWithRefreshToken(account_info.account_id));
}
// Deleting the creator while it is running does not crash.
TEST_P(DiceSignedInProfileCreatorTest, DeleteWhileCreating) {
AccountInfo account_info =
identity_test_env()->MakeAccountAvailable("bob@example.com");
std::unique_ptr<DiceSignedInProfileCreator> creator =
std::make_unique<DiceSignedInProfileCreator>(
profile(), account_info.account_id, base::string16(), base::nullopt,
use_guest_profile(),
base::BindOnce(&DiceSignedInProfileCreatorTest::OnProfileCreated,
base::Unretained(this), base::OnceClosure()));
EXPECT_FALSE(creator_callback_called());
creator.reset();
base::RunLoop().RunUntilIdle();
}
// Deleting the profile while waiting for the tokens.
TEST_P(DiceSignedInProfileCreatorTest, DeleteProfile) {
AccountInfo account_info =
identity_test_env()->MakeAccountAvailable("bob@example.com");
profile_manager()->set_tokens_loaded_at_creation(false);
base::RunLoop creator_loop;
base::RunLoop profile_added_loop;
set_profile_added_closure(profile_added_loop.QuitClosure());
std::unique_ptr<DiceSignedInProfileCreator> creator =
std::make_unique<DiceSignedInProfileCreator>(
profile(), account_info.account_id, base::string16(), base::nullopt,
use_guest_profile(),
base::BindOnce(&DiceSignedInProfileCreatorTest::OnProfileCreated,
base::Unretained(this), creator_loop.QuitClosure()));
profile_added_loop.Run();
base::RunLoop().RunUntilIdle();
// The profile was created, but tokens not loaded. The callback has not been
// called yet.
EXPECT_FALSE(creator_callback_called());
EXPECT_TRUE(added_profile());
EXPECT_NE(profile(), added_profile());
DeleteProfiles();
creator_loop.Run();
// The callback is called with nullptr profile.
EXPECT_TRUE(creator_callback_called());
EXPECT_FALSE(signed_in_profile());
}
INSTANTIATE_TEST_SUITE_P(AllGuestProfileTypes,
DiceSignedInProfileCreatorTest,
/*use_guest_profile=*/testing::Bool());