blob: 5c3d0c2a7061f2c955bb1ed953d96a3eab2b6857 [file] [log] [blame]
Vitaly Buka7ce499f2015-06-09 08:04:11 -07001// Copyright 2014 The Chromium OS 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 "buffet/privet/privet_handler.h"
6
7#include <memory>
8#include <set>
9#include <string>
10#include <utility>
11
12#include <base/bind.h>
13#include <base/location.h>
14#include <base/stl_util.h>
15#include <base/strings/string_number_conversions.h>
16#include <base/strings/stringprintf.h>
17#include <base/values.h>
18#include <chromeos/http/http_request.h>
19#include <chromeos/strings/string_utils.h>
20
21#include "buffet/privet/cloud_delegate.h"
22#include "buffet/privet/constants.h"
23#include "buffet/privet/device_delegate.h"
24#include "buffet/privet/identity_delegate.h"
25#include "buffet/privet/security_delegate.h"
26#include "buffet/privet/wifi_delegate.h"
27
28namespace privetd {
29
30namespace {
31
32const char kInfoVersionKey[] = "version";
33const char kInfoVersionValue[] = "3.0";
34
35const char kNameKey[] = "name";
36const char kDescrptionKey[] = "description";
37const char kLocationKey[] = "location";
38
39const char kGcdKey[] = "gcd";
40const char kWifiKey[] = "wifi";
41const char kStatusKey[] = "status";
42const char kErrorKey[] = "error";
43const char kCryptoKey[] = "crypto";
44const char kStatusErrorValue[] = "error";
45
46const char kInfoIdKey[] = "id";
47const char kInfoServicesKey[] = "services";
48
49const char kInfoEndpointsKey[] = "endpoints";
50const char kInfoEndpointsHttpPortKey[] = "httpPort";
51const char kInfoEndpointsHttpUpdatePortKey[] = "httpUpdatesPort";
52const char kInfoEndpointsHttpsPortKey[] = "httpsPort";
53const char kInfoEndpointsHttpsUpdatePortKey[] = "httpsUpdatesPort";
54
55const char kInfoModelIdKey[] = "modelManifestId";
56const char kInfoModelManifestKey[] = "basicModelManifest";
57const char kInfoManifestUiDeviceKind[] = "uiDeviceKind";
58const char kInfoManifestOemName[] = "oemName";
59const char kInfoManifestModelName[] = "modelName";
60
61const char kInfoAuthenticationKey[] = "authentication";
62
63const char kInfoAuthAnonymousMaxScopeKey[] = "anonymousMaxScope";
64
65const char kInfoWifiCapabilitiesKey[] = "capabilities";
66const char kInfoWifiSsidKey[] = "ssid";
67const char kInfoWifiHostedSsidKey[] = "hostedSsid";
68
69const char kInfoUptimeKey[] = "uptime";
70
71const char kPairingKey[] = "pairing";
72const char kPairingSessionIdKey[] = "sessionId";
73const char kPairingDeviceCommitmentKey[] = "deviceCommitment";
74const char kPairingClientCommitmentKey[] = "clientCommitment";
75const char kPairingFingerprintKey[] = "certFingerprint";
76const char kPairingSignatureKey[] = "certSignature";
77
78const char kAuthTypeAnonymousValue[] = "anonymous";
79const char kAuthTypePairingValue[] = "pairing";
80
81const char kAuthModeKey[] = "mode";
82const char kAuthCodeKey[] = "authCode";
83const char kAuthRequestedScopeKey[] = "requestedScope";
84const char kAuthScopeAutoValue[] = "auto";
85
86const char kAuthAccessTokenKey[] = "accessToken";
87const char kAuthTokenTypeKey[] = "tokenType";
88const char kAuthExpiresInKey[] = "expiresIn";
89const char kAuthScopeKey[] = "scope";
90
91const char kAuthorizationHeaderPrefix[] = "Privet";
92
93const char kErrorCodeKey[] = "code";
94const char kErrorMessageKey[] = "message";
95const char kErrorDebugInfoKey[] = "debugInfo";
96
97const char kSetupStartSsidKey[] = "ssid";
98const char kSetupStartPassKey[] = "passphrase";
99const char kSetupStartTicketIdKey[] = "ticketId";
100const char kSetupStartUserKey[] = "user";
101
102const char kFingerprintKey[] = "fingerprint";
103const char kStateKey[] = "state";
104const char kCommandsKey[] = "commands";
105const char kCommandsIdKey[] = "id";
106
107const char kInvalidParamValueFormat[] = "Invalid parameter: '%s'='%s'";
108
109const int kAccessTokenExpirationSeconds = 3600;
110
111// Threshold to reduce probability of expiration because of clock difference
112// between device and client. Value is just a guess.
113const int kAccessTokenExpirationThresholdSeconds = 300;
114
115template <class Container>
116std::unique_ptr<base::ListValue> ToValue(const Container& list) {
117 std::unique_ptr<base::ListValue> value_list(new base::ListValue());
118 for (const std::string& val : list)
119 value_list->AppendString(val);
120 return value_list;
121}
122
123template <typename T>
124class EnumToStringMap final {
125 public:
126 static std::string FindNameById(T id) {
127 for (const Map& m : kMap) {
128 if (m.id == id) {
129 CHECK(m.name);
130 return m.name;
131 }
132 }
133 NOTREACHED() << static_cast<int>(id) << " is not part of "
134 << typeid(T).name();
135 return std::string();
136 }
137
138 static bool FindIdByName(const std::string& name, T* id) {
139 for (const Map& m : kMap) {
140 if (m.name && m.name == name) {
141 *id = m.id;
142 return true;
143 }
144 }
145 return false;
146 }
147
148 private:
149 struct Map {
150 const T id;
151 const char* const name;
152 };
153 static const Map kMap[];
154};
155
156template <>
157const EnumToStringMap<ConnectionState::Status>::Map
158 EnumToStringMap<ConnectionState::Status>::kMap[] = {
159 {ConnectionState::kDisabled, "disabled"},
160 {ConnectionState::kUnconfigured, "unconfigured"},
161 {ConnectionState::kConnecting, "connecting"},
162 {ConnectionState::kOnline, "online"},
163 {ConnectionState::kOffline, "offline"},
164};
165
166template <>
167const EnumToStringMap<SetupState::Status>::Map
168 EnumToStringMap<SetupState::Status>::kMap[] = {
169 {SetupState::kNone, nullptr},
170 {SetupState::kInProgress, "inProgress"},
171 {SetupState::kSuccess, "success"},
172};
173
174template <>
175const EnumToStringMap<WifiType>::Map EnumToStringMap<WifiType>::kMap[] = {
176 {WifiType::kWifi24, "2.4GHz"},
177 {WifiType::kWifi50, "5.0GHz"},
178};
179
180template <>
181const EnumToStringMap<PairingType>::Map EnumToStringMap<PairingType>::kMap[] = {
182 {PairingType::kPinCode, "pinCode"},
183 {PairingType::kEmbeddedCode, "embeddedCode"},
184 {PairingType::kUltrasound32, "ultrasound32"},
185 {PairingType::kAudible32, "audible32"},
186};
187
188template <>
189const EnumToStringMap<CryptoType>::Map EnumToStringMap<CryptoType>::kMap[] = {
190 {CryptoType::kNone, "none"},
191 {CryptoType::kSpake_p224, "p224_spake2"},
192 {CryptoType::kSpake_p256, "p256_spake2"},
193};
194
195template <>
196const EnumToStringMap<AuthScope>::Map EnumToStringMap<AuthScope>::kMap[] = {
197 {AuthScope::kNone, "none"},
198 {AuthScope::kViewer, "viewer"},
199 {AuthScope::kUser, "user"},
200 {AuthScope::kOwner, "owner"},
201};
202
203struct {
204 const char* const reason;
205 int code;
206} kReasonToCode[] = {
207 {errors::kInvalidClientCommitment, chromeos::http::status_code::Forbidden},
208 {errors::kInvalidFormat, chromeos::http::status_code::BadRequest},
209 {errors::kMissingAuthorization, chromeos::http::status_code::Denied},
210 {errors::kInvalidAuthorization, chromeos::http::status_code::Denied},
211 {errors::kInvalidAuthorizationScope,
212 chromeos::http::status_code::Forbidden},
213 {errors::kAuthorizationExpired, chromeos::http::status_code::Forbidden},
214 {errors::kCommitmentMismatch, chromeos::http::status_code::Forbidden},
215 {errors::kUnknownSession, chromeos::http::status_code::NotFound},
216 {errors::kInvalidAuthCode, chromeos::http::status_code::Forbidden},
217 {errors::kInvalidAuthMode, chromeos::http::status_code::BadRequest},
218 {errors::kInvalidRequestedScope, chromeos::http::status_code::BadRequest},
219 {errors::kAccessDenied, chromeos::http::status_code::Forbidden},
220 {errors::kInvalidParams, chromeos::http::status_code::BadRequest},
221 {errors::kSetupUnavailable, chromeos::http::status_code::BadRequest},
222 {errors::kDeviceBusy, chromeos::http::status_code::ServiceUnavailable},
223 {errors::kInvalidState, chromeos::http::status_code::InternalServerError},
224 {errors::kNotFound, chromeos::http::status_code::NotFound},
225 {errors::kNotImplemented, chromeos::http::status_code::NotSupported},
226};
227
228template <typename T>
229std::string EnumToString(T id) {
230 return EnumToStringMap<T>::FindNameById(id);
231}
232
233template <typename T>
234bool StringToEnum(const std::string& name, T* id) {
235 return EnumToStringMap<T>::FindIdByName(name, id);
236}
237
238AuthScope AuthScopeFromString(const std::string& scope, AuthScope auto_scope) {
239 if (scope == kAuthScopeAutoValue)
240 return auto_scope;
241 AuthScope scope_id = AuthScope::kNone;
242 StringToEnum(scope, &scope_id);
243 return scope_id;
244}
245
246std::string GetAuthTokenFromAuthHeader(const std::string& auth_header) {
247 std::string name;
248 std::string value;
249 chromeos::string_utils::SplitAtFirst(auth_header, " ", &name, &value);
250 return value;
251}
252
253std::unique_ptr<base::DictionaryValue> ErrorInfoToJson(
254 const chromeos::Error& error) {
255 std::unique_ptr<base::DictionaryValue> output{new base::DictionaryValue};
256 output->SetString(kErrorMessageKey, error.GetMessage());
257 output->SetString(kErrorCodeKey, error.GetCode());
258 return output;
259}
260
261// Creates JSON similar to GCD server error format.
262std::unique_ptr<base::DictionaryValue> ErrorToJson(
263 const chromeos::Error& error) {
264 std::unique_ptr<base::DictionaryValue> output{ErrorInfoToJson(error)};
265
266 // Optional debug information.
267 std::unique_ptr<base::ListValue> errors{new base::ListValue};
268 for (const chromeos::Error* it = &error; it; it = it->GetInnerError()) {
269 std::unique_ptr<base::DictionaryValue> inner{ErrorInfoToJson(*it)};
270 tracked_objects::Location location{it->GetLocation().function_name.c_str(),
271 it->GetLocation().file_name.c_str(),
272 it->GetLocation().line_number,
273 nullptr};
274 inner->SetString(kErrorDebugInfoKey, location.ToString());
275 errors->Append(inner.release());
276 }
277 output->Set(kErrorDebugInfoKey, errors.release());
278 return output;
279}
280
281template <class T>
282void SetState(const T& state, base::DictionaryValue* parent) {
283 if (!state.error()) {
284 parent->SetString(kStatusKey, EnumToString(state.status()));
285 return;
286 }
287 parent->SetString(kStatusKey, kStatusErrorValue);
288 parent->Set(kErrorKey, ErrorToJson(*state.error()).release());
289}
290
291void ReturnError(const chromeos::Error& error,
292 const PrivetHandler::RequestCallback& callback) {
293 int code = chromeos::http::status_code::InternalServerError;
294 for (const auto& it : kReasonToCode) {
295 if (error.HasError(errors::kDomain, it.reason)) {
296 code = it.code;
297 break;
298 }
299 }
300 std::unique_ptr<base::DictionaryValue> output{new base::DictionaryValue};
301 output->Set(kErrorKey, ErrorToJson(error).release());
302 callback.Run(code, *output);
303}
304
305void OnCommandRequestSucceeded(const PrivetHandler::RequestCallback& callback,
306 const base::DictionaryValue& output) {
307 callback.Run(chromeos::http::status_code::Ok, output);
308}
309
310void OnCommandRequestFailed(const PrivetHandler::RequestCallback& callback,
311 chromeos::Error* error) {
312 if (error->HasError("gcd", "unknown_command")) {
313 chromeos::ErrorPtr new_error = error->Clone();
314 chromeos::Error::AddTo(&new_error, FROM_HERE, errors::kDomain,
315 errors::kNotFound, "Unknown command ID");
316 return ReturnError(*new_error, callback);
317 }
318 if (error->HasError("gcd", "access_denied")) {
319 chromeos::ErrorPtr new_error = error->Clone();
320 chromeos::Error::AddTo(&new_error, FROM_HERE, errors::kDomain,
321 errors::kAccessDenied, error->GetMessage());
322 return ReturnError(*new_error, callback);
323 }
324 return ReturnError(*error, callback);
325}
326
327std::string GetDeviceKind(const std::string& manifest_id) {
328 CHECK_EQ(5u, manifest_id.size());
329 std::string kind = manifest_id.substr(0, 2);
330 if (kind == "AC")
331 return "accessPoint";
332 if (kind == "AK")
333 return "aggregator";
334 if (kind == "AM")
335 return "camera";
336 if (kind == "AB")
337 return "developmentBoard";
338 if (kind == "AE")
339 return "printer";
340 if (kind == "AF")
341 return "scanner";
342 if (kind == "AD")
343 return "speaker";
344 if (kind == "AL")
345 return "storage";
346 if (kind == "AJ")
347 return "toy";
348 if (kind == "AA")
349 return "vendor";
350 if (kind == "AN")
351 return "video";
352 LOG(FATAL) << "Invalid model id: " << manifest_id;
353 return std::string();
354}
355
356std::unique_ptr<base::DictionaryValue> CreateManifestSection(
357 const std::string& model_id,
358 const CloudDelegate& cloud) {
359 std::unique_ptr<base::DictionaryValue> manifest(new base::DictionaryValue());
360 manifest->SetString(kInfoManifestUiDeviceKind, GetDeviceKind(model_id));
361 manifest->SetString(kInfoManifestOemName, cloud.GetOemName());
362 manifest->SetString(kInfoManifestModelName, cloud.GetModelName());
363 return manifest;
364}
365
366std::unique_ptr<base::DictionaryValue> CreateEndpointsSection(
367 const DeviceDelegate& device) {
368 std::unique_ptr<base::DictionaryValue> endpoints(new base::DictionaryValue());
369 auto http_endpoint = device.GetHttpEnpoint();
370 endpoints->SetInteger(kInfoEndpointsHttpPortKey, http_endpoint.first);
371 endpoints->SetInteger(kInfoEndpointsHttpUpdatePortKey, http_endpoint.second);
372
373 auto https_endpoint = device.GetHttpsEnpoint();
374 endpoints->SetInteger(kInfoEndpointsHttpsPortKey, https_endpoint.first);
375 endpoints->SetInteger(kInfoEndpointsHttpsUpdatePortKey,
376 https_endpoint.second);
377
378 return endpoints;
379}
380
381std::unique_ptr<base::DictionaryValue> CreateInfoAuthSection(
382 const SecurityDelegate& security,
383 AuthScope anonymous_max_scope) {
384 std::unique_ptr<base::DictionaryValue> auth(new base::DictionaryValue());
385
386 auth->SetString(kInfoAuthAnonymousMaxScopeKey,
387 EnumToString(anonymous_max_scope));
388
389 std::unique_ptr<base::ListValue> pairing_types(new base::ListValue());
390 for (PairingType type : security.GetPairingTypes())
391 pairing_types->AppendString(EnumToString(type));
392 auth->Set(kPairingKey, pairing_types.release());
393
394 std::unique_ptr<base::ListValue> auth_types(new base::ListValue());
395 auth_types->AppendString(kAuthTypeAnonymousValue);
396 auth_types->AppendString(kAuthTypePairingValue);
397
398 // TODO(vitalybuka): Implement cloud auth.
399 // if (cloud.GetConnectionState().IsStatusEqual(ConnectionState::kOnline)) {
400 // auth_types->AppendString(kAuthTypeCloudValue);
401 // }
402 auth->Set(kAuthModeKey, auth_types.release());
403
404 std::unique_ptr<base::ListValue> crypto_types(new base::ListValue());
405 for (CryptoType type : security.GetCryptoTypes())
406 crypto_types->AppendString(EnumToString(type));
407 auth->Set(kCryptoKey, crypto_types.release());
408
409 return auth;
410}
411
412std::unique_ptr<base::DictionaryValue> CreateWifiSection(
413 const WifiDelegate& wifi) {
414 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
415
416 std::unique_ptr<base::ListValue> capabilities(new base::ListValue());
417 for (WifiType type : wifi.GetTypes())
418 capabilities->AppendString(EnumToString(type));
419 result->Set(kInfoWifiCapabilitiesKey, capabilities.release());
420
421 result->SetString(kInfoWifiSsidKey, wifi.GetCurrentlyConnectedSsid());
422
423 std::string hosted_ssid = wifi.GetHostedSsid();
424 const ConnectionState& state = wifi.GetConnectionState();
425 if (!hosted_ssid.empty()) {
426 DCHECK(!state.IsStatusEqual(ConnectionState::kDisabled));
427 DCHECK(!state.IsStatusEqual(ConnectionState::kOnline));
428 result->SetString(kInfoWifiHostedSsidKey, hosted_ssid);
429 }
430 SetState(state, result.get());
431 return result;
432}
433
434std::unique_ptr<base::DictionaryValue> CreateGcdSection(
435 const CloudDelegate& cloud) {
436 std::unique_ptr<base::DictionaryValue> gcd(new base::DictionaryValue());
437 gcd->SetString(kInfoIdKey, cloud.GetCloudId());
438 SetState(cloud.GetConnectionState(), gcd.get());
439 return gcd;
440}
441
442AuthScope GetAnonymousMaxScope(const CloudDelegate& cloud,
443 const WifiDelegate* wifi) {
444 if (wifi && !wifi->GetHostedSsid().empty())
445 return AuthScope::kNone;
446 return cloud.GetAnonymousMaxScope();
447}
448
449} // namespace
450
451PrivetHandler::PrivetHandler(CloudDelegate* cloud,
452 DeviceDelegate* device,
453 SecurityDelegate* security,
454 WifiDelegate* wifi,
455 IdentityDelegate* identity)
456 : cloud_(cloud),
457 device_(device),
458 security_(security),
459 wifi_(wifi),
460 identity_(identity) {
461 CHECK(cloud_);
462 CHECK(device_);
463 CHECK(security_);
464 cloud_observer_.Add(cloud_);
465
466 AddHandler("/privet/info", &PrivetHandler::HandleInfo, AuthScope::kNone);
467 AddHandler("/privet/v3/pairing/start", &PrivetHandler::HandlePairingStart,
468 AuthScope::kNone);
469 AddHandler("/privet/v3/pairing/confirm", &PrivetHandler::HandlePairingConfirm,
470 AuthScope::kNone);
471 AddHandler("/privet/v3/pairing/cancel", &PrivetHandler::HandlePairingCancel,
472 AuthScope::kNone);
473 AddHandler("/privet/v3/auth", &PrivetHandler::HandleAuth, AuthScope::kNone);
474 AddHandler("/privet/v3/setup/start", &PrivetHandler::HandleSetupStart,
475 AuthScope::kOwner);
476 AddHandler("/privet/v3/setup/status", &PrivetHandler::HandleSetupStatus,
477 AuthScope::kOwner);
478 AddHandler("/privet/v3/state", &PrivetHandler::HandleState,
479 AuthScope::kViewer);
480 AddHandler("/privet/v3/commandDefs", &PrivetHandler::HandleCommandDefs,
481 AuthScope::kViewer);
482 AddHandler("/privet/v3/commands/execute",
483 &PrivetHandler::HandleCommandsExecute, AuthScope::kViewer);
484 AddHandler("/privet/v3/commands/status", &PrivetHandler::HandleCommandsStatus,
485 AuthScope::kViewer);
486 AddHandler("/privet/v3/commands/cancel", &PrivetHandler::HandleCommandsCancel,
487 AuthScope::kViewer);
488 AddHandler("/privet/v3/commands/list", &PrivetHandler::HandleCommandsList,
489 AuthScope::kViewer);
490}
491
492PrivetHandler::~PrivetHandler() {
493}
494
495void PrivetHandler::OnCommandDefsChanged() {
496 ++command_defs_fingerprint_;
497}
498
499void PrivetHandler::OnStateChanged() {
500 ++state_fingerprint_;
501}
502
503void PrivetHandler::HandleRequest(const std::string& api,
504 const std::string& auth_header,
505 const base::DictionaryValue* input,
506 const RequestCallback& callback) {
507 chromeos::ErrorPtr error;
508 if (!input) {
509 chromeos::Error::AddTo(&error, FROM_HERE, errors::kDomain,
510 errors::kInvalidFormat, "Malformed JSON");
511 return ReturnError(*error, callback);
512 }
513 auto handler = handlers_.find(api);
514 if (handler == handlers_.end()) {
515 chromeos::Error::AddTo(&error, FROM_HERE, errors::kDomain,
516 errors::kNotFound, "Path not found");
517 return ReturnError(*error, callback);
518 }
519 if (auth_header.empty()) {
520 chromeos::Error::AddTo(&error, FROM_HERE, errors::kDomain,
521 errors::kMissingAuthorization,
522 "Authorization header must not be empty");
523 return ReturnError(*error, callback);
524 }
525 std::string token = GetAuthTokenFromAuthHeader(auth_header);
526 if (token.empty()) {
527 chromeos::Error::AddToPrintf(
528 &error, FROM_HERE, errors::kDomain, errors::kInvalidAuthorization,
529 "Invalid authorization header: %s", auth_header.c_str());
530 return ReturnError(*error, callback);
531 }
532 UserInfo user_info;
533 if (token != kAuthTypeAnonymousValue) {
534 base::Time time;
535 user_info = security_->ParseAccessToken(token, &time);
536 if (user_info.scope() == AuthScope::kNone) {
537 chromeos::Error::AddToPrintf(&error, FROM_HERE, errors::kDomain,
538 errors::kInvalidAuthorization,
539 "Invalid access token: %s", token.c_str());
540 return ReturnError(*error, callback);
541 }
542 time += base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds);
543 time +=
544 base::TimeDelta::FromSeconds(kAccessTokenExpirationThresholdSeconds);
545 if (time < base::Time::Now()) {
546 chromeos::Error::AddToPrintf(&error, FROM_HERE, errors::kDomain,
547 errors::kAuthorizationExpired,
548 "Token expired: %s", token.c_str());
549 return ReturnError(*error, callback);
550 }
551 }
552
553 if (handler->second.first > user_info.scope()) {
554 chromeos::Error::AddToPrintf(
555 &error, FROM_HERE, errors::kDomain, errors::kInvalidAuthorizationScope,
556 "Scope '%s' does not allow '%s'",
557 EnumToString(user_info.scope()).c_str(), api.c_str());
558 return ReturnError(*error, callback);
559 }
560 (this->*handler->second.second)(*input, user_info, callback);
561}
562
563void PrivetHandler::AddHandler(const std::string& path,
564 ApiHandler handler,
565 AuthScope scope) {
566 CHECK(handlers_.emplace(path, std::make_pair(scope, handler)).second);
567}
568
569void PrivetHandler::HandleInfo(const base::DictionaryValue&,
570 const UserInfo& user_info,
571 const RequestCallback& callback) {
572 base::DictionaryValue output;
573
574 chromeos::ErrorPtr error;
575
576 std::string name;
577 std::string model_id;
578 if (!cloud_->GetName(&name, &error) ||
579 !cloud_->GetModelId(&model_id, &error)) {
580 return ReturnError(*error, callback);
581 }
582
583 output.SetString(kInfoVersionKey, kInfoVersionValue);
584 output.SetString(kInfoIdKey, identity_->GetId());
585 output.SetString(kNameKey, name);
586
587 std::string description{cloud_->GetDescription()};
588 if (!description.empty())
589 output.SetString(kDescrptionKey, description);
590
591 std::string location{cloud_->GetLocation()};
592 if (!location.empty())
593 output.SetString(kLocationKey, location);
594
595 output.SetString(kInfoModelIdKey, model_id);
596 output.Set(kInfoModelManifestKey,
597 CreateManifestSection(model_id, *cloud_).release());
598 output.Set(kInfoServicesKey, ToValue(cloud_->GetServices()).release());
599
600 output.Set(kInfoAuthenticationKey,
601 CreateInfoAuthSection(
602 *security_, GetAnonymousMaxScope(*cloud_, wifi_)).release());
603
604 output.Set(kInfoEndpointsKey, CreateEndpointsSection(*device_).release());
605
606 if (wifi_)
607 output.Set(kWifiKey, CreateWifiSection(*wifi_).release());
608
609 output.Set(kGcdKey, CreateGcdSection(*cloud_).release());
610
611 output.SetInteger(kInfoUptimeKey, device_->GetUptime().InSeconds());
612
613 callback.Run(chromeos::http::status_code::Ok, output);
614}
615
616void PrivetHandler::HandlePairingStart(const base::DictionaryValue& input,
617 const UserInfo& user_info,
618 const RequestCallback& callback) {
619 chromeos::ErrorPtr error;
620
621 std::string pairing_str;
622 input.GetString(kPairingKey, &pairing_str);
623
624 std::string crypto_str;
625 input.GetString(kCryptoKey, &crypto_str);
626
627 PairingType pairing;
628 std::set<PairingType> modes = security_->GetPairingTypes();
629 if (!StringToEnum(pairing_str, &pairing) || !ContainsKey(modes, pairing)) {
630 chromeos::Error::AddToPrintf(
631 &error, FROM_HERE, errors::kDomain, errors::kInvalidParams,
632 kInvalidParamValueFormat, kPairingKey, pairing_str.c_str());
633 return ReturnError(*error, callback);
634 }
635
636 CryptoType crypto;
637 std::set<CryptoType> cryptos = security_->GetCryptoTypes();
638 if (!StringToEnum(crypto_str, &crypto) || !ContainsKey(cryptos, crypto)) {
639 chromeos::Error::AddToPrintf(
640 &error, FROM_HERE, errors::kDomain, errors::kInvalidParams,
641 kInvalidParamValueFormat, kCryptoKey, crypto_str.c_str());
642 return ReturnError(*error, callback);
643 }
644
645 std::string id;
646 std::string commitment;
647 if (!security_->StartPairing(pairing, crypto, &id, &commitment, &error))
648 return ReturnError(*error, callback);
649
650 base::DictionaryValue output;
651 output.SetString(kPairingSessionIdKey, id);
652 output.SetString(kPairingDeviceCommitmentKey, commitment);
653 callback.Run(chromeos::http::status_code::Ok, output);
654}
655
656void PrivetHandler::HandlePairingConfirm(const base::DictionaryValue& input,
657 const UserInfo& user_info,
658 const RequestCallback& callback) {
659 std::string id;
660 input.GetString(kPairingSessionIdKey, &id);
661
662 std::string commitment;
663 input.GetString(kPairingClientCommitmentKey, &commitment);
664
665 std::string fingerprint;
666 std::string signature;
667 chromeos::ErrorPtr error;
668 if (!security_->ConfirmPairing(id, commitment, &fingerprint, &signature,
669 &error)) {
670 return ReturnError(*error, callback);
671 }
672
673 base::DictionaryValue output;
674 output.SetString(kPairingFingerprintKey, fingerprint);
675 output.SetString(kPairingSignatureKey, signature);
676 callback.Run(chromeos::http::status_code::Ok, output);
677}
678
679void PrivetHandler::HandlePairingCancel(const base::DictionaryValue& input,
680 const UserInfo& user_info,
681 const RequestCallback& callback) {
682 std::string id;
683 input.GetString(kPairingSessionIdKey, &id);
684
685 chromeos::ErrorPtr error;
686 if (!security_->CancelPairing(id, &error))
687 return ReturnError(*error, callback);
688
689 base::DictionaryValue output;
690 callback.Run(chromeos::http::status_code::Ok, output);
691}
692
693void PrivetHandler::HandleAuth(const base::DictionaryValue& input,
694 const UserInfo& user_info,
695 const RequestCallback& callback) {
696 chromeos::ErrorPtr error;
697
698 std::string auth_code_type;
699 input.GetString(kAuthModeKey, &auth_code_type);
700
701 std::string auth_code;
702 input.GetString(kAuthCodeKey, &auth_code);
703
704 AuthScope max_auth_scope = AuthScope::kNone;
705 if (auth_code_type == kAuthTypeAnonymousValue) {
706 max_auth_scope = GetAnonymousMaxScope(*cloud_, wifi_);
707 } else if (auth_code_type == kAuthTypePairingValue) {
708 if (!security_->IsValidPairingCode(auth_code)) {
709 chromeos::Error::AddToPrintf(
710 &error, FROM_HERE, errors::kDomain, errors::kInvalidAuthCode,
711 kInvalidParamValueFormat, kAuthCodeKey, auth_code.c_str());
712 return ReturnError(*error, callback);
713 }
714 max_auth_scope = AuthScope::kOwner;
715 } else {
716 chromeos::Error::AddToPrintf(
717 &error, FROM_HERE, errors::kDomain, errors::kInvalidAuthMode,
718 kInvalidParamValueFormat, kAuthModeKey, auth_code_type.c_str());
719 return ReturnError(*error, callback);
720 }
721
722 std::string requested_scope;
723 input.GetString(kAuthRequestedScopeKey, &requested_scope);
724
725 AuthScope requested_auth_scope =
726 AuthScopeFromString(requested_scope, max_auth_scope);
727 if (requested_auth_scope == AuthScope::kNone) {
728 chromeos::Error::AddToPrintf(
729 &error, FROM_HERE, errors::kDomain, errors::kInvalidRequestedScope,
730 kInvalidParamValueFormat, kAuthRequestedScopeKey,
731 requested_scope.c_str());
732 return ReturnError(*error, callback);
733 }
734
735 if (requested_auth_scope > max_auth_scope) {
736 chromeos::Error::AddToPrintf(
737 &error, FROM_HERE, errors::kDomain, errors::kAccessDenied,
738 "Scope '%s' is not allowed for '%s'",
739 EnumToString(requested_auth_scope).c_str(), auth_code.c_str());
740 return ReturnError(*error, callback);
741 }
742
743 base::DictionaryValue output;
744 output.SetString(
745 kAuthAccessTokenKey,
746 security_->CreateAccessToken(
747 UserInfo{requested_auth_scope, ++last_user_id_}, base::Time::Now()));
748 output.SetString(kAuthTokenTypeKey, kAuthorizationHeaderPrefix);
749 output.SetInteger(kAuthExpiresInKey, kAccessTokenExpirationSeconds);
750 output.SetString(kAuthScopeKey, EnumToString(requested_auth_scope));
751 callback.Run(chromeos::http::status_code::Ok, output);
752}
753
754void PrivetHandler::HandleSetupStart(const base::DictionaryValue& input,
755 const UserInfo& user_info,
756 const RequestCallback& callback) {
757 std::string name;
758 chromeos::ErrorPtr error;
759 if (!cloud_->GetName(&name, &error))
760 return ReturnError(*error, callback);
761 input.GetString(kNameKey, &name);
762
763 std::string description{cloud_->GetDescription()};
764 input.GetString(kDescrptionKey, &description);
765
766 std::string location{cloud_->GetLocation()};
767 input.GetString(kLocationKey, &location);
768
769 std::string ssid;
770 std::string passphrase;
771 std::string ticket;
772 std::string user;
773
774 const base::DictionaryValue* wifi = nullptr;
775 if (input.GetDictionary(kWifiKey, &wifi)) {
776 if (!wifi_ || wifi_->GetTypes().empty()) {
777 chromeos::Error::AddTo(&error, FROM_HERE, errors::kDomain,
778 errors::kSetupUnavailable,
779 "WiFi setup unavailible");
780 return ReturnError(*error, callback);
781 }
782 wifi->GetString(kSetupStartSsidKey, &ssid);
783 if (ssid.empty()) {
784 chromeos::Error::AddToPrintf(
785 &error, FROM_HERE, errors::kDomain, errors::kInvalidParams,
786 kInvalidParamValueFormat, kSetupStartSsidKey, "");
787 return ReturnError(*error, callback);
788 }
789 wifi->GetString(kSetupStartPassKey, &passphrase);
790 }
791
792 const base::DictionaryValue* registration = nullptr;
793 if (input.GetDictionary(kGcdKey, &registration)) {
794 registration->GetString(kSetupStartTicketIdKey, &ticket);
795 if (ticket.empty()) {
796 chromeos::Error::AddToPrintf(
797 &error, FROM_HERE, errors::kDomain, errors::kInvalidParams,
798 kInvalidParamValueFormat, kSetupStartTicketIdKey, "");
799 return ReturnError(*error, callback);
800 }
801 registration->GetString(kSetupStartUserKey, &user);
802 }
803
804 cloud_->UpdateDeviceInfo(name, description, location,
805 base::Bind(&PrivetHandler::OnUpdateDeviceInfoDone,
806 weak_ptr_factory_.GetWeakPtr(), ssid,
807 passphrase, ticket, user, callback),
808 base::Bind(&OnCommandRequestFailed, callback));
809}
810
811void PrivetHandler::OnUpdateDeviceInfoDone(
812 const std::string& ssid,
813 const std::string& passphrase,
814 const std::string& ticket,
815 const std::string& user,
816 const RequestCallback& callback) const {
817 chromeos::ErrorPtr error;
818
819 if (!ssid.empty() && !wifi_->ConfigureCredentials(ssid, passphrase, &error))
820 return ReturnError(*error, callback);
821
822 if (!ticket.empty() && !cloud_->Setup(ticket, user, &error))
823 return ReturnError(*error, callback);
824
825 ReplyWithSetupStatus(callback);
826}
827
828void PrivetHandler::HandleSetupStatus(const base::DictionaryValue&,
829 const UserInfo& user_info,
830 const RequestCallback& callback) {
831 ReplyWithSetupStatus(callback);
832}
833
834void PrivetHandler::ReplyWithSetupStatus(
835 const RequestCallback& callback) const {
836 base::DictionaryValue output;
837
838 const SetupState& state = cloud_->GetSetupState();
839 if (!state.IsStatusEqual(SetupState::kNone)) {
840 base::DictionaryValue* gcd = new base::DictionaryValue;
841 output.Set(kGcdKey, gcd);
842 SetState(state, gcd);
843 if (state.IsStatusEqual(SetupState::kSuccess))
844 gcd->SetString(kInfoIdKey, cloud_->GetCloudId());
845 }
846
847 if (wifi_) {
848 const SetupState& state = wifi_->GetSetupState();
849 if (!state.IsStatusEqual(SetupState::kNone)) {
850 base::DictionaryValue* wifi = new base::DictionaryValue;
851 output.Set(kWifiKey, wifi);
852 SetState(state, wifi);
853 if (state.IsStatusEqual(SetupState::kSuccess))
854 wifi->SetString(kInfoWifiSsidKey, wifi_->GetCurrentlyConnectedSsid());
855 }
856 }
857
858 callback.Run(chromeos::http::status_code::Ok, output);
859}
860
861void PrivetHandler::HandleState(const base::DictionaryValue& input,
862 const UserInfo& user_info,
863 const RequestCallback& callback) {
864 base::DictionaryValue output;
865 base::DictionaryValue* defs = cloud_->GetState().DeepCopy();
866 output.Set(kStateKey, defs);
867 output.SetString(kFingerprintKey, base::IntToString(state_fingerprint_));
868
869 callback.Run(chromeos::http::status_code::Ok, output);
870}
871
872void PrivetHandler::HandleCommandDefs(const base::DictionaryValue& input,
873 const UserInfo& user_info,
874 const RequestCallback& callback) {
875 base::DictionaryValue output;
876 base::DictionaryValue* defs = cloud_->GetCommandDef().DeepCopy();
877 output.Set(kCommandsKey, defs);
878 output.SetString(kFingerprintKey,
879 base::IntToString(command_defs_fingerprint_));
880
881 callback.Run(chromeos::http::status_code::Ok, output);
882}
883
884void PrivetHandler::HandleCommandsExecute(const base::DictionaryValue& input,
885 const UserInfo& user_info,
886 const RequestCallback& callback) {
887 cloud_->AddCommand(input, user_info,
888 base::Bind(&OnCommandRequestSucceeded, callback),
889 base::Bind(&OnCommandRequestFailed, callback));
890}
891
892void PrivetHandler::HandleCommandsStatus(const base::DictionaryValue& input,
893 const UserInfo& user_info,
894 const RequestCallback& callback) {
895 std::string id;
896 if (!input.GetString(kCommandsIdKey, &id)) {
897 chromeos::ErrorPtr error;
898 chromeos::Error::AddToPrintf(
899 &error, FROM_HERE, errors::kDomain, errors::kInvalidParams,
900 kInvalidParamValueFormat, kCommandsIdKey, id.c_str());
901 return ReturnError(*error, callback);
902 }
903 cloud_->GetCommand(id, user_info,
904 base::Bind(&OnCommandRequestSucceeded, callback),
905 base::Bind(&OnCommandRequestFailed, callback));
906}
907
908void PrivetHandler::HandleCommandsList(const base::DictionaryValue& input,
909 const UserInfo& user_info,
910 const RequestCallback& callback) {
911 cloud_->ListCommands(user_info,
912 base::Bind(&OnCommandRequestSucceeded, callback),
913 base::Bind(&OnCommandRequestFailed, callback));
914}
915
916void PrivetHandler::HandleCommandsCancel(const base::DictionaryValue& input,
917 const UserInfo& user_info,
918 const RequestCallback& callback) {
919 std::string id;
920 if (!input.GetString(kCommandsIdKey, &id)) {
921 chromeos::ErrorPtr error;
922 chromeos::Error::AddToPrintf(
923 &error, FROM_HERE, errors::kDomain, errors::kInvalidParams,
924 kInvalidParamValueFormat, kCommandsIdKey, id.c_str());
925 return ReturnError(*error, callback);
926 }
927 cloud_->CancelCommand(id, user_info,
928 base::Bind(&OnCommandRequestSucceeded, callback),
929 base::Bind(&OnCommandRequestFailed, callback));
930}
931
932bool StringToPairingType(const std::string& mode, PairingType* id) {
933 return StringToEnum(mode, id);
934}
935
936std::string PairingTypeToString(PairingType id) {
937 return EnumToString(id);
938}
939
940bool StringToAuthScope(const std::string& scope, AuthScope* id) {
941 return StringToEnum(scope, id);
942}
943
944std::string AuthScopeToString(AuthScope id) {
945 return EnumToString(id);
946}
947
948} // namespace privetd