OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "components/proximity_auth/cryptauth/cryptauth_client.h" |
| 6 |
| 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/switches.h" |
| 12 #include "net/url_request/test_url_fetcher_factory.h" |
| 13 #include "net/url_request/url_request_test_util.h" |
| 14 #include "testing/gmock/include/gmock/gmock.h" |
| 15 #include "testing/gtest/include/gtest/gtest.h" |
| 16 |
| 17 using testing::_; |
| 18 using testing::DoAll; |
| 19 using testing::Return; |
| 20 using testing::SaveArg; |
| 21 using testing::NiceMock; |
| 22 |
| 23 namespace proximity_auth { |
| 24 |
| 25 namespace { |
| 26 |
| 27 const char kTestGoogleApisUrl[] = "https://www.testgoogleapis.com"; |
| 28 const char kAccessToken[] = "access_token"; |
| 29 const char kPublicKey1[] = "public_key1"; |
| 30 const char kPublicKey2[] = "public_key2"; |
| 31 const char kBluetoothAddress1[] = "AA:AA:AA:AA:AA:AA"; |
| 32 const char kBluetoothAddress2[] = "BB:BB:BB:BB:BB:BB"; |
| 33 |
| 34 // CryptAuthAccessTokenFetcher implementation simply returning a predetermined |
| 35 // access token. |
| 36 class FakeCryptAuthAccessTokenFetcher : public CryptAuthAccessTokenFetcher { |
| 37 public: |
| 38 FakeCryptAuthAccessTokenFetcher() : access_token_(kAccessToken) {} |
| 39 |
| 40 void FetchAccessToken(const AccessTokenCallback& callback) override { |
| 41 callback.Run(access_token_); |
| 42 } |
| 43 |
| 44 void set_access_token(const std::string& access_token) { |
| 45 access_token_ = access_token; |
| 46 }; |
| 47 |
| 48 private: |
| 49 std::string access_token_; |
| 50 }; |
| 51 |
| 52 // Mock CryptAuthApiCallFlow, which handles the HTTP requests to CryptAuth. |
| 53 class MockCryptAuthApiCallFlow : public CryptAuthApiCallFlow { |
| 54 public: |
| 55 MockCryptAuthApiCallFlow() : CryptAuthApiCallFlow(GURL(std::string())) {} |
| 56 virtual ~MockCryptAuthApiCallFlow() {} |
| 57 |
| 58 MOCK_METHOD5(Start, |
| 59 void(net::URLRequestContextGetter* context, |
| 60 const std::string& access_token, |
| 61 const std::string& serialized_request, |
| 62 ResultCallback result_callback, |
| 63 ErrorCallback error_callback)); |
| 64 |
| 65 private: |
| 66 DISALLOW_COPY_AND_ASSIGN(MockCryptAuthApiCallFlow); |
| 67 }; |
| 68 |
| 69 // Subclass of CryptAuthClient to use as test harness. |
| 70 class MockCryptAuthClient : public CryptAuthClient { |
| 71 public: |
| 72 MockCryptAuthClient(net::URLRequestContextGetter* url_request_context, |
| 73 CryptAuthAccessTokenFetcher* access_token_fetcher) |
| 74 : CryptAuthClient(url_request_context, access_token_fetcher) {} |
| 75 virtual ~MockCryptAuthClient() {} |
| 76 |
| 77 MOCK_METHOD1(CreateFlow, CryptAuthApiCallFlow*(GURL request_url)); |
| 78 |
| 79 private: |
| 80 DISALLOW_COPY_AND_ASSIGN(MockCryptAuthClient); |
| 81 }; |
| 82 |
| 83 // Callback that should never be invoked. |
| 84 template <class T> |
| 85 void NotCalled(const T& type) { |
| 86 EXPECT_TRUE(false); |
| 87 } |
| 88 |
| 89 // Callback that saves the result returned by CryptAuthClient. |
| 90 template <class T> |
| 91 void SaveResult(T* out, const T& result) { |
| 92 // static assert |
| 93 *out = result; |
| 94 } |
| 95 |
| 96 } // namespace |
| 97 |
| 98 class ProximityAuthCryptAuthClientTest : public testing::Test { |
| 99 protected: |
| 100 ProximityAuthCryptAuthClientTest() |
| 101 : url_request_context_( |
| 102 new net::TestURLRequestContextGetter(new base::NullTaskRunner())), |
| 103 serialized_request_("") {} |
| 104 virtual ~ProximityAuthCryptAuthClientTest() {} |
| 105 |
| 106 void SetUp() override { |
| 107 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| 108 switches::kCryptAuthGoogleApisUrl, kTestGoogleApisUrl); |
| 109 |
| 110 client_.reset(new NiceMock<MockCryptAuthClient>(url_request_context_.get(), |
| 111 &access_token_fetcher_)); |
| 112 } |
| 113 |
| 114 // Sets up an expectation and captures a CryptAuth API request to |
| 115 // |request_url|. |
| 116 void ExpectRequest(const std::string& request_url) { |
| 117 NiceMock<MockCryptAuthApiCallFlow>* api_call_flow = |
| 118 new NiceMock<MockCryptAuthApiCallFlow>(); |
| 119 |
| 120 EXPECT_CALL(*client_, CreateFlow(GURL(request_url))) |
| 121 .WillOnce(Return(api_call_flow)); |
| 122 |
| 123 EXPECT_CALL(*api_call_flow, |
| 124 Start(url_request_context_.get(), kAccessToken, _, _, _)) |
| 125 .WillOnce(DoAll(SaveArg<2>(&serialized_request_), |
| 126 SaveArg<3>(&flow_result_callback_), |
| 127 SaveArg<4>(&flow_error_callback_))); |
| 128 } |
| 129 |
| 130 // Returns |response_proto| as the result to the current API request. |
| 131 // ExpectResult() must have been called first. |
| 132 void FinishApiCallFlow(const google::protobuf::MessageLite* response_proto) { |
| 133 std::string serialized_proto; |
| 134 response_proto->SerializeToString(&serialized_proto); |
| 135 flow_result_callback_.Run(serialized_proto); |
| 136 } |
| 137 |
| 138 // Ends the current API request with |error_message|. ExpectResult() must have |
| 139 // been called first. |
| 140 void FailApiCallFlow(const std::string& error_message) { |
| 141 flow_error_callback_.Run(error_message); |
| 142 } |
| 143 |
| 144 protected: |
| 145 scoped_refptr<net::URLRequestContextGetter> url_request_context_; |
| 146 FakeCryptAuthAccessTokenFetcher access_token_fetcher_; |
| 147 scoped_ptr<NiceMock<MockCryptAuthClient>> client_; |
| 148 |
| 149 std::string serialized_request_; |
| 150 CryptAuthApiCallFlow::ResultCallback flow_result_callback_; |
| 151 CryptAuthApiCallFlow::ErrorCallback flow_error_callback_; |
| 152 }; |
| 153 |
| 154 TEST_F(ProximityAuthCryptAuthClientTest, GetMyDevicesSuccess) { |
| 155 ExpectRequest( |
| 156 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" |
| 157 "getmydevices?alt=proto"); |
| 158 |
| 159 cryptauth::GetMyDevicesResponse result_proto; |
| 160 client_->GetMyDevices( |
| 161 true, |
| 162 base::Bind(&SaveResult<cryptauth::GetMyDevicesResponse>, &result_proto), |
| 163 base::Bind(&NotCalled<const std::string&>)); |
| 164 |
| 165 cryptauth::GetMyDevicesRequest request; |
| 166 EXPECT_TRUE(request.ParseFromString(serialized_request_)); |
| 167 EXPECT_TRUE(request.allow_stale_read()); |
| 168 |
| 169 // Return two devices, one unlock key and one unlockable device. |
| 170 { |
| 171 cryptauth::GetMyDevicesResponse response_proto; |
| 172 response_proto.add_devices(); |
| 173 response_proto.mutable_devices(0)->set_public_key(kPublicKey1); |
| 174 response_proto.mutable_devices(0)->set_unlock_key(true); |
| 175 response_proto.mutable_devices(0) |
| 176 ->set_bluetooth_address(kBluetoothAddress1); |
| 177 response_proto.add_devices(); |
| 178 response_proto.mutable_devices(1)->set_public_key(kPublicKey2); |
| 179 response_proto.mutable_devices(1)->set_unlockable(true); |
| 180 FinishApiCallFlow(&response_proto); |
| 181 } |
| 182 |
| 183 // Check that the result received in callback is the same as the response. |
| 184 ASSERT_EQ(2, result_proto.devices_size()); |
| 185 EXPECT_EQ(kPublicKey1, result_proto.devices(0).public_key()); |
| 186 EXPECT_TRUE(result_proto.devices(0).unlock_key()); |
| 187 EXPECT_EQ(kBluetoothAddress1, result_proto.devices(0).bluetooth_address()); |
| 188 EXPECT_EQ(kPublicKey2, result_proto.devices(1).public_key()); |
| 189 EXPECT_TRUE(result_proto.devices(1).unlockable()); |
| 190 } |
| 191 |
| 192 TEST_F(ProximityAuthCryptAuthClientTest, GetMyDevicesFailure) { |
| 193 ExpectRequest( |
| 194 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" |
| 195 "getmydevices?alt=proto"); |
| 196 |
| 197 std::string error_message; |
| 198 client_->GetMyDevices(true, |
| 199 base::Bind(&NotCalled<cryptauth::GetMyDevicesResponse>), |
| 200 base::Bind(&SaveResult<std::string>, &error_message)); |
| 201 |
| 202 std::string kStatus500Error("HTTP status: 500"); |
| 203 FailApiCallFlow(kStatus500Error); |
| 204 EXPECT_EQ(kStatus500Error, error_message); |
| 205 } |
| 206 |
| 207 TEST_F(ProximityAuthCryptAuthClientTest, FindEligibleUnlockDevicesSuccess) { |
| 208 ExpectRequest( |
| 209 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" |
| 210 "findeligibleunlockdevices?alt=proto"); |
| 211 |
| 212 cryptauth::FindEligibleUnlockDevicesResponse result_proto; |
| 213 client_->FindEligibleUnlockDevices( |
| 214 kBluetoothAddress2, |
| 215 base::Bind(&SaveResult<cryptauth::FindEligibleUnlockDevicesResponse>, |
| 216 &result_proto), |
| 217 base::Bind(&NotCalled<const std::string&>)); |
| 218 |
| 219 cryptauth::FindEligibleUnlockDevicesRequest request; |
| 220 EXPECT_TRUE(request.ParseFromString(serialized_request_)); |
| 221 EXPECT_EQ(kBluetoothAddress2, request.callback_bluetooth_address()); |
| 222 |
| 223 // Return a response proto with one eligible and one ineligible device. |
| 224 cryptauth::FindEligibleUnlockDevicesResponse response_proto; |
| 225 response_proto.add_eligible_devices(); |
| 226 response_proto.mutable_eligible_devices(0)->set_public_key(kPublicKey1); |
| 227 |
| 228 const std::string kIneligibilityReason = "You require more vespine gas."; |
| 229 response_proto.add_ineligible_devices(); |
| 230 response_proto.mutable_ineligible_devices(0) |
| 231 ->mutable_device() |
| 232 ->set_public_key(kPublicKey2); |
| 233 response_proto.mutable_ineligible_devices(0) |
| 234 ->add_reasons(kIneligibilityReason); |
| 235 FinishApiCallFlow(&response_proto); |
| 236 |
| 237 // Check that the result received in callback is the same as the response. |
| 238 ASSERT_EQ(1, result_proto.eligible_devices_size()); |
| 239 EXPECT_EQ(kPublicKey1, result_proto.eligible_devices(0).public_key()); |
| 240 ASSERT_EQ(1, result_proto.ineligible_devices_size()); |
| 241 EXPECT_EQ(kPublicKey2, |
| 242 result_proto.ineligible_devices(0).device().public_key()); |
| 243 ASSERT_EQ(1, result_proto.ineligible_devices(0).reasons_size()); |
| 244 EXPECT_EQ(kIneligibilityReason, |
| 245 result_proto.ineligible_devices(0).reasons(0)); |
| 246 } |
| 247 |
| 248 TEST_F(ProximityAuthCryptAuthClientTest, FindEligibleUnlockDevicesFailure) { |
| 249 ExpectRequest( |
| 250 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" |
| 251 "findeligibleunlockdevices?alt=proto"); |
| 252 |
| 253 std::string error_message; |
| 254 client_->FindEligibleUnlockDevices( |
| 255 kBluetoothAddress1, |
| 256 base::Bind(&NotCalled<cryptauth::FindEligibleUnlockDevicesResponse>), |
| 257 base::Bind(&SaveResult<std::string>, &error_message)); |
| 258 |
| 259 std::string kStatus403Error("HTTP status: 403"); |
| 260 FailApiCallFlow(kStatus403Error); |
| 261 EXPECT_EQ(kStatus403Error, error_message); |
| 262 } |
| 263 |
| 264 TEST_F(ProximityAuthCryptAuthClientTest, SendDeviceSyncTickleSuccess) { |
| 265 ExpectRequest( |
| 266 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" |
| 267 "senddevicesynctickle?alt=proto"); |
| 268 |
| 269 cryptauth::SendDeviceSyncTickleResponse result_proto; |
| 270 client_->SendDeviceSyncTickle( |
| 271 base::Bind(&SaveResult<cryptauth::SendDeviceSyncTickleResponse>, |
| 272 &result_proto), |
| 273 base::Bind(&NotCalled<const std::string&>)); |
| 274 |
| 275 cryptauth::SendDeviceSyncTickleRequest request; |
| 276 EXPECT_TRUE(request.ParseFromString(serialized_request_)); |
| 277 |
| 278 cryptauth::SendDeviceSyncTickleResponse response_proto; |
| 279 FinishApiCallFlow(&response_proto); |
| 280 } |
| 281 |
| 282 TEST_F(ProximityAuthCryptAuthClientTest, ToggleEasyUnlockSuccess) { |
| 283 ExpectRequest( |
| 284 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" |
| 285 "toggleeasyunlock?alt=proto"); |
| 286 |
| 287 cryptauth::ToggleEasyUnlockResponse result_proto; |
| 288 client_->ToggleEasyUnlock( |
| 289 true, false, kPublicKey1, |
| 290 base::Bind(&SaveResult<cryptauth::ToggleEasyUnlockResponse>, |
| 291 &result_proto), |
| 292 base::Bind(&NotCalled<const std::string&>)); |
| 293 |
| 294 cryptauth::ToggleEasyUnlockRequest request; |
| 295 EXPECT_TRUE(request.ParseFromString(serialized_request_)); |
| 296 EXPECT_TRUE(request.enable()); |
| 297 EXPECT_EQ(kPublicKey1, request.public_key()); |
| 298 EXPECT_FALSE(request.apply_to_all()); |
| 299 |
| 300 cryptauth::ToggleEasyUnlockResponse response_proto; |
| 301 FinishApiCallFlow(&response_proto); |
| 302 } |
| 303 |
| 304 TEST_F(ProximityAuthCryptAuthClientTest, SetupEnrollmentSuccess) { |
| 305 ExpectRequest( |
| 306 "https://www.testgoogleapis.com/cryptauth/v1/enrollment/" |
| 307 "setupenrollment?alt=proto"); |
| 308 |
| 309 std::string kApplicationId = "mkaes"; |
| 310 std::vector<std::string> supported_protocols; |
| 311 supported_protocols.push_back("gcmV1"); |
| 312 supported_protocols.push_back("testProtocol"); |
| 313 |
| 314 cryptauth::SetupEnrollmentResponse result_proto; |
| 315 client_->SetupEnrollment( |
| 316 kApplicationId, supported_protocols, |
| 317 base::Bind(&SaveResult<cryptauth::SetupEnrollmentResponse>, |
| 318 &result_proto), |
| 319 base::Bind(&NotCalled<const std::string&>)); |
| 320 |
| 321 cryptauth::SetupEnrollmentRequest request; |
| 322 EXPECT_TRUE(request.ParseFromString(serialized_request_)); |
| 323 EXPECT_EQ(kApplicationId, request.application_id()); |
| 324 ASSERT_EQ(2, request.types_size()); |
| 325 EXPECT_EQ("gcmV1", request.types(0)); |
| 326 EXPECT_EQ("testProtocol", request.types(1)); |
| 327 |
| 328 // Return a fake enrollment session. |
| 329 { |
| 330 cryptauth::SetupEnrollmentResponse response_proto; |
| 331 response_proto.set_status("OK"); |
| 332 response_proto.add_infos(); |
| 333 response_proto.mutable_infos(0)->set_type("gcmV1"); |
| 334 response_proto.mutable_infos(0)->set_enrollment_session_id("session_id"); |
| 335 response_proto.mutable_infos(0)->set_server_ephemeral_key("ephemeral_key"); |
| 336 FinishApiCallFlow(&response_proto); |
| 337 } |
| 338 |
| 339 // Check that the returned proto is the same as the one just created. |
| 340 EXPECT_EQ("OK", result_proto.status()); |
| 341 ASSERT_EQ(1, result_proto.infos_size()); |
| 342 EXPECT_EQ("gcmV1", result_proto.infos(0).type()); |
| 343 EXPECT_EQ("session_id", result_proto.infos(0).enrollment_session_id()); |
| 344 EXPECT_EQ("ephemeral_key", result_proto.infos(0).server_ephemeral_key()); |
| 345 } |
| 346 |
| 347 TEST_F(ProximityAuthCryptAuthClientTest, FinishEnrollmentSuccess) { |
| 348 ExpectRequest( |
| 349 "https://www.testgoogleapis.com/cryptauth/v1/enrollment/" |
| 350 "finishenrollment?alt=proto"); |
| 351 |
| 352 const char kEnrollmentSessionId[] = "enrollment_session_id"; |
| 353 const char kEnrollmentMessage[] = "enrollment_message"; |
| 354 const char kDeviceEphemeralKey[] = "device_ephermal_key"; |
| 355 cryptauth::FinishEnrollmentResponse result_proto; |
| 356 client_->FinishEnrollment( |
| 357 kEnrollmentSessionId, kEnrollmentMessage, kDeviceEphemeralKey, |
| 358 base::Bind(&SaveResult<cryptauth::FinishEnrollmentResponse>, |
| 359 &result_proto), |
| 360 base::Bind(&NotCalled<const std::string&>)); |
| 361 |
| 362 cryptauth::FinishEnrollmentRequest request; |
| 363 EXPECT_TRUE(request.ParseFromString(serialized_request_)); |
| 364 EXPECT_EQ(kEnrollmentSessionId, request.enrollment_session_id()); |
| 365 EXPECT_EQ(kEnrollmentMessage, request.enrollment_message()); |
| 366 EXPECT_EQ(kDeviceEphemeralKey, request.device_ephemeral_key()); |
| 367 |
| 368 { |
| 369 cryptauth::FinishEnrollmentResponse response_proto; |
| 370 response_proto.set_status("OK"); |
| 371 FinishApiCallFlow(&response_proto); |
| 372 } |
| 373 EXPECT_EQ("OK", result_proto.status()); |
| 374 } |
| 375 |
| 376 TEST_F(ProximityAuthCryptAuthClientTest, FetchAccessTokenFailure) { |
| 377 access_token_fetcher_.set_access_token(""); |
| 378 |
| 379 std::string error_message; |
| 380 client_->GetMyDevices(true, |
| 381 base::Bind(&NotCalled<cryptauth::GetMyDevicesResponse>), |
| 382 base::Bind(&SaveResult<std::string>, &error_message)); |
| 383 |
| 384 EXPECT_EQ("Failed to get a valid access token", error_message); |
| 385 } |
| 386 |
| 387 TEST_F(ProximityAuthCryptAuthClientTest, MakeRequestWhenOldRequestPending) { |
| 388 ExpectRequest( |
| 389 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" |
| 390 "getmydevices?alt=proto"); |
| 391 |
| 392 // Make first request. |
| 393 cryptauth::GetMyDevicesResponse result_proto; |
| 394 client_->GetMyDevices( |
| 395 false, |
| 396 base::Bind(&SaveResult<cryptauth::GetMyDevicesResponse>, &result_proto), |
| 397 base::Bind(&NotCalled<const std::string&>)); |
| 398 |
| 399 // With request pending, make second request. |
| 400 std::string error_message; |
| 401 client_->FindEligibleUnlockDevices( |
| 402 kBluetoothAddress2, |
| 403 base::Bind(&NotCalled<cryptauth::FindEligibleUnlockDevicesResponse>), |
| 404 base::Bind(&SaveResult<std::string>, &error_message)); |
| 405 EXPECT_EQ("Another request in progress", error_message); |
| 406 |
| 407 // Complete first request. |
| 408 { |
| 409 cryptauth::GetMyDevicesResponse response_proto; |
| 410 response_proto.add_devices(); |
| 411 response_proto.mutable_devices(0)->set_public_key(kPublicKey1); |
| 412 FinishApiCallFlow(&response_proto); |
| 413 } |
| 414 |
| 415 ASSERT_EQ(1, result_proto.devices_size()); |
| 416 EXPECT_EQ(kPublicKey1, result_proto.devices(0).public_key()); |
| 417 } |
| 418 |
| 419 TEST_F(ProximityAuthCryptAuthClientTest, MakeRequestAfterOldRequestFinishes) { |
| 420 { |
| 421 // Make first request successfully. |
| 422 ExpectRequest( |
| 423 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" |
| 424 "getmydevices?alt=proto"); |
| 425 cryptauth::GetMyDevicesResponse result_proto; |
| 426 client_->GetMyDevices( |
| 427 false, |
| 428 base::Bind(&SaveResult<cryptauth::GetMyDevicesResponse>, &result_proto), |
| 429 base::Bind(&NotCalled<const std::string&>)); |
| 430 |
| 431 cryptauth::GetMyDevicesResponse response_proto; |
| 432 response_proto.add_devices(); |
| 433 response_proto.mutable_devices(0)->set_public_key(kPublicKey1); |
| 434 FinishApiCallFlow(&response_proto); |
| 435 ASSERT_EQ(1, result_proto.devices_size()); |
| 436 EXPECT_EQ(kPublicKey1, result_proto.devices(0).public_key()); |
| 437 } |
| 438 { |
| 439 // Make second request successfully. |
| 440 cryptauth::FindEligibleUnlockDevicesResponse result_proto; |
| 441 ExpectRequest( |
| 442 "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/" |
| 443 "findeligibleunlockdevices?alt=proto"); |
| 444 client_->FindEligibleUnlockDevices( |
| 445 kBluetoothAddress2, |
| 446 base::Bind(&SaveResult<cryptauth::FindEligibleUnlockDevicesResponse>, |
| 447 &result_proto), |
| 448 base::Bind(&NotCalled<const std::string&>)); |
| 449 |
| 450 cryptauth::FindEligibleUnlockDevicesResponse response_proto; |
| 451 response_proto.add_eligible_devices(); |
| 452 response_proto.mutable_eligible_devices(0)->set_public_key(kPublicKey2); |
| 453 FinishApiCallFlow(&response_proto); |
| 454 ASSERT_EQ(1, result_proto.eligible_devices_size()); |
| 455 EXPECT_EQ(kPublicKey2, result_proto.eligible_devices(0).public_key()); |
| 456 } |
| 457 } |
| 458 |
| 459 } // namespace proximity_auth |
OLD | NEW |