chromium/ios/chrome/browser/passwords/model/ios_chrome_password_reuse_detection_manager_client.mm

// Copyright 2023 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/passwords/model/ios_chrome_password_reuse_detection_manager_client.h"

#import <memory>
#import <utility>

#import "base/functional/bind.h"
#import "base/functional/callback.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "components/autofill/core/browser/logging/log_manager.h"
#import "components/autofill/core/browser/logging/log_router.h"
#import "components/password_manager/core/browser/password_manager_client.h"
#import "components/password_manager/core/browser/password_sync_util.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_password_reuse_manager_factory.h"
#import "ios/chrome/browser/passwords/model/password_manager_log_router_factory.h"
#import "ios/chrome/browser/passwords/model/password_tab_helper.h"
#import "ios/chrome/browser/safe_browsing/model/chrome_password_protection_service.h"
#import "ios/chrome/browser/safe_browsing/model/chrome_password_protection_service_factory.h"
#import "ios/chrome/browser/safe_browsing/model/features.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/sync_service_factory.h"
#import "ios/web/public/web_state_observer_bridge.h"
#import "url/gurl.h"

using password_manager::metrics_util::PasswordType;

IOSChromePasswordReuseDetectionManagerClient::
    IOSChromePasswordReuseDetectionManagerClient(
        id<IOSChromePasswordReuseDetectionManagerClientBridge> bridge)
    : bridge_(bridge), password_reuse_detection_manager_(this) {
  log_manager_ = autofill::LogManager::Create(
      ios::PasswordManagerLogRouterFactory::GetForBrowserState(
          bridge_.browserState),
      base::RepeatingClosure());

  if (IsPasswordReuseDetectionEnabled()) {
    web_state_observation_.Observe(bridge_.webState);
    input_event_observation_.Observe(
        PasswordProtectionJavaScriptFeature::GetInstance());
  }
}

IOSChromePasswordReuseDetectionManagerClient::
    ~IOSChromePasswordReuseDetectionManagerClient() = default;

password_manager::PasswordReuseManager*
IOSChromePasswordReuseDetectionManagerClient::GetPasswordReuseManager() const {
  return IOSChromePasswordReuseManagerFactory::GetForBrowserState(
      bridge_.browserState);
}

const GURL& IOSChromePasswordReuseDetectionManagerClient::GetLastCommittedURL()
    const {
  return bridge_.lastCommittedURL;
}

autofill::LogManager*
IOSChromePasswordReuseDetectionManagerClient::GetLogManager() {
  return log_manager_.get();
}

safe_browsing::PasswordProtectionService*
IOSChromePasswordReuseDetectionManagerClient::GetPasswordProtectionService()
    const {
  return ChromePasswordProtectionServiceFactory::GetForBrowserState(
      bridge_.browserState);
}

bool IOSChromePasswordReuseDetectionManagerClient::IsHistorySyncAccountEmail(
    const std::string& username) {
  // Password reuse detection is tied to history sync.
  syncer::SyncService* sync_service =
      SyncServiceFactory::GetForBrowserState(bridge_.browserState);
  if (!sync_service || !sync_service->GetPreferredDataTypes().Has(
                           syncer::HISTORY_DELETE_DIRECTIVES)) {
    return false;
  }
  return password_manager::sync_util::IsSyncAccountEmail(
      username,
      IdentityManagerFactory::GetForBrowserState(bridge_.browserState),
      signin::ConsentLevel::kSignin);
}

bool IOSChromePasswordReuseDetectionManagerClient::
    IsPasswordFieldDetectedOnPage() {
  password_manager::PasswordManagerClient* password_manager_client =
      PasswordTabHelper::FromWebState(web_state())->GetPasswordManagerClient();
  if (!password_manager_client) {
    return false;
  }
  return password_manager_client->GetPasswordManager()
             ? password_manager_client->GetPasswordManager()
                   ->IsPasswordFieldDetectedOnPage()
             : false;
}

void IOSChromePasswordReuseDetectionManagerClient::CheckProtectedPasswordEntry(
    PasswordType password_type,
    const std::string& username,
    const std::vector<password_manager::MatchingReusedCredential>&
        matching_reused_credentials,
    bool password_field_exists,
    uint64_t reused_password_hash,
    const std::string& domain) {
  safe_browsing::PasswordProtectionService* service =
      GetPasswordProtectionService();
  if (service) {
    auto show_warning_callback =
        base::BindOnce(&IOSChromePasswordReuseDetectionManagerClient::
                           NotifyUserPasswordProtectionWarning,
                       weak_factory_.GetWeakPtr());
    service->MaybeStartProtectedPasswordEntryRequest(
        bridge_.webState, bridge_.webState->GetLastCommittedURL(), username,
        password_type, matching_reused_credentials, password_field_exists,
        std::move(show_warning_callback));
  }
}

void IOSChromePasswordReuseDetectionManagerClient::
    NotifyUserPasswordProtectionWarning(
        const std::u16string& warning_text,
        base::OnceCallback<void(safe_browsing::WarningAction)> callback) {
  __block auto block_callback = std::move(callback);
  [bridge_
      showPasswordProtectionWarning:base::SysUTF16ToNSString(warning_text)
                         completion:^(safe_browsing::WarningAction action) {
                           std::move(block_callback).Run(action);
                         }];
}

void IOSChromePasswordReuseDetectionManagerClient::
    MaybeLogPasswordReuseDetectedEvent() {
  safe_browsing::PasswordProtectionService* service =
      GetPasswordProtectionService();
  if (service) {
    service->MaybeLogPasswordReuseDetectedEvent(bridge_.webState);
  }
}

void IOSChromePasswordReuseDetectionManagerClient::DidFinishNavigation(
    web::WebState* web_state,
    web::NavigationContext* navigation_context) {
  password_reuse_detection_manager_.DidNavigateMainFrame(GetLastCommittedURL());
}

void IOSChromePasswordReuseDetectionManagerClient::OnKeyPressed(
    const std::string text) {
  password_reuse_detection_manager_.OnKeyPressedCommitted(
      base::UTF8ToUTF16(text));
}

void IOSChromePasswordReuseDetectionManagerClient::OnPaste(
    const std::string text) {
  password_reuse_detection_manager_.OnPaste(base::UTF8ToUTF16(text));
}

web::WebState* IOSChromePasswordReuseDetectionManagerClient::web_state() const {
  return bridge_.webState;
}