Home | History | Annotate | Download | only in privet
      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/privet/privet_handler.h"
      6 
      7 #include <algorithm>
      8 #include <memory>
      9 #include <set>
     10 #include <string>
     11 #include <utility>
     12 
     13 #include <base/bind.h>
     14 #include <base/location.h>
     15 #include <base/strings/stringprintf.h>
     16 #include <base/values.h>
     17 #include <weave/enum_to_string.h>
     18 #include <weave/provider/task_runner.h>
     19 
     20 #include "src/config.h"
     21 #include "src/http_constants.h"
     22 #include "src/privet/cloud_delegate.h"
     23 #include "src/privet/constants.h"
     24 #include "src/privet/device_delegate.h"
     25 #include "src/privet/device_ui_kind.h"
     26 #include "src/privet/security_delegate.h"
     27 #include "src/privet/wifi_delegate.h"
     28 #include "src/string_utils.h"
     29 #include "src/utils.h"
     30 
     31 namespace weave {
     32 namespace privet {
     33 
     34 namespace {
     35 
     36 const char kInfoVersionKey[] = "version";
     37 const char kInfoVersionValue[] = "3.0";
     38 
     39 const char kNameKey[] = "name";
     40 const char kDescrptionKey[] = "description";
     41 const char kLocationKey[] = "location";
     42 
     43 const char kGcdKey[] = "gcd";
     44 const char kWifiKey[] = "wifi";
     45 const char kStatusKey[] = "status";
     46 const char kErrorKey[] = "error";
     47 const char kCryptoKey[] = "crypto";
     48 const char kStatusErrorValue[] = "error";
     49 
     50 const char kInfoIdKey[] = "id";
     51 const char kInfoServicesKey[] = "services";
     52 
     53 const char kInfoEndpointsKey[] = "endpoints";
     54 const char kInfoEndpointsHttpPortKey[] = "httpPort";
     55 const char kInfoEndpointsHttpUpdatePortKey[] = "httpUpdatesPort";
     56 const char kInfoEndpointsHttpsPortKey[] = "httpsPort";
     57 const char kInfoEndpointsHttpsUpdatePortKey[] = "httpsUpdatesPort";
     58 
     59 const char kInfoModelIdKey[] = "modelManifestId";
     60 const char kInfoModelManifestKey[] = "basicModelManifest";
     61 const char kInfoManifestUiDeviceKind[] = "uiDeviceKind";
     62 const char kInfoManifestOemName[] = "oemName";
     63 const char kInfoManifestModelName[] = "modelName";
     64 
     65 const char kInfoAuthenticationKey[] = "authentication";
     66 
     67 const char kInfoAuthAnonymousMaxScopeKey[] = "anonymousMaxScope";
     68 
     69 const char kInfoWifiCapabilitiesKey[] = "capabilities";
     70 const char kInfoWifiSsidKey[] = "ssid";
     71 const char kInfoWifiHostedSsidKey[] = "hostedSsid";
     72 const char kInfoTimeKey[] = "time";
     73 const char kInfoSessionIdKey[] = "sessionId";
     74 
     75 const char kPairingKey[] = "pairing";
     76 const char kPairingSessionIdKey[] = "sessionId";
     77 const char kPairingDeviceCommitmentKey[] = "deviceCommitment";
     78 const char kPairingClientCommitmentKey[] = "clientCommitment";
     79 const char kPairingFingerprintKey[] = "certFingerprint";
     80 const char kPairingSignatureKey[] = "certSignature";
     81 
     82 const char kAuthModeKey[] = "mode";
     83 const char kAuthCodeKey[] = "authCode";
     84 const char kAuthRequestedScopeKey[] = "requestedScope";
     85 const char kAuthScopeAutoValue[] = "auto";
     86 
     87 const char kAuthAccessTokenKey[] = "accessToken";
     88 const char kAuthTokenTypeKey[] = "tokenType";
     89 const char kAuthExpiresInKey[] = "expiresIn";
     90 const char kAuthScopeKey[] = "scope";
     91 const char kAuthClientTokenKey[] = "clientToken";
     92 
     93 const char kAuthorizationHeaderPrefix[] = "Privet";
     94 
     95 const char kErrorDebugInfoKey[] = "debugInfo";
     96 
     97 const char kSetupStartSsidKey[] = "ssid";
     98 const char kSetupStartPassKey[] = "passphrase";
     99 const char kSetupStartTicketIdKey[] = "ticketId";
    100 const char kSetupStartUserKey[] = "user";
    101 
    102 const char kFingerprintKey[] = "fingerprint";
    103 const char kStateKey[] = "state";
    104 const char kCommandsKey[] = "commands";
    105 const char kTraitsKey[] = "traits";
    106 const char kComponentsKey[] = "components";
    107 const char kCommandsIdKey[] = "id";
    108 const char kPathKey[] = "path";
    109 const char kFilterKey[] = "filter";
    110 
    111 const char kStateFingerprintKey[] = "stateFingerprint";
    112 const char kCommandsFingerprintKey[] = "commandsFingerprint";
    113 const char kTraitsFingerprintKey[] = "traitsFingerprint";
    114 const char kComponentsFingerprintKey[] = "componentsFingerprint";
    115 const char kWaitTimeoutKey[] = "waitTimeout";
    116 
    117 const char kInvalidParamValueFormat[] = "Invalid parameter: '%s'='%s'";
    118 
    119 template <class Container>
    120 std::unique_ptr<base::ListValue> ToValue(const Container& list) {
    121   std::unique_ptr<base::ListValue> value_list(new base::ListValue());
    122   for (const std::string& val : list)
    123     value_list->AppendString(val);
    124   return value_list;
    125 }
    126 
    127 struct {
    128   const char* const reason;
    129   int code;
    130 } kReasonToCode[] = {
    131     {errors::kInvalidClientCommitment, http::kForbidden},
    132     {errors::kInvalidFormat, http::kBadRequest},
    133     {errors::kMissingAuthorization, http::kDenied},
    134     {errors::kInvalidAuthorization, http::kDenied},
    135     {errors::kInvalidAuthorizationScope, http::kForbidden},
    136     {errors::kAuthorizationExpired, http::kForbidden},
    137     {errors::kCommitmentMismatch, http::kForbidden},
    138     {errors::kUnknownSession, http::kNotFound},
    139     {errors::kInvalidAuthCode, http::kForbidden},
    140     {errors::kInvalidAuthMode, http::kBadRequest},
    141     {errors::kInvalidRequestedScope, http::kBadRequest},
    142     {errors::kAccessDenied, http::kForbidden},
    143     {errors::kInvalidParams, http::kBadRequest},
    144     {errors::kSetupUnavailable, http::kBadRequest},
    145     {errors::kDeviceBusy, http::kServiceUnavailable},
    146     {errors::kInvalidState, http::kInternalServerError},
    147     {errors::kNotFound, http::kNotFound},
    148     {errors::kNotImplemented, http::kNotSupported},
    149     {errors::kAlreadyClaimed, http::kDenied},
    150 };
    151 
    152 std::string GetAuthTokenFromAuthHeader(const std::string& auth_header) {
    153   return SplitAtFirst(auth_header, " ", true).second;
    154 }
    155 
    156 // Creates JSON similar to GCD server error format.
    157 std::unique_ptr<base::DictionaryValue> ErrorToJson(const Error& error) {
    158   std::unique_ptr<base::DictionaryValue> output{ErrorInfoToJson(error)};
    159 
    160   // Optional debug information.
    161   std::unique_ptr<base::ListValue> errors{new base::ListValue};
    162   for (const Error* it = &error; it; it = it->GetInnerError()) {
    163     std::unique_ptr<base::DictionaryValue> inner{ErrorInfoToJson(*it)};
    164     tracked_objects::Location location{it->GetLocation().function_name.c_str(),
    165                                        it->GetLocation().file_name.c_str(),
    166                                        it->GetLocation().line_number, nullptr};
    167     inner->SetString(kErrorDebugInfoKey, location.ToString());
    168     errors->Append(inner.release());
    169   }
    170   output->Set(kErrorDebugInfoKey, errors.release());
    171   return output;
    172 }
    173 
    174 template <class T>
    175 void SetStateProperties(const T& state, base::DictionaryValue* parent) {
    176   if (!state.error()) {
    177     parent->SetString(kStatusKey, EnumToString(state.status()));
    178     return;
    179   }
    180   parent->SetString(kStatusKey, kStatusErrorValue);
    181   parent->Set(kErrorKey, ErrorToJson(*state.error()).release());
    182 }
    183 
    184 void ReturnError(const Error& error,
    185                  const PrivetHandler::RequestCallback& callback) {
    186   int code = http::kInternalServerError;
    187   for (const auto& it : kReasonToCode) {
    188     if (error.HasError(it.reason)) {
    189       code = it.code;
    190       break;
    191     }
    192   }
    193   std::unique_ptr<base::DictionaryValue> output{new base::DictionaryValue};
    194   output->Set(kErrorKey, ErrorToJson(error).release());
    195   callback.Run(code, *output);
    196 }
    197 
    198 void OnCommandRequestSucceeded(const PrivetHandler::RequestCallback& callback,
    199                                const base::DictionaryValue& output,
    200                                ErrorPtr error) {
    201   if (!error)
    202     return callback.Run(http::kOk, output);
    203 
    204   if (error->HasError("unknown_command")) {
    205     Error::AddTo(&error, FROM_HERE, errors::kNotFound, "Unknown command ID");
    206     return ReturnError(*error, callback);
    207   }
    208   if (error->HasError("access_denied")) {
    209     Error::AddTo(&error, FROM_HERE, errors::kAccessDenied, error->GetMessage());
    210     return ReturnError(*error, callback);
    211   }
    212   return ReturnError(*error, callback);
    213 }
    214 
    215 std::unique_ptr<base::DictionaryValue> CreateManifestSection(
    216     const CloudDelegate& cloud) {
    217   std::unique_ptr<base::DictionaryValue> manifest(new base::DictionaryValue());
    218   manifest->SetString(kInfoManifestUiDeviceKind,
    219                       GetDeviceUiKind(cloud.GetModelId()));
    220   manifest->SetString(kInfoManifestOemName, cloud.GetOemName());
    221   manifest->SetString(kInfoManifestModelName, cloud.GetModelName());
    222   return manifest;
    223 }
    224 
    225 std::unique_ptr<base::DictionaryValue> CreateEndpointsSection(
    226     const DeviceDelegate& device) {
    227   std::unique_ptr<base::DictionaryValue> endpoints(new base::DictionaryValue());
    228   auto http_endpoint = device.GetHttpEnpoint();
    229   endpoints->SetInteger(kInfoEndpointsHttpPortKey, http_endpoint.first);
    230   endpoints->SetInteger(kInfoEndpointsHttpUpdatePortKey, http_endpoint.second);
    231 
    232   auto https_endpoint = device.GetHttpsEnpoint();
    233   endpoints->SetInteger(kInfoEndpointsHttpsPortKey, https_endpoint.first);
    234   endpoints->SetInteger(kInfoEndpointsHttpsUpdatePortKey,
    235                         https_endpoint.second);
    236 
    237   return endpoints;
    238 }
    239 
    240 std::unique_ptr<base::DictionaryValue> CreateInfoAuthSection(
    241     const SecurityDelegate& security,
    242     AuthScope anonymous_max_scope) {
    243   std::unique_ptr<base::DictionaryValue> auth(new base::DictionaryValue());
    244 
    245   auth->SetString(kInfoAuthAnonymousMaxScopeKey,
    246                   EnumToString(anonymous_max_scope));
    247 
    248   std::unique_ptr<base::ListValue> pairing_types(new base::ListValue());
    249   for (PairingType type : security.GetPairingTypes())
    250     pairing_types->AppendString(EnumToString(type));
    251   auth->Set(kPairingKey, pairing_types.release());
    252 
    253   std::unique_ptr<base::ListValue> auth_types(new base::ListValue());
    254   for (AuthType type : security.GetAuthTypes())
    255     auth_types->AppendString(EnumToString(type));
    256   auth->Set(kAuthModeKey, auth_types.release());
    257 
    258   std::unique_ptr<base::ListValue> crypto_types(new base::ListValue());
    259   for (CryptoType type : security.GetCryptoTypes())
    260     crypto_types->AppendString(EnumToString(type));
    261   auth->Set(kCryptoKey, crypto_types.release());
    262 
    263   return auth;
    264 }
    265 
    266 std::unique_ptr<base::DictionaryValue> CreateWifiSection(
    267     const WifiDelegate& wifi) {
    268   std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
    269 
    270   std::unique_ptr<base::ListValue> capabilities(new base::ListValue());
    271   for (WifiType type : wifi.GetTypes())
    272     capabilities->AppendString(EnumToString(type));
    273   result->Set(kInfoWifiCapabilitiesKey, capabilities.release());
    274 
    275   result->SetString(kInfoWifiSsidKey, wifi.GetCurrentlyConnectedSsid());
    276 
    277   std::string hosted_ssid = wifi.GetHostedSsid();
    278   const ConnectionState& state = wifi.GetConnectionState();
    279   if (!hosted_ssid.empty()) {
    280     DCHECK(!state.IsStatusEqual(ConnectionState::kDisabled));
    281     DCHECK(!state.IsStatusEqual(ConnectionState::kOnline));
    282     result->SetString(kInfoWifiHostedSsidKey, hosted_ssid);
    283   }
    284   SetStateProperties(state, result.get());
    285   return result;
    286 }
    287 
    288 std::unique_ptr<base::DictionaryValue> CreateGcdSection(
    289     const CloudDelegate& cloud) {
    290   std::unique_ptr<base::DictionaryValue> gcd(new base::DictionaryValue());
    291   gcd->SetString(kInfoIdKey, cloud.GetCloudId());
    292   SetStateProperties(cloud.GetConnectionState(), gcd.get());
    293   return gcd;
    294 }
    295 
    296 AuthScope GetAnonymousMaxScope(const CloudDelegate& cloud,
    297                                const WifiDelegate* wifi) {
    298   if (wifi && !wifi->GetHostedSsid().empty())
    299     return AuthScope::kNone;
    300   return cloud.GetAnonymousMaxScope();
    301 }
    302 
    303 // Forward-declaration.
    304 std::unique_ptr<base::DictionaryValue> CloneComponentTree(
    305     const base::DictionaryValue& parent,
    306     const std::set<std::string>& filter);
    307 
    308 // Clones a particular component JSON object in a manner similar to that of
    309 // DeepCopy(), except it includes only sub-objects specified in |filter| (if not
    310 // empty) and has special handling for "components" sub-dictionary.
    311 std::unique_ptr<base::DictionaryValue> CloneComponent(
    312     const base::DictionaryValue& component,
    313     const std::set<std::string>& filter) {
    314   std::unique_ptr<base::DictionaryValue> clone{new base::DictionaryValue};
    315   for (base::DictionaryValue::Iterator it(component); !it.IsAtEnd();
    316        it.Advance()) {
    317     if (filter.empty() || filter.find(it.key()) != filter.end()) {
    318       if (it.key() == kComponentsKey) {
    319         // Handle "components" separately as we need to recursively clone
    320         // sub-components.
    321         const base::DictionaryValue* sub_components = nullptr;
    322         CHECK(it.value().GetAsDictionary(&sub_components));
    323         clone->SetWithoutPathExpansion(
    324             it.key(), CloneComponentTree(*sub_components, filter).release());
    325       } else {
    326         clone->SetWithoutPathExpansion(it.key(), it.value().DeepCopy());
    327       }
    328     }
    329   }
    330   return clone;
    331 }
    332 
    333 // Clones a dictionary containing a bunch of component JSON objects in a manner
    334 // similar to that of DeepCopy(). Calls CloneComponent() on each instance of
    335 // the component sub-object.
    336 std::unique_ptr<base::DictionaryValue> CloneComponentTree(
    337     const base::DictionaryValue& parent,
    338     const std::set<std::string>& filter) {
    339   std::unique_ptr<base::DictionaryValue> clone{new base::DictionaryValue};
    340   for (base::DictionaryValue::Iterator it(parent); !it.IsAtEnd();
    341        it.Advance()) {
    342     const base::DictionaryValue* component = nullptr;
    343     CHECK(it.value().GetAsDictionary(&component));
    344     clone->SetWithoutPathExpansion(
    345         it.key(), CloneComponent(*component, filter).release());
    346   }
    347   return clone;
    348 }
    349 
    350 }  // namespace
    351 
    352 std::vector<std::string> PrivetHandler::GetHttpPaths() const {
    353   std::vector<std::string> result;
    354   for (const auto& pair : handlers_) {
    355     if (!pair.second.https_only)
    356       result.push_back(pair.first);
    357   }
    358   return result;
    359 }
    360 
    361 std::vector<std::string> PrivetHandler::GetHttpsPaths() const {
    362   std::vector<std::string> result;
    363   for (const auto& pair : handlers_)
    364     result.push_back(pair.first);
    365   return result;
    366 }
    367 
    368 PrivetHandler::PrivetHandler(CloudDelegate* cloud,
    369                              DeviceDelegate* device,
    370                              SecurityDelegate* security,
    371                              WifiDelegate* wifi,
    372                              base::Clock* clock)
    373     : cloud_(cloud),
    374       device_(device),
    375       security_(security),
    376       wifi_(wifi),
    377       clock_(clock ? clock : &default_clock_) {
    378   CHECK(cloud_);
    379   CHECK(device_);
    380   CHECK(security_);
    381   CHECK(clock_);
    382   cloud_observer_.Add(cloud_);
    383 
    384   AddHandler("/privet/info", &PrivetHandler::HandleInfo, AuthScope::kNone);
    385   AddHandler("/privet/v3/pairing/start", &PrivetHandler::HandlePairingStart,
    386              AuthScope::kNone);
    387   AddHandler("/privet/v3/pairing/confirm", &PrivetHandler::HandlePairingConfirm,
    388              AuthScope::kNone);
    389   AddHandler("/privet/v3/pairing/cancel", &PrivetHandler::HandlePairingCancel,
    390              AuthScope::kNone);
    391 
    392   AddSecureHandler("/privet/v3/auth", &PrivetHandler::HandleAuth,
    393                    AuthScope::kNone);
    394   AddSecureHandler("/privet/v3/accessControl/claim",
    395                    &PrivetHandler::HandleAccessControlClaim, AuthScope::kOwner);
    396   AddSecureHandler("/privet/v3/accessControl/confirm",
    397                    &PrivetHandler::HandleAccessControlConfirm,
    398                    AuthScope::kOwner);
    399   AddSecureHandler("/privet/v3/setup/start", &PrivetHandler::HandleSetupStart,
    400                    AuthScope::kManager);
    401   AddSecureHandler("/privet/v3/setup/status", &PrivetHandler::HandleSetupStatus,
    402                    AuthScope::kManager);
    403   AddSecureHandler("/privet/v3/state", &PrivetHandler::HandleState,
    404                    AuthScope::kViewer);
    405   AddSecureHandler("/privet/v3/commandDefs", &PrivetHandler::HandleCommandDefs,
    406                    AuthScope::kViewer);
    407   AddSecureHandler("/privet/v3/commands/execute",
    408                    &PrivetHandler::HandleCommandsExecute, AuthScope::kViewer);
    409   AddSecureHandler("/privet/v3/commands/status",
    410                    &PrivetHandler::HandleCommandsStatus, AuthScope::kViewer);
    411   AddSecureHandler("/privet/v3/commands/cancel",
    412                    &PrivetHandler::HandleCommandsCancel, AuthScope::kViewer);
    413   AddSecureHandler("/privet/v3/commands/list",
    414                    &PrivetHandler::HandleCommandsList, AuthScope::kViewer);
    415   AddSecureHandler("/privet/v3/checkForUpdates",
    416                    &PrivetHandler::HandleCheckForUpdates, AuthScope::kViewer);
    417   AddSecureHandler("/privet/v3/traits", &PrivetHandler::HandleTraits,
    418                    AuthScope::kViewer);
    419   AddSecureHandler("/privet/v3/components", &PrivetHandler::HandleComponents,
    420                    AuthScope::kViewer);
    421 }
    422 
    423 PrivetHandler::~PrivetHandler() {
    424   for (const auto& req : update_requests_)
    425     ReplyToUpdateRequest(req.callback);
    426 }
    427 
    428 void PrivetHandler::OnTraitDefsChanged() {
    429   ++traits_fingerprint_;
    430   auto pred = [this](const UpdateRequestParameters& params) {
    431     return params.traits_fingerprint == 0;
    432   };
    433   auto last =
    434       std::partition(update_requests_.begin(), update_requests_.end(), pred);
    435   for (auto p = last; p != update_requests_.end(); ++p)
    436     ReplyToUpdateRequest(p->callback);
    437   update_requests_.erase(last, update_requests_.end());
    438 }
    439 
    440 void PrivetHandler::OnStateChanged() {
    441   // State updates also change the component tree, so update both fingerprints.
    442   ++state_fingerprint_;
    443   ++components_fingerprint_;
    444   auto pred = [this](const UpdateRequestParameters& params) {
    445     return params.state_fingerprint == 0 && params.components_fingerprint == 0;
    446   };
    447   auto last =
    448       std::partition(update_requests_.begin(), update_requests_.end(), pred);
    449   for (auto p = last; p != update_requests_.end(); ++p)
    450     ReplyToUpdateRequest(p->callback);
    451   update_requests_.erase(last, update_requests_.end());
    452 }
    453 
    454 void PrivetHandler::OnComponentTreeChanged() {
    455   ++components_fingerprint_;
    456   auto pred = [this](const UpdateRequestParameters& params) {
    457     return params.components_fingerprint == 0;
    458   };
    459   auto last =
    460       std::partition(update_requests_.begin(), update_requests_.end(), pred);
    461   for (auto p = last; p != update_requests_.end(); ++p)
    462     ReplyToUpdateRequest(p->callback);
    463   update_requests_.erase(last, update_requests_.end());
    464 }
    465 
    466 void PrivetHandler::HandleRequest(const std::string& api,
    467                                   const std::string& auth_header,
    468                                   const base::DictionaryValue* input,
    469                                   const RequestCallback& callback) {
    470   ErrorPtr error;
    471   if (!input) {
    472     Error::AddTo(&error, FROM_HERE, errors::kInvalidFormat, "Malformed JSON");
    473     return ReturnError(*error, callback);
    474   }
    475   auto handler = handlers_.find(api);
    476   if (handler == handlers_.end()) {
    477     Error::AddTo(&error, FROM_HERE, errors::kNotFound, "Path not found");
    478     return ReturnError(*error, callback);
    479   }
    480   if (auth_header.empty()) {
    481     Error::AddTo(&error, FROM_HERE, errors::kMissingAuthorization,
    482                  "Authorization header must not be empty");
    483     return ReturnError(*error, callback);
    484   }
    485   std::string token = GetAuthTokenFromAuthHeader(auth_header);
    486   if (token.empty()) {
    487     Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidAuthorization,
    488                        "Invalid authorization header: %s", auth_header.c_str());
    489     return ReturnError(*error, callback);
    490   }
    491   UserInfo user_info;
    492   if (token != EnumToString(AuthType::kAnonymous)) {
    493     if (!security_->ParseAccessToken(token, &user_info, &error))
    494       return ReturnError(*error, callback);
    495   }
    496 
    497   if (handler->second.scope > user_info.scope()) {
    498     Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidAuthorizationScope,
    499                        "Scope '%s' does not allow '%s'",
    500                        EnumToString(user_info.scope()).c_str(), api.c_str());
    501     return ReturnError(*error, callback);
    502   }
    503   (this->*handler->second.handler)(*input, user_info, callback);
    504 }
    505 
    506 void PrivetHandler::AddHandler(const std::string& path,
    507                                ApiHandler handler,
    508                                AuthScope scope) {
    509   HandlerParameters params;
    510   params.handler = handler;
    511   params.scope = scope;
    512   params.https_only = false;
    513   CHECK(handlers_.insert(std::make_pair(path, params)).second);
    514 }
    515 
    516 void PrivetHandler::AddSecureHandler(const std::string& path,
    517                                      ApiHandler handler,
    518                                      AuthScope scope) {
    519   HandlerParameters params;
    520   params.handler = handler;
    521   params.scope = scope;
    522   params.https_only = true;
    523   CHECK(handlers_.insert(std::make_pair(path, params)).second);
    524 }
    525 
    526 void PrivetHandler::HandleInfo(const base::DictionaryValue&,
    527                                const UserInfo& user_info,
    528                                const RequestCallback& callback) {
    529   base::DictionaryValue output;
    530 
    531   std::string name = cloud_->GetName();
    532   std::string model_id = cloud_->GetModelId();
    533 
    534   output.SetString(kInfoVersionKey, kInfoVersionValue);
    535   output.SetString(kInfoIdKey, cloud_->GetDeviceId());
    536   output.SetString(kNameKey, name);
    537 
    538   std::string description{cloud_->GetDescription()};
    539   if (!description.empty())
    540     output.SetString(kDescrptionKey, description);
    541 
    542   std::string location{cloud_->GetLocation()};
    543   if (!location.empty())
    544     output.SetString(kLocationKey, location);
    545 
    546   output.SetString(kInfoModelIdKey, model_id);
    547   output.Set(kInfoModelManifestKey, CreateManifestSection(*cloud_).release());
    548   output.Set(
    549       kInfoServicesKey,
    550       ToValue(std::vector<std::string>{GetDeviceUiKind(cloud_->GetModelId())})
    551           .release());
    552 
    553   output.Set(
    554       kInfoAuthenticationKey,
    555       CreateInfoAuthSection(*security_, GetAnonymousMaxScope(*cloud_, wifi_))
    556           .release());
    557 
    558   output.Set(kInfoEndpointsKey, CreateEndpointsSection(*device_).release());
    559 
    560   if (wifi_)
    561     output.Set(kWifiKey, CreateWifiSection(*wifi_).release());
    562 
    563   output.Set(kGcdKey, CreateGcdSection(*cloud_).release());
    564 
    565   output.SetDouble(kInfoTimeKey, clock_->Now().ToJsTime());
    566   output.SetString(kInfoSessionIdKey, security_->CreateSessionId());
    567 
    568   callback.Run(http::kOk, output);
    569 }
    570 
    571 void PrivetHandler::HandlePairingStart(const base::DictionaryValue& input,
    572                                        const UserInfo& user_info,
    573                                        const RequestCallback& callback) {
    574   ErrorPtr error;
    575 
    576   std::string pairing_str;
    577   input.GetString(kPairingKey, &pairing_str);
    578 
    579   std::string crypto_str;
    580   input.GetString(kCryptoKey, &crypto_str);
    581 
    582   PairingType pairing;
    583   std::set<PairingType> modes = security_->GetPairingTypes();
    584   if (!StringToEnum(pairing_str, &pairing) ||
    585       modes.find(pairing) == modes.end()) {
    586     Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidParams,
    587                        kInvalidParamValueFormat, kPairingKey,
    588                        pairing_str.c_str());
    589     return ReturnError(*error, callback);
    590   }
    591 
    592   CryptoType crypto;
    593   std::set<CryptoType> cryptos = security_->GetCryptoTypes();
    594   if (!StringToEnum(crypto_str, &crypto) ||
    595       cryptos.find(crypto) == cryptos.end()) {
    596     Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidParams,
    597                        kInvalidParamValueFormat, kCryptoKey,
    598                        crypto_str.c_str());
    599     return ReturnError(*error, callback);
    600   }
    601 
    602   std::string id;
    603   std::string commitment;
    604   if (!security_->StartPairing(pairing, crypto, &id, &commitment, &error))
    605     return ReturnError(*error, callback);
    606 
    607   base::DictionaryValue output;
    608   output.SetString(kPairingSessionIdKey, id);
    609   output.SetString(kPairingDeviceCommitmentKey, commitment);
    610   callback.Run(http::kOk, output);
    611 }
    612 
    613 void PrivetHandler::HandlePairingConfirm(const base::DictionaryValue& input,
    614                                          const UserInfo& user_info,
    615                                          const RequestCallback& callback) {
    616   std::string id;
    617   input.GetString(kPairingSessionIdKey, &id);
    618 
    619   std::string commitment;
    620   input.GetString(kPairingClientCommitmentKey, &commitment);
    621 
    622   std::string fingerprint;
    623   std::string signature;
    624   ErrorPtr error;
    625   if (!security_->ConfirmPairing(id, commitment, &fingerprint, &signature,
    626                                  &error)) {
    627     return ReturnError(*error, callback);
    628   }
    629 
    630   base::DictionaryValue output;
    631   output.SetString(kPairingFingerprintKey, fingerprint);
    632   output.SetString(kPairingSignatureKey, signature);
    633   callback.Run(http::kOk, output);
    634 }
    635 
    636 void PrivetHandler::HandlePairingCancel(const base::DictionaryValue& input,
    637                                         const UserInfo& user_info,
    638                                         const RequestCallback& callback) {
    639   std::string id;
    640   input.GetString(kPairingSessionIdKey, &id);
    641 
    642   ErrorPtr error;
    643   if (!security_->CancelPairing(id, &error))
    644     return ReturnError(*error, callback);
    645 
    646   base::DictionaryValue output;
    647   callback.Run(http::kOk, output);
    648 }
    649 
    650 void PrivetHandler::HandleAuth(const base::DictionaryValue& input,
    651                                const UserInfo& user_info,
    652                                const RequestCallback& callback) {
    653   ErrorPtr error;
    654 
    655   std::string auth_code_type;
    656   AuthType auth_type{};
    657   if (!input.GetString(kAuthModeKey, &auth_code_type) ||
    658       !StringToEnum(auth_code_type, &auth_type)) {
    659     Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidAuthMode,
    660                        kInvalidParamValueFormat, kAuthModeKey,
    661                        auth_code_type.c_str());
    662     return ReturnError(*error, callback);
    663   }
    664 
    665   AuthScope desired_scope = AuthScope::kOwner;
    666   AuthScope acceptable_scope = AuthScope::kViewer;
    667 
    668   std::string requested_scope;
    669   input.GetString(kAuthRequestedScopeKey, &requested_scope);
    670   if (requested_scope != kAuthScopeAutoValue) {
    671     if (!StringToEnum(requested_scope, &desired_scope)) {
    672       Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidRequestedScope,
    673                          kInvalidParamValueFormat, kAuthRequestedScopeKey,
    674                          requested_scope.c_str());
    675       return ReturnError(*error, callback);
    676     }
    677     acceptable_scope = std::max(desired_scope, acceptable_scope);
    678   }
    679 
    680   if (auth_type == AuthType::kAnonymous)
    681     desired_scope = GetAnonymousMaxScope(*cloud_, wifi_);
    682 
    683   std::string auth_code;
    684   input.GetString(kAuthCodeKey, &auth_code);
    685 
    686   std::string access_token;
    687   base::TimeDelta access_token_ttl;
    688   AuthScope access_token_scope = AuthScope::kNone;
    689   if (!security_->CreateAccessToken(auth_type, auth_code, desired_scope,
    690                                     &access_token, &access_token_scope,
    691                                     &access_token_ttl, &error)) {
    692     return ReturnError(*error, callback);
    693   }
    694 
    695   if (access_token_scope < acceptable_scope) {
    696     Error::AddToPrintf(&error, FROM_HERE, errors::kAccessDenied,
    697                        "Scope '%s' is not allowed",
    698                        EnumToString(access_token_scope).c_str());
    699     return ReturnError(*error, callback);
    700   }
    701 
    702   base::DictionaryValue output;
    703   output.SetString(kAuthAccessTokenKey, access_token);
    704   output.SetString(kAuthTokenTypeKey, kAuthorizationHeaderPrefix);
    705   output.SetInteger(kAuthExpiresInKey, access_token_ttl.InSeconds());
    706   output.SetString(kAuthScopeKey, EnumToString(access_token_scope));
    707 
    708   callback.Run(http::kOk, output);
    709 }
    710 
    711 void PrivetHandler::HandleAccessControlClaim(const base::DictionaryValue& input,
    712                                              const UserInfo& user_info,
    713                                              const RequestCallback& callback) {
    714   ErrorPtr error;
    715   auto token = security_->ClaimRootClientAuthToken(&error);
    716   if (token.empty())
    717     return ReturnError(*error, callback);
    718 
    719   base::DictionaryValue output;
    720   output.SetString(kAuthClientTokenKey, token);
    721   callback.Run(http::kOk, output);
    722 }
    723 
    724 void PrivetHandler::HandleAccessControlConfirm(
    725     const base::DictionaryValue& input,
    726     const UserInfo& user_info,
    727     const RequestCallback& callback) {
    728   ErrorPtr error;
    729 
    730   std::string token;
    731   if (!input.GetString(kAuthClientTokenKey, &token)) {
    732     Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidParams,
    733                        kInvalidParamValueFormat, kAuthClientTokenKey,
    734                        token.c_str());
    735     return ReturnError(*error, callback);
    736   }
    737 
    738   if (!security_->ConfirmClientAuthToken(token, &error))
    739     return ReturnError(*error, callback);
    740 
    741   base::DictionaryValue output;
    742   callback.Run(http::kOk, output);
    743 }
    744 
    745 void PrivetHandler::HandleSetupStart(const base::DictionaryValue& input,
    746                                      const UserInfo& user_info,
    747                                      const RequestCallback& callback) {
    748   std::string name{cloud_->GetName()};
    749   input.GetString(kNameKey, &name);
    750 
    751   std::string description{cloud_->GetDescription()};
    752   input.GetString(kDescrptionKey, &description);
    753 
    754   std::string location{cloud_->GetLocation()};
    755   input.GetString(kLocationKey, &location);
    756 
    757   std::string ssid;
    758   std::string passphrase;
    759   std::string ticket;
    760   std::string user;
    761 
    762   const base::DictionaryValue* wifi = nullptr;
    763   if (input.GetDictionary(kWifiKey, &wifi)) {
    764     if (!wifi_ || wifi_->GetTypes().empty()) {
    765       ErrorPtr error;
    766       Error::AddTo(&error, FROM_HERE, errors::kSetupUnavailable,
    767                    "WiFi setup unavailable");
    768       return ReturnError(*error, callback);
    769     }
    770     wifi->GetString(kSetupStartSsidKey, &ssid);
    771     if (ssid.empty()) {
    772       ErrorPtr error;
    773       Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidParams,
    774                          kInvalidParamValueFormat, kSetupStartSsidKey, "");
    775       return ReturnError(*error, callback);
    776     }
    777     wifi->GetString(kSetupStartPassKey, &passphrase);
    778   }
    779 
    780   const base::DictionaryValue* registration = nullptr;
    781   if (input.GetDictionary(kGcdKey, &registration)) {
    782     if (user_info.scope() < AuthScope::kOwner) {
    783       ErrorPtr error;
    784       Error::AddTo(&error, FROM_HERE, errors::kInvalidAuthorizationScope,
    785                    "Only owner can register device");
    786       return ReturnError(*error, callback);
    787     }
    788     registration->GetString(kSetupStartTicketIdKey, &ticket);
    789     if (ticket.empty()) {
    790       ErrorPtr error;
    791       Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidParams,
    792                          kInvalidParamValueFormat, kSetupStartTicketIdKey, "");
    793       return ReturnError(*error, callback);
    794     }
    795     registration->GetString(kSetupStartUserKey, &user);
    796   }
    797 
    798   cloud_->UpdateDeviceInfo(name, description, location);
    799 
    800   ErrorPtr error;
    801   if (!ssid.empty() && !wifi_->ConfigureCredentials(ssid, passphrase, &error))
    802     return ReturnError(*error, callback);
    803 
    804   if (!ticket.empty() && !cloud_->Setup(ticket, user, &error))
    805     return ReturnError(*error, callback);
    806 
    807   ReplyWithSetupStatus(callback);
    808 }
    809 
    810 void PrivetHandler::HandleSetupStatus(const base::DictionaryValue&,
    811                                       const UserInfo& user_info,
    812                                       const RequestCallback& callback) {
    813   ReplyWithSetupStatus(callback);
    814 }
    815 
    816 void PrivetHandler::ReplyWithSetupStatus(
    817     const RequestCallback& callback) const {
    818   base::DictionaryValue output;
    819 
    820   const SetupState& state = cloud_->GetSetupState();
    821   if (!state.IsStatusEqual(SetupState::kNone)) {
    822     base::DictionaryValue* gcd = new base::DictionaryValue;
    823     output.Set(kGcdKey, gcd);
    824     SetStateProperties(state, gcd);
    825     if (state.IsStatusEqual(SetupState::kSuccess))
    826       gcd->SetString(kInfoIdKey, cloud_->GetCloudId());
    827   }
    828 
    829   if (wifi_) {
    830     const SetupState& state = wifi_->GetSetupState();
    831     if (!state.IsStatusEqual(SetupState::kNone)) {
    832       base::DictionaryValue* wifi = new base::DictionaryValue;
    833       output.Set(kWifiKey, wifi);
    834       SetStateProperties(state, wifi);
    835       if (state.IsStatusEqual(SetupState::kSuccess))
    836         wifi->SetString(kInfoWifiSsidKey, wifi_->GetCurrentlyConnectedSsid());
    837     }
    838   }
    839 
    840   callback.Run(http::kOk, output);
    841 }
    842 
    843 void PrivetHandler::HandleState(const base::DictionaryValue& input,
    844                                 const UserInfo& user_info,
    845                                 const RequestCallback& callback) {
    846   base::DictionaryValue output;
    847   output.Set(kStateKey, cloud_->GetLegacyState().DeepCopy());
    848   output.SetString(kFingerprintKey, std::to_string(state_fingerprint_));
    849 
    850   callback.Run(http::kOk, output);
    851 }
    852 
    853 void PrivetHandler::HandleTraits(const base::DictionaryValue& input,
    854                                  const UserInfo& user_info,
    855                                  const RequestCallback& callback) {
    856   base::DictionaryValue output;
    857   output.Set(kTraitsKey, cloud_->GetTraits().DeepCopy());
    858   output.SetString(kFingerprintKey, std::to_string(traits_fingerprint_));
    859 
    860   callback.Run(http::kOk, output);
    861 }
    862 
    863 void PrivetHandler::HandleComponents(const base::DictionaryValue& input,
    864                                      const UserInfo& user_info,
    865                                      const RequestCallback& callback) {
    866   std::string path;
    867   std::set<std::string> filter;
    868   std::unique_ptr<base::DictionaryValue> components;
    869 
    870   input.GetString(kPathKey, &path);
    871   const base::ListValue* filter_items = nullptr;
    872   if (input.GetList(kFilterKey, &filter_items)) {
    873     for (const base::Value* value : *filter_items) {
    874       std::string filter_item;
    875       if (value->GetAsString(&filter_item))
    876         filter.insert(filter_item);
    877     }
    878   }
    879   const base::DictionaryValue* component = nullptr;
    880   if (!path.empty()) {
    881     ErrorPtr error;
    882     component = cloud_->FindComponent(path, &error);
    883     if (!component)
    884       return ReturnError(*error, callback);
    885     components.reset(new base::DictionaryValue);
    886     // Get the last element of the path and use it as a dictionary key here.
    887     auto parts = Split(path, ".", true, false);
    888     components->Set(parts.back(), CloneComponent(*component, filter).release());
    889   } else {
    890     components = CloneComponentTree(cloud_->GetComponents(), filter);
    891   }
    892   base::DictionaryValue output;
    893   output.Set(kComponentsKey, components.release());
    894   output.SetString(kFingerprintKey, std::to_string(components_fingerprint_));
    895 
    896   callback.Run(http::kOk, output);
    897 }
    898 
    899 void PrivetHandler::HandleCommandDefs(const base::DictionaryValue& input,
    900                                       const UserInfo& user_info,
    901                                       const RequestCallback& callback) {
    902   base::DictionaryValue output;
    903   output.Set(kCommandsKey, cloud_->GetLegacyCommandDef().DeepCopy());
    904   // Use traits fingerprint since right now we treat traits and command defs
    905   // as being equivalent.
    906   output.SetString(kFingerprintKey, std::to_string(traits_fingerprint_));
    907 
    908   callback.Run(http::kOk, output);
    909 }
    910 
    911 void PrivetHandler::HandleCommandsExecute(const base::DictionaryValue& input,
    912                                           const UserInfo& user_info,
    913                                           const RequestCallback& callback) {
    914   cloud_->AddCommand(input, user_info,
    915                      base::Bind(&OnCommandRequestSucceeded, callback));
    916 }
    917 
    918 void PrivetHandler::HandleCommandsStatus(const base::DictionaryValue& input,
    919                                          const UserInfo& user_info,
    920                                          const RequestCallback& callback) {
    921   std::string id;
    922   if (!input.GetString(kCommandsIdKey, &id)) {
    923     ErrorPtr error;
    924     Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidParams,
    925                        kInvalidParamValueFormat, kCommandsIdKey, id.c_str());
    926     return ReturnError(*error, callback);
    927   }
    928   cloud_->GetCommand(id, user_info,
    929                      base::Bind(&OnCommandRequestSucceeded, callback));
    930 }
    931 
    932 void PrivetHandler::HandleCommandsList(const base::DictionaryValue& input,
    933                                        const UserInfo& user_info,
    934                                        const RequestCallback& callback) {
    935   cloud_->ListCommands(user_info,
    936                        base::Bind(&OnCommandRequestSucceeded, callback));
    937 }
    938 
    939 void PrivetHandler::HandleCommandsCancel(const base::DictionaryValue& input,
    940                                          const UserInfo& user_info,
    941                                          const RequestCallback& callback) {
    942   std::string id;
    943   if (!input.GetString(kCommandsIdKey, &id)) {
    944     ErrorPtr error;
    945     Error::AddToPrintf(&error, FROM_HERE, errors::kInvalidParams,
    946                        kInvalidParamValueFormat, kCommandsIdKey, id.c_str());
    947     return ReturnError(*error, callback);
    948   }
    949   cloud_->CancelCommand(id, user_info,
    950                         base::Bind(&OnCommandRequestSucceeded, callback));
    951 }
    952 
    953 void PrivetHandler::HandleCheckForUpdates(const base::DictionaryValue& input,
    954                                           const UserInfo& user_info,
    955                                           const RequestCallback& callback) {
    956   int timeout_seconds = -1;
    957   input.GetInteger(kWaitTimeoutKey, &timeout_seconds);
    958   base::TimeDelta timeout = device_->GetHttpRequestTimeout();
    959   // Allow 10 seconds to cut the timeout short to make sure HTTP server doesn't
    960   // kill the connection before we have a chance to respond. 10 seconds chosen
    961   // at random here without any scientific basis for the value.
    962   const base::TimeDelta safety_gap = base::TimeDelta::FromSeconds(10);
    963   if (timeout != base::TimeDelta::Max()) {
    964     if (timeout > safety_gap)
    965       timeout -= safety_gap;
    966     else
    967       timeout = base::TimeDelta::FromSeconds(0);
    968   }
    969   if (timeout_seconds >= 0)
    970     timeout = std::min(timeout, base::TimeDelta::FromSeconds(timeout_seconds));
    971   if (timeout == base::TimeDelta{})
    972     return ReplyToUpdateRequest(callback);
    973 
    974   std::string state_fingerprint;
    975   std::string commands_fingerprint;
    976   std::string traits_fingerprint;
    977   std::string components_fingerprint;
    978   input.GetString(kStateFingerprintKey, &state_fingerprint);
    979   input.GetString(kCommandsFingerprintKey, &commands_fingerprint);
    980   input.GetString(kTraitsFingerprintKey, &traits_fingerprint);
    981   input.GetString(kComponentsFingerprintKey, &components_fingerprint);
    982   const bool ignore_state = state_fingerprint.empty();
    983   const bool ignore_commands = commands_fingerprint.empty();
    984   const bool ignore_traits = traits_fingerprint.empty();
    985   const bool ignore_components = components_fingerprint.empty();
    986   // If all fingerprints are missing, nothing to wait for, return immediately.
    987   if (ignore_state && ignore_commands && ignore_traits && ignore_components)
    988     return ReplyToUpdateRequest(callback);
    989   // If the current state fingerprint is different from the requested one,
    990   // return new fingerprints.
    991   if (!ignore_state && state_fingerprint != std::to_string(state_fingerprint_))
    992     return ReplyToUpdateRequest(callback);
    993   // If the current commands fingerprint is different from the requested one,
    994   // return new fingerprints.
    995   // NOTE: We are using traits fingerprint for command fingerprint as well.
    996   if (!ignore_commands &&
    997       commands_fingerprint != std::to_string(traits_fingerprint_)) {
    998     return ReplyToUpdateRequest(callback);
    999   }
   1000   // If the current traits fingerprint is different from the requested one,
   1001   // return new fingerprints.
   1002   if (!ignore_traits &&
   1003       traits_fingerprint != std::to_string(traits_fingerprint_)) {
   1004     return ReplyToUpdateRequest(callback);
   1005   }
   1006   // If the current components fingerprint is different from the requested one,
   1007   // return new fingerprints.
   1008   if (!ignore_components &&
   1009       components_fingerprint != std::to_string(components_fingerprint_)) {
   1010     return ReplyToUpdateRequest(callback);
   1011   }
   1012 
   1013   UpdateRequestParameters params;
   1014   params.request_id = ++last_update_request_id_;
   1015   params.callback = callback;
   1016   params.traits_fingerprint =
   1017       (ignore_traits && ignore_commands) ? 0 : traits_fingerprint_;
   1018   params.state_fingerprint = ignore_state ? 0 : state_fingerprint_;
   1019   params.components_fingerprint =
   1020       ignore_components ? 0 : components_fingerprint_;
   1021   update_requests_.push_back(params);
   1022   if (timeout != base::TimeDelta::Max()) {
   1023     device_->PostDelayedTask(
   1024         FROM_HERE,
   1025         base::Bind(&PrivetHandler::OnUpdateRequestTimeout,
   1026                    weak_ptr_factory_.GetWeakPtr(), last_update_request_id_),
   1027         timeout);
   1028   }
   1029 }
   1030 
   1031 void PrivetHandler::ReplyToUpdateRequest(
   1032     const RequestCallback& callback) const {
   1033   base::DictionaryValue output;
   1034   output.SetString(kStateFingerprintKey, std::to_string(state_fingerprint_));
   1035   output.SetString(kCommandsFingerprintKey,
   1036                    std::to_string(traits_fingerprint_));
   1037   output.SetString(kTraitsFingerprintKey, std::to_string(traits_fingerprint_));
   1038   output.SetString(kComponentsFingerprintKey,
   1039                    std::to_string(components_fingerprint_));
   1040   callback.Run(http::kOk, output);
   1041 }
   1042 
   1043 void PrivetHandler::OnUpdateRequestTimeout(int update_request_id) {
   1044   auto pred = [update_request_id](const UpdateRequestParameters& params) {
   1045     return params.request_id != update_request_id;
   1046   };
   1047   auto last =
   1048       std::partition(update_requests_.begin(), update_requests_.end(), pred);
   1049   for (auto p = last; p != update_requests_.end(); ++p)
   1050     ReplyToUpdateRequest(p->callback);
   1051   update_requests_.erase(last, update_requests_.end());
   1052 }
   1053 
   1054 }  // namespace privet
   1055 }  // namespace weave
   1056