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 #ifndef LIBWEAVE_SRC_DEVICE_REGISTRATION_INFO_H_
6 #define LIBWEAVE_SRC_DEVICE_REGISTRATION_INFO_H_
7 
8 #include <map>
9 #include <memory>
10 #include <string>
11 #include <utility>
12 #include <vector>
13 
14 #include <base/callback.h>
15 #include <base/macros.h>
16 #include <base/memory/weak_ptr.h>
17 #include <base/time/time.h>
18 #include <weave/device.h>
19 #include <weave/error.h>
20 #include <weave/provider/http_client.h>
21 
22 #include "src/backoff_entry.h"
23 #include "src/commands/cloud_command_update_interface.h"
24 #include "src/component_manager.h"
25 #include "src/config.h"
26 #include "src/data_encoding.h"
27 #include "src/notification/notification_channel.h"
28 #include "src/notification/notification_delegate.h"
29 #include "src/notification/pull_channel.h"
30 
31 namespace base {
32 class DictionaryValue;
33 }  // namespace base
34 
35 namespace weave {
36 
37 class StateManager;
38 
39 namespace provider {
40 class Network;
41 class TaskRunner;
42 }
43 
44 namespace privet {
45 class AuthManager;
46 }
47 
48 // The DeviceRegistrationInfo class represents device registration information.
49 class DeviceRegistrationInfo : public NotificationDelegate,
50                                public CloudCommandUpdateInterface {
51  public:
52   using CloudRequestDoneCallback =
53       base::Callback<void(const base::DictionaryValue& response,
54                           ErrorPtr error)>;
55 
56   DeviceRegistrationInfo(Config* config,
57                          ComponentManager* component_manager,
58                          provider::TaskRunner* task_runner,
59                          provider::HttpClient* http_client,
60                          provider::Network* network,
61                          privet::AuthManager* auth_manager);
62 
63   ~DeviceRegistrationInfo() override;
64 
65   void AddGcdStateChangedCallback(
66       const Device::GcdStateChangedCallback& callback);
67   void RegisterDevice(const std::string& ticket_id,
68                       const DoneCallback& callback);
69 
70   void UpdateDeviceInfo(const std::string& name,
71                         const std::string& description,
72                         const std::string& location);
73   void UpdateBaseConfig(AuthScope anonymous_access_role,
74                         bool local_discovery_enabled,
75                         bool local_pairing_enabled);
76   bool UpdateServiceConfig(const std::string& client_id,
77                            const std::string& client_secret,
78                            const std::string& api_key,
79                            const std::string& oauth_url,
80                            const std::string& service_url,
81                            const std::string& xmpp_endpoint,
82                            ErrorPtr* error);
83 
84   void GetDeviceInfo(const CloudRequestDoneCallback& callback);
85 
86   // Returns the GCD service request URL. If |subpath| is specified, it is
87   // appended to the base URL which is normally
88   //    https://www.googleapis.com/weave/v1/".
89   // If |params| are specified, each key-value pair is formatted using
90   // WebParamsEncode() and appended to URL as a query
91   // string.
92   // So, calling:
93   //    GetServiceURL("ticket", {{"key","apiKey"}})
94   // will return something like:
95   //    https://www.googleapis.com/weave/v1/ticket?key=apiKey
96   std::string GetServiceURL(const std::string& subpath = {},
97                             const WebParamList& params = {}) const;
98 
99   // Returns a service URL to access the registered device on GCD server.
100   // The base URL used to construct the full URL looks like this:
101   //    https://www.googleapis.com/weave/v1/devices/<cloud_id>/
102   std::string GetDeviceURL(const std::string& subpath = {},
103                            const WebParamList& params = {}) const;
104 
105   // Similar to GetServiceURL, GetOAuthURL() returns a URL of OAuth 2.0 server.
106   // The base URL used is https://accounts.google.com/o/oauth2/.
107   std::string GetOAuthURL(const std::string& subpath = {},
108                           const WebParamList& params = {}) const;
109 
110   // Starts GCD device if credentials available.
111   void Start();
112 
113   // Updates a command (override from CloudCommandUpdateInterface).
114   void UpdateCommand(const std::string& command_id,
115                      const base::DictionaryValue& command_patch,
116                      const DoneCallback& callback) override;
117 
118   // TODO(vitalybuka): remove getters and pass config to dependent code.
GetSettings()119   const Config::Settings& GetSettings() const { return config_->GetSettings(); }
GetMutableConfig()120   Config* GetMutableConfig() { return config_; }
121 
GetGcdState()122   GcdState GetGcdState() const { return gcd_state_; }
123 
124  private:
125   friend class DeviceRegistrationInfoTest;
126 
AsWeakPtr()127   base::WeakPtr<DeviceRegistrationInfo> AsWeakPtr() {
128     return weak_factory_.GetWeakPtr();
129   }
130 
131   // Checks whether we have credentials generated during registration.
132   bool HaveRegistrationCredentials() const;
133   // Calls HaveRegistrationCredentials() and logs an error if no credentials
134   // are available.
135   bool VerifyRegistrationCredentials(ErrorPtr* error) const;
136 
137   // Cause DeviceRegistrationInfo to attempt to connect to cloud server on
138   // its own later.
139   void ScheduleCloudConnection(const base::TimeDelta& delay);
140 
141   // Initiates the connection to the cloud server.
142   // Device will do required start up chores and then start to listen
143   // to new commands.
144   void ConnectToCloud(ErrorPtr error);
145   // Notification called when ConnectToCloud() succeeds.
146   void OnConnectedToCloud(ErrorPtr error);
147 
148   // Forcibly refreshes the access token.
149   void RefreshAccessToken(const DoneCallback& callback);
150 
151   // Callbacks for RefreshAccessToken().
152   void OnRefreshAccessTokenDone(
153       const DoneCallback& callback,
154       std::unique_ptr<provider::HttpClient::Response> response,
155       ErrorPtr error);
156 
157   // Parse the OAuth response, and sets registration status to
158   // kInvalidCredentials if our registration is no longer valid.
159   std::unique_ptr<base::DictionaryValue> ParseOAuthResponse(
160       const provider::HttpClient::Response& response,
161       ErrorPtr* error);
162 
163   // This attempts to open a notification channel. The channel needs to be
164   // restarted anytime the access_token is refreshed.
165   void StartNotificationChannel();
166 
167   // Do a HTTPS request to cloud services.
168   // Handles many cases like reauthorization, 5xx HTTP response codes
169   // and device removal.  It is a recommended way to do cloud API
170   // requests.
171   // TODO(antonm): Consider moving into some other class.
172   void DoCloudRequest(provider::HttpClient::Method method,
173                       const std::string& url,
174                       const base::DictionaryValue* body,
175                       const CloudRequestDoneCallback& callback);
176 
177   // Helper for DoCloudRequest().
178   struct CloudRequestData {
179     provider::HttpClient::Method method;
180     std::string url;
181     std::string body;
182     CloudRequestDoneCallback callback;
183   };
184   void SendCloudRequest(const std::shared_ptr<const CloudRequestData>& data);
185   void OnCloudRequestDone(
186       const std::shared_ptr<const CloudRequestData>& data,
187       std::unique_ptr<provider::HttpClient::Response> response,
188       ErrorPtr error);
189   void RetryCloudRequest(const std::shared_ptr<const CloudRequestData>& data);
190   void OnAccessTokenRefreshed(
191       const std::shared_ptr<const CloudRequestData>& data,
192       ErrorPtr error);
193   void CheckAccessTokenError(ErrorPtr error);
194 
195   void UpdateDeviceResource(const DoneCallback& callback);
196   void StartQueuedUpdateDeviceResource();
197   void OnUpdateDeviceResourceDone(const base::DictionaryValue& device_info,
198                                   ErrorPtr error);
199   void OnUpdateDeviceResourceError(ErrorPtr error);
200 
201   void SendAuthInfo();
202   void OnSendAuthInfoDone(const std::vector<uint8_t>& token,
203                           const base::DictionaryValue& body,
204                           ErrorPtr error);
205 
206   // Callback from GetDeviceInfo() to retrieve the device resource timestamp
207   // and retry UpdateDeviceResource() call.
208   void OnDeviceInfoRetrieved(const base::DictionaryValue& device_info,
209                              ErrorPtr error);
210 
211   // Extracts the timestamp from the device resource and sets it to
212   // |last_device_resource_updated_timestamp_|.
213   // Returns false if the "lastUpdateTimeMs" field is not found in the device
214   // resource or it is invalid.
215   bool UpdateDeviceInfoTimestamp(const base::DictionaryValue& device_info);
216 
217   void FetchCommands(
218       const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback,
219       const std::string& reason);
220   void OnFetchCommandsDone(
221       const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback,
222       const base::DictionaryValue& json,
223       ErrorPtr);
224   // Called when FetchCommands completes (with either success or error).
225   // This method reschedules any pending/queued fetch requests.
226   void OnFetchCommandsReturned();
227 
228   // Processes the command list that is fetched from the server on connection.
229   // Aborts commands which are in transitional states and publishes queued
230   // commands which are queued.
231   void ProcessInitialCommandList(const base::ListValue& commands,
232                                  ErrorPtr error);
233 
234   void PublishCommands(const base::ListValue& commands, ErrorPtr error);
235   void PublishCommand(const base::DictionaryValue& command);
236 
237   // Helper function to pull the pending command list from the server using
238   // FetchCommands() and make them available on D-Bus with PublishCommands().
239   // |backup_fetch| is set to true when performing backup ("just-in-case")
240   // command fetch while XMPP channel is up and running.
241   void FetchAndPublishCommands(const std::string& reason);
242 
243   void PublishStateUpdates();
244   void OnPublishStateDone(ComponentManager::UpdateID update_id,
245                           const base::DictionaryValue& reply,
246                           ErrorPtr error);
247   void OnPublishStateError(ErrorPtr error);
248 
249   // If unrecoverable error occurred (e.g. error parsing command instance),
250   // notify the server that the command is aborted by the device.
251   void NotifyCommandAborted(const std::string& command_id, ErrorPtr error);
252 
253   // Builds Cloud API devices collection REST resource which matches
254   // current state of the device including command definitions
255   // for all supported commands and current device state.
256   std::unique_ptr<base::DictionaryValue> BuildDeviceResource() const;
257 
258   void SetGcdState(GcdState new_state);
259   void SetDeviceId(const std::string& cloud_id);
260 
261   // Callback called when command definitions are changed to re-publish new CDD.
262   void OnTraitDefsChanged();
263   void OnComponentTreeChanged();
264   void OnStateChanged();
265 
266   // Overrides from NotificationDelegate.
267   void OnConnected(const std::string& channel_name) override;
268   void OnDisconnected() override;
269   void OnPermanentFailure() override;
270   void OnCommandCreated(const base::DictionaryValue& command,
271                         const std::string& channel_name) override;
272   void OnDeviceDeleted(const std::string& cloud_id) override;
273 
274   // Wipes out the device registration information and stops server connections.
275   void RemoveCredentials();
276 
277   void RegisterDeviceError(const DoneCallback& callback, ErrorPtr error);
278   void RegisterDeviceOnTicketSent(
279       const std::string& ticket_id,
280       const DoneCallback& callback,
281       std::unique_ptr<provider::HttpClient::Response> response,
282       ErrorPtr error);
283   void RegisterDeviceOnTicketFinalized(
284       const DoneCallback& callback,
285       std::unique_ptr<provider::HttpClient::Response> response,
286       ErrorPtr error);
287   void RegisterDeviceOnAuthCodeSent(
288       const std::string& cloud_id,
289       const std::string& robot_account,
290       const DoneCallback& callback,
291       std::unique_ptr<provider::HttpClient::Response> response,
292       ErrorPtr error);
293 
294   // Transient data
295   std::string access_token_;
296   base::Time access_token_expiration_;
297   // The time stamp of last device resource update on the server.
298   std::string last_device_resource_updated_timestamp_;
299   // Set to true if the device has connected to the cloud server correctly.
300   // At this point, normal state and command updates can be dispatched to the
301   // server.
302   bool connected_to_cloud_{false};
303 
304   // HTTP transport used for communications.
305   provider::HttpClient* http_client_{nullptr};
306 
307   provider::TaskRunner* task_runner_{nullptr};
308 
309   Config* config_{nullptr};
310 
311   // Global component manager.
312   ComponentManager* component_manager_{nullptr};
313 
314   // Backoff manager for DoCloudRequest() method.
315   std::unique_ptr<BackoffEntry::Policy> cloud_backoff_policy_;
316   std::unique_ptr<BackoffEntry> cloud_backoff_entry_;
317   std::unique_ptr<BackoffEntry> oauth2_backoff_entry_;
318 
319   // Flag set to true while a device state update patch request is in flight
320   // to the cloud server.
321   bool device_state_update_pending_{false};
322 
323   // Set to true when command queue fetch request is in flight to the server.
324   bool fetch_commands_request_sent_{false};
325   // Set to true when another command queue fetch request is queued while
326   // another one was in flight.
327   bool fetch_commands_request_queued_{false};
328   // Specifies the reason for queued command fetch request.
329   std::string queued_fetch_reason_;
330 
331   using ResourceUpdateCallbackList = std::vector<DoneCallback>;
332   // Callbacks for device resource update request currently in flight to the
333   // cloud server.
334   ResourceUpdateCallbackList in_progress_resource_update_callbacks_;
335   // Callbacks for device resource update requests queued while another request
336   // is in flight to the cloud server.
337   ResourceUpdateCallbackList queued_resource_update_callbacks_;
338 
339   bool auth_info_update_inprogress_{false};
340 
341   std::unique_ptr<NotificationChannel> primary_notification_channel_;
342   std::unique_ptr<PullChannel> pull_channel_;
343   NotificationChannel* current_notification_channel_{nullptr};
344   bool notification_channel_starting_{false};
345 
346   provider::Network* network_{nullptr};
347   privet::AuthManager* auth_manager_{nullptr};
348 
349   // Tracks our GCD state.
350   GcdState gcd_state_{GcdState::kUnconfigured};
351 
352   std::vector<Device::GcdStateChangedCallback> gcd_state_changed_callbacks_;
353 
354   base::WeakPtrFactory<DeviceRegistrationInfo> weak_factory_{this};
355   DISALLOW_COPY_AND_ASSIGN(DeviceRegistrationInfo);
356 };
357 
358 }  // namespace weave
359 
360 #endif  // LIBWEAVE_SRC_DEVICE_REGISTRATION_INFO_H_
361