blob: 27f4cd33be5efbd999112457c64b274342ae46ce [file] [log] [blame]
// Copyright (c) 2012 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.
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_MANAGER_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_MANAGER_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "build/build_config.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
namespace password_manager {
class PasswordManager;
class PasswordManagerClient;
// Per-password-form-{on-page, dialog} class responsible for interactions
// between a given form, the per-tab PasswordManager, and the PasswordStore.
class PasswordFormManager : public PasswordStoreConsumer {
public:
// |password_manager| owns this object
// |form_on_page| is the form that may be submitted and could need login data.
// |ssl_valid| represents the security of the page containing observed_form,
// used to filter login results from database.
PasswordFormManager(PasswordManager* password_manager,
PasswordManagerClient* client,
const base::WeakPtr<PasswordManagerDriver>& driver,
const autofill::PasswordForm& observed_form,
bool ssl_valid);
~PasswordFormManager() override;
// Flags describing the result of comparing two forms as performed by
// DoesMatch. Individual flags are only relevant for HTML forms, but
// RESULT_COMPLETE_MATCH will also be returned to indicate non-HTML forms
// completely matching.
// The ordering of these flags is important. Larger matches are more
// preferred than lower matches. That is, since RESULT_HTML_ATTRIBUTES_MATCH
// is greater than RESULT_ACTION_MATCH, a match of only attributes and not
// actions will be preferred to one of actions and not attributes.
enum MatchResultFlags {
RESULT_NO_MATCH = 0,
RESULT_ACTION_MATCH = 1 << 0,
RESULT_HTML_ATTRIBUTES_MATCH = 1 << 1,
RESULT_ORIGINS_MATCH = 1 << 2,
RESULT_COMPLETE_MATCH = RESULT_ACTION_MATCH | RESULT_HTML_ATTRIBUTES_MATCH |
RESULT_ORIGINS_MATCH
};
// Use MatchResultMask to contain combinations of MatchResultFlags values.
// It's a signed int rather than unsigned to avoid signed/unsigned mismatch
// caused by the enum values implicitly converting to signed int.
typedef int MatchResultMask;
enum OtherPossibleUsernamesAction {
ALLOW_OTHER_POSSIBLE_USERNAMES,
IGNORE_OTHER_POSSIBLE_USERNAMES
};
// Chooses between the current and new password value which one to save. This
// is whichever is non-empty, with the preference being given to the new one.
static base::string16 PasswordToSave(const autofill::PasswordForm& form);
// Compares basic data of |observed_form_| with |form| and returns how much
// they match. The return value is a MatchResultMask bitmask.
MatchResultMask DoesManage(const autofill::PasswordForm& form) const;
// Retrieves potential matching logins from the database. In addition the
// statistics is retrived on platforms with the password bubble.
void FetchDataFromPasswordStore();
// Simple state-check to verify whether this object as received a callback
// from the PasswordStore and completed its matching phase. Note that the
// callback in question occurs on the same (and only) main thread from which
// instances of this class are ever used, but it is required since it is
// conceivable that a user (or ui test) could attempt to submit a login
// prompt before the callback has occured, which would InvokeLater a call to
// PasswordManager::ProvisionallySave, which would interact with this object
// before the db has had time to answer with matching password entries.
// This is intended to be a one-time check; if the return value is false the
// expectation is caller will give up. This clearly won't work if you put it
// in a loop and wait for matching to complete; you're (supposed to be) on
// the same thread!
bool HasCompletedMatching() const;
// Update |this| with the |form| that was actually submitted. Used to
// determine what type the submitted form is for
// IsIgnorableChangePasswordForm() and UMA stats.
void SetSubmittedForm(const autofill::PasswordForm& form);
// Determines if the user opted to 'never remember' passwords for this form.
bool IsBlacklisted() const;
// Used by PasswordManager to determine whether or not to display
// a SavePasswordBar when given the green light to save the PasswordForm
// managed by this.
bool IsNewLogin() const;
// Returns true if the current pending credentials were found using
// origin matching of the public suffix, instead of the signon realm of the
// form.
bool IsPendingCredentialsPublicSuffixMatch() const;
// Through |driver|, supply the associated frame with appropriate information
// (fill data, whether to allow password generation, etc.). If this is called
// before |this| has data from the PasswordStore, the execution will be
// delayed until the data arrives.
void ProcessFrame(const base::WeakPtr<PasswordManagerDriver>& driver);
// PasswordStoreConsumer:
void OnGetPasswordStoreResults(
ScopedVector<autofill::PasswordForm> results) override;
void OnGetSiteStatistics(
scoped_ptr<std::vector<scoped_ptr<InteractionsStats>>> stats) override;
// A user opted to 'never remember' passwords for this form.
// Blacklist it so that from now on when it is seen we ignore it.
// TODO(vasilii): remove the 'virtual' specifier.
virtual void PermanentlyBlacklist();
// If the user has submitted observed_form_, provisionally hold on to
// the submitted credentials until we are told by PasswordManager whether
// or not the login was successful. |action| describes how we deal with
// possible usernames. If |action| is ALLOW_OTHER_POSSIBLE_USERNAMES we will
// treat a possible usernames match as a sign that our original heuristics
// were wrong and that the user selected the correct username from the
// Autofill UI.
void ProvisionallySave(const autofill::PasswordForm& credentials,
OtherPossibleUsernamesAction action);
// Handles save-as-new or update of the form managed by this manager.
// Note the basic data of updated_credentials must match that of
// observed_form_ (e.g DoesManage(pending_credentials_) == true).
void Save();
// Update the password store entry for |credentials_to_update|, using the
// password from |pending_credentials_|. It modifies |pending_credentials_|.
// |credentials_to_update| should be one of |best_matches_| or
// |pending_credentials_|.
void Update(const autofill::PasswordForm& credentials_to_update);
// Call these if/when we know the form submission worked or failed.
// These routines are used to update internal statistics ("ActionsTaken").
void LogSubmitPassed();
void LogSubmitFailed();
// When attempting to provisionally save |form|, call this to check if it is
// actually a change-password form which should be ignored, i.e., whether:
// * the username was not explicitly marked with "autocomplete=username", and
// * both the current and new password fields are non-empty, and
// * the username and password do not match any credentials already stored.
// In these cases the username field is detection is unreliable (there might
// even be none), and the user should not be bothered with saving a
// potentially malformed credential. Once we handle change password forms
// correctly, this method should be replaced accordingly.
//
// Must be called after SetSubmittedForm().
bool is_ignorable_change_password_form() const {
DCHECK(form_type_ != kFormTypeUnspecified);
return is_ignorable_change_password_form_;
}
// These functions are used to determine if this form has had it's password
// auto generated by the browser.
bool has_generated_password() const { return has_generated_password_; }
void set_has_generated_password(bool generated_password) {
has_generated_password_ = generated_password;
}
bool is_manual_generation() { return is_manual_generation_; }
void set_is_manual_generation(bool is_manual_generation) {
is_manual_generation_ = is_manual_generation;
}
const base::string16& generation_element() { return generation_element_; }
void set_generation_element(const base::string16& generation_element) {
generation_element_ = generation_element;
}
bool password_overridden() const { return password_overridden_; }
bool retry_password_form_password_update() const {
return retry_password_form_password_update_;
}
// Called if the user could generate a password for this form.
void MarkGenerationAvailable() { generation_available_ = true; }
// Returns the pending credentials.
const autofill::PasswordForm& pending_credentials() const {
return pending_credentials_;
}
// Returns the best matches.
const autofill::PasswordFormMap& best_matches() const {
return best_matches_;
}
const autofill::PasswordForm* preferred_match() const {
return preferred_match_;
}
const ScopedVector<autofill::PasswordForm>& blacklisted_matches() const {
return blacklisted_matches_;
}
#if defined(UNIT_TEST)
void SimulateFetchMatchingLoginsFromPasswordStore() {
// Just need to update the internal states.
state_ = MATCHING_PHASE;
}
#endif
const std::vector<scoped_ptr<InteractionsStats>>& interactions_stats() const {
return interactions_stats_;
}
const autofill::PasswordForm& observed_form() const { return observed_form_; }
bool is_possible_change_password_form_without_username() const {
return is_possible_change_password_form_without_username_;
}
// Use this to wipe copies of |pending_credentials_| from the password store
// (and |best_matches_| as well. It will only wipe if:
// 1. The stored password differs from the one in |pending_credentials_|.
// 2. And the store already returned results for the observed form.
// This is designed for use with sync credentials, so it will use GAIA utils
// to catch equivalent usernames (e.g., if |pending_credentials_| have
// username 'test', and the store also contains outdated entries for
// 'test@gmail.com' and 'test@googlemail.com', those will be wiped).
void WipeStoreCopyIfOutdated();
// Called when the user chose not to update password.
void OnNopeUpdateClicked();
// Called when the user didn't interact with Update UI.
void OnNoInteractionOnUpdate();
private:
// ManagerAction - What does the manager do with this form? Either it
// fills it, or it doesn't. If it doesn't fill it, that's either
// because it has no match, or it is blacklisted, or it is disabled
// via the AUTOCOMPLETE=off attribute. Note that if we don't have
// an exact match, we still provide candidates that the user may
// end up choosing.
enum ManagerAction {
kManagerActionNone = 0,
kManagerActionAutofilled,
kManagerActionBlacklisted,
kManagerActionMax
};
// UserAction - What does the user do with this form? If he or she
// does nothing (either by accepting what the password manager did, or
// by simply (not typing anything at all), you get None. If there were
// multiple choices and the user selects one other than the default,
// you get Choose, if user selects an entry from matching against the Public
// Suffix List you get ChoosePslMatch, if the user types in a new value
// for just the password you get OverridePassword, and if the user types in a
// new value for the username and password you get
// OverrideUsernameAndPassword.
enum UserAction {
kUserActionNone = 0,
kUserActionChoose,
kUserActionChoosePslMatch,
kUserActionOverridePassword,
kUserActionOverrideUsernameAndPassword,
kUserActionMax
};
// Result - What happens to the form?
enum SubmitResult {
kSubmitResultNotSubmitted = 0,
kSubmitResultFailed,
kSubmitResultPassed,
kSubmitResultMax
};
// What the form is used for. kFormTypeUnspecified is only set before
// the SetSubmittedForm() is called, and should never be actually uploaded.
enum FormType {
kFormTypeLogin,
kFormTypeLoginNoUsername,
kFormTypeChangePasswordEnabled,
kFormTypeChangePasswordDisabled,
kFormTypeChangePasswordNoUsername,
kFormTypeSignup,
kFormTypeSignupNoUsername,
kFormTypeLoginAndSignup,
kFormTypeUnspecified,
kFormTypeMax
};
// The maximum number of combinations of the three preceding enums.
// This is used when recording the actions taken by the form in UMA.
static const int kMaxNumActionsTaken =
kManagerActionMax * kUserActionMax * kSubmitResultMax;
// Through |driver|, supply the associated frame with appropriate information
// (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);
// Helper for Save in the case that best_matches.size() == 0, meaning
// we have no prior record of this form/username/password and the user
// has opted to 'Save Password'. The previously preferred login from
// |best_matches_| will be reset.
void SaveAsNewLogin();
// Helper for OnGetPasswordStoreResults to score an individual result
// against the observed_form_.
uint32_t ScoreResult(const autofill::PasswordForm& candidate) const;
// For the blacklisted |form| returns true iff it blacklists |observed_form_|.
bool IsBlacklistMatch(const autofill::PasswordForm& form) const;
// Helper for Save in the case that best_matches.size() > 0, meaning
// we have at least one match for this form/username/password. This
// Updates the form managed by this object, as well as any matching forms
// that now need to have preferred bit changed, since updated_credentials
// is now implicitly 'preferred'.
void UpdateLogin();
// Check to see if |pending| corresponds to an account creation form. If we
// think that it does, we label it as such and upload this state to the
// Autofill server to vote for the correct username field, and also so that
// we will trigger password generation in the future. This function will
// update generation_upload_status of |pending| if an upload is performed.
void SendAutofillVotes(const autofill::PasswordForm& observed,
autofill::PasswordForm* pending);
// Update all login matches to reflect new preferred state - preferred flag
// will be reset on all matched logins that different than the current
// |pending_credentials_|.
void UpdatePreferredLoginState(PasswordStore* password_store);
// Returns true if |username| is one of the other possible usernames for a
// password form in |best_matches_| and sets |pending_credentials_| to the
// match which had this username.
bool UpdatePendingCredentialsIfOtherPossibleUsername(
const base::string16& username);
// Returns true if |form| is a username update of a credential already in
// |best_matches_|. Sets |pending_credentials_| to the appropriate
// PasswordForm if it returns true.
bool UpdatePendingCredentialsIfUsernameChanged(
const autofill::PasswordForm& form);
// Update state to reflect that |credential| was used. This is broken out from
// UpdateLogin() so that PSL matches can also be properly updated.
void UpdateMetadataForUsage(const autofill::PasswordForm& credential);
// Converts the "ActionsTaken" fields into an int so they can be logged to
// UMA.
int GetActionsTaken() const;
// Remove possible_usernames that may contains sensitive information and
// duplicates.
void SanitizePossibleUsernames(autofill::PasswordForm* form);
// Try to label password fields and upload |form_data|. This differs from
// AutofillManager::OnFormSubmitted() in a few ways.
// - This function will only label the first <input type="password"> field
// as |password_type|. Other fields will stay unlabeled, as they
// should have been labeled during the upload for OnFormSubmitted().
// - If the |username_field| attribute is nonempty, we will additionally
// label the field with that name as the username field.
// - This function does not assume that |form| is being uploaded during
// the same browsing session as it was originally submitted (as we may
// not have the necessary information to classify the form at that time)
// so it bypasses the cache and doesn't log the same quality UMA metrics.
// |login_form_signature| may be empty. It is non-empty when the user fills
// and submits a login form using a generated password. In this case,
// |login_form_signature| should be set to the submitted form's signature.
// Note that in this case, |form.FormSignature()| gives the signature for the
// registration form on which the password was generated, rather than the
// submitted form's signature.
bool UploadPasswordForm(const autofill::FormData& form_data,
const base::string16& username_field,
const autofill::ServerFieldType& password_type,
const std::string& login_form_signature);
// Try to label username, password and new password fields of |observed_form_|
// which is considered to be change password forms. Returns true on success.
// |password_type| should be equal to NEW_PASSWORD, PROBABLY_NEW_PASSWORD or
// NOT_NEW_PASSWORD. These values correspond to cases when the user conrirmed
// password update, did nothing or declined to update password respectively.
bool UploadChangePasswordForm(const autofill::ServerFieldType& password_type,
const std::string& login_form_signature);
// Try to label a password field that was used for generation with information
// that the password was generated and upload |form_data|. For labelling
// |generation_element_| and |is_manual_generation_| fields are used. Returns
// true on success.
bool UploadGeneratedVote();
// Create pending credentials from provisionally saved form and forms received
// from password store.
void CreatePendingCredentials();
// Create pending credentials from provisionally saved form when this form
// represents credentials that were not previosly saved.
void CreatePendingCredentialsForNewCredentials();
// If |pending_credentials_.username_value| is not empty, iterates over all
// forms from |best_matches_| and deletes from the password store all which
// are not PSL-matched, have an empty username, and a password equal to
// |pending_credentials_.password_value|.
void DeleteEmptyUsernameCredentials();
// If |best_matches| contains only one entry then return this entry. Otherwise
// for empty |password| return nullptr and for non-empty |password| returns
// the unique entry in |best_matches_| with the same password, if it exists,
// and nullptr otherwise.
autofill::PasswordForm* FindBestMatchForUpdatePassword(
const base::string16& password) const;
// Try to find best matched to |form| from |best_matches_| by the rules:
// 1. If there is an element in |best_matches_| with the same username then
// return it;
// 2. If |form| is created with Credential API return nullptr, i.e. we match
// Credentials API forms only by username;
// 3. If |form| has no |username_element| and no |new_password_element| (i.e.
// a form contains only one field which is a password) and there is an element
// from |best_matches_| with the same password as in |form| then return it;
// 4. Otherwise return nullptr.
autofill::PasswordForm* FindBestSavedMatch(
const autofill::PasswordForm* form) const;
// Set of nonblacklisted PasswordForms from the DB that best match the form
// being managed by this. Use a map instead of vector, because we most
// frequently require lookups by username value in IsNewLogin.
autofill::PasswordFormMap best_matches_;
// Set of forms from PasswordStore that correspond to the current site and
// that are not in |best_matches_|.
ScopedVector<autofill::PasswordForm> not_best_matches_;
// Set of blacklisted forms from the PasswordStore that best match the current
// form.
ScopedVector<autofill::PasswordForm> blacklisted_matches_;
// The PasswordForm from the page or dialog managed by |this|.
const autofill::PasswordForm observed_form_;
// Statistics for the current domain.
std::vector<scoped_ptr<InteractionsStats>> interactions_stats_;
// Stores a submitted form.
scoped_ptr<const autofill::PasswordForm> provisionally_saved_form_;
// Stores if for creating |pending_credentials_| other possible usernames
// option should apply.
OtherPossibleUsernamesAction other_possible_username_action_;
// The origin url path of observed_form_ tokenized, for convenience when
// scoring.
const std::vector<std::string> form_path_segments_;
// Stores updated credentials when the form was submitted but success is still
// unknown. This variable contains credentials that are ready to be written
// (saved or updated) to a password store. It is calculated based on
// |provisionally_saved_form_| and |best_matches_|.
autofill::PasswordForm pending_credentials_;
// Whether pending_credentials_ stores a new login or is an update
// to an existing one.
bool is_new_login_;
// Whether this form has an auto generated password.
bool has_generated_password_;
// Whether password generation was manually triggered.
bool is_manual_generation_;
// A password field name that is used for generation.
base::string16 generation_element_;
// Whether the saved password was overridden.
bool password_overridden_;
// A form is considered to be "retry" password if it has only one field which
// is a current password field.
// This variable is true if the password passed through ProvisionallySave() is
// a password that is not part of any password form stored for this origin
// and it was entered on a retry password form.
bool retry_password_form_password_update_;
// Whether the user can choose to generate a password for this form.
bool generation_available_;
// Set if the user has selected one of the other possible usernames in
// |pending_credentials_|.
base::string16 selected_username_;
// PasswordManager owning this.
PasswordManager* const password_manager_;
// Convenience pointer to entry in best_matches_ that is marked
// as preferred. This is only allowed to be null if there are no best matches
// at all, since there will always be one preferred login when there are
// multiple matches (when first saved, a login is marked preferred).
const autofill::PasswordForm* preferred_match_;
// If the submitted form is for a change password form and the username
// doesn't match saved credentials.
bool is_ignorable_change_password_form_;
// True if we consider this form to be a change password form without username
// field. We use only client heuristics, so it could include signup forms.
// The value of this variable is calculated based not only on information from
// |observed_form_| but also on the credentials that the user submitted.
bool is_possible_change_password_form_without_username_;
typedef enum {
PRE_MATCHING_PHASE, // Have not yet invoked a GetLogins query to find
// matching login information from password store.
MATCHING_PHASE, // We've made a GetLogins request, but
// haven't received or finished processing result.
POST_MATCHING_PHASE // We've queried the DB and processed matching
// login results.
} PasswordFormManagerState;
// State of matching process, used to verify that we don't call methods
// assuming we've already processed the request for matching logins,
// when we actually haven't.
PasswordFormManagerState state_;
// The client which implements embedder-specific PasswordManager operations.
PasswordManagerClient* client_;
// |this| is created for a form in some frame, which is represented by a
// driver. Similar form can appear in more frames, represented with more
// drivers. The drivers are needed to perform frame-specific operations
// (filling etc.). These drivers are kept in |drivers_| to allow updating of
// the filling information when needed.
std::vector<base::WeakPtr<PasswordManagerDriver>> drivers_;
// These three fields record the "ActionsTaken" by the browser and
// the user with this form, and the result. They are combined and
// recorded in UMA when the manager is destroyed.
ManagerAction manager_action_;
UserAction user_action_;
SubmitResult submit_result_;
// Form type of the form that |this| is managing. Set after SetSubmittedForm()
// as our classification of the form can change depending on what data the
// user has entered.
FormType form_type_;
// False unless FetchMatchingLoginsFromPasswordStore has been called again
// without the password store returning results in the meantime.
bool need_to_refetch_;
DISALLOW_COPY_AND_ASSIGN(PasswordFormManager);
};
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_MANAGER_H_