@naturalcycles/nodejs-lib
Version:
Standard library for Node.js
121 lines (120 loc) • 4.86 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.setSecretMap = exports.getSecretMap = exports.secretOptional = exports.secret = exports.loadSecretsFromEncryptedJsonFileValues = exports.loadSecretsFromEncryptedJsonFile = exports.removeSecretsFromEnv = exports.loadSecretsFromEnv = void 0;
const fs = require("fs");
const js_lib_1 = require("@naturalcycles/js-lib");
const __1 = require("..");
const crypto_util_1 = require("./crypto.util");
let loaded = false;
const secretMap = {};
/**
* Loads plaintext secrets from process.env, removes them, stores locally.
* Make sure to call this function early on server startup, so secrets are removed from process.env
*
* Does NOT delete previous secrets from secretMap.
*/
function loadSecretsFromEnv() {
require('dotenv').config(); // ensure .env is loaded
const secrets = {};
Object.keys(process.env)
.filter(k => k.toUpperCase().startsWith('SECRET_'))
.forEach(k => {
secrets[k.toUpperCase()] = process.env[k];
secretMap[k.toUpperCase()] = process.env[k];
delete process.env[k];
});
loaded = true;
console.log(`${Object.keys(secrets).length} secret(s) loaded from process.env: ${Object.keys(secrets).join(', ')}`);
}
exports.loadSecretsFromEnv = loadSecretsFromEnv;
/**
* Removes process.env.SECRET_*
*/
function removeSecretsFromEnv() {
Object.keys(process.env)
.filter(k => k.toUpperCase().startsWith('SECRET_'))
.forEach(k => delete process.env[k]);
}
exports.removeSecretsFromEnv = removeSecretsFromEnv;
/**
* Does NOT delete previous secrets from secretMap.
*
* If SECRET_ENCRYPTION_KEY argument is passed - will decrypt the contents of the file first, before parsing it as JSON.
*
* Whole file is encrypted.
* For "json-values encrypted" style - use `loadSecretsFromEncryptedJsonFileValues`
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
function loadSecretsFromEncryptedJsonFile(filePath, secretEncryptionKey) {
(0, js_lib_1._assert)(fs.existsSync(filePath), `loadSecretsFromEncryptedJsonFile() cannot load from path: ${filePath}`);
let secrets;
if (secretEncryptionKey) {
const buf = fs.readFileSync(filePath);
const plain = (0, crypto_util_1.decryptRandomIVBuffer)(buf, secretEncryptionKey).toString('utf8');
secrets = JSON.parse(plain);
}
else {
secrets = JSON.parse(fs.readFileSync(filePath, 'utf8'));
}
Object.entries(secrets).forEach(([k, v]) => (secretMap[k.toUpperCase()] = v));
loaded = true;
console.log(`${Object.keys(secrets).length} secret(s) loaded from ${filePath}: ${Object.keys(secrets)
.map(s => s.toUpperCase())
.join(', ')}`);
}
exports.loadSecretsFromEncryptedJsonFile = loadSecretsFromEncryptedJsonFile;
/**
* Whole file is NOT encrypted, but instead individual json values ARE encrypted..
* For whole-file encryption - use `loadSecretsFromEncryptedJsonFile`
*/
function loadSecretsFromEncryptedJsonFileValues(filePath, secretEncryptionKey) {
(0, js_lib_1._assert)(fs.existsSync(filePath), `loadSecretsFromEncryptedJsonFileValues() cannot load from path: ${filePath}`);
let secrets = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (secretEncryptionKey) {
secrets = (0, crypto_util_1.decryptObject)(secrets, secretEncryptionKey);
}
Object.entries(secrets).forEach(([k, v]) => (secretMap[k.toUpperCase()] = v));
loaded = true;
console.log(`${Object.keys(secrets).length} secret(s) loaded from ${filePath}: ${Object.keys(secrets)
.map(s => s.toUpperCase())
.join(', ')}`);
}
exports.loadSecretsFromEncryptedJsonFileValues = loadSecretsFromEncryptedJsonFileValues;
/**
* json secrets are always base64'd
*/
function secret(k, json = false) {
const v = secretOptional(k, json);
if (!v) {
throw new Error(`secret(${k.toUpperCase()}) not found!`);
}
return v;
}
exports.secret = secret;
function secretOptional(k, json = false) {
requireLoaded();
const v = secretMap[k.toUpperCase()];
return v && json ? JSON.parse((0, __1.base64ToString)(v)) : v;
}
exports.secretOptional = secretOptional;
function getSecretMap() {
requireLoaded();
return secretMap;
}
exports.getSecretMap = getSecretMap;
/**
* REPLACES secretMap with new map.
*/
function setSecretMap(map) {
Object.keys(secretMap).forEach(k => delete secretMap[k]);
Object.entries(map).forEach(([k, v]) => (secretMap[k.toUpperCase()] = v));
console.log(`setSecretMap set ${Object.keys(secretMap).length} secret(s): ${Object.keys(map)
.map(s => s.toUpperCase())
.join(', ')}`);
}
exports.setSecretMap = setSecretMap;
function requireLoaded() {
if (!loaded) {
throw new Error(`Secrets were not loaded! Call loadSecrets() before accessing secrets.`);
}
}
;