1 // Copyright 2014 The Chromium 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.
5 #include "components/proximity_auth/cryptauth/cryptauth_client_impl.h"
7 #include "base/command_line.h"
8 #include "base/test/null_task_runner.h"
9 #include "components/proximity_auth/cryptauth/cryptauth_access_token_fetcher.h"
10 #include "components/proximity_auth/cryptauth/cryptauth_api_call_flow.h"
11 #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h"
12 #include "components/proximity_auth/switches.h"
13 #include "google_apis/gaia/fake_oauth2_token_service.h"
14 #include "net/url_request/test_url_fetcher_factory.h"
15 #include "net/url_request/url_request_test_util.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
22 using testing::Return
;
23 using testing::SaveArg
;
24 using testing::StrictMock
;
26 namespace proximity_auth
{
30 const char kTestGoogleApisUrl
[] = "https://www.testgoogleapis.com";
31 const char kAccessToken
[] = "access_token";
32 const char kPublicKey1
[] = "public_key1";
33 const char kPublicKey2
[] = "public_key2";
34 const char kBluetoothAddress1
[] = "AA:AA:AA:AA:AA:AA";
35 const char kBluetoothAddress2
[] = "BB:BB:BB:BB:BB:BB";
37 // Values for the DeviceClassifier field.
38 const int kDeviceOsVersionCode
= 100;
39 const int kDeviceSoftwareVersionCode
= 200;
40 const char kDeviceSoftwarePackage
[] = "cryptauth_client_unittest";
41 const cryptauth::DeviceType kDeviceType
= cryptauth::CHROME
;
43 // CryptAuthAccessTokenFetcher implementation simply returning a predetermined
45 class FakeCryptAuthAccessTokenFetcher
: public CryptAuthAccessTokenFetcher
{
47 FakeCryptAuthAccessTokenFetcher() : access_token_(kAccessToken
) {}
49 void FetchAccessToken(const AccessTokenCallback
& callback
) override
{
50 callback
.Run(access_token_
);
53 void set_access_token(const std::string
& access_token
) {
54 access_token_
= access_token
;
58 std::string access_token_
;
61 // Mock CryptAuthApiCallFlow, which handles the HTTP requests to CryptAuth.
62 class MockCryptAuthApiCallFlow
: public CryptAuthApiCallFlow
{
64 MockCryptAuthApiCallFlow() : CryptAuthApiCallFlow() {}
65 virtual ~MockCryptAuthApiCallFlow() {}
69 net::URLRequestContextGetter
* context
,
70 const std::string
& access_token
,
71 const std::string
& serialized_request
,
72 const ResultCallback
& result_callback
,
73 const ErrorCallback
& error_callback
));
76 DISALLOW_COPY_AND_ASSIGN(MockCryptAuthApiCallFlow
);
79 // Callback that should never be invoked.
81 void NotCalled(const T
& type
) {
85 // Callback that saves the result returned by CryptAuthClient.
87 void SaveResult(T
* out
, const T
& result
) {
93 class ProximityAuthCryptAuthClientTest
: public testing::Test
{
95 ProximityAuthCryptAuthClientTest()
96 : access_token_fetcher_(new FakeCryptAuthAccessTokenFetcher()),
97 api_call_flow_(new StrictMock
<MockCryptAuthApiCallFlow
>()),
99 new net::TestURLRequestContextGetter(new base::NullTaskRunner())),
100 serialized_request_(std::string()) {}
102 void SetUp() override
{
103 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
104 switches::kCryptAuthHTTPHost
, kTestGoogleApisUrl
);
106 cryptauth::DeviceClassifier device_classifier
;
107 device_classifier
.set_device_os_version_code(kDeviceOsVersionCode
);
108 device_classifier
.set_device_software_version_code(
109 kDeviceSoftwareVersionCode
);
110 device_classifier
.set_device_software_package(kDeviceSoftwarePackage
);
111 device_classifier
.set_device_type(kDeviceType
);
113 client_
.reset(new CryptAuthClientImpl(
114 make_scoped_ptr(api_call_flow_
), make_scoped_ptr(access_token_fetcher_
),
115 url_request_context_
, device_classifier
));
118 // Sets up an expectation and captures a CryptAuth API request to
120 void ExpectRequest(const std::string
& request_url
) {
121 GURL
url(request_url
);
122 EXPECT_CALL(*api_call_flow_
,
123 Start(url
, url_request_context_
.get(), kAccessToken
, _
, _
, _
))
124 .WillOnce(DoAll(SaveArg
<3>(&serialized_request_
),
125 SaveArg
<4>(&flow_result_callback_
),
126 SaveArg
<5>(&flow_error_callback_
)));
129 // Returns |response_proto| as the result to the current API request.
130 // ExpectResult() must have been called first.
131 void FinishApiCallFlow(const google::protobuf::MessageLite
* response_proto
) {
132 flow_result_callback_
.Run(response_proto
->SerializeAsString());
135 // Ends the current API request with |error_message|. ExpectResult() must have
136 // been called first.
137 void FailApiCallFlow(const std::string
& error_message
) {
138 flow_error_callback_
.Run(error_message
);
142 // Owned by |client_|.
143 FakeCryptAuthAccessTokenFetcher
* access_token_fetcher_
;
144 // Owned by |client_|.
145 StrictMock
<MockCryptAuthApiCallFlow
>* api_call_flow_
;
147 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_
;
148 scoped_ptr
<CryptAuthClient
> client_
;
150 std::string serialized_request_
;
151 CryptAuthApiCallFlow::ResultCallback flow_result_callback_
;
152 CryptAuthApiCallFlow::ErrorCallback flow_error_callback_
;
155 TEST_F(ProximityAuthCryptAuthClientTest
, GetMyDevicesSuccess
) {
157 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
158 "getmydevices?alt=proto");
160 cryptauth::GetMyDevicesResponse result_proto
;
161 cryptauth::GetMyDevicesRequest request_proto
;
162 request_proto
.set_allow_stale_read(true);
163 client_
->GetMyDevices(
165 base::Bind(&SaveResult
<cryptauth::GetMyDevicesResponse
>, &result_proto
),
166 base::Bind(&NotCalled
<std::string
>));
168 cryptauth::GetMyDevicesRequest expected_request
;
169 EXPECT_TRUE(expected_request
.ParseFromString(serialized_request_
));
170 EXPECT_TRUE(expected_request
.allow_stale_read());
172 // Return two devices, one unlock key and one unlockable device.
174 cryptauth::GetMyDevicesResponse response_proto
;
175 response_proto
.add_devices();
176 response_proto
.mutable_devices(0)->set_public_key(kPublicKey1
);
177 response_proto
.mutable_devices(0)->set_unlock_key(true);
178 response_proto
.mutable_devices(0)
179 ->set_bluetooth_address(kBluetoothAddress1
);
180 response_proto
.add_devices();
181 response_proto
.mutable_devices(1)->set_public_key(kPublicKey2
);
182 response_proto
.mutable_devices(1)->set_unlockable(true);
183 FinishApiCallFlow(&response_proto
);
186 // Check that the result received in callback is the same as the response.
187 ASSERT_EQ(2, result_proto
.devices_size());
188 EXPECT_EQ(kPublicKey1
, result_proto
.devices(0).public_key());
189 EXPECT_TRUE(result_proto
.devices(0).unlock_key());
190 EXPECT_EQ(kBluetoothAddress1
, result_proto
.devices(0).bluetooth_address());
191 EXPECT_EQ(kPublicKey2
, result_proto
.devices(1).public_key());
192 EXPECT_TRUE(result_proto
.devices(1).unlockable());
195 TEST_F(ProximityAuthCryptAuthClientTest
, GetMyDevicesFailure
) {
197 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
198 "getmydevices?alt=proto");
200 std::string error_message
;
201 client_
->GetMyDevices(cryptauth::GetMyDevicesRequest(),
202 base::Bind(&NotCalled
<cryptauth::GetMyDevicesResponse
>),
203 base::Bind(&SaveResult
<std::string
>, &error_message
));
205 std::string
kStatus500Error("HTTP status: 500");
206 FailApiCallFlow(kStatus500Error
);
207 EXPECT_EQ(kStatus500Error
, error_message
);
210 TEST_F(ProximityAuthCryptAuthClientTest
, FindEligibleUnlockDevicesSuccess
) {
212 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
213 "findeligibleunlockdevices?alt=proto");
215 cryptauth::FindEligibleUnlockDevicesResponse result_proto
;
216 cryptauth::FindEligibleUnlockDevicesRequest request_proto
;
217 request_proto
.set_callback_bluetooth_address(kBluetoothAddress2
);
218 client_
->FindEligibleUnlockDevices(
220 base::Bind(&SaveResult
<cryptauth::FindEligibleUnlockDevicesResponse
>,
222 base::Bind(&NotCalled
<std::string
>));
224 cryptauth::FindEligibleUnlockDevicesRequest expected_request
;
225 EXPECT_TRUE(expected_request
.ParseFromString(serialized_request_
));
226 EXPECT_EQ(kBluetoothAddress2
, expected_request
.callback_bluetooth_address());
228 // Return a response proto with one eligible and one ineligible device.
229 cryptauth::FindEligibleUnlockDevicesResponse response_proto
;
230 response_proto
.add_eligible_devices();
231 response_proto
.mutable_eligible_devices(0)->set_public_key(kPublicKey1
);
233 const std::string kIneligibilityReason
= "You require more vespine gas.";
234 response_proto
.add_ineligible_devices();
235 response_proto
.mutable_ineligible_devices(0)
237 ->set_public_key(kPublicKey2
);
238 response_proto
.mutable_ineligible_devices(0)
239 ->add_reasons(kIneligibilityReason
);
240 FinishApiCallFlow(&response_proto
);
242 // Check that the result received in callback is the same as the response.
243 ASSERT_EQ(1, result_proto
.eligible_devices_size());
244 EXPECT_EQ(kPublicKey1
, result_proto
.eligible_devices(0).public_key());
245 ASSERT_EQ(1, result_proto
.ineligible_devices_size());
246 EXPECT_EQ(kPublicKey2
,
247 result_proto
.ineligible_devices(0).device().public_key());
248 ASSERT_EQ(1, result_proto
.ineligible_devices(0).reasons_size());
249 EXPECT_EQ(kIneligibilityReason
,
250 result_proto
.ineligible_devices(0).reasons(0));
253 TEST_F(ProximityAuthCryptAuthClientTest
, FindEligibleUnlockDevicesFailure
) {
255 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
256 "findeligibleunlockdevices?alt=proto");
258 std::string error_message
;
259 cryptauth::FindEligibleUnlockDevicesRequest request_proto
;
260 request_proto
.set_callback_bluetooth_address(kBluetoothAddress1
);
261 client_
->FindEligibleUnlockDevices(
263 base::Bind(&NotCalled
<cryptauth::FindEligibleUnlockDevicesResponse
>),
264 base::Bind(&SaveResult
<std::string
>, &error_message
));
266 std::string
kStatus403Error("HTTP status: 403");
267 FailApiCallFlow(kStatus403Error
);
268 EXPECT_EQ(kStatus403Error
, error_message
);
271 TEST_F(ProximityAuthCryptAuthClientTest
, SendDeviceSyncTickleSuccess
) {
273 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
274 "senddevicesynctickle?alt=proto");
276 cryptauth::SendDeviceSyncTickleResponse result_proto
;
277 client_
->SendDeviceSyncTickle(
278 cryptauth::SendDeviceSyncTickleRequest(),
279 base::Bind(&SaveResult
<cryptauth::SendDeviceSyncTickleResponse
>,
281 base::Bind(&NotCalled
<std::string
>));
283 cryptauth::SendDeviceSyncTickleRequest expected_request
;
284 EXPECT_TRUE(expected_request
.ParseFromString(serialized_request_
));
286 cryptauth::SendDeviceSyncTickleResponse response_proto
;
287 FinishApiCallFlow(&response_proto
);
290 TEST_F(ProximityAuthCryptAuthClientTest
, ToggleEasyUnlockSuccess
) {
292 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
293 "toggleeasyunlock?alt=proto");
295 cryptauth::ToggleEasyUnlockResponse result_proto
;
296 cryptauth::ToggleEasyUnlockRequest request_proto
;
297 request_proto
.set_enable(true);
298 request_proto
.set_apply_to_all(false);
299 request_proto
.set_public_key(kPublicKey1
);
300 client_
->ToggleEasyUnlock(
302 base::Bind(&SaveResult
<cryptauth::ToggleEasyUnlockResponse
>,
304 base::Bind(&NotCalled
<std::string
>));
306 cryptauth::ToggleEasyUnlockRequest expected_request
;
307 EXPECT_TRUE(expected_request
.ParseFromString(serialized_request_
));
308 EXPECT_TRUE(expected_request
.enable());
309 EXPECT_EQ(kPublicKey1
, expected_request
.public_key());
310 EXPECT_FALSE(expected_request
.apply_to_all());
312 cryptauth::ToggleEasyUnlockResponse response_proto
;
313 FinishApiCallFlow(&response_proto
);
316 TEST_F(ProximityAuthCryptAuthClientTest
, SetupEnrollmentSuccess
) {
318 "https://www.testgoogleapis.com/cryptauth/v1/enrollment/"
321 std::string kApplicationId
= "mkaes";
322 std::vector
<std::string
> supported_protocols
;
323 supported_protocols
.push_back("gcmV1");
324 supported_protocols
.push_back("testProtocol");
326 cryptauth::SetupEnrollmentResponse result_proto
;
327 cryptauth::SetupEnrollmentRequest request_proto
;
328 request_proto
.set_application_id(kApplicationId
);
329 request_proto
.add_types("gcmV1");
330 request_proto
.add_types("testProtocol");
331 client_
->SetupEnrollment(
332 request_proto
, base::Bind(&SaveResult
<cryptauth::SetupEnrollmentResponse
>,
334 base::Bind(&NotCalled
<std::string
>));
336 cryptauth::SetupEnrollmentRequest expected_request
;
337 EXPECT_TRUE(expected_request
.ParseFromString(serialized_request_
));
338 EXPECT_EQ(kApplicationId
, expected_request
.application_id());
339 ASSERT_EQ(2, expected_request
.types_size());
340 EXPECT_EQ("gcmV1", expected_request
.types(0));
341 EXPECT_EQ("testProtocol", expected_request
.types(1));
343 // Return a fake enrollment session.
345 cryptauth::SetupEnrollmentResponse response_proto
;
346 response_proto
.set_status("OK");
347 response_proto
.add_infos();
348 response_proto
.mutable_infos(0)->set_type("gcmV1");
349 response_proto
.mutable_infos(0)->set_enrollment_session_id("session_id");
350 response_proto
.mutable_infos(0)->set_server_ephemeral_key("ephemeral_key");
351 FinishApiCallFlow(&response_proto
);
354 // Check that the returned proto is the same as the one just created.
355 EXPECT_EQ("OK", result_proto
.status());
356 ASSERT_EQ(1, result_proto
.infos_size());
357 EXPECT_EQ("gcmV1", result_proto
.infos(0).type());
358 EXPECT_EQ("session_id", result_proto
.infos(0).enrollment_session_id());
359 EXPECT_EQ("ephemeral_key", result_proto
.infos(0).server_ephemeral_key());
362 TEST_F(ProximityAuthCryptAuthClientTest
, FinishEnrollmentSuccess
) {
364 "https://www.testgoogleapis.com/cryptauth/v1/enrollment/"
367 const char kEnrollmentSessionId
[] = "enrollment_session_id";
368 const char kEnrollmentMessage
[] = "enrollment_message";
369 const char kDeviceEphemeralKey
[] = "device_ephermal_key";
370 cryptauth::FinishEnrollmentResponse result_proto
;
371 cryptauth::FinishEnrollmentRequest request_proto
;
372 request_proto
.set_enrollment_session_id(kEnrollmentSessionId
);
373 request_proto
.set_enrollment_message(kEnrollmentMessage
);
374 request_proto
.set_device_ephemeral_key(kDeviceEphemeralKey
);
375 client_
->FinishEnrollment(
377 base::Bind(&SaveResult
<cryptauth::FinishEnrollmentResponse
>,
379 base::Bind(&NotCalled
<const std::string
&>));
381 cryptauth::FinishEnrollmentRequest expected_request
;
382 EXPECT_TRUE(expected_request
.ParseFromString(serialized_request_
));
383 EXPECT_EQ(kEnrollmentSessionId
, expected_request
.enrollment_session_id());
384 EXPECT_EQ(kEnrollmentMessage
, expected_request
.enrollment_message());
385 EXPECT_EQ(kDeviceEphemeralKey
, expected_request
.device_ephemeral_key());
388 cryptauth::FinishEnrollmentResponse response_proto
;
389 response_proto
.set_status("OK");
390 FinishApiCallFlow(&response_proto
);
392 EXPECT_EQ("OK", result_proto
.status());
395 TEST_F(ProximityAuthCryptAuthClientTest
, FetchAccessTokenFailure
) {
396 access_token_fetcher_
->set_access_token("");
398 std::string error_message
;
399 client_
->GetMyDevices(cryptauth::GetMyDevicesRequest(),
400 base::Bind(&NotCalled
<cryptauth::GetMyDevicesResponse
>),
401 base::Bind(&SaveResult
<std::string
>, &error_message
));
403 EXPECT_EQ("Failed to get a valid access token.", error_message
);
406 TEST_F(ProximityAuthCryptAuthClientTest
, ParseResponseProtoFailure
) {
408 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
409 "getmydevices?alt=proto");
411 std::string error_message
;
412 client_
->GetMyDevices(cryptauth::GetMyDevicesRequest(),
413 base::Bind(&NotCalled
<cryptauth::GetMyDevicesResponse
>),
414 base::Bind(&SaveResult
<std::string
>, &error_message
));
416 flow_result_callback_
.Run("Not a valid serialized response message.");
417 EXPECT_EQ("Failed to parse response proto.", error_message
);
420 TEST_F(ProximityAuthCryptAuthClientTest
,
421 MakeSecondRequestBeforeFirstRequestSucceeds
) {
423 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
424 "getmydevices?alt=proto");
426 // Make first request.
427 cryptauth::GetMyDevicesResponse result_proto
;
428 client_
->GetMyDevices(
429 cryptauth::GetMyDevicesRequest(),
430 base::Bind(&SaveResult
<cryptauth::GetMyDevicesResponse
>, &result_proto
),
431 base::Bind(&NotCalled
<std::string
>));
433 // With request pending, make second request.
435 std::string error_message
;
436 client_
->FindEligibleUnlockDevices(
437 cryptauth::FindEligibleUnlockDevicesRequest(),
438 base::Bind(&NotCalled
<cryptauth::FindEligibleUnlockDevicesResponse
>),
439 base::Bind(&SaveResult
<std::string
>, &error_message
));
440 EXPECT_EQ("Client has been used for another request. Do not reuse.",
444 // Complete first request.
446 cryptauth::GetMyDevicesResponse response_proto
;
447 response_proto
.add_devices();
448 response_proto
.mutable_devices(0)->set_public_key(kPublicKey1
);
449 FinishApiCallFlow(&response_proto
);
452 ASSERT_EQ(1, result_proto
.devices_size());
453 EXPECT_EQ(kPublicKey1
, result_proto
.devices(0).public_key());
456 TEST_F(ProximityAuthCryptAuthClientTest
,
457 MakeSecondRequestBeforeFirstRequestFails
) {
459 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
460 "getmydevices?alt=proto");
462 // Make first request.
463 std::string error_message
;
464 client_
->GetMyDevices(cryptauth::GetMyDevicesRequest(),
465 base::Bind(&NotCalled
<cryptauth::GetMyDevicesResponse
>),
466 base::Bind(&SaveResult
<std::string
>, &error_message
));
468 // With request pending, make second request.
470 std::string error_message
;
471 client_
->FindEligibleUnlockDevices(
472 cryptauth::FindEligibleUnlockDevicesRequest(),
473 base::Bind(&NotCalled
<cryptauth::FindEligibleUnlockDevicesResponse
>),
474 base::Bind(&SaveResult
<std::string
>, &error_message
));
475 EXPECT_EQ("Client has been used for another request. Do not reuse.",
479 // Fail first request.
480 std::string kStatus429Error
= "HTTP status: 429";
481 FailApiCallFlow(kStatus429Error
);
482 EXPECT_EQ(kStatus429Error
, error_message
);
485 TEST_F(ProximityAuthCryptAuthClientTest
,
486 MakeSecondRequestAfterFirstRequestSucceeds
) {
487 // Make first request successfully.
490 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
491 "getmydevices?alt=proto");
492 cryptauth::GetMyDevicesResponse result_proto
;
493 client_
->GetMyDevices(
494 cryptauth::GetMyDevicesRequest(),
495 base::Bind(&SaveResult
<cryptauth::GetMyDevicesResponse
>, &result_proto
),
496 base::Bind(&NotCalled
<std::string
>));
498 cryptauth::GetMyDevicesResponse response_proto
;
499 response_proto
.add_devices();
500 response_proto
.mutable_devices(0)->set_public_key(kPublicKey1
);
501 FinishApiCallFlow(&response_proto
);
502 ASSERT_EQ(1, result_proto
.devices_size());
503 EXPECT_EQ(kPublicKey1
, result_proto
.devices(0).public_key());
506 // Second request fails.
508 std::string error_message
;
509 client_
->FindEligibleUnlockDevices(
510 cryptauth::FindEligibleUnlockDevicesRequest(),
511 base::Bind(&NotCalled
<cryptauth::FindEligibleUnlockDevicesResponse
>),
512 base::Bind(&SaveResult
<std::string
>, &error_message
));
513 EXPECT_EQ("Client has been used for another request. Do not reuse.",
518 TEST_F(ProximityAuthCryptAuthClientTest
, DeviceClassifierIsSet
) {
520 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
521 "getmydevices?alt=proto");
523 cryptauth::GetMyDevicesResponse result_proto
;
524 cryptauth::GetMyDevicesRequest request_proto
;
525 request_proto
.set_allow_stale_read(true);
526 client_
->GetMyDevices(
528 base::Bind(&SaveResult
<cryptauth::GetMyDevicesResponse
>, &result_proto
),
529 base::Bind(&NotCalled
<std::string
>));
530 cryptauth::GetMyDevicesRequest expected_request
;
531 EXPECT_TRUE(expected_request
.ParseFromString(serialized_request_
));
533 const cryptauth::DeviceClassifier
& device_classifier
=
534 expected_request
.device_classifier();
535 EXPECT_EQ(kDeviceOsVersionCode
, device_classifier
.device_os_version_code());
536 EXPECT_EQ(kDeviceSoftwareVersionCode
,
537 device_classifier
.device_software_version_code());
538 EXPECT_EQ(kDeviceSoftwarePackage
,
539 device_classifier
.device_software_package());
540 EXPECT_EQ(kDeviceType
, device_classifier
.device_type());
543 TEST_F(ProximityAuthCryptAuthClientTest
, GetAccessTokenUsed
) {
544 EXPECT_TRUE(client_
->GetAccessTokenUsed().empty());
546 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
547 "getmydevices?alt=proto");
549 cryptauth::GetMyDevicesResponse result_proto
;
550 cryptauth::GetMyDevicesRequest request_proto
;
551 request_proto
.set_allow_stale_read(true);
552 client_
->GetMyDevices(
554 base::Bind(&SaveResult
<cryptauth::GetMyDevicesResponse
>, &result_proto
),
555 base::Bind(&NotCalled
<std::string
>));
556 EXPECT_EQ(kAccessToken
, client_
->GetAccessTokenUsed());
559 } // namespace proximity_auth