blob: 822a9b0e117e695db2447c329b312c40e6a23322 [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 <string>
#include <utility>
#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/autofill_client.h"
#include "components/autofill/core/browser/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/blacklisted_duplicates_cleaner.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/invalid_realm_credential_cleaner.h"
#include "components/password_manager/core/browser/log_manager.h"
#include "components/password_manager/core/browser/password_generation_manager.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_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/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 preferred while |rhs| is not preferred.
bool IsBetterMatch(const PasswordForm* lhs, const PasswordForm* rhs) {
return std::make_pair(!lhs->is_public_suffix_match, lhs->preferred) >
std::make_pair(!rhs->is_public_suffix_match, rhs->preferred);
}
} // 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->other_possible_usernames.clear();
}
password_manager::SyncState GetPasswordSyncState(
const syncer::SyncService* sync_service) {
if (sync_service && sync_service->GetUserSettings()->IsFirstSetupComplete() &&
sync_service->IsSyncFeatureActive() &&
sync_service->GetActiveDataTypes().Has(syncer::PASSWORDS)) {
return sync_service->GetUserSettings()->IsUsingSecondaryPassphrase()
? password_manager::SYNCING_WITH_CUSTOM_PASSPHRASE
: password_manager::SYNCING_NORMAL_ENCRYPTION;
}
return password_manager::NOT_SYNCING;
}
void FindDuplicates(std::vector<std::unique_ptr<PasswordForm>>* forms,
std::vector<std::unique_ptr<PasswordForm>>* duplicates,
std::vector<std::vector<PasswordForm*>>* tag_groups) {
if (forms->empty())
return;
// Linux backends used to treat the first form as a prime oneamong the
// duplicates. Therefore, the caller should try to preserve it.
std::stable_sort(forms->begin(), forms->end(), autofill::LessThanUniqueKey());
std::vector<std::unique_ptr<PasswordForm>> unique_forms;
unique_forms.push_back(std::move(forms->front()));
if (tag_groups) {
tag_groups->clear();
tag_groups->push_back(std::vector<PasswordForm*>());
tag_groups->front().push_back(unique_forms.front().get());
}
for (auto it = forms->begin() + 1; it != forms->end(); ++it) {
if (ArePasswordFormUniqueKeyEqual(**it, *unique_forms.back())) {
if (tag_groups)
tag_groups->back().push_back(it->get());
duplicates->push_back(std::move(*it));
} else {
if (tag_groups)
tag_groups->push_back(std::vector<PasswordForm*>(1, it->get()));
unique_forms.push_back(std::move(*it));
}
}
forms->swap(unique_forms);
}
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_USERNAME_ONLY &&
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_USERNAME_ONLY)
form->skip_zero_click = true;
});
}
bool IsLoggingActive(const password_manager::PasswordManagerClient* client) {
const password_manager::LogManager* log_manager = client->GetLogManager();
return log_manager && log_manager->IsLoggingActive();
}
bool ManualPasswordGenerationEnabled(
password_manager::PasswordManagerDriver* driver) {
password_manager::PasswordGenerationManager* password_generation_manager =
driver ? driver->GetPasswordGenerationManager() : 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->IsFillingFallbackEnabledForCurrentPage())
return false;
LogContextOfShowAllSavedPasswordsShown(
password_manager::metrics_util::
SHOW_ALL_SAVED_PASSWORDS_CONTEXT_CONTEXT_MENU);
return true;
}
void UserTriggeredShowAllSavedPasswordsFromContextMenu(
autofill::AutofillClient* autofill_client) {
if (!autofill_client)
return;
autofill_client->ExecuteCommand(
autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY);
password_manager::metrics_util::LogContextOfShowAllSavedPasswordsAccepted(
password_manager::metrics_util::
SHOW_ALL_SAVED_PASSWORDS_CONTEXT_CONTEXT_MENU);
}
void UserTriggeredManualGenerationFromContextMenu(
password_manager::PasswordManagerClient* password_manager_client) {
password_manager_client->GeneratePassword();
LogPasswordGenerationEvent(
autofill::password_generation::PASSWORD_GENERATION_CONTEXT_MENU_PRESSED);
}
// 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) {
// TODO(https://crbug.com/887889): Remove the knowledge of the particular
// preferences from this code.
const bool need_to_remove_blacklisted_duplicates = !prefs->GetBoolean(
password_manager::prefs::kDuplicatedBlacklistedCredentialsRemoved);
base::UmaHistogramBoolean(
"PasswordManager.BlacklistedSites.NeedRemoveBlacklistDuplicates",
need_to_remove_blacklisted_duplicates);
const bool need_to_remove_invalid_credentials = !prefs->GetBoolean(
password_manager::prefs::kCredentialsWithWrongSignonRealmRemoved);
base::UmaHistogramBoolean(
"PasswordManager.InvalidtHttpsCredentialsNeedToBeCleared",
need_to_remove_invalid_credentials);
// The object will delete itself once the clearing tasks are done.
auto* cleaning_tasks_runner =
new password_manager::CredentialsCleanerRunner();
// Cleaning of credentials with invalid signon_realm needs to search for
// blacklisted non-HTML HTTPS credentials for a corresponding HTTP
// credentials. Thus, this clean-up must be done before cleaning blacklisted
// credentials. Otherwise finding a corresponding HTTP credentials will fail.
if (need_to_remove_invalid_credentials) {
cleaning_tasks_runner->AddCleaningTask(
std::make_unique<password_manager::InvalidRealmCredentialCleaner>(
store, prefs));
}
if (need_to_remove_blacklisted_duplicates) {
cleaning_tasks_runner->AddCleaningTask(
std::make_unique<password_manager::BlacklistedDuplicatesCleaner>(
store, prefs));
}
#if !defined(OS_IOS)
// Can be null for some unittests.
if (!network_context_getter.is_null() &&
password_manager::HttpCredentialCleaner::ShouldRunCleanUp(prefs)) {
cleaning_tasks_runner->AddCleaningTask(
std::make_unique<password_manager::HttpCredentialCleaner>(
store, network_context_getter, prefs));
}
#endif // !defined(OS_IOS)
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&password_manager::CredentialsCleanerRunner::StartCleaning,
base::Unretained(cleaning_tasks_runner)),
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.origin.GetOrigin().GetContent());
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(
std::vector<const PasswordForm*> matches,
std::map<base::string16, const PasswordForm*>* best_matches,
std::vector<const PasswordForm*>* not_best_matches,
const PasswordForm** preferred_match) {
DCHECK(std::all_of(
matches.begin(), matches.end(),
[](const PasswordForm* match) { return !match->blacklisted_by_user; }));
DCHECK(best_matches);
DCHECK(not_best_matches);
DCHECK(preferred_match);
*preferred_match = nullptr;
best_matches->clear();
not_best_matches->clear();
if (matches.empty())
return;
// Sort matches using IsBetterMatch predicate.
std::sort(matches.begin(), matches.end(), IsBetterMatch);
for (const auto* match : matches) {
const base::string16& username = match->username_value;
// The first match for |username| in the sorted array is best match.
if (best_matches->find(username) == best_matches->end())
best_matches->insert(std::make_pair(username, match));
else
not_best_matches->push_back(match);
}
*preferred_match = *matches.begin();
}
} // namespace password_manager_util