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 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 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 <> 90 LIBWEAVE_EXPORT EnumToStringMap<RootClientTokenOwner>::EnumToStringMap() 91 : EnumToStringMap(kRootClientTokenOwnerMap) {} 92 93 Config::Config(provider::ConfigStore* config_store) 94 : settings_{CreateDefaultSettings()}, config_store_{config_store} { 95 Load(); 96 } 97 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 104 const Config::Settings& Config::GetSettings() const { 105 return settings_; 106 } 107 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 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 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 289 Config::Transaction::~Transaction() { 290 Commit(); 291 } 292 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