chromium/extensions/browser/api/lock_screen_data/lock_screen_item_storage.h

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef EXTENSIONS_BROWSER_API_LOCK_SCREEN_DATA_LOCK_SCREEN_ITEM_STORAGE_H_
#define EXTENSIONS_BROWSER_API_LOCK_SCREEN_DATA_LOCK_SCREEN_ITEM_STORAGE_H_

#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>

#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/task/sequenced_task_runner.h"
#include "base/values.h"
#include "extensions/browser/api/lock_screen_data/data_item.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/extension_id.h"

class PrefRegistrySimple;
class PrefService;

namespace base {
class TickClock;
}

namespace content {
class BrowserContext;
}

namespace extensions {

class Extension;
class LocalValueStoreCache;

namespace lock_screen_data {

class DataItem;
class LockScreenValueStoreMigrator;
enum class OperationResult;

// Keeps track of lock screen data items created by extensions.
// Note that only a single instance is allowed per process - once created, the
// instance can be retrieved using LockScreenItemStorage::GetIfAllowed().
class LockScreenItemStorage : public ExtensionRegistryObserver {
 public:
  using CreateCallback =
      base::OnceCallback<void(OperationResult result, const DataItem* item)>;
  using DataItemListCallback =
      base::OnceCallback<void(const std::vector<const DataItem*>& items)>;
  using WriteCallback = DataItem::WriteCallback;
  using ReadCallback = DataItem::ReadCallback;

  static void RegisterLocalState(PrefRegistrySimple* registry);

  // Gets the global LockScreenItemStorage instance if lock screen item storage
  // can be used from the browser context.
  // |context| - Context from which the item storage is needed, if the context
  // is not allowed to use LockScreenItemStorage, this will return nullptr.
  // LockScreenItemStorage can be only used from:
  //  *  lock screen browser context while session state is set to locked
  //  *  from the browser process passed to the LockScreenItemStorage ctor when
  //     the session state is set to unlocked.
  static LockScreenItemStorage* GetIfAllowed(content::BrowserContext* context);

  // Creates a LockScreenItemStorage instance. Note that only one
  // LockScreenItemStorage can be present at a time - the constructor will set a
  // global reference to the created object that can be retrieved using
  // LockScreenItemStorage::GetIfAllowed (the reference will be reset when the
  // instance goes out of scoped, i.e. in ~LockScreenItemStorage).
  //
  // |context| - primary user context, only apps from that context should be
  //     able to use the storage.
  // |local_state| - Local state preference.
  // |crypto_key| - Symmetric key that should be used to encrypt data content
  //     when it's persisted on the disk.
  // |deprecated_storage_root| - The directory on the disk to which data item
  //     content used to be persisted, and from which data items should be
  //     migrated.
  // |storage_root| - Directory on the disk to which data item content should be
  //     persisted.
  LockScreenItemStorage(content::BrowserContext* context,
                        PrefService* local_state,
                        const std::string& crypto_key,
                        const base::FilePath& deprecated_storage_root,
                        const base::FilePath& storage_root);

  LockScreenItemStorage(const LockScreenItemStorage&) = delete;
  LockScreenItemStorage& operator=(const LockScreenItemStorage&) = delete;

  ~LockScreenItemStorage() override;

  // Updates the LockScreenItemStorage's view of whether the user session is
  // locked.
  void SetSessionLocked(bool session_locked);

  // Creates a new data item for the extension.
  void CreateItem(const ExtensionId& extension_id, CreateCallback callback);

  // Returns all existing data items associated with the extension.
  void GetAllForExtension(const ExtensionId& extension_id,
                          DataItemListCallback callback);

  // Updates the content of the item identified by |item_id| that is associated
  // with the provided extension.
  void SetItemContent(const ExtensionId& extension_id,
                      const std::string& item_id,
                      const std::vector<char>& data,
                      WriteCallback callback);

  // Retrieves the content of the item identified by |item_id| that is
  // associated with the provided extension.
  void GetItemContent(const ExtensionId& extension_id,
                      const std::string& item_id,
                      ReadCallback callback);

  // Deletes the data item associated with the extension.
  void DeleteItem(const ExtensionId& extension_id,
                  const std::string& item_id,
                  WriteCallback callback);

  // extensions::ExtensionRegistryObserver:
  void OnExtensionUninstalled(content::BrowserContext* browser_context,
                              const Extension* extension,
                              UninstallReason reason) override;

  // Used in tests to inject fake/stub data value store.
  using ValueStoreCacheFactoryCallback =
      base::RepeatingCallback<std::unique_ptr<LocalValueStoreCache>(
          const base::FilePath& root)>;
  static void SetValueStoreCacheFactoryForTesting(
      ValueStoreCacheFactoryCallback* factory_callback);

  // Used in tests to inject fake value store migrator implementation.
  using ValueStoreMigratorFactoryCallback =
      base::RepeatingCallback<std::unique_ptr<LockScreenValueStoreMigrator>()>;
  static void SetValueStoreMigratorFactoryForTesting(
      ValueStoreMigratorFactoryCallback* factory_callback);

  // Used in tests to inject fake data items implementations.
  using ItemFactoryCallback = base::RepeatingCallback<std::unique_ptr<DataItem>(
      const std::string& id,
      const ExtensionId& extension_id,
      const std::string& crypto_key)>;
  using RegisteredItemsGetter = base::RepeatingCallback<void(
      const ExtensionId& extension_id,
      DataItem::RegisteredValuesCallback callback)>;
  using ItemStoreDeleter =
      base::RepeatingCallback<void(const ExtensionId& extension_id,
                                   base::OnceClosure callback)>;
  static void SetItemProvidersForTesting(
      RegisteredItemsGetter* item_fetch_callback,
      ItemFactoryCallback* factory_callback,
      ItemStoreDeleter* deleter_callback);

  const std::string& crypto_key_for_testing() const { return crypto_key_; }

 private:
  // Maps a data item ID to the data item instance.
  using DataItemMap =
      std::unordered_map<std::string, std::unique_ptr<DataItem>>;

  // Contains information about cached data item information for an extension -
  // in particular, the list of existing data items.
  struct CachedExtensionData {
    enum class State {
      // The set of registered data items has not been requested.
      kInitial,
      // The initial set of registered data items is being loaded.
      kLoading,
      // The set of registered data items has been loaded; the cached list of
      // items should be up to date.
      kLoaded,
      // The data for the extension is being removed.
      kRemoving,
    };

    CachedExtensionData();
    ~CachedExtensionData();

    State state = State::kInitial;
    // The set of registered data items.
    DataItemMap data_items;
    // When the initial data item list is being loaded, contains the callback
    // waiting for the load to finish. When the initial data item set is loaded,
    // all of the callbacks in this list will be run.
    std::vector<base::OnceClosure> load_callbacks;
  };

  // Maps an extension ID to data items associated with the extension.
  using ExtensionDataMap = std::unordered_map<std::string, CachedExtensionData>;

  // The session state as seen by the LockScreenItemStorage.
  enum class SessionLockedState { kUnknown, kLocked, kNotLocked };

  // Passed to the value store migrator as a callback that is run when an
  // extension data migration from deprecated value store is completed.
  // |extension_id| identifies the extension whose data is migrated.
  void OnItemsMigratedForExtension(const ExtensionId& extension_id);

  // Whether the context is allowed to use LockScreenItemStorage.
  // Result depends on the current LockScreenItemStorage state - see
  // |LockScreenItemStorage::GetIfAllowed|.
  bool IsContextAllowed(content::BrowserContext* context);

  // Implementations for data item management methods - called when the data
  // cache for the associated extension was initialized:
  void CreateItemImpl(const ExtensionId& extension_id, CreateCallback callback);
  void GetAllForExtensionImpl(const ExtensionId& extension_id,
                              DataItemListCallback callback);
  void SetItemContentImpl(const ExtensionId& extension_id,
                          const std::string& item_id,
                          const std::vector<char>& data,
                          WriteCallback callback);
  void GetItemContentImpl(const ExtensionId& extension_id,
                          const std::string& item_id,
                          ReadCallback callback);
  void DeleteItemImpl(const ExtensionId& extension_id,
                      const std::string& item_id,
                      WriteCallback callback);

  // Defers |callback| until the cached data item set is loaded for the
  // extension. If the data is already loaded, |callback| will be run
  // immediately.
  void EnsureCacheForExtensionLoaded(const ExtensionId& extension_id,
                                     base::OnceClosure callback);

  // Callback for loading initial set of known data items for an extension.
  void OnGotExtensionItems(const ExtensionId& extension_id,
                           const base::TimeTicks& start_time,
                           OperationResult result,
                           base::Value::Dict items);

  // Callback for registration operation of a newly created data item - it adds
  // the item to the cached data item state, and invoked the callback.
  void OnItemRegistered(std::unique_ptr<DataItem> item,
                        const ExtensionId& extension_id,
                        const base::TimeTicks& start_time,
                        CreateCallback callback,
                        OperationResult result);

  // Callback for item deletion operation. It removes the item from the cached
  // state and invokes the callback with the operation result.
  void OnItemDeleted(const ExtensionId& extension_id,
                     const std::string& item_id,
                     const base::TimeTicks& start_time,
                     WriteCallback callback,
                     OperationResult result);

  // Finds a data item associated with the extension.
  DataItem* FindItem(const ExtensionId& extension_id,
                     const std::string& item_id);

  // Gets the set of extensions that are known to have registered data items -
  // this information is kept in the local state so it can be used to
  // synchronously determine the set of extensions that should be sent a
  // DataItemsAvailable event, and to determine the set of extensions whose data
  // should be deleted (for example if they are not installed anymore).
  std::set<std::string> GetExtensionsWithDataItems(bool include_empty);

  // Gets the set of extensions whose data should be migrated from the
  // deprecated value store.
  // This set is inferred from the local state prefs - extensions whose
  // information in the local state prefs is saved as an integer value have data
  // items saved in the deprecated value store, and should thus be migrated.
  // Once the data migration is complete, the local state prefs for the
  // extension are updated to a dictionary with storage_version key set to the
  // current value store version.
  std::set<ExtensionId> GetExtensionsToMigrate();

  // Deletes data item data for all extensions that have existing data items,
  // but are not currently installed.
  void ClearUninstalledAppData();

  // Clears all data items associated with the extension.
  void ClearExtensionData(const std::string& id);

  // Removes local state entry for the extension.
  void RemoveExtensionFromLocalState(const std::string& id);

  // Runs callback queued up by |EnsureCacheForExtensionLoaded|.
  // |cache_data| - the per extension cache data that contains callback to be
  // run.
  void RunExtensionDataLoadCallbacks(CachedExtensionData* cache_data);

  raw_ptr<content::BrowserContext> context_;

  // The user associated with the primary profile.
  const std::string user_id_;
  const std::string crypto_key_;
  raw_ptr<PrefService> local_state_;

  raw_ptr<const base::TickClock> tick_clock_;

  SessionLockedState session_locked_state_ = SessionLockedState::kUnknown;

  base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
      extension_registry_observation_{this};

  // The deprecated (shared) lock screen data value store cache. Items in this
  // value store should be migrated to |value_store_cache_|.
  // Expected to be used only on |task_runner_|.
  std::unique_ptr<LocalValueStoreCache> deprecated_value_store_cache_;
  // Value store that contains extensions' persisted lock screen item data.
  // Expected to be used only on |task_runner_|.
  std::unique_ptr<LocalValueStoreCache> value_store_cache_;

  // Used to migrate lock screen data items from deprecated value store to the
  // currently active version of the lock screen data item value store.
  std::unique_ptr<LockScreenValueStoreMigrator> storage_migrator_;

  // Mapping containing all known data items for extensions.
  ExtensionDataMap data_item_cache_;

  scoped_refptr<base::SequencedTaskRunner> task_runner_;

  base::WeakPtrFactory<LockScreenItemStorage> weak_ptr_factory_{this};
};

}  // namespace lock_screen_data
}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_API_LOCK_SCREEN_DATA_LOCK_SCREEN_ITEM_STORAGE_H_