Source: encrypt/indexeddb-consumer-db.js

/**
 * Copyright (C) 2015-2016 Regents of the University of California.
 * @author: Jeff Thompson <[email protected]>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * A copy of the GNU Lesser General Public License is in the file COPYING.
 */

// Don't require modules since this is meant for the browser, not Node.js.

/**
 * IndexedDbConsumerDb extends ConsumerDb to implement the storage of decryption
 * keys for the consumer using the browser's IndexedDB service.
 * Create an IndexedDbConsumerDb to use the given IndexedDB database name.
 * @param {string} databaseName IndexedDB database name.
 * @note This class is an experimental feature. The API may change.
 * @constructor
 */
var IndexedDbConsumerDb = function IndexedDbConsumerDb(databaseName)
{
  ConsumerDb.call(this);

  this.database = new Dexie(databaseName);
  this.database.version(1).stores({
    // "keyName" is the key name URI // string
    //   (Note: In SQLite3, the key name is the TLV encoded bytes, but we can't
    //   index on a byte array in IndexedDb.)
    // "key" is the key bytes // Uint8Array
    decryptionKeys: "keyName"
  });
  this.database.open();
};

IndexedDbConsumerDb.prototype = new ConsumerDb();
IndexedDbConsumerDb.prototype.name = "IndexedDbConsumerDb";

/**
 * Get the key with keyName from the database.
 * @param {Name} keyName The key name.
 * @param {boolean} useSync (optional) If true then return a rejected promise
 * since this only supports async code.
 * @return {Promise} A promise that returns a Blob with the encoded key (or an
 * isNull Blob if cannot find the key with keyName), or that is
 * rejected with ConsumerDb.Error for a database error.
 */
IndexedDbConsumerDb.prototype.getKeyPromise = function(keyName, useSync)
{
  if (useSync)
    return Promise.reject(new ConsumerDb.Error(new Error
      ("IndexedDbConsumerDb.getKeyPromise is only supported for async")));

  return this.database.decryptionKeys.get(keyName.toUri())
  .then(function(decryptionKeysEntry) {
    if (decryptionKeysEntry)
      return Promise.resolve(new Blob(decryptionKeysEntry.key));
    else
      return Promise.resolve(new Blob());
  })
  .catch(function(ex) {
    return Promise.reject(new ConsumerDb.Error(new Error
      ("IndexedDbConsumerDb.getKeyPromise: Error: " + ex)));
  });
};

/**
 * Add the key with keyName and keyBlob to the database.
 * @param {Name} keyName The key name.
 * @param {Blob} keyBlob The encoded key.
 * @param {boolean} useSync (optional) If true then return a rejected promise
 * since this only supports async code.
 * @return {Promise} A promise that fulfills when the key is added, or that
 * is rejected with ConsumerDb.Error if a key with the same keyName already
 * exists, or other database error.
 */
IndexedDbConsumerDb.prototype.addKeyPromise = function(keyName, keyBlob, useSync)
{
  if (useSync)
    return Promise.reject(new ConsumerDb.Error(new Error
      ("IndexedDbConsumerDb.addKeyPromise is only supported for async")));

  // Add rejects if the primary key already exists.
  return this.database.decryptionKeys.add
    ({ keyName: keyName.toUri(), key: keyBlob.buf() })
  .catch(function(ex) {
    return Promise.reject(new ConsumerDb.Error(new Error
      ("IndexedDbConsumerDb.addKeyPromise: Error: " + ex)));
  });
};

/**
 * Delete the key with keyName from the database. If there is no key with
 * keyName, do nothing.
 * @param {Name} keyName The key name.
 * @param {boolean} useSync (optional) If true then return a rejected promise
 * since this only supports async code.
 * @return {Promise} A promise that fulfills when the key is deleted (or there
 * is no such key), or that is rejected with ConsumerDb.Error for a database
 * error.
 */
IndexedDbConsumerDb.prototype.deleteKeyPromise = function(keyName, useSync)
{
  if (useSync)
    return Promise.reject(new ConsumerDb.Error(new Error
      ("IndexedDbConsumerDb.deleteKeyPromise is only supported for async")));

  return this.database.decryptionKeys.delete(keyName.toUri())
  .catch(function(ex) {
    return Promise.reject(new ConsumerDb.Error(new Error
      ("IndexedDbConsumerDb.deleteKeyPromise: Error: " + ex)));
  });
};