blob: be27fc3ebc3b70bc1ae7be15ab98ab5dad2009cc [file] [log] [blame]
// Copyright 2014 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 "chrome/browser/password_manager/chrome_password_manager_client.h"
#include "base/command_line.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/profile_sync_service_mock.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/autofill/content/common/autofill_messages.h"
#include "components/password_manager/content/browser/password_manager_internals_service_factory.h"
#include "components/password_manager/content/common/credential_manager_messages.h"
#include "components/password_manager/content/common/credential_manager_types.h"
#include "components/password_manager/core/browser/log_receiver.h"
#include "components/password_manager/core/browser/password_manager_internals_service.h"
#include "components/password_manager/core/common/password_manager_switches.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_render_process_host.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::BrowserContext;
using content::WebContents;
using testing::Return;
namespace {
const char kTestText[] = "abcd1234";
class MockLogReceiver : public password_manager::LogReceiver {
public:
MOCK_METHOD1(LogSavePasswordProgress, void(const std::string&));
};
class TestChromePasswordManagerClient : public ChromePasswordManagerClient {
public:
explicit TestChromePasswordManagerClient(content::WebContents* web_contents)
: ChromePasswordManagerClient(web_contents, NULL),
is_sync_account_credential_(false) {}
~TestChromePasswordManagerClient() override {}
bool IsSyncAccountCredential(const std::string& username,
const std::string& origin) const override {
return is_sync_account_credential_;
}
void set_is_sync_account_credential(bool is_sync_account_credential) {
is_sync_account_credential_ = is_sync_account_credential;
}
private:
bool is_sync_account_credential_;
DISALLOW_COPY_AND_ASSIGN(TestChromePasswordManagerClient);
};
} // namespace
class ChromePasswordManagerClientTest : public ChromeRenderViewHostTestHarness {
public:
ChromePasswordManagerClientTest();
virtual void SetUp() override;
protected:
ChromePasswordManagerClient* GetClient();
// If the test IPC sink contains an AutofillMsg_SetLoggingState message, then
// copies its argument into |activation_flag| and returns true. Otherwise
// returns false.
bool WasLoggingActivationMessageSent(bool* activation_flag);
password_manager::PasswordManagerInternalsService* service_;
testing::StrictMock<MockLogReceiver> receiver_;
};
ChromePasswordManagerClientTest::ChromePasswordManagerClientTest()
: service_(NULL) {
}
void ChromePasswordManagerClientTest::SetUp() {
ChromeRenderViewHostTestHarness::SetUp();
ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
web_contents(), NULL);
service_ = password_manager::PasswordManagerInternalsServiceFactory::
GetForBrowserContext(profile());
ASSERT_TRUE(service_);
}
ChromePasswordManagerClient* ChromePasswordManagerClientTest::GetClient() {
return ChromePasswordManagerClient::FromWebContents(web_contents());
}
bool ChromePasswordManagerClientTest::WasLoggingActivationMessageSent(
bool* activation_flag) {
const uint32 kMsgID = AutofillMsg_SetLoggingState::ID;
const IPC::Message* message =
process()->sink().GetFirstMessageMatching(kMsgID);
if (!message)
return false;
Tuple<bool> param;
AutofillMsg_SetLoggingState::Read(message, &param);
*activation_flag = get<0>(param);
process()->sink().ClearMessages();
return true;
}
TEST_F(ChromePasswordManagerClientTest, LogSavePasswordProgressNoReceiver) {
ChromePasswordManagerClient* client = GetClient();
EXPECT_CALL(receiver_, LogSavePasswordProgress(kTestText)).Times(0);
// Before attaching the receiver, no text should be passed.
client->LogSavePasswordProgress(kTestText);
EXPECT_FALSE(client->IsLoggingActive());
}
TEST_F(ChromePasswordManagerClientTest, LogSavePasswordProgressAttachReceiver) {
ChromePasswordManagerClient* client = GetClient();
EXPECT_FALSE(client->IsLoggingActive());
// After attaching the logger, text should be passed.
service_->RegisterReceiver(&receiver_);
EXPECT_TRUE(client->IsLoggingActive());
EXPECT_CALL(receiver_, LogSavePasswordProgress(kTestText)).Times(1);
client->LogSavePasswordProgress(kTestText);
service_->UnregisterReceiver(&receiver_);
EXPECT_FALSE(client->IsLoggingActive());
}
TEST_F(ChromePasswordManagerClientTest, LogSavePasswordProgressDetachReceiver) {
ChromePasswordManagerClient* client = GetClient();
service_->RegisterReceiver(&receiver_);
EXPECT_TRUE(client->IsLoggingActive());
service_->UnregisterReceiver(&receiver_);
EXPECT_FALSE(client->IsLoggingActive());
// After detaching the logger, no text should be passed.
EXPECT_CALL(receiver_, LogSavePasswordProgress(kTestText)).Times(0);
client->LogSavePasswordProgress(kTestText);
}
TEST_F(ChromePasswordManagerClientTest, LogSavePasswordProgressNotifyRenderer) {
ChromePasswordManagerClient* client = GetClient();
bool logging_active = false;
// Initially, the logging should be off, so no IPC messages.
EXPECT_FALSE(WasLoggingActivationMessageSent(&logging_active));
service_->RegisterReceiver(&receiver_);
EXPECT_TRUE(client->IsLoggingActive());
EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_active));
EXPECT_TRUE(logging_active);
service_->UnregisterReceiver(&receiver_);
EXPECT_FALSE(client->IsLoggingActive());
EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_active));
EXPECT_FALSE(logging_active);
}
TEST_F(ChromePasswordManagerClientTest, AnswerToPingsAboutLoggingState_Active) {
service_->RegisterReceiver(&receiver_);
process()->sink().ClearMessages();
// Ping the client for logging activity update.
AutofillHostMsg_PasswordAutofillAgentConstructed msg(0);
static_cast<content::WebContentsObserver*>(GetClient())->OnMessageReceived(
msg, web_contents()->GetMainFrame());
bool logging_active = false;
EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_active));
EXPECT_TRUE(logging_active);
service_->UnregisterReceiver(&receiver_);
}
TEST_F(ChromePasswordManagerClientTest,
AnswerToPingsAboutLoggingState_Inactive) {
process()->sink().ClearMessages();
// Ping the client for logging activity update.
AutofillHostMsg_PasswordAutofillAgentConstructed msg(0);
static_cast<content::WebContentsObserver*>(GetClient())->OnMessageReceived(
msg, web_contents()->GetMainFrame());
bool logging_active = true;
EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_active));
EXPECT_FALSE(logging_active);
}
TEST_F(ChromePasswordManagerClientTest,
IsAutomaticPasswordSavingEnabledDefaultBehaviourTest) {
EXPECT_FALSE(GetClient()->IsAutomaticPasswordSavingEnabled());
}
TEST_F(ChromePasswordManagerClientTest,
IsAutomaticPasswordSavingEnabledWhenFlagIsSetTest) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
password_manager::switches::kEnableAutomaticPasswordSaving);
if (chrome::VersionInfo::GetChannel() == chrome::VersionInfo::CHANNEL_UNKNOWN)
EXPECT_TRUE(GetClient()->IsAutomaticPasswordSavingEnabled());
else
EXPECT_FALSE(GetClient()->IsAutomaticPasswordSavingEnabled());
}
TEST_F(ChromePasswordManagerClientTest, LogToAReceiver) {
ChromePasswordManagerClient* client = GetClient();
service_->RegisterReceiver(&receiver_);
EXPECT_TRUE(client->IsLoggingActive());
EXPECT_CALL(receiver_, LogSavePasswordProgress(kTestText)).Times(1);
client->LogSavePasswordProgress(kTestText);
service_->UnregisterReceiver(&receiver_);
EXPECT_FALSE(client->IsLoggingActive());
}
TEST_F(ChromePasswordManagerClientTest,
ShouldAskUserToSubmitURLDefaultBehaviour) {
ChromePasswordManagerClient* client = GetClient();
// TODO(melandory) Since "Ask user to submit URL" functionality is currently
// in development, so the user should not be asked to submit a URL.
EXPECT_FALSE(client->ShouldAskUserToSubmitURL(GURL("https://hostname.com/")));
}
TEST_F(ChromePasswordManagerClientTest, ShouldAskUserToSubmitURLEmptyURL) {
ChromePasswordManagerClient* client = GetClient();
EXPECT_FALSE(client->ShouldAskUserToSubmitURL(GURL::EmptyGURL()));
}
TEST_F(ChromePasswordManagerClientTest, ShouldFilterAutofillResult_Reauth) {
// Make client disallow only reauth requests.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitch(
password_manager::switches::kDisallowAutofillSyncCredentialForReauth);
scoped_ptr<TestChromePasswordManagerClient> client(
new TestChromePasswordManagerClient(web_contents()));
autofill::PasswordForm form;
client->set_is_sync_account_credential(false);
NavigateAndCommit(
GURL("https://accounts.google.com/login?rart=123&continue=blah"));
EXPECT_FALSE(client->ShouldFilterAutofillResult(form));
client->set_is_sync_account_credential(true);
NavigateAndCommit(
GURL("https://accounts.google.com/login?rart=123&continue=blah"));
EXPECT_TRUE(client->ShouldFilterAutofillResult(form));
// This counts as a reauth url, though a valid URL should have a value for
// "rart"
NavigateAndCommit(GURL("https://accounts.google.com/addlogin?rart"));
EXPECT_TRUE(client->ShouldFilterAutofillResult(form));
NavigateAndCommit(GURL("https://accounts.google.com/login?param=123"));
EXPECT_FALSE(client->ShouldFilterAutofillResult(form));
NavigateAndCommit(GURL("https://site.com/login?rart=678"));
EXPECT_FALSE(client->ShouldFilterAutofillResult(form));
}
TEST_F(ChromePasswordManagerClientTest, ShouldFilterAutofillResult) {
// Normally the client should allow any credentials through, even if they
// are the sync credential.
scoped_ptr<TestChromePasswordManagerClient> client(
new TestChromePasswordManagerClient(web_contents()));
autofill::PasswordForm form;
client->set_is_sync_account_credential(true);
NavigateAndCommit(GURL("https://accounts.google.com/Login"));
EXPECT_FALSE(client->ShouldFilterAutofillResult(form));
// Adding disallow switch should cause sync credential to be filtered.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitch(
password_manager::switches::kDisallowAutofillSyncCredential);
client.reset(new TestChromePasswordManagerClient(web_contents()));
client->set_is_sync_account_credential(true);
NavigateAndCommit(GURL("https://accounts.google.com/Login"));
EXPECT_TRUE(client->ShouldFilterAutofillResult(form));
}
TEST_F(ChromePasswordManagerClientTest,
IsPasswordManagerEnabledForCurrentPage) {
ChromePasswordManagerClient* client = GetClient();
NavigateAndCommit(
GURL("https://accounts.google.com/ServiceLogin?continue="
"https://passwords.google.com/settings&rart=123"));
EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
// Password site is inaccesible via HTTP, but because of HSTS the following
// link should still continue to https://passwords.google.com.
NavigateAndCommit(
GURL("https://accounts.google.com/ServiceLogin?continue="
"http://passwords.google.com/settings&rart=123"));
EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
// Specifying default port still passes.
NavigateAndCommit(
GURL("https://accounts.google.com/ServiceLogin?continue="
"https://passwords.google.com:443/settings&rart=123"));
EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
// Encoded URL is considered the same.
NavigateAndCommit(
GURL("https://accounts.google.com/ServiceLogin?continue="
"https://passwords.%67oogle.com/settings&rart=123"));
EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
// Make sure testing sites are disabled as well.
NavigateAndCommit(
GURL("https://accounts.google.com/Login?continue="
"https://passwords-ac-testing.corp.google.com/settings&rart=456"));
EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
// Fully qualified domain name is considered a different hostname by GURL.
// Ideally this would not be the case, but this quirk can be avoided by
// verification on the server. This test is simply documentation of this
// behavior.
NavigateAndCommit(
GURL("https://accounts.google.com/ServiceLogin?continue="
"https://passwords.google.com./settings&rart=123"));
EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
// Not a transactional reauth page.
NavigateAndCommit(
GURL("https://accounts.google.com/ServiceLogin?continue="
"https://passwords.google.com/settings"));
EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
// Should be enabled for other transactional reauth pages.
NavigateAndCommit(
GURL("https://accounts.google.com/ServiceLogin?continue="
"https://mail.google.com&rart=234"));
EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
// Reauth pages are only on accounts.google.com
NavigateAndCommit(
GURL("https://other.site.com/ServiceLogin?continue="
"https://passwords.google.com&rart=234"));
EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
}
TEST_F(ChromePasswordManagerClientTest, IsPasswordSyncEnabled) {
ChromePasswordManagerClient* client = GetClient();
ProfileSyncServiceMock* mock_sync_service =
static_cast<ProfileSyncServiceMock*>(
ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile(), ProfileSyncServiceMock::BuildMockProfileSyncService));
syncer::ModelTypeSet active_types;
active_types.Put(syncer::PASSWORDS);
EXPECT_CALL(*mock_sync_service, HasSyncSetupCompleted())
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_sync_service, SyncActive()).WillRepeatedly(Return(true));
EXPECT_CALL(*mock_sync_service, GetActiveDataTypes())
.WillRepeatedly(Return(active_types));
EXPECT_CALL(*mock_sync_service, IsUsingSecondaryPassphrase())
.WillRepeatedly(Return(false));
// Passwords are syncing and custom passphrase isn't used.
EXPECT_FALSE(
client->IsPasswordSyncEnabled(password_manager::ONLY_CUSTOM_PASSPHRASE));
EXPECT_TRUE(client->IsPasswordSyncEnabled(
password_manager::WITHOUT_CUSTOM_PASSPHRASE));
// Again, using a custom passphrase.
EXPECT_CALL(*mock_sync_service, IsUsingSecondaryPassphrase())
.WillRepeatedly(Return(true));
EXPECT_TRUE(
client->IsPasswordSyncEnabled(password_manager::ONLY_CUSTOM_PASSPHRASE));
EXPECT_FALSE(client->IsPasswordSyncEnabled(
password_manager::WITHOUT_CUSTOM_PASSPHRASE));
// Always return false if we aren't syncing passwords.
active_types.Remove(syncer::PASSWORDS);
active_types.Put(syncer::BOOKMARKS);
EXPECT_CALL(*mock_sync_service, GetActiveDataTypes())
.WillRepeatedly(Return(active_types));
EXPECT_FALSE(
client->IsPasswordSyncEnabled(password_manager::ONLY_CUSTOM_PASSPHRASE));
EXPECT_FALSE(client->IsPasswordSyncEnabled(
password_manager::WITHOUT_CUSTOM_PASSPHRASE));
// Again, without a custom passphrase.
EXPECT_CALL(*mock_sync_service, IsUsingSecondaryPassphrase())
.WillRepeatedly(Return(false));
EXPECT_FALSE(
client->IsPasswordSyncEnabled(password_manager::ONLY_CUSTOM_PASSPHRASE));
EXPECT_FALSE(client->IsPasswordSyncEnabled(
password_manager::WITHOUT_CUSTOM_PASSPHRASE));
}