blob: 96d11bcdcd445d967942e6d076d42ec30f6a13f6 [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/password_manager_util.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/logging/log_manager.h"
#include "components/autofill/core/browser/ui/popup_item_ids.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_generation_util.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/credentials_cleaner.h"
#include "components/password_manager/core/browser/credentials_cleaner_runner.h"
#include "components/password_manager/core/browser/http_credentials_cleaner.h"
#include "components/password_manager/core/browser/password_feature_manager.h"
#include "components/password_manager/core/browser/password_generation_frame_helper.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_manager_features_util.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/driver/sync_user_settings.h"
using autofill::PasswordForm;
namespace password_manager_util {
namespace {
// Return true if 1.|lhs| is non-PSL match, |rhs| is PSL match or 2.|lhs| and
// |rhs| have the same value of |is_public_suffix_match|, and |lhs| is more
// recently used than |rhs|.
bool IsBetterMatch(const PasswordForm* lhs, const PasswordForm* rhs) {
return std::make_pair(!lhs->is_public_suffix_match, lhs->date_last_used) >
std::make_pair(!rhs->is_public_suffix_match, rhs->date_last_used);
}
} // namespace
// Update |credential| to reflect usage.
void UpdateMetadataForUsage(PasswordForm* credential) {
++credential->times_used;
// Remove alternate usernames. At this point we assume that we have found
// the right username.
credential->all_possible_usernames.clear();
}
password_manager::SyncState GetPasswordSyncState(
const syncer::SyncService* sync_service) {
if (!sync_service ||
!sync_service->GetActiveDataTypes().Has(syncer::PASSWORDS)) {
return password_manager::NOT_SYNCING;
}
if (sync_service->IsSyncFeatureActive()) {
return sync_service->GetUserSettings()->IsUsingSecondaryPassphrase()
? password_manager::SYNCING_WITH_CUSTOM_PASSPHRASE
: password_manager::SYNCING_NORMAL_ENCRYPTION;
}
DCHECK(base::FeatureList::IsEnabled(
password_manager::features::kEnablePasswordsAccountStorage));
// Account passwords are enabled only for users with normal encryption at
// the moment. Data types won't become active for non-sync users with custom
// passphrase.
return password_manager::ACCOUNT_PASSWORDS_ACTIVE_NORMAL_ENCRYPTION;
}
bool IsSyncingWithNormalEncryption(const syncer::SyncService* sync_service) {
return GetPasswordSyncState(sync_service) ==
password_manager::SYNCING_NORMAL_ENCRYPTION;
}
void TrimUsernameOnlyCredentials(
std::vector<std::unique_ptr<PasswordForm>>* android_credentials) {
// Remove username-only credentials which are not federated.
base::EraseIf(*android_credentials,
[](const std::unique_ptr<PasswordForm>& form) {
return form->scheme == PasswordForm::Scheme::kUsernameOnly &&
form->federation_origin.opaque();
});
// Set "skip_zero_click" on federated credentials.
std::for_each(android_credentials->begin(), android_credentials->end(),
[](const std::unique_ptr<PasswordForm>& form) {
if (form->scheme == PasswordForm::Scheme::kUsernameOnly)
form->skip_zero_click = true;
});
}
bool IsLoggingActive(const password_manager::PasswordManagerClient* client) {
const autofill::LogManager* log_manager = client->GetLogManager();
return log_manager && log_manager->IsLoggingActive();
}
bool ManualPasswordGenerationEnabled(
password_manager::PasswordManagerDriver* driver) {
password_manager::PasswordGenerationFrameHelper* password_generation_manager =
driver ? driver->GetPasswordGenerationHelper() : nullptr;
if (!password_generation_manager ||
!password_generation_manager->IsGenerationEnabled(false /*logging*/)) {
return false;
}
LogPasswordGenerationEvent(
autofill::password_generation::PASSWORD_GENERATION_CONTEXT_MENU_SHOWN);
return true;
}
bool ShowAllSavedPasswordsContextMenuEnabled(
password_manager::PasswordManagerDriver* driver) {
password_manager::PasswordManager* password_manager =
driver ? driver->GetPasswordManager() : nullptr;
if (!password_manager)
return false;
password_manager::PasswordManagerClient* client = password_manager->client();
if (!client ||
!client->IsFillingFallbackEnabled(driver->GetLastCommittedURL()))
return false;
LogContextOfShowAllSavedPasswordsShown(
password_manager::metrics_util::ShowAllSavedPasswordsContext::
kContextMenu);
return true;
}
void UserTriggeredManualGenerationFromContextMenu(
password_manager::PasswordManagerClient* password_manager_client) {
if (!password_manager_client->GetPasswordFeatureManager()
->ShouldShowAccountStorageOptIn()) {
password_manager_client->GeneratePassword();
LogPasswordGenerationEvent(autofill::password_generation::
PASSWORD_GENERATION_CONTEXT_MENU_PRESSED);
return;
}
// The client ensures the callback won't be run if it is destroyed, so
// base::Unretained is safe.
password_manager_client->TriggerReauthForPrimaryAccount(base::BindOnce(
[](password_manager::PasswordManagerClient* client,
password_manager::PasswordManagerClient::ReauthSucceeded succeeded) {
if (succeeded) {
client->GeneratePassword();
LogPasswordGenerationEvent(
autofill::password_generation::
PASSWORD_GENERATION_CONTEXT_MENU_PRESSED);
}
},
base::Unretained(password_manager_client)));
}
// TODO(http://crbug.com/890318): Add unitests to check cleaners are correctly
// created.
void RemoveUselessCredentials(
scoped_refptr<password_manager::PasswordStore> store,
PrefService* prefs,
int delay_in_seconds,
base::RepeatingCallback<network::mojom::NetworkContext*()>
network_context_getter) {
auto cleaning_tasks_runner =
std::make_unique<password_manager::CredentialsCleanerRunner>();
#if !defined(OS_IOS)
// Can be null for some unittests.
if (!network_context_getter.is_null()) {
cleaning_tasks_runner->MaybeAddCleaningTask(
std::make_unique<password_manager::HttpCredentialCleaner>(
store, network_context_getter, prefs));
}
#endif // !defined(OS_IOS)
if (cleaning_tasks_runner->HasPendingTasks()) {
// The runner will delete itself once the clearing tasks are done, thus we
// are releasing ownership here.
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
&password_manager::CredentialsCleanerRunner::StartCleaning,
base::Unretained(cleaning_tasks_runner.release())),
base::TimeDelta::FromSeconds(delay_in_seconds));
}
}
base::StringPiece GetSignonRealmWithProtocolExcluded(const PasswordForm& form) {
base::StringPiece signon_realm_protocol_excluded = form.signon_realm;
// Find the web origin (with protocol excluded) in the signon_realm.
const size_t after_protocol =
signon_realm_protocol_excluded.find(form.url.host_piece());
DCHECK_NE(after_protocol, base::StringPiece::npos);
// Keep the string starting with position |after_protocol|.
signon_realm_protocol_excluded =
signon_realm_protocol_excluded.substr(after_protocol);
return signon_realm_protocol_excluded;
}
void FindBestMatches(
const std::vector<const PasswordForm*>& non_federated_matches,
PasswordForm::Scheme scheme,
std::vector<const PasswordForm*>* non_federated_same_scheme,
std::vector<const PasswordForm*>* best_matches,
const PasswordForm** preferred_match) {
DCHECK(std::all_of(
non_federated_matches.begin(), non_federated_matches.end(),
[](const PasswordForm* match) { return !match->blacklisted_by_user; }));
DCHECK(non_federated_same_scheme);
DCHECK(best_matches);
DCHECK(preferred_match);
*preferred_match = nullptr;
best_matches->clear();
non_federated_same_scheme->clear();
for (auto* match : non_federated_matches) {
if (match->scheme == scheme)
non_federated_same_scheme->push_back(match);
}
if (non_federated_same_scheme->empty())
return;
std::sort(non_federated_same_scheme->begin(),
non_federated_same_scheme->end(), IsBetterMatch);
std::set<std::pair<PasswordForm::Store, base::string16>> store_usernames;
for (const auto* match : *non_federated_same_scheme) {
auto store_username =
std::make_pair(match->in_store, match->username_value);
// The first match for |store_username| in the sorted array is best
// match.
if (!base::Contains(store_usernames, store_username)) {
store_usernames.insert(store_username);
best_matches->push_back(match);
}
}
*preferred_match = *non_federated_same_scheme->begin();
}
const PasswordForm* FindFormByUsername(
const std::vector<const PasswordForm*>& forms,
const base::string16& username_value) {
for (const PasswordForm* form : forms) {
if (form->username_value == username_value)
return form;
}
return nullptr;
}
const PasswordForm* GetMatchForUpdating(
const PasswordForm& submitted_form,
const std::vector<const PasswordForm*>& credentials) {
// This is the case for the credential management API. It should not depend on
// form managers. Once that's the case, this should be turned into a DCHECK.
// TODO(crbug/947030): turn it into a DCHECK.
if (!submitted_form.federation_origin.opaque())
return nullptr;
// Try to return form with matching |username_value|.
const PasswordForm* username_match =
FindFormByUsername(credentials, submitted_form.username_value);
if (username_match) {
if (!username_match->is_public_suffix_match)
return username_match;
const auto& password_to_save = submitted_form.new_password_value.empty()
? submitted_form.password_value
: submitted_form.new_password_value;
// Normally, the copy of the PSL matched credentials, adapted for the
// current domain, is saved automatically without asking the user, because
// the copy likely represents the same account, i.e., the one for which
// the user already agreed to store a password.
//
// However, if the user changes the suggested password, it might indicate
// that the autofilled credentials and |submitted_password_form|
// actually correspond to two different accounts (see
// http://crbug.com/385619).
return password_to_save == username_match->password_value ? username_match
: nullptr;
}
// Next attempt is to find a match by password value. It should not be tried
// when the username was actually detected.
if (submitted_form.type == PasswordForm::Type::kApi ||
!submitted_form.username_value.empty()) {
return nullptr;
}
for (const PasswordForm* stored_match : credentials) {
if (stored_match->password_value == submitted_form.password_value)
return stored_match;
}
// Last try. The submitted form had no username but a password. Assume that
// it's an existing credential.
return credentials.empty() ? nullptr : credentials.front();
}
autofill::PasswordForm MakeNormalizedBlacklistedForm(
password_manager::PasswordStore::FormDigest digest) {
autofill::PasswordForm result;
result.blacklisted_by_user = true;
result.scheme = std::move(digest.scheme);
result.signon_realm = std::move(digest.signon_realm);
// In case |digest| corresponds to an Android credential copy the origin as
// is, otherwise clear out the path by calling GetOrigin().
if (password_manager::FacetURI::FromPotentiallyInvalidSpec(
digest.origin.spec())
.IsValidAndroidFacetURI()) {
result.url = std::move(digest.origin);
} else {
// GetOrigin() will return an empty GURL if the origin is not valid or
// standard. DCHECK that this will not happen.
DCHECK(digest.origin.is_valid());
DCHECK(digest.origin.IsStandard());
result.url = digest.origin.GetOrigin();
}
return result;
}
} // namespace password_manager_util