blob: 7b0df366320fc677b6fbf0305d250f08e49331c9 [file] [log] [blame]
// Copyright 2016 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/credential_manager_password_form_manager.h"
#include <memory>
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/fake_form_fetcher.h"
#include "components/password_manager/core/browser/stub_form_saver.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using autofill::PasswordForm;
using base::ASCIIToUTF16;
using testing::_;
using testing::Invoke;
namespace password_manager {
namespace {
class MockDelegate : public CredentialManagerPasswordFormManagerDelegate {
public:
MOCK_METHOD0(OnProvisionalSaveComplete, void());
};
class MockFormSaver : public StubFormSaver {
public:
MockFormSaver() = default;
~MockFormSaver() override = default;
// FormSaver:
MOCK_METHOD3(Save,
void(PasswordForm pending,
const std::vector<const PasswordForm*>& matches,
const base::string16& old_password));
MOCK_METHOD3(Update,
void(PasswordForm pending,
const std::vector<const PasswordForm*>& matches,
const base::string16& old_password));
// Convenience downcasting method.
static MockFormSaver& Get(PasswordFormManager* form_manager) {
return *static_cast<MockFormSaver*>(form_manager->form_saver());
}
private:
DISALLOW_COPY_AND_ASSIGN(MockFormSaver);
};
MATCHER_P(FormMatches, form, "") {
return form.signon_realm == arg.signon_realm && form.origin == arg.origin &&
form.username_value == arg.username_value &&
form.password_value == arg.password_value &&
form.scheme == arg.scheme && form.type == arg.type;
}
} // namespace
class CredentialManagerPasswordFormManagerTest : public testing::Test {
public:
CredentialManagerPasswordFormManagerTest() {
form_to_save_.origin = GURL("https://example.com/path");
form_to_save_.signon_realm = "https://example.com/";
form_to_save_.username_value = ASCIIToUTF16("user1");
form_to_save_.password_value = ASCIIToUTF16("pass1");
form_to_save_.scheme = PasswordForm::Scheme::kHtml;
form_to_save_.type = PasswordForm::Type::kApi;
}
protected:
std::unique_ptr<CredentialManagerPasswordFormManager> CreateFormManager(
const PasswordForm& form_to_save) {
std::unique_ptr<FakeFormFetcher> fetcher(new FakeFormFetcher());
std::unique_ptr<MockFormSaver> saver(new MockFormSaver());
return std::make_unique<CredentialManagerPasswordFormManager>(
&client_, std::make_unique<PasswordForm>(form_to_save), &delegate_,
std::make_unique<MockFormSaver>(), std::make_unique<FakeFormFetcher>());
}
void SetNonFederatedAndNotifyFetchCompleted(
FormFetcher* fetcher,
const std::vector<const PasswordForm*>& non_federated) {
auto* fake_fetcher = static_cast<FakeFormFetcher*>(fetcher);
fake_fetcher->SetNonFederated(non_federated);
fake_fetcher->NotifyFetchCompleted();
// It is required because of PostTask in
// CredentialManagerPasswordFormManager::OnFetchCompleted
base::RunLoop().RunUntilIdle();
}
// Necessary for callbacks, and for TestAutofillDriver.
base::test::SingleThreadTaskEnvironment task_environment_;
StubPasswordManagerClient client_;
MockDelegate delegate_;
PasswordForm form_to_save_;
DISALLOW_COPY_AND_ASSIGN(CredentialManagerPasswordFormManagerTest);
};
// Test that aborting early does not cause use after free.
TEST_F(CredentialManagerPasswordFormManagerTest, AbortEarly) {
auto saved_form = std::make_unique<PasswordForm>();
saved_form->password_value = base::ASCIIToUTF16("password");
MockDelegate delegate;
auto form_manager = std::make_unique<CredentialManagerPasswordFormManager>(
&client_, std::move(saved_form), &delegate,
std::make_unique<StubFormSaver>(), std::make_unique<FakeFormFetcher>());
auto deleter = [&form_manager]() { form_manager.reset(); };
// Simulate that the PasswordStore responded to the FormFetcher. As a result,
// |form_manager| should call the delegate's OnProvisionalSaveComplete, which
// in turn should delete |form_fetcher|.
EXPECT_CALL(delegate, OnProvisionalSaveComplete()).WillOnce(Invoke(deleter));
static_cast<FakeFormFetcher*>(form_manager->GetFormFetcher())
->NotifyFetchCompleted();
// Check that |form_manager| was not deleted yet; doing so would have caused
// use after free during NotifyFetchCompleted.
EXPECT_TRUE(form_manager);
base::RunLoop().RunUntilIdle();
// Ultimately, |form_fetcher| should have been deleted. It just should happen
// after it finishes executing.
EXPECT_FALSE(form_manager);
}
// Ensure that GetCredentialSource is actually overriden and returns the proper
// value.
TEST_F(CredentialManagerPasswordFormManagerTest, GetCredentialSource) {
MockDelegate delegate;
auto form_manager = std::make_unique<CredentialManagerPasswordFormManager>(
&client_, std::make_unique<PasswordForm>(), &delegate,
std::make_unique<StubFormSaver>(), std::make_unique<FakeFormFetcher>());
ASSERT_EQ(metrics_util::CredentialSourceType::kCredentialManagementAPI,
form_manager->GetCredentialSource());
}
TEST_F(CredentialManagerPasswordFormManagerTest, SaveCredentialAPIEmptyStore) {
std::unique_ptr<CredentialManagerPasswordFormManager> form_manager =
CreateFormManager(form_to_save_);
MockFormSaver& form_saver = MockFormSaver::Get(form_manager.get());
EXPECT_CALL(delegate_, OnProvisionalSaveComplete());
SetNonFederatedAndNotifyFetchCompleted(form_manager->GetFormFetcher(), {});
EXPECT_TRUE(form_manager->IsNewLogin());
EXPECT_TRUE(form_manager->is_submitted());
EXPECT_CALL(form_saver, Save(FormMatches(form_to_save_), _, _));
form_manager->Save();
}
TEST_F(CredentialManagerPasswordFormManagerTest,
SaveCredentialAPINonEmptyStore) {
// Simulate that the password store has crendentials with different
// username/password as a submitted one.
PasswordForm saved_match = form_to_save_;
saved_match.username_value += ASCIIToUTF16("1");
saved_match.password_value += ASCIIToUTF16("1");
std::unique_ptr<CredentialManagerPasswordFormManager> form_manager =
CreateFormManager(form_to_save_);
MockFormSaver& form_saver = MockFormSaver::Get(form_manager.get());
EXPECT_CALL(delegate_, OnProvisionalSaveComplete());
SetNonFederatedAndNotifyFetchCompleted(form_manager->GetFormFetcher(),
{&saved_match});
EXPECT_TRUE(form_manager->IsNewLogin());
EXPECT_TRUE(form_manager->is_submitted());
EXPECT_EQ(form_to_save_.origin, form_manager->GetOrigin());
EXPECT_CALL(form_saver, Save(FormMatches(form_to_save_), _, _));
form_manager->Save();
}
TEST_F(CredentialManagerPasswordFormManagerTest, UpdatePasswordCredentialAPI) {
// Simulate that the submitted credential has the same username but the
// different password from already saved one.
PasswordForm saved_match = form_to_save_;
saved_match.password_value += ASCIIToUTF16("1");
std::unique_ptr<CredentialManagerPasswordFormManager> form_manager =
CreateFormManager(form_to_save_);
MockFormSaver& form_saver = MockFormSaver::Get(form_manager.get());
EXPECT_CALL(delegate_, OnProvisionalSaveComplete());
SetNonFederatedAndNotifyFetchCompleted(form_manager->GetFormFetcher(),
{&saved_match});
EXPECT_FALSE(form_manager->IsNewLogin());
EXPECT_TRUE(form_manager->is_submitted());
EXPECT_CALL(form_saver, Update(FormMatches(form_to_save_), _, _));
form_manager->Save();
}
} // namespace password_manager