Upload local auth data

Code still does not reset local_auth_info_changed. For now we will
update info on every refresh of access token.

BUG:25934771
Change-Id: Iab639395b7fc8dbba57769a8a018180fc8f1b0e1
Reviewed-on: https://weave-review.googlesource.com/1828
Reviewed-by: Alex Vakulenko <avakulenko@google.com>
diff --git a/src/device_registration_info.cc b/src/device_registration_info.cc
index b29bd52..a6d863c 100644
--- a/src/device_registration_info.cc
+++ b/src/device_registration_info.cc
@@ -27,6 +27,7 @@
 #include "src/http_constants.h"
 #include "src/json_error_codes.h"
 #include "src/notification/xmpp_channel.h"
+#include "src/privet/auth_manager.h"
 #include "src/string_utils.h"
 #include "src/utils.h"
 
@@ -234,6 +235,17 @@
   return code >= http::kContinue && code < http::kBadRequest;
 }
 
+std::unique_ptr<base::DictionaryValue> BuildDeviceLocalAuth(
+    const std::string& id,
+    const std::string& client_token,
+    const std::string& cert_fingerprint) {
+  std::unique_ptr<base::DictionaryValue> auth{new base::DictionaryValue};
+  auth->SetString("localId", id);
+  auth->SetString("clientToken", client_token);
+  auth->SetString("certFingerprint", cert_fingerprint);
+  return auth;
+}
+
 }  // anonymous namespace
 
 DeviceRegistrationInfo::DeviceRegistrationInfo(
@@ -430,6 +442,9 @@
     // Now that we have a new access token, retry the connection.
     StartNotificationChannel();
   }
+
+  SendAuthInfo();
+
   callback.Run(nullptr);
 }
 
@@ -639,6 +654,7 @@
   task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), {});
 
   StartNotificationChannel();
+  SendAuthInfo();
 
   // We're going to respond with our success immediately and we'll connect to
   // cloud shortly after.
@@ -914,6 +930,48 @@
                             AsWeakPtr()));
 }
 
+void DeviceRegistrationInfo::SendAuthInfo() {
+  if (!auth_manager_ || auth_info_update_inprogress_)
+    return;
+  auth_info_update_inprogress_ = true;
+
+  std::string id = GetSettings().device_id;
+  std::string token = Base64Encode(auth_manager_->GetRootDeviceToken());
+  std::string fingerprint =
+      Base64Encode(auth_manager_->GetCertificateFingerprint());
+
+  std::unique_ptr<base::DictionaryValue> auth =
+      BuildDeviceLocalAuth(id, token, fingerprint);
+
+  // TODO(vitalybuka): Remove args from URL when server is ready.
+  std::string url =
+      GetDeviceURL("upsertLocalAuthInfo", {{"localid", id},
+                                           {"clienttoken", token},
+                                           {"certfingerprint", fingerprint}});
+  DoCloudRequest(
+      HttpClient::Method::kPost, url, auth.get(),
+      base::Bind(&DeviceRegistrationInfo::OnSendAuthInfoDone, AsWeakPtr()));
+}
+
+void DeviceRegistrationInfo::OnSendAuthInfoDone(
+    const base::DictionaryValue& body,
+    ErrorPtr error) {
+  CHECK(auth_info_update_inprogress_);
+  auth_info_update_inprogress_ = false;
+
+  if (!error) {
+    // TODO(vitalybuka): Enable this when we start uploading real data.
+    // Config::Transaction change{config_.get()};
+    // change.set_local_auth_info_changed(false);
+    // change.Commit();
+    return;
+  }
+
+  task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&DeviceRegistrationInfo::SendAuthInfo, AsWeakPtr()),
+      {});
+}
+
 void DeviceRegistrationInfo::OnDeviceInfoRetrieved(
     const base::DictionaryValue& device_info,
     ErrorPtr error) {
diff --git a/src/device_registration_info.h b/src/device_registration_info.h
index c196e0c..04fb7b6 100644
--- a/src/device_registration_info.h
+++ b/src/device_registration_info.h
@@ -201,6 +201,9 @@
                                   ErrorPtr error);
   void OnUpdateDeviceResourceError(ErrorPtr error);
 
+  void SendAuthInfo();
+  void OnSendAuthInfoDone(const base::DictionaryValue& body, ErrorPtr error);
+
   // Callback from GetDeviceInfo() to retrieve the device resource timestamp
   // and retry UpdateDeviceResource() call.
   void OnDeviceInfoRetrieved(const base::DictionaryValue& device_info,
@@ -334,6 +337,8 @@
   // is in flight to the cloud server.
   ResourceUpdateCallbackList queued_resource_update_callbacks_;
 
+  bool auth_info_update_inprogress_{false};
+
   std::unique_ptr<NotificationChannel> primary_notification_channel_;
   std::unique_ptr<PullChannel> pull_channel_;
   NotificationChannel* current_notification_channel_{nullptr};
diff --git a/src/device_registration_info_unittest.cc b/src/device_registration_info_unittest.cc
index d03ae15..cc519d2 100644
--- a/src/device_registration_info_unittest.cc
+++ b/src/device_registration_info_unittest.cc
@@ -16,9 +16,12 @@
 #include "src/bind_lambda.h"
 #include "src/component_manager_impl.h"
 #include "src/http_constants.h"
+#include "src/privet/auth_manager.h"
+#include "src/test/mock_clock.h"
 
 using testing::_;
 using testing::AtLeast;
+using testing::HasSubstr;
 using testing::Invoke;
 using testing::InvokeWithoutArgs;
 using testing::Mock;
@@ -49,6 +52,7 @@
     "fkjh7f.apps.googleusercontent.com";
 const char kClientSecret[] = "5sdGdGlfolGlrFKfdFlgP6FG";
 const char kCloudId[] = "4a7ea2d1-b331-1e1f-b206-e863c7635196";
+const char kDeviceId[] = "f6885e46-b432-42d7-86a5-d759bfb61f62";
 const char kClaimTicketId[] = "RTcUE";
 const char kAccessToken[] =
     "ya29.1.AADtN_V-dLUM-sVZ0qVjG9Dxm5NgdS9J"
@@ -63,6 +67,12 @@
 const char kRobotAccountEmail[] =
     "6ed0b3f54f9bd619b942f4ad2441c252@"
     "clouddevices.gserviceaccount.com";
+const char kAuthInfo[] = R"({
+  "certFingerprint":
+  "FQY6BEINDjw3FgsmYChRWgMzMhc4TC8uG0UUUFhdDz0=",
+  "clientToken": "UBPkqttkiWt5VWgICLP0eHuCQgECRgMaVm0+gA==",
+  "localId": "f6885e46-b432-42d7-86a5-d759bfb61f62"
+})";
 
 }  // namespace test_data
 
@@ -113,9 +123,11 @@
 class DeviceRegistrationInfoTest : public ::testing::Test {
  protected:
   void SetUp() override {
+    EXPECT_CALL(clock_, Now())
+        .WillRepeatedly(Return(base::Time::FromTimeT(1450000000)));
     dev_reg_.reset(new DeviceRegistrationInfo{&config_, &component_manager_,
                                               &task_runner_, &http_client_,
-                                              nullptr, nullptr});
+                                              nullptr, &auth_});
 
     ReloadDefaults();
   }
@@ -146,6 +158,7 @@
     dict.SetString("refresh_token", test_data::kRefreshToken);
     dict.SetString("cloud_id", test_data::kCloudId);
     dict.SetString("robot_account", test_data::kRobotAccountEmail);
+    dict.SetString("device_id", test_data::kDeviceId);
     std::string json_string;
     base::JSONWriter::WriteWithOptions(
         dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string);
@@ -183,6 +196,13 @@
   StrictMock<MockHttpClient> http_client_;
   base::DictionaryValue data_;
   Config config_{&config_store_};
+  test::MockClock clock_;
+  privet::AuthManager auth_{
+      {68, 52, 36, 95, 74, 89, 25, 2, 31, 5, 65, 87, 64, 32, 17, 26, 8, 73, 57,
+       16, 33, 82, 71, 10, 72, 62, 45, 1, 77, 97, 70, 24},
+      {21, 6, 58, 4, 66, 13, 14, 60, 55, 22, 11, 38, 96, 40, 81, 90, 3, 51, 50,
+       23, 56, 76, 47, 46, 27, 69, 20, 80, 88, 93, 15, 61},
+      &clock_};
   std::unique_ptr<DeviceRegistrationInfo> dev_reg_;
   ComponentManagerImpl component_manager_;
 };
@@ -242,6 +262,18 @@
         callback.Run(ReplyWithJson(200, json), nullptr);
       })));
 
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kPost, HasSubstr("upsertLocalAuthInfo"),
+                  HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
+      .WillOnce(WithArgs<3, 4>(
+          Invoke([](const std::string& data,
+                    const HttpClient::SendRequestCallback& callback) {
+            EXPECT_JSON_EQ(test_data::kAuthInfo, *CreateDictionaryValue(data));
+            base::DictionaryValue json;
+            callback.Run(ReplyWithJson(200, json), nullptr);
+          })));
+
   EXPECT_TRUE(RefreshAccessToken(nullptr));
   EXPECT_TRUE(HaveRegistrationCredentials());
 }
@@ -338,6 +370,8 @@
 }
 
 TEST_F(DeviceRegistrationInfoTest, RegisterDevice) {
+  ReloadSettings();
+
   auto json_traits = CreateDictionaryValue(R"({
     'base': {
       'commands': {
@@ -520,6 +554,18 @@
         callback.Run(ReplyWithJson(200, json), nullptr);
       })));
 
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kPost, HasSubstr("upsertLocalAuthInfo"),
+                  HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
+      .WillOnce(WithArgs<3, 4>(
+          Invoke([](const std::string& data,
+                    const HttpClient::SendRequestCallback& callback) {
+            EXPECT_JSON_EQ(test_data::kAuthInfo, *CreateDictionaryValue(data));
+            base::DictionaryValue json;
+            callback.Run(ReplyWithJson(200, json), nullptr);
+          })));
+
   bool done = false;
   dev_reg_->RegisterDevice(
       test_data::kClaimTicketId, base::Bind([this, &done](ErrorPtr error) {
diff --git a/src/weave_unittest.cc b/src/weave_unittest.cc
index 66850cf..24c6973 100644
--- a/src/weave_unittest.cc
+++ b/src/weave_unittest.cc
@@ -183,10 +183,11 @@
  protected:
   void SetUp() override {}
 
+  template <class UrlMatcher>
   void ExpectRequest(HttpClient::Method method,
-                     const std::string& url,
+                     const UrlMatcher& url_matcher,
                      const std::string& json_response) {
-    EXPECT_CALL(http_client_, SendRequest(method, url, _, _, _))
+    EXPECT_CALL(http_client_, SendRequest(method, url_matcher, _, _, _))
         .WillOnce(WithArgs<4>(Invoke([json_response](
             const HttpClient::SendRequestCallback& callback) {
           std::unique_ptr<provider::test::MockHttpClientResponse> response{
@@ -400,6 +401,9 @@
                 "https://accounts.google.com/o/oauth2/token",
                 kAuthTokenResponse);
 
+  ExpectRequest(HttpClient::Method::kPost, HasSubstr("upsertLocalAuthInfo"),
+                {});
+
   InitDnsSdPublishing(true, "DB");
 
   bool done = false;