All files / lib/plugins/stores keychain-store.ts

96.97% Statements 32/33
50% Branches 4/8
87.5% Functions 7/8
95% Lines 19/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106    1x                                 20x 10x                                 10x 10x 5x     1x                       3x 3x         3x                             1x 1x 2x 1x                           1x 1x 1x   1x   1x              
"use strict";
 
import keytar from "keytar";
 
import { Store } from "./types";
 
type PropertyMeta = {
  schemaPath: string;
  variant?: string;
  storeOptions: { service: string; account: string };
};
 
/**
 * Given a list of strings, replace {variant} with the value of variant
 *
 * @param {String} variant - The name of the variant of the property.
 * @param {...String} values - store option values
 * @returns {Array} The service and account values.
 */
function replaceVariant(variant: string, ...values: string[]) {
  return values.map(value => value.replace("{variant}", variant));
}
 
/**
 * Extract store options from prop object.
 *
 * If store options are not provided then the prop name and variant are used as
 * the service and account values.
 *
 * @param {Object} prop - The property schema with name.
 * @param {String} prop.schemaPath - Dotted path to the property in the schema.
 * @param {String} prop.variant - The name of the variant of the property.
 * @param {Object} prop.storeOptions - Property options for the store.
 * @param {String} prop.storeOptions.service - The service name for the property.
 * @param {String} prop.storeOptions.account - The account name for the property.
 * @returns {Array} The service and account values.
 */
function extractProp({ schemaPath, variant = "", storeOptions }: PropertyMeta) {
  const { service = schemaPath, account = variant } = storeOptions || {};
  return replaceVariant(variant, service, account);
}
 
export class KeychainStore implements Store {
  /**
   * Get the value of a property from the store.
   *
   * @param {Object} prop - The property schema.
   * @param {String} prop.schemaPath - Dotted path to the property in the schema.
   * @param {String} prop.variant - The name of the variant of the property.
   * @param {Object} prop.storeOptions - Property options for the store.
   * @param {String} prop.storeOptions.service - The service name for the property.
   * @param {String} prop.storeOptions.account - The account name for the property.
   * @returns {Promise} the property value
   */
  get({ schemaPath, variant, storeOptions }: PropertyMeta) {
    const [service, account] = extractProp({
      schemaPath,
      variant,
      storeOptions
    });
    return keytar.getPassword(service, account);
  }
 
  /**
   * Set the value of a property in the store.
   *
   * @param {Object} prop - The property schema.
   * @param {String} prop.schemaPath - Dotted path to the property in the schema.
   * @param {String} prop.variant - The name of the variant of the property.
   * @param {Object} prop.storeOptions - Property options for the store.
   * @param {String} prop.storeOptions.service - The service name for the property.
   * @param {String} prop.storeOptions.account - The account name for the property.
   * @param {String} value - The value to set on the property.
   * @returns {Promise} the property value
   */
  async set(prop: PropertyMeta, value: string) {
    const [service, account] = extractProp(prop);
    await keytar.setPassword(service, account, value);
    return value;
  }
 
  /**
   * Delete a property from the store.
   *
   * @param {Object} prop - The property schema.
   * @param {String} prop.schemaPath - Dotted path to the property in the schema.
   * @param {String} prop.variant - The name of the variant of the property.
   * @param {Object} prop.storeOptions - Property options for the store.
   * @param {String} prop.storeOptions.service - The service name for the property.
   * @param {String} prop.storeOptions.account - The account name for the property.
   * @returns {Promise} the property value
   */
  delete(prop: PropertyMeta) {
    const [service, account] = extractProp(prop);
    return keytar.deletePassword(service, account);
  }
}
 
export function plugin() {
  return {
    stores: {
      keychain: new KeychainStore()
    }
  };
}