chromium/extensions/browser/api/networking_private/networking_private_chromeos_unittest.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include <string>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/json/json_string_value_serializer.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chromeos/ash/components/dbus/shill/shill_device_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/login/login_state/login_state.h"
#include "chromeos/ash/components/network/managed_network_configuration_handler.h"
#include "chromeos/ash/components/network/network_configuration_handler.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "components/account_id/account_id.h"
#include "components/onc/onc_constants.h"
#include "components/user_manager/fake_user_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "extensions/browser/api/networking_private/networking_private_api.h"
#include "extensions/browser/api_unittest.h"
#include "extensions/common/mojom/context_type.mojom.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace extensions {

namespace {

const char kUserHash[] = "test_user_hash";
const char kUserProfilePath[] = "/network_profile/user/shill";

const char kWifiDevicePath[] = "/device/stub_wifi_device";
const char kCellularDevicePath[] = "/device/stub_cellular_device";

const char kSharedWifiServicePath[] = "/service/shared_wifi";
const char kSharedWifiGuid[] = "shared_wifi_guid";
const char kSharedWifiName[] = "shared_wifi";

const char kPrivateWifiServicePath[] = "/service/private_wifi";
const char kPrivateWifiGuid[] = "private_wifi_guid";
const char kPrivateWifiName[] = "private_wifi";

const char kManagedUserWifiGuid[] = "managed_user_wifi_guid";
const char kManagedUserWifiSsid[] = "managed_user_wifi";

const char kManagedDeviceWifiGuid[] = "managed_device_wifi_guid";
const char kManagedDeviceWifiSsid[] = "managed_device_wifi";

const char kCellularServicePath[] = "/service/cellular";
const char kCellularGuid[] = "cellular_guid";
const char kCellularName[] = "cellular";

}  // namespace

class NetworkingPrivateApiTest : public ApiUnitTest {
 public:
  NetworkingPrivateApiTest() = default;
  ~NetworkingPrivateApiTest() override = default;

  NetworkingPrivateApiTest(const NetworkingPrivateApiTest&) = delete;
  NetworkingPrivateApiTest& operator=(const NetworkingPrivateApiTest&) = delete;

  void SetUp() override {
    ApiUnitTest::SetUp();

    // TODO(b/278643115) Remove LoginState dependency.
    ash::LoginState::Initialize();

    const AccountId account_id = AccountId::FromUserEmail("test@test");
    auto fake_user_manager = std::make_unique<user_manager::FakeUserManager>();
    fake_user_manager->AddUser(account_id);
    fake_user_manager->UserLoggedIn(account_id, kUserHash,
                                    /*browser_restart=*/false,
                                    /*is_child=*/false);
    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
        std::move(fake_user_manager));

    ash::LoginState::Get()->SetLoggedInState(
        ash::LoginState::LOGGED_IN_ACTIVE,
        ash::LoginState::LOGGED_IN_USER_KIOSK);
    base::RunLoop().RunUntilIdle();

    device_test()->ClearDevices();
    service_test()->ClearServices();

    SetUpNetworks();
    SetUpNetworkPolicy();

    base::RunLoop().RunUntilIdle();
  }

  void TearDown() override {
    scoped_user_manager_.reset();

    ash::LoginState::Shutdown();

    ApiUnitTest::TearDown();
  }

  void SetUpNetworks() {
    profile_test()->AddProfile(kUserProfilePath, kUserHash);
    profile_test()->AddProfile(ash::ShillProfileClient::GetSharedProfilePath(),
                               "");

    device_test()->AddDevice(kWifiDevicePath, shill::kTypeWifi, "wifi_device1");

    service_test()->AddService(kSharedWifiServicePath, kSharedWifiGuid,
                               kSharedWifiName, shill::kTypeWifi,
                               shill::kStateOnline, true /* visible */);
    service_test()->SetServiceProperty(kSharedWifiServicePath,
                                       shill::kDeviceProperty,
                                       base::Value(kWifiDevicePath));
    service_test()->SetServiceProperty(kSharedWifiServicePath,
                                       shill::kSecurityClassProperty,
                                       base::Value(shill::kSecurityClassPsk));
    service_test()->SetServiceProperty(
        kSharedWifiServicePath, shill::kPriorityProperty, base::Value(2));
    service_test()->SetServiceProperty(
        kSharedWifiServicePath, shill::kProfileProperty,
        base::Value(ash::ShillProfileClient::GetSharedProfilePath()));
    profile_test()->AddService(ash::ShillProfileClient::GetSharedProfilePath(),
                               kSharedWifiServicePath);

    service_test()->AddService(kPrivateWifiServicePath, kPrivateWifiGuid,
                               kPrivateWifiName, shill::kTypeWifi,
                               shill::kStateOnline, true /* visible */);
    service_test()->SetServiceProperty(kPrivateWifiServicePath,
                                       shill::kDeviceProperty,
                                       base::Value(kWifiDevicePath));
    service_test()->SetServiceProperty(kPrivateWifiServicePath,
                                       shill::kSecurityClassProperty,
                                       base::Value(shill::kSecurityClassPsk));
    service_test()->SetServiceProperty(
        kPrivateWifiServicePath, shill::kPriorityProperty, base::Value(2));

    service_test()->SetServiceProperty(kPrivateWifiServicePath,
                                       shill::kProfileProperty,
                                       base::Value(kUserProfilePath));
    profile_test()->AddService(kUserProfilePath, kPrivateWifiServicePath);
  }

  void SetUpNetworkPolicy() {
    ash::ManagedNetworkConfigurationHandler* config_handler =
        ash::NetworkHandler::Get()->managed_network_configuration_handler();

    const std::string user_policy_ssid = kManagedUserWifiSsid;
    base::Value::List user_policy_onc = base::Value::List().Append(
        base::Value::Dict()
            .Set("GUID", kManagedUserWifiGuid)
            .Set("Type", "WiFi")
            .Set("WiFi", base::Value::Dict()
                             .Set("Passphrase", "fake")
                             .Set("SSID", user_policy_ssid)
                             .Set("HexSSID", base::HexEncode(user_policy_ssid))
                             .Set("Security", "WPA-PSK")));

    config_handler->SetPolicy(::onc::ONC_SOURCE_USER_POLICY, kUserHash,
                              user_policy_onc,
                              /*global_network_config=*/base::Value::Dict());

    const std::string device_policy_ssid = kManagedDeviceWifiSsid;
    base::Value::List device_policy_onc = base::Value::List().Append(
        base::Value::Dict()
            .Set("GUID", kManagedDeviceWifiGuid)
            .Set("Type", "WiFi")
            .Set("WiFi",
                 base::Value::Dict()
                     .Set("SSID", device_policy_ssid)
                     .Set("HexSSID", base::HexEncode(device_policy_ssid))
                     .Set("Security", "None")));
    config_handler->SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, "",
                              device_policy_onc,
                              /*global_network_config=*/base::Value::Dict());
  }

  void SetDeviceProperty(const std::string& device_path,
                         const std::string& name,
                         const base::Value& value) {
    device_test()->SetDeviceProperty(device_path, name, value,
                                     /*notify_changed=*/false);
  }

  void SetUpCellular() {
    // Add a Cellular GSM Device.
    device_test()->AddDevice(kCellularDevicePath, shill::kTypeCellular,
                             "stub_cellular_device1");

    base::Value::Dict home_provider;
    home_provider.Set("name", "Cellular1_Provider");
    home_provider.Set("code", "000000");
    home_provider.Set("country", "us");
    SetDeviceProperty(kCellularDevicePath, shill::kHomeProviderProperty,
                      base::Value(std::move(home_provider)));
    SetDeviceProperty(kCellularDevicePath, shill::kTechnologyFamilyProperty,
                      base::Value(shill::kNetworkTechnologyGsm));
    SetDeviceProperty(kCellularDevicePath, shill::kMeidProperty,
                      base::Value("test_meid"));
    SetDeviceProperty(kCellularDevicePath, shill::kImeiProperty,
                      base::Value("test_imei"));
    SetDeviceProperty(kCellularDevicePath, shill::kIccidProperty,
                      base::Value("test_iccid"));
    SetDeviceProperty(kCellularDevicePath, shill::kImsiProperty,
                      base::Value("test_imsi"));
    SetDeviceProperty(kCellularDevicePath, shill::kEsnProperty,
                      base::Value("test_esn"));
    SetDeviceProperty(kCellularDevicePath, shill::kMdnProperty,
                      base::Value("test_mdn"));
    SetDeviceProperty(kCellularDevicePath, shill::kMinProperty,
                      base::Value("test_min"));
    SetDeviceProperty(kCellularDevicePath, shill::kModelIdProperty,
                      base::Value("test_model_id"));
    base::Value apn(base::Value::Dict()
                        .Set(shill::kApnProperty, "test-apn")
                        .Set(shill::kApnUsernameProperty, "test-user")
                        .Set(shill::kApnPasswordProperty, "test-password")
                        .Set(shill::kApnAuthenticationProperty, "chap"));
    base::Value apn_list(base::Value::Type::LIST);
    apn_list.GetList().Append(apn.Clone());
    SetDeviceProperty(kCellularDevicePath, shill::kCellularApnListProperty,
                      apn_list);

    service_test()->AddService(kCellularServicePath, kCellularGuid,
                               kCellularName, shill::kTypeCellular,
                               shill::kStateOnline, true /* visible */);
    service_test()->SetServiceProperty(kCellularServicePath,
                                       shill::kCellularAllowRoamingProperty,
                                       base::Value(false));
    service_test()->SetServiceProperty(
        kCellularServicePath, shill::kAutoConnectProperty, base::Value(true));
    service_test()->SetServiceProperty(
        kCellularServicePath, shill::kIccidProperty, base::Value("test_iccid"));
    service_test()->SetServiceProperty(
        kCellularServicePath, shill::kImsiProperty, base::Value("test_imsi"));
    service_test()->SetServiceProperty(
        kCellularServicePath, shill::kNetworkTechnologyProperty,
        base::Value(shill::kNetworkTechnologyGsm));
    service_test()->SetServiceProperty(kCellularServicePath,
                                       shill::kRoamingStateProperty,
                                       base::Value(shill::kRoamingStateHome));
    service_test()->SetServiceProperty(kCellularServicePath,
                                       shill::kCellularApnProperty, apn);
    service_test()->SetServiceProperty(
        kCellularServicePath, shill::kCellularLastGoodApnProperty, apn);

    profile_test()->AddService(kUserProfilePath, kCellularServicePath);

    base::RunLoop().RunUntilIdle();
  }

  void AddSharedNetworkToUserProfile(const std::string& service_path) {
    service_test()->SetServiceProperty(service_path, shill::kProfileProperty,
                                       base::Value(kUserProfilePath));
    profile_test()->AddService(kUserProfilePath, service_path);

    base::RunLoop().RunUntilIdle();
  }

  int GetNetworkPriority(const ash::NetworkState* network) {
    base::Value::Dict properties;
    network->GetStateProperties(&properties);
    return properties.FindInt(shill::kPriorityProperty).value_or(-1);
  }

  bool HasServiceProfile(const std::string& service_path,
                         std::string* profile_path) {
    return profile_test()->GetService(service_path, profile_path).has_value();
  }

  std::optional<base::Value::Dict> GetNetworkProperties(
      const std::string& service_path) {
    base::RunLoop run_loop;
    std::optional<base::Value::Dict> properties;
    ash::NetworkHandler::Get()
        ->network_configuration_handler()
        ->GetShillProperties(
            service_path,
            base::BindOnce(&NetworkingPrivateApiTest::OnNetworkProperties,
                           base::Unretained(this), service_path,
                           base::Unretained(&properties),
                           run_loop.QuitClosure()));
    run_loop.Run();
    if (!properties) {
      return std::nullopt;
    }
    return std::move(*properties);
  }

  void OnNetworkProperties(const std::string& expected_path,
                           std::optional<base::Value::Dict>* result,
                           base::OnceClosure callback,
                           const std::string& service_path,
                           std::optional<base::Value::Dict> properties) {
    if (!properties) {
      ADD_FAILURE() << "Error calling shill client.";
      std::move(callback).Run();
      return;
    }
    EXPECT_EQ(expected_path, service_path);
    *result = std::move(properties);
    std::move(callback).Run();
  }

  std::optional<base::Value::Dict> GetNetworkUiData(
      std::optional<base::Value::Dict>& properties) {
    const std::string* ui_data_json = properties->FindString("UIData");
    if (!ui_data_json) {
      return std::nullopt;
    }

    JSONStringValueDeserializer deserializer(*ui_data_json);
    auto deserialized = deserializer.Deserialize(nullptr, nullptr);

    if (!deserialized || !deserialized->is_dict()) {
      return std::nullopt;
    }
    return std::move(*deserialized).TakeDict();
  }

  bool GetUserSettingStringData(const std::string& guid,
                                const std::string& key,
                                std::string* value = nullptr) {
    const ash::NetworkState* network = ash::NetworkHandler::Get()
                                           ->network_state_handler()
                                           ->GetNetworkStateFromGuid(guid);

    std::optional<base::Value::Dict> properties =
        GetNetworkProperties(network->path());
    if (!properties.has_value()) {
      return false;
    }

    std::optional<base::Value::Dict> ui_data = GetNetworkUiData(properties);
    if (!ui_data) {
      return false;
    }

    const std::string* user_setting =
        ui_data->FindStringByDottedPath("user_settings." + key);
    if (!user_setting) {
      return false;
    }

    if (value) {
      *value = *user_setting;
    }
    return true;
  }

  ash::ShillServiceClient::TestInterface* service_test() {
    return network_handler_test_helper_.service_test();
  }
  ash::ShillDeviceClient::TestInterface* device_test() {
    return network_handler_test_helper_.device_test();
  }
  ash::ShillProfileClient::TestInterface* profile_test() {
    return network_handler_test_helper_.profile_test();
  }

 private:
  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
  ash::NetworkHandlerTestHelper network_handler_test_helper_;
};

TEST_F(NetworkingPrivateApiTest, SetSharedNetworkProperties) {
  EXPECT_EQ(networking_private::kErrorAccessToSharedConfig,
            RunFunctionAndReturnError(
                new NetworkingPrivateSetPropertiesFunction(),
                base::StringPrintf(
                    R"(["%s", {"WiFi": {"Passphrase": "passphrase"}}])",
                    kSharedWifiGuid)));
}

TEST_F(NetworkingPrivateApiTest, SetPrivateNetworkPropertiesWebUI) {
  scoped_refptr<NetworkingPrivateSetPropertiesFunction> set_properties =
      new NetworkingPrivateSetPropertiesFunction();
  set_properties->set_source_context_type(mojom::ContextType::kWebUi);

  RunFunction(
      set_properties.get(),
      base::StringPrintf(R"(["%s", {"Priority": 0}])", kSharedWifiGuid));
  EXPECT_EQ(ExtensionFunction::SUCCEEDED, *set_properties->response_type());

  const ash::NetworkState* network =
      ash::NetworkHandler::Get()
          ->network_state_handler()
          ->GetNetworkStateFromGuid(kSharedWifiGuid);
  ASSERT_TRUE(network);
  EXPECT_FALSE(network->IsPrivate());
  EXPECT_EQ(0, GetNetworkPriority(network));
}

TEST_F(NetworkingPrivateApiTest, SetPrivateNetworkProperties) {
  scoped_refptr<NetworkingPrivateSetPropertiesFunction> set_properties =
      new NetworkingPrivateSetPropertiesFunction();
  RunFunction(
      set_properties.get(),
      base::StringPrintf(R"(["%s", {"Priority": 0}])", kPrivateWifiGuid));
  EXPECT_EQ(ExtensionFunction::SUCCEEDED, *set_properties->response_type());

  const ash::NetworkState* network =
      ash::NetworkHandler::Get()
          ->network_state_handler()
          ->GetNetworkStateFromGuid(kPrivateWifiGuid);
  ASSERT_TRUE(network);
  EXPECT_TRUE(network->IsPrivate());
  EXPECT_EQ(0, GetNetworkPriority(network));
}

TEST_F(NetworkingPrivateApiTest, SetNetworkRestrictedProperties) {
  const char kProxySettings[] =
      R"({
           "ProxySettings": {
             "Type": "Manual",
             "Manual": {
               "HTTPProxy": {
                 "Host": "111.111.0.0",
                 "Port": 80
               },
               "SecureHTTPProxy": {
                 "Host": "111.111.0.0",
                 "Port": 80
               }
             }
           }
         })";

  EXPECT_EQ("Error.PropertiesNotAllowed: [ProxySettings]",
            RunFunctionAndReturnError(
                new NetworkingPrivateSetPropertiesFunction(),
                base::StringPrintf(R"(["%s", %s])", kPrivateWifiGuid,
                                   kProxySettings)));

  const char kStaticIpConfig[] =
      R"({
           "StaticIPConfig": {
             "Gateway": "111.111.0.0",
             "IPAddress": "123.123.123.1",
             "NameServers": ["8.8.8.8"],
             "Type": "IPv4"
           }
         })";
  EXPECT_EQ("Error.PropertiesNotAllowed: [StaticIPConfig]",
            RunFunctionAndReturnError(
                new NetworkingPrivateSetPropertiesFunction(),
                base::StringPrintf(R"(["%s", %s])", kPrivateWifiGuid,
                                   kStaticIpConfig)));

  const char kCombinedSettings[] =
      R"({
           "ProxySettings": {
             "Type": "Manual",
             "Manual": {
               "SecureHTTPProxy": {
                 "Host": "111.111.0.0",
                 "Port": 80
               }
             }
           },
           "StaticIPConfig": {
             "Gateway": "111.111.0.0",
             "IPAddress": "123.123.123.1",
             "NameServers": ["8.8.8.8"],
             "Type": "IPv4"
           }
         })";
  // Note: The order of properties listed in the error is not really important.
  // If the API implementation changes, the expected order can be changed, too.
  EXPECT_EQ("Error.PropertiesNotAllowed: [ProxySettings, StaticIPConfig]",
            RunFunctionAndReturnError(
                new NetworkingPrivateSetPropertiesFunction(),
                base::StringPrintf(R"(["%s", %s])", kPrivateWifiGuid,
                                   kCombinedSettings)));

  EXPECT_FALSE(
      GetUserSettingStringData(kPrivateWifiGuid, "ProxySettings.Type"));
  EXPECT_FALSE(
      GetUserSettingStringData(kPrivateWifiGuid, "StaticIPConfig.Type"));
}

TEST_F(NetworkingPrivateApiTest, SetNetworkRestrictedPropertiesFromWebUI) {
  scoped_refptr<NetworkingPrivateSetPropertiesFunction> set_properties =
      new NetworkingPrivateSetPropertiesFunction();
  set_properties->set_source_context_type(mojom::ContextType::kWebUi);
  set_properties->set_source_url(GURL("chrome://os-settings/networkDetail"));

  const char kCombinedSettings[] =
      R"({
           "ProxySettings": {
             "Type": "Manual",
             "Manual": {
               "SecureHTTPProxy": {
                 "Host": "111.111.0.0",
                 "Port": 80
               }
             }
           },
           "StaticIPConfig": {
             "Gateway": "111.111.0.0",
             "IPAddress": "123.123.123.1",
             "NameServers": ["8.8.8.8"],
             "Type": "IPv4"
           }
         })";
  RunFunction(
      set_properties.get(),
      base::StringPrintf(R"(["%s", %s])", kPrivateWifiGuid, kCombinedSettings));
  EXPECT_EQ(ExtensionFunction::SUCCEEDED, *set_properties->response_type());

  EXPECT_TRUE(GetUserSettingStringData(kPrivateWifiGuid, "ProxySettings.Type"));
  EXPECT_TRUE(
      GetUserSettingStringData(kPrivateWifiGuid, "StaticIPConfig.Type"));
}

TEST_F(NetworkingPrivateApiTest, CreateSharedNetwork) {
  const char kNetworkConfig[] =
      R"({
           "Type": "WiFi",
           "WiFi": {
             "SSID": "New network",
             "Security": "None"
           }
         })";
  EXPECT_EQ(networking_private::kErrorAccessToSharedConfig,
            RunFunctionAndReturnError(
                new NetworkingPrivateCreateNetworkFunction(),
                base::StringPrintf("[true, %s]", kNetworkConfig)));
}

TEST_F(NetworkingPrivateApiTest, CreateSharedNetworkWebUI) {
  scoped_refptr<NetworkingPrivateCreateNetworkFunction> create_network =
      new NetworkingPrivateCreateNetworkFunction();
  create_network->set_source_context_type(mojom::ContextType::kWebUi);

  const char kNetworkConfig[] =
      R"({
           "Type": "WiFi",
           "Priority": 1,
           "WiFi": {
             "SSID": "New network",
             "Security": "None"
           }
         })";
  std::optional<base::Value> result = RunFunctionAndReturnValue(
      create_network.get(), base::StringPrintf("[true, %s]", kNetworkConfig));

  ASSERT_TRUE(result);
  ASSERT_TRUE(result->is_string());

  std::string guid = result->GetString();
  const ash::NetworkState* network = ash::NetworkHandler::Get()
                                         ->network_state_handler()
                                         ->GetNetworkStateFromGuid(guid);
  ASSERT_TRUE(network);
  EXPECT_FALSE(network->IsPrivate());
  ASSERT_EQ(1, GetNetworkPriority(network));
}

TEST_F(NetworkingPrivateApiTest, CreatePrivateNetwork) {
  const char kNetworkConfig[] =
      R"({
           "Type": "WiFi",
           "Priority": 1,
           "WiFi": {
             "SSID": "New WiFi",
             "Security": "WPA-PSK"
           }
         })";
  std::optional<base::Value> result = RunFunctionAndReturnValue(
      new NetworkingPrivateCreateNetworkFunction(),
      base::StringPrintf("[false, %s]", kNetworkConfig));

  ASSERT_TRUE(result);
  ASSERT_TRUE(result->is_string());

  // Test the created config can be changed now.
  std::string guid = result->GetString();
  const ash::NetworkState* network = ash::NetworkHandler::Get()
                                         ->network_state_handler()
                                         ->GetNetworkStateFromGuid(guid);
  ASSERT_TRUE(network);
  EXPECT_TRUE(network->IsPrivate());
  EXPECT_EQ(1, GetNetworkPriority(network));

  scoped_refptr<NetworkingPrivateSetPropertiesFunction> set_properties =
      new NetworkingPrivateSetPropertiesFunction();

  RunFunction(set_properties.get(),
              base::StringPrintf(R"(["%s", {"Priority": 2}])", guid.c_str()));
  EXPECT_EQ(ExtensionFunction::SUCCEEDED, *set_properties->response_type());

  EXPECT_EQ(2, GetNetworkPriority(network));
}

TEST_F(NetworkingPrivateApiTest, CreateVpn) {
  const char kL2tpIpsecConfig[] =
      R"({
           "Type": "VPN",
           "VPN": {
             "Type": "L2TP-IPsec",
             "AutoConnect": true,
             "Host": "100.100.0.0",
             "IPsec": {
               "AuthenticationType": "PSK",
               "Group": "group",
               "IKEVersion": 1,
               "PSK": "foobar"
             },
             "L2TP": {
               "Username": "user",
               "Password": "fake_password"
              }
            }
         })";
  EXPECT_EQ("Error.PropertiesNotAllowed: [VPN.Host, VPN.IPsec, VPN.L2TP]",
            RunFunctionAndReturnError(
                new NetworkingPrivateCreateNetworkFunction(),
                base::StringPrintf("[false, %s]", kL2tpIpsecConfig)));

  const char kOpenVpnConfig[] =
      R"({
           "Type": "VPN",
           "VPN": {
             "Type": "OpenVPN",
             "AutoConnect": true,
             "Host": "100.100.0.0",
             "OpenVPN": {
               "ClientCertType": "None",
               "UserAuthenticationType": "PasswordAndOTP",
               "Username": "user",
               "Password": "fake_password",
               "OTP": "fake_otp"
             }
            }
          })";
  EXPECT_EQ("Error.PropertiesNotAllowed: [VPN.Host, VPN.OpenVPN]",
            RunFunctionAndReturnError(
                new NetworkingPrivateCreateNetworkFunction(),
                base::StringPrintf("[false, %s]", kOpenVpnConfig)));

  const char kThirdPartyVpnConfig[] =
      R"({
           "Type": "VPN",
           "VPN": {
             "Type": "ThirdPartyVPN",
             "AutoConnect": true,
             "Host": "100.100.0.0",
             "ThirdPartyVPN": {
               "ExtensionID": "fake_extension_id",
               "ProviderName": "some VPN provider"
             }
           }
         })";
  EXPECT_EQ("Error.PropertiesNotAllowed: [VPN.Host, VPN.ThirdPartyVPN]",
            RunFunctionAndReturnError(
                new NetworkingPrivateCreateNetworkFunction(),
                base::StringPrintf("[false, %s]", kThirdPartyVpnConfig)));
}

TEST_F(NetworkingPrivateApiTest, CreateL2TPVpnFromWebUi) {
  const char kL2tpIpsecConfig[] =
      R"({
           "Type": "VPN",
           "VPN": {
             "Type": "L2TP-IPsec",
             "AutoConnect": true,
             "Host": "100.100.0.0",
             "IPsec": {
               "AuthenticationType": "PSK",
               "Group": "group",
               "IKEVersion": 1,
               "PSK": "foobar"
             },
             "L2TP": {}
           }
         })";

  scoped_refptr<NetworkingPrivateCreateNetworkFunction> create_network =
      new NetworkingPrivateCreateNetworkFunction();
  create_network->set_source_context_type(mojom::ContextType::kWebUi);
  create_network->set_source_url(GURL("chrome://os-settings/networkDetail"));
  std::optional<base::Value> result = RunFunctionAndReturnValue(
      create_network.get(),
      base::StringPrintf("[false, %s]", kL2tpIpsecConfig));

  ASSERT_TRUE(result);
  ASSERT_TRUE(result->is_string());

  std::string guid = result->GetString();
  const char kL2tpCredentials[] =
      R"({
           "VPN": {
             "L2TP": {
               "Username": "user",
               "Password": "fake_password"
             }
           }
         })";

  // Setting password should fail from non-webui context.
  EXPECT_EQ(
      "Error.PropertiesNotAllowed: [VPN.L2TP]",
      RunFunctionAndReturnError(
          new NetworkingPrivateSetPropertiesFunction(),
          base::StringPrintf(R"(["%s", %s])", guid.c_str(), kL2tpCredentials)));

  EXPECT_FALSE(GetUserSettingStringData(guid, "VPN.L2TP.Username"));

  // VPN properties should be settable from Web UI.
  scoped_refptr<NetworkingPrivateSetPropertiesFunction> set_properties =
      new NetworkingPrivateSetPropertiesFunction();
  set_properties->set_source_context_type(mojom::ContextType::kWebUi);
  set_properties->set_source_url(GURL("chrome://os-settings/networkDetail"));
  result = RunFunctionAndReturnValue(
      set_properties.get(),
      base::StringPrintf(R"(["%s", %s])", guid.c_str(), kL2tpCredentials));

  std::string username;
  EXPECT_TRUE(GetUserSettingStringData(guid, "VPN.L2TP.Username", &username));
  EXPECT_EQ("user", username);
}

TEST_F(NetworkingPrivateApiTest, CreateOpenVpnFromWebUiAndSetProperties) {
  const char kOpenVpnConfig[] =
      R"({
           "Type": "VPN",
           "VPN": {
             "Type": "OpenVPN",
             "AutoConnect": true,
             "Host": "100.100.0.0",
             "OpenVPN": {
               "ClientCertType": "None",
               "UserAuthenticationType": "PasswordAndOTP"
             }
           }
         })";
  scoped_refptr<NetworkingPrivateCreateNetworkFunction> create_network =
      new NetworkingPrivateCreateNetworkFunction();
  create_network->set_source_context_type(mojom::ContextType::kWebUi);
  create_network->set_source_url(GURL("chrome://os-settings/networkDetail"));
  std::optional<base::Value> result = RunFunctionAndReturnValue(
      create_network.get(), base::StringPrintf("[false, %s]", kOpenVpnConfig));

  ASSERT_TRUE(result);
  ASSERT_TRUE(result->is_string());

  std::string guid = result->GetString();
  const char kOpenVpnCredentials[] =
      R"({
           "VPN": {
             "OpenVPN": {
               "Username": "user",
               "Password": "fake_password",
               "OTP": "fake_otp"
             }
           }
         })";

  // Setting OpenVPN properties should fail from non-webui context.
  EXPECT_EQ("Error.PropertiesNotAllowed: [VPN.OpenVPN]",
            RunFunctionAndReturnError(
                new NetworkingPrivateSetPropertiesFunction(),
                base::StringPrintf(R"(["%s", %s])", guid.c_str(),
                                   kOpenVpnCredentials)));

  EXPECT_FALSE(GetUserSettingStringData(guid, "VPN.OpenVPN.Username"));

  // VPN properties should be settable from Web UI.
  scoped_refptr<NetworkingPrivateSetPropertiesFunction> set_properties =
      new NetworkingPrivateSetPropertiesFunction();
  set_properties->set_source_context_type(mojom::ContextType::kWebUi);
  set_properties->set_source_url(GURL("chrome://os-settings/networkDetail"));
  result = RunFunctionAndReturnValue(
      set_properties.get(),
      base::StringPrintf(R"(["%s", %s])", guid.c_str(), kOpenVpnCredentials));

  std::string username;
  EXPECT_TRUE(
      GetUserSettingStringData(guid, "VPN.OpenVPN.Username", &username));
  EXPECT_EQ("user", username);
}

TEST_F(NetworkingPrivateApiTest, CreateNetworkWithRestrictedProperties) {
  const char kConfigWithProxySettings[] =
      R"({
           "Type": "WiFi",
           "WiFi": {
             "SSID": "new wifi",
             "Security": "None"
           },
           "ProxySettings": {
             "Type": "Manual",
             "Manual": {
               "HTTPProxy": {
                 "Host": "111.111.0.0",
                 "Port": 80
               },
               "SecureHTTPProxy": {
                 "Host": "111.111.0.0",
                 "Port": 80
               }
             }
           }
         })";
  EXPECT_EQ("Error.PropertiesNotAllowed: [ProxySettings]",
            RunFunctionAndReturnError(
                new NetworkingPrivateCreateNetworkFunction(),
                base::StringPrintf("[false, %s]", kConfigWithProxySettings)));

  const char kConfigWithStaticIpConfig[] =
      R"({
           "Type": "WiFi",
           "WiFi": {
             "SSID": "new wifi",
             "Security": "None"
           },
           "StaticIPConfig": {
             "Gateway": "111.111.0.0",
             "IPAddress": "123.123.123.1",
             "NameServers": ["8.8.8.8", "8.8.8.8.9"],
             "Type": "IPv4"
           }
         })";
  EXPECT_EQ("Error.PropertiesNotAllowed: [StaticIPConfig]",
            RunFunctionAndReturnError(
                new NetworkingPrivateCreateNetworkFunction(),
                base::StringPrintf("[false, %s]", kConfigWithStaticIpConfig)));

  const char kCombinedConfig[] =
      R"({
           "Type": "WiFi",
           "WiFi": {
             "SSID": "new wifi",
             "Security": "None"
           },
           "ProxySettings": {
             "Type": "Manual",
             "Manual": {
               "SecureHTTPProxy": {
                 "Host": "111.111.0.0",
                 "Port": 80
               }
             }
           },
           "StaticIPConfig": {
             "Gateway": "111.111.0.0",
             "IPAddress": "123.123.123.1",
             "NameServers": ["8.8.8.8", "8.8.8.8.9"],
             "Type": "IPv4"
           }
         })";
  // Note: The order of properties listed in the error is not really important.
  // If the API implementation changes, the expected order can be changed, too.
  EXPECT_EQ("Error.PropertiesNotAllowed: [ProxySettings, StaticIPConfig]",
            RunFunctionAndReturnError(
                new NetworkingPrivateCreateNetworkFunction(),
                base::StringPrintf("[false, %s]", kCombinedConfig)));
}

TEST_F(NetworkingPrivateApiTest,
       CreateNetworkWithRestrictedPropertiesFromWebUi) {
  const char kNetworkConfig[] =
      R"({
           "Type": "WiFi",
           "WiFi": {
             "Security": "WPA-PSK",
             "Passphrase": "Fake passphrase",
             "SSID": "New network config"
           },
           "ProxySettings": {
             "Type": "Manual",
             "Manual": {
               "SecureHTTPProxy": {
                 "Host": "111.111.0.0",
                 "Port": 80
               }
             }
           },
           "StaticIPConfig": {
             "Gateway": "111.111.0.0",
             "IPAddress": "123.123.123.1",
             "NameServers": ["8.8.8.8", "8.8.8.8.9"],
             "Type": "IPv4"
           }
         })";

  scoped_refptr<NetworkingPrivateCreateNetworkFunction> create_network =
      new NetworkingPrivateCreateNetworkFunction();
  create_network->set_source_context_type(mojom::ContextType::kWebUi);
  create_network->set_source_url(GURL("chrome://os-settings/networkDetail"));
  std::optional<base::Value> result = RunFunctionAndReturnValue(
      create_network.get(), base::StringPrintf("[false, %s]", kNetworkConfig));
  ASSERT_TRUE(result);
  ASSERT_TRUE(result->is_string());

  std::string guid = result->GetString();
  const ash::NetworkState* network = ash::NetworkHandler::Get()
                                         ->network_state_handler()
                                         ->GetNetworkStateFromGuid(guid);

  std::optional<base::Value::Dict> properties =
      GetNetworkProperties(network->path());
  ASSERT_TRUE(properties.has_value());

  std::optional<base::Value::Dict> ui_data = GetNetworkUiData(properties);
  ASSERT_TRUE(ui_data.has_value());

  EXPECT_TRUE(ui_data->FindByDottedPath("user_settings.ProxySettings"));
  EXPECT_TRUE(ui_data->FindByDottedPath("user_settings.StaticIPConfig"));
}

TEST_F(NetworkingPrivateApiTest, CreatePrivateNetwork_NonMatchingSsids) {
  const std::string ssid = "new_wifi_config";
  const std::string hex_ssid = base::HexEncode(ssid);
  const char kNetworkConfig[] =
      R"({
           "Priority": 1,
           "Type": "WiFi",
           "WiFi": {
             "SSID": "New WiFi",
             "HexSSID": "%s",
             "Security": "WPA-PSK"
           }
         })";

  std::optional<base::Value> result = RunFunctionAndReturnValue(
      new NetworkingPrivateCreateNetworkFunction(),
      base::StringPrintf(
          "[false, %s]",
          base::StringPrintf(kNetworkConfig, hex_ssid.c_str()).c_str()));

  ASSERT_TRUE(result);
  ASSERT_TRUE(result->is_string());

  // Test the created config can be changed now.
  const std::string guid = result->GetString();
  const ash::NetworkState* network = ash::NetworkHandler::Get()
                                         ->network_state_handler()
                                         ->GetNetworkStateFromGuid(guid);
  ASSERT_TRUE(network);
  EXPECT_TRUE(network->IsPrivate());
  EXPECT_EQ(1, GetNetworkPriority(network));
  EXPECT_EQ(hex_ssid, network->GetHexSsid());
  EXPECT_EQ(ssid, network->name());
}

TEST_F(NetworkingPrivateApiTest,
       CreateAlreadyConfiguredUserPrivateNetwork_BySsid) {
  const char kNetworkConfig[] =
      R"({
           "Type": "WiFi",
           "WiFi": {
             "SSID": "%s",
             "Security": "WPA-PSK"
           }
         })";
  EXPECT_EQ("NetworkAlreadyConfigured",
            RunFunctionAndReturnError(
                new NetworkingPrivateCreateNetworkFunction(),
                base::StringPrintf(
                    "[false, %s]",
                    base::StringPrintf(kNetworkConfig, kManagedUserWifiSsid)
                        .c_str())));
}

TEST_F(NetworkingPrivateApiTest,
       CreateAlreadyConfiguredUserPrivateNetwork_ByHexSsid) {
  std::string hex_ssid =
      base::HexEncode(kManagedUserWifiSsid, sizeof(kManagedUserWifiSsid) - 1);
  const char kNetworkConfig[] =
      R"({
           "Priority": 1,
           "Type": "WiFi",
           "WiFi": {
             "HexSSID": "%s",
             "Security": "WPA-PSK"
           }
         })";
  EXPECT_EQ(
      "NetworkAlreadyConfigured",
      RunFunctionAndReturnError(
          new NetworkingPrivateCreateNetworkFunction(),
          base::StringPrintf(
              "[false, %s]",
              base::StringPrintf(kNetworkConfig, hex_ssid.c_str()).c_str())));
}

TEST_F(NetworkingPrivateApiTest,
       CreateAlreadyConfiguredUserPrivateNetwork_NonMatchingSsids) {
  std::string hex_ssid =
      base::HexEncode(kManagedUserWifiSsid, sizeof(kManagedUserWifiSsid) - 1);
  const char kNetworkConfig[] =
      R"({
           "Priority": 1,
           "Type": "WiFi",
           "WiFi": {
             "SSID": "different ssid",
             "HexSSID": "%s",
             "Security": "WPA-PSK"
           }
         })";
  // HexSSID should take presedence over SSID when mathing existing networks.
  EXPECT_EQ(
      "NetworkAlreadyConfigured",
      RunFunctionAndReturnError(
          new NetworkingPrivateCreateNetworkFunction(),
          base::StringPrintf(
              "[false, %s]",
              base::StringPrintf(kNetworkConfig, hex_ssid.c_str()).c_str())));
}

TEST_F(NetworkingPrivateApiTest,
       CreateAlreadyConfiguredUserPrivateNetwork_ByHexSSID) {
  std::string hex_ssid =
      base::HexEncode(kManagedUserWifiSsid, sizeof(kManagedUserWifiSsid) - 1);
  const char kNetworkConfig[] =
      R"({
           "Priority": 1,
           "Type": "WiFi",
           "WiFi": {
             "HexSSID": "%s",
             "Security": "WPA-PSK"
           }
         })";
  EXPECT_EQ(
      "NetworkAlreadyConfigured",
      RunFunctionAndReturnError(
          new NetworkingPrivateCreateNetworkFunction(),
          base::StringPrintf(
              "[false, %s]",
              base::StringPrintf(kNetworkConfig, hex_ssid.c_str()).c_str())));
}

TEST_F(NetworkingPrivateApiTest, CreateAlreadyConfiguredDeviceNetwork) {
  const char kNetworkConfig[] =
      R"({
           "Type": "WiFi",
           "WiFi": {
             "SSID": "%s"
           }
         })";
  EXPECT_EQ("NetworkAlreadyConfigured",
            RunFunctionAndReturnError(
                new NetworkingPrivateCreateNetworkFunction(),
                base::StringPrintf(
                    "[false, %s]",
                    base::StringPrintf(kNetworkConfig, kManagedDeviceWifiSsid)
                        .c_str())));
}

TEST_F(NetworkingPrivateApiTest,
       CreateAlreadyConfiguredDeviceNetwork_ByHexSSID) {
  std::string hex_ssid = base::HexEncode(kManagedDeviceWifiSsid,
                                         sizeof(kManagedDeviceWifiSsid) - 1);
  const char kNetworkConfig[] =
      R"({
           "Type": "WiFi",
           "WiFi": {
             "HexSSID": "%s",
             "Security": "WPA-PSK"
           }
         })";
  EXPECT_EQ(
      "NetworkAlreadyConfigured",
      RunFunctionAndReturnError(
          new NetworkingPrivateCreateNetworkFunction(),
          base::StringPrintf(
              "[false, %s]",
              base::StringPrintf(kNetworkConfig, hex_ssid.c_str()).c_str())));
}

TEST_F(NetworkingPrivateApiTest, GetCellularProperties) {
  SetUpCellular();

  std::optional<base::Value> result =
      RunFunctionAndReturnValue(new NetworkingPrivateGetPropertiesFunction(),
                                base::StringPrintf(R"(["%s"])", kCellularGuid));

  ASSERT_TRUE(result);

  base::Value::Dict expected_result =
      base::Value::Dict()
          .Set("Cellular",
               base::Value::Dict()
                   .Set("AllowRoaming", false)
                   .Set("AutoConnect", true)
                   .Set("Family", "GSM")
                   .Set("HomeProvider", base::Value::Dict()
                                            .Set("Code", "000000")
                                            .Set("Country", "us")
                                            .Set("Name", "Cellular1_Provider"))
                   .Set("ModelID", "test_model_id")
                   .Set("NetworkTechnology", "GSM")
                   .Set("RoamingState", "Home")
                   .Set("Scanning", false))
          .Set("ConnectionState", "Connected")
          .Set("GUID", "cellular_guid")
          .Set("IPAddressConfigType", "DHCP")
          .Set("Metered", true)
          .Set("Name", "cellular")
          .Set("NameServersConfigType", "DHCP")
          .Set("Source", "User")
          .Set("TrafficCounterResetTime", 0.0)
          .Set("Type", "Cellular");

  EXPECT_EQ(base::Value(std::move(expected_result)), *result);
}

TEST_F(NetworkingPrivateApiTest, GetCellularPropertiesFromWebUi) {
  SetUpCellular();

  scoped_refptr<NetworkingPrivateGetPropertiesFunction> get_properties =
      new NetworkingPrivateGetPropertiesFunction();
  get_properties->set_source_context_type(mojom::ContextType::kWebUi);
  get_properties->set_source_url(GURL("chrome://os-settings/networkDetail"));

  std::optional<base::Value> result = RunFunctionAndReturnValue(
      get_properties.get(), base::StringPrintf(R"(["%s"])", kCellularGuid));

  ASSERT_TRUE(result);

  base::Value::Dict expected_apn = base::Value::Dict()
                                       .Set("AccessPointName", "test-apn")
                                       .Set("Username", "test-user")
                                       .Set("Password", "test-password")
                                       .Set("ApnTypes", base::Value::List())
                                       .Set("Authentication", "CHAP");
  base::Value::Dict expected_result =
      base::Value::Dict()
          .Set("Cellular",
               base::Value::Dict()
                   .Set("AllowRoaming", false)
                   .Set("AutoConnect", true)
                   .Set("ESN", "test_esn")
                   .Set("Family", "GSM")
                   .Set("HomeProvider", base::Value::Dict()
                                            .Set("Code", "000000")
                                            .Set("Country", "us")
                                            .Set("Name", "Cellular1_Provider"))
                   .Set("ModelID", "test_model_id")
                   .Set("ICCID", "test_iccid")
                   .Set("IMEI", "test_imei")
                   .Set("IMSI", "test_imsi")
                   .Set("MDN", "test_mdn")
                   .Set("MEID", "test_meid")
                   .Set("MIN", "test_min")
                   .Set("NetworkTechnology", "GSM")
                   .Set("RoamingState", "Home")
                   .Set("Scanning", false)
                   .Set("APNList",
                        base::Value::List().Append(expected_apn.Clone()))
                   .Set("APN", expected_apn.Clone())
                   .Set("LastGoodAPN", expected_apn.Clone()))
          .Set("ConnectionState", "Connected")
          .Set("GUID", "cellular_guid")
          .Set("IPAddressConfigType", "DHCP")
          .Set("Metered", true)
          .Set("Name", "cellular")
          .Set("NameServersConfigType", "DHCP")
          .Set("Source", "User")
          .Set("TrafficCounterResetTime", 0.0)
          .Set("Type", "Cellular");

  EXPECT_EQ(base::Value(std::move(expected_result)), *result);
}

TEST_F(NetworkingPrivateApiTest, ForgetSharedNetwork) {
  EXPECT_EQ(networking_private::kErrorAccessToSharedConfig,
            RunFunctionAndReturnError(
                new NetworkingPrivateForgetNetworkFunction(),
                base::StringPrintf(R"(["%s"])", kSharedWifiGuid)));

  base::RunLoop().RunUntilIdle();
  std::string profile_path;
  EXPECT_TRUE(HasServiceProfile(kSharedWifiServicePath, &profile_path));
  EXPECT_EQ(ash::ShillProfileClient::GetSharedProfilePath(), profile_path);
}

TEST_F(NetworkingPrivateApiTest, ForgetPrivateNetwork) {
  RunFunction(new NetworkingPrivateForgetNetworkFunction(),
              base::StringPrintf(R"(["%s"])", kPrivateWifiGuid));

  std::string profile_path;
  EXPECT_FALSE(HasServiceProfile(kPrivateWifiServicePath, &profile_path));
}

TEST_F(NetworkingPrivateApiTest, ForgetPrivateNetworkWebUI) {
  scoped_refptr<NetworkingPrivateForgetNetworkFunction> forget_network =
      new NetworkingPrivateForgetNetworkFunction();
  forget_network->set_source_context_type(mojom::ContextType::kWebUi);

  RunFunction(forget_network.get(),
              base::StringPrintf(R"(["%s"])", kPrivateWifiGuid));

  std::string profile_path;
  EXPECT_FALSE(HasServiceProfile(kPrivateWifiServicePath, &profile_path));
}

TEST_F(NetworkingPrivateApiTest, ForgetUserPolicyNetwork) {
  EXPECT_EQ(networking_private::kErrorPolicyControlled,
            RunFunctionAndReturnError(
                new NetworkingPrivateForgetNetworkFunction(),
                base::StringPrintf(R"(["%s"])", kManagedUserWifiGuid)));

  const ash::NetworkState* network =
      ash::NetworkHandler::Get()
          ->network_state_handler()
          ->GetNetworkStateFromGuid(kManagedUserWifiGuid);

  base::RunLoop().RunUntilIdle();
  std::string profile_path;
  EXPECT_TRUE(HasServiceProfile(network->path(), &profile_path));
  EXPECT_EQ(kUserProfilePath, profile_path);
}

TEST_F(NetworkingPrivateApiTest, ForgetUserPolicyNetworkWebUI) {
  scoped_refptr<NetworkingPrivateForgetNetworkFunction> forget_network =
      new NetworkingPrivateForgetNetworkFunction();
  forget_network->set_source_context_type(mojom::ContextType::kWebUi);

  EXPECT_EQ(networking_private::kErrorPolicyControlled,
            RunFunctionAndReturnError(
                forget_network.get(),
                base::StringPrintf(R"(["%s"])", kManagedUserWifiGuid)));

  const ash::NetworkState* network =
      ash::NetworkHandler::Get()
          ->network_state_handler()
          ->GetNetworkStateFromGuid(kManagedUserWifiGuid);

  base::RunLoop().RunUntilIdle();
  std::string profile_path;
  EXPECT_TRUE(HasServiceProfile(network->path(), &profile_path));
  EXPECT_EQ(kUserProfilePath, profile_path);
}

TEST_F(NetworkingPrivateApiTest, ForgetDevicePolicyNetworkWebUI) {
  const ash::NetworkState* network =
      ash::NetworkHandler::Get()
          ->network_state_handler()
          ->GetNetworkStateFromGuid(kManagedDeviceWifiGuid);
  ASSERT_TRUE(network);
  AddSharedNetworkToUserProfile(network->path());

  std::string profile_path;
  EXPECT_TRUE(HasServiceProfile(network->path(), &profile_path));
  ASSERT_EQ(kUserProfilePath, profile_path);

  scoped_refptr<NetworkingPrivateForgetNetworkFunction> forget_network =
      new NetworkingPrivateForgetNetworkFunction();
  forget_network->set_source_context_type(mojom::ContextType::kWebUi);
  RunFunction(forget_network.get(),
              base::StringPrintf(R"(["%s"])", kManagedDeviceWifiGuid));

  EXPECT_TRUE(HasServiceProfile(network->path(), &profile_path));
  EXPECT_EQ(ash::ShillProfileClient::GetSharedProfilePath(), profile_path);
}

// Tests that forgetNetwork in case there is a network config in both user and
// shared profile - only config from the user profile is expected to be removed.
TEST_F(NetworkingPrivateApiTest, ForgetNetworkInMultipleProfiles) {
  AddSharedNetworkToUserProfile(kSharedWifiServicePath);

  std::string profile_path;
  EXPECT_TRUE(HasServiceProfile(kSharedWifiServicePath, &profile_path));
  ASSERT_EQ(kUserProfilePath, profile_path);

  RunFunction(new NetworkingPrivateForgetNetworkFunction(),
              base::StringPrintf(R"(["%s"])", kSharedWifiGuid));

  EXPECT_TRUE(HasServiceProfile(kSharedWifiServicePath, &profile_path));
  EXPECT_EQ(ash::ShillProfileClient::GetSharedProfilePath(), profile_path);
}

TEST_F(NetworkingPrivateApiTest, ForgetNetworkInMultipleProfilesWebUI) {
  AddSharedNetworkToUserProfile(kSharedWifiServicePath);

  std::string profile_path;
  EXPECT_TRUE(HasServiceProfile(kSharedWifiServicePath, &profile_path));
  ASSERT_EQ(kUserProfilePath, profile_path);

  scoped_refptr<NetworkingPrivateForgetNetworkFunction> forget_network =
      new NetworkingPrivateForgetNetworkFunction();
  forget_network->set_source_context_type(mojom::ContextType::kWebUi);

  RunFunction(forget_network.get(),
              base::StringPrintf(R"(["%s"])", kSharedWifiGuid));

  EXPECT_FALSE(HasServiceProfile(kSharedWifiServicePath, &profile_path));
}

}  // namespace extensions