// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chromeos/network/onc/onc_validator.h" #include #include #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" #include "chromeos/network/onc/onc_signature.h" #include "chromeos/network/onc/onc_test_utils.h" #include "chromeos/network/onc/onc_utils.h" #include "components/onc/onc_constants.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos { namespace onc { class ONCValidatorTest : public ::testing::Test { public: // Validate |onc_object| with the given |signature|. The object is considered // to be managed if |managed_onc| is true. A strict validator is used if // |strict| is true. |onc_object| and the resulting repaired object of the // validation is stored, so that expectations can be checked afterwards using // one of the Expect* functions below. void Validate(bool strict, scoped_ptr onc_object, const OncValueSignature* signature, bool managed_onc, ::onc::ONCSource onc_source) { scoped_ptr validator; if (strict) { // Create a strict validator that complains about every error. validator.reset(new Validator(true, true, true, managed_onc)); } else { // Create a liberal validator that ignores or repairs non-critical errors. validator.reset(new Validator(false, false, false, managed_onc)); } validator->SetOncSource(onc_source); original_object_ = onc_object.Pass(); repaired_object_ = validator->ValidateAndRepairObject(signature, *original_object_, &validation_result_); } void ExpectValid() { EXPECT_EQ(Validator::VALID, validation_result_); EXPECT_TRUE(test_utils::Equals(original_object_.get(), repaired_object_.get())); } void ExpectRepairWithWarnings( const base::DictionaryValue& expected_repaired) { EXPECT_EQ(Validator::VALID_WITH_WARNINGS, validation_result_); EXPECT_TRUE(test_utils::Equals(&expected_repaired, repaired_object_.get())); } void ExpectInvalid() { EXPECT_EQ(Validator::INVALID, validation_result_); EXPECT_EQ(NULL, repaired_object_.get()); } private: Validator::Result validation_result_; scoped_ptr original_object_; scoped_ptr repaired_object_; }; namespace { struct OncParams { // |location_of_object| is a string to identify the object to be tested. It // may be used as a filename or as a dictionary key. OncParams(const std::string& location_of_object, const OncValueSignature* onc_signature, bool is_managed_onc, ::onc::ONCSource onc_source = ::onc::ONC_SOURCE_NONE) : location(location_of_object), signature(onc_signature), is_managed(is_managed_onc), onc_source(onc_source) { } std::string location; const OncValueSignature* signature; bool is_managed; ::onc::ONCSource onc_source; }; ::std::ostream& operator<<(::std::ostream& os, const OncParams& onc) { return os << "(" << onc.location << ", " << onc.signature << ", " << (onc.is_managed ? "managed" : "unmanaged") << ", " << GetSourceAsString(onc.onc_source) << ")"; } } // namespace // Ensure that the constant |kEmptyUnencryptedConfiguration| describes a valid // ONC toplevel object. TEST_F(ONCValidatorTest, EmptyUnencryptedConfiguration) { Validate(true, ReadDictionaryFromJson(kEmptyUnencryptedConfiguration), &kToplevelConfigurationSignature, false, ::onc::ONC_SOURCE_NONE); ExpectValid(); } // This test case is about validating valid ONC objects without any errors. Both // the strict and the liberal validator accept the object. class ONCValidatorValidTest : public ONCValidatorTest, public ::testing::WithParamInterface { }; TEST_P(ONCValidatorValidTest, StrictValidationValid) { OncParams onc = GetParam(); Validate(true, test_utils::ReadTestDictionary(onc.location), onc.signature, onc.is_managed, onc.onc_source); ExpectValid(); } TEST_P(ONCValidatorValidTest, LiberalValidationValid) { OncParams onc = GetParam(); Validate(false, test_utils::ReadTestDictionary(onc.location), onc.signature, onc.is_managed, onc.onc_source); ExpectValid(); } // The parameters are: // OncParams(string: Filename of a ONC file that is to be validated, // OncValueSignature: signature of that ONC, // bool: true if the ONC is managed). INSTANTIATE_TEST_CASE_P( ONCValidatorValidTest, ONCValidatorValidTest, ::testing::Values( OncParams("managed_toplevel1.onc", &kToplevelConfigurationSignature, true), OncParams("managed_toplevel2.onc", &kToplevelConfigurationSignature, true), OncParams("managed_toplevel_with_global_config.onc", &kToplevelConfigurationSignature, true), // Check that at least one configuration is accepted for // device policies. OncParams("managed_toplevel_wifi_peap.onc", &kToplevelConfigurationSignature, true, ::onc::ONC_SOURCE_DEVICE_POLICY), OncParams("managed_toplevel_l2tpipsec.onc", &kToplevelConfigurationSignature, true), OncParams("toplevel_wifi_hexssid.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_wifi_ssid_and_hexssid.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_wifi_wpa_psk.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_wifi_wep_proxy.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_wifi_leap.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_wifi_eap_clientcert_with_cert_pems.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_wifi_remove.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_wifi_open.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_openvpn_clientcert_with_cert_pems.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_empty.onc", &kToplevelConfigurationSignature, false), OncParams("toplevel_only_global_config.onc", &kToplevelConfigurationSignature, true), OncParams("encrypted.onc", &kToplevelConfigurationSignature, true), OncParams("managed_vpn.onc", &kNetworkConfigurationSignature, true), OncParams("ethernet.onc", &kNetworkConfigurationSignature, true), OncParams("ethernet_with_eap.onc", &kNetworkConfigurationSignature, true), OncParams("translation_of_shill_ethernet_with_ipconfig.onc", &kNetworkWithStateSignature, true), OncParams("translation_of_shill_wifi_with_state.onc", &kNetworkWithStateSignature, false), OncParams("valid_openvpn_with_cert_pems.onc", &kNetworkConfigurationSignature, false), OncParams("openvpn_with_password.onc", &kNetworkConfigurationSignature, false))); namespace { struct RepairParams { RepairParams(const std::string& strict_repaired, const std::string& liberal_repaired, bool liberal_valid) : location_of_strict_repaired(strict_repaired), location_of_liberal_repaired(liberal_repaired), expect_liberal_valid(liberal_valid) {} std::string location_of_strict_repaired; std::string location_of_liberal_repaired; bool expect_liberal_valid; }; // Both |strict_repaired| and |liberal_repaired| are strings to identify the // object that is expected as the validation result. They may either be used // as filenames or as dictionary keys. RepairParams ExpectBothNotValid(const std::string& strict_repaired, const std::string& liberal_repaired) { return RepairParams(strict_repaired, liberal_repaired, false); } // |strict_repaired| is a string to identify the object that is expected as the // validation result. They may either be used // as filenames or as dictionary keys. RepairParams ExpectStrictNotValid(const std::string& strict_repaired) { return RepairParams(strict_repaired, std::string(), true); } ::std::ostream& operator<<(::std::ostream& os, const RepairParams& rp) { if (rp.expect_liberal_valid) { os << "(" << rp.location_of_strict_repaired << ", liberal is valid)"; } else { os << "(" << rp.location_of_strict_repaired << ", " << rp.location_of_liberal_repaired << ")"; } return os; } } // namespace // This test case is about validating ONC objects that contain errors which can // be repaired (then the errors count as warnings). If a location of the // expected repaired object is given, then it is checked that the validator // (either strict or liberal) returns this repaired object and the result is // VALID_WITH_WARNINGS. If the location is the empty string, then it is expected // that the validator returns NULL and the result INVALID. class ONCValidatorTestRepairable : public ONCValidatorTest, public ::testing::WithParamInterface > { public: // Load the common test data and return the dictionary at the field with // name |name|. scoped_ptr GetDictionaryFromTestFile( const std::string &name) { scoped_ptr dict( test_utils::ReadTestDictionary("invalid_settings_with_repairs.json")); const base::DictionaryValue* onc_object = NULL; CHECK(dict->GetDictionary(name, &onc_object)); return make_scoped_ptr(onc_object->DeepCopy()); } }; TEST_P(ONCValidatorTestRepairable, StrictValidation) { OncParams onc = GetParam().first; Validate(true, GetDictionaryFromTestFile(onc.location), onc.signature, onc.is_managed, onc.onc_source); std::string location_of_repaired = GetParam().second.location_of_strict_repaired; if (location_of_repaired.empty()) ExpectInvalid(); else ExpectRepairWithWarnings(*GetDictionaryFromTestFile(location_of_repaired)); } TEST_P(ONCValidatorTestRepairable, LiberalValidation) { OncParams onc = GetParam().first; Validate(false, GetDictionaryFromTestFile(onc.location), onc.signature, onc.is_managed, onc.onc_source); if (GetParam().second.expect_liberal_valid) { ExpectValid(); } else { std::string location_of_repaired = GetParam().second.location_of_liberal_repaired; if (location_of_repaired.empty()) ExpectInvalid(); else ExpectRepairWithWarnings( *GetDictionaryFromTestFile(location_of_repaired)); } } // The parameters for all test case instantations below are: // OncParams(string: A fieldname in the dictionary from the file // "invalid_settings_with_repairs.json". That nested // dictionary will be tested. // OncValueSignature: signature of that ONC, // bool: true if the ONC is managed). // // If both strict and liberal validation are expected to be not valid: // ExpectBothNotValid(string: A fieldname in the dictionary from the file // "invalid_settings_with_repairs.json". That nested // dictionary is the expected result from strict // validation, // string: A fieldname in the dictionary from the file // "invalid_settings_with_repairs.json". That nested // dictionary is the expected result from liberal // validation). // // If liberal valiation is expected to return VALID and strict validation is // expected to be not valid: // ExpectStrictNotValid(string: A fieldname in the dictionary from the file // "invalid_settings_with_repairs.json". That nested // dictionary is the expected result from strict // validation). // Strict validator returns INVALID. Liberal validator returns VALID. INSTANTIATE_TEST_CASE_P( StrictInvalidLiberalValid, ONCValidatorTestRepairable, ::testing::Values( std::make_pair(OncParams("network-missing-required", &kNetworkConfigurationSignature, false), ExpectStrictNotValid("")), std::make_pair(OncParams("network-missing-required-type", &kNetworkConfigurationSignature, false), ExpectStrictNotValid("")), std::make_pair(OncParams("managed-network-missing-required", &kNetworkConfigurationSignature, true), ExpectStrictNotValid("")), std::make_pair(OncParams("openvpn-missing-verify-x509-name", &kNetworkConfigurationSignature, false), ExpectStrictNotValid("")))); // Strict validator returns INVALID. Liberal validator repairs. INSTANTIATE_TEST_CASE_P( StrictInvalidLiberalRepair, ONCValidatorTestRepairable, ::testing::Values( std::make_pair(OncParams("network-unknown-fieldname", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "network-repaired")), std::make_pair(OncParams("managed-network-unknown-fieldname", &kNetworkConfigurationSignature, true), ExpectBothNotValid("", "managed-network-repaired")), std::make_pair(OncParams("managed-network-unknown-recommended", &kNetworkConfigurationSignature, true), ExpectBothNotValid("", "managed-network-repaired")), std::make_pair(OncParams("managed-network-dict-recommended", &kNetworkConfigurationSignature, true), ExpectBothNotValid("", "managed-network-repaired")), // Ensure that state values from Shill aren't accepted as // configuration. std::make_pair(OncParams("network-state-field", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "network-repaired")), std::make_pair( OncParams("network-nested-state-field", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "network-nested-state-field-repaired")), std::make_pair(OncParams("network-with-ipconfigs", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "network-repaired")), std::make_pair( OncParams("ipsec-with-client-cert-missing-cacert", &kIPsecSignature, false), ExpectBothNotValid("", "ipsec-with-client-cert-missing-cacert")), std::make_pair(OncParams("toplevel-with-repairable-networks", &kToplevelConfigurationSignature, false, ::onc::ONC_SOURCE_DEVICE_POLICY), ExpectBothNotValid("", "toplevel-with-repaired-networks")))); // Strict and liberal validator repair identically. INSTANTIATE_TEST_CASE_P( StrictAndLiberalRepairIdentically, ONCValidatorTestRepairable, ::testing::Values( std::make_pair(OncParams("toplevel-invalid-network", &kToplevelConfigurationSignature, false), ExpectBothNotValid("toplevel-repaired", "toplevel-repaired")), std::make_pair(OncParams("duplicate-network-guid", &kToplevelConfigurationSignature, false), ExpectBothNotValid("repaired-duplicate-network-guid", "repaired-duplicate-network-guid")), std::make_pair(OncParams("duplicate-cert-guid", &kToplevelConfigurationSignature, false), ExpectBothNotValid("repaired-duplicate-cert-guid", "repaired-duplicate-cert-guid")), std::make_pair(OncParams("toplevel-invalid-network", &kToplevelConfigurationSignature, true), ExpectBothNotValid("toplevel-repaired", "toplevel-repaired")), // Ignore recommended arrays in unmanaged ONC. std::make_pair(OncParams("network-with-illegal-recommended", &kNetworkConfigurationSignature, false), ExpectBothNotValid("network-repaired", "network-repaired")), std::make_pair(OncParams("toplevel-with-vpn", &kToplevelConfigurationSignature, false, ::onc::ONC_SOURCE_DEVICE_POLICY), ExpectBothNotValid("toplevel-empty", "toplevel-empty")), std::make_pair( OncParams("toplevel-with-server-and-ca-cert", &kToplevelConfigurationSignature, true, ::onc::ONC_SOURCE_DEVICE_POLICY), ExpectBothNotValid("toplevel-server-and-ca-cert-dropped", "toplevel-server-and-ca-cert-dropped")))); // Strict and liberal validator both repair, but with different results. INSTANTIATE_TEST_CASE_P( StrictAndLiberalRepairDifferently, ONCValidatorTestRepairable, ::testing::Values(std::make_pair(OncParams("toplevel-with-nested-warning", &kToplevelConfigurationSignature, false), ExpectBothNotValid("toplevel-empty", "toplevel-repaired")))); // Strict and liberal validator return both INVALID. INSTANTIATE_TEST_CASE_P( StrictAndLiberalInvalid, ONCValidatorTestRepairable, ::testing::Values( std::make_pair(OncParams("network-unknown-value", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "")), std::make_pair(OncParams("network-wifi-hexssid-invalid-length", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "")), std::make_pair(OncParams("network-wifi-invalid-hexssid", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "")), std::make_pair(OncParams("network-wifi-ssid-and-hexssid-inconsistent", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "")), std::make_pair(OncParams("managed-network-unknown-value", &kNetworkConfigurationSignature, true), ExpectBothNotValid("", "")), std::make_pair(OncParams("network-value-out-of-range", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "")), std::make_pair( OncParams("ipsec-with-psk-and-cacert", &kIPsecSignature, false), ExpectBothNotValid("", "")), std::make_pair( OncParams("ipsec-with-empty-cacertrefs", &kIPsecSignature, false), ExpectBothNotValid("", "")), std::make_pair(OncParams("ipsec-with-servercaref-and-servercarefs", &kIPsecSignature, false), ExpectBothNotValid("", "")), std::make_pair(OncParams("openvpn-with-servercaref-and-servercarefs", &kOpenVPNSignature, false), ExpectBothNotValid("", "")), std::make_pair(OncParams("eap-with-servercaref-and-servercarefs", &kEAPSignature, false), ExpectBothNotValid("", "")), std::make_pair(OncParams("managed-network-value-out-of-range", &kNetworkConfigurationSignature, true), ExpectBothNotValid("", "")), std::make_pair(OncParams("network-wrong-type", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "")), std::make_pair(OncParams("managed-network-wrong-type", &kNetworkConfigurationSignature, true), ExpectBothNotValid("", "")), std::make_pair(OncParams("network-with-client-cert-pattern", &kNetworkConfigurationSignature, true, ::onc::ONC_SOURCE_DEVICE_POLICY), ExpectBothNotValid("", "")), std::make_pair(OncParams("openvpn-invalid-verify-x509-type", &kNetworkConfigurationSignature, false), ExpectBothNotValid("", "")))); } // namespace onc } // namespace chromeos