Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / chromeos / network / onc / onc_translator_onc_to_shill.cc
blob9cea03314c35f060a6d04cbc0f75ca4f11f50ed3
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // The implementation of TranslateONCObjectToShill is structured in two parts:
6 // - The recursion through the existing ONC hierarchy
7 // see TranslateONCHierarchy
8 // - The local translation of an object depending on the associated signature
9 // see LocalTranslator::TranslateFields
11 #include "chromeos/network/onc/onc_translator.h"
13 #include <string>
15 #include "base/json/json_reader.h"
16 #include "base/json/json_writer.h"
17 #include "base/logging.h"
18 #include "base/strings/string_util.h"
19 #include "base/values.h"
20 #include "chromeos/network/onc/onc_signature.h"
21 #include "chromeos/network/onc/onc_translation_tables.h"
22 #include "chromeos/network/shill_property_util.h"
23 #include "components/onc/onc_constants.h"
24 #include "third_party/cros_system_api/dbus/service_constants.h"
26 namespace chromeos {
27 namespace onc {
29 namespace {
31 scoped_ptr<base::StringValue> ConvertValueToString(const base::Value& value) {
32 std::string str;
33 if (!value.GetAsString(&str))
34 base::JSONWriter::Write(&value, &str);
35 return make_scoped_ptr(new base::StringValue(str));
38 // This class is responsible to translate the local fields of the given
39 // |onc_object| according to |onc_signature| into |shill_dictionary|. This
40 // translation should consider (if possible) only fields of this ONC object and
41 // not nested objects because recursion is handled by the calling function
42 // TranslateONCHierarchy.
43 class LocalTranslator {
44 public:
45 LocalTranslator(const OncValueSignature& onc_signature,
46 const base::DictionaryValue& onc_object,
47 base::DictionaryValue* shill_dictionary)
48 : onc_signature_(&onc_signature),
49 onc_object_(&onc_object),
50 shill_dictionary_(shill_dictionary) {
51 field_translation_table_ = GetFieldTranslationTable(onc_signature);
54 void TranslateFields();
56 private:
57 void TranslateEthernet();
58 void TranslateOpenVPN();
59 void TranslateIPsec();
60 void TranslateVPN();
61 void TranslateWiFi();
62 void TranslateEAP();
63 void TranslateNetworkConfiguration();
65 // Copies all entries from |onc_object_| to |shill_dictionary_| for which a
66 // translation (shill_property_name) is defined by the translation table for
67 // |onc_signature_|.
68 void CopyFieldsAccordingToSignature();
70 // If existent, copies the value of field |onc_field_name| from |onc_object_|
71 // to the property |shill_property_name| in |shill_dictionary_|.
72 void CopyFieldFromONCToShill(const std::string& onc_field_name,
73 const std::string& shill_property_name);
75 // Adds |value| to |shill_dictionary| at the field shill_property_name given
76 // by the associated signature. Takes ownership of |value|. Does nothing if
77 // |value| is NULL or the property name cannot be read from the signature.
78 void AddValueAccordingToSignature(const std::string& onc_field_name,
79 scoped_ptr<base::Value> value);
81 // Translates the value |onc_value| using |table|. It is an error if no
82 // matching table entry is found. Writes the result as entry at
83 // |shill_property_name| in |shill_dictionary_|.
84 void TranslateWithTableAndSet(const std::string& onc_value,
85 const StringTranslationEntry table[],
86 const std::string& shill_property_name);
88 const OncValueSignature* onc_signature_;
89 const FieldTranslationEntry* field_translation_table_;
90 const base::DictionaryValue* onc_object_;
91 base::DictionaryValue* shill_dictionary_;
93 DISALLOW_COPY_AND_ASSIGN(LocalTranslator);
96 void LocalTranslator::TranslateFields() {
97 if (onc_signature_ == &kNetworkConfigurationSignature)
98 TranslateNetworkConfiguration();
99 else if (onc_signature_ == &kEthernetSignature)
100 TranslateEthernet();
101 else if (onc_signature_ == &kVPNSignature)
102 TranslateVPN();
103 else if (onc_signature_ == &kOpenVPNSignature)
104 TranslateOpenVPN();
105 else if (onc_signature_ == &kIPsecSignature)
106 TranslateIPsec();
107 else if (onc_signature_ == &kWiFiSignature)
108 TranslateWiFi();
109 else if (onc_signature_ == &kEAPSignature)
110 TranslateEAP();
111 else
112 CopyFieldsAccordingToSignature();
115 void LocalTranslator::TranslateEthernet() {
116 std::string authentication;
117 onc_object_->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
118 &authentication);
120 const char* shill_type = shill::kTypeEthernet;
121 if (authentication == ::onc::ethernet::k8021X)
122 shill_type = shill::kTypeEthernetEap;
123 shill_dictionary_->SetStringWithoutPathExpansion(shill::kTypeProperty,
124 shill_type);
126 CopyFieldsAccordingToSignature();
129 void LocalTranslator::TranslateOpenVPN() {
130 // SaveCredentials needs special handling when translating from Shill -> ONC
131 // so handle it explicitly here.
132 CopyFieldFromONCToShill(::onc::vpn::kSaveCredentials,
133 shill::kSaveCredentialsProperty);
135 std::string user_auth_type;
136 onc_object_->GetStringWithoutPathExpansion(
137 ::onc::openvpn::kUserAuthenticationType, &user_auth_type);
138 // The default behavior (if user_auth_type is empty) is to use both password
139 // and OTP in a static challenge and only the password otherwise. As long as
140 // Shill doe not know about the exact user authentication type, this is
141 // identical to kPasswordAndOTP.
142 if (user_auth_type.empty())
143 user_auth_type = ::onc::openvpn_user_auth_type::kPasswordAndOTP;
145 if (user_auth_type == ::onc::openvpn_user_auth_type::kPassword ||
146 user_auth_type == ::onc::openvpn_user_auth_type::kPasswordAndOTP) {
147 CopyFieldFromONCToShill(::onc::openvpn::kPassword,
148 shill::kOpenVPNPasswordProperty);
150 if (user_auth_type == ::onc::openvpn_user_auth_type::kPasswordAndOTP)
151 CopyFieldFromONCToShill(::onc::openvpn::kOTP, shill::kOpenVPNOTPProperty);
152 if (user_auth_type == ::onc::openvpn_user_auth_type::kOTP)
153 CopyFieldFromONCToShill(::onc::openvpn::kOTP, shill::kOpenVPNTokenProperty);
155 // Shill supports only one RemoteCertKU but ONC a list.
156 // Copy only the first entry if existing.
157 const base::ListValue* cert_kus = NULL;
158 std::string cert_ku;
159 if (onc_object_->GetListWithoutPathExpansion(::onc::openvpn::kRemoteCertKU,
160 &cert_kus) &&
161 cert_kus->GetString(0, &cert_ku)) {
162 shill_dictionary_->SetStringWithoutPathExpansion(
163 shill::kOpenVPNRemoteCertKUProperty, cert_ku);
166 for (base::DictionaryValue::Iterator it(*onc_object_); !it.IsAtEnd();
167 it.Advance()) {
168 scoped_ptr<base::Value> translated;
169 if (it.key() == ::onc::openvpn::kRemoteCertKU ||
170 it.key() == ::onc::openvpn::kServerCAPEMs) {
171 translated.reset(it.value().DeepCopy());
172 } else {
173 // Shill wants all Provider/VPN fields to be strings.
174 translated = ConvertValueToString(it.value());
176 AddValueAccordingToSignature(it.key(), translated.Pass());
180 void LocalTranslator::TranslateIPsec() {
181 CopyFieldsAccordingToSignature();
183 // SaveCredentials needs special handling when translating from Shill -> ONC
184 // so handle it explicitly here.
185 CopyFieldFromONCToShill(::onc::vpn::kSaveCredentials,
186 shill::kSaveCredentialsProperty);
189 void LocalTranslator::TranslateVPN() {
190 std::string onc_type;
191 if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
192 &onc_type)) {
193 TranslateWithTableAndSet(onc_type, kVPNTypeTable,
194 shill::kProviderTypeProperty);
196 if (onc_type == ::onc::vpn::kThirdPartyVpn) {
197 // For third-party VPNs, |shill::kProviderHostProperty| is used to store the
198 // provider's extension ID.
199 const base::DictionaryValue* onc_third_party_vpn = nullptr;
200 onc_object_->GetDictionaryWithoutPathExpansion(::onc::vpn::kThirdPartyVpn,
201 &onc_third_party_vpn);
202 std::string onc_extension_id;
203 if (onc_third_party_vpn &&
204 onc_third_party_vpn->GetStringWithoutPathExpansion(
205 ::onc::third_party_vpn::kExtensionID, &onc_extension_id)) {
206 shill_dictionary_->SetStringWithoutPathExpansion(
207 shill::kProviderHostProperty, onc_extension_id);
209 } else {
210 CopyFieldFromONCToShill(::onc::vpn::kHost, shill::kProviderHostProperty);
213 CopyFieldsAccordingToSignature();
216 void LocalTranslator::TranslateWiFi() {
217 std::string security;
218 if (onc_object_->GetStringWithoutPathExpansion(::onc::wifi::kSecurity,
219 &security)) {
220 TranslateWithTableAndSet(security, kWiFiSecurityTable,
221 shill::kSecurityClassProperty);
224 // We currently only support managed and no adhoc networks.
225 shill_dictionary_->SetStringWithoutPathExpansion(shill::kModeProperty,
226 shill::kModeManaged);
228 bool allow_gateway_arp_polling;
229 if (onc_object_->GetBooleanWithoutPathExpansion(
230 ::onc::wifi::kAllowGatewayARPPolling, &allow_gateway_arp_polling)) {
231 shill_dictionary_->SetBooleanWithoutPathExpansion(
232 shill::kLinkMonitorDisableProperty, !allow_gateway_arp_polling);
235 CopyFieldsAccordingToSignature();
238 void LocalTranslator::TranslateEAP() {
239 std::string outer;
240 onc_object_->GetStringWithoutPathExpansion(::onc::eap::kOuter, &outer);
241 TranslateWithTableAndSet(outer, kEAPOuterTable, shill::kEapMethodProperty);
243 // Translate the inner protocol only for outer tunneling protocols.
244 if (outer == ::onc::eap::kPEAP || outer == ::onc::eap::kEAP_TTLS) {
245 // In ONC the Inner protocol defaults to "Automatic".
246 std::string inner = ::onc::eap::kAutomatic;
247 // ONC's Inner == "Automatic" translates to omitting the Phase2 property in
248 // Shill.
249 onc_object_->GetStringWithoutPathExpansion(::onc::eap::kInner, &inner);
250 if (inner != ::onc::eap::kAutomatic) {
251 const StringTranslationEntry* table = outer == ::onc::eap::kPEAP
252 ? kEAP_PEAP_InnerTable
253 : kEAP_TTLS_InnerTable;
254 TranslateWithTableAndSet(inner, table, shill::kEapPhase2AuthProperty);
258 CopyFieldsAccordingToSignature();
261 void LocalTranslator::TranslateNetworkConfiguration() {
262 std::string type;
263 onc_object_->GetStringWithoutPathExpansion(::onc::network_config::kType,
264 &type);
266 // Set the type except for Ethernet which is set in TranslateEthernet.
267 if (type != ::onc::network_type::kEthernet)
268 TranslateWithTableAndSet(type, kNetworkTypeTable, shill::kTypeProperty);
270 // Shill doesn't allow setting the name for non-VPN networks.
271 if (type == ::onc::network_type::kVPN)
272 CopyFieldFromONCToShill(::onc::network_config::kName, shill::kNameProperty);
274 std::string ip_address_config_type, name_servers_config_type;
275 onc_object_->GetStringWithoutPathExpansion(
276 ::onc::network_config::kIPAddressConfigType, &ip_address_config_type);
277 onc_object_->GetStringWithoutPathExpansion(
278 ::onc::network_config::kNameServersConfigType, &name_servers_config_type);
279 if ((ip_address_config_type == ::onc::network_config::kIPConfigTypeDHCP) ||
280 (name_servers_config_type == ::onc::network_config::kIPConfigTypeDHCP)) {
281 // If either type is set to DHCP, provide an empty dictionary to ensure
282 // that any unset properties are cleared. Note: if either type is specified,
283 // the other type defaults to DHCP if not specified.
284 shill_dictionary_->SetWithoutPathExpansion(shill::kStaticIPConfigProperty,
285 new base::DictionaryValue);
287 CopyFieldsAccordingToSignature();
290 void LocalTranslator::CopyFieldsAccordingToSignature() {
291 for (base::DictionaryValue::Iterator it(*onc_object_); !it.IsAtEnd();
292 it.Advance()) {
293 AddValueAccordingToSignature(it.key(),
294 make_scoped_ptr(it.value().DeepCopy()));
298 void LocalTranslator::CopyFieldFromONCToShill(
299 const std::string& onc_field_name,
300 const std::string& shill_property_name) {
301 const base::Value* value = NULL;
302 if (!onc_object_->GetWithoutPathExpansion(onc_field_name, &value))
303 return;
305 const OncFieldSignature* field_signature =
306 GetFieldSignature(*onc_signature_, onc_field_name);
307 if (field_signature) {
308 base::Value::Type expected_type =
309 field_signature->value_signature->onc_type;
310 if (value->GetType() != expected_type) {
311 LOG(ERROR) << "Found field " << onc_field_name << " of type "
312 << value->GetType() << " but expected type " << expected_type;
313 return;
315 } else {
316 LOG(ERROR)
317 << "Attempt to translate a field that is not part of the ONC format.";
318 return;
320 shill_dictionary_->SetWithoutPathExpansion(shill_property_name,
321 value->DeepCopy());
324 void LocalTranslator::AddValueAccordingToSignature(
325 const std::string& onc_name,
326 scoped_ptr<base::Value> value) {
327 if (!value || !field_translation_table_)
328 return;
329 std::string shill_property_name;
330 if (!GetShillPropertyName(onc_name, field_translation_table_,
331 &shill_property_name)) {
332 return;
335 shill_dictionary_->SetWithoutPathExpansion(shill_property_name,
336 value.release());
339 void LocalTranslator::TranslateWithTableAndSet(
340 const std::string& onc_value,
341 const StringTranslationEntry table[],
342 const std::string& shill_property_name) {
343 std::string shill_value;
344 if (TranslateStringToShill(table, onc_value, &shill_value)) {
345 shill_dictionary_->SetStringWithoutPathExpansion(shill_property_name,
346 shill_value);
347 return;
349 // As we previously validate ONC, this case should never occur. If it still
350 // occurs, we should check here. Otherwise the failure will only show up much
351 // later in Shill.
352 LOG(ERROR) << "Value '" << onc_value
353 << "' cannot be translated to Shill property: "
354 << shill_property_name;
357 // Iterates recursively over |onc_object| and its |signature|. At each object
358 // applies the local translation using LocalTranslator::TranslateFields. The
359 // results are written to |shill_dictionary|.
360 void TranslateONCHierarchy(const OncValueSignature& signature,
361 const base::DictionaryValue& onc_object,
362 base::DictionaryValue* shill_dictionary) {
363 base::DictionaryValue* target_shill_dictionary = shill_dictionary;
364 std::vector<std::string> path_to_shill_dictionary =
365 GetPathToNestedShillDictionary(signature);
366 for (std::vector<std::string>::const_iterator it =
367 path_to_shill_dictionary.begin();
368 it != path_to_shill_dictionary.end(); ++it) {
369 base::DictionaryValue* nested_shill_dict = NULL;
370 target_shill_dictionary->GetDictionaryWithoutPathExpansion(
371 *it, &nested_shill_dict);
372 if (!nested_shill_dict) {
373 nested_shill_dict = new base::DictionaryValue;
374 target_shill_dictionary->SetWithoutPathExpansion(*it, nested_shill_dict);
376 target_shill_dictionary = nested_shill_dict;
378 // Translates fields of |onc_object| and writes them to
379 // |target_shill_dictionary_| nested in |shill_dictionary|.
380 LocalTranslator translator(signature, onc_object, target_shill_dictionary);
381 translator.TranslateFields();
383 // Recurse into nested objects.
384 for (base::DictionaryValue::Iterator it(onc_object); !it.IsAtEnd();
385 it.Advance()) {
386 const base::DictionaryValue* inner_object = NULL;
387 if (!it.value().GetAsDictionary(&inner_object))
388 continue;
390 const OncFieldSignature* field_signature =
391 GetFieldSignature(signature, it.key());
393 TranslateONCHierarchy(*field_signature->value_signature, *inner_object,
394 shill_dictionary);
398 } // namespace
400 scoped_ptr<base::DictionaryValue> TranslateONCObjectToShill(
401 const OncValueSignature* onc_signature,
402 const base::DictionaryValue& onc_object) {
403 CHECK(onc_signature != NULL);
404 scoped_ptr<base::DictionaryValue> shill_dictionary(new base::DictionaryValue);
405 TranslateONCHierarchy(*onc_signature, onc_object, shill_dictionary.get());
406 return shill_dictionary.Pass();
409 } // namespace onc
410 } // namespace chromeos