OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "ios/chrome/browser/passwords/credential_manager.h" |
| 6 |
| 7 #include <memory> |
| 8 #include <utility> |
| 9 |
| 10 #include "base/mac/bind_objc_block.h" |
| 11 #include "base/memory/ref_counted.h" |
| 12 #include "base/strings/sys_string_conversions.h" |
| 13 #include "base/strings/utf_string_conversions.h" |
| 14 #include "base/values.h" |
| 15 #include "components/password_manager/core/browser/password_bubble_experiment.h" |
| 16 #include "components/password_manager/core/browser/stub_password_manager_client.
h" |
| 17 #include "components/password_manager/core/browser/stub_password_manager_driver.
h" |
| 18 #include "components/password_manager/core/browser/test_password_store.h" |
| 19 #include "components/password_manager/core/common/credential_manager_types.h" |
| 20 #include "components/password_manager/core/common/password_manager_pref_names.h" |
| 21 #include "components/prefs/pref_registry_simple.h" |
| 22 #include "components/prefs/testing_pref_service.h" |
| 23 #import "ios/web/public/web_state/web_state.h" |
| 24 #import "ios/chrome/browser/passwords/js_credential_manager.h" |
| 25 #import "ios/testing/ocmock_complex_type_helper.h" |
| 26 #import "ios/web/public/test/web_test_with_web_state.h" |
| 27 #include "testing/gmock/include/gmock/gmock.h" |
| 28 #include "testing/gtest/include/gtest/gtest.h" |
| 29 #include "testing/gtest_mac.h" |
| 30 #include "third_party/ocmock/OCMock/OCMock.h" |
| 31 #include "third_party/ocmock/gtest_support.h" |
| 32 #include "url/gurl.h" |
| 33 |
| 34 using testing::Return; |
| 35 |
| 36 namespace { |
| 37 // Type of a function invoked when a promise is resolved. |
| 38 typedef void (^ResolvePromiseBlock)(NSInteger request_id, |
| 39 const web::Credential& credential); |
| 40 } // namespace |
| 41 |
| 42 // A helper to mock methods that have C++ object parameters. |
| 43 @interface MockJSCredentialManager : OCMockComplexTypeHelper |
| 44 @end |
| 45 |
| 46 @implementation MockJSCredentialManager |
| 47 - (void)resolvePromiseWithRequestID:(NSInteger)requestID |
| 48 credential:(const web::Credential&)credential |
| 49 completionHandler:(void (^)(BOOL))completionHandler { |
| 50 static_cast<ResolvePromiseBlock>([self blockForSelector:_cmd])(requestID, |
| 51 credential); |
| 52 completionHandler(YES); |
| 53 } |
| 54 @end |
| 55 |
| 56 namespace { |
| 57 |
| 58 // Returns a test credential. |
| 59 autofill::PasswordForm GetTestPasswordForm1(bool zero_click_allowed) { |
| 60 autofill::PasswordForm form; |
| 61 form.username_value = base::ASCIIToUTF16("foo"); |
| 62 form.password_value = base::ASCIIToUTF16("bar"); |
| 63 form.skip_zero_click = !zero_click_allowed; |
| 64 form.ssl_valid = true; |
| 65 form.type = autofill::PasswordForm::Type::TYPE_API; |
| 66 return form; |
| 67 } |
| 68 |
| 69 // Returns a test credential matching |GetTestPasswordForm1()|. |
| 70 web::Credential GetTestWebCredential1(bool zero_click_allowed) { |
| 71 web::Credential credential; |
| 72 autofill::PasswordForm form(GetTestPasswordForm1(zero_click_allowed)); |
| 73 credential.type = web::CredentialType::CREDENTIAL_TYPE_PASSWORD; |
| 74 credential.id = form.username_value; |
| 75 credential.password = form.password_value; |
| 76 return credential; |
| 77 } |
| 78 |
| 79 // Returns a different test credential. |
| 80 autofill::PasswordForm GetTestPasswordForm2(bool zero_click_allowed) { |
| 81 autofill::PasswordForm form; |
| 82 form.username_value = base::ASCIIToUTF16("baz"); |
| 83 form.password_value = base::ASCIIToUTF16("bah"); |
| 84 form.skip_zero_click = !zero_click_allowed; |
| 85 form.ssl_valid = true; |
| 86 return form; |
| 87 } |
| 88 |
| 89 // Returns a test credential matching |GetTestPasswordForm2()|. |
| 90 web::Credential GetTestWebCredential2(bool zero_click_allowed) { |
| 91 web::Credential credential; |
| 92 autofill::PasswordForm form(GetTestPasswordForm2(zero_click_allowed)); |
| 93 credential.type = web::CredentialType::CREDENTIAL_TYPE_PASSWORD; |
| 94 credential.id = form.username_value; |
| 95 credential.password = form.password_value; |
| 96 return credential; |
| 97 } |
| 98 |
| 99 typedef BOOL (^StringPredicate)(NSString*); |
| 100 |
| 101 // Returns a block that takes a string argument and returns whether it is equal |
| 102 // to |string|. |
| 103 StringPredicate EqualsString(const char* string) { |
| 104 return [[^BOOL(NSString* other) { |
| 105 return [base::SysUTF8ToNSString(string) isEqualToString:other]; |
| 106 } copy] autorelease]; |
| 107 } |
| 108 |
| 109 // A stub PasswordManagerClient for testing. |
| 110 class StubPasswordManagerClient |
| 111 : public password_manager::StubPasswordManagerClient { |
| 112 public: |
| 113 StubPasswordManagerClient() |
| 114 : password_manager::StubPasswordManagerClient(), |
| 115 password_store_(nullptr) { |
| 116 prefs_.registry()->RegisterBooleanPref( |
| 117 password_manager::prefs::kCredentialsEnableAutosignin, false); |
| 118 password_bubble_experiment::RegisterPrefs(prefs_.registry()); |
| 119 } |
| 120 |
| 121 ~StubPasswordManagerClient() override { |
| 122 if (password_store_) |
| 123 password_store_->ShutdownOnUIThread(); |
| 124 } |
| 125 |
| 126 MOCK_CONST_METHOD0(IsSavingAndFillingEnabledForCurrentPage, bool()); |
| 127 |
| 128 void SetPasswordStore( |
| 129 scoped_refptr<password_manager::PasswordStore> password_store) { |
| 130 password_store_ = password_store; |
| 131 } |
| 132 |
| 133 password_manager::PasswordStore* GetPasswordStore() const override { |
| 134 return password_store_.get(); |
| 135 } |
| 136 |
| 137 void SetUserChosenCredential( |
| 138 const password_manager::CredentialInfo& credential) { |
| 139 user_chosen_credential_ = credential; |
| 140 } |
| 141 |
| 142 bool PromptUserToChooseCredentials( |
| 143 ScopedVector<autofill::PasswordForm> local_forms, |
| 144 ScopedVector<autofill::PasswordForm> federated_forms, |
| 145 const GURL& origin, |
| 146 const CredentialsCallback& callback) override { |
| 147 return false; |
| 148 } |
| 149 |
| 150 bool PromptUserToSaveOrUpdatePassword( |
| 151 std::unique_ptr<password_manager::PasswordFormManager> form_to_save, |
| 152 password_manager::CredentialSourceType type, |
| 153 bool update_password) override { |
| 154 if (!update_password) |
| 155 saved_form_ = std::move(form_to_save); |
| 156 return true; |
| 157 } |
| 158 |
| 159 PrefService* GetPrefs() override { return &prefs_; } |
| 160 |
| 161 password_manager::PasswordFormManager* saved_form() { |
| 162 return saved_form_.get(); |
| 163 } |
| 164 |
| 165 private: |
| 166 // PrefService for testing. |
| 167 TestingPrefServiceSimple prefs_; |
| 168 |
| 169 // PasswordStore for testing. |
| 170 scoped_refptr<password_manager::PasswordStore> password_store_; |
| 171 |
| 172 // The password form shown to the user for saving. |
| 173 std::unique_ptr<password_manager::PasswordFormManager> saved_form_; |
| 174 |
| 175 // The credential to be returned to callers of PromptUserToChooseCredentials. |
| 176 password_manager::CredentialInfo user_chosen_credential_; |
| 177 |
| 178 DISALLOW_COPY_AND_ASSIGN(StubPasswordManagerClient); |
| 179 }; |
| 180 |
| 181 // Tests for CredentialManager. |
| 182 class CredentialManagerTest : public web::WebTestWithWebState { |
| 183 public: |
| 184 CredentialManagerTest() {} |
| 185 ~CredentialManagerTest() override {} |
| 186 |
| 187 void SetUp() override { |
| 188 web::WebTestWithWebState::SetUp(); |
| 189 id originalMock = |
| 190 [OCMockObject niceMockForClass:[JSCredentialManager class]]; |
| 191 mock_js_credential_manager_.reset([[MockJSCredentialManager alloc] |
| 192 initWithRepresentedObject:originalMock]); |
| 193 credential_manager_.reset(new CredentialManager( |
| 194 web_state(), &stub_client_, &stub_driver_, |
| 195 static_cast<id>(mock_js_credential_manager_.get()))); |
| 196 } |
| 197 |
| 198 // Sets up an expectation that the promise identified by |request_id| will be |
| 199 // resolved with |credential|. |verified| must point to a variable that will |
| 200 // be checked by the caller to ensure that the expectations were run. (This |
| 201 // is necessary because OCMock doesn't handle methods with C++ object |
| 202 // parameters.) |
| 203 void ExpectPromiseResolved(bool* verified, |
| 204 int request_id, |
| 205 const web::Credential& credential) { |
| 206 SEL selector = |
| 207 @selector(resolvePromiseWithRequestID:credential:completionHandler:); |
| 208 web::Credential strong_credential(credential); |
| 209 [mock_js_credential_manager_ |
| 210 onSelector:selector |
| 211 callBlockExpectation:^(NSInteger block_request_id, |
| 212 const web::Credential& block_credential) { |
| 213 EXPECT_EQ(request_id, block_request_id); |
| 214 EXPECT_TRUE(CredentialsEqual(strong_credential, block_credential)); |
| 215 *verified = true; |
| 216 }]; |
| 217 } |
| 218 |
| 219 // Same as |ExpectPromiseResolved(bool*, int, const web::Credential&)| but |
| 220 // does not expect a credential to be passed. |
| 221 void ExpectPromiseResolved(int request_id) { |
| 222 [[[mock_js_credential_manager_ representedObject] expect] |
| 223 resolvePromiseWithRequestID:request_id |
| 224 completionHandler:nil]; |
| 225 } |
| 226 |
| 227 // Clears the expectations set up by |ExpectPromiseResolved()|. |
| 228 void ClearPromiseResolutionExpectations() { |
| 229 SEL selector = |
| 230 @selector(resolvePromiseWithRequestID:credential:completionHandler:); |
| 231 [mock_js_credential_manager_ removeBlockExpectationOnSelector:selector]; |
| 232 } |
| 233 |
| 234 // Sets up an expectation that the promise identified by |request_id| will be |
| 235 // rejected with the given |error_type| and |message|. The caller should use |
| 236 // EXPECT_OCMOCK_VERIFY to verify that the expectations were run. |
| 237 void ExpectPromiseRejected(int request_id, |
| 238 const char* error_type, |
| 239 const char* message) { |
| 240 [[[mock_js_credential_manager_ representedObject] expect] |
| 241 rejectPromiseWithRequestID:request_id |
| 242 errorType:[OCMArg |
| 243 checkWithBlock:EqualsString(error_type)] |
| 244 message:[OCMArg checkWithBlock:EqualsString(message)] |
| 245 completionHandler:[OCMArg any]]; |
| 246 } |
| 247 |
| 248 protected: |
| 249 // Mock for PasswordManagerClient. |
| 250 StubPasswordManagerClient stub_client_; |
| 251 |
| 252 // Stub for PasswordManagerDriver. |
| 253 password_manager::StubPasswordManagerDriver stub_driver_; |
| 254 |
| 255 // Mock for JSCredentialManager. |
| 256 base::scoped_nsobject<MockJSCredentialManager> mock_js_credential_manager_; |
| 257 |
| 258 // CredentialManager for testing. |
| 259 std::unique_ptr<CredentialManager> credential_manager_; |
| 260 |
| 261 private: |
| 262 explicit CredentialManagerTest(const CredentialManagerTest&) = delete; |
| 263 CredentialManagerTest& operator=(const CredentialManagerTest&) = delete; |
| 264 }; |
| 265 |
| 266 // Tests that a credential request is rejected properly when the PasswordStore |
| 267 // is unavailable. |
| 268 TEST_F(CredentialManagerTest, RequestRejectedWhenPasswordStoreUnavailable) { |
| 269 // Clear the password store. |
| 270 stub_client_.SetPasswordStore(nullptr); |
| 271 |
| 272 // Requesting a credential should reject the request with an error. |
| 273 const int request_id = 0; |
| 274 ExpectPromiseRejected(request_id, |
| 275 kCredentialsPasswordStoreUnavailableErrorType, |
| 276 kCredentialsPasswordStoreUnavailableErrorMessage); |
| 277 credential_manager_->CredentialsRequested(request_id, |
| 278 GURL("http://foo.com/login"), false, |
| 279 std::vector<std::string>(), true); |
| 280 |
| 281 // Pump the message loop and verify. |
| 282 WaitForBackgroundTasks(); |
| 283 EXPECT_OCMOCK_VERIFY([mock_js_credential_manager_ representedObject]); |
| 284 } |
| 285 |
| 286 // Tests that a credential request is rejected when another request is pending. |
| 287 TEST_F(CredentialManagerTest, RequestRejectedWhenExistingRequestIsPending) { |
| 288 // Set a password store, but prevent requests from completing. |
| 289 stub_client_.SetPasswordStore(new password_manager::TestPasswordStore); |
| 290 |
| 291 // Make an initial request. Don't pump the message loop, so that the task |
| 292 // doesn't complete. Expect the request to resolve with an empty credential |
| 293 // after the message loop is pumped. |
| 294 const int first_request_id = 0; |
| 295 bool first_verified = false; |
| 296 ExpectPromiseResolved(&first_verified, first_request_id, web::Credential()); |
| 297 credential_manager_->CredentialsRequested(first_request_id, |
| 298 GURL("http://foo.com/login"), false, |
| 299 std::vector<std::string>(), true); |
| 300 |
| 301 // Making a second request and then pumping the message loop should reject the |
| 302 // request with an error. |
| 303 const int second_request_id = 0; |
| 304 ExpectPromiseRejected(second_request_id, kCredentialsPendingRequestErrorType, |
| 305 kCredentialsPendingRequestErrorMessage); |
| 306 credential_manager_->CredentialsRequested(second_request_id, |
| 307 GURL("http://foo.com/login"), false, |
| 308 std::vector<std::string>(), true); |
| 309 |
| 310 // Pump the message loop and verify. |
| 311 WaitForBackgroundTasks(); |
| 312 EXPECT_TRUE(first_verified); |
| 313 EXPECT_OCMOCK_VERIFY([mock_js_credential_manager_ representedObject]); |
| 314 } |
| 315 |
| 316 // Tests that a zero-click credential request is resolved properly with an empty |
| 317 // credential when zero-click sign-in is disabled. |
| 318 TEST_F(CredentialManagerTest, |
| 319 ZeroClickRequestResolvedWithEmptyCredentialWhenZeroClickSignInDisabled) { |
| 320 // Set a password store, but request a zero-click credential with zero-click |
| 321 // disabled. |
| 322 stub_client_.SetPasswordStore(new password_manager::TestPasswordStore); |
| 323 const bool zero_click = true; |
| 324 static_cast<TestingPrefServiceSimple*>(stub_client_.GetPrefs()) |
| 325 ->SetUserPref(password_manager::prefs::kCredentialsEnableAutosignin, |
| 326 new base::FundamentalValue(!zero_click)); |
| 327 |
| 328 // Requesting a zero-click credential should immediately resolve the request |
| 329 // with an empty credential. |
| 330 const int request_id = 0; |
| 331 bool verified = false; |
| 332 ExpectPromiseResolved(&verified, request_id, web::Credential()); |
| 333 credential_manager_->CredentialsRequested( |
| 334 request_id, GURL("http://foo.com/login"), zero_click, |
| 335 std::vector<std::string>(), true); |
| 336 |
| 337 // Pump the message loop and verify. |
| 338 WaitForBackgroundTasks(); |
| 339 EXPECT_TRUE(verified); |
| 340 } |
| 341 |
| 342 // Tests that a credential request is properly resolved with an empty credential |
| 343 // when no credentials are available. |
| 344 TEST_F(CredentialManagerTest, |
| 345 RequestResolvedWithEmptyCredentialWhenNoneAvailable) { |
| 346 // Set a password store with no credentials, enable zero-click, and request a |
| 347 // zero-click credential. |
| 348 stub_client_.SetPasswordStore(new password_manager::TestPasswordStore); |
| 349 const bool zero_click = true; |
| 350 static_cast<TestingPrefServiceSimple*>(stub_client_.GetPrefs()) |
| 351 ->SetUserPref(password_manager::prefs::kCredentialsEnableAutosignin, |
| 352 new base::FundamentalValue(zero_click)); |
| 353 |
| 354 // Requesting a zero-click credential should try to retrieve PasswordForms |
| 355 // from the PasswordStore and resolve the request with an empty Credential |
| 356 // when none are found. |
| 357 const int request_id = 0; |
| 358 bool verified = false; |
| 359 ExpectPromiseResolved(&verified, request_id, web::Credential()); |
| 360 credential_manager_->CredentialsRequested( |
| 361 request_id, GURL("http://foo.com/login"), zero_click, |
| 362 std::vector<std::string>(), true); |
| 363 |
| 364 // Pump the message loop and verify. |
| 365 WaitForBackgroundTasks(); |
| 366 EXPECT_TRUE(verified); |
| 367 } |
| 368 |
| 369 // Tests that a zero-click credential request properly resolves. |
| 370 TEST_F(CredentialManagerTest, ZeroClickRequestResolved) { |
| 371 // Set a password store with a credential, enable zero-click, and request a |
| 372 // zero-click credential. |
| 373 scoped_refptr<password_manager::TestPasswordStore> store( |
| 374 new password_manager::TestPasswordStore); |
| 375 const bool zero_click = true; |
| 376 store->AddLogin(GetTestPasswordForm1(zero_click)); |
| 377 stub_client_.SetPasswordStore(store); |
| 378 static_cast<TestingPrefServiceSimple*>(stub_client_.GetPrefs()) |
| 379 ->SetUserPref(password_manager::prefs::kCredentialsEnableAutosignin, |
| 380 new base::FundamentalValue(zero_click)); |
| 381 // Without the first run experience, the zero-click credentials won't be |
| 382 // passed to the site. |
| 383 static_cast<TestingPrefServiceSimple*>(stub_client_.GetPrefs()) |
| 384 ->SetUserPref( |
| 385 password_manager::prefs::kWasAutoSignInFirstRunExperienceShown, |
| 386 new base::FundamentalValue(true)); |
| 387 WaitForBackgroundTasks(); |
| 388 |
| 389 // Requesting a zero-click credential should retrieve a PasswordForm from the |
| 390 // PasswordStore and resolve the request with a corresponding Credential. |
| 391 const int request_id = 0; |
| 392 bool verified = false; |
| 393 ExpectPromiseResolved(&verified, request_id, |
| 394 GetTestWebCredential1(zero_click)); |
| 395 credential_manager_->CredentialsRequested( |
| 396 request_id, GURL("http://foo.com/login"), zero_click, |
| 397 std::vector<std::string>(), true); |
| 398 |
| 399 // Pump the message loop and verify. |
| 400 WaitForBackgroundTasks(); |
| 401 EXPECT_TRUE(verified); |
| 402 } |
| 403 |
| 404 // Tests that a credential request properly resolves. |
| 405 // TODO(crbug.com/598851): Reenabled this test. |
| 406 TEST_F(CredentialManagerTest, DISABLED_RequestResolved) { |
| 407 // Set a password store with two credentials and set a credential to be |
| 408 // returned by the PasswordManagerClient as if chosen by the user. |
| 409 scoped_refptr<password_manager::TestPasswordStore> store( |
| 410 new password_manager::TestPasswordStore); |
| 411 const bool zero_click = false; |
| 412 store->AddLogin(GetTestPasswordForm1(zero_click)); |
| 413 store->AddLogin(GetTestPasswordForm2(zero_click)); |
| 414 stub_client_.SetPasswordStore(store); |
| 415 stub_client_.SetUserChosenCredential(password_manager::CredentialInfo( |
| 416 GetTestPasswordForm2(zero_click), |
| 417 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD)); |
| 418 WaitForBackgroundTasks(); |
| 419 |
| 420 // Request a credential. The request should be resolved with the credential |
| 421 // set on the stub client. |
| 422 const int request_id = 0; |
| 423 bool verified = false; |
| 424 ExpectPromiseResolved(&verified, request_id, |
| 425 GetTestWebCredential2(zero_click)); |
| 426 credential_manager_->CredentialsRequested( |
| 427 request_id, GURL("http://foo.com/login"), zero_click, |
| 428 std::vector<std::string>(), true); |
| 429 |
| 430 // Pump the message loop and verify. |
| 431 WaitForBackgroundTasks(); |
| 432 EXPECT_TRUE(verified); |
| 433 } |
| 434 |
| 435 // Tests that two requests back-to-back succeed when they wait to be resolved. |
| 436 // TODO(crbug.com/598851): Reenable this test. |
| 437 TEST_F(CredentialManagerTest, DISABLED_ConsecutiveRequestsResolve) { |
| 438 // Set a password store with two credentials and set a credential to be |
| 439 // returned by the PasswordManagerClient as if chosen by the user. |
| 440 scoped_refptr<password_manager::TestPasswordStore> store( |
| 441 new password_manager::TestPasswordStore); |
| 442 const bool zero_click = false; |
| 443 store->AddLogin(GetTestPasswordForm1(zero_click)); |
| 444 stub_client_.SetPasswordStore(store); |
| 445 stub_client_.SetUserChosenCredential(password_manager::CredentialInfo( |
| 446 GetTestPasswordForm1(zero_click), |
| 447 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD)); |
| 448 WaitForBackgroundTasks(); |
| 449 |
| 450 // Request a credential. The request should be resolved with the credential |
| 451 // set on the stub client. |
| 452 const int first_request_id = 0; |
| 453 bool first_verified = false; |
| 454 ExpectPromiseResolved(&first_verified, first_request_id, |
| 455 GetTestWebCredential1(zero_click)); |
| 456 credential_manager_->CredentialsRequested( |
| 457 first_request_id, GURL("http://foo.com/login"), zero_click, |
| 458 std::vector<std::string>(), true); |
| 459 |
| 460 // Pump the message loop and verify. |
| 461 WaitForBackgroundTasks(); |
| 462 EXPECT_TRUE(first_verified); |
| 463 |
| 464 ClearPromiseResolutionExpectations(); |
| 465 |
| 466 // Make a second request. It should be resolved again. |
| 467 const int second_request_id = 1; |
| 468 bool second_verified = false; |
| 469 ExpectPromiseResolved(&second_verified, second_request_id, |
| 470 GetTestWebCredential1(zero_click)); |
| 471 credential_manager_->CredentialsRequested( |
| 472 second_request_id, GURL("http://foo.com/login"), zero_click, |
| 473 std::vector<std::string>(), true); |
| 474 |
| 475 // Pump the message loop and verify. |
| 476 WaitForBackgroundTasks(); |
| 477 EXPECT_TRUE(second_verified); |
| 478 } |
| 479 |
| 480 // Tests that notifySignedIn prompts the user to save a password. |
| 481 TEST_F(CredentialManagerTest, |
| 482 SignInResolvesAndPromptsUserWhenSavingEnabledAndNotBlacklisted) { |
| 483 // Set a password store so the PasswordFormManager can retrieve credentials. |
| 484 scoped_refptr<password_manager::TestPasswordStore> store( |
| 485 new password_manager::TestPasswordStore); |
| 486 stub_client_.SetPasswordStore(store); |
| 487 const bool saving_enabled = true; |
| 488 EXPECT_CALL(stub_client_, IsSavingAndFillingEnabledForCurrentPage()) |
| 489 .WillOnce(Return(saving_enabled)) |
| 490 .WillOnce(Return(saving_enabled)); |
| 491 |
| 492 // Notify the browser that the user signed in. |
| 493 const int request_id = 0; |
| 494 ExpectPromiseResolved(request_id); |
| 495 const bool zero_click = false; |
| 496 credential_manager_->SignedIn(request_id, GURL("http://foo.com/login"), |
| 497 GetTestWebCredential1(zero_click)); |
| 498 |
| 499 // Pump the message loop and verify. |
| 500 WaitForBackgroundTasks(); |
| 501 autofill::PasswordForm expected_observed_form( |
| 502 GetTestPasswordForm1(zero_click)); |
| 503 expected_observed_form.username_value.clear(); |
| 504 expected_observed_form.password_value.clear(); |
| 505 EXPECT_EQ(expected_observed_form, stub_client_.saved_form()->observed_form()); |
| 506 } |
| 507 |
| 508 // Tests that notifySignedIn doesn't prompt the user to save a password when the |
| 509 // password manager is disabled for the current page. |
| 510 TEST_F(CredentialManagerTest, |
| 511 SignInResolvesAndDoesNotPromptsUserWhenSavingDisabledAndNotBlacklisted) { |
| 512 // Disable saving. |
| 513 scoped_refptr<password_manager::TestPasswordStore> store( |
| 514 new password_manager::TestPasswordStore); |
| 515 stub_client_.SetPasswordStore(store); |
| 516 const bool saving_enabled = false; |
| 517 EXPECT_CALL(stub_client_, IsSavingAndFillingEnabledForCurrentPage()) |
| 518 .WillOnce(Return(saving_enabled)); |
| 519 |
| 520 // Notify the browser that the user signed in. |
| 521 const int request_id = 0; |
| 522 ExpectPromiseResolved(request_id); |
| 523 const bool zero_click = false; |
| 524 credential_manager_->SignedIn(request_id, GURL("http://foo.com/login"), |
| 525 GetTestWebCredential1(zero_click)); |
| 526 |
| 527 // Pump the message loop and verify that no form was saved. |
| 528 WaitForBackgroundTasks(); |
| 529 EXPECT_FALSE(stub_client_.saved_form()); |
| 530 } |
| 531 |
| 532 // Tests that notifySignedIn doesn't prompt the user to save a password when the |
| 533 // submitted form is blacklisted by the password manager. |
| 534 TEST_F(CredentialManagerTest, |
| 535 SignInResolvesAndDoesNotPromptUserWhenSavingEnabledAndBlacklisted) { |
| 536 // Disable saving. |
| 537 scoped_refptr<password_manager::TestPasswordStore> store( |
| 538 new password_manager::TestPasswordStore); |
| 539 stub_client_.SetPasswordStore(store); |
| 540 const bool saving_enabled = true; |
| 541 EXPECT_CALL(stub_client_, IsSavingAndFillingEnabledForCurrentPage()) |
| 542 .WillOnce(Return(saving_enabled)) |
| 543 .WillOnce(Return(saving_enabled)); |
| 544 |
| 545 // Save the credential that will be signed in and mark it blacklisted. |
| 546 const bool zero_click = false; |
| 547 autofill::PasswordForm blacklisted_form(GetTestPasswordForm1(zero_click)); |
| 548 blacklisted_form.blacklisted_by_user = true; |
| 549 store->AddLogin(blacklisted_form); |
| 550 WaitForBackgroundTasks(); |
| 551 |
| 552 // Notify the browser that the user signed in. |
| 553 const int request_id = 0; |
| 554 ExpectPromiseResolved(request_id); |
| 555 credential_manager_->SignedIn(request_id, GURL("http://foo.com/login"), |
| 556 GetTestWebCredential1(zero_click)); |
| 557 |
| 558 // Pump the message loop and verify that no form was saved. |
| 559 WaitForBackgroundTasks(); |
| 560 EXPECT_FALSE(stub_client_.saved_form()); |
| 561 } |
| 562 |
| 563 // Tests that notifySignedOut marks credentials as non-zero-click. |
| 564 TEST_F(CredentialManagerTest, SignOutResolvesAndMarksFormsNonZeroClick) { |
| 565 // Create two zero-click credentials for the current page. |
| 566 std::string current_origin(credential_manager_->GetOrigin().spec()); |
| 567 autofill::PasswordForm form1(GetTestPasswordForm1(true)); |
| 568 form1.signon_realm = current_origin; |
| 569 autofill::PasswordForm form2(GetTestPasswordForm2(true)); |
| 570 form2.signon_realm = current_origin; |
| 571 |
| 572 // Add both credentials to the store. |
| 573 scoped_refptr<password_manager::TestPasswordStore> store( |
| 574 new password_manager::TestPasswordStore); |
| 575 stub_client_.SetPasswordStore(store); |
| 576 store->AddLogin(form1); |
| 577 store->AddLogin(form2); |
| 578 WaitForBackgroundTasks(); |
| 579 |
| 580 // Check that both credentials in the store are zero-click. |
| 581 EXPECT_EQ(1U, store->stored_passwords().size()); |
| 582 const std::vector<autofill::PasswordForm>& passwords_before_signout = |
| 583 store->stored_passwords().find(current_origin)->second; |
| 584 EXPECT_EQ(2U, passwords_before_signout.size()); |
| 585 for (const autofill::PasswordForm& form : passwords_before_signout) |
| 586 EXPECT_FALSE(form.skip_zero_click); |
| 587 |
| 588 // Sign out the current origin, which has credentials, and a second origin, |
| 589 // which doesnt. |
| 590 const int first_request_id = 0; |
| 591 ExpectPromiseResolved(first_request_id); |
| 592 credential_manager_->SignedOut(first_request_id, |
| 593 credential_manager_->GetOrigin()); |
| 594 const int second_request_id = 1; |
| 595 ExpectPromiseResolved(second_request_id); |
| 596 credential_manager_->SignedOut(second_request_id, GURL("https://foo.com")); |
| 597 |
| 598 // Pump the message loop to let the promises resolve. Check that the |
| 599 // credentials in the store are now non-zero-click and that signing out an |
| 600 // origin with no credentials had no effect. |
| 601 WaitForBackgroundTasks(); |
| 602 EXPECT_EQ(1U, store->stored_passwords().size()); |
| 603 const std::vector<autofill::PasswordForm>& passwords_after_signout = |
| 604 store->stored_passwords().find(current_origin)->second; |
| 605 EXPECT_EQ(2U, passwords_after_signout.size()); |
| 606 for (const autofill::PasswordForm& form : passwords_after_signout) |
| 607 EXPECT_TRUE(form.skip_zero_click); |
| 608 } |
| 609 |
| 610 } // namespace |
OLD | NEW |