text: // Copyright 2020 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 CHROME_BROWSER_PASSWORD_CHECK_ANDROID_PASSWORD_CHECK_MANAGER_H_
  #define CHROME_BROWSER_PASSWORD_CHECK_ANDROID_PASSWORD_CHECK_MANAGER_H_
  
  #include "base/memory/scoped_refptr.h"
  #include "base/scoped_observation.h"
  #include "base/strings/string_piece_forward.h"
  #include "chrome/browser/password_check/android/password_check_ui_status.h"
  #include "chrome/browser/password_entry_edit/android/credential_edit_bridge.h"
  #include "chrome/browser/password_manager/bulk_leak_check_service_factory.h"
  #include "chrome/browser/password_manager/password_scripts_fetcher_factory.h"
  #include "chrome/browser/password_manager/password_store_factory.h"
  #include "chrome/browser/profiles/profile.h"
  #include "components/password_manager/core/browser/bulk_leak_check_service.h"
  #include "components/password_manager/core/browser/bulk_leak_check_service_interface.h"
  #include "components/password_manager/core/browser/password_scripts_fetcher.h"
  #include "components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.h"
  #include "components/password_manager/core/browser/ui/insecure_credentials_manager.h"
  #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
  #include "third_party/abseil-cpp/absl/types/optional.h"
  
  class PasswordCheckManager
      : public password_manager::SavedPasswordsPresenter::Observer,
        public password_manager::InsecureCredentialsManager::Observer,
        public password_manager::BulkLeakCheckServiceInterface::Observer {
   public:
    class Observer {
     public:
      virtual ~Observer() = default;
      virtual void OnSavedPasswordsFetched(int count) = 0;
      virtual void OnCompromisedCredentialsChanged(int count) = 0;
      virtual void OnPasswordCheckStatusChanged(
          password_manager::PasswordCheckUIStatus status) = 0;
      virtual void OnPasswordCheckProgressChanged(int already_processed,
                                                  int remaining_in_queue) = 0;
    };
  
    struct CompromisedCredentialForUI : password_manager::CredentialWithPassword {
      explicit CompromisedCredentialForUI(
          const password_manager::CredentialWithPassword& credential);
  
      CompromisedCredentialForUI(const CompromisedCredentialForUI& other);
      CompromisedCredentialForUI(CompromisedCredentialForUI&& other);
      CompromisedCredentialForUI& operator=(
          const CompromisedCredentialForUI& other);
      CompromisedCredentialForUI& operator=(CompromisedCredentialForUI&& other);
      ~CompromisedCredentialForUI();
  
      std::u16string display_username;
      std::u16string display_origin;
      std::string package_name;
      std::string change_password_url;
      bool has_startable_script = false;
      bool has_auto_change_button = false;
    };
  
    // `observer` must outlive `this`.
    PasswordCheckManager(Profile* profile, Observer* observer);
    ~PasswordCheckManager() override;
  
    // Requests to start the password check.
    void StartCheck();
  
    // Stops a running check.
    void StopCheck();
  
    // Called by java to retireve the timestamp of the last password check.
    base::Time GetLastCheckTimestamp();
  
    // Called by java to retrieve the number of compromised credentials. If the
    // credentials haven't been fetched yet, this will return 0.
    int GetCompromisedCredentialsCount() const;
  
    // Called by java to retrieve the number of saved passwords.
    // If the saved passwords haven't been fetched yet, this will return 0.
    int GetSavedPasswordsCount() const;
  
    // Called by java to retrieve the compromised credentials.
    std::vector<CompromisedCredentialForUI> GetCompromisedCredentials() const;
  
    // Called by java to update the given compromised `credential` and set its
    // password to `new_password`.
    void UpdateCredential(const password_manager::CredentialView& credential,
                          base::StringPiece new_password);
  
    // Called by java to launch the edit credential UI for `credential`.
    void OnEditCredential(
        const password_manager::CredentialView& credential,
        const base::android::JavaParamRef<jobject>& context,
        const base::android::JavaParamRef<jobject>& settings_launcher);
  
    // Called by java to remove the given compromised `credential` and trigger a
    // UI update on completion.
    void RemoveCredential(const password_manager::CredentialView& credential);
  
    // Invokes `PasswordScriptsFetcher`'s scripts refreshment.
    void RefreshScripts();
  
    // Returns if scripts refreshment is finished.
    bool AreScriptsRefreshed() const;
  
    // Not copyable or movable
    PasswordCheckManager(const PasswordCheckManager&) = delete;
    PasswordCheckManager& operator=(const PasswordCheckManager&) = delete;
    PasswordCheckManager(PasswordCheckManager&&) = delete;
    PasswordCheckManager& operator=(PasswordCheckManager&&) = delete;
  
   private:
    // Helps to track which preconditions are fulfilled.
    enum CheckPreconditions {
      // No preconditions have been fulfilled.
      kNone = 0,
      // Saved passwords can be accessed.
      kSavedPasswordsAvailable = 1 << 0,
      // Sites with available Scripts are fetched.
      kScriptsCachePrewarmed = 1 << 1,
      // Already known compromised credentials were loaded.
      kKnownCredentialsFetched = 1 << 2,
      // All preconditions have been fulfilled.
      kAll = kSavedPasswordsAvailable | kScriptsCachePrewarmed |
             kKnownCredentialsFetched,
    };
  
    // Class remembering the state required to update the progress of an ongoing
    // Password Check.
    class PasswordCheckProgress {
     public:
      PasswordCheckProgress();
      ~PasswordCheckProgress();
  
      size_t remaining_in_queue() const { return remaining_in_queue_; }
      size_t already_processed() const { return already_processed_; }
  
      // Increments the counts corresponding to `password`. Intended to be called
      // for each credential that is passed to the bulk check.
      void IncrementCounts(const password_manager::PasswordForm& password);
  
      // Updates the counts after a `credential` has been processed by the bulk
      // check.
      void OnProcessed(const password_manager::LeakCheckCredential& credential);
  
     private:
      // Count variables needed to correctly show the progress of the check to the
      // user. `already_processed_` contains the number of credentials that have
      // been checked already, while `remaining_in_queue_` remembers how many
      // passwords still need to be checked.
      // Since the bulk leak check tries to be as efficient as possible, it
      // performs a deduplication step before starting to check passwords. In this
      // step it canonicalizes each credential, and only processes the
      // combinations that are unique. Since this number likely does not match the
      // total number of saved passwords, we remember in `counts_` how many saved
      // passwords a given canonicalized credential corresponds to.
      size_t already_processed_ = 0;
      size_t remaining_in_queue_ = 0;
      std::map<password_manager::CanonicalizedCredential, size_t> counts_;
    };
  
    // password_manager::SavedPasswordsPresenter::Observer:
    void OnSavedPasswordsChanged(
        password_manager::SavedPasswordsPresenter::SavedPasswordsView passwords)
        override;
  
    // InsecureCredentialsManager::Observer
    void OnInsecureCredentialsChanged(
        password_manager::InsecureCredentialsManager::CredentialsView credentials)
        override;
  
    // BulkLeakCheckServiceInterface::Observer
    void OnStateChanged(
        password_manager::BulkLeakCheckServiceInterface::State state) override;
    void OnCredentialDone(const password_manager::LeakCheckCredential& credential,
                          password_manager::IsLeaked is_leaked) override;
    void OnBulkCheckServiceShutDown() override;
  
    // Turns a `CredentialWithPassword` into a `CompromisedCredentialForUI`,
    // getting suitable strings for all display elements (e.g. url, app name,
    // app package, username, etc.).
    CompromisedCredentialForUI MakeUICredential(
        const password_manager::CredentialWithPassword& credential) const;
  
    // Converts the state retrieved from the check service into a state that
    // can be used by the UI to display appropriate messages.
    password_manager::PasswordCheckUIStatus GetUIStatus(
        password_manager::BulkLeakCheckServiceInterface::State state) const;
  
    // Returns true if the user has their passwords available in their Google
    // Account. Used to determine whether the user could use the password check
    // in the account if the quota limit was reached.
    bool CanUseAccountCheck() const;
  
    // Returns true if there is auto change button for given credential.
    bool IsEligibleForAutoChange(
        const password_manager::CredentialWithPassword& credenital) const;
  
    // Returns true if the password scripts fetching (kPasswordScriptsFetching) is
    // enabled. To have precise metrics about user actions on credentials with
    // scripts, scripts are fetched only for the users who can start a script,
    // i.e. sync users.
    bool ShouldFetchPasswordScripts() const;
  
    // Callback when PasswordScriptsFetcher's cache has been warmed up.
    void OnScriptsFetched();
  
    // Returns true if the passed |condition| was already met.
    bool IsPreconditionFulfilled(CheckPreconditions condition) const;
  
    // Marks the passed |condition| as fulfilled and runs a check if applicable.
    void FulfillPrecondition(CheckPreconditions condition);
  
    // Resets the passed |condition| so that it's expected to happen again.
    void ResetPrecondition(CheckPreconditions condition);
  
    // Destroys the edit ui bridge.
    void OnEditUIDismissed();
  
    // Obsever being notified of UI-relevant events.
    // It must outlive `this`.
    Observer* observer_ = nullptr;
  
    // The profile for which the passwords are checked.
    Profile* profile_ = nullptr;
  
    // Object storing the progress of a running password check.
    std::unique_ptr<PasswordCheckProgress> progress_;
  
    // Handle to the password store, powering both `saved_passwords_presenter_`
    // and `insecure_credentials_manager_`.
    scoped_refptr<password_manager::PasswordStoreInterface> password_store_ =
        PasswordStoreFactory::GetForProfile(profile_,
                                            ServiceAccessType::EXPLICIT_ACCESS);
  
    // Used to check whether autofill assistant scripts are available for
    // the specified domain.
    password_manager::PasswordScriptsFetcher* password_script_fetcher_ =
        PasswordScriptsFetcherFactory::GetInstance()->GetForBrowserContext(
            profile_);
  
    // Used by `insecure_credentials_manager_` to obtain the list of saved
    // passwords.
    password_manager::SavedPasswordsPresenter saved_passwords_presenter_{
        password_store_};
  
    // Used to obtain the list of insecure credentials.
    password_manager::InsecureCredentialsManager insecure_credentials_manager_{
        &saved_passwords_presenter_, password_store_};
  
    // Adapter used to start, monitor and stop a bulk leak check.
    password_manager::BulkLeakCheckServiceAdapter
        bulk_leak_check_service_adapter_{
            &saved_passwords_presenter_,
            BulkLeakCheckServiceFactory::GetForProfile(profile_),
            profile_->GetPrefs()};
  
    // The check can be run only of this is CheckPreconditions::kAll;
    int fulfilled_preconditions_ = CheckPreconditions::kNone;
  
    // Whether the check start was requested.
    bool was_start_requested_ = false;
  
    // Whether a check is currently running.
    bool is_check_running_ = false;
  
    // Latest number of changed compromised credentials while script fetching
    // was running. If `credentials_count_to_notify_` has value, after scripts are
    // fetched `onCompromisedCredentials` should be called.
    absl::optional<size_t> credentials_count_to_notify_;
  
    // Used to open the view/edit/delete UI.
    std::unique_ptr<CredentialEditBridge> credential_edit_bridge_;
  
    // A scoped observer for `saved_passwords_presenter_`.
    base::ScopedObservation<password_manager::SavedPasswordsPresenter,
                            password_manager::SavedPasswordsPresenter::Observer>
        observed_saved_passwords_presenter_{this};
  
    // A scoped observer for `insecure_credentials_manager_`.
    base::ScopedObservation<
        password_manager::InsecureCredentialsManager,
        password_manager::InsecureCredentialsManager::Observer>
        observed_insecure_credentials_manager_{this};
  
    // A scoped observer for the BulkLeakCheckService.
    base::ScopedObservation<
        password_manager::BulkLeakCheckServiceInterface,
        password_manager::BulkLeakCheckServiceInterface::Observer>
        observed_bulk_leak_check_service_{this};
  };
  
  #endif  // CHROME_BROWSER_PASSWORD_CHECK_ANDROID_PASSWORD_CHECK_MANAGER_H_
  