@onboardbase/cli
Version:
[](https://www.npmjs.com/package/@onboardbase/cli) [](https://www.npmjs.com/package/@onboardbase/cli) [ • 48.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isDirectoryWritable = exports.makeRandomId = exports.isUnix = exports.isSudoUser = exports.isOnboardbaseServiceInstalled = exports.getPathToServiceFiles = exports.createProjectLogTable = exports.getLatestCliVersion = exports.downloadTarballs = exports.parseEnvContentToObject = exports.parseObjectToEnv = exports.uploadSecretsToOnboardbase = exports.duplicateExistingSecretAndUpdateEnvironment = exports.removeDuplicateSecrete = exports.formatDate = exports.encryptWithAESAndRSA = exports.rsaEncryptSecret = exports.aesDecryptSecret = exports.getFrontendEncryptionKey = exports.rsaDecryptSecret = exports.generateRsaKeys = exports.checkProjectCacheStatus = exports.recheckUserAccess = exports.handleSocketConnection = exports.deleteFallbackSecret = exports.deleteRecursiveFiles = exports.getFallbackSecret = exports.createFallbackSecret = exports.decryptSecrets = exports.decryptRawSecretsAndReturnAllProperties = exports.decryptGenericSecret = exports.decryptPlainSecretAndReturnPlainValue = exports.encryptSecrets = exports.getFallbackDirectory = exports.getMachineID = exports.updateLocalSecrets = exports.getLocalSecrets = exports.parseLocalSecrets = exports.getCurrentWorkingDirectory = exports.getHomeDirectory = exports.addPrefixToEnvs = exports.downloadSecretsForRecommendation = exports.downloadSecrets = exports.throwSecretsNotAccessibleError = exports.fetchRawSecrets = exports.fetchRawSecretsIncludingSecretProperties = exports.getEnvironmentId = exports.isExist = exports.executeCommand = exports.executeCommandUsingChildProcessExec = void 0;
exports.isJSON = exports.isAPartialUrl = exports.isValidURL = exports.getShellRc = void 0;
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const path_1 = require("path");
const node_machine_id_1 = require("node-machine-id");
const configuration_1 = require("../configuration");
const os_1 = require("os");
const CryptoJS = require("crypto-js");
// @ts-ignore
const io = require("socket.io-client");
const config_1 = require("../configuration/config");
const http_1 = require("../http");
const NodeRSA = require("node-rsa");
const YAML = require("yaml");
const chalk = require("chalk");
const jwt_decode_1 = require("jwt-decode");
const axios_1 = require("axios");
const isDocker_1 = require("./isDocker");
const uploadSecretsActionTypes_enum_1 = require("../enums/uploadSecretsActionTypes.enum");
const util_1 = require("util");
const cli_ux_1 = require("cli-ux");
const executeAsync = (0, util_1.promisify)(child_process_1.exec);
const executeCommandUsingChildProcessExec = async (command, env) => {
const daemon = await executeAsync(command, {
env: Object.assign(process.env, { env }),
windowsHide: false,
cwd: process.cwd(),
});
return daemon;
};
exports.executeCommandUsingChildProcessExec = executeCommandUsingChildProcessExec;
const executeCommand = (command, envs, logProcess,
// Can only accept false since STDIO will be enabled by default if this object is not passed
stdio) => {
//let socketClient = SyncSocketClient.getInstance();
const daemon = (0, child_process_1.spawn)(command, {
env: Object.assign(envs, { FORCE_COLOR: true }),
cwd: process.cwd(),
stdio: "inherit",
windowsHide: false,
shell: true,
});
return daemon;
};
exports.executeCommand = executeCommand;
const isExist = (filePath) => {
return (0, fs_1.existsSync)(filePath);
};
exports.isExist = isExist;
const getEnvironmentId = async (project, environment, accessToken) => {
const environments = await (0, http_1.fetchSingleProject)(accessToken, project);
const environmentId = environments === null || environments === void 0 ? void 0 : environments[0].environments.list.find(({ title }) => title === environment).id;
return environmentId;
};
exports.getEnvironmentId = getEnvironmentId;
const fetchRawSecretsIncludingSecretProperties = async (project, environment, deviceToken) => {
var _a, _b, _c, _d, _e;
const { accessToken, user } = await (0, http_1.generateAccessToken)(deviceToken);
let secrets = await (0, http_1.fetchSecrets)(project, environment, accessToken);
//secrets is empty because it is a new account
secrets = (_a = secrets === null || secrets === void 0 ? void 0 : secrets.data) === null || _a === void 0 ? void 0 : _a.generalSecrets.list;
const userCanFetchSecretUnderEnvironment = Boolean((_b = secrets === null || secrets === void 0 ? void 0 : secrets[0]) === null || _b === void 0 ? void 0 : _b.member);
if (secrets.length > 0 && !userCanFetchSecretUnderEnvironment) {
console.log(`Error: ${chalk.red(`You don't have enough permission to update/upload/delete secrets under the ${chalk.bold.greenBright(environment)} environment.`)}`);
process.exit(1);
}
const environmentId = (_e = (_d = (_c = secrets === null || secrets === void 0 ? void 0 : secrets[0]) === null || _c === void 0 ? void 0 : _c.environment) === null || _d === void 0 ? void 0 : _d.id) !== null && _e !== void 0 ? _e : (await (0, exports.getEnvironmentId)(project, environment, accessToken));
if (Array.isArray(secrets) && secrets.length) {
const secretKeyAndValue = secrets.map(({ key, value, id }) => {
return { key, value, id };
});
const aesSecret = await (0, exports.decryptRawSecretsAndReturnAllProperties)(secretKeyAndValue, config_1.default.getEncryptionSecretKey());
return {
env: aesSecret,
user,
accessToken,
environmentId,
};
}
return {
env: {},
user,
accessToken,
environmentId,
};
};
exports.fetchRawSecretsIncludingSecretProperties = fetchRawSecretsIncludingSecretProperties;
const fetchRawSecrets = async (project, environment, cliToken) => {
var _a, _b;
const { accessToken, user } = await (0, http_1.generateAccessToken)(cliToken);
let secrets = await (0, http_1.fetchSecrets)(project, environment, accessToken);
secrets = (_a = secrets === null || secrets === void 0 ? void 0 : secrets.data) === null || _a === void 0 ? void 0 : _a.generalSecrets.list;
const userCanFetchSecretUnderEnvironment = Boolean((_b = secrets === null || secrets === void 0 ? void 0 : secrets[0]) === null || _b === void 0 ? void 0 : _b.member);
if (!userCanFetchSecretUnderEnvironment) {
console.log(`Error: ${chalk.red(`You don't have enough permission to update/upload/delete secrets under the ${chalk.bold.greenBright(environment)} environment.`)}`);
process.exit(1);
}
const environmentId = secrets === null || secrets === void 0 ? void 0 : secrets[0].environment.id;
if (secrets) {
const secretKeyAndValue = secrets.map(({ key, value, id }) => {
return { key, value, id };
});
const aesSecret = await (0, exports.aesDecryptSecret)(secretKeyAndValue);
return {
env: aesSecret,
user,
accessToken,
environmentId,
};
}
return {
env: {},
user,
accessToken,
environmentId,
};
};
exports.fetchRawSecrets = fetchRawSecrets;
const throwSecretsNotAccessibleError = (project, environment, logProcess) => {
console.log(`There was an error fetching secrets for ${chalk.greenBright.bold(project)} with the environment: ${chalk.greenBright.bold(environment)}`);
console.log(`
${chalk.greenBright.bold("Possible causes")}
> Invalid Device / Service Token
> Not enough permission to access the project you want to fetch secrets for.
${chalk.greenBright.bold("Possible solutions")}
> Run onboardbase login command again so you can get a fresh auth token.
> Run onboardbase setup command again to update your .onboardbase.yaml file
> Contact an admin to give you access to the project.`);
console.log("");
if (logProcess) {
//const socketClient = ConfigManager.getSynSocketClient()
}
};
exports.throwSecretsNotAccessibleError = throwSecretsNotAccessibleError;
/**
*
* @param project string
* @param environment string
* @param passphrase string
*
* This function retrive an access token for this request and fetches the token
* from onboardbase. It also create the backup file for caching purposed.
*
* This function returns a merged version of both local and online secrets.
*
*/
const downloadSecrets = async (project, environment, passphrase, noSystemSecrets = false, logProcess, verbose, shouldUseCache, sourcePath, deviceToken) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
// Fetch localSecrets if any exist
const localSecrets = (_a = (0, exports.getLocalSecrets)()) !== null && _a !== void 0 ? _a : {};
let finalEnvs = {};
let finalBackupSecrets = {};
let teamName;
if (sourcePath) {
try {
/**
* That is read from stdin
*/
if (sourcePath === "-") {
const stdin = (0, fs_1.readFileSync)(0, "utf-8");
const parsedSecrets = JSON.parse(Object(stdin));
return {
env: Object.assign(parsedSecrets, localSecrets, noSystemSecrets ? {} : process.env),
user: {},
};
}
const isUrl = (0, exports.isAPartialUrl)(sourcePath);
/**
* That is if a JSON string is passed
*/
if (!isUrl) {
const parsedSecrets = JSON.parse(sourcePath);
return {
env: Object.assign(parsedSecrets, localSecrets, noSystemSecrets ? {} : process.env),
user: {},
};
}
/**
* if a path to a local file is passed
*/
if (isUrl) {
const secrets = (0, fs_1.readFileSync)(sourcePath, { encoding: "utf8" });
const isJson = (0, exports.isJSON)(secrets);
let finalSecrets = {};
if (isJson)
finalSecrets = JSON.parse(secrets);
if (!isJson) {
const newEnvs = secrets.split("\n");
newEnvs.map((keyAndValue) => {
const splittedKeyAndValue = keyAndValue.split("=");
const key = splittedKeyAndValue[0].toUpperCase();
const value = splittedKeyAndValue[1];
// if (value === undefined) {
// throw new Error(
// `value for ${chalk.greenBright.bold(key)} was not specified.`
// );
// }
if (value !== undefined)
finalSecrets[key] = value;
});
}
return {
env: Object.assign(finalSecrets, localSecrets, noSystemSecrets ? {} : process.env),
user: {},
};
}
}
catch (error) {
console.log(error.message);
}
}
if (shouldUseCache) {
try {
const fallbackSecrets = await (0, exports.getFallbackSecret)(project, environment, passphrase);
if (verbose) {
let updatedLocalSecret = {};
const recommendationSecrets = [];
const aesSecret = fallbackSecrets;
if (Object.keys(localSecrets).length) {
Object.keys(localSecrets).map((localSecretkey) => {
const key = localSecretkey;
const value = localSecrets[key];
if (aesSecret[key] && aesSecret[key] === value) {
console.log(`Duplicate secret found: ${chalk.greenBright.bold(`${key}=${value}`)}`, chalk.greenBright("...removing the secret from your local file"));
}
else
updatedLocalSecret[key] = value;
if (!aesSecret[key])
recommendationSecrets.push(key);
});
if (verbose && recommendationSecrets.length) {
let formattedMessage = "We noticed you have some new secret which have not been added to Onboardbase and suggest you create a recommendation to avoid having sensitive secrets locally. The secret keynames are:\n\n";
recommendationSecrets.map((secretKey) => (formattedMessage += `-${secretKey}\n`));
console.log(formattedMessage);
console.log(`Run ${chalk.greenBright("onboardbase recommendation:create --from-local-secrets")} to upload your local secrets to onboardbase.`);
}
config_1.default.updateLocalProjectSecrets(updatedLocalSecret);
}
}
return {
env: Object.assign(fallbackSecrets, localSecrets, noSystemSecrets ? {} : process.env),
user: {},
};
}
catch (error) {
const currentVersion = await (0, http_1.checkLiveProjectAndEnvironmentVersion)(project, environment, deviceToken);
config_1.default.updateProjectAndEnvironmentVersion(project, environment, currentVersion);
return await (0, exports.downloadSecrets)(project, environment, passphrase, false, true, verbose, false, "", deviceToken);
}
}
try {
const { accessToken, user } = await (0, http_1.generateAccessToken)(deviceToken);
// socketClient.emitEvent("SYNC::LOGS", {
// data: "Downloading secrets from onboardbase servers...",
// });
let secrets = await (0, http_1.fetchSecrets)(project, environment, accessToken);
const userCanFetchSecretUnderEnvironment = Boolean((_e = (_d = (_c = (_b = secrets === null || secrets === void 0 ? void 0 : secrets.data) === null || _b === void 0 ? void 0 : _b.generalSecrets) === null || _c === void 0 ? void 0 : _c.list) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.member);
if (!userCanFetchSecretUnderEnvironment) {
// socketClient.emitEvent("SYNC::LOGS", {
// data: `Error: You don't have enough permission to fetch secrets under the ${chalk.bold.greenBright(
// environment
// )} environment.`,
// });
console.log(`Error: ${chalk.red(`You don't have enough permission to fetch secrets under the ${chalk.bold.greenBright(environment)} environment.`)}`);
// socketClient.emitEvent("SYNC::LOGS", {
// data: "Process Exited with exit code of 1",
// });
process.exit(1);
}
/**
* save the current user's auth details & project ID to our configManager
*/
config_1.default.setAuthSessionDetails(Object.assign((0, jwt_decode_1.default)(accessToken), {
project: { id: (_k = (_j = (_h = (_g = (_f = secrets === null || secrets === void 0 ? void 0 : secrets.data) === null || _f === void 0 ? void 0 : _f.generalSecrets) === null || _g === void 0 ? void 0 : _g.list) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.project) === null || _k === void 0 ? void 0 : _k.id },
}));
const { team } = config_1.default.getAuthSessionDetails();
teamName = team.name;
finalEnvs = Object.assign({}, (noSystemSecrets ? {} : process.env));
/**
* Check for errors coming from onboardbase itself.
* E.g: unauthorized error and revert back to cache if any exist
*/
if (secrets.errors) {
(0, exports.throwSecretsNotAccessibleError)(project, environment);
/**
* if CLI failed to fetch secrets from onboardbase, do not create
* a project log for that session
*/
config_1.default.shouldCreateProjectLog = false;
const fallbackSecrets = await (0, exports.getFallbackSecret)(project, environment, passphrase);
const mergedEnvs = Object.assign(fallbackSecrets, localSecrets);
return { env: mergedEnvs, user: {} };
}
else {
secrets = (_l = secrets === null || secrets === void 0 ? void 0 : secrets.data) === null || _l === void 0 ? void 0 : _l.generalSecrets.list;
// secrets = JSON.parse(secrets[0].key);
const secretArray = [];
if (secrets && Array.isArray(secrets)) {
// socketClient.emitEvent("SYNC::LOGS", {
// data: "Decrypting secrets using RSA Encryption...",
// });
// socketClient.emitEvent("SYNC::LOGS", {
// data: "Decrypting secrets using AES Encryption...",
// });
const secretKeyAndValue = secrets.map(({ key, value }) => {
return { key, value };
});
const aesSecret = await (0, exports.aesDecryptSecret)(secretKeyAndValue);
/**
* check localSecrets to see if it has a secret key * value from
* what's on onboardbase, remove the secret to avoid duplicate
*/
let updatedLocalSecret = {};
const recommendationSecrets = [];
if (Object.keys(localSecrets).length) {
Object.keys(localSecrets).map((localSecretkey) => {
const key = localSecretkey;
const value = localSecrets[key];
if (aesSecret[key] && aesSecret[key] === value) {
console.log(`Duplicate secret found: ${chalk.greenBright.bold(`${key}=${value}`)}`, chalk.greenBright("...removing the secret from your local file"));
}
else
updatedLocalSecret[key] = value;
if (!aesSecret[key])
recommendationSecrets.push(key);
});
if (verbose && recommendationSecrets.length) {
let formattedMessage = "We noticed you have some new secret which have not been added to Onboardbase and suggest you create a merge requests to avoid having sensitive secrets locally. The secret keynames are:\n\n";
recommendationSecrets.map((secretKey) => (formattedMessage += `-${secretKey}\n`));
console.log(formattedMessage);
console.log(`Run ${chalk.greenBright("onboardbase mr:create --from-local-secrets")} to upload your local secrets to onboardbase.`);
}
config_1.default.updateLocalProjectSecrets(updatedLocalSecret);
}
finalEnvs = Object.assign(finalEnvs, Object.assign(Object.assign({}, aesSecret), localSecrets));
finalBackupSecrets = aesSecret;
}
// Create fallback secrets
// socketClient.emitEvent("SYNC::LOGS", {
// data: "Creating a 24hrs Fallback Secrets file",
// });
await (0, exports.createFallbackSecret)(project, JSON.stringify(finalBackupSecrets), environment, passphrase);
return { env: finalEnvs, user };
}
}
catch (error) {
/**
* if CLI failed to fetch secrets from onboardbase, do not create
* a project log for that session
*/
config_1.default.shouldCreateProjectLog = false;
(0, exports.throwSecretsNotAccessibleError)(project, environment);
console.log("Reverting to fallback...", `${chalk.greenBright.bold(project)}/${chalk.greenBright.bold(environment)}`);
// cache network error here and fallback to cache
}
};
exports.downloadSecrets = downloadSecrets;
const downloadSecretsForRecommendation = async (project, environment, accessToken) => {
var _a, _b, _c, _d, _e, _f;
try {
const localSecrets = (_a = (0, exports.getLocalSecrets)()) !== null && _a !== void 0 ? _a : {};
let secrets = await (0, http_1.fetchSecrets)(project, environment, accessToken);
const userCanFetchSecretUnderEnvironment = Boolean((_e = (_d = (_c = (_b = secrets === null || secrets === void 0 ? void 0 : secrets.data) === null || _b === void 0 ? void 0 : _b.generalSecrets) === null || _c === void 0 ? void 0 : _c.list) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.member);
if (!userCanFetchSecretUnderEnvironment) {
console.log(`Error: ${chalk.red(`You don't have enough permission to fetch secrets under the ${chalk.bold.greenBright(environment)} environment.`)}`);
process.exit(1);
}
if (secrets.errors) {
console.error("Error fetching secrets");
process.exit(1);
}
else {
secrets = (_f = secrets === null || secrets === void 0 ? void 0 : secrets.data) === null || _f === void 0 ? void 0 : _f.generalSecrets.list;
if (secrets && Array.isArray(secrets)) {
const secretKeyAndValue = secrets.map(({ key, value }) => {
return { key, value };
});
const aesSecret = await (0, exports.aesDecryptSecret)(secretKeyAndValue);
const recommendationSecrets = [];
if (Object.keys(localSecrets).length) {
Object.keys(localSecrets).map((localSecretkey) => {
const key = localSecretkey;
const value = localSecrets[key];
if (!aesSecret[key])
recommendationSecrets.push(key);
});
return recommendationSecrets;
}
}
}
}
catch (error) {
console.error("Error fetching secrets", error === null || error === void 0 ? void 0 : error.message);
process.exit(1);
}
};
exports.downloadSecretsForRecommendation = downloadSecretsForRecommendation;
const addPrefixToEnvs = (prefix, envs) => {
let modifiedEnvs = {};
Object.keys(envs).map((key) => {
const envKey = `${prefix}_${key}`;
const envValue = envs[key];
modifiedEnvs[envKey] = envValue;
});
return modifiedEnvs;
};
exports.addPrefixToEnvs = addPrefixToEnvs;
const getHomeDirectory = () => {
return (0, os_1.homedir)();
};
exports.getHomeDirectory = getHomeDirectory;
const getCurrentWorkingDirectory = () => {
return process.cwd();
};
exports.getCurrentWorkingDirectory = getCurrentWorkingDirectory;
const parseLocalSecrets = (secrets) => {
let secretStore = {};
if (Array.isArray(secrets)) {
secrets.map((secret) => {
secretStore[Object.keys(secret)[0]] = Object.values(secret)[0];
});
}
if (!Array.isArray(secrets)) {
Object.keys(secrets).map((secret) => (secretStore[secret] = secrets[secret]));
}
return secretStore;
};
exports.parseLocalSecrets = parseLocalSecrets;
const getLocalSecrets = () => {
var _a, _b, _c;
const workingDirectory = (0, exports.getCurrentWorkingDirectory)();
const localEnvironmentFile = config_1.default.projectConfigFile;
const pathToSecret = localEnvironmentFile;
if ((0, exports.isExist)(pathToSecret)) {
try {
const parsedSecrets = (_c = (_b = (_a = YAML.parse((0, fs_1.readFileSync)(pathToSecret, "utf8"))) === null || _a === void 0 ? void 0 : _a.secrets) === null || _b === void 0 ? void 0 : _b.local) !== null && _c !== void 0 ? _c : {};
return (0, exports.parseLocalSecrets)(parsedSecrets);
}
catch (error) {
console.log(chalk.red.bold(`${localEnvironmentFile} file is not valid`));
process.exit(1);
}
}
return;
};
exports.getLocalSecrets = getLocalSecrets;
const updateLocalSecrets = (localSecrets) => {
var _a, _b;
const workingDirectory = (0, exports.getCurrentWorkingDirectory)();
const localEnvironmentFile = config_1.default.projectConfigFile;
const pathToSecret = (0, path_1.join)(workingDirectory, localEnvironmentFile);
const data = Object.assign(Object.assign({}, ((0, exports.isExist)(pathToSecret)
? YAML.parse((0, fs_1.readFileSync)(pathToSecret, "utf8"))
: {})), { secrets: {
local: Object.assign({}, ((0, exports.isExist)(pathToSecret)
? (_b = (_a = YAML.parse((0, fs_1.readFileSync)(pathToSecret, "utf8"))) === null || _a === void 0 ? void 0 : _a.secrets) === null || _b === void 0 ? void 0 : _b.local
: {})),
} });
Object.keys(localSecrets).map((key) => {
data.secrets.local[key] = localSecrets[key];
});
(0, fs_1.writeFileSync)(pathToSecret, YAML.stringify(data));
};
exports.updateLocalSecrets = updateLocalSecrets;
const getMachineID = async () => {
return await (0, node_machine_id_1.machineId)();
};
exports.getMachineID = getMachineID;
const getFallbackDirectory = () => {
return (0, path_1.join)((0, exports.getHomeDirectory)(), ".onboardbase", "fallback");
};
exports.getFallbackDirectory = getFallbackDirectory;
const encryptSecrets = async (secrets, passphrase) => {
const encryptionPassphrase = await (0, configuration_1.getEncryptionPassphrase)();
const bytes = CryptoJS.AES.encrypt(secrets, passphrase || encryptionPassphrase);
return bytes.toString();
};
exports.encryptSecrets = encryptSecrets;
const decryptPlainSecretAndReturnPlainValue = async (secret, passphrase) => {
const encryptionPassphrase = passphrase !== null && passphrase !== void 0 ? passphrase : (await (0, configuration_1.getEncryptionPassphrase)());
let decryptedSecret = CryptoJS.AES.decrypt(secret, encryptionPassphrase).toString(CryptoJS.enc.Utf8);
return decryptedSecret;
};
exports.decryptPlainSecretAndReturnPlainValue = decryptPlainSecretAndReturnPlainValue;
const decryptGenericSecret = async (genericSecrets, passphrase) => {
const encryptionPassphrase = passphrase !== null && passphrase !== void 0 ? passphrase : (await (0, configuration_1.getEncryptionPassphrase)());
let decryptedSecrets = CryptoJS.AES.decrypt(genericSecrets, encryptionPassphrase).toString(CryptoJS.enc.Utf8);
return JSON.parse(decryptedSecrets);
};
exports.decryptGenericSecret = decryptGenericSecret;
const decryptRawSecretsAndReturnAllProperties = async (secrets, passphrase) => {
let secretMap = {};
try {
secrets.map((secret) => {
const secretKey = CryptoJS.AES.decrypt(secret.key, passphrase).toString(CryptoJS.enc.Utf8);
const secretValue = CryptoJS.AES.decrypt(secret.value, passphrase).toString(CryptoJS.enc.Utf8);
secretMap[secretKey.toUpperCase()] = {
value: secretValue,
id: secret.id,
};
});
return secretMap;
}
catch (error) {
// Silent this error and just delete the config files from the user's device
console.error("Invalid passprase.. pls check your passphrase and try again");
process.exit(1);
}
};
exports.decryptRawSecretsAndReturnAllProperties = decryptRawSecretsAndReturnAllProperties;
const decryptSecrets = async (secrets, passphrase) => {
const encryptionPassphrase = await (0, configuration_1.getEncryptionPassphrase)();
let secretMap = {};
try {
secrets.map(({ key, value }) => {
const secretKey = CryptoJS.AES.decrypt(key, passphrase).toString(CryptoJS.enc.Utf8);
const secretValue = CryptoJS.AES.decrypt(value, passphrase).toString(CryptoJS.enc.Utf8);
secretMap[secretKey] = secretValue;
});
return secretMap;
}
catch (error) {
// Silent this error and just delete the config files from the user's device
console.error("Invalid passprase.. pls check your passphrase and try again");
process.exit(1);
}
};
exports.decryptSecrets = decryptSecrets;
const createFallbackSecret = async (project, secrets, environment = "development", passphrase) => {
const projectDirectory = (0, path_1.join)((0, exports.getFallbackDirectory)(), project);
const encryptedSecrets = await (0, exports.encryptSecrets)(secrets, passphrase);
(0, fs_1.writeFileSync)(`${projectDirectory}_${environment}`, encryptedSecrets);
};
exports.createFallbackSecret = createFallbackSecret;
const getFallbackSecret = async (project, environment = "development", passphrase) => {
const fallbackPath = (0, path_1.join)((0, exports.getFallbackDirectory)(), project);
if (!(0, exports.isExist)(`${fallbackPath.concat(`_${environment}`)}`)) {
console.log("Could not access project and no fallback exist for %s with the environment %s", chalk.green.bold(project), chalk.green.bold(environment));
throw new Error("Error while trying to fetch secrets from fallback directory");
}
const fallbackFile = (0, fs_1.readFileSync)(`${fallbackPath}_${environment}`, {
encoding: "utf8",
});
const decryptedSecrets = (await (0, exports.decryptGenericSecret)(fallbackFile, passphrase));
return decryptedSecrets;
};
exports.getFallbackSecret = getFallbackSecret;
const deleteRecursiveFiles = async (file) => {
const fallbackDirectory = (0, path_1.join)((0, os_1.homedir)(), ".onboardbase", "fallback");
const directoryFiles = (0, fs_1.readdirSync)(fallbackDirectory);
const pattern = file;
directoryFiles.map((fileName) => {
if (pattern.test(fileName))
(0, fs_1.unlinkSync)((0, path_1.join)(fallbackDirectory, fileName));
});
};
exports.deleteRecursiveFiles = deleteRecursiveFiles;
const deleteFallbackSecret = async (project) => {
const regex = RegExp(`^${project}+`);
await (0, exports.deleteRecursiveFiles)(regex);
};
exports.deleteFallbackSecret = deleteFallbackSecret;
const handleSocketConnection = (project, environment, userEmail) => {
var _a, _b, _c, _d;
const allConfigs = config_1.default.getConfigs();
const socket = io.connect((_d = (_b = (_a = allConfigs[process.cwd()]) === null || _a === void 0 ? void 0 : _a["api-host"]) !== null && _b !== void 0 ? _b : (_c = allConfigs["/"]) === null || _c === void 0 ? void 0 : _c["api-host"]) !== null && _d !== void 0 ? _d : "http://localhost:3000", {
transports: ["websocket"],
query: `project=${project}_${environment}&email=${userEmail}`,
});
return socket;
};
exports.handleSocketConnection = handleSocketConnection;
const recheckUserAccess = async (project, environment) => {
const ONE_DAY_IN_MS = 86400000;
const accessInterval = setInterval(async () => {
const isOnline = await (0, http_1.isUserOnline)();
if (isOnline) {
// try to fetch secrets again to see if the user still have access to the project
try {
const { accessToken } = await (0, http_1.generateAccessToken)(await config_1.default.getToken());
await (0, http_1.fetchSecrets)(project, environment, accessToken);
}
catch (error) {
await (0, exports.deleteFallbackSecret)(project);
config_1.default.closeRunningProcess();
clearInterval(accessInterval);
}
}
if (!isOnline) {
// Force the user to restart their server and delete the fallback secrets
console.log("Server due for a restart...");
await (0, exports.deleteFallbackSecret)(project);
config_1.default.closeRunningProcess();
clearInterval(accessInterval);
}
}, ONE_DAY_IN_MS);
};
exports.recheckUserAccess = recheckUserAccess;
const checkProjectCacheStatus = (project, environment) => {
return new Promise((resolve) => {
const ONE_DAY_IN_MS = 86400000;
const fallbackFile = (0, path_1.join)((0, os_1.homedir)(), ".onboardbase", "fallback", `${project}_${environment}`);
if ((0, exports.isExist)(fallbackFile)) {
const fileStats = (0, fs_1.statSync)(fallbackFile);
const now = new Date().getTime();
const fileDuration = new Date(fileStats.ctime).getTime() + ONE_DAY_IN_MS;
if (now > fileDuration) {
(0, fs_1.unlinkSync)(fallbackFile);
resolve(true);
}
else
resolve(false);
}
resolve(true);
});
};
exports.checkProjectCacheStatus = checkProjectCacheStatus;
const generateRsaKeys = () => {
const key = new NodeRSA({ b: 512 });
const keys = key.generateKeyPair();
const publicKey = keys.exportKey("public");
const privateKey = keys.exportKey("private");
return { publicKey, privateKey };
};
exports.generateRsaKeys = generateRsaKeys;
const rsaDecryptSecret = (data, privateKey) => {
try {
const rsaPrivateKey = new NodeRSA(privateKey);
return rsaPrivateKey.decrypt(data, "utf8");
}
catch (e) {
return "";
}
};
exports.rsaDecryptSecret = rsaDecryptSecret;
const getFrontendEncryptionKey = () => {
return "e8%!w%yM!eAewzdsegg8%!walmart%yelpMUSIC!eggAPPLEeggwalmartzip_H@BQF3J^2Bjqum3d2.4L@y/FCK3=~pTf~?s(}r8H</4gFnXn@8`wL^gWTVP26tD";
};
exports.getFrontendEncryptionKey = getFrontendEncryptionKey;
const aesDecryptSecret = (secrets) => {
var _a;
//const passphrase = getFrontendEncryptionKey();
const passphrase = (_a = config_1.default.getEncryptionSecretKey()) !== null && _a !== void 0 ? _a : (0, exports.getFrontendEncryptionKey)();
return (0, exports.decryptSecrets)(secrets, passphrase);
};
exports.aesDecryptSecret = aesDecryptSecret;
const rsaEncryptSecret = (data, publicKey) => {
try {
const rsaPublicKey = new NodeRSA(publicKey);
return rsaPublicKey.encrypt(data, "base64");
}
catch (e) {
return "";
}
};
exports.rsaEncryptSecret = rsaEncryptSecret;
const encryptWithAESAndRSA = async (rsaPublicKey, secrets) => {
const aesEncryptionKey = (0, exports.getFrontendEncryptionKey)();
const encryptedAES = await (0, exports.encryptSecrets)(secrets, aesEncryptionKey);
const encryptedRSA = (0, exports.rsaEncryptSecret)(encryptedAES, rsaPublicKey);
return encryptedRSA;
};
exports.encryptWithAESAndRSA = encryptWithAESAndRSA;
const formatDate = (date, sec) => {
let day = "", month = "", convMonth = "", year = "", time = "";
const months = [
"Jan",
"Feb",
"Mar",
"April",
"May",
"June",
"July",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
const getMonth = (val) => months[Number(val) - 1];
let split;
if (date) {
split = date.split("-");
day = `${split[2][0]}${split[2][1]}`;
month = split[1];
year = split[0];
time = split[2].slice(3, sec ? 11 : 8);
convMonth = getMonth(month);
return `${convMonth} ${day}, ${year} | ${time} GMT`;
}
};
exports.formatDate = formatDate;
const removeDuplicateSecrete = (data) => {
const comparisonMap = {};
const cleanArr = [];
data.map((currentSecret) => {
const { key = null, value = null } = comparisonMap[currentSecret.key] || {};
if (!key || (key && value !== currentSecret.value)) {
comparisonMap[currentSecret.key] = currentSecret;
}
});
Object.keys(comparisonMap).map((key) => cleanArr.push(comparisonMap[key]));
return cleanArr;
};
exports.removeDuplicateSecrete = removeDuplicateSecrete;
const duplicateExistingSecretAndUpdateEnvironment = async (project, environmentToDuplicateFrom, environmentToUpdate, cliToken) => {
const { env } = await (0, exports.fetchRawSecrets)(project, environmentToDuplicateFrom, cliToken);
let parsedJSON = env;
cli_ux_1.cli.action.start("Uploading secrets");
await (0, exports.uploadSecretsToOnboardbase)(project, environmentToUpdate, parsedJSON, cliToken, [], uploadSecretsActionTypes_enum_1.UploadSecretsActionTypes.DUPLICATE);
cli_ux_1.cli.action.stop();
};
exports.duplicateExistingSecretAndUpdateEnvironment = duplicateExistingSecretAndUpdateEnvironment;
const uploadSecretsToOnboardbase = async (currentProject, currentEnvironment, parsedJSON, deviceToken, excludeFromExistingSecrets, action, accessToken) => {
let env;
let environmentId;
if (action !== uploadSecretsActionTypes_enum_1.UploadSecretsActionTypes.DUPLICATE) {
const secretProperties = await (0, exports.fetchRawSecretsIncludingSecretProperties)(currentProject, currentEnvironment, deviceToken);
env = secretProperties.env;
accessToken = secretProperties.accessToken;
environmentId = secretProperties.environmentId;
}
/**
* if action is duplicate, environment
*/
if (action === uploadSecretsActionTypes_enum_1.UploadSecretsActionTypes.DUPLICATE) {
if (!accessToken) {
const { accessToken: duplicateQueryAccessToken, user } = await (0, http_1.generateAccessToken)(deviceToken);
accessToken = duplicateQueryAccessToken;
}
environmentId = await (0, exports.getEnvironmentId)(currentProject, currentEnvironment, accessToken);
}
const encryptionPassphrase = config_1.default.getEncryptionSecretKey();
let secretUpdateStore = [];
if (action && action === uploadSecretsActionTypes_enum_1.UploadSecretsActionTypes.DELETE) {
const secretsToDelete = [];
excludeFromExistingSecrets.map((secretKey) => {
if (env[secretKey])
secretsToDelete.push(env[secretKey]);
else
console.log(chalk.greenBright(`The Secret: ${secretKey} doesn't exist on onboardbase...`));
});
await Promise.all(secretsToDelete.map(async (singleSecret) => {
await (0, http_1.deleteSecret)(accessToken, singleSecret.id);
}));
return;
}
if (action === uploadSecretsActionTypes_enum_1.UploadSecretsActionTypes.UPDATE) {
await Promise.all(Object.keys(parsedJSON).map(async (secretKey) => {
if (!env[secretKey]) {
console.log(chalk.greenBright(`${secretKey} doesnt exist on onboardbase.. you're trying to update a secret that doesnt exist on onboardbase`));
return;
}
const encryptedSecretKey = await (0, exports.encryptSecrets)(secretKey, encryptionPassphrase);
const encryptedSecretValue = await (0, exports.encryptSecrets)(parsedJSON[secretKey], encryptionPassphrase);
secretUpdateStore.push({
key: encryptedSecretKey,
value: encryptedSecretValue,
// @ts-ignore
id: env[secretKey].id,
});
}));
await Promise.all(secretUpdateStore.map(async (secret) => {
await (0, http_1.updateSecret)(accessToken, secret.id, {
key: secret.key,
value: secret.value,
});
}));
}
if (action === uploadSecretsActionTypes_enum_1.UploadSecretsActionTypes.CREATE ||
action === uploadSecretsActionTypes_enum_1.UploadSecretsActionTypes.DUPLICATE) {
await Promise.all(Object.keys(parsedJSON).map(async (secretKey) => {
/**
* check for duplicate and skip
*/
if (action !== uploadSecretsActionTypes_enum_1.UploadSecretsActionTypes.DUPLICATE) {
if (env[secretKey]) {
console.log(chalk.greenBright(`Duplicate Secret key found: ${secretKey}.. you can use the ${chalk.bold("onboardbase secrets:update")} command to update the secret value`));
return;
}
}
const encryptedSecretKey = await (0, exports.encryptSecrets)(secretKey.toUpperCase(), encryptionPassphrase);
const encryptedSecretValue = await (0, exports.encryptSecrets)(parsedJSON[secretKey], encryptionPassphrase);
secretUpdateStore.push({
key: encryptedSecretKey,
value: encryptedSecretValue,
comment: await (0, exports.encryptSecrets)("", encryptionPassphrase),
environmentId,
});
}));
if (secretUpdateStore && secretUpdateStore.length)
await (0, http_1.addSecrets)(accessToken, secretUpdateStore);
}
};
exports.uploadSecretsToOnboardbase = uploadSecretsToOnboardbase;
const parseObjectToEnv = (env) => {
let parsedData = "";
Object.keys(env).map((secretKey) => {
const data = `${secretKey}=${env[secretKey]}\n`;
parsedData += data;
});
return parsedData;
};
exports.parseObjectToEnv = parseObjectToEnv;
const parseEnvContentToObject = (envFileContent) => {
const parsedJSON = {};
let certKeys = [];
let currentCertKey = { name: "", value: "", exists: false };
const modifiedData = envFileContent.split("\n").map((keyAndValue) => {
if (keyAndValue.includes("-----BEGIN")) {
currentCertKey.exists = true;
const nameBegin = keyAndValue.split("=");
currentCertKey.name = nameBegin[0];
currentCertKey.value = nameBegin[1];
return "";
}
else if (keyAndValue.includes("-----END")) {
certKeys.push({
name: currentCertKey.name,
value: `${currentCertKey.value}\n${keyAndValue}`,
});
currentCertKey = { name: "", value: "", exists: false };
return "";
}
else if (currentCertKey.exists) {
currentCertKey.value = `${currentCertKey.value}\n${keyAndValue}`;
return "";
}
return keyAndValue;
});
const restructuredData = modifiedData
.join("\n")
.trim()
.split(/\r?\n/)
.filter((x) => x.trim() !== "");
restructuredData.map((data) => {
const keyAndValue = data.split("=");
if (!keyAndValue[0].trim().startsWith("#") &&
keyAndValue[1] !== undefined) {
parsedJSON[keyAndValue[0]] = keyAndValue[1];
}
});
certKeys = certKeys.filter((x) => { var _a, _b; return ((_a = x.value) === null || _a === void 0 ? void 0 : _a.trim()) !== "" && ((_b = x.key) === null || _b === void 0 ? void 0 : _b.trim()) !== ""; });
certKeys.map((data) => {
if (!data.name.trim().startsWith("#") && data.value !== undefined) {
parsedJSON[data.name] = data.value;
}
});
return parsedJSON;
};
exports.parseEnvContentToObject = parseEnvContentToObject;
const downloadTarballs = async (url, filePath) => {
try {
const { data } = await (0, axios_1.default)({
method: "get",
url,
responseType: "stream",
});
const readable = data.pipe((0, fs_1.createWriteStream)(filePath, { encoding: "utf8" }));
// Check if stream has ended before returning a response
return new Promise((resolve, reject) => {
readable.on("finish", async () => {
resolve(filePath);
});
});
}
catch (error) {
console.log(error);
}
};
exports.downloadTarballs = downloadTarballs;
const getLatestCliVersion = async () => {
try {
const registryUrl = "https://registry.npmjs.org/@onboardbase/cli";
const { data } = await axios_1.default.get(registryUrl);
return data["dist-tags"];
}
catch (error) {
if (error.response.data) {
console.error(error.response.data);
}
else
console.error("Could not check for updates... Please check your Network connection and try again");
}
};
exports.getLatestCliVersion = getLatestCliVersion;
/**
* Keep a record of the current project information, the current path, the team that
* owns the project, the user info & the last time they ran the `onboardbase build` command
*/
const createProjectLogTable = (user, team, project) => {
/**
* @todo
*
* Encrypt log files before saving to the DB
* rename onboardbase.json to onboardbase.db
*/
const logTableDirectory = (0, path_1.join)(config_1.default.onboardbaseDirectory, "db");
if (!(0, fs_1.existsSync)(logTableDirectory))
(0, fs_1.mkdirSync)(logTableDirectory);
const logTableFile = (0, path_1.join)(logTableDirectory, "onboardbase.json");
const logFiles = (0, fs_1.existsSync)(logTableFile)
? JSON.parse((0, fs_1.readFileSync)(logTableFile, "utf8"))
: [];
/**
* Check our LOG DB to see if we have a record for the current
* teamId, projectId && user's email
*/
const logWithTeamAndProjectId = logFiles.find((log) => {
var _a, _b;
return ((_a = log === null || log === void 0 ? void 0 : log.team) === null || _a === void 0 ? void 0 : _a.id) === team.id &&
((_b = log === null || log === void 0 ? void 0 : log.project) === null || _b === void 0 ? void 0 : _b.id) === project.id &&
log.user.email === user.email;
});
const otherLogs = logFiles.filter((log) => {
var _a, _b;
return ((_a = log === null || log === void 0 ? void 0 : log.team) === null || _a === void 0 ? void 0 : _a.id) !== team.id &&
((_b = log === null || log === void 0 ? void 0 : log.project) === null || _b === void 0 ? void 0 : _b.id) !== project.id &&
log.user.email !== user.email;
});
let finalData;
if (logWithTeamAndProjectId) {
finalData = [
...otherLogs,
Object.assign(Object.assign({}, logWithTeamAndProjectId), { updatedAt: Date.now(), projectPath: process.cwd() }),
];
}
else {
finalData = [
...otherLogs,
{
team,
user,
project,
projectPath: process.cwd(),
createdAt: Date.now(),
updatedAt: Date.now(),
},
];
}
(0, fs_1.writeFileSync)(logTableFile, JSON.stringify(finalData), "utf8");
};
exports.createProjectLogTable = createProjectLogTable;
const getPathToServiceFiles = () => {
return process.platform === "darwin"
? (0, path_1.join)((0, os_1.homedir)(), "Library", "LaunchAgents", "onboardbase.application.plist")
: (0, path_1.join)("/etc", "systemd", "system", "onboardbase.service");
};
exports.getPathToServiceFiles = getPathToServiceFiles;
const isOnboardbaseServiceInstalled = () => {
return (0, exports.isExist)((0, exports.getPathToServiceFiles)());
};
exports.isOnboardbaseServiceInstalled = isOnboardbaseServiceInstalled;
const isSudoUser = () => {
return process.getuid() === 0 || process.env.SUDO_UID ? true : false;
};
exports.isSudoUser = isSudoUser;
const isUnix = () => {
return ["darwin", "linux"].includes(process.platform) && !(0, isDocker_1.isDocker)();
};
exports.isUnix = isUnix;
const makeRandomId = (length) => {
let result = "";
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result.toLowerCase();
};
exports.makeRandomId = makeRandomId;
const isDirectoryWritable = (path) => {
return new Promise((resolve) => {
(0, fs_1.access)(path, fs_1.constants.W_OK, (err) => {
if (err)
resolve(false);
resolve(true);
});
});
};
exports.isDirectoryWritable = isDirectoryWritable;
const getShellRc = () => {
const shell = process.env.SHELL;
const split = shell.split("/");
const fileName = `.${split[split.length - 1]}rc`;
return (0, path_1.join)(process.env.HOME, fileName);
};
exports.getShellRc = getShellRc;
const isValidURL = (url) => {
let isValid = true;
try {
new URL(url);
}
catch (err) {
isValid = false;
}
return isValid;
};
exports.isValidURL = isValidURL;
const isAPartialUrl = (sourcePath) => {
const isAPartialUrl = (0, exports.isValidURL)(sourcePath) ||
sourcePath.endsWith("json") ||
sourcePath.endsWith("env");
return isAPartialUrl;
};
exports.isAPartialUrl = isAPartialUrl;
const isJSON = (data) => {
let isJson = false;
try {
JSON.parse(data);
isJson = true;
}
catch (error) {
isJson = false;
}
return isJson;
};
exports.isJSON = isJSON;