services/mobileid/MobileIdentityCredentialsStore.jsm
author Dave Townsend <dtownsend@oxymoronical.com>
Thu, 06 Nov 2014 12:16:15 -0800
changeset 214476 8094e9f641e3940b9e45df2802625367ece3acd4
parent 192985 3bc782198bd7759291ca1b04e73d632ee2a364f6
permissions -rw-r--r--
Bug 1095024: Port bug 987849 to mochitest-jetpack. r=jmaher

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

this.EXPORTED_SYMBOLS = ["MobileIdentityCredentialsStore"];

const {classes: Cc, interfaces: Ci, utils: Cu} = Components;

Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
Cu.import("resource://gre/modules/MobileIdentityCommon.jsm");
Cu.import("resource://gre/modules/Promise.jsm");

const CREDENTIALS_DB_NAME     = "mobile-id-credentials";
const CREDENTIALS_DB_VERSION  = 1;
const CREDENTIALS_STORE_NAME  = "credentials-store";

this.MobileIdentityCredentialsStore = function() {
};

this.MobileIdentityCredentialsStore.prototype = {

  __proto__: IndexedDBHelper.prototype,

  init: function() {
    log.debug("MobileIdentityCredentialsStore init");
    this.initDBHelper(CREDENTIALS_DB_NAME,
                      CREDENTIALS_DB_VERSION,
                      [CREDENTIALS_STORE_NAME]);
  },

  upgradeSchema: function(aTransaction, aDb, aOldVersion, aNewVersion) {
    log.debug("upgradeSchema");
    /**
     * We will be storing objects like:
     *  {
     *    msisdn: <string> (key),
     *    iccId: <string> (index, optional),
     *    deviceIccIds: <array>,
     *    origin: <array> (index),
     *    msisdnSessionToken: <string>,
     *  }
     */
    let objectStore = aDb.createObjectStore(CREDENTIALS_STORE_NAME, {
      keyPath: "msisdn"
    });

    objectStore.createIndex("iccId", "iccId", { unique: true });
    objectStore.createIndex("origin", "origin", { unique: true, multiEntry: true });
  },

  add: function(aIccId, aMsisdn, aOrigin, aSessionToken, aDeviceIccIds) {
    log.debug("put " + aIccId + ", " + aMsisdn + ", " + aOrigin + ", " +
              aSessionToken + ", " + aDeviceIccIds);
    if (!aOrigin || !aSessionToken) {
      return Promise.reject(ERROR_INTERNAL_DB_ERROR);
    }

    let deferred = Promise.defer();

    // We first try get an existing record for the given MSISDN.
    this.newTxn(
      "readwrite",
      CREDENTIALS_STORE_NAME,
      (aTxn, aStore) => {
        let range = IDBKeyRange.only(aMsisdn);
        let cursorReq = aStore.openCursor(range);
        cursorReq.onsuccess = function(aEvent) {
          let cursor = aEvent.target.result;
          let record;
          // If we already have a record of this MSISDN, we add the origin to
          // the list of allowed origins.
          if (cursor && cursor.value) {
            record = cursor.value;
            if (record.origin.indexOf(aOrigin) == -1) {
              record.origin.push(aOrigin);
            }
            cursor.update(record);
          } else {
            // Otherwise, we store a new record.
            record = {
              iccId: aIccId,
              msisdn: aMsisdn,
              origin: [aOrigin],
              sessionToken: aSessionToken,
              deviceIccIds: aDeviceIccIds
            };
            aStore.add(record);
          }
          deferred.resolve();
        };
        cursorReq.onerror = function(aEvent) {
          log.error(aEvent.target.error);
          deferred.reject(ERROR_INTERNAL_DB_ERROR);
        };
      }, null, deferred.reject);

    return deferred.promise;
  },

  getByMsisdn: function(aMsisdn) {
    log.debug("getByMsisdn " + aMsisdn);
    if (!aMsisdn) {
      return Promise.resolve(null);
    }

    let deferred = Promise.defer();
    this.newTxn(
      "readonly",
      CREDENTIALS_STORE_NAME,
      (aTxn, aStore) => {
        aStore.get(aMsisdn).onsuccess = function(aEvent) {
          aTxn.result = aEvent.target.result;
        };
      },
      function(result) {
        deferred.resolve(result);
      },
      deferred.reject
    );
    return deferred.promise;
  },

  getByIndex: function(aIndex, aValue) {
    log.debug("getByIndex " + aIndex + ", " + aValue);
    if (!aValue || !aIndex) {
      return Promise.resolve(null);
    }

    let deferred = Promise.defer();
    this.newTxn(
      "readonly",
      CREDENTIALS_STORE_NAME,
      (aTxn, aStore) => {
        let index = aStore.index(aIndex);
        index.get(aValue).onsuccess = function(aEvent) {
          aTxn.result = aEvent.target.result;
        };
      },
      function(result) {
        deferred.resolve(result);
      },
      deferred.reject
    );
    return deferred.promise;
  },

  getByOrigin: function(aOrigin) {
    return this.getByIndex("origin", aOrigin);
  },

  getByIccId: function(aIccId) {
    return this.getByIndex("iccId", aIccId);
  },

  delete: function(aMsisdn) {
    log.debug("delete " + aMsisdn);
    if (!aMsisdn) {
      return Promise.resolve();
    }

    let deferred = Promise.defer();
    this.newTxn(
      "readwrite",
      CREDENTIALS_STORE_NAME,
      (aTxn, aStore) => {
        aStore.delete(aMsisdn);
      },
      deferred.resolve,
      deferred.reject
    );
    return deferred.promise;
  },

  removeValue: function(aMsisdn, aKey, aValue) {
    log.debug("Removing " + aKey + " with value " + aValue);
    if (!aMsisdn || !aKey) {
      return Promise.reject();
    }

    let deferred = Promise.defer();
    this.newTxn(
      "readwrite",
      CREDENTIALS_STORE_NAME,
      (aTxn, aStore) => {
        let range = IDBKeyRange.only(aMsisdn);
        let cursorReq = aStore.openCursor(range);
        cursorReq.onsuccess = function(aEvent) {
          let cursor = aEvent.target.result;
          let record;
          if (!cursor || !cursor.value) {
            return Promise.resolve();
          }
          record = cursor.value;
          if (!record[aKey]) {
            return Promise.reject();
          }
          if (aValue) {
            let index = record[aKey].indexOf(aValue);
            if (index != -1) {
              record[aKey].splice(index, 1);
            }
          } else {
            record[aKey] = undefined;
          }
          log.debug("Removal done ${}", record);
          cursor.update(record);
          deferred.resolve();
        };
        cursorReq.onerror = function(aEvent) {
          log.error(aEvent.target.error);
          deferred.reject(ERROR_INTERNAL_DB_ERROR);
        };
      }, null, deferred.reject);

    return deferred.promise;
  },

  removeOrigin: function(aMsisdn, aOrigin) {
    log.debug("removeOrigin " + aMsisdn + " " + aOrigin);
    return this.removeValue(aMsisdn, "origin", aOrigin);
  },

  setDeviceIccIds: function(aMsisdn, aDeviceIccIds) {
    log.debug("Setting icc ids " + aDeviceIccIds + " for " + aMsisdn);
    if (!aMsisdn) {
      return Promise.reject();
    }

    let deferred = Promise.defer();
    this.newTxn(
      "readwrite",
      CREDENTIALS_STORE_NAME,
      (aTxn, aStore) => {
        let range = IDBKeyRange.only(aMsisdn);
        let cursorReq = aStore.openCursor(range);
        cursorReq.onsuccess = function(aEvent) {
          let cursor = aEvent.target.result;
          let record;
          if (!cursor || !cursor.value) {
            return Promise.resolve();
          }
          record = cursor.value;
          record.deviceIccIds = aDeviceIccIds;
          cursor.update(record);
          deferred.resolve();
        };
        cursorReq.onerror = function(aEvent) {
          log.error(aEvent.target.error);
          deferred.reject(ERROR_INTERNAL_DB_ERROR);
        };
      }, null, deferred.reject);

    return deferred.promise;
  }
};