// 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.

#ifndef COMPONENTS_WEBAUTHN_CORE_BROWSER_PASSKEY_MODEL_H_
#define COMPONENTS_WEBAUTHN_CORE_BROWSER_PASSKEY_MODEL_H_

#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
#include "base/types/strong_alias.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/webauthn/core/browser/passkey_model_change.h"

namespace base {
class Location;
}

namespace sync_pb {
class WebauthnCredentialSpecifics;
}

namespace syncer {
class DataTypeControllerDelegate;
}

namespace webauthn {

// PasskeyModel provides access to passkeys, which are represented as
// WebauthnCredentialSpecifics in Sync.
//
// By design, non-syncing passkeys are not supported (unlike passwords e.g.,
// which can be created on the local device only, without uploading them to
// Sync).
//
// Aside from Sync passkeys, there might be other WebAuthn platform credentials
// on the device. E.g., non-passkey credentials in the browser-provided
// authenticators on CrOS (u2fd) and macOS (//device/fido/mac); or platform
// credentials owned by Windows Hello or iCloud Keychain. None of these are
// accessible through PasskeyModel.
class PasskeyModel : public KeyedService {
 public:
  // The following types specify different criteria for fetching passkeys.
  using AnyRp = base::StrongAlias<class AnyRp, std::monostate>;
  enum class ShadowedCredentials { kExclude, kInclude };

  class Observer : public base::CheckedObserver {
   public:
    // Notifies the observer that passkeys have changed, e.g. because a new one
    // was downloaded or deleted.
    virtual void OnPasskeysChanged(
        const std::vector<PasskeyModelChange>& changes) = 0;

    // Notifies the observer that the passkey model is shutting down.
    virtual void OnPasskeyModelShuttingDown() = 0;

    // Notifies the observer when the passkey model becomes ready.
    virtual void OnPasskeyModelIsReady(bool is_ready) = 0;
  };

  // Represents the WebAuthn PublicKeyCredentialUserEntity passed by the Relying
  // Party during registration.
  struct UserEntity {
    UserEntity(std::vector<uint8_t> id,
               std::string name,
               std::string display_name);
    ~UserEntity();
    std::vector<uint8_t> id;
    std::string name;
    std::string display_name;
  };

  // Attributes of a passkey that can be updated. If an attribute is set to
  // empty, then the entity attribute will also be set to empty.
  struct PasskeyUpdate {
    std::string user_name;
    std::string user_display_name;
  };

  // Hidden passkeys are permanently removed after `kHiddenPasskeyLifetime`.
  static constexpr base::TimeDelta kHiddenPasskeyLifetime = base::Days(30);

  virtual void AddObserver(Observer* observer) = 0;
  virtual void RemoveObserver(Observer* observer) = 0;

  // Returns the sync DataTypeControllerDelegate for the WEBAUTHN_CREDENTIAL
  // data type.
  virtual base::WeakPtr<syncer::DataTypeControllerDelegate>
  GetDataTypeControllerDelegate() = 0;

  // Returns true if the model has finished loading state from disk and is ready
  // to sync.
  virtual bool IsReady() const = 0;

  // Returns true if there are no passkeys in the account.
  virtual bool IsEmpty() const = 0;

  virtual base::flat_set<std::string> GetAllSyncIds() const = 0;

  // Returns the list of all passkeys matching the provided criteria:
  // - `rp_id`: Either a specific Relying Party ID or any.
  // - `shadowed_credentials`: Whether to include shadowed credentials.
  virtual std::vector<sync_pb::WebauthnCredentialSpecifics> GetPasskeys(
      std::variant<AnyRp, std::string_view> rp_id,
      ShadowedCredentials shadowed_credentials) const = 0;

  // Returns the passkey matching the provided criteria (or std::nullopt if
  // there isn't any):
  // - `rp_id`: Either a specific Relying Party ID or any.
  // - `credential_id`: Credential ID of the passkey.
  // - `shadowed_credentials`: Whether to include shadowed credentials.
  virtual std::optional<sync_pb::WebauthnCredentialSpecifics> GetPasskey(
      std::variant<AnyRp, std::string_view> rp_id,
      std::string_view credential_id,
      ShadowedCredentials shadowed_credentials) const = 0;

  // Deletes the passkey with the given `credential_id`. If the passkey is the
  // head of the shadow chain, then all passkeys for the same (user id, rp id)
  // are deleted as well. `location` is used for logging purposes and
  // investigations. Returns true if a passkey was found and deleted, false
  // otherwise.
  virtual bool DeletePasskey(const std::string& credential_id,
                             const base::Location& location) = 0;

  // Sets `hidden = true` and `hidden_time` for the passkey with the given
  // `credential_id`. Returns true if a passkey was found and updated, false
  // otherwise.
  virtual bool HidePasskey(const std::string& credential_id,
                           base::Time hidden_time) = 0;

  // Sets `hidden = false` for the passkey with the given `credential_id`
  // and clears its `hidden_time` property. Returns true if a
  // passkey was found and updated, false otherwise.
  virtual bool UnhidePasskey(const std::string& credential_id) = 0;

  // Deletes all passkeys.
  virtual void DeleteAllPasskeys() = 0;

  // Updates attributes of the passkey with the given `credential_id`. Returns
  // true if the credential was found and updated, false otherwise.
  // |updated_by_user| should be true if the user explicitly requested this
  // update, e.g. through the password manager. Passkeys updated by the user
  // will be permantently marked as such. Any further attempts to update the
  // passkey with |updated_by_user| set to |false| will be dropped.
  virtual bool UpdatePasskey(const std::string& credential_id,
                             PasskeyUpdate change,
                             bool updated_by_user) = 0;

  // Updates the `last_used_time_windows_epoch_micros` attribute of the
  // passkey with the given `credential_id`. Returns true if the credential was
  // found and updated, false otherwise.
  virtual bool UpdatePasskeyTimestamp(const std::string& credential_id,
                                      base::Time last_used_time) = 0;

  // Replaces the `encrypted` blob of an existing passkey.
  // Used when the enclave rotates ciphertext after a large blob
  // write. Returns false if the credential isn’t found.
  virtual bool UpdatePasskeyEncryptedBlob(
      const std::string& credential_id,
      const std::string& new_encrypted_blob) = 0;

  // Creates a passkey for the given RP and user and returns the new entity
  // specifics.
  //
  // The returned entity's `encrypted` field will contain an
  // `WebauthnCredentialSpecifics_EncryptedData` message, encrypted to
  // `trusted_vault_key`. `public_key_spki_der_out`, if non-null, will be set to
  // a DER-serialized X.509 SubjectPublicKeyInfo of the corresponding public
  // key.
  //
  // Any existing passkeys for the same RP and user ID are shadowed (i.e., added
  // to the new passkey's `newly_shadowed_credential_ids`), causing them not to
  // be enumerated by other methods in the model.
  virtual sync_pb::WebauthnCredentialSpecifics CreatePasskey(
      std::string_view rp_id,
      const UserEntity& user_entity,
      base::span<const uint8_t> trusted_vault_key,
      int32_t trusted_vault_key_version,
      std::vector<uint8_t>* public_key_spki_der_out) = 0;

  // Creates a passkey from a pre-constructed protobuf.
  // Existing passkeys for the same RP and user ID will be shadowed, but
  // otherwise all fields in the entity should be filled by the caller.
  virtual void CreatePasskey(sync_pb::WebauthnCredentialSpecifics& passkey) = 0;

  // Inserts the given passkey specifics into the model and returns the entity
  // sync_id.
  virtual std::string AddNewPasskeyForTesting(
      sync_pb::WebauthnCredentialSpecifics passkey) = 0;
};

}  // namespace webauthn

#endif  // COMPONENTS_WEBAUTHN_CORE_BROWSER_PASSKEY_MODEL_H_
