From 2119e9de9ab1e5ce1222658f554ac74f4f35cf4d Mon Sep 17 00:00:00 2001 From: Saad Jutt Date: Tue, 21 Jun 2022 03:17:14 +0500 Subject: [PATCH] feat(certs): ENV variables updated and set CA Root for HTTPS server --- README.md | 3 ++- api/src/controllers/internal/Execution.ts | 19 +++++++++++---- .../internal/FileUploadController.ts | 24 +++++++++++++++---- api/src/controllers/internal/Session.ts | 11 +++++---- .../controllers/internal/createJSProgram.ts | 5 ++-- .../controllers/internal/processProgram.ts | 2 +- api/src/server.ts | 4 ++-- api/src/types/system/process.d.ts | 4 ++-- api/src/utils/getCertificates.ts | 16 +++++++++---- api/src/utils/getDesktopFields.ts | 17 ++++++++----- api/src/utils/setProcessVariables.ts | 11 ++++----- api/src/utils/verifyEnvVariables.ts | 8 +++---- 12 files changed, 81 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 2a4df5b..5d8ca20 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,8 @@ SASV9_OPTIONS= -NOXCMD # ENV variables required for PROTOCOL: `https` PRIVATE_KEY=privkey.pem -FULL_CHAIN=fullchain.pem +CERT_CHAIN=certificate.pem +CA_ROOT=fullchain.pem # ENV variables required for MODE: `server` ACCESS_TOKEN_SECRET= diff --git a/api/src/controllers/internal/Execution.ts b/api/src/controllers/internal/Execution.ts index b742402..60d3508 100644 --- a/api/src/controllers/internal/Execution.ts +++ b/api/src/controllers/internal/Execution.ts @@ -1,6 +1,8 @@ import path from 'path' import fs from 'fs' import { + SASSessionController, + JSSessionController, getSASSessionController, getJSSessionController, processProgram @@ -76,10 +78,19 @@ export class ExecutionController { session: sessionByFileUpload, runTime }: ExecuteProgramParams): Promise { - const sessionController = - runTime === RunTimeType.SAS - ? getSASSessionController() - : getJSSessionController() + let sessionController: SASSessionController | JSSessionController + + switch (runTime) { + case RunTimeType.SAS: + sessionController = getSASSessionController() + break + case RunTimeType.JS: + sessionController = getJSSessionController() + break + + default: + throw new Error('No Runtime is configured1') + } const session = sessionByFileUpload ?? (await sessionController.getSession()) diff --git a/api/src/controllers/internal/FileUploadController.ts b/api/src/controllers/internal/FileUploadController.ts index 7c6737c..92af4e9 100644 --- a/api/src/controllers/internal/FileUploadController.ts +++ b/api/src/controllers/internal/FileUploadController.ts @@ -1,7 +1,12 @@ import { Request, RequestHandler } from 'express' import multer from 'multer' import { uuidv4 } from '@sasjs/utils' -import { getSASSessionController, getJSSessionController } from '.' +import { + SASSessionController, + JSSessionController, + getSASSessionController, + getJSSessionController +} from '.' import { executeProgramRawValidation, getRunTimeAndFilePath, @@ -44,10 +49,19 @@ export class FileUploadController { }) } - const sessionController = - runTime === RunTimeType.SAS - ? getSASSessionController() - : getJSSessionController() + let sessionController: SASSessionController | JSSessionController + + switch (runTime) { + case RunTimeType.SAS: + sessionController = getSASSessionController() + break + case RunTimeType.JS: + sessionController = getJSSessionController() + break + + default: + return res.status(400).send('No Runtime is configured1') + } const session = await sessionController.getSession() // marking consumed true, so that it's not available diff --git a/api/src/controllers/internal/Session.ts b/api/src/controllers/internal/Session.ts index dc29866..4080f16 100644 --- a/api/src/controllers/internal/Session.ts +++ b/api/src/controllers/internal/Session.ts @@ -12,7 +12,8 @@ import { createFile, fileExists, generateTimestamp, - readFile + readFile, + isWindows } from '@sasjs/utils' const execFilePromise = promisify(execFile) @@ -88,7 +89,7 @@ ${autoExecContent}` // Additional windows specific options to avoid the desktop popups. - execFilePromise(process.sasLoc, [ + execFilePromise(process.sasLoc!, [ '-SYSIN', codePath, '-LOG', @@ -99,9 +100,9 @@ ${autoExecContent}` session.path, '-AUTOEXEC', autoExecPath, - process.platform === 'win32' ? '-nosplash' : '', - process.platform === 'win32' ? '-icon' : '', - process.platform === 'win32' ? '-nologo' : '' + isWindows() ? '-nosplash' : '', + isWindows() ? '-icon' : '', + isWindows() ? '-nologo' : '' ]) .then(() => { session.completed = true diff --git a/api/src/controllers/internal/createJSProgram.ts b/api/src/controllers/internal/createJSProgram.ts index 5176dae..b1e041c 100644 --- a/api/src/controllers/internal/createJSProgram.ts +++ b/api/src/controllers/internal/createJSProgram.ts @@ -1,3 +1,4 @@ +import { isWindows } from '@sasjs/utils' import { PreProgramVars, Session } from '../../types' import { generateFileUploadJSCode } from '../../utils' import { ExecutionVars } from './' @@ -20,9 +21,7 @@ export const createJSProgram = async ( const preProgramVarStatments = ` let _webout = ''; const weboutPath = '${ - process.platform === 'win32' - ? weboutPath.replace(/\\/g, '\\\\') - : weboutPath + isWindows() ? weboutPath.replace(/\\/g, '\\\\') : weboutPath }'; const _sasjs_tokenfile = '${tokenFile}'; const _sasjs_username = '${preProgramVariables?.username}'; diff --git a/api/src/controllers/internal/processProgram.ts b/api/src/controllers/internal/processProgram.ts index f9f3f6b..c059797 100644 --- a/api/src/controllers/internal/processProgram.ts +++ b/api/src/controllers/internal/processProgram.ts @@ -40,7 +40,7 @@ export const processProgram = async ( // waiting for the open event so that we can have underlying file descriptor await once(writeStream, 'open') - execFileSync(process.nodeLoc, [codePath], { + execFileSync(process.nodeLoc!, [codePath], { stdio: ['ignore', writeStream, writeStream] }) diff --git a/api/src/server.ts b/api/src/server.ts index 3745169..0743469 100644 --- a/api/src/server.ts +++ b/api/src/server.ts @@ -16,9 +16,9 @@ appPromise.then(async (app) => { ) }) } else { - const { key, cert } = await getCertificates() + const { key, cert, ca } = await getCertificates() - const httpsServer = createServer({ key, cert }, app) + const httpsServer = createServer({ key, cert, ca }, app) httpsServer.listen(sasJsPort, () => { console.log( `⚡️[server]: Server is running at https://localhost:${sasJsPort}` diff --git a/api/src/types/system/process.d.ts b/api/src/types/system/process.d.ts index 45f9572..1e56d75 100644 --- a/api/src/types/system/process.d.ts +++ b/api/src/types/system/process.d.ts @@ -1,7 +1,7 @@ declare namespace NodeJS { export interface Process { - sasLoc: string - nodeLoc: string + sasLoc?: string + nodeLoc?: string driveLoc: string sasSessionController?: import('../../controllers/internal').SASSessionController jsSessionController?: import('../../controllers/internal').JSSessionController diff --git a/api/src/utils/getCertificates.ts b/api/src/utils/getCertificates.ts index e1e96e7..8b59a26 100644 --- a/api/src/utils/getCertificates.ts +++ b/api/src/utils/getCertificates.ts @@ -2,22 +2,30 @@ import path from 'path' import { fileExists, getString, readFile } from '@sasjs/utils' export const getCertificates = async () => { - const { PRIVATE_KEY, FULL_CHAIN } = process.env + const { PRIVATE_KEY, CERT_CHAIN, CA_ROOT } = process.env const keyPath = PRIVATE_KEY ?? (await getFileInput('Private Key (PEM)')) - const certPath = FULL_CHAIN ?? (await getFileInput('Full Chain (PEM)')) + const certPath = CERT_CHAIN ?? (await getFileInput('Certificate Chain (PEM)')) + const caPath = CA_ROOT ?? (await getFileInput('CA ROOT (PEM)')) console.log('keyPath: ', keyPath) console.log('certPath: ', certPath) + console.log('caPath: ', caPath) const key = await readFile(keyPath) const cert = await readFile(certPath) + const ca = await readFile(caPath) - return { key, cert } + return { key, cert, ca } } -const getFileInput = async (filename: string): Promise => { +const getFileInput = async ( + filename: string, + required: boolean = true +): Promise => { const validator = async (filePath: string) => { + if (!required) return true + if (!filePath) return `Path to ${filename} is required.` if (!(await fileExists(path.join(process.cwd(), filePath)))) { diff --git a/api/src/utils/getDesktopFields.ts b/api/src/utils/getDesktopFields.ts index 972ef78..e1e0966 100644 --- a/api/src/utils/getDesktopFields.ts +++ b/api/src/utils/getDesktopFields.ts @@ -1,15 +1,20 @@ import path from 'path' import { getString } from '@sasjs/utils/input' -import { createFolder, fileExists, folderExists } from '@sasjs/utils' - -const isWindows = () => process.platform === 'win32' +import { createFolder, fileExists, folderExists, isWindows } from '@sasjs/utils' +import { RunTimeType } from './verifyEnvVariables' export const getDesktopFields = async () => { const { SAS_PATH, NODE_PATH } = process.env - const sasLoc = SAS_PATH ?? (await getSASLocation()) - const nodeLoc = NODE_PATH ?? (await getNodeLocation()) - // const driveLoc = DRIVE_PATH ?? (await getDriveLocation()) + let sasLoc, nodeLoc + + if (process.runTimes.includes(RunTimeType.SAS)) { + sasLoc = SAS_PATH ?? (await getSASLocation()) + } + + if (process.runTimes.includes(RunTimeType.JS)) { + nodeLoc = NODE_PATH ?? (await getNodeLocation()) + } return { sasLoc, nodeLoc } } diff --git a/api/src/utils/setProcessVariables.ts b/api/src/utils/setProcessVariables.ts index d365ec1..f6f21b0 100644 --- a/api/src/utils/setProcessVariables.ts +++ b/api/src/utils/setProcessVariables.ts @@ -9,11 +9,13 @@ export const setProcessVariables = async () => { return } - const { MODE } = process.env + const { MODE, RUN_TIMES } = process.env + + process.runTimes = (RUN_TIMES?.split(',') as RunTimeType[]) ?? [] if (MODE === ModeType.Server) { - process.sasLoc = process.env.SAS_PATH as string - process.nodeLoc = process.env.NODE_PATH as string + process.sasLoc = process.env.SAS_PATH + process.nodeLoc = process.env.NODE_PATH } else { const { sasLoc, nodeLoc } = await getDesktopFields() @@ -26,9 +28,6 @@ export const setProcessVariables = async () => { await createFolder(absPath) process.driveLoc = getRealPath(absPath) - const { RUN_TIMES } = process.env - process.runTimes = (RUN_TIMES as string).split(',') as RunTimeType[] - console.log('sasLoc: ', process.sasLoc) console.log('sasDrive: ', process.driveLoc) console.log('runTimes: ', process.runTimes) diff --git a/api/src/utils/verifyEnvVariables.ts b/api/src/utils/verifyEnvVariables.ts index d53338f..8ab857f 100644 --- a/api/src/utils/verifyEnvVariables.ts +++ b/api/src/utils/verifyEnvVariables.ts @@ -129,16 +129,16 @@ const verifyPROTOCOL = (): string[] => { } if (process.env.PROTOCOL === ProtocolType.HTTPS) { - const { PRIVATE_KEY, FULL_CHAIN } = process.env + const { PRIVATE_KEY, CERT_CHAIN } = process.env if (!PRIVATE_KEY) errors.push( `- PRIVATE_KEY is required for PROTOCOL '${ProtocolType.HTTPS}'` ) - if (!FULL_CHAIN) + if (!CERT_CHAIN) errors.push( - `- FULL_CHAIN is required for PROTOCOL '${ProtocolType.HTTPS}'` + `- CERT_CHAIN is required for PROTOCOL '${ProtocolType.HTTPS}'` ) } @@ -258,5 +258,5 @@ const DEFAULTS = { PORT: '5000', HELMET_COEP: HelmetCoepType.TRUE, LOG_FORMAT_MORGAN: LOG_FORMAT_MORGANType.Common, - RUN_TIMES: `${RunTimeType.SAS}` + RUN_TIMES: RunTimeType.SAS }