blob: 024505772334f7c97dd5774fe78a65dfb951b510 [file] [log] [blame]
// 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 "chromeos/components/onc/onc_utils.h"
#include <optional>
#include <string>
#include <vector>
#include "base/check.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/json/json_file_value_serializer.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/notreached.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "chromeos/components/onc/onc_signature.h"
#include "chromeos/components/onc/onc_test_utils.h"
#include "chromeos/components/onc/variable_expander.h"
#include "components/onc/onc_constants.h"
#include "onc_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::Pointee;
namespace chromeos::onc {
TEST(ONCDecrypterTest, BrokenEncryptionIterations) {
base::Value::Dict encrypted_onc =
test_utils::ReadTestDictionary("broken-encrypted-iterations.onc");
std::optional<base::Value::Dict> decrypted_onc = Decrypt(encrypted_onc);
EXPECT_FALSE(decrypted_onc.has_value());
}
TEST(ONCDecrypterTest, BrokenEncryptionZeroIterations) {
base::Value::Dict encrypted_onc =
test_utils::ReadTestDictionary("broken-encrypted-zero-iterations.onc");
std::optional<base::Value::Dict> decrypted_onc = Decrypt(encrypted_onc);
EXPECT_FALSE(decrypted_onc.has_value());
}
TEST(ONCDecrypterTest, LoadEncryptedOnc) {
base::Value::Dict encrypted_onc =
test_utils::ReadTestDictionary("encrypted.onc");
base::Value::Dict expected_decrypted_onc =
test_utils::ReadTestDictionary("decrypted.onc");
std::string error;
std::optional<base::Value::Dict> actual_decrypted_onc =
Decrypt(encrypted_onc);
ASSERT_TRUE(actual_decrypted_onc.has_value());
EXPECT_TRUE(test_utils::Equals(&expected_decrypted_onc,
&actual_decrypted_onc.value()));
}
namespace {
const char* kLoginId = "hans";
const char* kLoginEmail = "hans@my.domain.com";
const char* kDeviceSerialNumber = "ABC123DEF456";
const std::vector<std::string> kValidApnTypes = {
::onc::cellular_apn::kIpTypeAutomatic,
::onc::cellular_apn::kIpTypeIpv4,
::onc::cellular_apn::kIpTypeIpv4Ipv6,
::onc::cellular_apn::kIpTypeIpv6,
};
const std::vector<std::string>& kTestAdminApnListAllIds = {"id-1", "id-2",
"id-3"};
const std::vector<std::string>& kTestAdminApnListSubsetIds = {"id-1", "id-3"};
const std::vector<std::string>& kTestNonAdminApnListIds = {"id-x", "id-y"};
base::flat_map<std::string, std::string> GetTestStringSubstitutions() {
base::flat_map<std::string, std::string> substitutions;
substitutions[::onc::substitutes::kLoginID] = kLoginId;
substitutions[::onc::substitutes::kLoginEmail] = kLoginEmail;
substitutions[::onc::substitutes::kDeviceSerialNumber] = kDeviceSerialNumber;
return substitutions;
}
} // namespace
TEST(ONCStringExpansion, OpenVPN) {
base::Value::Dict vpn_onc =
test_utils::ReadTestDictionary("valid_openvpn.onc");
VariableExpander variable_expander(GetTestStringSubstitutions());
ExpandStringsInOncObject(kNetworkConfigurationSignature, variable_expander,
&vpn_onc);
std::string* actual_expanded =
vpn_onc.FindStringByDottedPath("VPN.OpenVPN.Username");
ASSERT_TRUE(actual_expanded);
EXPECT_EQ(*actual_expanded, std::string("abc ") + kLoginEmail + " def");
}
TEST(ONCStringExpansion, WiFiEap) {
base::Value::Dict wifi_onc =
test_utils::ReadTestDictionary("wifi_clientcert_with_cert_pems.onc");
VariableExpander variable_expander(GetTestStringSubstitutions());
ExpandStringsInOncObject(kNetworkConfigurationSignature, variable_expander,
&wifi_onc);
std::string* actual_expanded =
wifi_onc.FindStringByDottedPath("WiFi.EAP.Identity");
ASSERT_TRUE(actual_expanded);
EXPECT_EQ(*actual_expanded,
std::string("abc ") + kLoginId + "@my.domain.com");
}
// Test that placeholders are being expanded in the ClientCertPattern fields.
TEST(ONCStringExpansion, WiFiEapPlaceholdersAreReplaced) {
base::Value::Dict wifi_onc = test_utils::ReadTestDictionary(
"wifi_clientcert_with_cert_placeholders.onc");
VariableExpander variable_expander(GetTestStringSubstitutions());
ExpandStringsInOncObject(kNetworkConfigurationSignature, variable_expander,
&wifi_onc);
base::Value::Dict* expanded_issuer =
wifi_onc.FindDictByDottedPath("WiFi.EAP.ClientCertPattern.Issuer");
ASSERT_TRUE(expanded_issuer);
EXPECT_EQ(*expanded_issuer,
base::Value::Dict()
.Set("CommonName", kDeviceSerialNumber)
.Set("Locality", kDeviceSerialNumber)
.Set("Organization", kDeviceSerialNumber)
.Set("OrganizationalUnit", kDeviceSerialNumber));
base::Value::Dict* expanded_subject =
wifi_onc.FindDictByDottedPath("WiFi.EAP.ClientCertPattern.Subject");
ASSERT_TRUE(expanded_subject);
EXPECT_EQ(*expanded_subject,
base::Value::Dict()
.Set("CommonName", kDeviceSerialNumber)
.Set("Locality", kDeviceSerialNumber)
.Set("Organization", kDeviceSerialNumber)
.Set("OrganizationalUnit", kDeviceSerialNumber));
}
// Test that strings that contain names of placeholders, but don't have the ${}
// brackets around them are treated like normal strings and are not replaced.
TEST(ONCStringExpansion, WiFiEapAlmostPlaceholdersAreNotReplaced) {
base::Value::Dict wifi_onc = test_utils::ReadTestDictionary(
"wifi_clientcert_with_almost_placeholders.onc");
VariableExpander variable_expander(GetTestStringSubstitutions());
ExpandStringsInOncObject(kNetworkConfigurationSignature, variable_expander,
&wifi_onc);
std::string* name = wifi_onc.FindStringByDottedPath("Name");
ASSERT_TRUE(name);
EXPECT_EQ(*name, "DEVICE_SERIAL_NUMBER_placeholder_test");
constexpr char kExpectedValue[] = "DEVICE_SERIAL_NUMBER";
base::Value::Dict* expanded_issuer =
wifi_onc.FindDictByDottedPath("WiFi.EAP.ClientCertPattern.Issuer");
ASSERT_TRUE(expanded_issuer);
EXPECT_EQ(*expanded_issuer, base::Value::Dict()
.Set("CommonName", kExpectedValue)
.Set("Locality", kExpectedValue)
.Set("Organization", kExpectedValue)
.Set("OrganizationalUnit", kExpectedValue));
base::Value::Dict* expanded_subject =
wifi_onc.FindDictByDottedPath("WiFi.EAP.ClientCertPattern.Subject");
ASSERT_TRUE(expanded_subject);
EXPECT_EQ(*expanded_subject, base::Value::Dict()
.Set("CommonName", kExpectedValue)
.Set("Locality", kExpectedValue)
.Set("Organization", kExpectedValue)
.Set("OrganizationalUnit", kExpectedValue));
}
TEST(ONCResolveServerCertRefs, ResolveServerCertRefs) {
base::Value::Dict test_cases = test_utils::ReadTestDictionary(
"network_configs_with_resolved_certs.json");
CertPEMsByGUIDMap certs;
certs["cert_google"] = "pem_google";
certs["cert_webkit"] = "pem_webkit";
for (auto iter : test_cases) {
SCOPED_TRACE("Test case: " + iter.first);
const base::Value::Dict* test_case_dict = iter.second.GetIfDict();
ASSERT_TRUE(test_case_dict);
const base::Value::List* networks_with_cert_refs =
test_case_dict->FindList("WithCertRefs");
ASSERT_TRUE(networks_with_cert_refs);
const base::Value::List* expected_resolved_onc =
test_case_dict->FindList("WithResolvedRefs");
ASSERT_TRUE(expected_resolved_onc);
bool expected_success =
(networks_with_cert_refs->size() == expected_resolved_onc->size());
base::Value::List actual_resolved_onc(networks_with_cert_refs->Clone());
bool success = ResolveServerCertRefsInNetworks(certs, actual_resolved_onc);
EXPECT_EQ(expected_success, success);
EXPECT_EQ(*expected_resolved_onc, actual_resolved_onc);
}
}
TEST(ONCUtils, SetHiddenSSIDField_WithNoValueSet) {
// WiFi configuration that doesn't have HiddenSSID field set.
base::Value::Dict wifi_onc =
test_utils::ReadTestDictionary("wifi_clientcert_with_cert_pems.onc");
base::Value::Dict* wifi_fields = wifi_onc.FindDict("WiFi");
ASSERT_TRUE(wifi_fields);
ASSERT_FALSE(wifi_fields->Find(::onc::wifi::kHiddenSSID));
SetHiddenSSIDField(*wifi_fields);
base::Value* hidden_ssid_field = wifi_fields->Find(::onc::wifi::kHiddenSSID);
ASSERT_TRUE(hidden_ssid_field);
EXPECT_FALSE(hidden_ssid_field->GetBool());
}
TEST(ONCUtils, SetHiddenSSIDField_WithValueSetFalse) {
// WiFi configuration that have HiddenSSID field set to false.
base::Value::Dict wifi_onc = test_utils::ReadTestDictionary(
"translation_of_shill_wifi_with_state.onc");
base::Value::Dict* wifi_fields = wifi_onc.FindDict("WiFi");
ASSERT_TRUE(wifi_fields);
ASSERT_TRUE(wifi_fields->Find(::onc::wifi::kHiddenSSID));
SetHiddenSSIDField(*wifi_fields);
EXPECT_FALSE(wifi_fields->Find(::onc::wifi::kHiddenSSID)->GetBool());
}
TEST(ONCUtils, SetHiddenSSIDField_WithValueSetTrue) {
// WiFi configuration that have HiddenSSID field set to true.
base::Value::Dict wifi_onc =
test_utils::ReadTestDictionary("wifi_with_hidden_ssid.onc");
base::Value::Dict* wifi_fields = wifi_onc.FindDict("WiFi");
ASSERT_TRUE(wifi_fields);
ASSERT_TRUE(wifi_fields->Find(::onc::wifi::kHiddenSSID));
SetHiddenSSIDField(*wifi_fields);
EXPECT_TRUE(wifi_fields->Find(::onc::wifi::kHiddenSSID)->GetBool());
}
TEST(ONCUtils, ParseAndValidateOncForImport_ApnProvided) {
const auto onc_blob = test_utils::ReadTestData("valid_cellular_with_apn.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
base::Value::Dict expected;
expected.Set(::onc::cellular_apn::kAccessPointName, "test-apn");
expected.Set(::onc::cellular_apn::kAuthentication, "");
expected.Set(::onc::cellular_apn::kUsername, "test-username");
expected.Set(::onc::cellular_apn::kPassword, "test-password");
const auto* cellular_apn =
network_configs[0].GetDict().FindByDottedPath("Cellular.APN");
EXPECT_THAT(cellular_apn,
Pointee(base::test::DictionaryHasValues(std::move(expected))));
}
TEST(ONCUtils, ParseAndValidateOncForImport_NoApnProvided) {
const auto onc_blob = test_utils::ReadTestData("valid_cellular_no_apn.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
const auto* cellular_apn =
network_configs[0].GetDict().FindByDottedPath("Cellular.APN");
ASSERT_NE(nullptr, cellular_apn);
ASSERT_NE(nullptr, cellular_apn->GetDict().Find(::onc::kRecommended));
base::Value::List recommended =
base::Value::List()
.Append(::onc::cellular_apn::kAccessPointName)
.Append(::onc::cellular_apn::kAttach)
.Append(::onc::cellular_apn::kAuthentication)
.Append(::onc::cellular_apn::kUsername)
.Append(::onc::cellular_apn::kPassword);
base::Value::Dict expected;
expected.Set(::onc::kRecommended, std::move(recommended));
EXPECT_THAT(cellular_apn,
Pointee(base::test::DictionaryHasValues(std::move(expected))));
}
TEST(ONCUtils, ParseAndValidateOncForImport_APNAccessPointName) {
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
test_utils::TestToplevelApnData apn_data;
// Test APN with only Access Point Name
apn_data.access_point_name = "test-access-point-name";
std::string onc_blob =
test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
// Failure if APN has empty Access Point Name
apn_data.access_point_name = "";
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
// Failure if APN has no AccessPointName field.
apn_data.access_point_name = std::nullopt;
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
}
TEST(ONCUtils, ParseAndValidateOncForImport_APNApnType) {
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
test_utils::TestToplevelApnData apn_data;
// Test that no APN Types field is fine
apn_data.apn_types = std::nullopt;
std::string onc_blob =
test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
// Test valid APN types
apn_data.apn_types = {
::onc::cellular_apn::kApnTypeDefault,
::onc::cellular_apn::kApnTypeAttach,
::onc::cellular_apn::kApnTypeTether,
};
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
// Test invalid APN types
apn_data.apn_types = {"invalidApn", ::onc::cellular_apn::kApnTypeDefault};
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
// Test empty APN types array
apn_data.apn_types->clear();
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
}
TEST(ONCUtils, ParseAndValidateOncForImport_APNIpType) {
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
test_utils::TestToplevelApnData apn_data;
// Test that no IpType provided is fine
std::string onc_blob =
test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
// Test valid IpTypes
for (const std::string& ip_type : kValidApnTypes) {
apn_data.ip_type = ip_type;
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
}
// Failure if Invalid IP type
apn_data.ip_type = "InvalidApnType";
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
}
TEST(ONCUtils, ParseAndValidateOncForImport_AdminAPNsExistForAdminAPNIds) {
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
test_utils::TestToplevelApnData apn_data;
apn_data.admin_apn_list_ids = kTestAdminApnListAllIds;
apn_data.psim_admin_assigned_apn_ids = kTestAdminApnListSubsetIds;
std::string onc_blob =
test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
apn_data.psim_admin_assigned_apn_ids = kTestAdminApnListSubsetIds;
apn_data.admin_assigned_apn_ids = std::vector<std::string>();
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
apn_data.psim_admin_assigned_apn_ids = std::vector<std::string>();
apn_data.admin_assigned_apn_ids = kTestAdminApnListSubsetIds;
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
apn_data.psim_admin_assigned_apn_ids = kTestAdminApnListSubsetIds;
apn_data.admin_assigned_apn_ids = kTestAdminApnListSubsetIds;
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
}
TEST(ONCUtils, ParseAndValidateOncForImport_AdminAPNsDoNotExistForAdminAPNIds) {
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
test_utils::TestToplevelApnData apn_data;
apn_data.admin_apn_list_ids = kTestAdminApnListAllIds;
apn_data.psim_admin_assigned_apn_ids = kTestNonAdminApnListIds;
apn_data.admin_assigned_apn_ids = std::nullopt;
std::string onc_blob =
test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
apn_data.psim_admin_assigned_apn_ids = std::nullopt;
apn_data.admin_assigned_apn_ids = kTestNonAdminApnListIds;
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
apn_data.psim_admin_assigned_apn_ids = kTestNonAdminApnListIds;
apn_data.admin_assigned_apn_ids = std::vector<std::string>();
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
apn_data.psim_admin_assigned_apn_ids = std::vector<std::string>();
apn_data.admin_assigned_apn_ids = kTestNonAdminApnListIds;
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
apn_data.psim_admin_assigned_apn_ids = kTestAdminApnListAllIds;
apn_data.admin_assigned_apn_ids = kTestNonAdminApnListIds;
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
apn_data.psim_admin_assigned_apn_ids = kTestNonAdminApnListIds;
apn_data.admin_assigned_apn_ids = kTestAdminApnListAllIds;
onc_blob = test_utils::GenerateTopLevelWithCellularWithAPNAsJSON(apn_data);
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
}
TEST(ONCUtils, ParseAndValidateOncForImport_CustomApnListRecommendedByDefault) {
const auto onc_blob =
test_utils::ReadTestData("valid_cellular_no_recommended.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
const auto* recommended =
network_configs[0].GetDict().FindByDottedPath("Cellular.Recommended");
ASSERT_NE(nullptr, recommended);
base::Value::List expected_recommended =
base::Value::List().Append(::onc::cellular::kCustomAPNList);
EXPECT_EQ(expected_recommended, *recommended);
}
TEST(
ONCUtils,
ParseAndValidateOncForImport_CustomApnListRecommendedWhenApnModificationNotProvided) {
const auto onc_blob = test_utils::ReadTestData(
"managed_cellular_no_recommended_allow_apn_modification_not_provided."
"onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
const auto* recommended =
network_configs[0].GetDict().FindByDottedPath("Cellular.Recommended");
ASSERT_NE(nullptr, recommended);
base::Value::List expected_recommended =
base::Value::List().Append(::onc::cellular::kCustomAPNList);
EXPECT_EQ(expected_recommended, *recommended);
}
TEST(
ONCUtils,
ParseAndValidateOncForImport_CustomApnListRecommendedWhenApnModificationAllowed) {
const auto onc_blob = test_utils::ReadTestData(
"managed_cellular_no_recommended_allow_apn_modification_true.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
const auto* recommended =
network_configs[0].GetDict().FindByDottedPath("Cellular.Recommended");
ASSERT_NE(nullptr, recommended);
base::Value::List expected_recommended =
base::Value::List().Append(::onc::cellular::kCustomAPNList);
EXPECT_EQ(expected_recommended, *recommended);
}
TEST(
ONCUtils,
ParseAndValidateOncForImport_CustomApnListNotRecommendeWhenApnModificationProhibited) {
const auto onc_blob = test_utils::ReadTestData(
"managed_cellular_no_recommended_allow_apn_modification_false.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
const auto* recommended =
network_configs[0].GetDict().FindByDottedPath("Cellular.Recommended");
EXPECT_EQ(nullptr, recommended);
}
TEST(ONCUtils, ParseAndValidateOncForImport_AdminApnProvided) {
const auto onc_blob = test_utils::ReadTestData(
"managed_toplevel_with_multiple_cellular_and_admin_apns.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
// Expected custom APN list for the first network configuration
base::Value::List expected_custom_apns;
// First expected custom APN details for the first network configuration
base::Value::Dict first_custom_apn_details;
first_custom_apn_details.Set(::onc::cellular_apn::kId, "admin-apn-id-y");
first_custom_apn_details.Set(::onc::cellular_apn::kAccessPointName,
"test-apn-admin-y");
first_custom_apn_details.Set(::onc::cellular_apn::kAuthentication, "");
first_custom_apn_details.Set(::onc::cellular_apn::kUsername,
"test-username-y");
first_custom_apn_details.Set(::onc::cellular_apn::kPassword,
"test-password-y");
first_custom_apn_details.Set(::onc::cellular_apn::kSource,
::onc::cellular_apn::kSourceAdmin);
// Second expected custom APN details for the first network configuration
base::Value::Dict second_custom_apn_details;
second_custom_apn_details.Set(::onc::cellular_apn::kId, "admin-apn-id-x");
second_custom_apn_details.Set(::onc::cellular_apn::kAccessPointName,
"test-apn-admin-x");
second_custom_apn_details.Set(::onc::cellular_apn::kAuthentication, "");
second_custom_apn_details.Set(::onc::cellular_apn::kUsername,
"test-username-x");
second_custom_apn_details.Set(::onc::cellular_apn::kPassword,
"test-password-x");
second_custom_apn_details.Set(::onc::cellular_apn::kSource,
::onc::cellular_apn::kSourceAdmin);
// Add the APN details to the expected custom APN list for the first network
// configuration
expected_custom_apns.Append(std::move(first_custom_apn_details));
expected_custom_apns.Append(std::move(second_custom_apn_details));
// Get the actual APN list from the first network configuration
const auto* actual_custom_apns =
network_configs[0].GetDict().FindByDottedPath("Cellular.CustomAPNList");
// Verify that the actual APN list for the first network configuration matches
// the expected custom APN list
EXPECT_TRUE(actual_custom_apns->GetList() == expected_custom_apns);
// Expected custom APN list for the second network configuration
base::Value::List expected_custom_apns_2;
// First expected custom APN details for the second network configuration
base::Value::Dict first_custom_apn_details_2;
first_custom_apn_details_2.Set(::onc::cellular_apn::kId, "admin-apn-id-z");
first_custom_apn_details_2.Set(::onc::cellular_apn::kAccessPointName,
"test-apn-admin-z");
first_custom_apn_details_2.Set(::onc::cellular_apn::kAuthentication, "");
first_custom_apn_details_2.Set(::onc::cellular_apn::kUsername,
"test-username-z");
first_custom_apn_details_2.Set(::onc::cellular_apn::kPassword,
"test-password-z");
first_custom_apn_details_2.Set(::onc::cellular_apn::kSource,
::onc::cellular_apn::kSourceAdmin);
// Second expected custom APN details for the second network configuration
base::Value::Dict second_custom_apn_details_2;
second_custom_apn_details_2.Set(::onc::cellular_apn::kId, "admin-apn-id-x");
second_custom_apn_details_2.Set(::onc::cellular_apn::kAccessPointName,
"test-apn-admin-x");
second_custom_apn_details_2.Set(::onc::cellular_apn::kAuthentication, "");
second_custom_apn_details_2.Set(::onc::cellular_apn::kUsername,
"test-username-x");
second_custom_apn_details_2.Set(::onc::cellular_apn::kPassword,
"test-password-x");
second_custom_apn_details_2.Set(::onc::cellular_apn::kSource,
::onc::cellular_apn::kSourceAdmin);
// Add the APN details to the expected custom APN list for the second network
// configuration
expected_custom_apns_2.Append(std::move(first_custom_apn_details_2));
expected_custom_apns_2.Append(std::move(second_custom_apn_details_2));
// Get the actual APN list from the second network configuration
const auto* actual_custom_apns_2 =
network_configs[1].GetDict().FindByDottedPath("Cellular.CustomAPNList");
// Verify that the actual APN list for the second network configuration
// matches the expected custom APN list
EXPECT_TRUE(actual_custom_apns_2->GetList() == expected_custom_apns_2);
// Expected custom APN list for the third network configuration, which does
// not have a admin provided custom APN.
base::Value::List expected_custom_apns_3;
// The APN should remain unchanged since the admin assigned APN Ids field was
// not provided.
base::Value::Dict only_custom_apn_details_3;
only_custom_apn_details_3.Set(::onc::cellular_apn::kAccessPointName,
"test-apn-3");
only_custom_apn_details_3.Set(::onc::cellular_apn::kAuthentication, "");
only_custom_apn_details_3.Set(::onc::cellular_apn::kUsername,
"test-username-3");
only_custom_apn_details_3.Set(::onc::cellular_apn::kPassword,
"test-password-3");
// Add the APN details to the expected custom APN list for the third network
// configuration
expected_custom_apns_3.Append(std::move(only_custom_apn_details_3));
// Get the actual APN list from the third network configuration
const auto* actual_custom_apns_3 =
network_configs[2].GetDict().FindByDottedPath("Cellular.CustomAPNList");
// Verify that the actual APN list for the third network configuration
// matches the expected custom APN list
EXPECT_TRUE(actual_custom_apns_3->GetList() == expected_custom_apns_3);
// Get the actual APN list from the fourth network configuration
const auto* actual_custom_apns_4 =
network_configs[3].GetDict().FindByDottedPath("Cellular.CustomAPNList");
// Verify that the custom APN list is empty if the admin provides an empty
// list of APN IDs.
EXPECT_TRUE(actual_custom_apns_4->GetList() == base::Value::List());
}
TEST(ONCUtils,
ParseAndValidateOncForImport_InvalidPSIMAdminAssignedApnIdsProvided) {
const auto onc_blob = test_utils::ReadTestData(
"managed_toplevel_with_invalid_psim_admin_assigned_apn_id_list.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
}
TEST(ONCUtils, ParseAndValidateOncForImport_PSIMAdminAssignedApnIdsProvided) {
const auto onc_blob = test_utils::ReadTestData(
"managed_toplevel_with_psim_admin_assigned_apn_id_list.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
// Expected PSIM Admin APN list
base::Value::List expected_psim_admin_assigned_apns;
// First expected admin assigned APN details.
base::Value::Dict first_psim_admin_assigned_apn;
first_psim_admin_assigned_apn.Set(::onc::cellular_apn::kId, "admin-apn-id-y");
first_psim_admin_assigned_apn.Set(::onc::cellular_apn::kAccessPointName,
"test-apn-admin-y");
first_psim_admin_assigned_apn.Set(::onc::cellular_apn::kAuthentication, "");
first_psim_admin_assigned_apn.Set(::onc::cellular_apn::kUsername,
"test-username-y");
first_psim_admin_assigned_apn.Set(::onc::cellular_apn::kPassword,
"test-password-y");
first_psim_admin_assigned_apn.Set(::onc::cellular_apn::kSource,
::onc::cellular_apn::kSourceAdmin);
// Second expected admin assigned APN details.
base::Value::Dict second_psim_admin_assigned_apn;
second_psim_admin_assigned_apn.Set(::onc::cellular_apn::kId,
"admin-apn-id-x");
second_psim_admin_assigned_apn.Set(::onc::cellular_apn::kAccessPointName,
"test-apn-admin-x");
second_psim_admin_assigned_apn.Set(::onc::cellular_apn::kAuthentication, "");
second_psim_admin_assigned_apn.Set(::onc::cellular_apn::kUsername,
"test-username-x");
second_psim_admin_assigned_apn.Set(::onc::cellular_apn::kPassword,
"test-password-x");
second_psim_admin_assigned_apn.Set(::onc::cellular_apn::kSource,
::onc::cellular_apn::kSourceAdmin);
// Add the APN details to the expected PSIM admin assigned APN list.
expected_psim_admin_assigned_apns.Append(
std::move(first_psim_admin_assigned_apn));
expected_psim_admin_assigned_apns.Append(
std::move(second_psim_admin_assigned_apn));
// Get the constructed PSIM admin assigned APN list from the global network
// configuration
const auto* actual_psim_admin_assigned_apns =
global_network_config.FindByDottedPath("PSIMAdminAssignedAPNs");
// Verify that the constructed PSIM admin assigned APN list matches the
// expected
EXPECT_TRUE(actual_psim_admin_assigned_apns->GetList() ==
expected_psim_admin_assigned_apns);
}
TEST(ONCUtils, ParseAndValidateOncForImport_AdminApnProvidedWithDuplicateIds) {
const auto onc_blob = test_utils::ReadTestData("duplicate_admin_apn_ids.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_FALSE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY, &network_configs,
&global_network_config, &certificates));
}
TEST(ONCUtils, ParseAndValidateOncForImport_WithAdvancedOpenVPNSettings) {
constexpr auto* auth_key =
"-----BEGIN OpenVPN Static key V1-----\n"
"83f8e7ccd99be189b4663e18615f9166\n"
"d885cdea6c8accb0ebf5be304f0b8081\n"
"5404f2a6574e029815d7a2fb65b83d0c\n"
"676850714c6a56b23415a78e06aad6b1\n"
"34900dd512049598382039e4816cb5ff\n"
"1848532b71af47578c9b4a14b5bca49f\n"
"99e0ae4dae2f4e5eadfea374aeb8fb1e\n"
"a6fdf02adc73ea778dfd43d64bf7bc75\n"
"7779d629498f8c2fbfd32812bfdf6df7\n"
"8cebafafef3e5496cb13202274f2768a\n"
"1959bc53d67a70945c4c8c6f34b63327\n"
"fb60dc84990ffec1243461e0b6310f61\n"
"e90aee1f11fb6292d6f5fcd7cd508aab\n"
"50d80f9963589c148cb4b933ec86128d\n"
"ed77d3fad6005b62f36369e2319f52bd\n"
"09c6d2e52cce2362a05009dc29b6b39a\n"
"-----END OpenVPN Static key V1-----\n";
const auto onc_blob = test_utils::ReadTestData("valid_openvpn_full.onc");
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
ASSERT_TRUE(ParseAndValidateOncForImport(
onc_blob, ::onc::ONCSource::ONC_SOURCE_USER_POLICY, &network_configs,
&global_network_config, &certificates));
const auto* open_vpn =
network_configs[0].GetDict().FindByDottedPath("VPN.OpenVPN");
ASSERT_NE(open_vpn, nullptr);
base::Value::Dict expected;
expected.Set(::onc::openvpn::kAuth, "MD5");
expected.Set(::onc::openvpn::kCipher, "AES-192-CBC");
expected.Set(::onc::openvpn::kCompressionAlgorithm,
::onc::openvpn_compression_algorithm::kLzo);
expected.Set(::onc::openvpn::kTLSAuthContents, auth_key);
expected.Set(::onc::openvpn::kKeyDirection, "1");
EXPECT_THAT(open_vpn,
Pointee(base::test::DictionaryHasValues(std::move(expected))));
}
struct MaskCredentialsTestCase {
// This field is not a raw_ptr<> because it only ever points to statically-
// allocated memory that is never freed, so it can't possibly dangle.
RAW_PTR_EXCLUSION const OncValueSignature* onc_signature;
const char* onc;
const char* expected_after_masking;
};
using ONCUtilsMaskCredentialsTest =
testing::TestWithParam<MaskCredentialsTestCase>;
TEST_P(ONCUtilsMaskCredentialsTest, Test) {
std::optional<base::Value> onc_value = base::JSONReader::Read(GetParam().onc);
ASSERT_TRUE(onc_value) << "Could not parse " << GetParam().onc;
std::optional<base::Value> expected_after_masking_value =
base::JSONReader::Read(GetParam().expected_after_masking);
ASSERT_TRUE(expected_after_masking_value)
<< "Could not parse " << GetParam().expected_after_masking;
base::Value::Dict masked = MaskCredentialsInOncObject(
*(GetParam().onc_signature), onc_value->GetDict(), "******");
EXPECT_EQ(masked, expected_after_masking_value->GetDict());
}
constexpr MaskCredentialsTestCase kMaskCredentialsTestCases[] = {
// Actual passwords in the L2TP Password field in NetworkConfiguration are
// masked.
{&kNetworkConfigurationSignature,
R"({ "GUID": "guid",
"VPN": {
"L2TP": {
"Username": "some username",
"Password": "secret_pwd"
}
}
}
)",
R"({ "GUID": "guid",
"VPN": {
"L2TP": {
"Username": "some username",
"Password": "******"
}
}
}
)"},
// The ${PASSWORD} variable in the L2TP Password field in
// NetworkConfiguration is not masked.
{&kNetworkConfigurationSignature,
R"({ "GUID": "guid",
"VPN": {
"L2TP": {
"Username": "some username",
"Password": "${PASSWORD}"
}
}
}
)",
R"({ "GUID": "guid",
"VPN": {
"L2TP": {
"Username": "some username",
"Password": "${PASSWORD}"
}
}
}
)"},
// Actual passwords in the EAP Password field in NetworkConfiguration are
// masked.
{&kNetworkConfigurationSignature,
R"({ "GUID": "guid",
"WiFi": {
"EAP": {
"Identity": "some username",
"Password": "secret_pwd"
}
}
}
)",
R"({ "GUID": "guid",
"WiFi": {
"EAP": {
"Identity": "some username",
"Password": "******"
}
}
}
)"},
// The ${PASSWORD} variable in the EAP Password field in
// NetworkConfiguration is not masked.
{&kNetworkConfigurationSignature,
R"({ "GUID": "guid",
"WiFi": {
"EAP": {
"Identity": "some username",
"Password": "${PASSWORD}"
}
}
}
)",
R"({ "GUID": "guid",
"WiFi": {
"EAP": {
"Identity": "some username",
"Password": "${PASSWORD}"
}
}
}
)"},
// The PSK Passphrase is masked no matter if it contains ${PASSWORD} or not.
{&kNetworkConfigurationSignature,
R"({ "GUID": "guid",
"WiFi": {
"Passphrase": "${PASSWORD}"
}
}
)",
R"({ "GUID": "guid",
"WiFi": {
"Passphrase": "******"
}
}
)"},
};
INSTANTIATE_TEST_SUITE_P(ONCUtilsMaskCredentialsTest,
ONCUtilsMaskCredentialsTest,
::testing::ValuesIn(kMaskCredentialsTestCases));
} // namespace chromeos::onc