UNPKG

@naturalcycles/nodejs-lib

Version:
121 lines (120 loc) 4.86 kB
"use strict"; 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.`); } }