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