diff --git a/package.json b/package.json index 50f3e1dc6..c351a7eae 100644 --- a/package.json +++ b/package.json @@ -349,9 +349,9 @@ "default": "8005", "description": "Port to connect to IBM i Debug Service." }, - "debugSecure": { + "debugIsSecure": { "type": "boolean", - "default": true, + "default": false, "description": "Used to determine if the client should connect securely or not." }, "debugUpdateProductionFiles": { @@ -786,12 +786,12 @@ }, { "command": "code-for-ibmi.debug.setup.local", - "title": "Setup local certificate", + "title": "Import local certificate", "category": "IBM i Debug" }, { "command": "code-for-ibmi.debug.start", - "title": "Start debug server", + "title": "Start debug service", "category": "IBM i Debug" }, { diff --git a/src/api/Configuration.ts b/src/api/Configuration.ts index 347151be1..6ab14494f 100644 --- a/src/api/Configuration.ts +++ b/src/api/Configuration.ts @@ -38,7 +38,7 @@ export namespace ConnectionConfiguration { autoSaveBeforeAction: boolean; showDescInLibList: boolean; debugPort: string; - debugSecure: boolean; + debugIsSecure: boolean; debugUpdateProductionFiles: boolean; debugEnableDebugTracing: boolean; [name: string]: any; @@ -105,7 +105,7 @@ export namespace ConnectionConfiguration { autoSaveBeforeAction : (parameters.autoSaveBeforeAction === true), showDescInLibList : (parameters.showDescInLibList === true), debugPort: (parameters.debugPort || "8005"), - debugSecure: (parameters.debugSecure === true || parameters.debugSecure === undefined), + debugIsSecure: (parameters.debugIsSecure === true), debugUpdateProductionFiles: (parameters.debugUpdateProductionFiles === true), debugEnableDebugTracing: (parameters.debugEnableDebugTracing === true), } diff --git a/src/api/debug/certificates.ts b/src/api/debug/certificates.ts index 819d06129..6df21b0a7 100644 --- a/src/api/debug/certificates.ts +++ b/src/api/debug/certificates.ts @@ -11,7 +11,7 @@ export function getKeystorePath() { return path.posix.join(directory, pfxName); } -export function getLocalCert(connection: IBMi) { +export function getLocalCertPath(connection: IBMi) { const host = connection.currentHost; return path.join(os.homedir(), `${host}_${crtName}`); } @@ -59,17 +59,10 @@ export async function setup(connection: IBMi) { export async function checkLocalExists(connection: IBMi) { try { - await fs.stat(getLocalCert(connection)); + await fs.stat(getLocalCertPath(connection)); // TODO: if local exists, but it's out of date with the server? e.g. md5 is different for example return true; } catch (e) { return false; } -} - -export function downloadToLocal(connection: IBMi) { - return connection.downloadFile( - getLocalCert(connection), - path.posix.join(directory, crtName) - ); } \ No newline at end of file diff --git a/src/api/debug/index.ts b/src/api/debug/index.ts index 91831e9c9..374ee0064 100644 --- a/src/api/debug/index.ts +++ b/src/api/debug/index.ts @@ -6,6 +6,7 @@ import path from "path"; import * as certificates from "./certificates"; import * as server from "./server"; +import { copyFileSync } from "fs"; const debugExtensionId = `IBM.ibmidebug`; @@ -158,26 +159,30 @@ export async function initialise(instance: Instance, context: ExtensionContext) vscode.window.showInformationMessage(`Certificates already exist on the server.`); remoteCertsOk = true; } else { - const doSetup = await vscode.window.showInformationMessage(`Debug setup`, { - modal: true, - detail: `Debug certificates are not setup on the system. Continue with setup?` - }, `Continue`); - - if (doSetup) { - try { - await certificates.setup(connection); - vscode.window.showInformationMessage(`Certificates successfully generated on server.`); - remoteCertsOk = true; - remoteCertsAreNew = true; - } catch (e: any) { - vscode.window.showErrorMessage(e.message || e); - } + + } + + const doSetup = await vscode.window.showInformationMessage(`Debug setup`, { + modal: true, + detail: `${remoteExists + ? `Debug certificates already exist on this system! Running this setup will overwrite them, which will require the debug service to be restarted.` + : `Debug certificates are not setup on the system.` + } Continue with setup?` + }, `Continue`); + + if (doSetup) { + try { + await certificates.setup(connection); + vscode.window.showInformationMessage(`Certificates successfully generated on server.`); + remoteCertsOk = true; + remoteCertsAreNew = true; + } catch (e: any) { + vscode.window.showErrorMessage(e.message || e); } } if (remoteCertsOk) { vscode.commands.executeCommand(`setContext`, remoteCertContext, true); - vscode.commands.executeCommand(`code-for-ibmi.debug.setup.local`, remoteCertsAreNew); } } else { vscode.window.showErrorMessage(`Debug PTF not installed.`); @@ -188,29 +193,49 @@ export async function initialise(instance: Instance, context: ExtensionContext) } }), - vscode.commands.registerCommand(`code-for-ibmi.debug.setup.local`, async (force: boolean = false) => { + vscode.commands.registerCommand(`code-for-ibmi.debug.setup.local`, async () => { const connection = instance.connection; if (connection) { const ptfInstalled = await debugPTFInstalled(); if (ptfInstalled) { - const localExists = await certificates.checkLocalExists(connection); let localCertsOk = false; - - if (localExists && !force) { - localCertsOk = true; - vscode.window.showInformationMessage(`Debug certificates already exist locally. Skipping this step.`); - } else { - try { - await certificates.downloadToLocal(connection); - vscode.window.showInformationMessage(`Debug certificates successfully downloaded to local device.`); - localCertsOk = true; - } catch (e: any) { - vscode.window.showErrorMessage(`Failed to download new local debug certificate.`); + if (connection.config!.debugIsSecure) { + const selection = await vscode.window.showInformationMessage( + `Client certificate`, + { + modal: true, + detail: `To debug securely, a client certificate needs to be imported.` + }, + `Import certificate` + ); + + if (selection === `Import certificate`) { + const selectedFile = await vscode.window.showOpenDialog({ + canSelectFiles: true, + canSelectFolders: false, + canSelectMany: false, + title: `Select client certificate` + }); + + if (selectedFile && selectedFile.length === 1) { + try { + copyFileSync(selectedFile[0].fsPath, certificates.getLocalCertPath(connection)); + localCertsOk = true; + vscode.window.showInformationMessage(`Certificate imported.`); + } catch (e) { + vscode.window.showErrorMessage(`Failed to import local certificate.`); + } + } } + } else { + vscode.window.showWarningMessage(`Certificates can only be imported when secure mode is enabled.`, `Open configuration`).then(result => { + if (result === `Open configuration`) { + vscode.commands.executeCommand(`code-for-ibmi.showAdditionalSettings`); + } + }); } - if (localCertsOk) { vscode.commands.executeCommand(`setContext`, localCertContext, true); } @@ -228,44 +253,35 @@ export async function initialise(instance: Instance, context: ExtensionContext) const remoteExists = await certificates.checkRemoteExists(connection); if (remoteExists) { - const localExists = await certificates.checkLocalExists(connection); - if (localExists) { - vscode.window.withProgress({ location: vscode.ProgressLocation.Notification }, async (progress) => { + vscode.window.withProgress({ location: vscode.ProgressLocation.Notification }, async (progress) => { - let startupService = false; + let startupService = false; - progress.report({ increment: 20, message: `Checking if service is already running.` }); - const isRunning = await server.isRunning(connection.config?.debugPort || "8005", instance.content!); + progress.report({ increment: 20, message: `Checking if service is already running.` }); + const isRunning = await server.isRunning(connection.config?.debugPort || "8005", instance.content!); - if (isRunning) { - const confirmEndServer = await vscode.window.showInformationMessage(`Starting debug service`, { - detail: `Looks like the debug service is currently running. Do you want to end it to start a new instance?`, - modal: true - }, `End service`); + if (isRunning) { + const confirmEndServer = await vscode.window.showInformationMessage(`Starting debug service`, { + detail: `Looks like the debug service is currently running. Do you want to end it to start a new instance?`, + modal: true + }, `End service`); - if (confirmEndServer === `End service`) { - progress.report({ increment: 25, message: `Ending currently running service.` }); - const endResult = await server.end(connection); - startupService = true; - } - } else { + if (confirmEndServer === `End service`) { + progress.report({ increment: 25, message: `Ending currently running service.` }); + const endResult = await server.end(connection); startupService = true; } + } else { + startupService = true; + } - if (startupService) { - progress.report({ increment: 25, message: `Starting service up.` }); - await server.startup(connection); - } else { - vscode.window.showInformationMessage(`Cancelled startup of debug service.`); - } - }) - - } else { - const localResult = await vscode.window.showErrorMessage(`Local debug certificate does not exist.`, `Setup`); - if (localResult === `Setup`) { - vscode.commands.executeCommand(`code-for-ibmi.debug.setup.local`); + if (startupService) { + progress.report({ increment: 25, message: `Starting service up.` }); + await server.startup(connection); + } else { + vscode.window.showInformationMessage(`Cancelled startup of debug service.`); } - } + }) } else { vscode.commands.executeCommand(`code-for-ibmi.debug.setup.remote`); @@ -288,12 +304,14 @@ export async function initialise(instance: Instance, context: ExtensionContext) if (remoteCerts) { vscode.commands.executeCommand(`setContext`, remoteCertContext, true); - const localExists = await certificates.checkLocalExists(instance.connection); + if (instance.connection.config!.debugIsSecure) { + const localExists = await certificates.checkLocalExists(instance.connection); - if (localExists) { - vscode.commands.executeCommand(`setContext`, localCertContext, true); - } else { - vscode.commands.executeCommand(`code-for-ibmi.debug.setup.local`); + if (localExists) { + vscode.commands.executeCommand(`setContext`, localCertContext, true); + } else { + vscode.commands.executeCommand(`code-for-ibmi.debug.setup.local`); + } } } else { const openTut = await vscode.window.showInformationMessage(`Looks like you have the debug PTF installed. Do you want to see the Walkthrough to set it up?`, `Take me there`); @@ -319,12 +337,12 @@ export async function startDebug(instance: Instance, options: DebugOptions) { const port = config?.debugPort; const updateProductionFiles = config?.debugUpdateProductionFiles; - const enableDebugTracing = config?.debugEnableDebugTracing; // TODO: configurable + const enableDebugTracing = config?.debugEnableDebugTracing; - const secure = config?.debugSecure; // TODO: make configurable + const secure = config?.debugIsSecure; if (secure) { - process.env[`DEBUG_CA_PATH`] = certificates.getLocalCert(connection!); + process.env[`DEBUG_CA_PATH`] = certificates.getLocalCertPath(connection!); } const pathKey = options.library.trim() + `/` + options.object.trim(); diff --git a/src/webviews/settings/index.js b/src/webviews/settings/index.js index b186aebe3..3ca3f47a1 100644 --- a/src/webviews/settings/index.js +++ b/src/webviews/settings/index.js @@ -208,11 +208,6 @@ module.exports = class SettingsUI { field.description = `Default secure port is 8005. Tells the client which port the debug service is running on.`; ui.addField(field); - field = new Field(`checkbox`, `debugSecure`, `Debug securely`); - field.default = (config.debugSecure ? `checked` : ``); - field.description = `Ensures that the client and debug service are connected securely.`; - ui.addField(field); - field = new Field(`checkbox`, `debugUpdateProductionFiles`, `Update production files`); field.default = (config.debugUpdateProductionFiles ? `checked` : ``); field.description = `Determines whether the job being debugged can update objects in production (*PROD) libraries.`; @@ -222,6 +217,11 @@ module.exports = class SettingsUI { field.default = (config.debugEnableDebugTracing ? `checked` : ``); field.description = `Tells the debug service to send more data to the client. Only useful for debugging issues in the service. Not recommended for general debugging.`; ui.addField(field); + + field = new Field(`checkbox`, `debugIsSecure`, `Debug securely`); + field.default = (config.debugIsSecure ? `checked` : ``); + field.description = `Tells the debug service to authenticate by server and client certificates. Ensure that the client certificate is imported when enabled.`; + ui.addField(field); } ui.addField(new Field(`hr`));