blob: 0012e5f796feb9e3d9e30e0cbdd2f9da6a132864 [file] [log] [blame]
// Copyright 2020 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/signin/dice_web_signin_interceptor.h"
#include <map>
#include <string>
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/enterprise/util/managed_browser_utils.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_init_params.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_manager_observer.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/signin/chrome_signin_pref_names.h"
#include "chrome/browser/signin/dice_intercepted_session_startup_helper.h"
#include "chrome/browser/signin/dice_web_signin_interceptor_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/signin/signin_browser_test_base.h"
#include "chrome/browser/signin/web_signin_interceptor.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/profile_waiter.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/account_id/account_id.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/password_manager/core/browser/features/password_manager_features_util.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/search_engines_pref_names.h"
#include "components/search_engines/search_engines_switches.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_service.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/signin/public/identity_manager/account_capabilities_test_mutator.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h"
#include "components/sync/base/pref_names.h"
#include "components/version_info/version_info.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_launcher.h"
#include "google_apis/gaia/gaia_urls.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ui_base_features.h"
#include "url/gurl.h"
namespace {
const char kCustomSearchEngineDomain[] = "bar.com";
// Fake response for OAuth multilogin.
const char kMultiloginSuccessResponse[] =
R"()]}'
{
"status": "OK",
"cookies":[
{
"name":"SID",
"value":"SID_value",
"domain":".google.fr",
"path":"/",
"isSecure":true,
"isHttpOnly":false,
"priority":"HIGH",
"maxAge":63070000
}
]
}
)";
class FakeDiceWebSigninInterceptorDelegate;
class FakeBubbleHandle final : public ScopedWebSigninInterceptionBubbleHandle {
public:
~FakeBubbleHandle() override = default;
base::WeakPtr<FakeBubbleHandle> AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
base::WeakPtrFactory<FakeBubbleHandle> weak_ptr_factory_{this};
};
// Dummy interception delegate that automatically accepts multi user
// interception.
class FakeDiceWebSigninInterceptorDelegate
: public DiceWebSigninInterceptorDelegate {
public:
std::unique_ptr<ScopedWebSigninInterceptionBubbleHandle>
ShowSigninInterceptionBubble(
content::WebContents* web_contents,
const BubbleParameters& bubble_parameters,
base::OnceCallback<void(SigninInterceptionResult)> callback) override {
EXPECT_EQ(bubble_parameters.interception_type, expected_interception_type_);
auto bubble_handle = std::make_unique<FakeBubbleHandle>();
weak_bubble_handle_ = bubble_handle->AsWeakPtr();
// The callback must not be called synchronously (see the documentation for
// ShowSigninInterceptionBubble).
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), expected_interception_result_));
return bubble_handle;
}
void ShowFirstRunExperienceInNewProfile(
Browser* browser,
const CoreAccountId& account_id,
WebSigninInterceptor::SigninInterceptionType interception_type) override {
EXPECT_FALSE(fre_browser_)
<< "First run experience must be shown only once.";
EXPECT_EQ(interception_type, expected_interception_type_);
fre_browser_ = browser;
fre_account_id_ = account_id;
}
Browser* fre_browser() { return fre_browser_; }
const CoreAccountId& fre_account_id() { return fre_account_id_; }
void set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType type) {
expected_interception_type_ = type;
}
void set_expected_interception_result(SigninInterceptionResult result) {
expected_interception_result_ = result;
}
bool intercept_bubble_shown() const { return weak_bubble_handle_.get(); }
bool intercept_bubble_destroyed() const {
return weak_bubble_handle_.WasInvalidated();
}
private:
raw_ptr<Browser, AcrossTasksDanglingUntriaged> fre_browser_ = nullptr;
CoreAccountId fre_account_id_;
WebSigninInterceptor::SigninInterceptionType expected_interception_type_ =
WebSigninInterceptor::SigninInterceptionType::kMultiUser;
SigninInterceptionResult expected_interception_result_ =
SigninInterceptionResult::kAccepted;
base::WeakPtr<FakeBubbleHandle> weak_bubble_handle_;
};
// Runs the interception and returns the new profile that was created.
Profile* InterceptAndWaitProfileCreation(content::WebContents* contents,
const CoreAccountId& account_id) {
ProfileWaiter profile_waiter;
// Start the interception.
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(
Profile::FromBrowserContext(contents->GetBrowserContext()));
interceptor->MaybeInterceptWebSignin(
contents, account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/true,
/*is_sync_signin=*/false);
// Wait for the interception to be complete.
return profile_waiter.WaitForProfileAdded();
}
// Checks that the interception histograms were correctly recorded.
void CheckHistograms(const base::HistogramTester& histogram_tester,
SigninInterceptionHeuristicOutcome outcome) {
histogram_tester.ExpectUniqueSample("Signin.Intercept.HeuristicOutcome",
outcome, 1);
}
void SetUserSelectedDefaultSearchProvider(
TemplateURLService* template_url_service) {
TemplateURLData data;
data.SetShortName(base::UTF8ToUTF16(std::string(kCustomSearchEngineDomain)));
data.SetKeyword(base::UTF8ToUTF16(std::string(kCustomSearchEngineDomain)));
data.SetURL("https://" + std::string(kCustomSearchEngineDomain) +
"url?bar={searchTerms}");
data.new_tab_url =
"https://" + std::string(kCustomSearchEngineDomain) + "newtab";
data.alternate_urls.push_back("https://" +
std::string(kCustomSearchEngineDomain) +
"alt#quux={searchTerms}");
TemplateURL* template_url =
template_url_service->Add(std::make_unique<TemplateURL>(data));
template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
}
} // namespace
class DiceWebSigninInterceptorBrowserTest : public SigninBrowserTestBase {
public:
DiceWebSigninInterceptorBrowserTest()
: SigninBrowserTestBase(/*use_main_profile=*/true) {}
content::WebContents* AddTab(const GURL& url) {
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
return browser()->tab_strip_model()->GetActiveWebContents();
}
FakeDiceWebSigninInterceptorDelegate* GetInterceptorDelegate(
Profile* profile) {
// Make sure the interceptor has been created.
DiceWebSigninInterceptorFactory::GetForProfile(profile);
FakeDiceWebSigninInterceptorDelegate* interceptor_delegate =
interceptor_delegates_[profile];
return interceptor_delegate;
}
void SetupGaiaResponses() {
// Instantly return from Gaia calls, to avoid timing out when injecting the
// account in the new profile.
network::TestURLLoaderFactory* loader_factory = test_url_loader_factory();
loader_factory->SetInterceptor(base::BindLambdaForTesting(
[loader_factory](const network::ResourceRequest& request) {
std::string path = request.url.path();
if (path == "/ListAccounts" || path == "/GetCheckConnectionInfo") {
loader_factory->AddResponse(request.url.spec(), std::string());
return;
}
if (path == "/oauth/multilogin") {
loader_factory->AddResponse(request.url.spec(),
kMultiloginSuccessResponse);
return;
}
}));
}
AccountInfo MakeAccountInfoAvailableAndUpdate(
const std::string email,
const std::string& hosted_domain = "example.com") {
AccountInfo account_info = identity_test_env()->MakeAccountAvailable(email);
// Fill the account info, in particular for the hosted_domain field.
account_info.full_name = "fullname";
account_info.given_name = "givenname";
account_info.hosted_domain = hosted_domain;
account_info.locale = "en";
account_info.picture_url = "https://example.com";
// Fill in the required account capabilities for the sign in intercept.
AccountCapabilitiesTestMutator mutator(&account_info.capabilities);
mutator.set_is_subject_to_parental_controls(false);
DCHECK(account_info.IsValid());
identity_test_env()->UpdateAccountInfoForAccount(account_info);
return account_info;
}
private:
// InProcessBrowserTest:
void SetUpOnMainThread() override {
SigninBrowserTestBase::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
->SetInterceptedAccountProfileSeparationPoliciesForTesting(
policy::ProfileSeparationPolicies(""));
}
void OnWillCreateBrowserContextServices(
content::BrowserContext* context) override {
SigninBrowserTestBase::OnWillCreateBrowserContextServices(context);
DiceWebSigninInterceptorFactory::GetInstance()->SetTestingFactory(
context,
base::BindRepeating(&DiceWebSigninInterceptorBrowserTest::
BuildDiceWebSigninInterceptorWithFakeDelegate,
base::Unretained(this)));
}
// Builds a DiceWebSigninInterceptor with a fake delegate. To be used as a
// testing factory.
std::unique_ptr<KeyedService> BuildDiceWebSigninInterceptorWithFakeDelegate(
content::BrowserContext* context) {
std::unique_ptr<FakeDiceWebSigninInterceptorDelegate> fake_delegate =
std::make_unique<FakeDiceWebSigninInterceptorDelegate>();
interceptor_delegates_[context] = fake_delegate.get();
return std::make_unique<DiceWebSigninInterceptor>(
Profile::FromBrowserContext(context), std::move(fake_delegate));
}
web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
std::map<content::BrowserContext*, FakeDiceWebSigninInterceptorDelegate*>
interceptor_delegates_;
};
// Tests the complete profile switch flow when the profile is not loaded.
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest, SwitchAndLoad) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Add a profile in the cache (simulate the profile on disk).
ProfileManager* profile_manager = g_browser_process->profile_manager();
ProfileAttributesStorage* profile_storage =
&profile_manager->GetProfileAttributesStorage();
const base::FilePath profile_path =
profile_manager->GenerateNextProfileDirectoryPath();
ProfileAttributesInitParams params;
params.profile_path = profile_path;
params.profile_name = u"TestProfileName";
params.gaia_id = account_info.gaia;
params.user_name = base::UTF8ToUTF16(account_info.email);
profile_storage->AddProfile(std::move(params));
ProfileAttributesEntry* entry =
profile_storage->GetProfileAttributesWithPath(profile_path);
ASSERT_TRUE(entry);
ASSERT_EQ(entry->GetGAIAId(), account_info.gaia);
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
source_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kProfileSwitch);
Profile* new_profile =
InterceptAndWaitProfileCreation(web_contents, account_info.account_id);
ASSERT_TRUE(new_profile);
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_shown());
signin::IdentityManager* new_identity_manager =
IdentityManagerFactory::GetForProfile(new_profile);
EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
account_info.account_id));
// Check that the right profile was opened.
EXPECT_EQ(new_profile->GetPath(), profile_path);
// Add the account to the cookies (simulates the account reconcilor).
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
signin::SetCookieAccounts(new_identity_manager, test_url_loader_factory(),
{{account_info.email, account_info.gaia}});
// A browser has been created for the new profile and the tab was moved there.
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
Browser* added_browser = BrowserList::GetInstance()->get(1);
ASSERT_TRUE(added_browser);
EXPECT_EQ(added_browser->profile(), new_profile);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
EXPECT_EQ(
added_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptProfileSwitch);
// Interception bubble was closed.
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_destroyed());
// First run experience was not shown.
EXPECT_EQ(GetInterceptorDelegate(new_profile)->fre_browser(), nullptr);
EXPECT_EQ(source_interceptor_delegate->fre_browser(), nullptr);
}
// Tests the complete profile switch flow when the profile is already loaded.
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest, SwitchAlreadyOpen) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Create another profile with a browser window.
ProfileManager* profile_manager = g_browser_process->profile_manager();
const base::FilePath profile_path =
profile_manager->GenerateNextProfileDirectoryPath();
base::RunLoop loop;
Profile* other_profile = nullptr;
base::OnceCallback<void(Browser*)> callback =
base::BindLambdaForTesting([&other_profile, &loop](Browser* browser) {
other_profile = browser->profile();
loop.Quit();
});
profiles::SwitchToProfile(profile_path, /*always_create=*/true,
std::move(callback));
loop.Run();
ASSERT_TRUE(other_profile);
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
Browser* other_browser = BrowserList::GetInstance()->get(1);
ASSERT_TRUE(other_browser);
ASSERT_EQ(other_browser->profile(), other_profile);
// Add the account to the other profile.
signin::IdentityManager* other_identity_manager =
IdentityManagerFactory::GetForProfile(other_profile);
other_identity_manager->GetAccountsMutator()->AddOrUpdateAccount(
account_info.gaia, account_info.email, "dummy_refresh_token",
/*is_under_advanced_protection=*/false,
signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN,
signin_metrics::SourceForRefreshTokenOperation::kUnknown);
other_identity_manager->GetPrimaryAccountMutator()->SetPrimaryAccount(
account_info.account_id, signin::ConsentLevel::kSync);
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
int other_original_tab_count = other_browser->tab_strip_model()->count();
// Start the interception.
GetInterceptorDelegate(GetProfile())
->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kProfileSwitch);
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile());
interceptor->MaybeInterceptWebSignin(
web_contents, account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/true,
/*is_sync_signin=*/false);
// Add the account to the cookies (simulates the account reconcilor).
signin::SetCookieAccounts(other_identity_manager, test_url_loader_factory(),
{{account_info.email, account_info.gaia}});
// The tab was moved to the new browser.
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
EXPECT_EQ(other_browser->tab_strip_model()->count(),
other_original_tab_count + 1);
EXPECT_EQ(
other_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptProfileSwitch);
// First run experience was not shown.
EXPECT_EQ(GetInterceptorDelegate(other_profile)->fre_browser(), nullptr);
EXPECT_EQ(GetInterceptorDelegate(GetProfile())->fre_browser(), nullptr);
}
// Close the source tab during the interception and check that the NTP is opened
// in the new profile (regression test for https://crbug.com/1153321).
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest, CloseSourceTab) {
// Setup profile for interception.
AccountInfo primary_account_info =
identity_test_env()->MakePrimaryAccountAvailable(
"alice@gmail.com", signin::ConsentLevel::kSignin);
AccountInfo account_info = MakeAccountInfoAvailableAndUpdate(
"bob@example.com", kNoHostedDomainFound);
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
ProfileWaiter profile_waiter;
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(
Profile::FromBrowserContext(contents->GetBrowserContext()));
interceptor->MaybeInterceptWebSignin(
contents, account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/true,
/*is_sync_signin=*/false);
// Close the source tab during the profile creation.
contents->Close();
// Wait for the interception to be complete.
Profile* new_profile = profile_waiter.WaitForProfileAdded();
ASSERT_TRUE(new_profile);
signin::IdentityManager* new_identity_manager =
IdentityManagerFactory::GetForProfile(new_profile);
EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
account_info.account_id));
// Add the account to the cookies (simulates the account reconcilor).
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
signin::SetCookieAccounts(new_identity_manager, test_url_loader_factory(),
{{account_info.email, account_info.gaia}});
// A browser has been created for the new profile on the new tab page.
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
Browser* added_browser = BrowserList::GetInstance()->get(1);
ASSERT_TRUE(added_browser);
EXPECT_EQ(added_browser->profile(), new_profile);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
EXPECT_EQ(
added_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
GURL("chrome://newtab/"));
}
class DiceWebSigninInterceptorWithChromeSigninHelpersBrowserTest
: public DiceWebSigninInterceptorBrowserTest {
public:
ChromeSigninUserChoice GetChromeSigninUserChoicePref(
const AccountInfo& account_info) {
return SigninPrefs(*GetProfile()->GetPrefs())
.GetChromeSigninInterceptionUserChoice(account_info.gaia);
}
int GetChromeSigninInterceptDismissCountPref(
const AccountInfo& account_info) {
return SigninPrefs(*GetProfile()->GetPrefs())
.GetChromeSigninInterceptionDismissCount(account_info.gaia);
}
void Signout() { identity_test_env()->ClearPrimaryAccount(); }
FakeDiceWebSigninInterceptorDelegate* ShowSigninBubble(
const AccountInfo& account_info,
std::optional<SigninInterceptionResult> expected_result) {
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* contents = AddTab(intercepted_url);
// Set up the result expectations.
FakeDiceWebSigninInterceptorDelegate* interceptor_delegate =
GetInterceptorDelegate(GetProfile());
interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kChromeSignin);
if (expected_result.has_value()) {
interceptor_delegate->set_expected_interception_result(
expected_result.value());
}
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(
Profile::FromBrowserContext(contents->GetBrowserContext()));
interceptor->MaybeInterceptWebSignin(
contents, account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/true,
/*is_sync_signin=*/false);
return interceptor_delegate;
}
void ShowAndCompleteSigninBubbleWithResult(
const AccountInfo& account_info,
SigninInterceptionResult expected_result) {
FakeDiceWebSigninInterceptorDelegate* interceptor_delegate =
ShowSigninBubble(account_info, expected_result);
// Bubble should be shown following the intercept.
EXPECT_TRUE(interceptor_delegate->intercept_bubble_shown());
// The handling of the response to the bubble is done asynchronously in
// `FakeDiceWebSigninInterceptorDelegate::ShowSigninInterceptionBubble()`.
base::RunLoop().RunUntilIdle();
// Following the result the bubble should have been desrtoyed.
EXPECT_TRUE(interceptor_delegate->intercept_bubble_destroyed());
}
// Attempts to show the Chrome SigninBubble, checks that it doesn't.
void ExpectAttemptToShowChromeSigninBubbleNotToShow(const AccountInfo& info) {
FakeDiceWebSigninInterceptorDelegate* delegate =
ShowSigninBubble(info, /*expected_result=*/std::nullopt);
EXPECT_FALSE(delegate->intercept_bubble_shown());
}
bool IsChromeSignedIn() const {
return identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin);
}
void SetSignoutAllowed(bool allow) {
// Accepting management in order not to get signed out when restarting the
// browser. Since this test uses the fake IdentityManager cookies will not
// be saved on disc, therefore unable to find them back on startup which is
// causing a startup signout. Managed accounts cannot be signed out which is
// a workaround not to be signed out on Chrome restart.
chrome::enterprise_util::SetUserAcceptedAccountManagement(GetProfile(),
!allow);
}
};
// Test to sign in to Chrome from the Chrome Signin Bubble Intercept.
class DiceWebSigninInterceptorWithExplicitSigninEnabledBrowserTest
: public DiceWebSigninInterceptorWithChromeSigninHelpersBrowserTest {
private:
base::test::ScopedFeatureList feature_list_{
switches::kExplicitBrowserSigninUIOnDesktop};
};
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithExplicitSigninEnabledBrowserTest,
ChromeSigninInterceptAccepted) {
base::HistogramTester histogram_tester;
base::UserActionTester user_action_tester;
// Setup account for interception.
const std::string account_email("alice@example.com");
AccountInfo account_info = MakeAccountInfoAvailableAndUpdate(account_email);
// Makes sure Chrome is not signed in to trigger the Chrome Sigin intercept
// bubble.
ASSERT_FALSE(IsChromeSignedIn());
ShowAndCompleteSigninBubbleWithResult(account_info,
SigninInterceptionResult::kAccepted);
EXPECT_TRUE(IsChromeSignedIn());
// Check that the password account storage is enabled.
PrefService* pref_service = GetProfile()->GetPrefs();
syncer::SyncService* sync_service =
SyncServiceFactory::GetForProfile(GetProfile());
EXPECT_TRUE(password_manager::features_util::IsOptedInForAccountStorage(
pref_service, sync_service));
EXPECT_EQ(password_manager::features_util::GetDefaultPasswordStore(
pref_service, sync_service),
password_manager::PasswordForm::Store::kAccountStore);
CheckHistograms(histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptChromeSignin);
auto access_point =
signin_metrics::AccessPoint::ACCESS_POINT_CHROME_SIGNIN_INTERCEPT_BUBBLE;
histogram_tester.ExpectUniqueSample("Signin.SignIn.Started", access_point, 1);
histogram_tester.ExpectUniqueSample("Signin.SignIn.Completed", access_point,
1);
histogram_tester.ExpectUniqueSample(
"Signin.Intercept.ChromeSignin.DismissesBeforeAccept", 0, 1);
ChromeSigninUserChoice user_choice =
GetChromeSigninUserChoicePref(account_info);
// User choice is remembered.
EXPECT_EQ(user_choice, ChromeSigninUserChoice::kSignin);
// Attempting to show the bubble after an explicit choice.
// Signout to attempt signing in again and show the bubble.
Signout();
ASSERT_FALSE(IsChromeSignedIn());
// Make account available again.
account_info = MakeAccountInfoAvailableAndUpdate(account_email);
// Chrome Signin bubble should not show if the user already made a choice.
ExpectAttemptToShowChromeSigninBubbleNotToShow(account_info);
}
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithExplicitSigninEnabledBrowserTest,
ChromeSigninInterceptDeclined) {
base::HistogramTester histogram_tester;
base::UserActionTester user_action_tester;
// Setup account for interception.
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Makes sure Chrome is not signed in to trigger the Chrome Sigin intercept
// bubble.
ASSERT_FALSE(IsChromeSignedIn());
ShowAndCompleteSigninBubbleWithResult(account_info,
SigninInterceptionResult::kDeclined);
EXPECT_FALSE(IsChromeSignedIn());
EXPECT_FALSE(password_manager::features_util::IsOptedInForAccountStorage(
GetProfile()->GetPrefs(),
SyncServiceFactory::GetForProfile(GetProfile())));
CheckHistograms(histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptChromeSignin);
auto access_point =
signin_metrics::AccessPoint::ACCESS_POINT_CHROME_SIGNIN_INTERCEPT_BUBBLE;
histogram_tester.ExpectUniqueSample("Signin.SignIn.Started", access_point, 0);
histogram_tester.ExpectUniqueSample("Signin.SignIn.Completed", access_point,
0);
ChromeSigninUserChoice user_choice =
GetChromeSigninUserChoicePref(account_info);
// User choice is remembered.
EXPECT_EQ(user_choice, ChromeSigninUserChoice::kDoNotSignin);
histogram_tester.ExpectUniqueSample(
"Signin.Intercept.ChromeSignin.DismissesBeforeDecline", 0, 1);
// Attempting to show the bubble after an explicit choice.
ASSERT_FALSE(IsChromeSignedIn());
// Chrome Signin bubble should not show if the user already made a choice.
ExpectAttemptToShowChromeSigninBubbleNotToShow(account_info);
}
// Test the memory of the user's account storage preference.
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithExplicitSigninEnabledBrowserTest,
OptOutOfAccountStorage) {
// Setup account and accept intersection.
const std::string email("alice@example.com");
AccountInfo account_info = MakeAccountInfoAvailableAndUpdate(email);
ShowAndCompleteSigninBubbleWithResult(account_info,
SigninInterceptionResult::kAccepted);
// Check that the password account storage is enabled.
PrefService* pref_service = GetProfile()->GetPrefs();
syncer::SyncService* sync_service =
SyncServiceFactory::GetForProfile(GetProfile());
EXPECT_TRUE(password_manager::features_util::IsOptedInForAccountStorage(
pref_service, sync_service));
// Opt out of account storage.
password_manager::features_util::OptOutOfAccountStorageAndClearSettings(
pref_service, sync_service);
// Check that the password account storage is disabled.
EXPECT_FALSE(password_manager::features_util::IsOptedInForAccountStorage(
pref_service, sync_service));
Signout();
// Check that the password account storage is false if there is no account.
EXPECT_FALSE(password_manager::features_util::IsOptedInForAccountStorage(
pref_service, sync_service));
// Log in again.
// Force a Chrome Signin. The bubble will not be shown again.
identity_test_env()->MakePrimaryAccountAvailable(
email, signin::ConsentLevel::kSignin);
// Check that the password account storage is still disabled.
EXPECT_FALSE(password_manager::features_util::IsOptedInForAccountStorage(
pref_service, sync_service));
}
// Test to sign in to Chrome from the Chrome Signin Bubble Intercept with
// the full `switches::kExplicitBrowserSigninUIOnDesktop` enabled.
class DiceWebSigninInterceptorWithBrowserSigninFullPhaseBrowserTest
: public DiceWebSigninInterceptorWithChromeSigninHelpersBrowserTest {
private:
base::test::ScopedFeatureList feature_list_{
switches::kExplicitBrowserSigninUIOnDesktop};
};
// This test mainly checks the combination of dismissal and the effect it has on
// the user choice. Simulating multiple accounts and checks that they do not
// affect each other:
// - Account1 dismisses the bubble twice.
// - Account2 dismisses the bubble once.
// - Account1 dismisses the bubble three more times, and make sure it is a
// decline.
// - Account2 accept the bubble.
// - Account1 changes it's pref to always ask and should show the bubble even
// after 5 dismisses.
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithBrowserSigninFullPhaseBrowserTest,
ChromeSigninInerceptDismissBehavior) {
base::HistogramTester histogram_tester;
// Setup a first account for interception.
const std::string email1("alice1@example.com");
AccountInfo info1 = MakeAccountInfoAvailableAndUpdate(email1);
ASSERT_EQ(GetChromeSigninInterceptDismissCountPref(info1), 0);
ASSERT_EQ(GetChromeSigninUserChoicePref(info1),
ChromeSigninUserChoice::kNoChoice);
// Makes sure Chrome is not signed in to trigger the Chrome Sigin intercept
// bubble.
ASSERT_FALSE(IsChromeSignedIn());
// Intercept declined on account1 twice.
ShowAndCompleteSigninBubbleWithResult(info1,
SigninInterceptionResult::kDismissed);
int expected_dismiss_count = 1;
EXPECT_EQ(GetChromeSigninInterceptDismissCountPref(info1),
expected_dismiss_count);
EXPECT_EQ(GetChromeSigninUserChoicePref(info1),
ChromeSigninUserChoice::kNoChoice);
ShowAndCompleteSigninBubbleWithResult(info1,
SigninInterceptionResult::kDismissed);
++expected_dismiss_count;
EXPECT_EQ(GetChromeSigninInterceptDismissCountPref(info1),
expected_dismiss_count);
// Setup the second account for interception.
AccountInfo info2 = MakeAccountInfoAvailableAndUpdate("alice2@example.com");
ASSERT_FALSE(info2.IsEmpty());
ASSERT_EQ(GetChromeSigninInterceptDismissCountPref(info2), 0);
// Intercept dismissed on account2.
ShowAndCompleteSigninBubbleWithResult(info2,
SigninInterceptionResult::kDismissed);
// Account2 pref should be affected and account1 should not.
EXPECT_EQ(GetChromeSigninInterceptDismissCountPref(info1),
expected_dismiss_count);
EXPECT_EQ(GetChromeSigninInterceptDismissCountPref(info2), 1);
// 3 more dismisses on account1:
for (int i = 0; i < 3; ++i) {
ShowAndCompleteSigninBubbleWithResult(info1,
SigninInterceptionResult::kDismissed);
++expected_dismiss_count;
EXPECT_EQ(GetChromeSigninInterceptDismissCountPref(info1),
expected_dismiss_count);
}
// 5 dismiss treated as a do not sign in.
EXPECT_EQ(GetChromeSigninUserChoicePref(info1),
ChromeSigninUserChoice::kDoNotSignin);
// A decline should have been recorded.
size_t expected_decline_count = 1;
histogram_tester.ExpectBucketCount(
"Signin.Intercept.ChromeSignin.DismissesBeforeDecline",
/*sample=*/5, /*expected_count=*/expected_decline_count);
// A 6h attempt to show should fail.
ExpectAttemptToShowChromeSigninBubbleNotToShow(info1);
// Accepting the intercept on account2 should reset the pref and log in the
// histogram.
ShowAndCompleteSigninBubbleWithResult(info2,
SigninInterceptionResult::kAccepted);
// Dismiss count remains.
EXPECT_EQ(GetChromeSigninInterceptDismissCountPref(info2), 1);
// Record the 1 dismiss that happened before accepting the intercept.
histogram_tester.ExpectUniqueSample(
"Signin.Intercept.ChromeSignin.DismissesBeforeAccept",
/*sample=*/1, /*expected_bucket_count=*/1);
// Make sure to signout account2.
Signout();
// Make account1 available again.
info1 = MakeAccountInfoAvailableAndUpdate(email1);
// Override account1 pref to always ask.
SigninPrefs(*GetProfile()->GetPrefs())
.SetChromeSigninInterceptionUserChoice(
info1.gaia, ChromeSigninUserChoice::kAlwaysAsk);
// Showing the bubble should succeed -- result is not important, only affect
// histogram recorded.
ShowAndCompleteSigninBubbleWithResult(info1,
SigninInterceptionResult::kDeclined);
++expected_decline_count;
histogram_tester.ExpectBucketCount(
"Signin.Intercept.ChromeSignin.DismissesBeforeDecline",
/*sample=*/5, /*expected_count=*/expected_decline_count);
}
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithBrowserSigninFullPhaseBrowserTest,
OverrideUserChoicePrefAfterAccept) {
// Setup an account for interception.
const std::string email("alice1@example.com");
AccountInfo info = MakeAccountInfoAvailableAndUpdate(email);
// Makes sure Chrome is not signed in to trigger the Chrome Sigin intercept
// bubble.
ASSERT_FALSE(IsChromeSignedIn());
ShowAndCompleteSigninBubbleWithResult(info,
SigninInterceptionResult::kAccepted);
// Choice is remembered.
EXPECT_EQ(GetChromeSigninUserChoicePref(info),
ChromeSigninUserChoice::kSignin);
// Signout to attempt signing in again.
Signout();
// Make account available again.
info = MakeAccountInfoAvailableAndUpdate(email);
// Attempting to show the bubble again should fail since we already have a
// user choice.
ExpectAttemptToShowChromeSigninBubbleNotToShow(info);
// Override account1 pref to always ask -- simulating changing it through the
// settings.
SigninPrefs(*GetProfile()->GetPrefs())
.SetChromeSigninInterceptionUserChoice(
info.gaia, ChromeSigninUserChoice::kAlwaysAsk);
// Showing the bubble should succeed -- result is not important.
ShowAndCompleteSigninBubbleWithResult(info,
SigninInterceptionResult::kDismissed);
}
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithBrowserSigninFullPhaseBrowserTest,
OverrideUserChoicePrefAfterDecline) {
// Setup an account for interception.
const std::string email("alice1@example.com");
AccountInfo info = MakeAccountInfoAvailableAndUpdate(email);
// Makes sure Chrome is not signed in to trigger the Chrome Sigin intercept
// bubble.
ASSERT_FALSE(IsChromeSignedIn());
ShowAndCompleteSigninBubbleWithResult(info,
SigninInterceptionResult::kDeclined);
// Choice is remembered.
EXPECT_EQ(GetChromeSigninUserChoicePref(info),
ChromeSigninUserChoice::kDoNotSignin);
// Attempting to show the bubble again should fail since we already have a
// user choice.
ExpectAttemptToShowChromeSigninBubbleNotToShow(info);
// Override account1 pref to always ask -- simulating changing it through the
// settings.
SigninPrefs(*GetProfile()->GetPrefs())
.SetChromeSigninInterceptionUserChoice(
info.gaia, ChromeSigninUserChoice::kAlwaysAsk);
// Showing the bubble should succeed -- result is not important.
ShowAndCompleteSigninBubbleWithResult(info,
SigninInterceptionResult::kDismissed);
}
// Test Suite where PRE_* tests are with
// `switches::kExplicitBrowserSigninUIOnDesktop` disabled, and regular test with
// `switches::kExplicitBrowserSigninUIOnDesktop` enabled, simulating users
// transitioning in to `switches::kExplicitBrowserSigninUIOnDesktop` active.
class DiceWebSigninInterceptorWithUnoEnabledAndPREDisabledBrowserTest
: public DiceWebSigninInterceptorWithChromeSigninHelpersBrowserTest {
public:
DiceWebSigninInterceptorWithUnoEnabledAndPREDisabledBrowserTest() {
feature_list_.InitWithFeatureState(
switches::kExplicitBrowserSigninUIOnDesktop, !content::IsPreTest());
}
protected:
const std::string email_ = "alice@example.com";
private:
base::test::ScopedFeatureList feature_list_;
};
// Signing in to Chrome while `switches::kExplicitBrowserSigninUIOnDesktop` is
// disabled, to simulate a signed in user prior to
// `switches::kExplicitBrowserSigninUIOnDesktop` activation, then enabling the
// feature for them.
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithUnoEnabledAndPREDisabledBrowserTest,
PRE_ChromeSignedInTransitionToUnoEnabled) {
ASSERT_FALSE(switches::IsExplicitBrowserSigninUIOnDesktopEnabled());
signin::AccountAvailabilityOptionsBuilder builder;
AccountInfo account_info = signin::MakeAccountAvailable(
identity_manager(),
builder
.AsPrimary(signin::ConsentLevel::kSignin)
// `ACCESS_POINT_UNKNOWN` is not explicit signin.
.WithAccessPoint(signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN)
.Build(email_));
EXPECT_TRUE(IsChromeSignedIn());
EXPECT_FALSE(browser()->profile()->GetPrefs()->GetBoolean(
prefs::kExplicitBrowserSignin));
// Passwords are defaulted to disabled without an explicit signin.
EXPECT_FALSE(password_manager::features_util::IsOptedInForAccountStorage(
GetProfile()->GetPrefs(),
SyncServiceFactory::GetForProfile(GetProfile())));
SetSignoutAllowed(false);
}
// Enabling `switches::kExplicitBrowserSigninUIOnDesktop`, after being signed in
// already.
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithUnoEnabledAndPREDisabledBrowserTest,
ChromeSignedInTransitionToUnoEnabled) {
ASSERT_TRUE(switches::IsExplicitBrowserSigninUIOnDesktopEnabled());
// We are still signed in from the PRE_ test.
ASSERT_TRUE(IsChromeSignedIn());
// Starting Chrome with a Signed in account prior to
// `switches::kExplicitBrowserSigninUIOnDesktop` activation should not turn
// this pref on.
EXPECT_FALSE(browser()->profile()->GetPrefs()->GetBoolean(
prefs::kExplicitBrowserSignin));
// Since we did not interact with passwords before, passwords should remain
// disabled as long as we did not explicitly sign in.
PrefService* pref_service = GetProfile()->GetPrefs();
syncer::SyncService* sync_service =
SyncServiceFactory::GetForProfile(GetProfile());
EXPECT_FALSE(password_manager::features_util::IsOptedInForAccountStorage(
pref_service, sync_service));
// Sign out, and sign back in.
SetSignoutAllowed(true);
identity_test_env()->ClearPrimaryAccount();
ASSERT_FALSE(IsChromeSignedIn());
signin::MakeAccountAvailable(
identity_manager(),
signin::AccountAvailabilityOptionsBuilder()
.AsPrimary(signin::ConsentLevel::kSignin)
.WithAccessPoint(signin_metrics::AccessPoint::
ACCESS_POINT_CHROME_SIGNIN_INTERCEPT_BUBBLE)
.Build(email_));
// Explicit Signing in while `switches::kExplicitBrowserSigninUIOnDesktop` is
// active should be stored.
EXPECT_TRUE(browser()->profile()->GetPrefs()->GetBoolean(
prefs::kExplicitBrowserSignin));
// Signing in with `switches::kExplicitBrowserSigninUIOnDesktop` enabled,
// should affect the passwords default.
EXPECT_TRUE(password_manager::features_util::IsOptedInForAccountStorage(
pref_service, sync_service));
// Sign out should clear the explicit signin pref.
identity_test_env()->ClearPrimaryAccount();
EXPECT_FALSE(browser()->profile()->GetPrefs()->GetBoolean(
prefs::kExplicitBrowserSignin));
}
// Test Suite where PRE_* tests are with
// `switches::kExplicitBrowserSigninUIOnDesktop` enabled, and regular test with
// `switches::kExplicitBrowserSigninUIOnDesktop` disabled. Simulating a
// rollback.
class DiceWebSigninInterceptorWithUnoDisabledAndPREEnabledBrowserTest
: public DiceWebSigninInterceptorWithChromeSigninHelpersBrowserTest {
public:
DiceWebSigninInterceptorWithUnoDisabledAndPREEnabledBrowserTest() {
feature_list_.InitWithFeatureState(
switches::kExplicitBrowserSigninUIOnDesktop, content::IsPreTest());
}
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithUnoDisabledAndPREEnabledBrowserTest,
PRE_ChromeSignedinWithUnoShouldRevertBackToDefaultWithUnoDisabled) {
ASSERT_TRUE(switches::IsExplicitBrowserSigninUIOnDesktopEnabled());
signin::MakeAccountAvailable(
identity_manager(),
signin::AccountAvailabilityOptionsBuilder()
.AsPrimary(signin::ConsentLevel::kSignin)
.WithAccessPoint(signin_metrics::AccessPoint::
ACCESS_POINT_CHROME_SIGNIN_INTERCEPT_BUBBLE)
.Build("alice@example.com"));
EXPECT_TRUE(IsChromeSignedIn());
EXPECT_TRUE(browser()->profile()->GetPrefs()->GetBoolean(
prefs::kExplicitBrowserSignin));
// Passwords are defaulted to enabled with an explicit sign in and
// `switches::kExplicitBrowserSigninUIOnDesktop` active.
EXPECT_TRUE(password_manager::features_util::IsOptedInForAccountStorage(
GetProfile()->GetPrefs(),
SyncServiceFactory::GetForProfile(GetProfile())));
SetSignoutAllowed(false);
}
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorWithUnoDisabledAndPREEnabledBrowserTest,
ChromeSignedinWithUnoShouldRevertBackToDefaultWithUnoDisabled) {
ASSERT_FALSE(switches::IsExplicitBrowserSigninUIOnDesktopEnabled());
// Disabling `switches::kExplicitBrowserSigninUIOnDesktop` should reset the
// pref.
EXPECT_FALSE(browser()->profile()->GetPrefs()->GetBoolean(
prefs::kExplicitBrowserSignin));
// Disabling `switches::kExplicitBrowserSigninUIOnDesktop` feature should
// revert back to the previous default state, since there were no
// interactions, defaults to disabled.
EXPECT_FALSE(password_manager::features_util::IsOptedInForAccountStorage(
GetProfile()->GetPrefs(),
SyncServiceFactory::GetForProfile(GetProfile())));
}
// WebApps do not trigger interception. Regression test for
// https://crbug.com/1414988
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
WebAppNoInterception) {
base::HistogramTester histogram_tester;
// Setup profile for interception.
identity_test_env()->MakeAccountAvailable("alice@example.com");
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("bob@example.com");
SetupGaiaResponses();
// Install web app
Profile* profile = browser()->profile();
const GURL kWebAppURL("http://www.webapp.com");
auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
web_app_info->start_url = kWebAppURL;
web_app_info->scope = kWebAppURL.GetWithoutFilename();
web_app_info->user_display_mode =
web_app::mojom::UserDisplayMode::kStandalone;
web_app_info->title = u"A Web App";
webapps::AppId app_id =
web_app::test::InstallWebApp(profile, std::move(web_app_info));
Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id);
ASSERT_NE(app_browser, nullptr);
ASSERT_EQ(app_browser->type(), Browser::Type::TYPE_APP);
// Trigger signin interception in the web app.
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(profile);
interceptor->MaybeInterceptWebSignin(
app_browser->tab_strip_model()->GetActiveWebContents(),
account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/true,
/*is_sync_signin=*/false);
// Check that the interception was aborted.
histogram_tester.ExpectUniqueSample(
"Signin.Intercept.HeuristicOutcome",
SigninInterceptionHeuristicOutcome::kAbortNoSupportedBrowser, 1);
}
// Tests the complete interception flow including profile and browser creation.
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTestNoForcedInterception) {
base::HistogramTester histogram_tester;
AccountInfo primary_account_info =
MakeAccountInfoAvailableAndUpdate("bob@example.com");
IdentityManagerFactory::GetForProfile(GetProfile())
->GetPrimaryAccountMutator()
->SetPrimaryAccount(primary_account_info.account_id,
signin::ConsentLevel::kSync);
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Enforce enterprise profile sepatation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"none");
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
->SetInterceptedAccountProfileSeparationPoliciesForTesting(
policy::ProfileSeparationPolicies(""));
SetupGaiaResponses();
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
source_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterprise);
Profile* new_profile =
InterceptAndWaitProfileCreation(web_contents, account_info.account_id);
EXPECT_FALSE(
chrome::enterprise_util::UserAcceptedAccountManagement(new_profile));
ASSERT_TRUE(new_profile);
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_shown());
signin::IdentityManager* new_identity_manager =
IdentityManagerFactory::GetForProfile(new_profile);
EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
account_info.account_id));
FakeDiceWebSigninInterceptorDelegate* new_interceptor_delegate =
GetInterceptorDelegate(new_profile);
new_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterprise);
IdentityTestEnvironmentProfileAdaptor adaptor(new_profile);
adaptor.identity_test_env()->SetAutomaticIssueOfAccessTokens(true);
// Check the profile name.
ProfileAttributesStorage& storage =
g_browser_process->profile_manager()->GetProfileAttributesStorage();
ProfileAttributesEntry* entry =
storage.GetProfileAttributesWithPath(new_profile->GetPath());
ASSERT_TRUE(entry);
EXPECT_EQ("example.com", base::UTF16ToUTF8(entry->GetLocalProfileName()));
// Check the profile color.
if (features::IsChromeWebuiRefresh2023()) {
EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
->GetUserColor()
.has_value());
} else {
EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
->UsingAutogeneratedTheme());
}
// A browser has been created for the new profile and the tab was moved there.
Browser* added_browser = ui_test_utils::WaitForBrowserToOpen();
ASSERT_TRUE(added_browser);
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
EXPECT_EQ(added_browser->profile(), new_profile);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
EXPECT_EQ(
added_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptEnterprise);
// First run experience UI was shown exactly once in the new profile.
EXPECT_EQ(new_interceptor_delegate->fre_browser(), added_browser);
EXPECT_EQ(new_interceptor_delegate->fre_account_id(),
account_info.account_id);
EXPECT_EQ(source_interceptor_delegate->fre_browser(), nullptr);
}
// Tests the complete interception flow including profile and browser creation.
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
EnterpriseInterceptionDeclined) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
AccountInfo primary_account_info =
MakeAccountInfoAvailableAndUpdate("bob@example.com");
IdentityManagerFactory::GetForProfile(GetProfile())
->GetPrimaryAccountMutator()
->SetPrimaryAccount(primary_account_info.account_id,
signin::ConsentLevel::kSignin);
// Enforce enterprise profile sepatation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"none");
SetupGaiaResponses();
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
source_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterprise);
source_interceptor_delegate->set_expected_interception_result(
SigninInterceptionResult::kDeclined);
// Start the interception.
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile());
interceptor->MaybeInterceptWebSignin(
web_contents, account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/true,
/*is_sync_signin=*/false);
base::RunLoop run_loop;
run_loop.RunUntilIdle();
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(GetProfile());
EXPECT_FALSE(
chrome::enterprise_util::UserAcceptedAccountManagement(GetProfile()));
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_destroyed());
EXPECT_TRUE(
identity_manager->HasAccountWithRefreshToken(account_info.account_id));
ASSERT_EQ(BrowserList::GetInstance()->size(), 1u);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count);
EXPECT_EQ(
browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptEnterprise);
}
// Tests the complete interception flow including profile and browser creation.
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTestAccountLevelPolicy) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Enforce enterprise profile sepatation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"none");
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
->SetInterceptedAccountProfileSeparationPoliciesForTesting(
policy::ProfileSeparationPolicies("primary_account"));
SetupGaiaResponses();
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
source_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced);
Profile* new_profile =
InterceptAndWaitProfileCreation(web_contents, account_info.account_id);
EXPECT_TRUE(
chrome::enterprise_util::UserAcceptedAccountManagement(new_profile));
ASSERT_TRUE(new_profile);
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_shown());
signin::IdentityManager* new_identity_manager =
IdentityManagerFactory::GetForProfile(new_profile);
EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
account_info.account_id));
FakeDiceWebSigninInterceptorDelegate* new_interceptor_delegate =
GetInterceptorDelegate(new_profile);
new_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced);
IdentityTestEnvironmentProfileAdaptor adaptor(new_profile);
adaptor.identity_test_env()->SetAutomaticIssueOfAccessTokens(true);
// Check the profile name.
ProfileAttributesStorage& storage =
g_browser_process->profile_manager()->GetProfileAttributesStorage();
ProfileAttributesEntry* entry =
storage.GetProfileAttributesWithPath(new_profile->GetPath());
ASSERT_TRUE(entry);
EXPECT_EQ("example.com", base::UTF16ToUTF8(entry->GetLocalProfileName()));
// Check the profile color.
if (features::IsChromeWebuiRefresh2023()) {
EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
->GetUserColor()
.has_value());
} else {
EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
->UsingAutogeneratedTheme());
}
// A browser has been created for the new profile and the tab was moved there.
Browser* added_browser = ui_test_utils::WaitForBrowserToOpen();
ASSERT_TRUE(added_browser);
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
EXPECT_EQ(added_browser->profile(), new_profile);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
EXPECT_EQ(
added_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(
histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptEnterpriseForced);
// First run experience UI was shown exactly once in the new profile.
EXPECT_EQ(new_interceptor_delegate->fre_browser(), added_browser);
EXPECT_EQ(new_interceptor_delegate->fre_account_id(),
account_info.account_id);
EXPECT_EQ(source_interceptor_delegate->fre_browser(), nullptr);
}
// Tests the complete interception flow including profile and browser creation.
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTestAccountLevelPolicyDeclined) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Enforce enterprise profile sepatation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"none");
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
->SetInterceptedAccountProfileSeparationPoliciesForTesting(
policy::ProfileSeparationPolicies("primary_account"));
SetupGaiaResponses();
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
source_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced);
source_interceptor_delegate->set_expected_interception_result(
SigninInterceptionResult::kDeclined);
// Start the interception.
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile());
interceptor->MaybeInterceptWebSignin(
web_contents, account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/true,
/*is_sync_signin=*/false);
base::RunLoop run_loop;
run_loop.RunUntilIdle();
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(GetProfile());
EXPECT_FALSE(
chrome::enterprise_util::UserAcceptedAccountManagement(GetProfile()));
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_destroyed());
EXPECT_FALSE(
identity_manager->HasAccountWithRefreshToken(account_info.account_id));
ASSERT_EQ(BrowserList::GetInstance()->size(), 1u);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count);
EXPECT_EQ(
browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(
histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptEnterpriseForced);
}
// Tests the complete interception flow including profile and browser creation.
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTestAccountLevelPolicyStrictDeclined) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Enforce enterprise profile sepatation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"none");
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile())
->SetInterceptedAccountProfileSeparationPoliciesForTesting(
policy::ProfileSeparationPolicies("primary_account_strict"));
SetupGaiaResponses();
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
source_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced);
source_interceptor_delegate->set_expected_interception_result(
SigninInterceptionResult::kDeclined);
// Start the interception.
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile());
interceptor->MaybeInterceptWebSignin(
web_contents, account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/true,
/*is_sync_signin=*/false);
base::RunLoop run_loop;
run_loop.RunUntilIdle();
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(GetProfile());
EXPECT_FALSE(
chrome::enterprise_util::UserAcceptedAccountManagement(GetProfile()));
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_destroyed());
EXPECT_FALSE(
identity_manager->HasAccountWithRefreshToken(account_info.account_id));
ASSERT_EQ(BrowserList::GetInstance()->size(), 1u);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count);
EXPECT_EQ(
browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(
histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptEnterpriseForced);
}
// Tests the complete interception flow including profile and browser creation.
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionTest) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Enforce enterprise profile separation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"primary_account_strict");
SetupGaiaResponses();
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
source_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced);
Profile* new_profile =
InterceptAndWaitProfileCreation(web_contents, account_info.account_id);
EXPECT_TRUE(
chrome::enterprise_util::UserAcceptedAccountManagement(new_profile));
ASSERT_TRUE(new_profile);
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_shown());
signin::IdentityManager* new_identity_manager =
IdentityManagerFactory::GetForProfile(new_profile);
EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
account_info.account_id));
FakeDiceWebSigninInterceptorDelegate* new_interceptor_delegate =
GetInterceptorDelegate(new_profile);
new_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced);
IdentityTestEnvironmentProfileAdaptor adaptor(new_profile);
adaptor.identity_test_env()->SetAutomaticIssueOfAccessTokens(true);
// Check the profile name.
ProfileAttributesStorage& storage =
g_browser_process->profile_manager()->GetProfileAttributesStorage();
ProfileAttributesEntry* entry =
storage.GetProfileAttributesWithPath(new_profile->GetPath());
ASSERT_TRUE(entry);
EXPECT_EQ("example.com", base::UTF16ToUTF8(entry->GetLocalProfileName()));
// Check the profile color.
if (features::IsChromeWebuiRefresh2023()) {
EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
->GetUserColor()
.has_value());
} else {
EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
->UsingAutogeneratedTheme());
}
// A browser has been created for the new profile and the tab was moved there.
Browser* added_browser = ui_test_utils::WaitForBrowserToOpen();
ASSERT_TRUE(added_browser);
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
EXPECT_EQ(added_browser->profile(), new_profile);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
EXPECT_EQ(
added_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(
histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptEnterpriseForced);
// First run experience UI was shown exactly once in the new profile.
EXPECT_EQ(new_interceptor_delegate->fre_browser(), added_browser);
EXPECT_EQ(new_interceptor_delegate->fre_account_id(),
account_info.account_id);
EXPECT_EQ(source_interceptor_delegate->fre_browser(), nullptr);
}
// Tests the complete interception flow for a reauth of the primary account of a
// non-syncing profile.
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionPrimaryACcountReauthSyncDisabledTest) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
IdentityManagerFactory::GetForProfile(GetProfile())
->GetPrimaryAccountMutator()
->SetPrimaryAccount(account_info.account_id,
signin::ConsentLevel::kSignin);
// Enforce enterprise profile separation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"primary_account_strict");
SetupGaiaResponses();
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
source_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kEnterpriseForced);
EXPECT_FALSE(
chrome::enterprise_util::UserAcceptedAccountManagement(GetProfile()));
// Start the interception.
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile());
interceptor->MaybeInterceptWebSignin(
web_contents, account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/false,
/*is_sync_signin=*/false);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
chrome::enterprise_util::UserAcceptedAccountManagement(GetProfile()));
// Interception bubble was closed.
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_destroyed());
EXPECT_TRUE(IdentityManagerFactory::GetForProfile(GetProfile())
->HasAccountWithRefreshToken(account_info.account_id));
ASSERT_EQ(BrowserList::GetInstance()->size(), 1u);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count);
EXPECT_EQ(
browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(
histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptEnterpriseForced);
}
// Tests the complete interception flow for a reauth of the primary account of a
// syncing profile.
IN_PROC_BROWSER_TEST_F(
DiceWebSigninInterceptorBrowserTest,
ForcedEnterpriseInterceptionPrimaryAccountReauthSyncEnabledTest) {
base::HistogramTester histogram_tester;
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
IdentityManagerFactory::GetForProfile(GetProfile())
->GetPrimaryAccountMutator()
->SetPrimaryAccount(account_info.account_id, signin::ConsentLevel::kSync);
// Enforce enterprise profile separation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"primary_account_strict");
SetupGaiaResponses();
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
EXPECT_FALSE(
chrome::enterprise_util::UserAcceptedAccountManagement(GetProfile()));
// Start the interception.
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile());
interceptor->MaybeInterceptWebSignin(
web_contents, account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/false,
/*is_sync_signin=*/false);
base::RunLoop().RunUntilIdle();
// Interception bubble was closed.
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
EXPECT_FALSE(source_interceptor_delegate->intercept_bubble_shown());
EXPECT_FALSE(source_interceptor_delegate->intercept_bubble_destroyed());
EXPECT_TRUE(IdentityManagerFactory::GetForProfile(GetProfile())
->HasAccountWithRefreshToken(account_info.account_id));
ASSERT_EQ(BrowserList::GetInstance()->size(), 1u);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count);
EXPECT_EQ(
browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(
histogram_tester,
SigninInterceptionHeuristicOutcome::kAbortAccountNotNew);
}
// Tests the complete profile switch flow when the profile is not loaded.
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
EnterpriseSwitchAndLoad) {
base::HistogramTester histogram_tester;
// Enforce enterprise profile separation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"primary_account_strict");
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Add a profile in the cache (simulate the profile on disk).
ProfileManager* profile_manager = g_browser_process->profile_manager();
ProfileAttributesStorage* profile_storage =
&profile_manager->GetProfileAttributesStorage();
const base::FilePath profile_path =
profile_manager->GenerateNextProfileDirectoryPath();
ProfileAttributesInitParams params;
params.profile_path = profile_path;
params.profile_name = u"TestProfileName";
params.gaia_id = account_info.gaia;
params.user_name = base::UTF8ToUTF16(account_info.email);
profile_storage->AddProfile(std::move(params));
ProfileAttributesEntry* entry =
profile_storage->GetProfileAttributesWithPath(profile_path);
ASSERT_TRUE(entry);
ASSERT_EQ(entry->GetGAIAId(), account_info.gaia);
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
source_interceptor_delegate->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kProfileSwitchForced);
Profile* new_profile =
InterceptAndWaitProfileCreation(web_contents, account_info.account_id);
ASSERT_TRUE(new_profile);
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_shown());
signin::IdentityManager* new_identity_manager =
IdentityManagerFactory::GetForProfile(new_profile);
EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
account_info.account_id));
// Check that the right profile was opened.
EXPECT_EQ(new_profile->GetPath(), profile_path);
// Add the account to the cookies (simulates the account reconcilor).
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
signin::SetCookieAccounts(new_identity_manager, test_url_loader_factory(),
{{account_info.email, account_info.gaia}});
// A browser has been created for the new profile and the tab was moved there.
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
Browser* added_browser = BrowserList::GetInstance()->get(1);
ASSERT_TRUE(added_browser);
EXPECT_EQ(added_browser->profile(), new_profile);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
EXPECT_EQ(
added_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(histogram_tester,
SigninInterceptionHeuristicOutcome::
kInterceptEnterpriseForcedProfileSwitch);
// Interception bubble was closed.
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_destroyed());
// First run experience was not shown.
EXPECT_EQ(GetInterceptorDelegate(new_profile)->fre_browser(), nullptr);
EXPECT_EQ(source_interceptor_delegate->fre_browser(), nullptr);
}
// Tests the complete profile switch flow when the profile is already loaded.
IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest,
EnterpriseSwitchAlreadyOpen) {
base::HistogramTester histogram_tester;
// Enforce enterprise profile separation.
GetProfile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
"primary_account_strict");
AccountInfo account_info =
MakeAccountInfoAvailableAndUpdate("alice@example.com");
// Create another profile with a browser window.
ProfileManager* profile_manager = g_browser_process->profile_manager();
const base::FilePath profile_path =
profile_manager->GenerateNextProfileDirectoryPath();
base::RunLoop loop;
Profile* other_profile = nullptr;
base::OnceCallback<void(Browser*)> callback =
base::BindLambdaForTesting([&other_profile, &loop](Browser* browser) {
other_profile = browser->profile();
loop.Quit();
});
profiles::SwitchToProfile(profile_path, /*always_create=*/true,
std::move(callback));
loop.Run();
ASSERT_TRUE(other_profile);
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
Browser* other_browser = BrowserList::GetInstance()->get(1);
ASSERT_TRUE(other_browser);
ASSERT_EQ(other_browser->profile(), other_profile);
// Add the account to the other profile.
signin::IdentityManager* other_identity_manager =
IdentityManagerFactory::GetForProfile(other_profile);
other_identity_manager->GetAccountsMutator()->AddOrUpdateAccount(
account_info.gaia, account_info.email, "dummy_refresh_token",
/*is_under_advanced_protection=*/false,
signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN,
signin_metrics::SourceForRefreshTokenOperation::kUnknown);
other_identity_manager->GetPrimaryAccountMutator()->SetPrimaryAccount(
account_info.account_id, signin::ConsentLevel::kSync);
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
int other_original_tab_count = other_browser->tab_strip_model()->count();
// Start the interception.
GetInterceptorDelegate(GetProfile())
->set_expected_interception_type(
WebSigninInterceptor::SigninInterceptionType::kProfileSwitchForced);
DiceWebSigninInterceptor* interceptor =
DiceWebSigninInterceptorFactory::GetForProfile(GetProfile());
interceptor->MaybeInterceptWebSignin(
web_contents, account_info.account_id,
signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN,
/*is_new_account=*/true,
/*is_sync_signin=*/false);
// Add the account to the cookies (simulates the account reconcilor).
signin::SetCookieAccounts(other_identity_manager, test_url_loader_factory(),
{{account_info.email, account_info.gaia}});
// The tab was moved to the new browser.
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
EXPECT_EQ(other_browser->tab_strip_model()->count(),
other_original_tab_count + 1);
EXPECT_EQ(
other_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(histogram_tester,
SigninInterceptionHeuristicOutcome::
kInterceptEnterpriseForcedProfileSwitch);
// First run experience was not shown.
EXPECT_EQ(GetInterceptorDelegate(other_profile)->fre_browser(), nullptr);
EXPECT_EQ(GetInterceptorDelegate(GetProfile())->fre_browser(), nullptr);
}
class DiceWebSigninInterceptorParametrizedBrowserTest
: public DiceWebSigninInterceptorBrowserTest,
public testing::WithParamInterface<bool> {
public:
DiceWebSigninInterceptorParametrizedBrowserTest() {
if (WithSearchEngineChoiceEnabled()) {
scoped_feature_list_.InitAndEnableFeature(
switches::kSearchEngineChoiceTrigger);
} else {
scoped_feature_list_.InitAndDisableFeature(
switches::kSearchEngineChoiceTrigger);
}
}
bool WithSearchEngineChoiceEnabled() const { return GetParam(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// Tests the complete interception flow including profile and browser creation.
IN_PROC_BROWSER_TEST_P(DiceWebSigninInterceptorParametrizedBrowserTest,
InterceptionTest) {
base::HistogramTester histogram_tester;
// Setup profile for interception.
identity_test_env()->MakePrimaryAccountAvailable(
"alice@example.com", signin::ConsentLevel::kSignin);
AccountInfo account_info = MakeAccountInfoAvailableAndUpdate(
"bob@example.com", kNoHostedDomainFound);
SetupGaiaResponses();
int64_t search_engine_choice_timestamp =
base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds();
const char kChoiceVersion[] = "1.2.3.4";
if (WithSearchEngineChoiceEnabled()) {
PrefService* pref_service = browser()->profile()->GetPrefs();
pref_service->SetInt64(
prefs::kDefaultSearchProviderChoiceScreenCompletionTimestamp,
search_engine_choice_timestamp);
pref_service->SetString(
prefs::kDefaultSearchProviderChoiceScreenCompletionVersion,
kChoiceVersion);
TemplateURLService* template_url_service =
TemplateURLServiceFactory::GetForProfile(browser()->profile());
SetUserSelectedDefaultSearchProvider(template_url_service);
}
// Add a tab.
GURL intercepted_url = embedded_test_server()->GetURL("/defaultresponse");
content::WebContents* web_contents = AddTab(intercepted_url);
int original_tab_count = browser()->tab_strip_model()->count();
// Do the signin interception.
EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
Profile* new_profile =
InterceptAndWaitProfileCreation(web_contents, account_info.account_id);
ASSERT_TRUE(new_profile);
FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
GetInterceptorDelegate(GetProfile());
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_shown());
signin::IdentityManager* new_identity_manager =
IdentityManagerFactory::GetForProfile(new_profile);
EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
account_info.account_id));
IdentityTestEnvironmentProfileAdaptor adaptor(new_profile);
adaptor.identity_test_env()->SetAutomaticIssueOfAccessTokens(true);
// Check the profile name.
ProfileAttributesStorage& storage =
g_browser_process->profile_manager()->GetProfileAttributesStorage();
ProfileAttributesEntry* entry =
storage.GetProfileAttributesWithPath(new_profile->GetPath());
ASSERT_TRUE(entry);
EXPECT_EQ("givenname", base::UTF16ToUTF8(entry->GetLocalProfileName()));
// Check the profile color.
if (features::IsChromeWebuiRefresh2023()) {
EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
->GetUserColor()
.has_value());
} else {
EXPECT_TRUE(ThemeServiceFactory::GetForProfile(new_profile)
->UsingAutogeneratedTheme());
}
if (WithSearchEngineChoiceEnabled()) {
PrefService* new_pref_service = new_profile->GetPrefs();
EXPECT_EQ(new_pref_service->GetInt64(
prefs::kDefaultSearchProviderChoiceScreenCompletionTimestamp),
search_engine_choice_timestamp);
EXPECT_EQ(new_pref_service->GetString(
prefs::kDefaultSearchProviderChoiceScreenCompletionVersion),
kChoiceVersion);
TemplateURLService* new_template_url_service =
TemplateURLServiceFactory::GetForProfile(new_profile);
EXPECT_EQ(
new_template_url_service->GetDefaultSearchProvider()->short_name(),
base::UTF8ToUTF16(std::string(kCustomSearchEngineDomain)));
}
// A browser has been created for the new profile and the tab was moved there.
Browser* added_browser = ui_test_utils::WaitForBrowserToOpen();
ASSERT_TRUE(added_browser);
ASSERT_EQ(BrowserList::GetInstance()->size(), 2u);
EXPECT_EQ(added_browser->profile(), new_profile);
EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
EXPECT_EQ(
added_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
intercepted_url);
CheckHistograms(histogram_tester,
SigninInterceptionHeuristicOutcome::kInterceptMultiUser);
// Interception bubble is destroyed in the source profile, and was not shown
// in the new profile.
FakeDiceWebSigninInterceptorDelegate* new_interceptor_delegate =
GetInterceptorDelegate(new_profile);
EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_destroyed());
EXPECT_FALSE(new_interceptor_delegate->intercept_bubble_shown());
EXPECT_FALSE(new_interceptor_delegate->intercept_bubble_destroyed());
// First run experience UI was shown exactly once in the new profile.
EXPECT_EQ(new_interceptor_delegate->fre_browser(), added_browser);
EXPECT_EQ(new_interceptor_delegate->fre_account_id(),
account_info.account_id);
EXPECT_EQ(source_interceptor_delegate->fre_browser(), nullptr);
}
INSTANTIATE_TEST_SUITE_P(All,
DiceWebSigninInterceptorParametrizedBrowserTest,
testing::Bool());