| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/net/system_proxy_manager.h" |
| |
| #include <array> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #include "base/functional/bind.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/current_thread.h" |
| #include "base/test/bind.h" |
| #include "base/test/gtest_tags.h" |
| #include "base/test/run_until.h" |
| #include "chrome/browser/ash/login/test/device_state_mixin.h" |
| #include "chrome/browser/ash/notifications/request_system_proxy_credentials_view.h" |
| #include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h" |
| #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" |
| #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h" |
| #include "chrome/browser/notifications/notification_display_service_tester.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/login/login_handler.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h" |
| #include "chromeos/ash/components/dbus/shill/shill_profile_client.h" |
| #include "chromeos/ash/components/dbus/shill/shill_service_client.h" |
| #include "chromeos/ash/components/dbus/system_proxy/system_proxy_client.h" |
| #include "chromeos/ash/components/dbus/system_proxy/system_proxy_service.pb.h" |
| #include "chromeos/ash/components/install_attributes/stub_install_attributes.h" |
| #include "chromeos/ash/components/login/login_state/login_state.h" |
| #include "chromeos/ash/components/network/network_handler.h" |
| #include "chromeos/ash/components/network/network_state.h" |
| #include "chromeos/ash/components/network/network_state_handler.h" |
| #include "chromeos/ash/components/network/proxy/proxy_config_handler.h" |
| #include "chromeos/ash/components/settings/cros_settings_names.h" |
| #include "components/policy/core/common/mock_configuration_policy_provider.h" |
| #include "components/policy/core/common/policy_map.h" |
| #include "components/policy/core/common/policy_types.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/policy/proto/chrome_device_policy.pb.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/proxy_config/proxy_config_dictionary.h" |
| #include "components/proxy_config/proxy_config_pref_names.h" |
| #include "components/proxy_config/proxy_prefs.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "net/base/host_port_pair.h" |
| #include "net/base/proxy_server.h" |
| #include "net/http/http_auth_cache.h" |
| #include "net/test/embedded_test_server/default_handlers.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/register_basic_auth_handler.h" |
| #include "net/url_request/url_request_context.h" |
| #include "services/network/public/mojom/network_context.mojom.h" |
| #include "third_party/cros_system_api/dbus/shill/dbus-constants.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/controls/textfield/textfield.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kRealm[] = "My proxy"; |
| constexpr char kScheme[] = "dIgEsT"; |
| constexpr char kProxyAuthUrl[] = "http://example.com:3128"; |
| constexpr char kSystemProxyNotificationId[] = "system-proxy.auth_required"; |
| constexpr char kUsername[] = "testuser"; |
| constexpr char16_t kUsername16[] = u"testuser"; |
| constexpr char kPassword[] = "testpwd"; |
| constexpr char16_t kPassword16[] = u"testpwd"; |
| |
| constexpr char kUserProfilePath[] = "user_profile"; |
| constexpr char kDefaultServicePath[] = "default_wifi"; |
| constexpr char kDefaultServiceSsid[] = "default_wifi_guid"; |
| constexpr char kDefaultServiceGuid[] = "eth0"; |
| |
| constexpr char kWifiServicePath[] = "stub_wifi"; |
| constexpr char kWifiSsid[] = "wifi0"; |
| constexpr char kWifiGuid[] = "{wifi0_guid}"; |
| |
| constexpr char kONCPolicyWifi0Proxy[] = |
| R"({ |
| "NetworkConfigurations": [ { |
| "GUID": "{wifi0_guid}", |
| "Name": "wifi0", |
| "ProxySettings": { |
| "Manual": { |
| "HTTPProxy": { |
| "Host": "proxyhost", |
| "Port": 3128 |
| }, |
| "SecureHTTPProxy": { |
| "Host": "proxyhost", |
| "Port": 3128 |
| } |
| }, |
| "Type": "Manual" |
| }, |
| "Type": "WiFi", |
| "WiFi": { |
| "AutoConnect": true, |
| "HiddenSSID": false, |
| "SSID": "wifi0", |
| "Security": "None" |
| } |
| } ] |
| })"; |
| |
| constexpr char kONCPolicyWifi0AltProxy[] = |
| R"({ |
| "NetworkConfigurations": [ { |
| "GUID": "{wifi0_guid}", |
| "Name": "wifi0", |
| "ProxySettings": { |
| "Manual": { |
| "HTTPProxy": { |
| "Host": "proxyhostalt", |
| "Port": 3129 |
| }, |
| "SecureHTTPProxy": { |
| "Host": "proxyhostalt", |
| "Port": 3129 |
| } |
| }, |
| "Type": "Manual" |
| }, |
| "Type": "WiFi", |
| "WiFi": { |
| "AutoConnect": true, |
| "HiddenSSID": false, |
| "SSID": "wifi0", |
| "Security": "None" |
| } |
| } ] |
| })"; |
| |
| constexpr char kSystemProxyPolicyJson[] = |
| R"({ |
| "system_proxy_enabled": true, |
| "system_services_username": "%s", |
| "system_services_password": "%s", |
| %s |
| })"; |
| |
| constexpr char kAuthSchemesPolicyEntry[] = |
| R"("policy_credentials_auth_schemes": [%s],)"; |
| |
| void RunUntilIdle() { |
| DCHECK(base::CurrentThread::Get()); |
| base::RunLoop loop; |
| loop.RunUntilIdle(); |
| } |
| |
| } // namespace |
| |
| class SystemProxyManagerBrowserTest : public InProcessBrowserTest { |
| public: |
| SystemProxyManagerBrowserTest() = default; |
| SystemProxyManagerBrowserTest(const SystemProxyManagerBrowserTest&) = delete; |
| SystemProxyManagerBrowserTest& operator=( |
| const SystemProxyManagerBrowserTest&) = delete; |
| ~SystemProxyManagerBrowserTest() override = default; |
| |
| void SetUpOnMainThread() override { |
| GetSystemProxyManager()->StartObservingPrimaryProfilePrefs( |
| browser()->profile()); |
| display_service_tester_ = |
| std::make_unique<NotificationDisplayServiceTester>(/*profile=*/nullptr); |
| GetSystemProxyManager()->SetSystemProxyEnabledForTest(true); |
| } |
| |
| void TearDownOnMainThread() override { |
| GetSystemProxyManager()->SetSystemProxyEnabledForTest(false); |
| } |
| |
| protected: |
| SystemProxyManager* GetSystemProxyManager() { |
| return SystemProxyManager::Get(); |
| } |
| |
| RequestSystemProxyCredentialsView* dialog() { |
| return GetSystemProxyManager()->GetActiveAuthDialogForTest(); |
| } |
| |
| SystemProxyClient::TestInterface* client_test_interface() { |
| return SystemProxyClient::Get()->GetTestInterface(); |
| } |
| |
| void SendAuthenticationRequest(bool bad_cached_credentials) { |
| system_proxy::ProtectionSpace protection_space; |
| protection_space.set_origin(kProxyAuthUrl); |
| protection_space.set_scheme(kScheme); |
| protection_space.set_realm(kRealm); |
| |
| system_proxy::AuthenticationRequiredDetails details; |
| details.set_bad_cached_credentials(bad_cached_credentials); |
| *details.mutable_proxy_protection_space() = protection_space; |
| |
| client_test_interface()->SendAuthenticationRequiredSignal(details); |
| } |
| |
| void WaitForNotification() { |
| base::RunLoop run_loop; |
| display_service_tester_->SetNotificationAddedClosure( |
| run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| std::unique_ptr<NotificationDisplayServiceTester> display_service_tester_; |
| }; |
| |
| // Tests the flow for setting user credentials for System-proxy: |
| // - Receiving an authentication request prompts a notification; |
| // - Clicking on the notification opens a dialog; |
| // - Credentials introduced in the dialog are sent via D-Bus to System-proxy. |
| IN_PROC_BROWSER_TEST_F(SystemProxyManagerBrowserTest, AuthenticationDialog) { |
| base::RunLoop run_loop; |
| GetSystemProxyManager()->SetSendAuthDetailsClosureForTest( |
| run_loop.QuitClosure()); |
| |
| EXPECT_FALSE( |
| display_service_tester_->GetNotification(kSystemProxyNotificationId)); |
| SendAuthenticationRequest(/* bad_cached_credentials = */ false); |
| WaitForNotification(); |
| |
| EXPECT_TRUE( |
| display_service_tester_->GetNotification(kSystemProxyNotificationId)); |
| |
| display_service_tester_->SimulateClick( |
| NotificationHandler::Type::TRANSIENT, kSystemProxyNotificationId, |
| /*action_index=*/std::nullopt, /*reply=*/std::nullopt); |
| // Dialog is created. |
| ASSERT_TRUE(dialog()); |
| |
| // Expect warning is not shown. |
| ASSERT_FALSE(dialog()->error_label_for_testing()->GetVisible()); |
| dialog()->username_textfield_for_testing()->SetText(kUsername16); |
| dialog()->password_textfield_for_testing()->SetText(kPassword16); |
| |
| // Simulate clicking on "OK" button. |
| dialog()->Accept(); |
| |
| // Wait for the callback set via |SetSendAuthDetailsClosureForTest| to be |
| // called. The callback will be called when SystemProxyManager calls the D-Bus |
| // method |SetAuthenticationDetails| |
| run_loop.Run(); |
| |
| system_proxy::SetAuthenticationDetailsRequest request = |
| client_test_interface()->GetLastAuthenticationDetailsRequest(); |
| |
| ASSERT_TRUE(request.has_credentials()); |
| EXPECT_EQ(request.credentials().username(), kUsername); |
| EXPECT_EQ(request.credentials().password(), kPassword); |
| |
| // Verify that the UI elements are reset. |
| GetSystemProxyManager()->CloseAuthDialogForTest(); |
| EXPECT_FALSE( |
| display_service_tester_->GetNotification(kSystemProxyNotificationId)); |
| EXPECT_FALSE(dialog()); |
| } |
| |
| // Tests that canceling the authentication dialog sends empty credentials to |
| // System-proxy. |
| IN_PROC_BROWSER_TEST_F(SystemProxyManagerBrowserTest, |
| CancelAuthenticationDialog) { |
| EXPECT_FALSE( |
| display_service_tester_->GetNotification(kSystemProxyNotificationId)); |
| SendAuthenticationRequest(/* bad_cached_credentials = */ false); |
| WaitForNotification(); |
| EXPECT_TRUE( |
| display_service_tester_->GetNotification(kSystemProxyNotificationId)); |
| |
| display_service_tester_->SimulateClick( |
| NotificationHandler::Type::TRANSIENT, kSystemProxyNotificationId, |
| /*action_index=*/std::nullopt, /*reply=*/std::nullopt); |
| |
| // Dialog is created. |
| ASSERT_TRUE(dialog()); |
| |
| // Expect warning is not shown. |
| ASSERT_FALSE(dialog()->error_label_for_testing()->GetVisible()); |
| dialog()->username_textfield_for_testing()->SetText(kUsername16); |
| dialog()->password_textfield_for_testing()->SetText(kPassword16); |
| |
| base::RunLoop run_loop; |
| GetSystemProxyManager()->SetSendAuthDetailsClosureForTest( |
| run_loop.QuitClosure()); |
| // Simulate clicking on "Cancel" button. |
| dialog()->Cancel(); |
| run_loop.Run(); |
| |
| system_proxy::SetAuthenticationDetailsRequest request = |
| client_test_interface()->GetLastAuthenticationDetailsRequest(); |
| |
| ASSERT_TRUE(request.has_credentials()); |
| EXPECT_EQ(request.credentials().username(), ""); |
| EXPECT_EQ(request.credentials().password(), ""); |
| |
| // Verify that the UI elements are reset. |
| GetSystemProxyManager()->CloseAuthDialogForTest(); |
| EXPECT_FALSE( |
| display_service_tester_->GetNotification(kSystemProxyNotificationId)); |
| EXPECT_FALSE(dialog()); |
| } |
| |
| // Tests that the warning informing the user that the previous credentials are |
| // incorrect is shown in the UI. |
| IN_PROC_BROWSER_TEST_F(SystemProxyManagerBrowserTest, |
| BadCachedCredentialsWarning) { |
| EXPECT_FALSE( |
| display_service_tester_->GetNotification(kSystemProxyNotificationId)); |
| SendAuthenticationRequest(/* bad_cached_credentials = */ true); |
| WaitForNotification(); |
| EXPECT_TRUE( |
| display_service_tester_->GetNotification(kSystemProxyNotificationId)); |
| |
| display_service_tester_->SimulateClick( |
| NotificationHandler::Type::TRANSIENT, kSystemProxyNotificationId, |
| /*action_index=*/std::nullopt, /*reply=*/std::nullopt); |
| ASSERT_TRUE(dialog()); |
| |
| // Expect warning is shown. |
| EXPECT_TRUE(dialog()->error_label_for_testing()->GetVisible()); |
| } |
| |
| class SystemProxyManagerPolicyCredentialsBrowserTest |
| : public MixinBasedInProcessBrowserTest { |
| public: |
| SystemProxyManagerPolicyCredentialsBrowserTest() { |
| device_state_.SetState( |
| DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED); |
| device_state_.set_skip_initial_policy_setup(true); |
| } |
| SystemProxyManagerPolicyCredentialsBrowserTest( |
| const SystemProxyManagerPolicyCredentialsBrowserTest&) = delete; |
| SystemProxyManagerPolicyCredentialsBrowserTest& operator=( |
| const SystemProxyManagerPolicyCredentialsBrowserTest&) = delete; |
| ~SystemProxyManagerPolicyCredentialsBrowserTest() override = default; |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| SessionManagerClient::InitializeFakeInMemory(); |
| |
| MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture(); |
| constexpr std::string_view kAffiliationID = "id"; |
| // Initialize device policy. |
| auto affiliation_helper = policy::AffiliationTestHelper::CreateForCloud( |
| FakeSessionManagerClient::Get()); |
| ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetDeviceAffiliationIDs( |
| &policy_helper_, std::array{kAffiliationID})); |
| |
| provider_.SetDefaultReturns( |
| /*is_initialization_complete_return=*/true, |
| /*is_first_policy_load_complete_return=*/true); |
| policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_); |
| } |
| |
| void SetUpOnMainThread() override { SetupNetworkEnvironment(); } |
| |
| protected: |
| void SetPolicyCredentials(const std::string& username, |
| const std::string& password, |
| const std::string& auth_schemes = "") { |
| const std::string schemes_json_entry = |
| auth_schemes.empty() |
| ? "" |
| : base::StringPrintf(kAuthSchemesPolicyEntry, auth_schemes.c_str()); |
| const std::string policy_value = |
| base::StringPrintf(kSystemProxyPolicyJson, username.c_str(), |
| password.c_str(), schemes_json_entry.c_str()); |
| enterprise_management::ChromeDeviceSettingsProto& proto( |
| policy_helper_.device_policy()->payload()); |
| proto.mutable_system_proxy_settings()->set_system_proxy_settings( |
| policy_value); |
| policy_helper_.RefreshPolicyAndWaitUntilDeviceSettingsUpdated( |
| {kSystemProxySettings}); |
| RunUntilIdle(); |
| } |
| |
| void SetOncPolicy(const std::string& policy_json, policy::PolicyScope scope) { |
| policy::PolicyMap policy; |
| policy.Set(policy::key::kOpenNetworkConfiguration, |
| policy::POLICY_LEVEL_MANDATORY, scope, |
| policy::POLICY_SOURCE_CLOUD, base::Value(policy_json), nullptr); |
| provider_.UpdateChromePolicy(policy); |
| RunUntilIdle(); |
| } |
| |
| void DisconnectNetworkService(const std::string& service_path) { |
| ShillServiceClient::TestInterface* service_test = |
| ShillServiceClient::Get()->GetTestInterface(); |
| base::Value value(shill::kStateIdle); |
| service_test->SetServiceProperty(service_path, shill::kStateProperty, |
| value); |
| RunUntilIdle(); |
| } |
| |
| void ConnectWifiNetworkService(const std::string& service_path, |
| const std::string& guid, |
| const std::string& ssid) { |
| ShillServiceClient::TestInterface* service_test = |
| ShillServiceClient::Get()->GetTestInterface(); |
| |
| service_test->AddService(service_path, guid, ssid, shill::kTypeWifi, |
| shill::kStateOnline, true /* add_to_visible */); |
| |
| service_test->SetServiceProperty(service_path, shill::kProfileProperty, |
| base::Value(kUserProfilePath)); |
| RunUntilIdle(); |
| } |
| |
| void SetProxyConfigForNetworkService(const std::string& service_path, |
| base::Value::Dict proxy_config) { |
| ProxyConfigDictionary proxy_config_dict(std::move(proxy_config)); |
| DCHECK(NetworkHandler::IsInitialized()); |
| const NetworkState* network = |
| NetworkHandler::Get()->network_state_handler()->GetNetworkState( |
| service_path); |
| ASSERT_TRUE(network); |
| proxy_config::SetProxyConfigForNetwork(proxy_config_dict, *network); |
| } |
| |
| void ExpectSystemCredentialsSent( |
| const std::string& username, |
| const std::string& password, |
| const std::vector<std::string>& auth_schemes = {}) { |
| system_proxy::SetAuthenticationDetailsRequest request = |
| client_test_interface()->GetLastAuthenticationDetailsRequest(); |
| EXPECT_EQ(username, request.credentials().username()); |
| EXPECT_EQ(password, request.credentials().password()); |
| ASSERT_EQ( |
| auth_schemes.size(), |
| static_cast<size_t>( |
| request.credentials().policy_credentials_auth_schemes().size())); |
| for (size_t i = 0; i < auth_schemes.size(); ++i) { |
| EXPECT_EQ(request.credentials().policy_credentials_auth_schemes()[i], |
| auth_schemes[i]); |
| } |
| EXPECT_EQ(system_proxy::TrafficOrigin::SYSTEM, request.traffic_type()); |
| } |
| |
| SystemProxyClient::TestInterface* client_test_interface() { |
| return SystemProxyClient::Get()->GetTestInterface(); |
| } |
| |
| private: |
| void SetupNetworkEnvironment() { |
| ShillProfileClient::TestInterface* profile_test = |
| ShillProfileClient::Get()->GetTestInterface(); |
| ShillServiceClient::TestInterface* service_test = |
| ShillServiceClient::Get()->GetTestInterface(); |
| |
| profile_test->AddProfile(kUserProfilePath, "user"); |
| |
| service_test->ClearServices(); |
| ConnectWifiNetworkService(kDefaultServicePath, kDefaultServiceSsid, |
| kDefaultServiceGuid); |
| } |
| DeviceStateMixin device_state_{ |
| &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_UNOWNED}; |
| |
| ScopedStubInstallAttributes test_install_attributes_; |
| testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_; |
| policy::DevicePolicyCrosTestHelper policy_helper_; |
| }; |
| |
| // Tests that the SystemProxyManager syncs credentials correctly for managed |
| // proxies configured via device ONC policy. It also tests the overall policy |
| // credentials syncing behaviour, more specifically that SystemProxyManager: |
| // - sends a D-Bus request when enabled by policy; |
| // - doesn't send redundant requests to set credentials, i.e. when the default |
| // network is updated; |
| // - sends requests to clear credentials if the network is not managed. |
| IN_PROC_BROWSER_TEST_F(SystemProxyManagerPolicyCredentialsBrowserTest, |
| DeviceONCPolicyUpdates) { |
| int set_auth_details_call_count = 0; |
| SetPolicyCredentials(/*username=*/"", /*password=*/""); |
| // Expect that if System-proxy policy is enabled, one initial call to set |
| // empty credentials is sent, regardless of network configuration. |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent("", ""); |
| DisconnectNetworkService(kDefaultServicePath); |
| |
| ConnectWifiNetworkService(kWifiServicePath, kWifiGuid, kWifiSsid); |
| |
| // Set an ONC policy with proxy configuration and expect that no D-Bus call to |
| // update credentials is sent, since the credentials were not changed. |
| SetOncPolicy(kONCPolicyWifi0Proxy, policy::POLICY_SCOPE_MACHINE); |
| EXPECT_EQ(set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| |
| // Set policy credentials for the remote proxy and expect that a D-Bus request |
| // to set policy credentials was sent. |
| SetPolicyCredentials(kUsername, kPassword); |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent(kUsername, kPassword); |
| |
| // Connect to a different managed proxy and expect that no additional request |
| // is sent to System-proxy since credentials were not changed. |
| SetOncPolicy(kONCPolicyWifi0AltProxy, policy::POLICY_SCOPE_MACHINE); |
| EXPECT_EQ(set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| |
| // Change credentials and expect that a D-Bus request to set the new |
| // credentials is sent to System-proxy. |
| SetPolicyCredentials("test_user_alt", "test_pwd_alt"); |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent("test_user_alt", "test_pwd_alt"); |
| |
| DisconnectNetworkService(kWifiServicePath); |
| |
| // Expect credentials are cleared on non-managed networks. |
| ConnectWifiNetworkService(kDefaultServicePath, kDefaultServiceSsid, |
| kDefaultServiceGuid); |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent("", ""); |
| } |
| |
| // Tests that the SystemProxyManager syncs credentials correctly for managed |
| // proxies configured via user ONC policy. |
| IN_PROC_BROWSER_TEST_F(SystemProxyManagerPolicyCredentialsBrowserTest, |
| UserONCPolicy) { |
| int set_auth_details_call_count = 0; |
| SetPolicyCredentials(kUsername, kPassword); |
| // Expect that credentials were not sent, since there's no managed proxy |
| // configured. |
| ExpectSystemCredentialsSent("", ""); |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| DisconnectNetworkService(kDefaultServicePath); |
| |
| // Configure a proxy via user ONC policy and expect that credentials were |
| // forwarded to System-proxy. |
| SetOncPolicy(kONCPolicyWifi0Proxy, policy::POLICY_SCOPE_USER); |
| ConnectWifiNetworkService(kWifiServicePath, kWifiGuid, kWifiSsid); |
| |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent(kUsername, kPassword); |
| } |
| |
| // Tests that the SystemProxyManager syncs credentials correctly for managed |
| // proxies configured via the user policy which sets the kProxy pref. |
| IN_PROC_BROWSER_TEST_F(SystemProxyManagerPolicyCredentialsBrowserTest, |
| UserPolicy) { |
| int set_auth_details_call_count = 0; |
| SetPolicyCredentials(kUsername, kPassword); |
| // Expect that credentials were not sent, since there's no managed proxy |
| // configured. |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent("", ""); |
| |
| // Configure a proxy via user policy. |
| auto proxy_config = base::Value::Dict() |
| .Set("mode", ProxyPrefs::kPacScriptProxyModeName) |
| .Set("pac_url", "http://proxy"); |
| browser()->profile()->GetPrefs()->SetDict(::proxy_config::prefs::kProxy, |
| std::move(proxy_config)); |
| RunUntilIdle(); |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent(kUsername, kPassword); |
| } |
| |
| // Tests that the SystemProxyManager doesn't send credentials for user |
| // configured proxies. |
| IN_PROC_BROWSER_TEST_F(SystemProxyManagerPolicyCredentialsBrowserTest, |
| UserSetProxy) { |
| SetPolicyCredentials(kUsername, kPassword); |
| auto proxy_config = base::Value::Dict() |
| .Set("mode", ProxyPrefs::kFixedServersProxyModeName) |
| .Set("server", "proxy:8080"); |
| SetProxyConfigForNetworkService(kDefaultServicePath, std::move(proxy_config)); |
| RunUntilIdle(); |
| int set_auth_details_call_count = 0; |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent("", ""); |
| } |
| |
| // Tests that the SystemProxyManager forwards the authentication schemes set by |
| // policy to System-proxy via D-Bus. |
| IN_PROC_BROWSER_TEST_F(SystemProxyManagerPolicyCredentialsBrowserTest, |
| PolicySetAuthSchemes) { |
| int set_auth_details_call_count = 0; |
| SetPolicyCredentials(kUsername, kPassword, R"("basic","digest")"); |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent("", "", {"basic", "digest"}); |
| DisconnectNetworkService(kDefaultServicePath); |
| // Configure a proxy via user ONC policy and expect that credentials were |
| // forwarded to System-proxy. |
| SetOncPolicy(kONCPolicyWifi0Proxy, policy::POLICY_SCOPE_USER); |
| ConnectWifiNetworkService(kWifiServicePath, kWifiGuid, kWifiSsid); |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent(kUsername, kPassword, {"basic", "digest"}); |
| |
| SetPolicyCredentials(kUsername, kPassword, R"("ntlm")"); |
| EXPECT_EQ(++set_auth_details_call_count, |
| client_test_interface()->GetSetAuthenticationDetailsCallCount()); |
| ExpectSystemCredentialsSent(kUsername, kPassword, {"ntlm"}); |
| } |
| |
| namespace { |
| constexpr char kProxyUsername[] = "foo"; |
| constexpr char16_t kProxyUsername16[] = u"foo"; |
| constexpr char kProxyPassword[] = "bar"; |
| constexpr char16_t kProxyPassword16[] = u"bar"; |
| constexpr char kBadUsername[] = "bad-username"; |
| constexpr char kBadPassword[] = "bad-pwd"; |
| constexpr char kOriginHostname[] = "a.test"; |
| } // namespace |
| |
| class SystemProxyCredentialsReuseBrowserTest |
| : public SystemProxyManagerPolicyCredentialsBrowserTest { |
| public: |
| SystemProxyCredentialsReuseBrowserTest() {} |
| SystemProxyCredentialsReuseBrowserTest( |
| const SystemProxyCredentialsReuseBrowserTest&) = delete; |
| SystemProxyCredentialsReuseBrowserTest& operator=( |
| const SystemProxyCredentialsReuseBrowserTest&) = delete; |
| ~SystemProxyCredentialsReuseBrowserTest() override = default; |
| |
| void SetUpOnMainThread() override { |
| InProcessBrowserTest::SetUpOnMainThread(); |
| |
| https_server_ = std::make_unique<net::EmbeddedTestServer>( |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); |
| https_server_->ServeFilesFromSourceDirectory("chrome/test/data"); |
| net::test_server::RegisterDefaultHandlers(https_server_.get()); |
| ASSERT_TRUE(https_server_->Start()); |
| |
| RegisterProxyBasicAuthHandler(proxy_server_, kProxyUsername, |
| kProxyPassword); |
| proxy_server_.EnableConnectProxy({net::HostPortPair::FromURL( |
| https_server_->GetURL(kOriginHostname, "/"))}); |
| ASSERT_TRUE(proxy_server_.Start()); |
| } |
| |
| protected: |
| content::WebContents* GetWebContents() const { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| void SetManagedProxy() { |
| // Configure a proxy via user policy. |
| auto proxy_config = |
| base::Value::Dict() |
| .Set("mode", ProxyPrefs::kFixedServersProxyModeName) |
| .Set("server", proxy_server_.host_port_pair().ToString()); |
| browser()->profile()->GetPrefs()->SetDict(::proxy_config::prefs::kProxy, |
| std::move(proxy_config)); |
| RunUntilIdle(); |
| } |
| |
| GURL GetServerUrl(const std::string& page) { |
| return https_server_->GetURL(kOriginHostname, page); |
| } |
| |
| // Navigates to the test page "/simple.html" and authenticates in the proxy |
| // login dialog with `username` and `password`. |
| void LoginWithDialog(const std::u16string& username, |
| const std::u16string& password) { |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), GetServerUrl("/simple.html"))); |
| ASSERT_TRUE(base::test::RunUntil([]() { |
| return LoginHandler::GetAllLoginHandlersForTest().size() == 1; |
| })); |
| LoginHandler* login_handler = |
| LoginHandler::GetAllLoginHandlersForTest().front(); |
| login_handler->SetAuth(username, password); |
| } |
| |
| void CheckEntryInHttpAuthCache(const std::string& auth_scheme, |
| const std::string& expected_username, |
| const std::string& expected_password) { |
| network::mojom::NetworkContext* network_context = |
| browser()->profile()->GetDefaultStoragePartition()->GetNetworkContext(); |
| std::string username; |
| std::string password; |
| base::RunLoop loop; |
| network_context->LookupProxyAuthCredentials( |
| net::ProxyServer(net::ProxyServer::SCHEME_HTTP, |
| proxy_server_.host_port_pair()), |
| auth_scheme, "TestServer", |
| base::BindOnce( |
| [](std::string* username, std::string* password, |
| base::OnceClosure closure, |
| const std::optional<net::AuthCredentials>& credentials) { |
| if (credentials) { |
| *username = base::UTF16ToUTF8(credentials->username()); |
| *password = base::UTF16ToUTF8(credentials->password()); |
| } |
| std::move(closure).Run(); |
| }, |
| &username, &password, loop.QuitClosure())); |
| loop.Run(); |
| EXPECT_EQ(username, expected_username); |
| EXPECT_EQ(password, expected_password); |
| } |
| |
| SystemProxyManager* GetSystemProxyManager() { |
| return SystemProxyManager::Get(); |
| } |
| |
| std::unique_ptr<net::EmbeddedTestServer> https_server_; |
| // A proxy server which requires authentication using the 'Basic' |
| // authentication method. |
| net::EmbeddedTestServer proxy_server_{net::EmbeddedTestServer::TYPE_HTTP}; |
| }; |
| |
| // Verifies that the policy provided credentials are not used for regular users. |
| IN_PROC_BROWSER_TEST_F(SystemProxyCredentialsReuseBrowserTest, RegularUser) { |
| base::AddTagToTestResult("feature_id", |
| "screenplay-04b4c463-f720-4cd4-9e50-7ee009d9a241"); |
| SetManagedProxy(); |
| SetPolicyCredentials(kProxyUsername, kProxyPassword); |
| LoginWithDialog(kProxyUsername16, kProxyPassword16); |
| CheckEntryInHttpAuthCache("Basic", kProxyUsername, kProxyPassword); |
| } |
| |
| // Verifies that the policy provided credentials are used for MGS. |
| IN_PROC_BROWSER_TEST_F(SystemProxyCredentialsReuseBrowserTest, |
| PolicyCredentialsUsed) { |
| SetManagedProxy(); |
| LoginState::Get()->SetLoggedInState( |
| LoginState::LOGGED_IN_ACTIVE, LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT); |
| SetPolicyCredentials(kProxyUsername, kProxyPassword); |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), GetServerUrl("/simple.html"))); |
| CheckEntryInHttpAuthCache("Basic", kProxyUsername, kProxyPassword); |
| } |
| |
| // Verifies that if the policy provided proxy credentials are not correct in a |
| // MGS, then the user is prompted for credentials. |
| IN_PROC_BROWSER_TEST_F(SystemProxyCredentialsReuseBrowserTest, |
| BadPolicyCredentials) { |
| SetManagedProxy(); |
| LoginState::Get()->SetLoggedInState( |
| LoginState::LOGGED_IN_ACTIVE, LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT); |
| SetPolicyCredentials(kBadUsername, kBadPassword); |
| LoginWithDialog(kProxyUsername16, kProxyPassword16); |
| CheckEntryInHttpAuthCache("Basic", kProxyUsername, kProxyPassword); |
| } |
| |
| // Verifies that the policy provided proxy credentials are only used for |
| // authentication schemes allowed by the SystemProxySettings policy. |
| IN_PROC_BROWSER_TEST_F(SystemProxyCredentialsReuseBrowserTest, |
| RestrictedPolicyCredentials) { |
| SetManagedProxy(); |
| LoginState::Get()->SetLoggedInState( |
| LoginState::LOGGED_IN_ACTIVE, LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT); |
| SetPolicyCredentials(kProxyUsername, kProxyPassword, R"("ntlm","digest")"); |
| LoginWithDialog(kProxyUsername16, kProxyPassword16); |
| CheckEntryInHttpAuthCache("Basic", kProxyUsername, kProxyPassword); |
| } |
| |
| } // namespace ash |