Home | History | Annotate | Download | only in src
      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