| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/autofill/core/browser/payments/credit_card_access_manager.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/base64.h" |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/metrics_hashes.h" |
| #include "base/run_loop.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/post_task.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "components/autofill/core/browser/autocomplete_history_manager.h" |
| #include "components/autofill/core/browser/autofill_download_manager.h" |
| #include "components/autofill/core/browser/autofill_test_utils.h" |
| #include "components/autofill/core/browser/data_model/autofill_profile.h" |
| #include "components/autofill/core/browser/data_model/credit_card.h" |
| #include "components/autofill/core/browser/form_structure.h" |
| #include "components/autofill/core/browser/metrics/form_events.h" |
| #include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h" |
| #include "components/autofill/core/browser/payments/test_payments_client.h" |
| #include "components/autofill/core/browser/personal_data_manager.h" |
| #include "components/autofill/core/browser/test_autofill_client.h" |
| #include "components/autofill/core/browser/test_autofill_clock.h" |
| #include "components/autofill/core/browser/test_autofill_driver.h" |
| #include "components/autofill/core/browser/test_personal_data_manager.h" |
| #include "components/autofill/core/browser/validation.h" |
| #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" |
| #include "components/autofill/core/common/autofill_clock.h" |
| #include "components/autofill/core/common/autofill_features.h" |
| #include "components/autofill/core/common/autofill_payments_features.h" |
| #include "components/autofill/core/common/autofill_prefs.h" |
| #include "components/autofill/core/common/autofill_switches.h" |
| #include "components/autofill/core/common/autofill_util.h" |
| #include "components/autofill/core/common/form_data.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/security_state/core/security_state.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/sync/driver/test_sync_service.h" |
| #include "components/variations/variations_associated_data.h" |
| #include "components/variations/variations_params_manager.h" |
| #include "components/version_info/channel.h" |
| #include "net/base/url_util.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "url/gurl.h" |
| |
| using base::ASCIIToUTF16; |
| |
| namespace autofill { |
| namespace { |
| |
| const char kTestGUID[] = "00000000-0000-0000-0000-000000000001"; |
| const char kTestNumber[] = "4234567890123456"; // Visa |
| |
| class TestAccessor : public CreditCardAccessManager::Accessor { |
| public: |
| TestAccessor() : weak_ptr_factory_(this) {} |
| |
| base::WeakPtr<TestAccessor> GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| void OnCreditCardFetched(bool did_succeed, |
| const CreditCard* card, |
| const base::string16& cvc) override { |
| did_succeed_ = did_succeed; |
| if (did_succeed_) { |
| DCHECK(card); |
| number_ = card->number(); |
| } |
| } |
| |
| base::string16 number() { return number_; } |
| |
| bool did_succeed() { return did_succeed_; } |
| |
| private: |
| // Is set to true if authentication was successful. |
| bool did_succeed_ = false; |
| // The card number returned from OnCreditCardFetched(). |
| base::string16 number_; |
| base::WeakPtrFactory<TestAccessor> weak_ptr_factory_; |
| }; |
| |
| std::string NextYear() { |
| base::Time::Exploded now; |
| base::Time::Now().LocalExplode(&now); |
| return base::NumberToString(now.year + 1); |
| } |
| |
| std::string NextMonth() { |
| base::Time::Exploded now; |
| base::Time::Now().LocalExplode(&now); |
| return base::NumberToString(now.month % 12 + 1); |
| } |
| |
| } // namespace |
| |
| class CreditCardAccessManagerTest : public testing::Test { |
| public: |
| CreditCardAccessManagerTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT, |
| base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode:: |
| QUEUED) {} |
| |
| void SetUp() override { |
| autofill_client_.SetPrefs(test::PrefServiceForTesting()); |
| personal_data_manager_.Init(/*profile_database=*/database_, |
| /*account_database=*/nullptr, |
| /*pref_service=*/autofill_client_.GetPrefs(), |
| /*identity_manager=*/nullptr, |
| /*client_profile_validator=*/nullptr, |
| /*history_service=*/nullptr, |
| /*is_off_the_record=*/false); |
| personal_data_manager_.SetPrefService(autofill_client_.GetPrefs()); |
| |
| accessor_.reset(new TestAccessor()); |
| autofill_driver_ = |
| std::make_unique<testing::NiceMock<TestAutofillDriver>>(); |
| request_context_ = new net::TestURLRequestContextGetter( |
| base::ThreadTaskRunnerHandle::Get()); |
| autofill_driver_->SetURLRequestContext(request_context_.get()); |
| |
| payments_client_ = new payments::TestPaymentsClient( |
| autofill_driver_->GetURLLoaderFactory(), |
| autofill_client_.GetIdentityManager(), &personal_data_manager_); |
| autofill_client_.set_test_payments_client( |
| std::unique_ptr<payments::TestPaymentsClient>(payments_client_)); |
| credit_card_access_manager_ = std::make_unique<CreditCardAccessManager>( |
| &autofill_client_, &personal_data_manager_, nullptr); |
| credit_card_access_manager_->set_fido_authenticator_for_testing( |
| std::make_unique<TestCreditCardFIDOAuthenticator>(&autofill_client_)); |
| } |
| |
| void TearDown() override { |
| // Order of destruction is important as AutofillDriver relies on |
| // PersonalDataManager to be around when it gets destroyed. |
| autofill_driver_.reset(); |
| |
| personal_data_manager_.SetPrefService(nullptr); |
| personal_data_manager_.ClearCreditCards(); |
| |
| request_context_ = nullptr; |
| } |
| |
| bool IsAuthenticationInProgress() { |
| return credit_card_access_manager_->is_authentication_in_progress(); |
| } |
| |
| void CreateLocalCard(std::string guid, std::string number = std::string()) { |
| CreditCard local_card = CreditCard(); |
| test::SetCreditCardInfo(&local_card, "Elvis Presley", number.c_str(), |
| NextMonth().c_str(), NextYear().c_str(), "1"); |
| local_card.set_guid(guid); |
| local_card.set_record_type(CreditCard::LOCAL_CARD); |
| |
| personal_data_manager_.ClearCreditCards(); |
| personal_data_manager_.AddCreditCard(local_card); |
| } |
| |
| void CreateServerCard(std::string guid, std::string number = std::string()) { |
| CreditCard masked_server_card = CreditCard(); |
| test::SetCreditCardInfo(&masked_server_card, "Elvis Presley", |
| number.c_str(), NextMonth().c_str(), |
| NextYear().c_str(), "1"); |
| masked_server_card.set_guid(guid); |
| masked_server_card.set_record_type(CreditCard::MASKED_SERVER_CARD); |
| |
| personal_data_manager_.ClearCreditCards(); |
| personal_data_manager_.AddServerCreditCard(masked_server_card); |
| } |
| |
| // Returns true if full card request was sent from CVC auth. |
| bool GetRealPanForCVCAuth(AutofillClient::PaymentsRpcResult result, |
| const std::string& real_pan) { |
| payments::FullCardRequest* full_card_request = |
| GetCVCAuthenticator()->full_card_request_.get(); |
| |
| if (!full_card_request) |
| return false; |
| |
| full_card_request->OnDidGetRealPan(result, real_pan); |
| return true; |
| } |
| |
| // Returns true if full card request was sent from FIDO auth. |
| bool GetRealPanForFIDOAuth(AutofillClient::PaymentsRpcResult result, |
| const std::string& real_pan) { |
| payments::FullCardRequest* full_card_request = |
| GetFIDOAuthenticator()->full_card_request_.get(); |
| |
| if (!full_card_request) |
| return false; |
| |
| full_card_request->OnDidGetRealPan(result, real_pan); |
| return true; |
| } |
| |
| CreditCardCVCAuthenticator* GetCVCAuthenticator() { |
| return credit_card_access_manager_->GetOrCreateCVCAuthenticator(); |
| } |
| |
| TestCreditCardFIDOAuthenticator* GetFIDOAuthenticator() { |
| return static_cast<TestCreditCardFIDOAuthenticator*>( |
| credit_card_access_manager_->GetOrCreateFIDOAuthenticator()); |
| } |
| |
| void OnFIDOUserVerification(bool did_succeed) { |
| // TODO(crbug/949269): Currently CreditCardFIDOAuthenticator fails by |
| // default. Once implemented, update this function along with |
| // TestCreditCardFIDOAuthenticator to mock a user verification gesture. |
| } |
| |
| void InvokeUnmaskDetailsTimeout() { |
| credit_card_access_manager_->ready_to_start_authentication_.Signal(); |
| } |
| |
| void WaitForCallbacks() { scoped_task_environment_.RunUntilIdle(); } |
| |
| protected: |
| std::unique_ptr<TestAccessor> accessor_; |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| payments::TestPaymentsClient* payments_client_; |
| TestAutofillClient autofill_client_; |
| std::unique_ptr<TestAutofillDriver> autofill_driver_; |
| scoped_refptr<net::TestURLRequestContextGetter> request_context_; |
| scoped_refptr<AutofillWebDataService> database_; |
| TestPersonalDataManager personal_data_manager_; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| std::unique_ptr<CreditCardAccessManager> credit_card_access_manager_; |
| }; |
| |
| // Ensures GetCreditCard() successfully retrieves Card. |
| TEST_F(CreditCardAccessManagerTest, GetCreditCardSuccess) { |
| CreateLocalCard(kTestGUID); |
| |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| EXPECT_NE(card, nullptr); |
| } |
| |
| // Ensures GetCreditCard() returns nullptr for invalid GUID. |
| TEST_F(CreditCardAccessManagerTest, GetCreditCardFailure) { |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| EXPECT_EQ(card, nullptr); |
| } |
| |
| // Ensures DeleteCard() successfully removes local cards. |
| TEST_F(CreditCardAccessManagerTest, RemoveLocalCreditCard) { |
| CreateLocalCard(kTestGUID); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| EXPECT_TRUE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID)); |
| EXPECT_TRUE(credit_card_access_manager_->DeleteCard(card)); |
| EXPECT_FALSE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID)); |
| } |
| |
| // Ensures DeleteCard() does nothing for server cards. |
| TEST_F(CreditCardAccessManagerTest, RemoveServerCreditCard) { |
| CreateServerCard(kTestGUID); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| EXPECT_TRUE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID)); |
| EXPECT_FALSE(credit_card_access_manager_->DeleteCard(card)); |
| |
| // Cannot delete server cards. |
| EXPECT_TRUE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID)); |
| } |
| |
| // Ensures GetDeletionConfirmationText(~) returns correct values for local |
| // cards. |
| TEST_F(CreditCardAccessManagerTest, LocalCardGetDeletionConfirmationText) { |
| CreateLocalCard(kTestGUID); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| base::string16 title = base::string16(); |
| base::string16 body = base::string16(); |
| EXPECT_TRUE(credit_card_access_manager_->GetDeletionConfirmationText( |
| card, &title, &body)); |
| |
| // |title| and |body| should be updated appropriately. |
| EXPECT_EQ(title, card->NetworkOrBankNameAndLastFourDigits()); |
| EXPECT_EQ(body, |
| l10n_util::GetStringUTF16( |
| IDS_AUTOFILL_DELETE_CREDIT_CARD_SUGGESTION_CONFIRMATION_BODY)); |
| } |
| |
| // Ensures GetDeletionConfirmationText(~) returns false for server cards. |
| TEST_F(CreditCardAccessManagerTest, ServerCardGetDeletionConfirmationText) { |
| CreateServerCard(kTestGUID); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| base::string16 title = base::string16(); |
| base::string16 body = base::string16(); |
| EXPECT_FALSE(credit_card_access_manager_->GetDeletionConfirmationText( |
| card, &title, &body)); |
| |
| // |title| and |body| should remain unchanged. |
| EXPECT_EQ(title, base::string16()); |
| EXPECT_EQ(body, base::string16()); |
| } |
| |
| // Tests retrieving local cards. |
| TEST_F(CreditCardAccessManagerTest, FetchLocalCardSuccess) { |
| CreateLocalCard(kTestGUID, kTestNumber); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); |
| |
| EXPECT_TRUE(accessor_->did_succeed()); |
| EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); |
| } |
| |
| // Ensures that FetchCreditCard() reports a failure when a card does not exist. |
| TEST_F(CreditCardAccessManagerTest, FetchNullptrFailure) { |
| personal_data_manager_.ClearCreditCards(); |
| |
| credit_card_access_manager_->FetchCreditCard(nullptr, |
| accessor_->GetWeakPtr()); |
| EXPECT_FALSE(accessor_->did_succeed()); |
| } |
| |
| // Ensures that FetchCreditCard() returns the full PAN upon a successful |
| // response from payments. |
| TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCSuccess) { |
| CreateServerCard(kTestGUID, kTestNumber); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); |
| |
| EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber)); |
| EXPECT_TRUE(accessor_->did_succeed()); |
| EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); |
| } |
| |
| // Ensures that FetchCreditCard() returns a failure upon a negative response |
| // from the server. |
| TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCNetworkError) { |
| CreateServerCard(kTestGUID, kTestNumber); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); |
| |
| EXPECT_TRUE( |
| GetRealPanForCVCAuth(AutofillClient::NETWORK_ERROR, std::string())); |
| EXPECT_FALSE(accessor_->did_succeed()); |
| } |
| |
| // Ensures that FetchCreditCard() returns a failure upon a negative response |
| // from the server. |
| TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCPermanentFailure) { |
| CreateServerCard(kTestGUID, kTestNumber); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); |
| |
| EXPECT_TRUE( |
| GetRealPanForCVCAuth(AutofillClient::PERMANENT_FAILURE, std::string())); |
| EXPECT_FALSE(accessor_->did_succeed()); |
| } |
| |
| // Ensures that a "try again" response from payments does not end the flow. |
| TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCTryAgainFailure) { |
| CreateServerCard(kTestGUID, kTestNumber); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); |
| |
| EXPECT_TRUE( |
| GetRealPanForCVCAuth(AutofillClient::TRY_AGAIN_FAILURE, std::string())); |
| EXPECT_FALSE(accessor_->did_succeed()); |
| |
| EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber)); |
| EXPECT_TRUE(accessor_->did_succeed()); |
| EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); |
| } |
| |
| // Ensures that CVC prompt is invoked after WebAuthn fails. |
| TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOFailureCVCFallback) { |
| CreateServerCard(kTestGUID, kTestNumber); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| GetFIDOAuthenticator()->SetUserVerifiable(true); |
| GetFIDOAuthenticator()->SetUserOptIn(true); |
| payments_client_->AddFidoEligibleCard(card->server_id()); |
| |
| credit_card_access_manager_->PrepareToFetchCreditCard(); |
| WaitForCallbacks(); |
| |
| credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); |
| WaitForCallbacks(); |
| |
| // FIDO Failure. |
| OnFIDOUserVerification(/*did_succeed=*/false); |
| EXPECT_FALSE(GetRealPanForFIDOAuth(AutofillClient::SUCCESS, kTestNumber)); |
| EXPECT_FALSE(accessor_->did_succeed()); |
| |
| // Followed by a fallback to CVC. |
| EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber)); |
| EXPECT_TRUE(accessor_->did_succeed()); |
| EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); |
| } |
| |
| // Ensures that CVC prompt is invoked when the pre-flight call to Google |
| // Payments times out. |
| TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOTimeoutCVCFallback) { |
| CreateServerCard(kTestGUID, kTestNumber); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| GetFIDOAuthenticator()->SetUserVerifiable(true); |
| GetFIDOAuthenticator()->SetUserOptIn(true); |
| |
| credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); |
| InvokeUnmaskDetailsTimeout(); |
| WaitForCallbacks(); |
| |
| EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber)); |
| EXPECT_TRUE(accessor_->did_succeed()); |
| EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); |
| } |
| |
| // Ensures that |is_authentication_in_progress_| is set correctly. |
| TEST_F(CreditCardAccessManagerTest, AuthenticationInProgress) { |
| CreateServerCard(kTestGUID, kTestNumber); |
| CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); |
| |
| EXPECT_FALSE(IsAuthenticationInProgress()); |
| |
| credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); |
| EXPECT_TRUE(IsAuthenticationInProgress()); |
| |
| EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber)); |
| EXPECT_FALSE(IsAuthenticationInProgress()); |
| } |
| |
| } // namespace autofill |