summaryrefslogtreecommitdiffstats
path: root/chromeos
diff options
context:
space:
mode:
authorbartfab <bartfab@chromium.org>2015-03-18 16:58:36 -0700
committerCommit bot <commit-bot@chromium.org>2015-03-18 23:59:30 +0000
commit82bb909334491a927f4e77ae5e2f0326fe4da0e2 (patch)
tree430d33fdec0f616ed32d82cdc48e2b5e964b88eb /chromeos
parent07840d895caf8b511ffd2209d3ce2ffb1ae4efe7 (diff)
downloadchromium_src-82bb909334491a927f4e77ae5e2f0326fe4da0e2.zip
chromium_src-82bb909334491a927f4e77ae5e2f0326fe4da0e2.tar.gz
chromium_src-82bb909334491a927f4e77ae5e2f0326fe4da0e2.tar.bz2
Add an ONC property for the third-party VPN provider extension ID
This CL maps the third-party VPN provider extension ID (which is stored in shill's |Provider.Host| field) to an ONC property. BUG=460428 TEST=Extended unit tests and API test Review URL: https://codereview.chromium.org/1019033002 Cr-Commit-Position: refs/heads/master@{#321238}
Diffstat (limited to 'chromeos')
-rw-r--r--chromeos/network/network_state.cc43
-rw-r--r--chromeos/network/network_state.h15
-rw-r--r--chromeos/network/network_state_unittest.cc28
-rw-r--r--chromeos/network/onc/onc_normalizer.cc1
-rw-r--r--chromeos/network/onc/onc_signature.cc9
-rw-r--r--chromeos/network/onc/onc_signature.h1
-rw-r--r--chromeos/network/onc/onc_translator_onc_to_shill.cc26
-rw-r--r--chromeos/network/onc/onc_translator_shill_to_onc.cc29
-rw-r--r--chromeos/network/onc/onc_translator_unittest.cc7
-rw-r--r--chromeos/network/onc/onc_validator.cc14
-rw-r--r--chromeos/network/onc/onc_validator.h1
-rw-r--r--chromeos/network/onc/onc_validator_unittest.cc7
-rw-r--r--chromeos/test/data/network/invalid_settings_with_repairs.json9
-rw-r--r--chromeos/test/data/network/shill_output_third_party_vpn.json8
-rw-r--r--chromeos/test/data/network/shill_third_party_vpn.json6
-rw-r--r--chromeos/test/data/network/third_party_vpn.onc11
16 files changed, 181 insertions, 34 deletions
diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc
index 609c9f0..f4ad959 100644
--- a/chromeos/network/network_state.cc
+++ b/chromeos/network/network_state.cc
@@ -4,6 +4,7 @@
#include "chromeos/network/network_state.h"
+#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
@@ -166,25 +167,29 @@ bool NetworkState::PropertyChanged(const std::string& key,
}
return true;
} else if (key == shill::kProviderProperty) {
+ std::string vpn_provider_type;
const base::DictionaryValue* dict;
- std::string provider_type;
if (!value.GetAsDictionary(&dict) ||
!dict->GetStringWithoutPathExpansion(shill::kTypeProperty,
- &provider_type)) {
+ &vpn_provider_type)) {
+ NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
return false;
}
- if (provider_type != shill::kProviderThirdPartyVpn) {
- // If the network uses the built-in OpenVPN and L2TP support, set the
- // provider extension ID to an empty string.
- vpn_provider_extension_id_.clear();
- return true;
+ if (vpn_provider_type == shill::kProviderThirdPartyVpn) {
+ // If the network uses a third-party VPN provider, copy over the
+ // provider's extension ID, which is held in |shill::kHostProperty|.
+ if (!dict->GetStringWithoutPathExpansion(
+ shill::kHostProperty, &third_party_vpn_provider_extension_id_)) {
+ NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
+ return false;
+ }
+ } else {
+ third_party_vpn_provider_extension_id_.clear();
}
- // If the network uses a third-party VPN provider, copy over the provider's
- // extension ID, which is held in |shill::kHostProperty|.
- return dict->GetStringWithoutPathExpansion(shill::kHostProperty,
- &vpn_provider_extension_id_);
+ vpn_provider_type_ = vpn_provider_type;
+ return true;
}
return false;
}
@@ -232,6 +237,22 @@ void NetworkState::GetStateProperties(base::DictionaryValue* dictionary) const {
connection_state());
}
+ // VPN properties.
+ if (NetworkTypePattern::VPN().MatchesType(type())) {
+ // Shill sends VPN provider properties in a nested dictionary. |dictionary|
+ // must replicate that nested structure.
+ scoped_ptr<base::DictionaryValue> provider_property(
+ new base::DictionaryValue);
+ provider_property->SetStringWithoutPathExpansion(shill::kTypeProperty,
+ vpn_provider_type_);
+ if (vpn_provider_type_ == shill::kProviderThirdPartyVpn) {
+ provider_property->SetStringWithoutPathExpansion(
+ shill::kHostProperty, third_party_vpn_provider_extension_id_);
+ }
+ dictionary->SetWithoutPathExpansion(shill::kProviderProperty,
+ provider_property.release());
+ }
+
// Wireless properties
if (!NetworkTypePattern::Wireless().MatchesType(type()))
return;
diff --git a/chromeos/network/network_state.h b/chromeos/network/network_state.h
index c456909..e4d9cce 100644
--- a/chromeos/network/network_state.h
+++ b/chromeos/network/network_state.h
@@ -92,8 +92,11 @@ class CHROMEOS_EXPORT NetworkState : public ManagedState {
const std::string& roaming() const { return roaming_; }
const std::string& payment_url() const { return payment_url_; }
bool cellular_out_of_credits() const { return cellular_out_of_credits_; }
- const std::string& vpn_provider_extension_id() const {
- return vpn_provider_extension_id_;
+
+ // VPN property accessors
+ const std::string& vpn_provider_type() const { return vpn_provider_type_; }
+ const std::string& third_party_vpn_provider_extension_id() const {
+ return third_party_vpn_provider_extension_id_;
}
// Returns true if |connection_state_| is a connected/connecting state.
@@ -183,10 +186,10 @@ class CHROMEOS_EXPORT NetworkState : public ManagedState {
std::string payment_url_;
bool cellular_out_of_credits_;
- // VPN property. For networks using a third-party VPN provider, this will be
- // the provider's extension ID. For networks using the built-in OpenVPN and
- // L2TP support, this will be an empty string.
- std::string vpn_provider_extension_id_;
+ // VPN properties, used to construct the display name and to show the correct
+ // configuration dialog.
+ std::string vpn_provider_type_;
+ std::string third_party_vpn_provider_extension_id_;
// TODO(pneubeck): Remove this once (Managed)NetworkConfigurationHandler
// provides proxy configuration. crbug.com/241775
diff --git a/chromeos/network/network_state_unittest.cc b/chromeos/network/network_state_unittest.cc
index aa636d7..3ba359a 100644
--- a/chromeos/network/network_state_unittest.cc
+++ b/chromeos/network/network_state_unittest.cc
@@ -54,11 +54,14 @@ class NetworkStateTest : public testing::Test {
}
protected:
+ bool SetProperty(const std::string& key, scoped_ptr<base::Value> value) {
+ const bool result = network_state_.PropertyChanged(key, *value);
+ properties_.SetWithoutPathExpansion(key, value.release());
+ return result;
+ }
+
bool SetStringProperty(const std::string& key, const std::string& value) {
- TestStringValue* string_value = new TestStringValue(value);
- bool res = network_state_.PropertyChanged(key, *string_value);
- properties_.SetWithoutPathExpansion(key, string_value);
- return res;
+ return SetProperty(key, make_scoped_ptr(new TestStringValue(value)));
}
bool SignalInitialPropertiesReceived() {
@@ -213,4 +216,21 @@ TEST_F(NetworkStateTest, CaptivePortalState) {
EXPECT_TRUE(network_state_.is_captive_portal());
}
+// Third-party VPN provider.
+TEST_F(NetworkStateTest, VPNThirdPartyProvider) {
+ EXPECT_TRUE(SetStringProperty(shill::kTypeProperty, shill::kTypeVPN));
+ EXPECT_TRUE(SetStringProperty(shill::kNameProperty, "VPN"));
+
+ scoped_ptr<base::DictionaryValue> provider(new base::DictionaryValue);
+ provider->SetStringWithoutPathExpansion(shill::kTypeProperty,
+ shill::kProviderThirdPartyVpn);
+ provider->SetStringWithoutPathExpansion(
+ shill::kHostProperty, "third-party-vpn-provider-extension-id");
+ EXPECT_TRUE(SetProperty(shill::kProviderProperty, provider.Pass()));
+ SignalInitialPropertiesReceived();
+ EXPECT_EQ(network_state_.vpn_provider_type(), shill::kProviderThirdPartyVpn);
+ EXPECT_EQ(network_state_.third_party_vpn_provider_extension_id(),
+ "third-party-vpn-provider-extension-id");
+}
+
} // namespace chromeos
diff --git a/chromeos/network/onc/onc_normalizer.cc b/chromeos/network/onc/onc_normalizer.cc
index 4ec5ddf..627835a 100644
--- a/chromeos/network/onc/onc_normalizer.cc
+++ b/chromeos/network/onc/onc_normalizer.cc
@@ -232,6 +232,7 @@ void Normalizer::NormalizeVPN(base::DictionaryValue* vpn) {
RemoveEntryUnless(vpn, kOpenVPN, type == kOpenVPN);
RemoveEntryUnless(vpn, kIPsec, type == kIPsec || type == kTypeL2TP_IPsec);
RemoveEntryUnless(vpn, kL2TP, type == kTypeL2TP_IPsec);
+ RemoveEntryUnless(vpn, kThirdPartyVpn, type == kThirdPartyVpn);
}
void Normalizer::NormalizeWiFi(base::DictionaryValue* wifi) {
diff --git a/chromeos/network/onc/onc_signature.cc b/chromeos/network/onc/onc_signature.cc
index a421909..ad05682 100644
--- a/chromeos/network/onc/onc_signature.cc
+++ b/chromeos/network/onc/onc_signature.cc
@@ -151,6 +151,11 @@ const OncFieldSignature openvpn_fields[] = {
{ ::onc::openvpn::kVerifyX509, &kVerifyX509Signature},
{NULL}};
+const OncFieldSignature third_party_vpn_fields[] = {
+ { ::onc::kRecommended, &kRecommendedSignature},
+ { ::onc::third_party_vpn::kExtensionID, &kStringSignature},
+ {NULL}};
+
const OncFieldSignature verify_x509_fields[] = {
{ ::onc::verify_x509::kName, &kStringSignature},
{ ::onc::verify_x509::kType, &kStringSignature},
@@ -163,6 +168,7 @@ const OncFieldSignature vpn_fields[] = {
{ ::onc::vpn::kIPsec, &kIPsecSignature},
{ ::onc::vpn::kL2TP, &kL2TPSignature},
{ ::onc::vpn::kOpenVPN, &kOpenVPNSignature},
+ { ::onc::vpn::kThirdPartyVpn, &kThirdPartyVPNSignature},
{ ::onc::vpn::kType, &kStringSignature},
{NULL}};
@@ -385,6 +391,9 @@ const OncValueSignature kL2TPSignature = {
const OncValueSignature kOpenVPNSignature = {
base::Value::TYPE_DICTIONARY, openvpn_fields, NULL
};
+const OncValueSignature kThirdPartyVPNSignature = {
+ base::Value::TYPE_DICTIONARY, third_party_vpn_fields, NULL
+};
const OncValueSignature kVerifyX509Signature = {
base::Value::TYPE_DICTIONARY, verify_x509_fields, NULL
};
diff --git a/chromeos/network/onc/onc_signature.h b/chromeos/network/onc/onc_signature.h
index acb8406..316ee86 100644
--- a/chromeos/network/onc/onc_signature.h
+++ b/chromeos/network/onc/onc_signature.h
@@ -43,6 +43,7 @@ CHROMEOS_EXPORT extern const OncValueSignature kIPsecSignature;
CHROMEOS_EXPORT extern const OncValueSignature kL2TPSignature;
CHROMEOS_EXPORT extern const OncValueSignature kXAUTHSignature;
CHROMEOS_EXPORT extern const OncValueSignature kOpenVPNSignature;
+CHROMEOS_EXPORT extern const OncValueSignature kThirdPartyVPNSignature;
CHROMEOS_EXPORT extern const OncValueSignature kVerifyX509Signature;
CHROMEOS_EXPORT extern const OncValueSignature kVPNSignature;
CHROMEOS_EXPORT extern const OncValueSignature kEthernetSignature;
diff --git a/chromeos/network/onc/onc_translator_onc_to_shill.cc b/chromeos/network/onc/onc_translator_onc_to_shill.cc
index fdd8fbd..3cf9789f 100644
--- a/chromeos/network/onc/onc_translator_onc_to_shill.cc
+++ b/chromeos/network/onc/onc_translator_onc_to_shill.cc
@@ -188,10 +188,28 @@ void LocalTranslator::TranslateIPsec() {
}
void LocalTranslator::TranslateVPN() {
- CopyFieldFromONCToShill(::onc::vpn::kHost, shill::kProviderHostProperty);
- std::string type;
- if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType, &type))
- TranslateWithTableAndSet(type, kVPNTypeTable, shill::kProviderTypeProperty);
+ std::string onc_type;
+ if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
+ &onc_type)) {
+ TranslateWithTableAndSet(onc_type, kVPNTypeTable,
+ shill::kProviderTypeProperty);
+ }
+ if (onc_type == ::onc::vpn::kThirdPartyVpn) {
+ // For third-party VPNs, |shill::kProviderHostProperty| is used to store the
+ // provider's extension ID.
+ const base::DictionaryValue* onc_third_party_vpn = nullptr;
+ onc_object_->GetDictionaryWithoutPathExpansion(::onc::vpn::kThirdPartyVpn,
+ &onc_third_party_vpn);
+ std::string onc_extension_id;
+ if (onc_third_party_vpn &&
+ onc_third_party_vpn->GetStringWithoutPathExpansion(
+ ::onc::third_party_vpn::kExtensionID, &onc_extension_id)) {
+ shill_dictionary_->SetStringWithoutPathExpansion(
+ shill::kProviderHostProperty, onc_extension_id);
+ }
+ } else {
+ CopyFieldFromONCToShill(::onc::vpn::kHost, shill::kProviderHostProperty);
+ }
CopyFieldsAccordingToSignature();
}
diff --git a/chromeos/network/onc/onc_translator_shill_to_onc.cc b/chromeos/network/onc/onc_translator_shill_to_onc.cc
index 1ec647a..43e7b3a 100644
--- a/chromeos/network/onc/onc_translator_shill_to_onc.cc
+++ b/chromeos/network/onc/onc_translator_shill_to_onc.cc
@@ -77,6 +77,7 @@ class ShillToONCTranslator {
void TranslateEthernet();
void TranslateOpenVPN();
void TranslateIPsec();
+ void TranslateThirdPartyVPN();
void TranslateVPN();
void TranslateWiFiWithState();
void TranslateWiMAXWithState();
@@ -159,6 +160,8 @@ ShillToONCTranslator::CreateTranslatedONCObject() {
TranslateOpenVPN();
} else if (onc_signature_ == &kIPsecSignature) {
TranslateIPsec();
+ } else if (onc_signature_ == &kThirdPartyVPNSignature) {
+ TranslateThirdPartyVPN();
} else if (onc_signature_ == &kWiFiWithStateSignature) {
TranslateWiFiWithState();
} else if (onc_signature_ == &kWiMAXWithStateSignature) {
@@ -266,6 +269,18 @@ void ShillToONCTranslator::TranslateIPsec() {
authentication_type);
}
+void ShillToONCTranslator::TranslateThirdPartyVPN() {
+ CopyPropertiesAccordingToSignature();
+
+ // For third-party VPNs, |shill::kProviderHostProperty| is used to store the
+ // provider's extension ID.
+ std::string shill_extension_id;
+ shill_dictionary_->GetStringWithoutPathExpansion(shill::kHostProperty,
+ &shill_extension_id);
+ onc_object_->SetStringWithoutPathExpansion(
+ ::onc::third_party_vpn::kExtensionID, shill_extension_id);
+}
+
void ShillToONCTranslator::TranslateVPN() {
CopyPropertiesAccordingToSignature();
@@ -285,11 +300,12 @@ void ShillToONCTranslator::TranslateVPN() {
}
onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kType,
onc_provider_type);
- std::string provider_host;
- if (provider->GetStringWithoutPathExpansion(shill::kHostProperty,
- &provider_host)) {
+ std::string shill_provider_host;
+ if (onc_provider_type != ::onc::vpn::kThirdPartyVpn &&
+ provider->GetStringWithoutPathExpansion(shill::kHostProperty,
+ &shill_provider_host)) {
onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kHost,
- provider_host);
+ shill_provider_host);
}
// Translate the nested dictionary.
@@ -298,13 +314,14 @@ void ShillToONCTranslator::TranslateVPN() {
TranslateAndAddNestedObject(::onc::vpn::kIPsec, *provider);
TranslateAndAddNestedObject(::onc::vpn::kL2TP, *provider);
provider_type_dictionary = ::onc::vpn::kIPsec;
- } else if (onc_provider_type != ::onc::vpn::kThirdPartyVpn) {
+ } else {
TranslateAndAddNestedObject(onc_provider_type, *provider);
provider_type_dictionary = onc_provider_type;
}
bool save_credentials;
- if (shill_dictionary_->GetBooleanWithoutPathExpansion(
+ if (onc_provider_type != ::onc::vpn::kThirdPartyVpn &&
+ shill_dictionary_->GetBooleanWithoutPathExpansion(
shill::kSaveCredentialsProperty, &save_credentials)) {
SetNestedOncValue(provider_type_dictionary,
::onc::vpn::kSaveCredentials,
diff --git a/chromeos/network/onc/onc_translator_unittest.cc b/chromeos/network/onc/onc_translator_unittest.cc
index 9444614..f637761 100644
--- a/chromeos/network/onc/onc_translator_unittest.cc
+++ b/chromeos/network/onc/onc_translator_unittest.cc
@@ -61,7 +61,8 @@ INSTANTIATE_TEST_CASE_P(
std::make_pair("openvpn_clientcert_with_cert_pems.onc",
"shill_openvpn_clientcert.json"),
std::make_pair("cellular.onc", "shill_cellular.json"),
- std::make_pair("wimax.onc", "shill_wimax.json")));
+ std::make_pair("wimax.onc", "shill_wimax.json"),
+ std::make_pair("third_party_vpn.onc", "shill_third_party_vpn.json")));
// First parameter: Filename of source Shill json.
// Second parameter: Filename of expected translated ONC network part.
@@ -113,7 +114,9 @@ INSTANTIATE_TEST_CASE_P(
std::make_pair("shill_cellular_with_state.json",
"translation_of_shill_cellular_with_state.onc"),
std::make_pair("shill_wimax_with_state.json",
- "translation_of_shill_wimax_with_state.onc")));
+ "translation_of_shill_wimax_with_state.onc"),
+ std::make_pair("shill_output_third_party_vpn.json",
+ "third_party_vpn.onc")));
} // namespace onc
} // namespace chromeos
diff --git a/chromeos/network/onc/onc_validator.cc b/chromeos/network/onc/onc_validator.cc
index c3cd2bb..e3ea49a 100644
--- a/chromeos/network/onc/onc_validator.cc
+++ b/chromeos/network/onc/onc_validator.cc
@@ -122,6 +122,8 @@ scoped_ptr<base::DictionaryValue> Validator::MapObject(
valid = ValidateIPsec(repaired.get());
} else if (&signature == &kOpenVPNSignature) {
valid = ValidateOpenVPN(repaired.get());
+ } else if (&signature == &kThirdPartyVPNSignature) {
+ valid = ValidateThirdPartyVPN(repaired.get());
} else if (&signature == &kVerifyX509Signature) {
valid = ValidateVerifyX509(repaired.get());
} else if (&signature == &kCertificatePatternSignature) {
@@ -683,7 +685,8 @@ bool Validator::ValidateWiFi(base::DictionaryValue* result) {
bool Validator::ValidateVPN(base::DictionaryValue* result) {
using namespace ::onc::vpn;
- const char* const kValidTypes[] = {kIPsec, kTypeL2TP_IPsec, kOpenVPN};
+ const char* const kValidTypes[] = {
+ kIPsec, kTypeL2TP_IPsec, kOpenVPN, kThirdPartyVpn};
const std::vector<const char*> valid_types(toVector(kValidTypes));
if (FieldExistsAndHasNoValidValue(*result, ::onc::vpn::kType, valid_types))
return false;
@@ -698,6 +701,8 @@ bool Validator::ValidateVPN(base::DictionaryValue* result) {
} else if (type == kTypeL2TP_IPsec) {
all_required_exist &=
RequireField(*result, kIPsec) && RequireField(*result, kL2TP);
+ } else if (type == kThirdPartyVpn) {
+ all_required_exist &= RequireField(*result, kThirdPartyVpn);
}
return !error_on_missing_field_ || all_required_exist;
@@ -802,6 +807,13 @@ bool Validator::ValidateOpenVPN(base::DictionaryValue* result) {
return !error_on_missing_field_ || all_required_exist;
}
+bool Validator::ValidateThirdPartyVPN(base::DictionaryValue* result) {
+ const bool all_required_exist =
+ RequireField(*result, ::onc::third_party_vpn::kExtensionID);
+
+ return !error_on_missing_field_ || all_required_exist;
+}
+
bool Validator::ValidateVerifyX509(base::DictionaryValue* result) {
using namespace ::onc::verify_x509;
diff --git a/chromeos/network/onc/onc_validator.h b/chromeos/network/onc/onc_validator.h
index b535f9f..424f7eb 100644
--- a/chromeos/network/onc/onc_validator.h
+++ b/chromeos/network/onc/onc_validator.h
@@ -160,6 +160,7 @@ class CHROMEOS_EXPORT Validator : public Mapper {
bool ValidateVPN(base::DictionaryValue* result);
bool ValidateIPsec(base::DictionaryValue* result);
bool ValidateOpenVPN(base::DictionaryValue* result);
+ bool ValidateThirdPartyVPN(base::DictionaryValue* result);
bool ValidateVerifyX509(base::DictionaryValue* result);
bool ValidateCertificatePattern(base::DictionaryValue* result);
bool ValidateProxySettings(base::DictionaryValue* result);
diff --git a/chromeos/network/onc/onc_validator_unittest.cc b/chromeos/network/onc/onc_validator_unittest.cc
index add3572..8517f71 100644
--- a/chromeos/network/onc/onc_validator_unittest.cc
+++ b/chromeos/network/onc/onc_validator_unittest.cc
@@ -208,6 +208,9 @@ INSTANTIATE_TEST_CASE_P(
false),
OncParams("openvpn_with_password.onc",
&kNetworkConfigurationSignature,
+ false),
+ OncParams("third_party_vpn.onc",
+ &kNetworkConfigurationSignature,
false)));
namespace {
@@ -348,6 +351,10 @@ INSTANTIATE_TEST_CASE_P(
std::make_pair(OncParams("openvpn-missing-verify-x509-name",
&kNetworkConfigurationSignature,
false),
+ ExpectStrictNotValid("")),
+ std::make_pair(OncParams("third-party-vpn-missing-extension-id",
+ &kNetworkConfigurationSignature,
+ false),
ExpectStrictNotValid(""))));
// Strict validator returns INVALID. Liberal validator repairs.
diff --git a/chromeos/test/data/network/invalid_settings_with_repairs.json b/chromeos/test/data/network/invalid_settings_with_repairs.json
index 67c375be..d9d4b6d 100644
--- a/chromeos/test/data/network/invalid_settings_with_repairs.json
+++ b/chromeos/test/data/network/invalid_settings_with_repairs.json
@@ -396,6 +396,15 @@
}
}
},
+ "third-party-vpn-missing-extension-id": {
+ "GUID": "guid",
+ "Name": "third-party VPN",
+ "Type": "VPN",
+ "VPN": {
+ "Type": "ThirdPartyVPN",
+ "ThirdPartyVPN": { }
+ }
+ },
"toplevel-empty": {
"Type": "UnencryptedConfiguration",
"NetworkConfigurations": [ ]
diff --git a/chromeos/test/data/network/shill_output_third_party_vpn.json b/chromeos/test/data/network/shill_output_third_party_vpn.json
new file mode 100644
index 0000000..382f2f4
--- /dev/null
+++ b/chromeos/test/data/network/shill_output_third_party_vpn.json
@@ -0,0 +1,8 @@
+{ "GUID": "guid",
+ "Type": "vpn",
+ "Name": "third-party VPN",
+ "Provider": {
+ "Host": "deadbeefdeadbeefdeadbeefdeadbeef",
+ "Type": "thirdpartyvpn",
+ },
+}
diff --git a/chromeos/test/data/network/shill_third_party_vpn.json b/chromeos/test/data/network/shill_third_party_vpn.json
new file mode 100644
index 0000000..9ee0fe0
--- /dev/null
+++ b/chromeos/test/data/network/shill_third_party_vpn.json
@@ -0,0 +1,6 @@
+{ "GUID": "guid",
+ "Type": "vpn",
+ "Name": "third-party VPN",
+ "Provider.Host": "deadbeefdeadbeefdeadbeefdeadbeef",
+ "Provider.Type": "thirdpartyvpn",
+}
diff --git a/chromeos/test/data/network/third_party_vpn.onc b/chromeos/test/data/network/third_party_vpn.onc
new file mode 100644
index 0000000..e062178
--- /dev/null
+++ b/chromeos/test/data/network/third_party_vpn.onc
@@ -0,0 +1,11 @@
+{
+ "GUID": "guid",
+ "Name": "third-party VPN",
+ "Type": "VPN",
+ "VPN": {
+ "Type": "ThirdPartyVPN",
+ "ThirdPartyVPN": {
+ "ExtensionID": "deadbeefdeadbeefdeadbeefdeadbeef",
+ }
+ }
+}