summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvabr <vabr@chromium.org>2015-10-07 04:05:48 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-07 11:07:05 +0000
commite0e1e99f39349e912612e58249393f2281de4d00 (patch)
treec9ab79671cb5c8bc49f50ca432258e19bd0429d7
parentc8a0c23e2e7b5fc52b7d2bd911016289b8b25ebc (diff)
downloadchromium_src-e0e1e99f39349e912612e58249393f2281de4d00.zip
chromium_src-e0e1e99f39349e912612e58249393f2281de4d00.tar.gz
chromium_src-e0e1e99f39349e912612e58249393f2281de4d00.tar.bz2
Do not involve PasswordManagerDriver in filling HTTP-auth forms; also check realm
The HTTP authentication is not tied to a renderer and happens completely browser-side. This CL simplifies the password manager API for filling these forms to avoid referencing the renderer. It also enhances LoginModelObserver to filter irrelevant credentials by realm. BUG=537823 Review URL: https://codereview.chromium.org/1384283003 Cr-Commit-Position: refs/heads/master@{#352816}
-rw-r--r--chrome/browser/password_manager/password_manager_browsertest.cc68
-rw-r--r--chrome/browser/ui/android/login_prompt_android.cc17
-rw-r--r--chrome/browser/ui/cocoa/login_prompt_cocoa.mm16
-rw-r--r--chrome/browser/ui/login/login_prompt.cc73
-rw-r--r--chrome/browser/ui/login/login_prompt.h36
-rw-r--r--chrome/browser/ui/views/login_prompt_views.cc13
-rw-r--r--chrome/browser/ui/views/login_view.cc15
-rw-r--r--chrome/browser/ui/views/login_view.h13
-rw-r--r--components/autofill/core/common/save_password_progress_logger.cc2
-rw-r--r--components/autofill/core/common/save_password_progress_logger.h1
-rw-r--r--components/components_tests.gyp1
-rw-r--r--components/password_manager.gypi1
-rw-r--r--components/password_manager/core/browser/BUILD.gn2
-rw-r--r--components/password_manager/core/browser/login_model.cc27
-rw-r--r--components/password_manager/core/browser/login_model.h53
-rw-r--r--components/password_manager/core/browser/login_model_unittest.cc66
-rw-r--r--components/password_manager/core/browser/password_form_manager.cc18
-rw-r--r--components/password_manager/core/browser/password_form_manager.h3
-rw-r--r--components/password_manager/core/browser/password_manager.cc84
-rw-r--r--components/password_manager/core/browser/password_manager.h9
20 files changed, 398 insertions, 120 deletions
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 7688c10..1d7ae87 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -35,6 +35,7 @@
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/content/browser/content_password_manager_driver.h"
#include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
+#include "components/password_manager/core/browser/login_model.h"
#include "components/password_manager/core/browser/test_password_store.h"
#include "components/password_manager/core/common/password_manager_switches.h"
#include "components/version_info/version_info.h"
@@ -61,8 +62,19 @@
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/geometry/point.h"
+using testing::_;
+
namespace {
+class MockLoginModelObserver : public password_manager::LoginModelObserver {
+ public:
+ MOCK_METHOD2(OnAutofillDataAvailableInternal,
+ void(const base::string16&, const base::string16&));
+
+ private:
+ void OnLoginModelDestroying() override {}
+};
+
GURL GetFileURL(const char* filename) {
base::FilePath path;
PathService::Get(chrome::DIR_TEST_DATA, &path);
@@ -2485,4 +2497,60 @@ IN_PROC_BROWSER_TEST_F(
EXPECT_EQ("", retyped_password);
}
+// When there are multiple LoginModelObservers (e.g., multiple HTTP auth dialogs
+// as in http://crbug.com/537823), ensure that credentials from PasswordStore
+// distributed to them are filtered by the realm.
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
+ BasicAuthSeparateRealms) {
+ embedded_test_server()->RegisterRequestHandler(
+ base::Bind(&HandleTestAuthRequest));
+
+ // Save credentials for "test realm" in the store.
+ scoped_refptr<password_manager::TestPasswordStore> password_store =
+ static_cast<password_manager::TestPasswordStore*>(
+ PasswordStoreFactory::GetForProfile(
+ browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
+ .get());
+ autofill::PasswordForm creds;
+ creds.scheme = autofill::PasswordForm::SCHEME_BASIC;
+ creds.signon_realm = embedded_test_server()->base_url().spec() + "test realm";
+ creds.password_value = base::ASCIIToUTF16("pw");
+ creds.username_value = base::ASCIIToUTF16("temp");
+ password_store->AddLogin(creds);
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ ASSERT_FALSE(password_store->IsEmpty());
+
+ // In addition to the LoginModelObserver created automatically for the HTTP
+ // auth dialog, also create a mock observer, for a different realm.
+ MockLoginModelObserver mock_login_model_observer;
+ PasswordManager* password_manager =
+ ChromePasswordManagerClient::FromWebContents(WebContents())
+ ->GetPasswordManager();
+ autofill::PasswordForm other_form(creds);
+ other_form.signon_realm = "https://example.com/other realm";
+ password_manager->AddObserverAndDeliverCredentials(&mock_login_model_observer,
+ other_form);
+ // The mock observer should not receive the stored credentials.
+ EXPECT_CALL(mock_login_model_observer, OnAutofillDataAvailableInternal(_, _))
+ .Times(0);
+
+ // Now wait until the navigation to the test server causes a HTTP auth dialog
+ // to appear.
+ content::NavigationController* nav_controller =
+ &WebContents()->GetController();
+ WindowedAuthNeededObserver auth_needed_observer(nav_controller);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), embedded_test_server()->GetURL("/basic_auth"), CURRENT_TAB,
+ ui_test_utils::BROWSER_TEST_NONE);
+ auth_needed_observer.Wait();
+
+ // The auth dialog caused a query to PasswordStore, make sure it was
+ // processed.
+ base::RunLoop run_loop2;
+ run_loop2.RunUntilIdle();
+
+ password_manager->RemoveObserver(&mock_login_model_observer);
+}
+
} // namespace password_manager
diff --git a/chrome/browser/ui/android/login_prompt_android.cc b/chrome/browser/ui/android/login_prompt_android.cc
index 7f478bf..367cb2c 100644
--- a/chrome/browser/ui/android/login_prompt_android.cc
+++ b/chrome/browser/ui/android/login_prompt_android.cc
@@ -10,7 +10,6 @@
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/android/chrome_http_auth_handler.h"
#include "chrome/browser/ui/android/window_android_helper.h"
-#include "chrome/browser/ui/login/login_prompt.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "net/base/auth.h"
@@ -28,8 +27,9 @@ class LoginHandlerAndroid : public LoginHandler {
// LoginHandler methods:
- void OnAutofillDataAvailable(const base::string16& username,
- const base::string16& password) override {
+ void OnAutofillDataAvailableInternal(
+ const base::string16& username,
+ const base::string16& password) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(chrome_http_auth_handler_.get() != NULL);
chrome_http_auth_handler_->OnAutofillDataAvailable(
@@ -37,8 +37,8 @@ class LoginHandlerAndroid : public LoginHandler {
}
void OnLoginModelDestroying() override {}
- void BuildViewForPasswordManager(password_manager::PasswordManager* manager,
- const base::string16& explanation) override {
+ void BuildView(const base::string16& explanation,
+ LoginModelData* login_model_data) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Get pointer to TabAndroid
@@ -55,8 +55,11 @@ class LoginHandlerAndroid : public LoginHandler {
chrome_http_auth_handler_->ShowDialog(
window_helper->GetWindowAndroid()->GetJavaObject().obj());
- // Register to receive a callback to OnAutofillDataAvailable().
- SetModel(manager);
+ if (login_model_data)
+ SetModel(*login_model_data);
+ else
+ ResetModel();
+
NotifyAuthNeeded();
} else {
CancelAuth();
diff --git a/chrome/browser/ui/cocoa/login_prompt_cocoa.mm b/chrome/browser/ui/cocoa/login_prompt_cocoa.mm
index c2a6a8e..daa4f9b 100644
--- a/chrome/browser/ui/cocoa/login_prompt_cocoa.mm
+++ b/chrome/browser/ui/cocoa/login_prompt_cocoa.mm
@@ -41,8 +41,9 @@ class LoginHandlerMac : public LoginHandler,
}
// LoginModelObserver implementation.
- void OnAutofillDataAvailable(const base::string16& username,
- const base::string16& password) override {
+ void OnAutofillDataAvailableInternal(
+ const base::string16& username,
+ const base::string16& password) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
[sheet_controller_ autofillLogin:base::SysUTF16ToNSString(username)
@@ -51,14 +52,17 @@ class LoginHandlerMac : public LoginHandler,
void OnLoginModelDestroying() override {}
// LoginHandler:
- void BuildViewForPasswordManager(password_manager::PasswordManager* manager,
- const base::string16& explanation) override {
+ void BuildView(const base::string16& explanation,
+ LoginModelData* login_model_data) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
sheet_controller_.reset(
[[LoginHandlerSheet alloc] initWithLoginHandler:this]);
- SetModel(manager);
+ if (login_model_data)
+ SetModel(*login_model_data);
+ else
+ ResetModel();
[sheet_controller_ setExplanation:base::SysUTF16ToNSString(explanation)];
@@ -88,7 +92,7 @@ class LoginHandlerMac : public LoginHandler,
// Overridden from ConstrainedWindowMacDelegate:
void OnConstrainedWindowClosed(ConstrainedWindowMac* window) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- SetModel(NULL);
+ ResetModel();
ReleaseSoon();
constrained_window_.reset();
diff --git a/chrome/browser/ui/login/login_prompt.cc b/chrome/browser/ui/login/login_prompt.cc
index 8051d83..6ee6149 100644
--- a/chrome/browser/ui/login/login_prompt.cc
+++ b/chrome/browser/ui/login/login_prompt.cc
@@ -21,7 +21,6 @@
#include "chrome/browser/ui/login/login_interstitial_delegate.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
-#include "components/password_manager/content/browser/content_password_manager_driver.h"
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/url_formatter/elide_url.h"
@@ -90,6 +89,13 @@ std::string GetSignonRealm(const GURL& url,
// ----------------------------------------------------------------------------
// LoginHandler
+LoginHandler::LoginModelData::LoginModelData(
+ password_manager::LoginModel* login_model,
+ const autofill::PasswordForm& observed_form)
+ : model(login_model), form(observed_form) {
+ DCHECK(model);
+}
+
LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
net::URLRequest* request)
: handled_auth_(false),
@@ -100,8 +106,8 @@ LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
password_manager_(NULL),
login_model_(NULL) {
// This constructor is called on the I/O thread, so we cannot load the nib
- // here. BuildViewForPasswordManager() will be invoked on the UI thread
- // later, so wait with loading the nib until then.
+ // here. BuildView() will be invoked on the UI thread later, so wait with
+ // loading the nib until then.
DCHECK(request_) << "LoginHandler constructed with NULL request";
DCHECK(auth_info_.get()) << "LoginHandler constructed with NULL auth info";
@@ -145,12 +151,10 @@ WebContents* LoginHandler::GetWebContentsForLogin() const {
return WebContents::FromRenderFrameHost(rfh);
}
-password_manager::ContentPasswordManagerDriver*
-LoginHandler::GetPasswordManagerDriverForLogin() {
- content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
- render_process_host_id_, render_frame_id_);
- return password_manager::ContentPasswordManagerDriver::GetForRenderFrameHost(
- rfh);
+password_manager::PasswordManager* LoginHandler::GetPasswordManagerForLogin() {
+ ChromePasswordManagerClient* client =
+ ChromePasswordManagerClient::FromWebContents(GetWebContentsForLogin());
+ return client ? client->GetPasswordManager() : nullptr;
}
void LoginHandler::SetAuth(const base::string16& username,
@@ -274,15 +278,20 @@ bool LoginHandler::WasAuthHandled() const {
}
LoginHandler::~LoginHandler() {
- SetModel(NULL);
+ ResetModel();
}
-void LoginHandler::SetModel(password_manager::LoginModel* model) {
+void LoginHandler::SetModel(LoginModelData model_data) {
if (login_model_)
login_model_->RemoveObserver(this);
- login_model_ = model;
+ login_model_ = model_data.model;
+ login_model_->AddObserverAndDeliverCredentials(this, model_data.form);
+}
+
+void LoginHandler::ResetModel() {
if (login_model_)
- login_model_->AddObserver(this);
+ login_model_->RemoveObserver(this);
+ login_model_ = nullptr;
}
void LoginHandler::NotifyAuthNeeded() {
@@ -429,13 +438,11 @@ void LoginHandler::CloseContentsDeferred() {
interstitial_page->Proceed();
}
-// Helper to create a PasswordForm and stuff it into a vector as input
-// for PasswordManager::PasswordFormsParsed, the hook into PasswordManager.
-void MakeInputForPasswordManager(
- const GURL& request_url,
- net::AuthChallengeInfo* auth_info,
- LoginHandler* handler,
- std::vector<PasswordForm>* password_manager_input) {
+// Helper to create a PasswordForm for PasswordManager to start looking for
+// saved credentials.
+PasswordForm MakeInputForPasswordManager(const GURL& request_url,
+ net::AuthChallengeInfo* auth_info,
+ LoginHandler* handler) {
PasswordForm dialog_form;
if (base::LowerCaseEqualsASCII(auth_info->scheme, "basic")) {
dialog_form.scheme = PasswordForm::SCHEME_BASIC;
@@ -459,9 +466,9 @@ void MakeInputForPasswordManager(
dialog_form.origin = GURL(request_url.scheme() + "://" + host_and_port);
}
dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, *auth_info);
- password_manager_input->push_back(dialog_form);
// Set the password form for the handler (by copy).
handler->SetPasswordForm(dialog_form);
+ return dialog_form;
}
void ShowLoginPrompt(const GURL& request_url,
@@ -478,9 +485,6 @@ void ShowLoginPrompt(const GURL& request_url,
return;
}
- password_manager::ContentPasswordManagerDriver* driver =
- handler->GetPasswordManagerDriverForLogin();
-
// The realm is controlled by the remote server, so there is no reason
// to believe it is of a reasonable length.
base::string16 elided_realm;
@@ -504,12 +508,15 @@ void ShowLoginPrompt(const GURL& request_url,
: l10n_util::GetStringFUTF16(IDS_LOGIN_DIALOG_DESCRIPTION, authority,
elided_realm);
- if (!driver) {
+ password_manager::PasswordManager* password_manager =
+ handler->GetPasswordManagerForLogin();
+
+ if (!password_manager) {
#if defined(ENABLE_EXTENSIONS)
// A WebContents in a <webview> (a GuestView type) does not have a password
// manager, but still needs to be able to show login prompts.
if (guest_view::GuestViewBase::FromWebContents(parent_contents)) {
- handler->BuildViewForPasswordManager(nullptr, explanation);
+ handler->BuildView(explanation, nullptr);
return;
}
#endif
@@ -517,8 +524,6 @@ void ShowLoginPrompt(const GURL& request_url,
return;
}
- password_manager::PasswordManager* password_manager =
- driver->GetPasswordManager();
if (password_manager && password_manager->client()->IsLoggingActive()) {
password_manager::BrowserSavePasswordProgressLogger logger(
password_manager->client());
@@ -526,14 +531,12 @@ void ShowLoginPrompt(const GURL& request_url,
autofill::SavePasswordProgressLogger::STRING_SHOW_LOGIN_PROMPT_METHOD);
}
- // Tell the password manager to look for saved passwords.
- std::vector<PasswordForm> v;
- MakeInputForPasswordManager(request_url, auth_info, handler, &v);
- driver->OnPasswordFormsParsedNoRenderCheck(v);
- handler->SetPasswordManager(driver->GetPasswordManager());
+ handler->SetPasswordManager(password_manager);
- handler->BuildViewForPasswordManager(driver->GetPasswordManager(),
- explanation);
+ PasswordForm observed_form(
+ MakeInputForPasswordManager(request_url, auth_info, handler));
+ LoginHandler::LoginModelData model_data(password_manager, observed_form);
+ handler->BuildView(explanation, &model_data);
}
// This callback is run on the UI thread and creates a constrained window with
diff --git a/chrome/browser/ui/login/login_prompt.h b/chrome/browser/ui/login/login_prompt.h
index 9f58dcd..de5c12c 100644
--- a/chrome/browser/ui/login/login_prompt.h
+++ b/chrome/browser/ui/login/login_prompt.h
@@ -38,6 +38,21 @@ class LoginHandler : public content::ResourceDispatcherHostLoginDelegate,
public password_manager::LoginModelObserver,
public content::NotificationObserver {
public:
+ // The purpose of this struct is to enforce that BuildView receives either
+ // both the login model and the observed form, or none. That is a bit spoiled
+ // by the fact that the model is a pointer to LoginModel, as opposed to a
+ // reference. Having it as a reference would go against the style guide, which
+ // forbids non-const references in arguments, presumably also inside passed
+ // structs, because the guide's rationale still applies. Therefore at least
+ // the constructor DCHECKs that |login_model| is not null.
+ struct LoginModelData {
+ LoginModelData(password_manager::LoginModel* login_model,
+ const autofill::PasswordForm& observed_form);
+
+ password_manager::LoginModel* const model;
+ const autofill::PasswordForm& form;
+ };
+
LoginHandler(net::AuthChallengeInfo* auth_info, net::URLRequest* request);
// Builds the platform specific LoginHandler. Used from within
@@ -48,10 +63,11 @@ class LoginHandler : public content::ResourceDispatcherHostLoginDelegate,
// ResourceDispatcherHostLoginDelegate implementation:
void OnRequestCancelled() override;
- // Initializes the underlying platform specific view.
- virtual void BuildViewForPasswordManager(
- password_manager::PasswordManager* manager,
- const base::string16& explanation) = 0;
+ // Implement this to initialize the underlying platform specific view. If
+ // |login_model_data| is not null, the contained LoginModel and PasswordForm
+ // can be used to register the view.
+ virtual void BuildView(const base::string16& explanation,
+ LoginModelData* login_model_data) = 0;
// Sets information about the authentication type (|form|) and the
// |password_manager| for this profile.
@@ -61,9 +77,8 @@ class LoginHandler : public content::ResourceDispatcherHostLoginDelegate,
// Returns the WebContents that needs authentication.
content::WebContents* GetWebContentsForLogin() const;
- // Returns the PasswordManager for the render frame that needs login.
- password_manager::ContentPasswordManagerDriver*
- GetPasswordManagerDriverForLogin();
+ // Returns the PasswordManager for the web contents that needs login.
+ password_manager::PasswordManager* GetPasswordManagerForLogin();
// Resend the request with authentication credentials.
// This function can be called from either thread.
@@ -90,7 +105,12 @@ class LoginHandler : public content::ResourceDispatcherHostLoginDelegate,
protected:
~LoginHandler() override;
- void SetModel(password_manager::LoginModel* model);
+ // Sets |model_data.model| as |login_model_| and registers |this| as an
+ // observer for |model_data.form|-related events.
+ void SetModel(LoginModelData model_data);
+
+ // Clear |login_model_| and remove |this| as an observer.
+ void ResetModel();
// Notify observers that authentication is needed.
void NotifyAuthNeeded();
diff --git a/chrome/browser/ui/views/login_prompt_views.cc b/chrome/browser/ui/views/login_prompt_views.cc
index cde26d5..02402fd 100644
--- a/chrome/browser/ui/views/login_prompt_views.cc
+++ b/chrome/browser/ui/views/login_prompt_views.cc
@@ -34,8 +34,9 @@ class LoginHandlerViews : public LoginHandler, public views::DialogDelegate {
}
// LoginModelObserver:
- void OnAutofillDataAvailable(const base::string16& username,
- const base::string16& password) override {
+ void OnAutofillDataAvailableInternal(
+ const base::string16& username,
+ const base::string16& password) override {
// Nothing to do here since LoginView takes care of autofill for win.
}
void OnLoginModelDestroying() override {}
@@ -67,7 +68,7 @@ class LoginHandlerViews : public LoginHandler, public views::DialogDelegate {
// The widget is going to delete itself; clear our pointer.
dialog_ = NULL;
- SetModel(NULL);
+ ResetModel();
ReleaseSoon();
}
@@ -97,8 +98,8 @@ class LoginHandlerViews : public LoginHandler, public views::DialogDelegate {
}
// LoginHandler:
- void BuildViewForPasswordManager(password_manager::PasswordManager* manager,
- const base::string16& explanation) override {
+ void BuildView(const base::string16& explanation,
+ LoginModelData* login_model_data) override {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Create a new LoginView and set the model for it. The model (password
@@ -106,7 +107,7 @@ class LoginHandlerViews : public LoginHandler, public views::DialogDelegate {
// browser window, so the view may be destroyed after the password
// manager. The view listens for model destruction and unobserves
// accordingly.
- login_view_ = new LoginView(explanation, manager);
+ login_view_ = new LoginView(explanation, login_model_data);
// Scary thread safety note: This can potentially be called *after* SetAuth
// or CancelAuth (say, if the request was cancelled before the UI thread got
diff --git a/chrome/browser/ui/views/login_view.cc b/chrome/browser/ui/views/login_view.cc
index 50f697a..78768e7 100644
--- a/chrome/browser/ui/views/login_view.cc
+++ b/chrome/browser/ui/views/login_view.cc
@@ -21,7 +21,7 @@ using views::GridLayout;
// LoginView, public:
LoginView::LoginView(const base::string16& explanation,
- LoginModel* model)
+ LoginHandler::LoginModelData* login_model_data)
: username_field_(new views::Textfield()),
password_field_(new views::Textfield()),
username_label_(new views::Label(
@@ -29,7 +29,7 @@ LoginView::LoginView(const base::string16& explanation,
password_label_(new views::Label(
l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_PASSWORD_FIELD))),
message_label_(new views::Label(explanation)),
- login_model_(model) {
+ login_model_(login_model_data ? login_model_data->model : nullptr) {
password_field_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
message_label_->SetMultiLine(true);
message_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
@@ -75,8 +75,10 @@ LoginView::LoginView(const base::string16& explanation,
layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
- if (login_model_)
- login_model_->AddObserver(this);
+ if (login_model_data) {
+ login_model_->AddObserverAndDeliverCredentials(this,
+ login_model_data->form);
+ }
}
LoginView::~LoginView() {
@@ -99,8 +101,9 @@ views::View* LoginView::GetInitiallyFocusedView() {
///////////////////////////////////////////////////////////////////////////////
// LoginView, views::View, password_manager::LoginModelObserver overrides:
-void LoginView::OnAutofillDataAvailable(const base::string16& username,
- const base::string16& password) {
+void LoginView::OnAutofillDataAvailableInternal(
+ const base::string16& username,
+ const base::string16& password) {
if (username_field_->text().empty()) {
username_field_->SetText(username);
password_field_->SetText(password);
diff --git a/chrome/browser/ui/views/login_view.h b/chrome/browser/ui/views/login_view.h
index eab866d..b2cb06f 100644
--- a/chrome/browser/ui/views/login_view.h
+++ b/chrome/browser/ui/views/login_view.h
@@ -6,6 +6,8 @@
#define CHROME_BROWSER_UI_VIEWS_LOGIN_VIEW_H_
#include "base/compiler_specific.h"
+#include "chrome/browser/ui/login/login_prompt.h"
+#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/login_model.h"
#include "ui/views/view.h"
@@ -19,10 +21,11 @@ class Textfield;
class LoginView : public views::View,
public password_manager::LoginModelObserver {
public:
- // |model| is observed for the entire lifetime of the LoginView.
- // Therefore |model| should not be destroyed before the LoginView object.
+ // |login_model_data->model| is observed for the entire lifetime of the
+ // LoginView. Therefore |login_model_data->model| should not be destroyed
+ // before the LoginView object. |login_model_data| may be null.
LoginView(const base::string16& explanation,
- password_manager::LoginModel* model);
+ LoginHandler::LoginModelData* login_model_data);
~LoginView() override;
// Access the data in the username/password text fields.
@@ -30,8 +33,8 @@ class LoginView : public views::View,
const base::string16& GetPassword() const;
// password_manager::LoginModelObserver:
- void OnAutofillDataAvailable(const base::string16& username,
- const base::string16& password) override;
+ void OnAutofillDataAvailableInternal(const base::string16& username,
+ const base::string16& password) override;
void OnLoginModelDestroying() override;
// Used by LoginHandlerWin to set the initial focus.
diff --git a/components/autofill/core/common/save_password_progress_logger.cc b/components/autofill/core/common/save_password_progress_logger.cc
index 58e1c2d..57ffd1d 100644
--- a/components/autofill/core/common/save_password_progress_logger.cc
+++ b/components/autofill/core/common/save_password_progress_logger.cc
@@ -299,6 +299,8 @@ std::string SavePasswordProgressLogger::GetStringFromID(
return "Show password prompt";
case SavePasswordProgressLogger::STRING_PASSWORDMANAGER_AUTOFILL:
return "PasswordManager::Autofill";
+ case SavePasswordProgressLogger::STRING_PASSWORDMANAGER_AUTOFILLHTTPAUTH:
+ return "PasswordManager::AutofillHttpAuth";
case SavePasswordProgressLogger::STRING_WAIT_FOR_USERNAME:
return "wait_for_username";
case SavePasswordProgressLogger::STRING_LOGINMODELOBSERVER_PRESENT:
diff --git a/components/autofill/core/common/save_password_progress_logger.h b/components/autofill/core/common/save_password_progress_logger.h
index ff43e90..ccfa6cc 100644
--- a/components/autofill/core/common/save_password_progress_logger.h
+++ b/components/autofill/core/common/save_password_progress_logger.h
@@ -102,6 +102,7 @@ class SavePasswordProgressLogger {
STRING_ONLY_VISIBLE,
STRING_SHOW_PASSWORD_PROMPT,
STRING_PASSWORDMANAGER_AUTOFILL,
+ STRING_PASSWORDMANAGER_AUTOFILLHTTPAUTH,
STRING_WAIT_FOR_USERNAME,
STRING_LOGINMODELOBSERVER_PRESENT,
STRING_WAS_LAST_NAVIGATION_HTTP_ERROR_METHOD,
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 7bfe6af..4f43573 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -420,6 +420,7 @@
'password_manager/core/browser/import/csv_reader_unittest.cc',
'password_manager/core/browser/log_router_unittest.cc',
'password_manager/core/browser/login_database_unittest.cc',
+ 'password_manager/core/browser/login_model_unittest.cc',
'password_manager/core/browser/mock_affiliated_match_helper.cc',
'password_manager/core/browser/mock_affiliated_match_helper.h',
'password_manager/core/browser/password_autofill_manager_unittest.cc',
diff --git a/components/password_manager.gypi b/components/password_manager.gypi
index a541d0a..f73a9e8 100644
--- a/components/password_manager.gypi
+++ b/components/password_manager.gypi
@@ -70,6 +70,7 @@
'password_manager/core/browser/login_database_mac.cc',
'password_manager/core/browser/login_database_posix.cc',
'password_manager/core/browser/login_database_win.cc',
+ 'password_manager/core/browser/login_model.cc',
'password_manager/core/browser/login_model.h',
'password_manager/core/browser/password_autofill_manager.cc',
'password_manager/core/browser/password_autofill_manager.h',
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 5668e64..764d291 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -51,6 +51,7 @@ source_set("browser") {
"login_database_mac.cc",
"login_database_posix.cc",
"login_database_win.cc",
+ "login_model.cc",
"login_model.h",
"password_autofill_manager.cc",
"password_autofill_manager.h",
@@ -181,6 +182,7 @@ source_set("unit_tests") {
"import/csv_reader_unittest.cc",
"log_router_unittest.cc",
"login_database_unittest.cc",
+ "login_model_unittest.cc",
"mock_affiliated_match_helper.cc",
"mock_affiliated_match_helper.h",
"password_autofill_manager_unittest.cc",
diff --git a/components/password_manager/core/browser/login_model.cc b/components/password_manager/core/browser/login_model.cc
new file mode 100644
index 0000000..a2bf712
--- /dev/null
+++ b/components/password_manager/core/browser/login_model.cc
@@ -0,0 +1,27 @@
+// 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/login_model.h"
+
+namespace password_manager {
+
+LoginModelObserver::LoginModelObserver() {}
+
+LoginModelObserver::~LoginModelObserver() {}
+
+void LoginModelObserver::OnAutofillDataAvailable(
+ const autofill::PasswordForm& credentials) {
+ DCHECK(!signon_realm_.empty())
+ << "The model did not call set_signon_realm properly.";
+
+ if (credentials.signon_realm == signon_realm_) {
+ OnAutofillDataAvailableInternal(credentials.username_value,
+ credentials.password_value);
+ }
+}
+
+LoginModel::LoginModel() {}
+LoginModel::~LoginModel() {}
+
+} // namespace password_manager
diff --git a/components/password_manager/core/browser/login_model.h b/components/password_manager/core/browser/login_model.h
index 0401926..612d45a 100644
--- a/components/password_manager/core/browser/login_model.h
+++ b/components/password_manager/core/browser/login_model.h
@@ -5,34 +5,67 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LOGIN_MODEL_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LOGIN_MODEL_H_
+#include <string>
+
+#include "base/macros.h"
#include "base/strings/string16.h"
+#include "components/autofill/core/common/password_form.h"
namespace password_manager {
-// Simple Model & Observer interfaces for a LoginView to facilitate exchanging
-// information.
+// A base class for the http-auth UI to communicate with the provider of stored
+// credentials.
class LoginModelObserver {
public:
- // Called by the model when a username,password pair has been identified
- // as a match for the pending login prompt.
- virtual void OnAutofillDataAvailable(const base::string16& username,
- const base::string16& password) = 0;
+ LoginModelObserver();
+
+ // Called by the model when |credentials| has been identified as a match for
+ // the pending login prompt. Checks that the realm matches, and passes
+ // |credentials| to OnAutofillDataAvailableInternal.
+ void OnAutofillDataAvailable(const autofill::PasswordForm& credentials);
virtual void OnLoginModelDestroying() = 0;
+ // To be called by the model during AddObserverAndDeliverCredentials.
+ void set_signon_realm(const std::string& signon_realm) {
+ signon_realm_ = signon_realm;
+ }
+
protected:
- virtual ~LoginModelObserver() {}
+ virtual ~LoginModelObserver();
+
+ virtual void OnAutofillDataAvailableInternal(
+ const base::string16& username,
+ const base::string16& password) = 0;
+
+ private:
+ // Signon realm for which this observer wishes to receive credentials.
+ std::string signon_realm_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoginModelObserver);
};
+// Corresponding Model interface to be inherited by the provider of stored
+// passwords.
class LoginModel {
public:
- // Add an observer interested in the data from the model.
- virtual void AddObserver(LoginModelObserver* observer) = 0;
+ LoginModel();
+
+ // Add an observer interested in the data from the model. Also, requests that
+ // the model sends a callback to |observer| with stored credentials for
+ // |observed_form|.
+ virtual void AddObserverAndDeliverCredentials(
+ LoginModelObserver* observer,
+ const autofill::PasswordForm& observed_form) = 0;
+
// Remove an observer from the model.
virtual void RemoveObserver(LoginModelObserver* observer) = 0;
protected:
- virtual ~LoginModel() {}
+ virtual ~LoginModel();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LoginModel);
};
} // namespace password_manager
diff --git a/components/password_manager/core/browser/login_model_unittest.cc b/components/password_manager/core/browser/login_model_unittest.cc
new file mode 100644
index 0000000..b9915a0
--- /dev/null
+++ b/components/password_manager/core/browser/login_model_unittest.cc
@@ -0,0 +1,66 @@
+// 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/login_model.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using autofill::PasswordForm;
+using base::ASCIIToUTF16;
+using testing::_;
+
+namespace password_manager {
+
+namespace {
+
+// Mocking the class under test is fine, because the mocked methods and the
+// tested methods are disjoint. In particular, the virtual methods of
+// LoginModelObserver are all pure, so what can be tested are only non-virtual
+// (and hence non-mockable) methods.
+class MockLoginModelObserver : public LoginModelObserver {
+ public:
+ MOCK_METHOD2(OnAutofillDataAvailableInternal,
+ void(const base::string16& username,
+ const base::string16& password));
+
+ private:
+ void OnLoginModelDestroying() override {}
+};
+
+PasswordForm CreateFormWithRealm(const std::string& realm) {
+ PasswordForm form;
+ form.origin = GURL("http://accounts.google.com/a/LoginAuth");
+ form.action = GURL("http://accounts.google.com/a/Login");
+ form.username_element = ASCIIToUTF16("Email");
+ form.password_element = ASCIIToUTF16("Passwd");
+ form.username_value = ASCIIToUTF16("expected username");
+ form.password_value = ASCIIToUTF16("expected password");
+ form.signon_realm = realm;
+ return form;
+}
+
+} // namespace
+
+TEST(LoginModelObserverTest, FilterNotMatchingRealm) {
+ MockLoginModelObserver observer;
+ observer.set_signon_realm("http://example.com/");
+ EXPECT_CALL(observer, OnAutofillDataAvailableInternal(_, _)).Times(0);
+ PasswordForm form = CreateFormWithRealm("http://test.org/");
+ observer.OnAutofillDataAvailable(form);
+}
+
+TEST(LoginModelObserverTest, NotFilterMatchingRealm) {
+ MockLoginModelObserver observer;
+ observer.set_signon_realm("http://example.com/");
+ EXPECT_CALL(observer, OnAutofillDataAvailableInternal(
+ ASCIIToUTF16("expected username"),
+ ASCIIToUTF16("expected password")));
+ PasswordForm form = CreateFormWithRealm("http://example.com/");
+ observer.OnAutofillDataAvailable(form);
+}
+
+} // namespace password_manager
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 6ba4ba2..1d5dd83 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -113,7 +113,10 @@ PasswordFormManager::PasswordFormManager(
user_action_(kUserActionNone),
submit_result_(kSubmitResultNotSubmitted),
form_type_(kFormTypeUnspecified) {
- drivers_.push_back(driver);
+ DCHECK_EQ(observed_form.scheme == PasswordForm::SCHEME_HTML,
+ driver != nullptr);
+ if (driver)
+ drivers_.push_back(driver);
}
PasswordFormManager::~PasswordFormManager() {
@@ -489,6 +492,7 @@ void PasswordFormManager::OnRequestDone(
void PasswordFormManager::ProcessFrame(
const base::WeakPtr<PasswordManagerDriver>& driver) {
+ DCHECK_EQ(PasswordForm::SCHEME_HTML, observed_form_.scheme);
if (state_ == POST_MATCHING_PHASE)
ProcessFrameInternal(driver);
@@ -504,6 +508,7 @@ void PasswordFormManager::ProcessFrame(
void PasswordFormManager::ProcessFrameInternal(
const base::WeakPtr<PasswordManagerDriver>& driver) {
+ DCHECK_EQ(PasswordForm::SCHEME_HTML, observed_form_.scheme);
if (!driver || manager_action_ == kManagerActionBlacklisted)
return;
@@ -536,6 +541,15 @@ void PasswordFormManager::ProcessFrameInternal(
*preferred_match_, wait_for_username);
}
+void PasswordFormManager::ProcessLoginPrompt() {
+ DCHECK_NE(PasswordForm::SCHEME_HTML, observed_form_.scheme);
+ if (!preferred_match_)
+ return;
+
+ manager_action_ = kManagerActionAutofilled;
+ password_manager_->AutofillHttpAuth(best_matches_, *preferred_match_);
+}
+
void PasswordFormManager::OnGetPasswordStoreResults(
ScopedVector<PasswordForm> results) {
DCHECK_EQ(state_, MATCHING_PHASE);
@@ -567,6 +581,8 @@ void PasswordFormManager::OnGetPasswordStoreResults(
if (manager_action_ != kManagerActionBlacklisted) {
for (auto const& driver : drivers_)
ProcessFrameInternal(driver);
+ if (observed_form_.scheme != PasswordForm::SCHEME_HTML)
+ ProcessLoginPrompt();
}
}
diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h
index 1740d89..d37bcd8 100644
--- a/components/password_manager/core/browser/password_form_manager.h
+++ b/components/password_manager/core/browser/password_form_manager.h
@@ -287,6 +287,9 @@ class PasswordFormManager : public PasswordStoreConsumer {
// (fill data, whether to allow password generation, etc.).
void ProcessFrameInternal(const base::WeakPtr<PasswordManagerDriver>& driver);
+ // Trigger filling of HTTP auth dialog and update |manager_action_|.
+ void ProcessLoginPrompt();
+
// Determines if we need to autofill given the results of the query.
// Takes ownership of the elements in |result|.
void OnRequestDone(ScopedVector<autofill::PasswordForm> result);
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index d30b10a..3529e88 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -4,6 +4,8 @@
#include "components/password_manager/core/browser/password_manager.h"
+#include <map>
+
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
@@ -395,8 +397,16 @@ void PasswordManager::AddSubmissionCallback(
submission_callbacks_.push_back(callback);
}
-void PasswordManager::AddObserver(LoginModelObserver* observer) {
+void PasswordManager::AddObserverAndDeliverCredentials(
+ LoginModelObserver* observer,
+ const PasswordForm& observed_form) {
observers_.AddObserver(observer);
+
+ observer->set_signon_realm(observed_form.signon_realm);
+
+ std::vector<PasswordForm> observed_forms;
+ observed_forms.push_back(observed_form);
+ OnPasswordFormsParsed(nullptr, observed_forms);
}
void PasswordManager::RemoveObserver(LoginModelObserver* observer) {
@@ -470,7 +480,8 @@ void PasswordManager::CreatePendingLoginManagers(
continue;
}
old_manager_found = true;
- old_manager->ProcessFrame(driver->AsWeakPtr());
+ if (driver)
+ old_manager->ProcessFrame(driver->AsWeakPtr());
break;
}
if (old_manager_found)
@@ -492,7 +503,9 @@ void PasswordManager::CreatePendingLoginManagers(
logger->LogFormSignatures(Logger::STRING_ADDING_SIGNATURE, *iter);
bool ssl_valid = iter->origin.SchemeIsCryptographic();
PasswordFormManager* manager = new PasswordFormManager(
- this, client_, driver->AsWeakPtr(), *iter, ssl_valid);
+ this, client_,
+ (driver ? driver->AsWeakPtr() : base::WeakPtr<PasswordManagerDriver>()),
+ *iter, ssl_valid);
pending_login_managers_.push_back(manager);
PasswordStore::AuthorizationPromptPolicy prompt_policy =
@@ -703,45 +716,46 @@ void PasswordManager::Autofill(password_manager::PasswordManagerDriver* driver,
const PasswordFormMap& best_matches,
const PasswordForm& preferred_match,
bool wait_for_username) const {
+ DCHECK_EQ(PasswordForm::SCHEME_HTML, preferred_match.scheme);
+
scoped_ptr<BrowserSavePasswordProgressLogger> logger;
if (client_->IsLoggingActive()) {
logger.reset(new BrowserSavePasswordProgressLogger(client_));
logger->LogMessage(Logger::STRING_PASSWORDMANAGER_AUTOFILL);
}
- switch (form_for_autofill.scheme) {
- case PasswordForm::SCHEME_HTML: {
- // Note the check above is required because the observers_ for a non-HTML
- // schemed password form may have been freed, so we need to distinguish.
- autofill::PasswordFormFillData fill_data;
- InitPasswordFormFillData(form_for_autofill,
- best_matches,
- &preferred_match,
- wait_for_username,
- OtherPossibleUsernamesEnabled(),
- &fill_data);
- if (logger)
- logger->LogBoolean(Logger::STRING_WAIT_FOR_USERNAME, wait_for_username);
- UMA_HISTOGRAM_BOOLEAN(
- "PasswordManager.FillSuggestionsIncludeAndroidAppCredentials",
- ContainsAndroidCredentials(fill_data));
- metrics_util::LogFilledCredentialIsFromAndroidApp(
- PreferredRealmIsFromAndroid(fill_data));
- driver->FillPasswordForm(fill_data);
- break;
- }
- default:
- if (logger) {
- logger->LogBoolean(Logger::STRING_LOGINMODELOBSERVER_PRESENT,
- observers_.might_have_observers());
- }
- FOR_EACH_OBSERVER(
- LoginModelObserver,
- observers_,
- OnAutofillDataAvailable(preferred_match.username_value,
- preferred_match.password_value));
- break;
+
+ autofill::PasswordFormFillData fill_data;
+ InitPasswordFormFillData(form_for_autofill, best_matches, &preferred_match,
+ wait_for_username, OtherPossibleUsernamesEnabled(),
+ &fill_data);
+ if (logger)
+ logger->LogBoolean(Logger::STRING_WAIT_FOR_USERNAME, wait_for_username);
+ UMA_HISTOGRAM_BOOLEAN(
+ "PasswordManager.FillSuggestionsIncludeAndroidAppCredentials",
+ ContainsAndroidCredentials(fill_data));
+ metrics_util::LogFilledCredentialIsFromAndroidApp(
+ PreferredRealmIsFromAndroid(fill_data));
+ driver->FillPasswordForm(fill_data);
+
+ client_->PasswordWasAutofilled(best_matches);
+}
+
+void PasswordManager::AutofillHttpAuth(
+ const PasswordFormMap& best_matches,
+ const PasswordForm& preferred_match) const {
+ DCHECK_NE(PasswordForm::SCHEME_HTML, preferred_match.scheme);
+
+ scoped_ptr<BrowserSavePasswordProgressLogger> logger;
+ if (client_->IsLoggingActive()) {
+ logger.reset(new BrowserSavePasswordProgressLogger(client_));
+ logger->LogMessage(Logger::STRING_PASSWORDMANAGER_AUTOFILLHTTPAUTH);
+ logger->LogBoolean(Logger::STRING_LOGINMODELOBSERVER_PRESENT,
+ observers_.might_have_observers());
}
+ FOR_EACH_OBSERVER(LoginModelObserver, observers_,
+ OnAutofillDataAvailable(preferred_match));
+
client_->PasswordWasAutofilled(best_matches);
}
diff --git a/components/password_manager/core/browser/password_manager.h b/components/password_manager/core/browser/password_manager.h
index 16bde65..410bf33 100644
--- a/components/password_manager/core/browser/password_manager.h
+++ b/components/password_manager/core/browser/password_manager.h
@@ -67,8 +67,15 @@ class PasswordManager : public LoginModel {
const autofill::PasswordForm& preferred_match,
bool wait_for_username) const;
+ // Called by a PasswordFormManager when it decides a HTTP auth dialog can be
+ // autofilled.
+ void AutofillHttpAuth(const autofill::PasswordFormMap& best_matches,
+ const autofill::PasswordForm& preferred_match) const;
+
// LoginModel implementation.
- void AddObserver(LoginModelObserver* observer) override;
+ void AddObserverAndDeliverCredentials(
+ LoginModelObserver* observer,
+ const autofill::PasswordForm& observed_form) override;
void RemoveObserver(LoginModelObserver* observer) override;
void GenerationAvailableForForm(const autofill::PasswordForm& form);