1 // Copyright 2015 The Weave 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.
4 
5 #include "src/config.h"
6 
7 #include <set>
8 
9 #include <base/bind.h>
10 #include <base/guid.h>
11 #include <base/json/json_reader.h>
12 #include <base/json/json_writer.h>
13 #include <base/logging.h>
14 #include <base/strings/string_number_conversions.h>
15 #include <base/values.h>
16 #include <weave/enum_to_string.h>
17 
18 #include "src/data_encoding.h"
19 #include "src/privet/privet_types.h"
20 #include "src/string_utils.h"
21 #include "src/bind_lambda.h"
22 
23 namespace weave {
24 
25 const char kConfigName[] = "config";
26 
27 namespace config_keys {
28 
29 const char kVersion[] = "version";
30 
31 const char kClientId[] = "client_id";
32 const char kClientSecret[] = "client_secret";
33 const char kApiKey[] = "api_key";
34 const char kOAuthURL[] = "oauth_url";
35 const char kServiceURL[] = "service_url";
36 const char kXmppEndpoint[] = "xmpp_endpoint";
37 const char kName[] = "name";
38 const char kDescription[] = "description";
39 const char kLocation[] = "location";
40 const char kLocalAnonymousAccessRole[] = "local_anonymous_access_role";
41 const char kLocalDiscoveryEnabled[] = "local_discovery_enabled";
42 const char kLocalPairingEnabled[] = "local_pairing_enabled";
43 const char kRefreshToken[] = "refresh_token";
44 const char kCloudId[] = "cloud_id";
45 const char kDeviceId[] = "device_id";
46 const char kRobotAccount[] = "robot_account";
47 const char kLastConfiguredSsid[] = "last_configured_ssid";
48 const char kSecret[] = "secret";
49 const char kRootClientTokenOwner[] = "root_client_token_owner";
50 
51 }  // namespace config_keys
52 
53 const char kWeaveUrl[] = "https://www.googleapis.com/weave/v1/";
54 const char kDeprecatedUrl[] = "https://www.googleapis.com/clouddevices/v1/";
55 const char kXmppEndpoint[] = "talk.google.com:5223";
56 
57 namespace {
58 
59 const int kCurrentConfigVersion = 1;
60 
MigrateFromV0(base::DictionaryValue * dict)61 void MigrateFromV0(base::DictionaryValue* dict) {
62   std::string cloud_id;
63   if (dict->GetString(config_keys::kCloudId, &cloud_id) && !cloud_id.empty())
64     return;
65   scoped_ptr<base::Value> tmp;
66   if (dict->Remove(config_keys::kDeviceId, &tmp))
67     dict->Set(config_keys::kCloudId, std::move(tmp));
68 }
69 
CreateDefaultSettings()70 Config::Settings CreateDefaultSettings() {
71   Config::Settings result;
72   result.oauth_url = "https://accounts.google.com/o/oauth2/";
73   result.service_url = kWeaveUrl;
74   result.xmpp_endpoint = kXmppEndpoint;
75   result.local_anonymous_access_role = AuthScope::kViewer;
76   result.pairing_modes.insert(PairingType::kPinCode);
77   result.device_id = base::GenerateGUID();
78   return result;
79 }
80 
81 const EnumToStringMap<RootClientTokenOwner>::Map kRootClientTokenOwnerMap[] = {
82     {RootClientTokenOwner::kNone, "none"},
83     {RootClientTokenOwner::kClient, "client"},
84     {RootClientTokenOwner::kCloud, "cloud"},
85 };
86 
87 }  // namespace
88 
89 template <>
EnumToStringMap()90 LIBWEAVE_EXPORT EnumToStringMap<RootClientTokenOwner>::EnumToStringMap()
91     : EnumToStringMap(kRootClientTokenOwnerMap) {}
92 
Config(provider::ConfigStore * config_store)93 Config::Config(provider::ConfigStore* config_store)
94     : settings_{CreateDefaultSettings()}, config_store_{config_store} {
95   Load();
96 }
97 
AddOnChangedCallback(const OnChangedCallback & callback)98 void Config::AddOnChangedCallback(const OnChangedCallback& callback) {
99   on_changed_.push_back(callback);
100   // Force to read current state.
101   callback.Run(settings_);
102 }
103 
GetSettings() const104 const Config::Settings& Config::GetSettings() const {
105   return settings_;
106 }
107 
Load()108 void Config::Load() {
109   Transaction change{this};
110   change.save_ = false;
111 
112   settings_ = CreateDefaultSettings();
113 
114   if (!config_store_)
115     return;
116 
117   // Crash on any mistakes in defaults.
118   CHECK(config_store_->LoadDefaults(&settings_));
119 
120   CHECK(!settings_.client_id.empty());
121   CHECK(!settings_.client_secret.empty());
122   CHECK(!settings_.api_key.empty());
123   CHECK(!settings_.oauth_url.empty());
124   CHECK(!settings_.service_url.empty());
125   CHECK(!settings_.xmpp_endpoint.empty());
126   CHECK(!settings_.oem_name.empty());
127   CHECK(!settings_.model_name.empty());
128   CHECK(!settings_.model_id.empty());
129   CHECK(!settings_.name.empty());
130   CHECK(!settings_.device_id.empty());
131   CHECK_EQ(settings_.embedded_code.empty(),
132            (settings_.pairing_modes.find(PairingType::kEmbeddedCode) ==
133               settings_.pairing_modes.end()));
134 
135   // Values below will be generated at runtime.
136   CHECK(settings_.cloud_id.empty());
137   CHECK(settings_.refresh_token.empty());
138   CHECK(settings_.robot_account.empty());
139   CHECK(settings_.last_configured_ssid.empty());
140   CHECK(settings_.secret.empty());
141   CHECK(settings_.root_client_token_owner == RootClientTokenOwner::kNone);
142 
143   change.LoadState();
144 }
145 
LoadState()146 void Config::Transaction::LoadState() {
147   if (!config_->config_store_)
148     return;
149   std::string json_string = config_->config_store_->LoadSettings(kConfigName);
150   if (json_string.empty()) {
151     json_string = config_->config_store_->LoadSettings();
152     if (json_string.empty())
153       return;
154   }
155 
156   auto value = base::JSONReader::Read(json_string);
157   base::DictionaryValue* dict = nullptr;
158   if (!value || !value->GetAsDictionary(&dict)) {
159     LOG(ERROR) << "Failed to parse settings.";
160     return;
161   }
162 
163   int loaded_version = 0;
164   dict->GetInteger(config_keys::kVersion, &loaded_version);
165 
166   if (loaded_version != kCurrentConfigVersion) {
167     LOG(INFO) << "State version mismatch. expected: " << kCurrentConfigVersion
168               << ", loaded: " << loaded_version;
169     save_ = true;
170   }
171 
172   if (loaded_version == 0) {
173     MigrateFromV0(dict);
174   }
175 
176   std::string tmp;
177   bool tmp_bool{false};
178 
179   if (dict->GetString(config_keys::kClientId, &tmp))
180     set_client_id(tmp);
181 
182   if (dict->GetString(config_keys::kClientSecret, &tmp))
183     set_client_secret(tmp);
184 
185   if (dict->GetString(config_keys::kApiKey, &tmp))
186     set_api_key(tmp);
187 
188   if (dict->GetString(config_keys::kOAuthURL, &tmp))
189     set_oauth_url(tmp);
190 
191   if (dict->GetString(config_keys::kServiceURL, &tmp)) {
192     if (tmp == kDeprecatedUrl)
193       tmp = kWeaveUrl;
194     set_service_url(tmp);
195   }
196 
197   if (dict->GetString(config_keys::kXmppEndpoint, &tmp)) {
198     set_xmpp_endpoint(tmp);
199   }
200 
201   if (dict->GetString(config_keys::kName, &tmp))
202     set_name(tmp);
203 
204   if (dict->GetString(config_keys::kDescription, &tmp))
205     set_description(tmp);
206 
207   if (dict->GetString(config_keys::kLocation, &tmp))
208     set_location(tmp);
209 
210   AuthScope scope{AuthScope::kNone};
211   if (dict->GetString(config_keys::kLocalAnonymousAccessRole, &tmp) &&
212       StringToEnum(tmp, &scope)) {
213     set_local_anonymous_access_role(scope);
214   }
215 
216   if (dict->GetBoolean(config_keys::kLocalDiscoveryEnabled, &tmp_bool))
217     set_local_discovery_enabled(tmp_bool);
218 
219   if (dict->GetBoolean(config_keys::kLocalPairingEnabled, &tmp_bool))
220     set_local_pairing_enabled(tmp_bool);
221 
222   if (dict->GetString(config_keys::kCloudId, &tmp))
223     set_cloud_id(tmp);
224 
225   if (dict->GetString(config_keys::kDeviceId, &tmp))
226     set_device_id(tmp);
227 
228   if (dict->GetString(config_keys::kRefreshToken, &tmp))
229     set_refresh_token(tmp);
230 
231   if (dict->GetString(config_keys::kRobotAccount, &tmp))
232     set_robot_account(tmp);
233 
234   if (dict->GetString(config_keys::kLastConfiguredSsid, &tmp))
235     set_last_configured_ssid(tmp);
236 
237   std::vector<uint8_t> secret;
238   if (dict->GetString(config_keys::kSecret, &tmp) && Base64Decode(tmp, &secret))
239     set_secret(secret);
240 
241   RootClientTokenOwner token_owner{RootClientTokenOwner::kNone};
242   if (dict->GetString(config_keys::kRootClientTokenOwner, &tmp) &&
243       StringToEnum(tmp, &token_owner)) {
244     set_root_client_token_owner(token_owner);
245   }
246 }
247 
Save()248 void Config::Save() {
249   if (!config_store_)
250     return;
251 
252   base::DictionaryValue dict;
253   dict.SetInteger(config_keys::kVersion, kCurrentConfigVersion);
254 
255   dict.SetString(config_keys::kClientId, settings_.client_id);
256   dict.SetString(config_keys::kClientSecret, settings_.client_secret);
257   dict.SetString(config_keys::kApiKey, settings_.api_key);
258   dict.SetString(config_keys::kOAuthURL, settings_.oauth_url);
259   dict.SetString(config_keys::kServiceURL, settings_.service_url);
260   dict.SetString(config_keys::kXmppEndpoint, settings_.xmpp_endpoint);
261   dict.SetString(config_keys::kRefreshToken, settings_.refresh_token);
262   dict.SetString(config_keys::kCloudId, settings_.cloud_id);
263   dict.SetString(config_keys::kDeviceId, settings_.device_id);
264   dict.SetString(config_keys::kRobotAccount, settings_.robot_account);
265   dict.SetString(config_keys::kLastConfiguredSsid,
266                  settings_.last_configured_ssid);
267   dict.SetString(config_keys::kSecret, Base64Encode(settings_.secret));
268   dict.SetString(config_keys::kRootClientTokenOwner,
269                  EnumToString(settings_.root_client_token_owner));
270   dict.SetString(config_keys::kName, settings_.name);
271   dict.SetString(config_keys::kDescription, settings_.description);
272   dict.SetString(config_keys::kLocation, settings_.location);
273   dict.SetString(config_keys::kLocalAnonymousAccessRole,
274                  EnumToString(settings_.local_anonymous_access_role));
275   dict.SetBoolean(config_keys::kLocalDiscoveryEnabled,
276                   settings_.local_discovery_enabled);
277   dict.SetBoolean(config_keys::kLocalPairingEnabled,
278                   settings_.local_pairing_enabled);
279 
280   std::string json_string;
281   base::JSONWriter::WriteWithOptions(
282       dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string);
283 
284   config_store_->SaveSettings(
285       kConfigName, json_string,
286       base::Bind([](ErrorPtr error) { CHECK(!error); }));
287 }
288 
~Transaction()289 Config::Transaction::~Transaction() {
290   Commit();
291 }
292 
Commit()293 void Config::Transaction::Commit() {
294   if (!config_)
295     return;
296   if (save_)
297     config_->Save();
298   for (const auto& cb : config_->on_changed_)
299     cb.Run(*settings_);
300   config_ = nullptr;
301 }
302 
303 }  // namespace weave
304