UNPKG

ionic

Version:

A tool for creating and developing Ionic Framework mobile apps.

187 lines (182 loc) • 8.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const format_1 = require("@ionic/cli-framework/utils/format"); const utils_fs_1 = require("@ionic/utils-fs"); const lodash = require("lodash"); const path = require("path"); const color_1 = require("../../lib/color"); const errors_1 = require("../../lib/errors"); const base_1 = require("./base"); const DEFAULT_BITS = '2048'; const DEFAULT_COUNTRY_NAME = 'US'; const DEFAULT_STATE_OR_PROVINCE_NAME = 'Wisconsin'; const DEFAULT_LOCALITY_NAME = 'Madison'; const DEFAULT_ORGANIZATION_NAME = 'Ionic'; const DEFAULT_COMMON_NAME = 'localhost'; const DEFAULT_KEY_FILE = '.ionic/ssl/key.pem'; const DEFAULT_CERT_FILE = '.ionic/ssl/cert.pem'; class SSLGenerateCommand extends base_1.SSLBaseCommand { getDefaultKeyPath() { return path.resolve(this.project ? this.project.directory : '', DEFAULT_KEY_FILE); } getDefaultCertPath() { return path.resolve(this.project ? this.project.directory : '', DEFAULT_CERT_FILE); } async getMetadata() { const defaultKeyPath = format_1.prettyPath(this.getDefaultKeyPath()); const defaultCertPath = format_1.prettyPath(this.getDefaultCertPath()); return { name: 'generate', type: 'project', summary: 'Generates an SSL key & certificate', // TODO: document how to add trusted certs description: ` Uses OpenSSL to create a self-signed certificate for ${color_1.strong('localhost')} (by default). After the certificate is generated, you will still need to add it to your system or browser as a trusted certificate. The default directory for ${color_1.input('--key-path')} and ${color_1.input('--cert-path')} is ${color_1.input('.ionic/ssl/')}. `, options: [ { name: 'key-path', summary: 'Destination of private key file', default: defaultKeyPath, spec: { value: 'path' }, }, { name: 'cert-path', summary: 'Destination of certificate file', default: defaultCertPath, spec: { value: 'path' }, }, { name: 'country-name', summary: 'The country name (C) of the SSL certificate', default: DEFAULT_COUNTRY_NAME, groups: ["advanced" /* ADVANCED */], spec: { value: 'C' }, }, { name: 'state-or-province-name', summary: 'The state or province name (ST) of the SSL certificate', default: DEFAULT_STATE_OR_PROVINCE_NAME, groups: ["advanced" /* ADVANCED */], spec: { value: 'ST' }, }, { name: 'locality-name', summary: 'The locality name (L) of the SSL certificate', default: DEFAULT_LOCALITY_NAME, groups: ["advanced" /* ADVANCED */], spec: { value: 'L' }, }, { name: 'organization-name', summary: 'The organization name (O) of the SSL certificate', default: DEFAULT_ORGANIZATION_NAME, groups: ["advanced" /* ADVANCED */], spec: { value: 'O' }, }, { name: 'common-name', summary: 'The common name (CN) of the SSL certificate', default: DEFAULT_COMMON_NAME, groups: ["advanced" /* ADVANCED */], spec: { value: 'CN' }, }, { name: 'bits', summary: 'Number of bits in the key', aliases: ['b'], default: DEFAULT_BITS, groups: ["advanced" /* ADVANCED */], }, ], groups: ["experimental" /* EXPERIMENTAL */], }; } async preRun(inputs, options) { await this.checkForOpenSSL(); } async run(inputs, options) { if (!this.project) { throw new errors_1.FatalException(`Cannot run ${color_1.input('ionic ssl generate')} outside a project directory.`); } const keyPath = path.resolve(options['key-path'] ? String(options['key-path']) : this.getDefaultKeyPath()); const keyPathDir = path.dirname(keyPath); const certPath = path.resolve(options['cert-path'] ? String(options['cert-path']) : this.getDefaultCertPath()); const certPathDir = path.dirname(certPath); const bits = options['bits'] ? String(options['bits']) : DEFAULT_BITS; const countryName = options['country-name'] ? String(options['country-name']) : DEFAULT_COUNTRY_NAME; const stateOrProvinceName = options['state-or-province-name'] ? String(options['state-or-province-name']) : DEFAULT_STATE_OR_PROVINCE_NAME; const localityName = options['locality-name'] ? String(options['locality-name']) : DEFAULT_LOCALITY_NAME; const organizationName = options['organization-name'] ? String(options['organization-name']) : DEFAULT_ORGANIZATION_NAME; const commonName = options['common-name'] ? String(options['common-name']) : DEFAULT_COMMON_NAME; await this.ensureDirectory(keyPathDir); await this.ensureDirectory(certPathDir); const overwriteKeyPath = await this.checkExistingFile(keyPath); const overwriteCertPath = await this.checkExistingFile(certPath); if (overwriteKeyPath) { await utils_fs_1.unlink(keyPath); } if (overwriteCertPath) { await utils_fs_1.unlink(certPath); } const cnf = { bits, countryName, stateOrProvinceName, localityName, organizationName, commonName }; const cnfPath = await this.writeConfig(cnf); await this.env.shell.run('openssl', ['req', '-x509', '-newkey', `rsa:${bits}`, '-nodes', '-subj', this.formatSubj(cnf), '-reqexts', 'SAN', '-extensions', 'SAN', '-config', cnfPath, '-days', '365', '-keyout', keyPath, '-out', certPath], {}); this.env.log.nl(); this.env.log.rawmsg(`Key: ${color_1.strong(format_1.prettyPath(keyPath))}\n` + `Cert: ${color_1.strong(format_1.prettyPath(certPath))}\n\n`); this.env.log.ok('Generated key & certificate!'); } formatSubj(cnf) { const subjNames = new Map([ ['countryName', 'C'], ['stateOrProvinceName', 'ST'], ['localityName', 'L'], ['organizationName', 'O'], ['commonName', 'CN'], ]); return '/' + lodash.toPairs(cnf).filter(([k]) => subjNames.has(k)).map(([k, v]) => `${subjNames.get(k)}=${v}`).join('/'); } async ensureDirectory(p) { if (!(await utils_fs_1.pathExists(p))) { await utils_fs_1.mkdirp(p, 0o700); // tslint:disable-line this.env.log.msg(`Created ${color_1.strong(format_1.prettyPath(p))} directory for you.`); } } async checkExistingFile(p) { if (await utils_fs_1.pathExists(p)) { const confirm = await this.env.prompt({ type: 'confirm', name: 'confirm', message: `Key ${color_1.strong(format_1.prettyPath(p))} exists. Overwrite?`, }); if (confirm) { return true; } else { throw new errors_1.FatalException(`Not overwriting ${color_1.strong(format_1.prettyPath(p))}.`); } } } async writeConfig({ bits, countryName, stateOrProvinceName, localityName, organizationName, commonName }) { const cnf = ` [req] default_bits = ${bits} distinguished_name = req_distinguished_name [req_distinguished_name] countryName = ${countryName} stateOrProvinceName = ${stateOrProvinceName} localityName = ${localityName} organizationName = ${organizationName} commonName = ${commonName} [SAN] subjectAltName=DNS:${commonName} `.trim(); const p = utils_fs_1.tmpfilepath('ionic-ssl'); await utils_fs_1.writeFile(p, cnf, { encoding: 'utf8' }); return p; } } exports.SSLGenerateCommand = SSLGenerateCommand;