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>
ToValue(const Container & list)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
GetAuthTokenFromAuthHeader(const std::string & auth_header)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.
ErrorToJson(const Error & error)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>
SetStateProperties(const T & state,base::DictionaryValue * parent)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
ReturnError(const Error & error,const PrivetHandler::RequestCallback & callback)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
OnCommandRequestSucceeded(const PrivetHandler::RequestCallback & callback,const base::DictionaryValue & output,ErrorPtr error)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
CreateManifestSection(const CloudDelegate & cloud)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
CreateEndpointsSection(const DeviceDelegate & device)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
CreateInfoAuthSection(const SecurityDelegate & security,AuthScope anonymous_max_scope)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
CreateWifiSection(const WifiDelegate & wifi)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
CreateGcdSection(const CloudDelegate & cloud)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
GetAnonymousMaxScope(const CloudDelegate & cloud,const WifiDelegate * wifi)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.
CloneComponent(const base::DictionaryValue & component,const std::set<std::string> & filter)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.
CloneComponentTree(const base::DictionaryValue & parent,const std::set<std::string> & filter)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
GetHttpPaths() const352 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
GetHttpsPaths() const361 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
PrivetHandler(CloudDelegate * cloud,DeviceDelegate * device,SecurityDelegate * security,WifiDelegate * wifi,base::Clock * clock)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
~PrivetHandler()423 PrivetHandler::~PrivetHandler() {
424 for (const auto& req : update_requests_)
425 ReplyToUpdateRequest(req.callback);
426 }
427
OnTraitDefsChanged()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
OnStateChanged()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
OnComponentTreeChanged()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
HandleRequest(const std::string & api,const std::string & auth_header,const base::DictionaryValue * input,const RequestCallback & callback)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
AddHandler(const std::string & path,ApiHandler handler,AuthScope scope)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
AddSecureHandler(const std::string & path,ApiHandler handler,AuthScope scope)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
HandleInfo(const base::DictionaryValue &,const UserInfo & user_info,const RequestCallback & callback)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
HandlePairingStart(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandlePairingConfirm(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandlePairingCancel(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleAuth(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleAccessControlClaim(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleAccessControlConfirm(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleSetupStart(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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, ®istration)) {
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
HandleSetupStatus(const base::DictionaryValue &,const UserInfo & user_info,const RequestCallback & callback)810 void PrivetHandler::HandleSetupStatus(const base::DictionaryValue&,
811 const UserInfo& user_info,
812 const RequestCallback& callback) {
813 ReplyWithSetupStatus(callback);
814 }
815
ReplyWithSetupStatus(const RequestCallback & callback) const816 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
HandleState(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleTraits(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleComponents(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleCommandDefs(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleCommandsExecute(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleCommandsStatus(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleCommandsList(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleCommandsCancel(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
HandleCheckForUpdates(const base::DictionaryValue & input,const UserInfo & user_info,const RequestCallback & callback)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
ReplyToUpdateRequest(const RequestCallback & callback) const1031 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
OnUpdateRequestTimeout(int update_request_id)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