UNPKG

@tableau/taco-toolkit

Version:
302 lines 23.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.signTacoFile = exports.generateCsr = void 0; var fs_1 = __importDefault(require("fs")); var fs_extra_1 = __importDefault(require("fs-extra")); var path_1 = __importDefault(require("path")); var process_1 = __importDefault(require("process")); var inquirer_1 = __importDefault(require("inquirer")); var logging_1 = require("../utils/logging"); var status_code_1 = require("../../../shared/errors/status-code"); var error_source_1 = require("../../../shared/errors/error-source"); var error_codes_1 = require("../../../shared/errors/error-codes"); var connector_json_1 = require("../utils/connector-json"); var connector_1 = require("../utils/connector"); var command_1 = require("../utils/command"); var constants_1 = require("../utils/constants"); var console_1 = require("../utils/console"); /** * Creates a signing resources folder in the connector folder that contains a newly * created keypair/keystore, a self-signed certificate, and a certificate signing * request that can be sent to a trusted CA for signing. */ function generateCsr(config, state) { var spinner = state.spinner; var tacoRootPath = config.tacoRootPath; var absRootPath = (0, command_1.resolvePath)(tacoRootPath); var connectorName = getConnectorName(tacoRootPath); // Create/locate resource folder var signingResourcePath = path_1.default.join(absRootPath, 'signing_resources'); if (!fs_1.default.existsSync(signingResourcePath)) { fs_1.default.mkdirSync(signingResourcePath); } else { // Empty signing_resources folder fs_extra_1.default.emptyDirSync(signingResourcePath); } spinner.info('Generating Certificate Signing Request\n'); // Generate KeyPair var keystorePath = path_1.default.join(signingResourcePath, 'taco.keystore'); var genkeypairResult = (0, command_1.executeCmd)('keytool', [ '-genkeypair', '-alias', connectorName, '-keystore', keystorePath, '-storetype', 'PKCS12', '-storepass', 'password', '-validity', '3650', '-keyalg', 'RSA', ], { stdio: 'inherit' } // the cmd requires user's input ); if ((0, command_1.isErrorResult)(genkeypairResult)) { spinner.fail('Failed to generate key pair.'); var _a = genkeypairResult.error, error = _a.error, stderr = _a.stderr; (0, logging_1.logFatalError)({ statusCode: status_code_1.StatusCode.NotFound, errorSource: error_source_1.ErrorSources.Content, errorCode: error_codes_1.ErrorCodes.LNQB4OXC, message: "Failed to generate key pair.\nError: ".concat(error, ".\nMore info: ").concat(stderr), error: error, }); process_1.default.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE); } spinner.success("Generated KeyPair with alias '".concat(connectorName, "' at ").concat(keystorePath)); // Export to a .crt file var certPath = path_1.default.join(signingResourcePath, "".concat(connectorName, ".crt")); var exportCertResult = (0, command_1.executeCmd)('keytool', [ '-export', '-alias', connectorName, '-file', certPath, '-keystore', keystorePath, '-storepass', 'password', ]); if ((0, command_1.isErrorResult)(exportCertResult)) { spinner.fail('Failed to generate .crt file.'); var error = exportCertResult.error.error; (0, logging_1.logFatalError)({ statusCode: status_code_1.StatusCode.NotFound, errorSource: error_source_1.ErrorSources.Content, errorCode: error_codes_1.ErrorCodes.LNQB4OXC, message: 'Failed to generate .crt file.', error: error, }); process_1.default.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE); } spinner.success("Exported certificate under ".concat(certPath)); // Generate .csr file var csrPath = path_1.default.join(signingResourcePath, "".concat(connectorName, ".csr")); var generateCsrResult = (0, command_1.executeCmd)('keytool', [ '-certreq', '-alias', connectorName, '-keystore', keystorePath, '-file', csrPath, '-storepass', 'password', // TODO: figure out why it's hardcoded ]); if ((0, command_1.isErrorResult)(generateCsrResult)) { spinner.fail('Failed to generate .csr file.'); var error = generateCsrResult.error.error; (0, logging_1.logFatalError)({ statusCode: status_code_1.StatusCode.NotFound, errorSource: error_source_1.ErrorSources.Content, errorCode: error_codes_1.ErrorCodes.LNQB4OXC, message: 'Failed to generate .csr file.', error: error, }); process_1.default.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE); } spinner.success("Generated CSR at ".concat(csrPath)); spinner.success('A keystore, certificate, and CSR have been generated and are now ' + "located in ".concat(path_1.default.join(process_1.default.cwd(), 'signing_resources'), ". Please keep the following information ") + "on hand for the next steps:\n" + '\n-----------------------------------------------------------------------------------\n' + "\nKeystore path: ".concat(path_1.default.join(tacoRootPath, 'signing_resources', 'taco.keystore'), "\n") + "Keystore password: password\n" + "Alias for your taco: ".concat(connectorName, "\n") + '\n-----------------------------------------------------------------------------------\n' + "Once you have signed the CSR with a CA and imported, run " + 'taco sign --keystore=<keystore path> in order to sign your TACO.\n'); spinner.success("Successfully generated CSR."); } exports.generateCsr = generateCsr; function getConnectorName(tacoRootPath) { try { var connectorJsonPath = (0, connector_1.getConnectorJsonPath)(tacoRootPath); var connectorJson = (0, connector_json_1.getConnectorJson)(connectorJsonPath); return (0, connector_json_1.getConnectorClassName)(connectorJson); } catch (error) { (0, logging_1.logFatalError)({ statusCode: status_code_1.StatusCode.NotFound, errorSource: error_source_1.ErrorSources.Content, errorCode: error_codes_1.ErrorCodes.NAGGOW13, message: 'Failed to generate .csr file.', error: error, }); process_1.default.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE); } } /** * Prompts the user for their keystore password, their signature alias and what they * want their signed TACO to be named, and then signs their .taco file while also * prompting the user to see if they want to delete the original. * * @param keystorePath */ function signTacoFile(config, state) { return __awaiter(this, void 0, void 0, function () { var currentPath, keystorePath, spinner, connectorName, tacoFilename, tacoFilePath, _a, password, alias, saveAsFilename, deleteOriginal, signingResult, _b, error, stderr; return __generator(this, function (_c) { switch (_c.label) { case 0: currentPath = config.currentPath, keystorePath = config.keystorePath; spinner = state.spinner; connectorName = getConnectorName(currentPath); tacoFilename = "".concat(connectorName, ".taco"); tacoFilePath = path_1.default.join(currentPath, tacoFilename); // taco path should be from user? if (!fs_1.default.existsSync(tacoFilePath)) { (0, logging_1.logFatalError)({ statusCode: status_code_1.StatusCode.Cancelled, errorSource: error_source_1.ErrorSources.Content, errorCode: error_codes_1.ErrorCodes.IRGS6QUI, message: "A valid .taco file does not exist in the current directory. Please make sure to run 'taco pack' before continuing.", }); process_1.default.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE); } return [4 /*yield*/, askForSigningInfo(connectorName)]; case 1: _a = _c.sent(), password = _a.password, alias = _a.alias, saveAsFilename = _a.saveAsFilename, deleteOriginal = _a.deleteOriginal; spinner.start("Signing ".concat(tacoFilename, "...")); signingResult = (0, command_1.executeCmd)('jarsigner', [ '-keystore', keystorePath, '-storepass', password, '-signedjar', "".concat(saveAsFilename, ".taco"), tacoFilename, alias, ]); if ((0, command_1.isErrorResult)(signingResult)) { spinner.fail('Signing failed.'); _b = signingResult.error, error = _b.error, stderr = _b.stderr; (0, logging_1.logFatalError)({ statusCode: status_code_1.StatusCode.Cancelled, errorSource: error_source_1.ErrorSources.Client, errorCode: error_codes_1.ErrorCodes.MSAAL1S9, message: "Signing failed with error:\n".concat(error, ". More information: ").concat(stderr), }); process_1.default.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE); } spinner.success("Successfully signed ".concat(tacoFilename, ". A signed taco file '").concat(saveAsFilename, ".taco' has been created.")); if (deleteOriginal) { try { fs_1.default.rmSync(tacoFilename); } catch (error) { (0, console_1.consoleError)("Failed to remove the original file ".concat(tacoFilename), error); process_1.default.exit(1); } logging_1.logger.info("".concat(tacoFilename, " was deleted.")); } return [2 /*return*/]; } }); }); } exports.signTacoFile = signTacoFile; function askForSigningInfo(tacoFilename) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, inquirer_1.default.prompt([ { name: 'password', message: 'What is the keystore password?', type: 'password', mask: '*', default: 'password', // TODO: remove this default value when we figure out the password for KeyPair generation }, { name: 'alias', message: 'What is your key alias?', default: tacoFilename, }, { name: 'saveAsFilename', message: 'Save the signed taco as a separate file? (enter the filename without file extension or press enter to skip)', default: tacoFilename, validate: function (saveAsFilename) { // align with taco filename pattern, number, letter, dash and underscore return /^[\w\-_]+$/.test(saveAsFilename); }, }, { name: 'deleteOriginal', when: function (_a) { var saveAsFilename = _a.saveAsFilename; return saveAsFilename !== tacoFilename; }, message: 'Do you want to remove the original unsigned taco file?', type: 'confirm', default: false, }, ])]; case 1: return [2 /*return*/, _a.sent()]; } }); }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jbGkvdGFjby1jb21tYW5kcy9zaWduL3NpZ24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsMENBQW1CO0FBQ25CLHNEQUEwQjtBQUMxQiw4Q0FBdUI7QUFDdkIsb0RBQTZCO0FBQzdCLHNEQUErQjtBQUUvQiw0Q0FBd0Q7QUFDeEQsa0VBQStEO0FBQy9ELG9FQUFrRTtBQUNsRSxrRUFBK0Q7QUFDL0QsMERBQWlGO0FBQ2pGLGdEQUF5RDtBQUN6RCw0Q0FBeUY7QUFDekYsZ0RBQWlFO0FBQ2pFLDRDQUErQztBQVcvQzs7OztHQUlHO0FBQ0gsU0FBZ0IsV0FBVyxDQUFDLE1BQXlCLEVBQUUsS0FBcUI7SUFDbEUsSUFBQSxPQUFPLEdBQUssS0FBSyxRQUFWLENBQVU7SUFDakIsSUFBQSxZQUFZLEdBQUssTUFBTSxhQUFYLENBQVc7SUFFL0IsSUFBTSxXQUFXLEdBQUcsSUFBQSxxQkFBVyxFQUFDLFlBQVksQ0FBQyxDQUFBO0lBRTdDLElBQU0sYUFBYSxHQUFHLGdCQUFnQixDQUFDLFlBQVksQ0FBQyxDQUFBO0lBRXBELGdDQUFnQztJQUNoQyxJQUFNLG1CQUFtQixHQUFHLGNBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLG1CQUFtQixDQUFDLENBQUE7SUFDdkUsSUFBSSxDQUFDLFlBQUUsQ0FBQyxVQUFVLENBQUMsbUJBQW1CLENBQUMsRUFBRTtRQUN2QyxZQUFFLENBQUMsU0FBUyxDQUFDLG1CQUFtQixDQUFDLENBQUE7S0FDbEM7U0FBTTtRQUNMLGlDQUFpQztRQUNqQyxrQkFBRyxDQUFDLFlBQVksQ0FBQyxtQkFBbUIsQ0FBQyxDQUFBO0tBQ3RDO0lBRUQsT0FBTyxDQUFDLElBQUksQ0FBQywwQ0FBMEMsQ0FBQyxDQUFBO0lBQ3hELG1CQUFtQjtJQUNuQixJQUFNLFlBQVks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