nodecredstash
Version:
Node.js CredStash implementation
168 lines (167 loc) • 8.27 kB
JavaScript
;
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _CredStash_kmsClient, _CredStash_ddb;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CredStash = void 0;
const client_kms_1 = require("@aws-sdk/client-kms");
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
const debug_1 = require("debug");
const dynamoDb_1 = require("./lib/dynamoDb");
const utils_1 = require("./lib/utils");
const keyService_1 = require("./lib/keyService");
const aesCredstash_1 = require("./lib/aesCredstash");
const debug = (0, debug_1.default)('credStash');
class CredStash {
constructor({ kmsOpts = {}, dynamoOpts = {}, } = {}) {
_CredStash_kmsClient.set(this, void 0);
_CredStash_ddb.set(this, void 0);
this.paddedInt = utils_1.paddedInt;
__classPrivateFieldSet(this, _CredStash_kmsClient, new client_kms_1.KMSClient(kmsOpts), "f");
__classPrivateFieldSet(this, _CredStash_ddb, new dynamoDb_1.DynamoDB(new client_dynamodb_1.DynamoDBClient(dynamoOpts)), "f");
// eslint-disable-next-line @typescript-eslint/no-this-alias
const credStash = this;
Object.getOwnPropertyNames(CredStash.prototype).forEach((key) => {
const method = credStash[key];
credStash[key] = (...args) => {
const lastArg = args.slice(-1)[0];
let cb;
if (typeof lastArg === 'function') {
cb = args.pop();
}
return method.apply(credStash, args)
.then((res) => {
if (cb) {
return cb(undefined, res);
}
return res;
})
.catch((err) => {
if (cb) {
return cb(err);
}
throw err;
});
};
});
}
async getHighestVersion(opts) {
const { Items = [] } = await __classPrivateFieldGet(this, _CredStash_ddb, "f").getLatestVersion(opts);
const [{ version = (0, utils_1.paddedInt)(0) } = {}] = Items;
return version;
}
async incrementVersion(opts) {
const rawVersion = await this.getHighestVersion(opts);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-template-expression
if (`${rawVersion}`.match(/^[0-9]+$/)) {
const version = Number.parseInt(rawVersion, 10) + 1;
return (0, utils_1.paddedInt)(version);
}
throw new Error(`Can not autoincrement version. The current version: ${rawVersion} is not an int`);
}
async putSecret({ name, version: origVersion, secret, context, digest, kmsKey, tableName, }) {
const version = (0, utils_1.sanitizeVersion)(origVersion, 1);
const keyService = new keyService_1.KeyService(__classPrivateFieldGet(this, _CredStash_kmsClient, "f"), kmsKey, context);
const sealed = await (0, aesCredstash_1.sealAesCtrLegacy)(keyService, secret, digest);
const data = Object.assign({ name, version }, sealed);
try {
const result = await __classPrivateFieldGet(this, _CredStash_ddb, "f").createSecret(data, tableName);
return result;
}
catch (err) {
if (err instanceof client_dynamodb_1.ConditionalCheckFailedException) {
throw new Error(`${name} version ${version} is already in the credential store.`);
}
throw err;
}
}
async getAllVersions({ name, context, limit, kmsKey, tableName, }) {
const keyService = new keyService_1.KeyService(__classPrivateFieldGet(this, _CredStash_kmsClient, "f"), kmsKey, context);
const { Items = [] } = await __classPrivateFieldGet(this, _CredStash_ddb, "f").getAllVersions({ name, tableName, limit });
return Promise.all(Items.map(async (record) => ({
version: record.version,
secret: await (0, aesCredstash_1.openAesCtrLegacy)(keyService, record),
})));
}
async getSecret({ name, context, version: origVersion, kmsKey, tableName, }) {
const version = (0, utils_1.sanitizeVersion)(origVersion);
const keyService = new keyService_1.KeyService(__classPrivateFieldGet(this, _CredStash_kmsClient, "f"), kmsKey, context);
let record;
if (version) {
({ Item: record } = await __classPrivateFieldGet(this, _CredStash_ddb, "f").getByVersion({ name, version, tableName }));
}
else {
({ Items: [record] } = await __classPrivateFieldGet(this, _CredStash_ddb, "f").getLatestVersion({ name, tableName }));
}
if (!record || !record.key) {
throw new Error(`Item {'name': '${name}'} could not be found.`);
}
const decrypted = await (0, aesCredstash_1.openAesCtrLegacy)(keyService, record);
return decrypted;
}
async deleteSecrets(opts) {
const { Items = [] } = await __classPrivateFieldGet(this, _CredStash_ddb, "f").getAllVersions(opts);
const results = [];
for (const secret of Items) {
const result = await this.deleteSecret({ ...opts, version: secret.version });
results.push(result);
}
return results;
}
async deleteSecret({ version: origVersion, name, ...opts }) {
const version = (0, utils_1.sanitizeVersion)(origVersion);
if (!version) {
throw new Error('version is a required parameter');
}
debug(`Deleting ${name} -- version ${version}`);
return __classPrivateFieldGet(this, _CredStash_ddb, "f").deleteSecret({ ...opts, name, version });
}
async listSecrets(opts) {
const { Items = [] } = await __classPrivateFieldGet(this, _CredStash_ddb, "f").getAllSecretsAndVersions(opts);
return Items.sort(utils_1.sortSecrets);
}
async getAllSecrets({ version, startsWith, ...opts } = {}) {
const unOrdered = {};
const secrets = await this.listSecrets(opts);
const position = {};
const ordered = {};
const filtered = [];
secrets
.filter((secret) => secret.version === (version || secret.version))
.filter((secret) => !startsWith || secret.name.startsWith(startsWith))
.forEach((next) => {
position[next.name] = position[next.name]
? position[next.name] : filtered.push(next);
});
for (const secret of filtered) {
try {
unOrdered[secret.name] = await this.getSecret({
name: secret.name,
version: secret.version,
...opts,
});
}
catch (e) {
debug(`Ran into some issue ${JSON.stringify(e)}`);
}
}
Object.keys(unOrdered).sort().forEach((key) => {
ordered[key] = unOrdered[key];
});
return ordered;
}
createDdbTable(opts) {
return __classPrivateFieldGet(this, _CredStash_ddb, "f").createTable(opts);
}
}
exports.CredStash = CredStash;
_CredStash_kmsClient = new WeakMap(), _CredStash_ddb = new WeakMap();