blob: 5108e35bd27373887121d9d11b07544cf6a5da54 [file] [log] [blame]
Alex Vakulenko8e34d392014-04-29 11:02:56 -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 <base/json/json_reader.h>
6#include <base/values.h>
7#include <gtest/gtest.h>
8
9#include "buffet/bind_lambda.h"
10#include "buffet/device_registration_info.h"
11#include "buffet/device_registration_storage_keys.h"
12#include "buffet/http_request.h"
13#include "buffet/http_transport_fake.h"
14#include "buffet/mime_utils.h"
Christopher Wiley006e94e2014-05-02 13:44:48 -070015#include "buffet/storage_impls.h"
Alex Vakulenko8e34d392014-04-29 11:02:56 -070016
Alex Vakulenkob3aac252014-05-07 17:35:24 -070017using namespace buffet; // NOLINT(build/namespaces)
Alex Vakulenkoaf23b322014-05-08 16:25:45 -070018using namespace buffet::http; // NOLINT(build/namespaces)
Alex Vakulenko8e34d392014-04-29 11:02:56 -070019
20namespace {
Alex Vakulenko8e34d392014-04-29 11:02:56 -070021
22namespace test_data {
23
24const char kServiceURL[] = "http://gcd.server.com/";
25const char kOAuthURL[] = "http://oauth.server.com/";
26const char kApiKey[] = "GOadRdTf9FERf0k4w6EFOof56fUJ3kFDdFL3d7f";
27const char kClientId[] = "123543821385-sfjkjshdkjhfk234sdfsdfkskd"
28 "fkjh7f.apps.googleusercontent.com";
29const char kClientSecret[] = "5sdGdGlfolGlrFKfdFlgP6FG";
30const char kDeviceId[] = "4a7ea2d1-b331-1e1f-b206-e863c7635196";
31const char kClaimTicketId[] = "RTcUE";
32const char kAccessToken[] = "ya29.1.AADtN_V-dLUM-sVZ0qVjG9Dxm5NgdS9J"
33 "Mx_JLUqhC9bED_YFjzHZtYt65ZzXCS35NMAeaVZ"
34 "Dei530-w0yE2urpQ";
35const char kRefreshToken[] = "1/zQmxR6PKNvhcxf9SjXUrCjcmCrcqRKXctc6cp"
36 "1nI-GQ";
37const char kRobotAccountAuthCode[] = "4/Mf_ujEhPejVhOq-OxW9F5cSOnWzx."
38 "YgciVjTYGscRshQV0ieZDAqiTIjMigI";
39const char kRobotAccountEmail[] = "6ed0b3f54f9bd619b942f4ad2441c252@"
40 "clouddevices.gserviceaccount.com";
41const char kUserAccountAuthCode[] = "2/sd_GD1TGFKpJOLJ34-0g5fK0fflp.GlT"
42 "I0F5g7hNtFgj5HFGOf8FlGK9eflO";
43const char kUserAccessToken[] = "sd56.4.FGDjG_F-gFGF-dFG6gGOG9Dxm5NgdS9"
44 "JMx_JLUqhC9bED_YFjLKjlkjLKJlkjLKjlKJea"
45 "VZDei530-w0yE2urpQ";
46const char kUserRefreshToken[] = "1/zQLKjlKJlkLkLKjLkjLKjLkjLjLkjl0ftc6"
47 "cp1nI-GQ";
48
49} // namespace test_data
50
51// Fill in the storage with default environment information (URLs, etc).
52void InitDefaultStorage(base::DictionaryValue* data) {
53 data->SetString(storage_keys::kClientId, test_data::kClientId);
54 data->SetString(storage_keys::kClientSecret, test_data::kClientSecret);
55 data->SetString(storage_keys::kApiKey, test_data::kApiKey);
56 data->SetString(storage_keys::kRefreshToken, "");
57 data->SetString(storage_keys::kDeviceId, "");
58 data->SetString(storage_keys::kOAuthURL, test_data::kOAuthURL);
59 data->SetString(storage_keys::kServiceURL, test_data::kServiceURL);
60 data->SetString(storage_keys::kRobotAccount, "");
61}
62
63// Add the test device registration information.
64void SetDefaultDeviceRegistration(base::DictionaryValue* data) {
65 data->SetString(storage_keys::kRefreshToken, test_data::kRefreshToken);
66 data->SetString(storage_keys::kDeviceId, test_data::kDeviceId);
67 data->SetString(storage_keys::kRobotAccount, test_data::kRobotAccountEmail);
68}
69
70void OAuth2Handler(const fake::ServerRequest& request,
71 fake::ServerResponse* response) {
72 base::DictionaryValue json;
73 if (request.GetFormField("grant_type") == "refresh_token") {
74 // Refresh device access token.
75 EXPECT_EQ(test_data::kRefreshToken, request.GetFormField("refresh_token"));
76 EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
77 EXPECT_EQ(test_data::kClientSecret, request.GetFormField("client_secret"));
78 json.SetString("access_token", test_data::kAccessToken);
79 } else if (request.GetFormField("grant_type") == "authorization_code") {
80 // Obtain access token.
81 std::string code = request.GetFormField("code");
82 if (code == test_data::kUserAccountAuthCode) {
83 // Get user access token.
84 EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
85 EXPECT_EQ(test_data::kClientSecret,
86 request.GetFormField("client_secret"));
87 EXPECT_EQ("urn:ietf:wg:oauth:2.0:oob",
88 request.GetFormField("redirect_uri"));
89 json.SetString("access_token", test_data::kUserAccessToken);
90 json.SetString("token_type", "Bearer");
91 json.SetString("refresh_token", test_data::kUserRefreshToken);
92 } else if (code == test_data::kRobotAccountAuthCode) {
93 // Get device access token.
94 EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
95 EXPECT_EQ(test_data::kClientSecret,
96 request.GetFormField("client_secret"));
97 EXPECT_EQ("oob", request.GetFormField("redirect_uri"));
98 EXPECT_EQ("https://www.googleapis.com/auth/clouddevices",
99 request.GetFormField("scope"));
100 json.SetString("access_token", test_data::kAccessToken);
101 json.SetString("token_type", "Bearer");
102 json.SetString("refresh_token", test_data::kRefreshToken);
103 } else {
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700104 FAIL() << "Unexpected authorization code";
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700105 }
106 } else {
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700107 FAIL() << "Unexpected grant type";
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700108 }
109 json.SetInteger("expires_in", 3600);
110 response->ReplyJson(status_code::Ok, &json);
111}
112
113void DeviceInfoHandler(const fake::ServerRequest& request,
114 fake::ServerResponse* response) {
115 std::string auth = "Bearer ";
116 auth += test_data::kAccessToken;
117 EXPECT_EQ(auth, request.GetHeader(http::request_header::kAuthorization));
118 response->ReplyJson(status_code::Ok, {
119 {"channel.supportedType", "xmpp"},
120 {"deviceKind", "vendor"},
121 {"id", test_data::kDeviceId},
122 {"kind", "clouddevices#device"},
123 });
124}
125
126void FinalizeTicketHandler(const fake::ServerRequest& request,
127 fake::ServerResponse* response) {
128 EXPECT_EQ(test_data::kApiKey, request.GetFormField("key"));
129 EXPECT_TRUE(request.GetData().empty());
130
131 response->ReplyJson(status_code::Ok, {
132 {"id", test_data::kClaimTicketId},
133 {"kind", "clouddevices#registrationTicket"},
134 {"oauthClientId", test_data::kClientId},
135 {"userEmail", "user@email.com"},
136 {"deviceDraft.id", test_data::kDeviceId},
137 {"deviceDraft.kind", "clouddevices#device"},
138 {"deviceDraft.channel.supportedType", "xmpp"},
139 {"robotAccountEmail", test_data::kRobotAccountEmail},
140 {"robotAccountAuthorizationCode", test_data::kRobotAccountAuthCode},
141 });
142}
143
144} // anonymous namespace
145
146// This is a helper class that allows the unit tests to set the private
147// member DeviceRegistrationInfo::ticket_id_, since TestHelper is declared
148// as a friend to DeviceRegistrationInfo.
149class DeviceRegistrationInfo::TestHelper {
150 public:
151 static void SetTestTicketId(DeviceRegistrationInfo* info) {
152 info->ticket_id_ = test_data::kClaimTicketId;
153 }
154};
155
156class DeviceRegistrationInfoTest : public ::testing::Test {
157 protected:
158 virtual void SetUp() override {
159 InitDefaultStorage(&data);
160 storage = std::make_shared<MemStorage>();
161 storage->Save(&data);
162 transport = std::make_shared<fake::Transport>();
163 dev_reg = std::unique_ptr<DeviceRegistrationInfo>(
164 new DeviceRegistrationInfo(transport, storage));
165 }
166
167 base::DictionaryValue data;
168 std::shared_ptr<MemStorage> storage;
169 std::shared_ptr<fake::Transport> transport;
170 std::unique_ptr<DeviceRegistrationInfo> dev_reg;
171};
172
173////////////////////////////////////////////////////////////////////////////////
174TEST_F(DeviceRegistrationInfoTest, GetServiceURL) {
175 EXPECT_TRUE(dev_reg->Load());
176 EXPECT_EQ(test_data::kServiceURL, dev_reg->GetServiceURL());
177 std::string url = test_data::kServiceURL;
178 url += "registrationTickets";
179 EXPECT_EQ(url, dev_reg->GetServiceURL("registrationTickets"));
180 url += "?key=";
181 url += test_data::kApiKey;
182 EXPECT_EQ(url, dev_reg->GetServiceURL("registrationTickets", {
183 {"key", test_data::kApiKey}
184 }));
185 url += "&restart=true";
186 EXPECT_EQ(url, dev_reg->GetServiceURL("registrationTickets", {
187 {"key", test_data::kApiKey},
188 {"restart", "true"},
189 }));
190}
191
192TEST_F(DeviceRegistrationInfoTest, GetOAuthURL) {
193 EXPECT_TRUE(dev_reg->Load());
194 EXPECT_EQ(test_data::kOAuthURL, dev_reg->GetOAuthURL());
195 std::string url = test_data::kOAuthURL;
196 url += "auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fclouddevices&";
197 url += "redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&";
198 url += "response_type=code&";
199 url += "client_id=";
200 url += test_data::kClientId;
201 EXPECT_EQ(url, dev_reg->GetOAuthURL("auth", {
202 {"scope", "https://www.googleapis.com/auth/clouddevices"},
203 {"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
204 {"response_type", "code"},
205 {"client_id", test_data::kClientId}
206 }));
207}
208
209TEST_F(DeviceRegistrationInfoTest, CheckRegistration) {
210 EXPECT_TRUE(dev_reg->Load());
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700211 EXPECT_FALSE(dev_reg->CheckRegistration(nullptr));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700212 EXPECT_EQ(0, transport->GetRequestCount());
213
214 SetDefaultDeviceRegistration(&data);
215 storage->Save(&data);
216 EXPECT_TRUE(dev_reg->Load());
217
218 transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost,
219 base::Bind(OAuth2Handler));
220 transport->ResetRequestCount();
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700221 EXPECT_TRUE(dev_reg->CheckRegistration(nullptr));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700222 EXPECT_EQ(1, transport->GetRequestCount());
223}
224
225TEST_F(DeviceRegistrationInfoTest, GetDeviceInfo) {
226 SetDefaultDeviceRegistration(&data);
227 storage->Save(&data);
228 EXPECT_TRUE(dev_reg->Load());
229
230 transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost,
231 base::Bind(OAuth2Handler));
232 transport->AddHandler(dev_reg->GetDeviceURL(), request_type::kGet,
233 base::Bind(DeviceInfoHandler));
234 transport->ResetRequestCount();
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700235 auto device_info = dev_reg->GetDeviceInfo(nullptr);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700236 EXPECT_EQ(2, transport->GetRequestCount());
237 EXPECT_NE(nullptr, device_info.get());
238 base::DictionaryValue* dict = nullptr;
239 EXPECT_TRUE(device_info->GetAsDictionary(&dict));
240 std::string id;
241 EXPECT_TRUE(dict->GetString("id", &id));
242 EXPECT_EQ(test_data::kDeviceId, id);
243}
244
245TEST_F(DeviceRegistrationInfoTest, GetDeviceId) {
246 SetDefaultDeviceRegistration(&data);
247 storage->Save(&data);
248 EXPECT_TRUE(dev_reg->Load());
249
250 transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost,
251 base::Bind(OAuth2Handler));
252 transport->AddHandler(dev_reg->GetDeviceURL(), request_type::kGet,
253 base::Bind(DeviceInfoHandler));
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700254 std::string id = dev_reg->GetDeviceId(nullptr);
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700255 EXPECT_EQ(test_data::kDeviceId, id);
256}
257
258TEST_F(DeviceRegistrationInfoTest, StartRegistration) {
259 EXPECT_TRUE(dev_reg->Load());
260
261 auto create_ticket = [](const fake::ServerRequest& request,
262 fake::ServerResponse* response) {
263 EXPECT_EQ(test_data::kApiKey, request.GetFormField("key"));
264 auto json = request.GetDataAsJson();
265 EXPECT_NE(nullptr, json.get());
266 std::string value;
267 EXPECT_TRUE(json->GetString("deviceDraft.channel.supportedType", &value));
268 EXPECT_EQ("xmpp", value);
269 EXPECT_TRUE(json->GetString("oauthClientId", &value));
270 EXPECT_EQ(test_data::kClientId, value);
271 EXPECT_TRUE(json->GetString("deviceDraft.deviceKind", &value));
272 EXPECT_EQ("vendor", value);
273
274 base::DictionaryValue json_resp;
275 json_resp.SetString("id", test_data::kClaimTicketId);
276 json_resp.SetString("kind", "clouddevices#registrationTicket");
277 json_resp.SetString("oauthClientId", test_data::kClientId);
278 base::DictionaryValue* device_draft = nullptr;
279 EXPECT_TRUE(json->GetDictionary("deviceDraft", &device_draft));
280 device_draft = device_draft->DeepCopy();
281 device_draft->SetString("id", test_data::kDeviceId);
282 device_draft->SetString("kind", "clouddevices#device");
283 json_resp.Set("deviceDraft", device_draft);
284
285 response->ReplyJson(status_code::Ok, &json_resp);
286 };
287
288 transport->AddHandler(dev_reg->GetServiceURL("registrationTickets"),
289 request_type::kPost,
290 base::Bind(create_ticket));
291 std::map<std::string, std::shared_ptr<base::Value>> params;
292 std::string json_resp = dev_reg->StartRegistration(params, nullptr);
293 auto json = std::unique_ptr<base::Value>(base::JSONReader::Read(json_resp));
294 EXPECT_NE(nullptr, json.get());
295 base::DictionaryValue* dict = nullptr;
296 EXPECT_TRUE(json->GetAsDictionary(&dict));
297 std::string value;
298 EXPECT_TRUE(dict->GetString("ticket_id", &value));
299 EXPECT_EQ(test_data::kClaimTicketId, value);
300}
301
302TEST_F(DeviceRegistrationInfoTest, FinishRegistration_NoAuth) {
303 // Test finalizing ticket with no user authorization token.
304 // This assumes that a client would patch in their email separately.
305 EXPECT_TRUE(dev_reg->Load());
306
307 // General ticket finalization handler.
308 std::string ticket_url =
309 dev_reg->GetServiceURL("registrationTickets/" +
310 std::string(test_data::kClaimTicketId));
311 transport->AddHandler(ticket_url + "/finalize", request_type::kPost,
312 base::Bind(FinalizeTicketHandler));
313
314 transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost,
315 base::Bind(OAuth2Handler));
316
Christopher Wiley006e94e2014-05-02 13:44:48 -0700317 storage->reset_save_count();
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700318 DeviceRegistrationInfo::TestHelper::SetTestTicketId(dev_reg.get());
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700319 EXPECT_TRUE(dev_reg->FinishRegistration("", nullptr));
320 EXPECT_EQ(1, storage->save_count()); // The device info must have been saved.
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700321 EXPECT_EQ(2, transport->GetRequestCount());
322
323 // Validate the device info saved to storage...
324 auto storage_data = storage->Load();
325 base::DictionaryValue* dict = nullptr;
326 EXPECT_TRUE(storage_data->GetAsDictionary(&dict));
327 std::string value;
328 EXPECT_TRUE(dict->GetString(storage_keys::kApiKey, &value));
329 EXPECT_EQ(test_data::kApiKey, value);
330 EXPECT_TRUE(dict->GetString(storage_keys::kClientId, &value));
331 EXPECT_EQ(test_data::kClientId, value);
332 EXPECT_TRUE(dict->GetString(storage_keys::kClientSecret, &value));
333 EXPECT_EQ(test_data::kClientSecret, value);
334 EXPECT_TRUE(dict->GetString(storage_keys::kDeviceId, &value));
335 EXPECT_EQ(test_data::kDeviceId, value);
336 EXPECT_TRUE(dict->GetString(storage_keys::kOAuthURL, &value));
337 EXPECT_EQ(test_data::kOAuthURL, value);
338 EXPECT_TRUE(dict->GetString(storage_keys::kRefreshToken, &value));
339 EXPECT_EQ(test_data::kRefreshToken, value);
340 EXPECT_TRUE(dict->GetString(storage_keys::kRobotAccount, &value));
341 EXPECT_EQ(test_data::kRobotAccountEmail, value);
342 EXPECT_TRUE(dict->GetString(storage_keys::kServiceURL, &value));
343 EXPECT_EQ(test_data::kServiceURL, value);
344}
345
346TEST_F(DeviceRegistrationInfoTest, FinishRegistration_Auth) {
347 // Test finalizing ticket with user authorization token.
348 EXPECT_TRUE(dev_reg->Load());
349
350 // General ticket finalization handler.
351 std::string ticket_url =
352 dev_reg->GetServiceURL("registrationTickets/" +
353 std::string(test_data::kClaimTicketId));
354 transport->AddHandler(ticket_url + "/finalize", request_type::kPost,
355 base::Bind(FinalizeTicketHandler));
356
357 transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost,
358 base::Bind(OAuth2Handler));
359
360 // Handle patching in the user email onto the device record.
361 auto email_patch_handler = [](const fake::ServerRequest& request,
362 fake::ServerResponse* response) {
363 std::string auth_header = "Bearer ";
364 auth_header += test_data::kUserAccessToken;
365 EXPECT_EQ(auth_header,
366 request.GetHeader(http::request_header::kAuthorization));
367 auto json = request.GetDataAsJson();
368 EXPECT_NE(nullptr, json.get());
369 std::string value;
370 EXPECT_TRUE(json->GetString("userEmail", &value));
371 EXPECT_EQ("me", value);
372
373 response->ReplyJson(status_code::Ok, {
374 {"id", test_data::kClaimTicketId},
375 {"kind", "clouddevices#registrationTicket"},
376 {"oauthClientId", test_data::kClientId},
377 {"userEmail", "user@email.com"},
378 {"deviceDraft.id", test_data::kDeviceId},
379 {"deviceDraft.kind", "clouddevices#device"},
380 {"deviceDraft.channel.supportedType", "xmpp"},
381 });
382 };
383 transport->AddHandler(ticket_url, request_type::kPatch,
384 base::Bind(email_patch_handler));
385
Christopher Wiley006e94e2014-05-02 13:44:48 -0700386 storage->reset_save_count();
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700387 DeviceRegistrationInfo::TestHelper::SetTestTicketId(dev_reg.get());
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700388 EXPECT_TRUE(dev_reg->FinishRegistration(test_data::kUserAccountAuthCode,
389 nullptr));
390 EXPECT_EQ(1, storage->save_count()); // The device info must have been saved.
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700391 EXPECT_EQ(4, transport->GetRequestCount());
392}