| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/json/json_writer.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/strcat.h" |
| #include "base/test/bind.h" |
| #include "base/test/run_until.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/enterprise/browser_management/management_service_factory.h" |
| #include "chrome/browser/enterprise/util/managed_browser_utils.h" |
| #include "chrome/browser/glic/glic_pref_names.h" |
| #include "chrome/browser/glic/glic_user_status_code.h" |
| #include "chrome/browser/glic/glic_user_status_fetcher.h" |
| #include "chrome/browser/glic/public/glic_enabling.h" |
| #include "chrome/browser/glic/public/glic_keyed_service.h" |
| #include "chrome/browser/glic/public/glic_keyed_service_factory.h" |
| #include "chrome/browser/glic/test_support/glic_test_environment.h" |
| #include "chrome/browser/glic/test_support/glic_test_util.h" |
| #include "chrome/browser/prefs/browser_prefs.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/signin/chrome_signin_client_factory.h" |
| #include "chrome/browser/signin/chrome_signin_client_test_util.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/policy/core/common/management/scoped_management_service_override_for_testing.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/signin/public/base/consent_level.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/identity_manager.h" |
| #include "components/signin/public/identity_manager/identity_test_environment.h" |
| #include "content/public/test/browser_test.h" |
| #include "google_apis/common/api_error_codes.h" |
| #include "net/base/net_errors.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "services/network/test/test_utils.h" |
| #include "url/gurl.h" |
| |
| namespace glic { |
| |
| namespace { |
| const char kGlicUserStatusRelativeTestUrl[] = "/userstatus"; |
| |
| // Simple wrapper to serves as a POD for the test accounts. |
| struct TestAccount { |
| const std::string email; |
| const std::string host_domain; |
| }; |
| |
| TestAccount nonEnterpriseAccount = {"foo@testbar.com", ""}; |
| TestAccount enterpriseAccount = {"foo@testenterprise.com", |
| "testenterprise.com"}; |
| TestAccount googleDotComAccount = {"foo@google.com", "google.com"}; |
| |
| class GlicUserStatusBrowserTest : public InProcessBrowserTest { |
| protected: |
| GlicUserStatusBrowserTest() { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{features::kGlicRollout, {}}, |
| {features::kGlicUserStatusCheck, |
| {{features::kGlicUserStatusRequestDelay.name, "200ms"}, |
| {features::kGlicUserStatusRequestDelayJitter.name, "0"}}}}, |
| {/* disabled_features */}); |
| |
| RegisterGeminiSettingsPrefs(pref_service_.registry()); |
| } |
| |
| void SetUpBrowserContextKeyedServices( |
| content::BrowserContext* context) override { |
| IdentityTestEnvironmentProfileAdaptor:: |
| SetIdentityTestEnvironmentFactoriesOnBrowserContext(context); |
| |
| ChromeSigninClientFactory::GetInstance()->SetTestingFactory( |
| context, base::BindRepeating(&BuildChromeSigninClientWithURLLoader, |
| &test_url_loader_factory_)); |
| } |
| void SetUpOnMainThread() override { |
| InProcessBrowserTest::SetUpOnMainThread(); |
| |
| adaptor_ = |
| std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile()); |
| |
| identity_test_env_ = adaptor_->identity_test_env(); |
| identity_test_env_->SetTestURLLoaderFactory(&test_url_loader_factory_); |
| identity_manager_ = IdentityManagerFactory::GetForProfile(profile()); |
| |
| embedded_test_server()->AddDefaultHandlers(GetChromeTestDataDir()); |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| |
| profile()->GetPrefs()->SetInteger( |
| ::prefs::kGeminiSettings, |
| static_cast<int>(glic::prefs::SettingsPolicyState::kEnabled)); |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // TODO(crbug.com/460830699): Evaluate whether this is necessary on |
| // ChromeOS. |
| disclaimer_service_resetter_ = |
| enterprise_util::DisableAutomaticManagementDisclaimerUntilReset( |
| profile()); |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| } |
| |
| void TearDownOnMainThread() override { |
| ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); |
| identity_manager_ = nullptr; |
| identity_test_env_ = nullptr; |
| adaptor_.reset(); |
| |
| InProcessBrowserTest::TearDownOnMainThread(); |
| } |
| |
| void RegisterUserStatusHandler(net::HttpStatusCode status_code, |
| std::string response_body) { |
| embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting( |
| [=, this](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (request.relative_url != kGlicUserStatusRelativeTestUrl) { |
| return nullptr; |
| } |
| most_recent_request_ = request; |
| auto response = |
| std::make_unique<net::test_server::BasicHttpResponse>(); |
| |
| response->set_code(status_code); |
| response->set_content(response_body); |
| response->set_content_type("application/json"); |
| |
| return response; |
| })); |
| } |
| // Simulates user signing in and getting a refresh token. |
| void SimulatePrimaryAccountChangedSignIn(TestAccount* account, |
| bool fetch_account_info = true) { |
| identity_test_env_->SetAutomaticIssueOfAccessTokens(true); |
| |
| AccountInfo account_info = identity_test_env_->MakePrimaryAccountAvailable( |
| account->email, signin::ConsentLevel::kSignin); |
| |
| AccountCapabilitiesTestMutator mutator(&account_info.capabilities); |
| SetGlicCapability(mutator, true); |
| mutator.set_is_subject_to_enterprise_features( |
| !account->host_domain.empty()); |
| identity_test_env_->UpdateAccountInfoForAccount(account_info); |
| |
| if (fetch_account_info) { |
| SimulateSuccessfulFetchOfAccountInfo(account, &account_info); |
| } |
| } |
| |
| void SimulateSuccessfulFetchOfAccountInfo(const TestAccount* test_account, |
| const AccountInfo* account_info) { |
| identity_test_env_->SimulateSuccessfulFetchOfAccountInfo( |
| account_info->account_id, account_info->email, account_info->gaia, |
| test_account->host_domain, |
| base::StrCat({"full_name-", test_account->email}), |
| base::StrCat({"given_name-", test_account->email}), |
| base::StrCat({"local-", test_account->email}), |
| base::StrCat({"full_name-", test_account->email})); |
| } |
| |
| void SetGlicUserStatusUrlForTest() { |
| GlicKeyedServiceFactory::GetGlicKeyedService(browser()->profile()) |
| ->enabling() |
| .SetGlicUserStatusUrlForTest( |
| embedded_test_server()->GetURL(kGlicUserStatusRelativeTestUrl)); |
| } |
| |
| // Gets the user status code from the pref. |
| std::optional<UserStatusCode> GetCachedStatusCode() { |
| PrefService* prefs = profile()->GetPrefs(); |
| const base::DictValue& dict = prefs->GetDict(prefs::kGlicUserStatus); |
| if (!dict.FindInt(kUserStatus)) { |
| return std::nullopt; |
| } |
| return static_cast<UserStatusCode>(dict.FindInt(kUserStatus).value()); |
| } |
| |
| std::string GetGaiaIdHashBase64() { |
| auto* identity_manager = IdentityManagerFactory::GetForProfile(profile()); |
| CoreAccountInfo primary_account = |
| identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin); |
| if (primary_account.IsEmpty()) { |
| return ""; |
| } |
| return signin::GaiaIdHash::FromGaiaId(primary_account.gaia).ToBase64(); |
| } |
| |
| void SetGlicUserStatus(UserStatusCode code) { |
| base::DictValue data; |
| data.Set(kAccountId, GetGaiaIdHashBase64()); |
| data.Set(kUserStatus, code); |
| data.Set(kUpdatedAt, base::Time::Now().InSecondsFSinceUnixEpoch()); |
| profile()->GetPrefs()->SetDict(glic::prefs::kGlicUserStatus, |
| std::move(data)); |
| } |
| |
| // Gets the full user status details from prefs. |
| std::optional<base::DictValue> GetCachedStatusDict() { |
| PrefService* prefs = profile()->GetPrefs(); |
| const base::DictValue& dict = prefs->GetDict(prefs::kGlicUserStatus); |
| if (dict.empty()) { |
| return std::nullopt; |
| } |
| return dict.Clone(); |
| } |
| |
| bool IsGlicEnabled() { return GlicEnabling::IsEnabledForProfile(profile()); } |
| Profile* profile() { return browser()->profile(); } |
| |
| net::test_server::HttpRequest& most_recent_request() { |
| return most_recent_request_.value(); |
| } |
| |
| GlicTestEnvironment glic_test_env_; |
| base::test::ScopedFeatureList feature_list_; |
| sync_preferences::TestingPrefServiceSyncable pref_service_; |
| std::unique_ptr<IdentityTestEnvironmentProfileAdaptor> adaptor_; |
| raw_ptr<signin::IdentityManager> identity_manager_; |
| raw_ptr<signin::IdentityTestEnvironment> identity_test_env_; |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| base::ScopedClosureRunner disclaimer_service_resetter_; |
| std::optional<net::test_server::HttpRequest> most_recent_request_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, EnterpriseSignInEnabled) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| // Verify Prefs |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| std::optional<base::DictValue> cached_dict = GetCachedStatusDict(); |
| EXPECT_EQ(cached_dict->FindInt(kUserStatus).value_or(-1), |
| UserStatusCode::ENABLED); |
| EXPECT_EQ(*cached_dict->FindString(kAccountId), GetGaiaIdHashBase64()); |
| EXPECT_TRUE(cached_dict->FindDouble(kUpdatedAt).has_value()); |
| |
| // Verify GlicEnabling status (assuming other criteria met) |
| EXPECT_TRUE(IsGlicEnabled()); |
| } |
| |
| // TODO(460830699): Re-enable on ChromeOS. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_EnterpriseSignInEnabledGeminiSettings \ |
| DISABLED_EnterpriseSignInEnabledGeminiSettings |
| #else |
| #define MAYBE_EnterpriseSignInEnabledGeminiSettings \ |
| EnterpriseSignInEnabledGeminiSettings |
| #endif |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, |
| MAYBE_EnterpriseSignInEnabledGeminiSettings) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| // Verify request is sent by the non-existence of the Prefs initially and the |
| // existence of it after sign-in simulation. |
| ASSERT_FALSE(GetCachedStatusDict().has_value()); |
| |
| bool request_received = false; |
| embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting( |
| [=, &request_received](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (request.relative_url != kGlicUserStatusRelativeTestUrl) { |
| return nullptr; |
| } |
| request_received = true; |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| |
| response->set_code(net::HTTP_OK); |
| response->set_content( |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| response->set_content_type("application/json"); |
| |
| return response; |
| })); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| // Verify request is sent by the existence of the Prefs and request handler is |
| // inovked. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| ASSERT_TRUE(request_received); |
| |
| // Sign out to clear the Prefs and reset request_received. |
| identity_test_env_->ClearPrimaryAccount(); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return profile()->GetPrefs()->GetDict(prefs::kGlicUserStatus).empty(); |
| })); |
| request_received = false; |
| |
| // Setting kGeminiSettings to disabled so that no RPC would be sent. |
| profile()->GetPrefs()->SetInteger( |
| ::prefs::kGeminiSettings, |
| static_cast<int>(glic::prefs::SettingsPolicyState::kDisabled)); |
| |
| // Sign in again and wait for a while. |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| // Verifying the absence of a request by verifying the absence for a time |
| // period longer than the polling interval. |
| base::RunLoop run_loop; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(300)); |
| run_loop.Run(); |
| |
| // Verify no request is sent. |
| EXPECT_FALSE(request_received); |
| ASSERT_FALSE(GetCachedStatusDict().has_value()); |
| |
| // Sign out. |
| identity_test_env_->ClearPrimaryAccount(); |
| |
| // Make the account enterprise again by setting kGeminiSettings to enabled. |
| profile()->GetPrefs()->SetInteger( |
| ::prefs::kGeminiSettings, |
| static_cast<int>(glic::prefs::SettingsPolicyState::kEnabled)); |
| |
| // Sign in again. |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| // Verify request is sent again by the existence of the Prefs and request |
| // handler is inovked. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| ASSERT_TRUE(request_received); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, |
| EnterpriseGeminiSettingsChangeNoSignedOut) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| // Verify request is sent by the non-existence of the Prefs initially and the |
| // existence of it after sign-in simulation. |
| ASSERT_FALSE(GetCachedStatusDict().has_value()); |
| |
| bool request_received = false; |
| embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting( |
| [=, &request_received](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (request.relative_url != kGlicUserStatusRelativeTestUrl) { |
| return nullptr; |
| } |
| request_received = true; |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| |
| response->set_code(net::HTTP_OK); |
| response->set_content( |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| response->set_content_type("application/json"); |
| |
| return response; |
| })); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| // Verify request is sent by the existence of the Prefs and request handler is |
| // inovked. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| ASSERT_TRUE(request_received); |
| |
| // Setting kGeminiSettings to disabled so that no RPC would be sent. |
| request_received = false; |
| profile()->GetPrefs()->SetInteger( |
| ::prefs::kGeminiSettings, |
| static_cast<int>(glic::prefs::SettingsPolicyState::kDisabled)); |
| |
| // Verifying the absence of a request by verifying the absence for a time |
| // period longer than the polling interval. |
| base::RunLoop run_loop; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(300)); |
| run_loop.Run(); |
| |
| // Verify no request is sent. |
| EXPECT_FALSE(request_received); |
| |
| // Make the account enterprise again by setting kGeminiSettings to enabled. |
| profile()->GetPrefs()->SetInteger( |
| ::prefs::kGeminiSettings, |
| static_cast<int>(glic::prefs::SettingsPolicyState::kEnabled)); |
| |
| // Verify request handler is inovked. |
| ASSERT_TRUE(base::test::RunUntil([&]() { return request_received; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, |
| EnterpriseSignInDisabledByAdmin) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": false, "isAccessDeniedByAdmin": true})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| // Verify Prefs |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| std::optional<base::DictValue> cached_dict = GetCachedStatusDict(); |
| EXPECT_EQ(cached_dict->FindInt(kUserStatus).value_or(-1), |
| UserStatusCode::DISABLED_BY_ADMIN); |
| EXPECT_EQ(*cached_dict->FindString(kAccountId), GetGaiaIdHashBase64()); |
| |
| // Verify GlicEnabling status - Should be disabled by this status |
| EXPECT_FALSE(IsGlicEnabled()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, |
| EnterpriseSignInDisabledOther) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": false, "isAccessDeniedByAdmin": false})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| // Verify Prefs |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| std::optional<base::DictValue> cached_dict = GetCachedStatusDict(); |
| EXPECT_EQ(cached_dict->FindInt(kUserStatus).value_or(-1), |
| UserStatusCode::DISABLED_OTHER); |
| EXPECT_EQ(*cached_dict->FindString(kAccountId), GetGaiaIdHashBase64()); |
| |
| // Verify GlicEnabling status - Should be disabled by this status |
| EXPECT_FALSE(IsGlicEnabled()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| GlicUserStatusBrowserTest, |
| EnterpriseSignInDisabledButManagedStatusNotImmediatelyKnown) { |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": false, "isAccessDeniedByAdmin": true})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| identity_test_env_->SetAutomaticIssueOfAccessTokens(true); |
| AccountInfo account_info = identity_test_env_->MakePrimaryAccountAvailable( |
| enterpriseAccount.email, signin::ConsentLevel::kSignin); |
| enterprise_util::SetUserAcceptedAccountManagement(profile(), true); |
| AccountCapabilitiesTestMutator mutator(&account_info.capabilities); |
| SetGlicCapability(mutator, true); |
| identity_test_env_->UpdateAccountInfoForAccount(account_info); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(GetCachedStatusDict().has_value()); |
| |
| // Only now, after the fetch would have happened, does the information about |
| // the account's managed status become available. This should cause an RPC |
| // to be sent. |
| SimulateSuccessfulFetchOfAccountInfo(&enterpriseAccount, &account_info); |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| // Verify Prefs |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| std::optional<base::DictValue> cached_dict = GetCachedStatusDict(); |
| EXPECT_EQ(cached_dict->FindInt(kUserStatus).value_or(-1), |
| UserStatusCode::DISABLED_BY_ADMIN); |
| EXPECT_EQ(*cached_dict->FindString(kAccountId), GetGaiaIdHashBase64()); |
| |
| // Verify GlicEnabling status - Should be disabled by this status |
| EXPECT_FALSE(IsGlicEnabled()); |
| } |
| |
| // Regression test for crbug.com/481357491. |
| IN_PROC_BROWSER_TEST_F( |
| GlicUserStatusBrowserTest, |
| EnterpriseSignInRefreshTokenLostAndRecoveredDuringManagedCheck) { |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| // Sign in, but don't provide extended account info. This will leave the |
| // account managed status as pending. |
| identity_test_env_->SetAutomaticIssueOfAccessTokens(true); |
| AccountInfo account_info = identity_test_env_->MakePrimaryAccountAvailable( |
| enterpriseAccount.email, signin::ConsentLevel::kSignin); |
| enterprise_util::SetUserAcceptedAccountManagement(profile(), true); |
| AccountCapabilitiesTestMutator mutator(&account_info.capabilities); |
| SetGlicCapability(mutator, true); |
| identity_test_env_->UpdateAccountInfoForAccount(account_info); |
| EXPECT_FALSE(GetCachedStatusDict().has_value()); |
| |
| // Revoke the refresh token. This should cause the managed status check to |
| // fail, and no RPC will be sent. |
| identity_test_env_->RemoveRefreshTokenForAccount(account_info.account_id); |
| |
| // Verify no request is sent. |
| { |
| base::RunLoop run_loop; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(300)); |
| run_loop.Run(); |
| EXPECT_FALSE(GetCachedStatusDict().has_value()); |
| } |
| |
| // Now, restore the refresh token and provide account info. This should allow |
| // the fetcher to retry and succeed. |
| identity_test_env_->MakeAccountAvailable(account_info.email); |
| |
| // Re-apply capabilities, as they are lost when the token is restored. |
| account_info = identity_manager_->FindExtendedAccountInfoByAccountId( |
| account_info.account_id); |
| AccountCapabilitiesTestMutator refreshed_mutator(&account_info.capabilities); |
| SetGlicCapability(refreshed_mutator, true); |
| identity_test_env_->UpdateAccountInfoForAccount(account_info); |
| |
| SimulateSuccessfulFetchOfAccountInfo(&enterpriseAccount, &account_info); |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| // Verify Prefs are now set, meaning the RPC was successful. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| std::optional<base::DictValue> cached_dict = GetCachedStatusDict(); |
| EXPECT_EQ(cached_dict->FindInt(kUserStatus).value_or(-1), |
| UserStatusCode::ENABLED); |
| EXPECT_EQ(*cached_dict->FindString(kAccountId), GetGaiaIdHashBase64()); |
| |
| EXPECT_TRUE(IsGlicEnabled()); |
| } |
| |
| // It happens that google.com accounts are always considered enterprise |
| // accounts, even before extended account info is available. |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, |
| EnterpriseSignInDisabledByAdminGoogleDotCom) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": false, "isAccessDeniedByAdmin": true})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&googleDotComAccount); |
| |
| // Verify Prefs |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| std::optional<base::DictValue> cached_dict = GetCachedStatusDict(); |
| EXPECT_EQ(cached_dict->FindInt(kUserStatus).value_or(-1), |
| UserStatusCode::DISABLED_BY_ADMIN); |
| EXPECT_EQ(*cached_dict->FindString(kAccountId), GetGaiaIdHashBase64()); |
| |
| // Verify GlicEnabling status - Should be disabled by this status |
| EXPECT_FALSE(IsGlicEnabled()); |
| } |
| |
| // This ensures that the check using the policy management service still works, |
| // until/unless we need to switch back to it. |
| class GlicUserStatusBrowserTestWithPolicyManagementService |
| : public GlicUserStatusBrowserTest { |
| protected: |
| GlicUserStatusBrowserTestWithPolicyManagementService() { |
| scoped_feature_list_.InitAndEnableFeatureWithParameters( |
| features::kGlicUserStatusCheck, |
| {{features::kGlicUserStatusEnterpriseCheckStrategy.name, "policy"}}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTestWithPolicyManagementService, |
| EnterpriseSignInDisabledByAdmin) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": false, "isAccessDeniedByAdmin": true})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| // Verify Prefs |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| std::optional<base::DictValue> cached_dict = GetCachedStatusDict(); |
| EXPECT_EQ(cached_dict->FindInt(kUserStatus).value_or(-1), |
| UserStatusCode::DISABLED_BY_ADMIN); |
| EXPECT_EQ(*cached_dict->FindString(kAccountId), GetGaiaIdHashBase64()); |
| |
| // Verify GlicEnabling status - Should be disabled by this status |
| EXPECT_FALSE(IsGlicEnabled()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, |
| EnterpriseSignInServerUnavailableNoStoredResult) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_NOT_FOUND, |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| // Verify that when the user status code is SERVER_UNAVAILABLE, the glic user |
| // status result is not stored. |
| std::optional<base::DictValue> cached_dict = GetCachedStatusDict(); |
| EXPECT_FALSE(cached_dict.has_value()); |
| |
| EXPECT_TRUE(IsGlicEnabled()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, |
| EnterpriseSignInServerUnavailableHasStoredResult) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_NOT_FOUND, |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| auto user_status_code = UserStatusCode::DISABLED_BY_ADMIN; |
| SetGlicUserStatus(user_status_code); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| // Verify that when the user status code is SERVER_UNAVAILABLE, the previous |
| // stored pref value is not overwritten. |
| std::optional<base::DictValue> cached_dict = GetCachedStatusDict(); |
| EXPECT_TRUE(cached_dict.has_value()); |
| EXPECT_EQ(cached_dict->FindInt(kUserStatus).value_or(-1), user_status_code); |
| |
| EXPECT_FALSE(IsGlicEnabled()); |
| } |
| |
| // TODO(460830699): Re-enable on ChromeOS. |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_EnterpriseSignOut DISABLED_EnterpriseSignOut |
| #else |
| #define MAYBE_EnterpriseSignOut EnterpriseSignOut |
| #endif |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, MAYBE_EnterpriseSignOut) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| // Sign in and set user status to enabled. |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| ASSERT_TRUE(IsGlicEnabled()); |
| |
| // Sign out. |
| identity_test_env_->ClearPrimaryAccount(); |
| |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return profile()->GetPrefs()->GetDict(prefs::kGlicUserStatus).empty(); |
| })); |
| |
| // This is false because the IsNonEnterpriseEnabled() will return false if no |
| // account is signed in. The UserStatusCheck is true when pref is cleared. |
| EXPECT_FALSE(IsGlicEnabled()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, NonEnterpriseSignIn) { |
| bool request_received = false; |
| embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting( |
| [=, &request_received](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (request.relative_url != kGlicUserStatusRelativeTestUrl) { |
| return nullptr; |
| } |
| request_received = true; |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| |
| response->set_code(net::HTTP_OK); |
| response->set_content( |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| response->set_content_type("application/json"); |
| |
| return response; |
| })); |
| |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&nonEnterpriseAccount); |
| |
| // wait for a while. |
| base::RunLoop run_loop; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(300)); |
| run_loop.Run(); |
| |
| ASSERT_FALSE(request_received); |
| ASSERT_FALSE(GetCachedStatusDict().has_value()); |
| |
| ASSERT_TRUE(IsGlicEnabled()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, EnterpriseDataProtection) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false, |
| "isEnterpriseAccountDataProtected": true })"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| // Verify the isEnterpriseAccountDataProtected field. |
| EXPECT_EQ(profile() |
| ->GetPrefs() |
| ->GetDict(prefs::kGlicUserStatus) |
| .FindBool(kIsEnterpriseAccountDataProtected), |
| true); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, |
| EnterpriseDataProtectionMissingInServerResponse) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| // Verify the isEnterpriseAccountDataProtected field. |
| EXPECT_EQ(profile() |
| ->GetPrefs() |
| ->GetDict(prefs::kGlicUserStatus) |
| .FindBool(kIsEnterpriseAccountDataProtected), |
| false); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(GlicUserStatusBrowserTest, ClientDataHeaderExists) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::CLOUD); |
| |
| RegisterUserStatusHandler( |
| net::HTTP_OK, |
| R"({"isGlicEnabled": true, "isAccessDeniedByAdmin": false})"); |
| net::test_server::EmbeddedTestServerHandle test_server_handle; |
| ASSERT_TRUE(test_server_handle = |
| embedded_test_server()->StartAndReturnHandle()); |
| |
| SetGlicUserStatusUrlForTest(); |
| |
| SimulatePrimaryAccountChangedSignIn(&enterpriseAccount); |
| |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetCachedStatusDict().has_value(); })); |
| |
| EXPECT_NE(most_recent_request().headers["X-Client-Data"], ""); |
| } |
| |
| class GlicShareImageEnablementBrowserTest : public GlicUserStatusBrowserTest { |
| protected: |
| GlicShareImageEnablementBrowserTest() { |
| feature_list_.InitWithFeatures({features::kGlicShareImage}, {}); |
| } |
| |
| bool IsShareImageEnabled() { |
| return GlicEnabling::IsShareImageEnabledForProfile(profile()); |
| } |
| |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(GlicShareImageEnablementBrowserTest, LiveEligibility) { |
| policy::ScopedManagementServiceOverrideForTesting platform_management( |
| policy::ManagementServiceFactory::GetForProfile(profile()), |
| policy::EnterpriseManagementAuthority::NONE); |
| auto* const identity_manager = |
| IdentityManagerFactory::GetForProfile(profile()); |
| |
| SimulatePrimaryAccountChangedSignIn(&nonEnterpriseAccount); |
| AccountInfo primary_account = |
| identity_manager->FindExtendedAccountInfoByAccountId( |
| identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSignin)); |
| ASSERT_FALSE(primary_account.IsEmpty()); |
| AccountCapabilitiesTestMutator mutator(&primary_account.capabilities); |
| |
| // Set the account capability to true. |
| mutator.set_can_use_model_execution_features(true); |
| signin::UpdateAccountInfoForAccount(identity_test_env_->identity_manager(), |
| primary_account); |
| |
| // Share image should be enabled here. |
| EXPECT_TRUE(IsShareImageEnabled()); |
| |
| // Now disable the account capability. |
| mutator.set_can_use_model_execution_features(false); |
| signin::UpdateAccountInfoForAccount(identity_test_env_->identity_manager(), |
| primary_account); |
| |
| // Share image should now be disabled without this capability. |
| EXPECT_FALSE(IsShareImageEnabled()); |
| } |
| |
| } // namespace |
| } // namespace glic |