blob: 18fddf584b1a4fc36167e251416c68282fb68359 [file] [log] [blame]
// Copyright 2015 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/password_manager/core/browser/sync_credentials_filter.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "components/password_manager/core/browser/fake_form_fetcher.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_form_manager.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/password_save_manager_impl.h"
#include "components/password_manager/core/browser/stub_form_saver.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
#include "components/password_manager/core/browser/sync_username_test_base.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace password_manager {
namespace {
const char kFilledAndLoginActionName[] =
"PasswordManager_SyncCredentialFilledAndLoginSuccessfull";
const char kEnterpriseURL[] = "https://enterprise.test/";
class FakePasswordManagerClient : public StubPasswordManagerClient {
public:
explicit FakePasswordManagerClient(signin::IdentityManager* identity_manager)
: identity_manager_(identity_manager) {
if (!base::FeatureList::IsEnabled(
features::kPasswordReuseDetectionEnabled)) {
return;
}
// Initializes and configures prefs.
prefs_ = std::make_unique<TestingPrefServiceSimple>();
prefs_->registry()->RegisterStringPref(
prefs::kPasswordProtectionChangePasswordURL, "");
prefs_->registry()->RegisterListPref(prefs::kPasswordProtectionLoginURLs);
prefs_->SetString(prefs::kPasswordProtectionChangePasswordURL,
kEnterpriseURL);
}
~FakePasswordManagerClient() override {
password_store_->ShutdownOnUIThread();
}
// PasswordManagerClient:
url::Origin GetLastCommittedOrigin() const override {
return last_committed_origin_;
}
MockPasswordStore* GetProfilePasswordStore() const override {
return password_store_.get();
}
signin::IdentityManager* GetIdentityManager() override {
return identity_manager_;
}
void set_last_committed_entry_url(base::StringPiece url_spec) {
last_committed_origin_ = url::Origin::Create(GURL(url_spec));
}
PrefService* GetPrefs() const override { return prefs_.get(); }
bool IsIncognito() const override { return is_incognito_; }
void SetIsIncognito(bool is_incognito) { is_incognito_ = is_incognito; }
private:
url::Origin last_committed_origin_;
scoped_refptr<testing::NiceMock<MockPasswordStore>> password_store_ =
new testing::NiceMock<MockPasswordStore>;
bool is_incognito_ = false;
signin::IdentityManager* identity_manager_;
std::unique_ptr<TestingPrefServiceSimple> prefs_;
DISALLOW_COPY_AND_ASSIGN(FakePasswordManagerClient);
};
} // namespace
// The bool param specifies whether features::kEnablePasswordsAccountStorage is
// enabled.
class CredentialsFilterTest : public SyncUsernameTestBase,
public testing::WithParamInterface<bool> {
public:
// Flag for creating a PasswordFormManager, deciding its IsNewLogin() value.
enum class LoginState { NEW, EXISTING };
CredentialsFilterTest() : pending_(SimpleGaiaForm("user@gmail.com")) {
if (GetParam()) {
feature_list_.InitWithFeatures(
/*enabled_features=*/{features::kPasswordReuseDetectionEnabled,
features::kEnablePasswordsAccountStorage},
/*disabled_features=*/{});
} else {
feature_list_.InitWithFeatures(
/*enabled_features=*/{features::kPasswordReuseDetectionEnabled},
/*disabled_features=*/{features::kEnablePasswordsAccountStorage});
}
client_ = std::make_unique<FakePasswordManagerClient>(identity_manager());
form_manager_ = std::make_unique<PasswordFormManager>(
client_.get(), driver_.AsWeakPtr(), pending_.form_data, &fetcher_,
std::make_unique<PasswordSaveManagerImpl>(
std::make_unique<StubFormSaver>()),
nullptr /* metrics_recorder */);
filter_ = std::make_unique<SyncCredentialsFilter>(
client_.get(), base::BindRepeating(&SyncUsernameTestBase::sync_service,
base::Unretained(this)));
fetcher_.Fetch();
}
// Makes |form_manager_| provisionally save |pending_|. Depending on
// |login_state| being NEW or EXISTING, prepares |form_manager_| in a state in
// which |pending_| looks like a new or existing credential, respectively.
void SavePending(LoginState login_state) {
std::vector<const PasswordForm*> matches;
if (login_state == LoginState::EXISTING) {
matches.push_back(&pending_);
}
fetcher_.SetNonFederated(matches);
fetcher_.NotifyFetchCompleted();
form_manager_->ProvisionallySave(pending_.form_data, &driver_, nullptr);
}
protected:
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<FakePasswordManagerClient> client_;
StubPasswordManagerDriver driver_;
PasswordForm pending_;
FakeFormFetcher fetcher_;
std::unique_ptr<PasswordFormManager> form_manager_;
std::unique_ptr<SyncCredentialsFilter> filter_;
};
TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_ExistingSyncCredentials) {
FakeSigninAs("user@gmail.com");
SetSyncingPasswords(true);
base::UserActionTester tester;
SavePending(LoginState::EXISTING);
filter_->ReportFormLoginSuccess(*form_manager_);
EXPECT_EQ(1, tester.GetActionCount(kFilledAndLoginActionName));
}
TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_NewSyncCredentials) {
FakeSigninAs("user@gmail.com");
SetSyncingPasswords(true);
base::UserActionTester tester;
SavePending(LoginState::NEW);
filter_->ReportFormLoginSuccess(*form_manager_);
EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
}
TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_GAIANotSyncCredentials) {
const char kOtherUsername[] = "other_user@gmail.com";
FakeSigninAs(kOtherUsername);
ASSERT_NE(pending_.username_value, base::ASCIIToUTF16(kOtherUsername));
SetSyncingPasswords(true);
base::UserActionTester tester;
SavePending(LoginState::EXISTING);
filter_->ReportFormLoginSuccess(*form_manager_);
EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
}
TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_NotGAIACredentials) {
pending_ = SimpleNonGaiaForm("user@gmail.com");
FakeSigninAs("user@gmail.com");
SetSyncingPasswords(true);
base::UserActionTester tester;
SavePending(LoginState::EXISTING);
filter_->ReportFormLoginSuccess(*form_manager_);
EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
}
TEST_P(CredentialsFilterTest, ReportFormLoginSuccess_NotSyncing) {
FakeSigninAs("user@gmail.com");
SetSyncingPasswords(false);
base::UserActionTester tester;
SavePending(LoginState::EXISTING);
filter_->ReportFormLoginSuccess(*form_manager_);
EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
}
TEST_P(CredentialsFilterTest, ShouldSave_NotSignedIn) {
PasswordForm form = SimpleGaiaForm("user@example.org");
ASSERT_TRUE(identity_manager()->GetPrimaryAccountInfo().IsEmpty());
SetSyncingPasswords(false);
// If kEnablePasswordsAccountStorage is enabled, then Chrome shouldn't offer
// to save the password for the primary account. If there is no primary
// account yet, then the just-signed-in account will *become* the primary
// account immediately, so it shouldn't be saved either.
if (base::FeatureList::IsEnabled(features::kEnablePasswordsAccountStorage))
EXPECT_FALSE(filter_->ShouldSave(form));
else
EXPECT_TRUE(filter_->ShouldSave(form));
}
TEST_P(CredentialsFilterTest, ShouldSave_NotSyncCredential) {
PasswordForm form = SimpleGaiaForm("user@example.org");
FakeSigninAs("different_user@example.org");
SetSyncingPasswords(true);
EXPECT_TRUE(filter_->ShouldSave(form));
}
TEST_P(CredentialsFilterTest, ShouldSave_SyncCredential) {
PasswordForm form = SimpleGaiaForm("user@example.org");
FakeSigninAs("user@example.org");
SetSyncingPasswords(true);
EXPECT_FALSE(filter_->ShouldSave(form));
}
TEST_P(CredentialsFilterTest, ShouldSave_SignIn_Form) {
PasswordForm form = SimpleGaiaForm("user@example.org");
form.form_data.is_gaia_with_skip_save_password_form = true;
SetSyncingPasswords(false);
EXPECT_FALSE(filter_->ShouldSave(form));
}
TEST_P(CredentialsFilterTest, ShouldSave_SyncCredential_NotSyncingPasswords) {
PasswordForm form = SimpleGaiaForm("user@example.org");
FakeSigninAs("user@example.org");
SetSyncingPasswords(false);
// If kEnablePasswordsAccountStorage is enabled, then Chrome shouldn't offer
// to save the password for the primary account - doesn't matter if passwords
// are being synced or not.
if (base::FeatureList::IsEnabled(features::kEnablePasswordsAccountStorage))
EXPECT_FALSE(filter_->ShouldSave(form));
else
EXPECT_TRUE(filter_->ShouldSave(form));
}
TEST_P(CredentialsFilterTest, ShouldSaveGaiaPasswordHash) {
PasswordForm gaia_form = SimpleGaiaForm("user@gmail.org");
EXPECT_TRUE(filter_->ShouldSaveGaiaPasswordHash(gaia_form));
PasswordForm other_form = SimpleNonGaiaForm("user@example.org");
EXPECT_FALSE(filter_->ShouldSaveGaiaPasswordHash(other_form));
}
TEST_P(CredentialsFilterTest, ShouldNotSaveGaiaPasswordHashIncognito) {
client_->SetIsIncognito(true);
PasswordForm gaia_form = SimpleGaiaForm("user@gmail.org");
EXPECT_FALSE(filter_->ShouldSaveGaiaPasswordHash(gaia_form));
PasswordForm other_form = SimpleNonGaiaForm("user@example.org");
EXPECT_FALSE(filter_->ShouldSaveGaiaPasswordHash(other_form));
}
TEST_P(CredentialsFilterTest, ShouldSaveEnterprisePasswordHash) {
PasswordForm gaia_form = SimpleGaiaForm("user@gmail.org");
EXPECT_FALSE(filter_->ShouldSaveEnterprisePasswordHash(gaia_form));
PasswordForm other_form = SimpleNonGaiaForm("user@example.org");
EXPECT_FALSE(filter_->ShouldSaveEnterprisePasswordHash(other_form));
PasswordForm enterprise_form =
SimpleNonGaiaForm("user@enterprise.test", kEnterpriseURL);
EXPECT_TRUE(filter_->ShouldSaveEnterprisePasswordHash(enterprise_form));
}
TEST_P(CredentialsFilterTest, ShouldNotSaveEnterprisePasswordHashIncognito) {
client_->SetIsIncognito(true);
PasswordForm gaia_form = SimpleGaiaForm("user@gmail.org");
EXPECT_FALSE(filter_->ShouldSaveEnterprisePasswordHash(gaia_form));
PasswordForm other_form = SimpleNonGaiaForm("user@example.org");
EXPECT_FALSE(filter_->ShouldSaveEnterprisePasswordHash(other_form));
PasswordForm enterprise_form =
SimpleNonGaiaForm("user@enterprise.test", kEnterpriseURL);
EXPECT_FALSE(filter_->ShouldSaveEnterprisePasswordHash(enterprise_form));
}
TEST_P(CredentialsFilterTest, IsSyncAccountEmail) {
FakeSigninAs("user@gmail.com");
EXPECT_FALSE(filter_->IsSyncAccountEmail("user"));
EXPECT_FALSE(filter_->IsSyncAccountEmail("user2@gmail.com"));
EXPECT_FALSE(filter_->IsSyncAccountEmail("user2@example.com"));
EXPECT_TRUE(filter_->IsSyncAccountEmail("user@gmail.com"));
EXPECT_TRUE(filter_->IsSyncAccountEmail("us.er@gmail.com"));
EXPECT_TRUE(filter_->IsSyncAccountEmail("user@googlemail.com"));
}
TEST_P(CredentialsFilterTest, IsSyncAccountEmailIncognito) {
client_->SetIsIncognito(true);
FakeSigninAs("user@gmail.com");
EXPECT_FALSE(filter_->IsSyncAccountEmail("user"));
EXPECT_FALSE(filter_->IsSyncAccountEmail("user2@gmail.com"));
EXPECT_FALSE(filter_->IsSyncAccountEmail("user2@example.com"));
EXPECT_TRUE(filter_->IsSyncAccountEmail("user@gmail.com"));
EXPECT_TRUE(filter_->IsSyncAccountEmail("us.er@gmail.com"));
EXPECT_TRUE(filter_->IsSyncAccountEmail("user@googlemail.com"));
}
INSTANTIATE_TEST_SUITE_P(, CredentialsFilterTest, ::testing::Bool());
} // namespace password_manager