@onboardbase/cli
Version:
[](https://www.npmjs.com/package/@onboardbase/cli) [](https://www.npmjs.com/package/@onboardbase/cli) [ • 9.78 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RunCommandService = void 0;
const utils_1 = require("../utils");
const chalk = require("chalk");
const inquirer = require("inquirer");
const kill = require("tree-kill");
const base_service_1 = require("../common/base.service");
const child_process_1 = require("child_process");
const secrets_1 = require("./secrets");
const socket_service_1 = require("./socket.service");
const access_manager_1 = require("./access-manager");
const types_1 = require("../common/types");
const errors_1 = require("../common/errors");
class RunCommandService extends base_service_1.BaseService {
constructor(configManager) {
super(configManager);
this.reInitializeReason = "remote updates";
this.shouldReInitialize = false;
this.currentSecrets = null;
this.secretService = new secrets_1.SecretsService(configManager);
this.socketService = new socket_service_1.SocketService(configManager);
this.accessManager = new access_manager_1.AccessManager(configManager);
}
_getPrefix(userPrefix) {
const prefix = userPrefix !== null && userPrefix !== void 0 ? userPrefix : this.configManager.getFromProject("setup.prefix");
return prefix;
}
_executeCommandAsAProcess(cmd, envs) {
this.currentDaemon = (0, child_process_1.spawnSync)(cmd, {
env: Object.assign(envs, { FORCE_COLOR: true }),
cwd: process.cwd(),
stdio: "inherit",
windowsHide: false,
shell: true,
});
}
async initialize({ args, flags }) {
const commandToExecute = this._getFromProjectConfigOrThrow({
userConfig: args.command || flags.command,
configPath: "setup.start_script",
errMsg: "Please specify the command to run",
});
const config = this.getConfigForCurrentScope();
if (config.requirePasswordForCurrentSession) {
const passwordVerified = await this.verifyPassword(flags.password, config.password);
if (!passwordVerified) {
throw errors_1.BadInputError.from("password not verified");
}
}
const currentPrefix = this.configManager.getEnvPrefix(flags.prefix);
const genericSecret = flags["secrets"];
const sourcePath = flags["source-path"];
const verbose = flags["verbose"];
if (genericSecret) {
const envs = this.secretService.parseUserGeneratedSecrets(genericSecret);
const mergedEnvs = this.secretService.generateEnvs(envs, currentPrefix, verbose);
this._executeCommandAsAProcess(commandToExecute, mergedEnvs);
return;
}
if (sourcePath) {
const envs = this.secretService.getSecretsFromSourcePath(sourcePath);
const mergedEnvs = this.secretService.generateEnvs(envs, currentPrefix);
this._executeCommandAsAProcess(commandToExecute, mergedEnvs);
return;
}
const { currentProject, currentEnvironment } = this._handleGetProjectAndEnvironment(flags);
const sync = flags["sync"];
const shouldHandleLiveReload = flags["live-reload"];
const shouldAcceptSecretShare = flags["accept-secret-sharing"];
const cliToken = this._getFromGlobalConfigOrThrow({
configPath: "token",
envName: types_1.ENV_NAMES.TOKEN,
});
await this.handleUnauthorizedEnvironmentAccess(cliToken, currentProject, currentEnvironment);
// Although a project and an environment might not exist in
// the current team,
// the API does respond with an empty array list of secrets.
// in the event that a project does not exist or does not have secrets
// or the user does not have access to the project.
// We have to prevent secret leaks which is assumed to be best done in the
// API layer.
//
// Force Sync only if the last successful project known in the global config
// is not the same with the provided project however it was provided.
const forceSync = this.shouldForceSync(currentProject);
const resolvedSecrets = await this.secretService.getSecrets(cliToken, currentProject, currentEnvironment, forceSync || sync //
);
const mergedEnvs = this.secretService.generateEnvs(resolvedSecrets, currentPrefix, verbose);
await this._setupSideEffects({
cliToken,
project: currentProject,
environment: currentEnvironment,
shouldHandleLiveReload,
shouldAcceptSecretShare,
});
this._executeCommandAsAProcess(commandToExecute, mergedEnvs);
if (this.shouldReInitialize) {
await this._reInitialize(args, flags);
}
this._runCleanups();
}
shouldForceSync(suppliedProjectName) {
const configProjectName = this.getConfigForCurrentScope("project-name");
return suppliedProjectName && configProjectName !== suppliedProjectName;
}
async handleUnauthorizedEnvironmentAccess(cliToken, currentProject, currentEnvironment) {
// always ensure user has access to this project and environment
const isAuthorized = await this.accessManager.checkUserAccess(cliToken, currentProject, currentEnvironment);
if (!isAuthorized) {
this.secretService.clearCache(currentProject, currentEnvironment);
throw errors_1.BadNetworkError.from(`You do not have access to this project and/or environment`);
}
}
async _reInitialize(args, flags) {
this.shouldReInitialize = false;
// Recursively re-initializes this command,
// this is used for restarts
console.log("Re-starting session...", this.reInitializeReason ? `due to ${this.reInitializeReason}` : "");
this._stopCurrentDaemon();
await this.initialize({ args, flags: Object.assign({}, flags) });
}
async _setupSideEffects({ cliToken, project, environment, shouldHandleLiveReload, shouldAcceptSecretShare, }) {
const { user } = await this.accessManager.getAuthInfoFromDeviceToken(cliToken);
if (!user) {
console.log("Please authenticate your CLI");
return;
}
await this.socketService.createSocketInstance({
project,
environment,
userEmail: user.email,
});
if (shouldHandleLiveReload) {
this.socketService.onUpdateEventEmitted(this._handleLiveReload);
}
if (shouldAcceptSecretShare) {
this.socketService.onSecretShareEventEmitted(user.email, project, this._handleSecretShareEvent);
}
}
async _handleLiveReload() {
this.reInitializeReason = `${chalk.green("√")} ${chalk.bold(chalk.gray("Fetched new secrets.. and restarting your command"))}`;
this.shouldReInitialize = true;
await this._stopCurrentDaemon();
}
async _handleSecretShareEvent(data) {
// Load the .onboardbase file or create new one if it does not exist
// const localSecrets = getLocalSecrets() ?? {};
// // Add the new suggestions to the .onboardbase file
// if (data.secrets && data.secrets.length) {
// await Promise.all(
// data.secrets.map(async (encryptedSecret) => {
// const decryptedSecret = await decryptPlainSecretAndReturnPlainValue(
// encryptedSecret,
// data.email
// );
// const secret = JSON.parse(decryptedSecret);
// localSecrets[secret.key] = secret.value;
// })
// );
// }
// // Update the onboardbase file
// updateLocalSecrets(localSecrets);
this.shouldReInitialize = true;
this.reInitializeReason = chalk.green(`!A new secret has been suggested by ${chalk.bold.green(data.email)}...`);
await this._stopCurrentDaemon();
}
async _runCleanups() {
this.socketService.closeSocket();
}
async _stopCurrentDaemon() {
try {
kill(-this.currentDaemon.pid);
}
catch (error) {
process.exit(0);
}
}
async getPasswordFromUser() {
const { sessionPassword } = await inquirer.prompt([
{
message: "Password Required",
type: "password",
name: "sessionPassword",
},
]);
if (!sessionPassword) {
throw errors_1.BadInputError.from("Password required for this session");
}
return sessionPassword;
}
async validatePassword(userPassword, configPasswordCipher) {
const configPassword = await (0, utils_1.decryptGenericSecret)(configPasswordCipher);
return userPassword.trim() === configPassword.toString();
}
async verifyPassword(plainPassword, encryptedConfigPassword) {
if (!plainPassword) {
plainPassword = await this.getPasswordFromUser();
}
const validatonResult = await this.validatePassword(plainPassword, encryptedConfigPassword);
return validatonResult;
}
_registerProjectAccessLog(project, environment) {
// * Only a create a project log when CLI is able to fetch secrets successfully.
// */
// if (ConfigManager.shouldCreateProjectLog) {
// createProjectLogTable(
// { email: ConfigManager.getAuthSessionDetails().email },
// ConfigManager.getAuthSessionDetails().team,
// ConfigManager.getAuthSessionDetails().project
// );
// }
}
}
exports.RunCommandService = RunCommandService;