// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/safe_browsing/model/chrome_password_protection_service.h"
#import <memory>
#import "base/command_line.h"
#import "base/feature_list.h"
#import "base/metrics/histogram_functions.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#import "base/notreached.h"
#import "base/ranges/algorithm.h"
#import "base/strings/utf_string_conversions.h"
#import "base/time/time.h"
#import "components/keyed_service/core/service_access_type.h"
#import "components/omnibox/common/omnibox_features.h"
#import "components/password_manager/core/browser/insecure_credentials_helper.h"
#import "components/password_manager/core/browser/ui/password_check_referrer.h"
#import "components/prefs/pref_service.h"
#import "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
#import "components/safe_browsing/core/browser/user_population.h"
#import "components/safe_browsing/core/browser/verdict_cache_manager.h"
#import "components/safe_browsing/core/common/features.h"
#import "components/safe_browsing/core/common/safe_browsing_prefs.h"
#import "components/safe_browsing/core/common/safebrowsing_constants.h"
#import "components/safe_browsing/core/common/utils.h"
#import "components/safe_browsing/ios/browser/password_protection/password_protection_request_ios.h"
#import "components/signin/public/identity_manager/identity_manager.h"
#import "components/strings/grit/components_strings.h"
#import "components/sync/base/data_type.h"
#import "components/sync/protocol/user_event_specifics.pb.h"
#import "components/sync/service/sync_service.h"
#import "components/sync_user_events/user_event_service.h"
#import "components/variations/service/variations_service.h"
#import "ios/chrome/browser/history/model/history_service_factory.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_account_password_store_factory.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.h"
#import "ios/chrome/browser/safe_browsing/model/user_population_helper.h"
#import "ios/chrome/browser/safe_browsing/model/verdict_cache_manager_factory.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
#import "ios/chrome/browser/sync/model/ios_user_event_service_factory.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/components/security_interstitials/safe_browsing/safe_browsing_service.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/thread/web_thread.h"
#import "ios/web/public/web_state.h"
#import "ui/base/l10n/l10n_util.h"
#import "url/gurl.h"
using base::RecordAction;
using base::UserMetricsAction;
using password_manager::metrics_util::PasswordType;
using safe_browsing::ChromeUserPopulation;
using safe_browsing::LoginReputationClientRequest;
using safe_browsing::LoginReputationClientResponse;
using safe_browsing::PasswordProtectionTrigger;
using safe_browsing::ReferrerChain;
using safe_browsing::RequestOutcome;
using safe_browsing::ReusedPasswordAccountType;
using safe_browsing::WarningAction;
using sync_pb::UserEventSpecifics;
using InteractionResult = sync_pb::GaiaPasswordReuse::
PasswordReuseDialogInteraction::InteractionResult;
using PasswordReuseDialogInteraction =
sync_pb::GaiaPasswordReuse::PasswordReuseDialogInteraction;
using PasswordReuseEvent =
safe_browsing::LoginReputationClientRequest::PasswordReuseEvent;
using SafeBrowsingStatus =
sync_pb::GaiaPasswordReuse::PasswordReuseDetected::SafeBrowsingStatus;
using ShowWarningCallback =
safe_browsing::PasswordProtectionService::ShowWarningCallback;
namespace {
// Returns true if the command line has an artificial unsafe cached verdict.
bool HasArtificialCachedVerdict() {
std::string phishing_url_string =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
safe_browsing::kArtificialCachedPhishGuardVerdictFlag);
return !phishing_url_string.empty();
}
// Given a `web_state`, returns a timestamp of its last committed
// navigation.
int64_t GetLastCommittedNavigationTimestamp(web::WebState* web_state) {
if (!web_state) {
return 0;
}
web::NavigationItem* navigation =
web_state->GetNavigationManager()->GetLastCommittedItem();
return navigation ? navigation->GetTimestamp()
.ToDeltaSinceWindowsEpoch()
.InMicroseconds()
: 0;
}
// Return a new UserEventSpecifics w/o the navigation_id populated
std::unique_ptr<UserEventSpecifics> GetNewUserEventSpecifics() {
auto specifics = std::make_unique<UserEventSpecifics>();
specifics->set_event_time_usec(
base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
return specifics;
}
// Return a new UserEventSpecifics w/ the navigation_id populated
std::unique_ptr<UserEventSpecifics> GetUserEventSpecificsWithNavigationId(
int64_t navigation_id) {
if (navigation_id <= 0) {
return nullptr;
}
auto specifics = GetNewUserEventSpecifics();
specifics->set_navigation_id(navigation_id);
return specifics;
}
// Return a new UserEventSpecifics populated from the web_state
std::unique_ptr<UserEventSpecifics> GetUserEventSpecifics(
web::WebState* web_state) {
return GetUserEventSpecificsWithNavigationId(
GetLastCommittedNavigationTimestamp(web_state));
}
} // namespace
ChromePasswordProtectionService::ChromePasswordProtectionService(
SafeBrowsingService* sb_service,
ChromeBrowserState* browser_state,
history::HistoryService* history_service,
safe_browsing::SafeBrowsingMetricsCollector*
safe_browsing_metrics_collector,
ChangePhishedCredentialsCallback add_phished_credentials,
ChangePhishedCredentialsCallback remove_phished_credentials)
: safe_browsing::PasswordProtectionService(
sb_service->GetDatabaseManager(),
sb_service->GetURLLoaderFactory(),
history_service,
/*pref_service=*/nullptr,
/*token_fetcher=*/nullptr,
browser_state->IsOffTheRecord(),
/*identity_manager=*/nullptr,
/*try_token_fetch=*/false,
safe_browsing_metrics_collector),
browser_state_(browser_state),
add_phished_credentials_(std::move(add_phished_credentials)),
remove_phished_credentials_(std::move(remove_phished_credentials)) {}
ChromePasswordProtectionService::~ChromePasswordProtectionService() = default;
// Removes ShowWarningCallbacks after requests have finished, even if they were
// not called.
void ChromePasswordProtectionService::RequestFinished(
safe_browsing::PasswordProtectionRequest* request,
RequestOutcome outcome,
std::unique_ptr<LoginReputationClientResponse> response) {
// Ensure parent class method runs before removing callback.
PasswordProtectionService::RequestFinished(request, outcome,
std::move(response));
show_warning_callbacks_.erase(request);
}
void ChromePasswordProtectionService::ShowModalWarning(
safe_browsing::PasswordProtectionRequest* request,
LoginReputationClientResponse::VerdictType verdict_type,
const std::string& verdict_token,
ReusedPasswordAccountType password_type) {
safe_browsing::PasswordProtectionRequestIOS* request_ios =
static_cast<safe_browsing::PasswordProtectionRequestIOS*>(request);
// Don't show warning again if there is already a modal warning showing.
if (IsModalWarningShowingInWebState(request_ios->web_state())) {
return;
}
auto callback = std::move(show_warning_callbacks_[request]);
if (callback) {
ReusedPasswordAccountType reused_password_account_type =
GetPasswordProtectionReusedPasswordAccountType(request->password_type(),
request->username());
const std::u16string warning_text =
GetWarningDetailText(reused_password_account_type);
// Partial bind WebState and password_type.
auto completion_callback = base::BindOnce(
&ChromePasswordProtectionService::OnUserAction,
weak_factory_.GetWeakPtr(), request_ios->web_state(), password_type);
std::move(callback).Run(warning_text, std::move(completion_callback));
}
}
void ChromePasswordProtectionService::CacheVerdict(
const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
ReusedPasswordAccountType password_type,
const LoginReputationClientResponse& verdict,
const base::Time& receive_time) {
if (!CanGetReputationOfURL(url) || IsIncognito()) {
return;
}
VerdictCacheManagerFactory::GetForBrowserState(browser_state_)
->CachePhishGuardVerdict(trigger_type, password_type, verdict,
receive_time);
}
LoginReputationClientResponse::VerdictType
ChromePasswordProtectionService::GetCachedVerdict(
const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
ReusedPasswordAccountType password_type,
LoginReputationClientResponse* out_response) {
if (HasArtificialCachedVerdict() ||
(url.is_valid() && CanGetReputationOfURL(url))) {
return VerdictCacheManagerFactory::GetForBrowserState(browser_state_)
->GetCachedPhishGuardVerdict(url, trigger_type, password_type,
out_response);
}
return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
}
int ChromePasswordProtectionService::GetStoredVerdictCount(
LoginReputationClientRequest::TriggerType trigger_type) {
return VerdictCacheManagerFactory::GetForBrowserState(browser_state_)
->GetStoredPhishGuardVerdictCount(trigger_type);
}
void ChromePasswordProtectionService::MaybeReportPasswordReuseDetected(
const GURL& main_frame_url,
const std::string& username,
PasswordType password_type,
bool is_phishing_url,
bool warning_shown) {
// Enterprise reporting extension not yet supported in iOS.
}
void ChromePasswordProtectionService::ReportPasswordChanged() {
// Enterprise reporting extension not yet supported in iOS.
}
void ChromePasswordProtectionService::FillReferrerChain(
const GURL& event_url,
SessionID event_tab_id, // SessionID::InvalidValue()
// if tab not available.
LoginReputationClientRequest::Frame* frame) {
// Not yet supported in iOS.
}
void ChromePasswordProtectionService::SanitizeReferrerChain(
ReferrerChain* referrer_chain) {
// Sample pings not yet supported in iOS.
}
void ChromePasswordProtectionService::PersistPhishedSavedPasswordCredential(
const std::vector<password_manager::MatchingReusedCredential>&
matching_reused_credentials) {
if (!browser_state_) {
return;
}
for (const auto& credential : matching_reused_credentials) {
password_manager::PasswordStoreInterface* password_store =
GetStoreForReusedCredential(credential);
// Password store can be null in tests.
if (!password_store) {
continue;
}
add_phished_credentials_.Run(password_store, credential);
}
}
void ChromePasswordProtectionService::RemovePhishedSavedPasswordCredential(
const std::vector<password_manager::MatchingReusedCredential>&
matching_reused_credentials) {
if (!browser_state_) {
return;
}
for (const auto& credential : matching_reused_credentials) {
password_manager::PasswordStoreInterface* password_store =
GetStoreForReusedCredential(credential);
// Password store can be null in tests.
if (!password_store) {
continue;
}
remove_phished_credentials_.Run(password_store, credential);
}
}
RequestOutcome ChromePasswordProtectionService::GetPingNotSentReason(
LoginReputationClientRequest::TriggerType trigger_type,
const GURL& url,
ReusedPasswordAccountType password_type) {
DCHECK(!CanSendPing(trigger_type, url, password_type));
if (IsInExcludedCountry()) {
return RequestOutcome::EXCLUDED_COUNTRY;
}
if (!IsSafeBrowsingEnabled()) {
return RequestOutcome::SAFE_BROWSING_DISABLED;
}
if (IsIncognito()) {
return RequestOutcome::DISABLED_DUE_TO_INCOGNITO;
}
if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
password_type.account_type() !=
ReusedPasswordAccountType::SAVED_PASSWORD &&
GetPasswordProtectionWarningTriggerPref(password_type) ==
safe_browsing::PASSWORD_PROTECTION_OFF) {
return RequestOutcome::TURNED_OFF_BY_ADMIN;
}
PrefService* prefs = browser_state_->GetPrefs();
if (safe_browsing::IsURLAllowlistedByPolicy(url, *prefs)) {
return RequestOutcome::MATCHED_ENTERPRISE_ALLOWLIST;
}
if (safe_browsing::MatchesPasswordProtectionChangePasswordURL(url, *prefs)) {
return RequestOutcome::MATCHED_ENTERPRISE_CHANGE_PASSWORD_URL;
}
if (safe_browsing::MatchesPasswordProtectionLoginURL(url, *prefs)) {
return RequestOutcome::MATCHED_ENTERPRISE_LOGIN_URL;
}
if (IsInPasswordAlertMode(password_type)) {
return RequestOutcome::PASSWORD_ALERT_MODE;
}
return RequestOutcome::DISABLED_DUE_TO_USER_POPULATION;
}
void ChromePasswordProtectionService::
RemoveUnhandledSyncPasswordReuseOnURLsDeleted(
bool all_history,
const history::URLRows& deleted_rows) {
// Sync password not yet supported in iOS.
}
bool ChromePasswordProtectionService::UserClickedThroughSBInterstitial(
safe_browsing::PasswordProtectionRequest* request) {
// Not yet supported in iOS.
return false;
}
PasswordProtectionTrigger
ChromePasswordProtectionService::GetPasswordProtectionWarningTriggerPref(
ReusedPasswordAccountType password_type) const {
if (password_type.account_type() ==
ReusedPasswordAccountType::SAVED_PASSWORD) {
return safe_browsing::PHISHING_REUSE;
}
bool is_policy_managed =
GetPrefs()->HasPrefPath(prefs::kPasswordProtectionWarningTrigger);
PasswordProtectionTrigger trigger_level =
static_cast<PasswordProtectionTrigger>(
GetPrefs()->GetInteger(prefs::kPasswordProtectionWarningTrigger));
return is_policy_managed ? trigger_level : safe_browsing::PHISHING_REUSE;
}
LoginReputationClientRequest::UrlDisplayExperiment
ChromePasswordProtectionService::GetUrlDisplayExperiment() const {
safe_browsing::LoginReputationClientRequest::UrlDisplayExperiment experiment;
experiment.set_simplified_url_display_enabled(
base::FeatureList::IsEnabled(safe_browsing::kSimplifiedUrlDisplay));
// Delayed warnings parameters:
experiment.set_delayed_warnings_enabled(
base::FeatureList::IsEnabled(safe_browsing::kDelayedWarnings));
experiment.set_delayed_warnings_mouse_clicks_enabled(
safe_browsing::kDelayedWarningsEnableMouseClicks.Get());
return experiment;
}
AccountInfo ChromePasswordProtectionService::GetAccountInfo() const {
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForBrowserState(browser_state_);
if (!identity_manager) {
return AccountInfo();
}
return identity_manager->FindExtendedAccountInfo(
identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin));
}
safe_browsing::ChromeUserPopulation::UserPopulation
ChromePasswordProtectionService::GetUserPopulationPref() const {
return safe_browsing::GetUserPopulationPref(browser_state_->GetPrefs());
}
AccountInfo ChromePasswordProtectionService::GetAccountInfoForUsername(
const std::string& username) const {
auto* identity_manager =
IdentityManagerFactory::GetForBrowserState(browser_state_);
if (!identity_manager) {
return AccountInfo();
}
std::vector<CoreAccountInfo> signed_in_accounts =
identity_manager->GetAccountsWithRefreshTokens();
auto account_iterator = base::ranges::find_if(
signed_in_accounts, [username](const auto& account) {
return password_manager::AreUsernamesSame(
account.email,
/*is_username1_gaia_account=*/true, username,
/*is_username2_gaia_account=*/true);
});
if (account_iterator == signed_in_accounts.end()) {
return AccountInfo();
}
return identity_manager->FindExtendedAccountInfo(*account_iterator);
}
LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType
ChromePasswordProtectionService::GetSyncAccountType() const {
const AccountInfo account_info = GetAccountInfo();
if (!IsPrimaryAccountSignedIn()) {
return PasswordReuseEvent::NOT_SIGNED_IN;
}
// For gmail or googlemail account, the hosted_domain will always be
// kNoHostedDomainFound.
return account_info.hosted_domain == kNoHostedDomainFound
? PasswordReuseEvent::GMAIL
: PasswordReuseEvent::GSUITE;
}
bool ChromePasswordProtectionService::CanShowInterstitial(
ReusedPasswordAccountType password_type,
const GURL& main_frame_url) {
// Not yet supported in iOS.
return false;
}
bool ChromePasswordProtectionService::IsURLAllowlistedForPasswordEntry(
const GURL& url) const {
if (!browser_state_) {
return false;
}
PrefService* prefs = GetPrefs();
return safe_browsing::IsURLAllowlistedByPolicy(url, *prefs) ||
safe_browsing::MatchesPasswordProtectionChangePasswordURL(url,
*prefs) ||
safe_browsing::MatchesPasswordProtectionLoginURL(url, *prefs);
}
bool ChromePasswordProtectionService::IsInPasswordAlertMode(
ReusedPasswordAccountType password_type) {
return GetPasswordProtectionWarningTriggerPref(password_type) ==
safe_browsing::PASSWORD_REUSE;
}
bool ChromePasswordProtectionService::CanSendSamplePing() {
// Sample pings not yet enabled in iOS.
return false;
}
bool ChromePasswordProtectionService::IsPingingEnabled(
LoginReputationClientRequest::TriggerType trigger_type,
ReusedPasswordAccountType password_type) {
if (!IsSafeBrowsingEnabled()) {
return false;
}
// Currently, pinging is only enabled for saved passwords reuse events in iOS.
if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
password_type.account_type() ==
ReusedPasswordAccountType::SAVED_PASSWORD) {
return true;
}
return false;
}
bool ChromePasswordProtectionService::IsIncognito() {
return browser_state_->IsOffTheRecord();
}
bool ChromePasswordProtectionService::IsExtendedReporting() {
// Not yet supported in iOS.
return false;
}
bool ChromePasswordProtectionService::IsPrimaryAccountSyncingHistory() const {
syncer::SyncService* sync =
SyncServiceFactory::GetForBrowserState(browser_state_);
return sync &&
sync->GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES) &&
!sync->IsLocalSyncEnabled();
}
bool ChromePasswordProtectionService::IsPrimaryAccountSignedIn() const {
return !GetAccountInfo().account_id.empty() &&
!GetAccountInfo().hosted_domain.empty();
}
bool ChromePasswordProtectionService::IsAccountGmail(
const std::string& username) const {
return GetAccountInfoForUsername(username).hosted_domain ==
kNoHostedDomainFound;
}
bool ChromePasswordProtectionService::IsInExcludedCountry() {
variations::VariationsService* variations_service =
GetApplicationContext()->GetVariationsService();
if (!variations_service) {
return false;
}
return base::Contains(safe_browsing::GetExcludedCountries(),
variations_service->GetLatestCountry());
}
void ChromePasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
web::WebState* web_state,
const GURL& main_frame_url,
const std::string& username,
PasswordType password_type,
const std::vector<password_manager::MatchingReusedCredential>&
matching_reused_credentials,
bool password_field_exists,
ShowWarningCallback show_warning_callback) {
DCHECK_CURRENTLY_ON(web::WebThread::UI);
LoginReputationClientRequest::TriggerType trigger_type =
LoginReputationClientRequest::PASSWORD_REUSE_EVENT;
ReusedPasswordAccountType reused_password_account_type =
GetPasswordProtectionReusedPasswordAccountType(password_type, username);
if (IsSupportedPasswordTypeForPinging(password_type)) {
if (CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
main_frame_url, reused_password_account_type)) {
saved_passwords_matching_reused_credentials_ =
matching_reused_credentials;
StartRequest(web_state, main_frame_url, username, password_type,
matching_reused_credentials,
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
password_field_exists, std::move(show_warning_callback));
} else {
RequestOutcome reason = GetPingNotSentReason(
trigger_type, main_frame_url, reused_password_account_type);
LogNoPingingReason(trigger_type, reason, reused_password_account_type);
if (reused_password_account_type.is_account_syncing()) {
MaybeLogPasswordReuseLookupEvent(web_state, reason, password_type,
nullptr);
}
}
}
}
void ChromePasswordProtectionService::MaybeLogPasswordReuseLookupEvent(
web::WebState* web_state,
RequestOutcome outcome,
PasswordType password_type,
const LoginReputationClientResponse* response) {
// TODO(crbug.com/40731022): Complete PhishGuard iOS implementation.
}
void ChromePasswordProtectionService::MaybeLogPasswordReuseDetectedEvent(
web::WebState* web_state) {
DCHECK_CURRENTLY_ON(web::WebThread::UI);
if (IsIncognito()) {
return;
}
syncer::UserEventService* user_event_service =
IOSUserEventServiceFactory::GetForBrowserState(browser_state_);
if (!user_event_service) {
return;
}
std::unique_ptr<UserEventSpecifics> specifics =
GetUserEventSpecifics(web_state);
if (!specifics) {
return;
}
auto* const status = specifics->mutable_gaia_password_reuse_event()
->mutable_reuse_detected()
->mutable_status();
status->set_enabled(IsSafeBrowsingEnabled());
status->set_safe_browsing_reporting_population(SafeBrowsingStatus::NONE);
user_event_service->RecordUserEvent(std::move(specifics));
}
void ChromePasswordProtectionService::MaybeLogPasswordReuseDialogInteraction(
int64_t navigation_id,
InteractionResult interaction_result) {
DCHECK_CURRENTLY_ON(web::WebThread::UI);
if (IsIncognito()) {
return;
}
syncer::UserEventService* user_event_service =
IOSUserEventServiceFactory::GetForBrowserState(browser_state_);
if (!user_event_service) {
return;
}
std::unique_ptr<UserEventSpecifics> specifics =
GetUserEventSpecificsWithNavigationId(navigation_id);
if (!specifics) {
return;
}
PasswordReuseDialogInteraction* const dialog_interaction =
specifics->mutable_gaia_password_reuse_event()
->mutable_dialog_interaction();
dialog_interaction->set_interaction_result(interaction_result);
user_event_service->RecordUserEvent(std::move(specifics));
}
std::u16string ChromePasswordProtectionService::GetWarningDetailText(
ReusedPasswordAccountType password_type) const {
DCHECK(password_type.account_type() ==
ReusedPasswordAccountType::SAVED_PASSWORD);
return l10n_util::GetStringUTF16(IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED);
}
void ChromePasswordProtectionService::StartRequest(
web::WebState* web_state,
const GURL& main_frame_url,
const std::string& username,
PasswordType password_type,
const std::vector<password_manager::MatchingReusedCredential>&
matching_reused_credentials,
LoginReputationClientRequest::TriggerType trigger_type,
bool password_field_exists,
ShowWarningCallback show_warning_callback) {
DCHECK_CURRENTLY_ON(web::WebThread::UI);
scoped_refptr<safe_browsing::PasswordProtectionRequest> request(
new safe_browsing::PasswordProtectionRequestIOS(
web_state, main_frame_url, web_state->GetContentsMimeType(), username,
password_type, matching_reused_credentials, trigger_type,
password_field_exists, this, GetRequestTimeoutInMS()));
request->Start();
show_warning_callbacks_[request.get()] = std::move(show_warning_callback);
pending_requests_.insert(std::move(request));
}
void ChromePasswordProtectionService::OnUserAction(
web::WebState* web_state,
ReusedPasswordAccountType password_type,
WarningAction action) {
// Only SAVED_PASSWORD is supported in iOS.
DCHECK_EQ(ReusedPasswordAccountType::SAVED_PASSWORD,
password_type.account_type());
LogWarningAction(safe_browsing::WarningUIType::MODAL_DIALOG, action,
password_type);
switch (action) {
case WarningAction::CHANGE_PASSWORD:
RecordAction(UserMetricsAction(
"PasswordProtection.ModalWarning.ChangePasswordButtonClicked"));
password_manager::LogPasswordCheckReferrer(
password_manager::PasswordCheckReferrer::kPhishGuardDialog);
break;
case WarningAction::CLOSE:
RecordAction(
UserMetricsAction("PasswordProtection.ModalWarning.CloseWarning"));
break;
default:
NOTREACHED_IN_MIGRATION();
break;
}
RemoveWarningRequestsByWebState(web_state);
}
bool ChromePasswordProtectionService::IsModalWarningShowingInWebState(
web::WebState* web_state) {
for (const auto& request : warning_requests_) {
safe_browsing::PasswordProtectionRequestIOS* request_ios =
static_cast<safe_browsing::PasswordProtectionRequestIOS*>(
request.get());
if (request_ios->web_state() == web_state) {
return true;
}
}
return false;
}
void ChromePasswordProtectionService::RemoveWarningRequestsByWebState(
web::WebState* web_state) {
for (auto it = warning_requests_.begin(); it != warning_requests_.end();) {
safe_browsing::PasswordProtectionRequestIOS* request_ios =
static_cast<safe_browsing::PasswordProtectionRequestIOS*>(it->get());
if (request_ios->web_state() == web_state) {
it = warning_requests_.erase(it);
} else {
++it;
}
}
}
void ChromePasswordProtectionService::FillUserPopulation(
const GURL& main_frame_url,
LoginReputationClientRequest* request_proto) {
*request_proto->mutable_population() =
GetUserPopulationForBrowserState(browser_state_);
safe_browsing::VerdictCacheManager* cache_manager =
VerdictCacheManagerFactory::GetForBrowserState(browser_state_);
ChromeUserPopulation::PageLoadToken token =
cache_manager->GetPageLoadToken(main_frame_url);
// It's possible that the token is not found because real time URL check is
// not performed for this navigation. Create a new page load token in this
// case.
if (!token.has_token_value()) {
token = cache_manager->CreatePageLoadToken(main_frame_url);
}
request_proto->mutable_population()->mutable_page_load_tokens()->Add()->Swap(
&token);
}
password_manager::PasswordStoreInterface*
ChromePasswordProtectionService::GetStoreForReusedCredential(
const password_manager::MatchingReusedCredential& reused_credential) {
if (!browser_state_) {
return nullptr;
}
return reused_credential.in_store ==
password_manager::PasswordForm::Store::kAccountStore
? GetAccountPasswordStore()
: GetProfilePasswordStore();
}
password_manager::PasswordStoreInterface*
ChromePasswordProtectionService::GetProfilePasswordStore() const {
// Always use EXPLICIT_ACCESS as the password manager checks IsIncognito
// itself when it shouldn't access the PasswordStore.
return IOSChromeProfilePasswordStoreFactory::GetForBrowserState(
browser_state_, ServiceAccessType::EXPLICIT_ACCESS)
.get();
}
password_manager::PasswordStoreInterface*
ChromePasswordProtectionService::GetAccountPasswordStore() const {
return IOSChromeAccountPasswordStoreFactory::GetForBrowserState(
browser_state_, ServiceAccessType::EXPLICIT_ACCESS)
.get();
}
PrefService* ChromePasswordProtectionService::GetPrefs() const {
return browser_state_->GetPrefs();
}
bool ChromePasswordProtectionService::IsSafeBrowsingEnabled() {
return ::safe_browsing::IsSafeBrowsingEnabled(*GetPrefs());
}