diff --git a/package.json b/package.json index d4261029f..bf9ddcf83 100644 --- a/package.json +++ b/package.json @@ -964,36 +964,42 @@ "command": "code-for-ibmi.connect", "title": "New Connection", "category": "IBM i", - "icon": "$(add)" + "icon": "$(add)", + "enablement": "code-for-ibmi:editingConnection !== true" }, { "command": "code-for-ibmi.connectTo", "title": "Connect to IBM i", "category": "IBM i", - "icon": "$(debug-start)" + "icon": "$(debug-start)", + "enablement": "code-for-ibmi:editingConnection !== true" }, { "command": "code-for-ibmi.connectToPrevious", "title": "Connect to Previous IBM i", "category": "IBM i", - "icon": "$(remote)" + "icon": "$(remote)", + "enablement": "code-for-ibmi:editingConnection !== true" }, { "command": "code-for-ibmi.connectToAndReload", "title": "Connect and Reload Server Settings", - "category": "IBM i" + "category": "IBM i", + "enablement": "code-for-ibmi:editingConnection !== true" }, { "command": "code-for-ibmi.refreshConnections", "title": "Refresh", "category": "IBM i", - "icon": "$(refresh)" + "icon": "$(refresh)", + "enablement": "code-for-ibmi:editingConnection !== true" }, { "command": "code-for-ibmi.sortConnections", "title": "Sort", "category": "IBM i", - "icon": "$(list-ordered)" + "icon": "$(list-ordered)", + "enablement": "code-for-ibmi:editingConnection !== true" }, { "command": "code-for-ibmi.showAdditionalSettings", @@ -1008,17 +1014,20 @@ { "command": "code-for-ibmi.renameConnection", "title": "Rename...", - "category": "IBM i" + "category": "IBM i", + "enablement": "code-for-ibmi:editingConnection !== true" }, { "command": "code-for-ibmi.deleteConnection", "title": "Delete...", - "category": "IBM i" + "category": "IBM i", + "enablement": "code-for-ibmi:editingConnection !== true" }, { "command": "code-for-ibmi.copyConnection", "title": "Copy...", - "category": "IBM i" + "category": "IBM i", + "enablement": "code-for-ibmi:editingConnection !== true" }, { "command": "code-for-ibmi.disconnect", diff --git a/src/api/Configuration.ts b/src/api/Configuration.ts index a4208f6dd..5fcf25e2c 100644 --- a/src/api/Configuration.ts +++ b/src/api/Configuration.ts @@ -1,6 +1,6 @@ import os from "os"; import * as vscode from 'vscode'; -import { DeploymentMethod } from '../typings'; +import { ConnectionData, DeploymentMethod } from '../typings'; import { FilterType } from './Filter'; export type SourceDateMode = "edit" | "diff"; @@ -29,6 +29,79 @@ export namespace GlobalConfiguration { } } +export interface StoredConnection { + index: number, + data: ConnectionData +}; + +const getPasswordKey = (connectionName:string) => `${connectionName}_password`; + +export namespace ConnectionManager { + export function getByName(name: string): StoredConnection | undefined { + const connections = getAll(); + const index = connections.findIndex(conn => conn.name === name); + if (index !== -1) { + return { index, data: connections[index] }; + } + } + + export function sort() { + const connections = getAll(); + connections.sort((a, b) => a.name.localeCompare(b.name)); + return GlobalConfiguration.set(`connections`, connections); + } + + export function getAll(): ConnectionData[] { + return GlobalConfiguration.get(`connections`) || []; + } + + function setAll(connections: ConnectionData[]) { + return GlobalConfiguration.set(`connections`, connections); + } + + export async function storeNew(data: ConnectionData): Promise { + const connections = getAll(); + const newId = connections.length; + connections.push(data); + await setAll(connections); + return { index: newId, data }; + } + + export function deleteByName(name: string) { + const connections = getAll(); + const index = connections.findIndex(conn => conn.name === name); + if (index !== -1) { + connections.splice(index, 1); + return setAll(connections); + } + } + + export function updateByIndex(index: number, data: ConnectionData) { + const connections = getAll(); + connections[index] = data; + + // Remove possible password from any connection + connections.forEach(conn => delete conn.password); + + return GlobalConfiguration.set(`connections`, connections); + } + + export function getStoredPassword(context: vscode.ExtensionContext, connectionName: string) { + const connectionKey = getPasswordKey(connectionName); + return context.secrets.get(connectionKey); + } + + export function setStoredPassword(context: vscode.ExtensionContext, connectionName: string, password: string) { + const connectionKey = getPasswordKey(connectionName); + return context.secrets.store(connectionKey, password); + } + + export function deleteStoredPassword(context: vscode.ExtensionContext, connectionName: string) { + const connectionKey = getPasswordKey(connectionName); + return context.secrets.delete(connectionKey); + } +} + export namespace ConnectionConfiguration { export interface Parameters extends ConnectionProfile { host: string; diff --git a/src/api/Tools.ts b/src/api/Tools.ts index bb6f93682..bd50862d1 100644 --- a/src/api/Tools.ts +++ b/src/api/Tools.ts @@ -363,7 +363,8 @@ export namespace Tools { activeContexts.set(context, 0); } else { - activeContexts.set(context, stack++); + stack++; + activeContexts.set(context, stack); } return await task(); } @@ -371,7 +372,8 @@ export namespace Tools { let stack = activeContexts.get(context); if (stack !== undefined) { if (stack) { - activeContexts.set(context, stack--); + stack--; + activeContexts.set(context, stack); } else { await vscode.commands.executeCommand(`setContext`, context, undefined); diff --git a/src/api/debug/index.ts b/src/api/debug/index.ts index eabd480de..3e1625034 100644 --- a/src/api/debug/index.ts +++ b/src/api/debug/index.ts @@ -12,6 +12,7 @@ import { getEnvConfig } from "../local/env"; import * as certificates from "./certificates"; import { DEBUG_CONFIG_FILE, getDebugServiceDetails, resetDebugServiceDetails } from "./config"; import * as server from "./server"; +import { ConnectionManager } from "../Configuration"; const debugExtensionId = `IBM.ibmidebug`; @@ -176,7 +177,7 @@ export async function initialize(context: ExtensionContext) { const getPassword = async () => { const connection = instance.getConnection(); - let password = await context.secrets.get(`${connection!.currentConnectionName}_password`); + let password = await ConnectionManager.getStoredPassword(context, connection!.currentConnectionName); if (!password) { password = temporaryPassword; diff --git a/src/extension.ts b/src/extension.ts index 28e514e6d..2e68582cf 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -8,7 +8,7 @@ import { CustomUI } from "./api/CustomUI"; import { instance, loadAllofExtension } from './instantiate'; import { CompileTools } from "./api/CompileTools"; -import { ConnectionConfiguration, GlobalConfiguration, onCodeForIBMiConfigurationChange } from "./api/Configuration"; +import { ConnectionConfiguration, ConnectionManager, GlobalConfiguration, onCodeForIBMiConfigurationChange } from "./api/Configuration"; import IBMi from "./api/IBMi"; import { GlobalStorage } from "./api/Storage"; import { Tools } from "./api/Tools"; @@ -38,7 +38,7 @@ export async function activate(context: ExtensionContext): Promise await loadAllofExtension(context); const checkLastConnections = () => { - const connections = (GlobalConfiguration.get(`connections`) || []); + const connections = ConnectionManager.getAll(); const lastConnections = (GlobalStorage.get().getLastConnections() || []).filter(lc => connections.find(c => c.name === lc.name)); GlobalStorage.get().setLastConnections(lastConnections); commands.executeCommand(`setContext`, `code-for-ibmi:hasPreviousConnection`, lastConnections.length > 0); @@ -72,7 +72,7 @@ export async function activate(context: ExtensionContext): Promise } if (savePassword && connectionData.password) { - await context.secrets.store(`${connectionData.name}_password`, `${connectionData.password}`); + await ConnectionManager.setStoredPassword(context, connectionData.name, connectionData.password); } return (await new IBMi().connect(connectionData, undefined, reloadSettings)).success; @@ -119,8 +119,6 @@ export async function activate(context: ExtensionContext): Promise ]); }); - await fixLoginSettings(); - return { instance, customUI: () => new CustomUI(), deployTools: DeployTools, evfeventParser: parseErrors, tools: Tools }; } diff --git a/src/instantiate.ts b/src/instantiate.ts index 86df5a831..1d705c794 100644 --- a/src/instantiate.ts +++ b/src/instantiate.ts @@ -3,7 +3,7 @@ import { Tools } from './api/Tools'; import path, { dirname } from 'path'; import * as vscode from "vscode"; import { CompileTools } from './api/CompileTools'; -import { ConnectionConfiguration, DefaultOpenMode, GlobalConfiguration, onCodeForIBMiConfigurationChange } from "./api/Configuration"; +import { ConnectionConfiguration, ConnectionManager, DefaultOpenMode, GlobalConfiguration, onCodeForIBMiConfigurationChange } from "./api/Configuration"; import Instance from "./api/Instance"; import { Search } from "./api/Search"; import { Terminal } from './api/Terminal'; @@ -625,8 +625,7 @@ export async function loadAllofExtension(context: vscode.ExtensionContext) { throw new Error(`Password request denied for extension ${displayName}.`); } - const connectionKey = `${instance.getConnection()!.currentConnectionName}_password`; - const storedPassword = await context.secrets.get(connectionKey); + const storedPassword = await ConnectionManager.getStoredPassword(context, instance.getConnection()!.currentConnectionName); if (storedPassword) { let isAuthed = storage.getExtensionAuthorisation(extension) !== undefined; diff --git a/src/sandbox.ts b/src/sandbox.ts index e7f9cc091..b7e5dd380 100644 --- a/src/sandbox.ts +++ b/src/sandbox.ts @@ -1,7 +1,7 @@ import { env } from "process"; import querystring from "querystring"; import { commands, ExtensionContext, Uri, window } from "vscode"; -import { ConnectionConfiguration, GlobalConfiguration } from "./api/Configuration"; +import { ConnectionConfiguration, ConnectionManager, GlobalConfiguration } from "./api/Configuration"; import { Tools } from "./api/Tools"; import { instance } from "./instantiate"; import { t } from "./locale"; @@ -62,21 +62,11 @@ export async function registerUriHandler(context: ExtensionContext) { await initialSetup(connectionData.username); if (save) { - let existingConnections: ConnectionData[] | undefined = GlobalConfiguration.get(`connections`); + const existingConnection = ConnectionManager.getByName(connectionData.name); - if (existingConnections) { - const existingConnection = existingConnections.find(item => item.name === host); - - if (!existingConnection) { - // New connection! - existingConnections.push({ - ...connectionData, - password: undefined, // Removes the password from the object - }); - - await context.secrets.store(`${host}_password`, pass); - await GlobalConfiguration.set(`connections`, existingConnections); - } + if (!existingConnection) { + // New connection! + await ConnectionManager.storeNew(connectionData); } } diff --git a/src/views/ConnectionBrowser.ts b/src/views/ConnectionBrowser.ts index b93e958d3..5edacc8f9 100644 --- a/src/views/ConnectionBrowser.ts +++ b/src/views/ConnectionBrowser.ts @@ -1,7 +1,7 @@ import vscode from 'vscode'; import { ConnectionData, Server } from '../typings'; -import { ConnectionConfiguration, GlobalConfiguration } from '../api/Configuration'; +import { ConnectionConfiguration, ConnectionManager, GlobalConfiguration } from '../api/Configuration'; import { GlobalStorage } from '../api/Storage'; import { instance } from '../instantiate'; import { t } from "../locale"; @@ -80,7 +80,7 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { vscode.commands.registerCommand(`code-for-ibmi.renameConnection`, async (server: Server) => { if (!connectionBrowser.attemptingConnection && server) { - const existingConnections = GlobalConfiguration.get(`connections`) || []; + const existingConnections = ConnectionManager.getAll(); const newName = await vscode.window.showInputBox({ prompt: t(`connectionBrowser.renameConnection.prompt`, server.name), value: server.name, @@ -95,12 +95,11 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { if (newName) { try { - let index; // First rename the connection details - const connections = GlobalConfiguration.get(`connections`) || []; - index = connections.findIndex(connection => connection.name === server.name); + let { index, data } = ConnectionManager.getByName(server.name)! if (index === -1) throw (t(`connectionBrowser.renameConnection.noConnectionFound`, server.name)); - connections[index].name = newName; + data.name = newName; + await ConnectionManager.updateByIndex(index, data); // Then rename the connection settings const connectionSettings = GlobalConfiguration.get(`connectionSettings`) || []; @@ -112,18 +111,17 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { const cachedConnectionSettings = GlobalStorage.get().getServerSettingsCache(server.name); // Then get the password key - const secret = await context.secrets.get(`${server.name}_password`); + const secret = await ConnectionManager.getStoredPassword(context, server.name); // No errors - update the settings. await GlobalConfiguration.set(`connectionSettings`, connectionSettings); - await GlobalConfiguration.set(`connections`, connections); if (cachedConnectionSettings) { GlobalStorage.get().setServerSettingsCache(newName, cachedConnectionSettings); GlobalStorage.get().deleteServerSettingsCache(server.name); } if (secret) { - await context.secrets.store(`${newName}_password`, secret); - await context.secrets.delete(`${server.name}_password`); + await ConnectionManager.setStoredPassword(context, newName, secret); + await ConnectionManager.deleteStoredPassword(context, server.name); } connectionBrowser.refresh(); @@ -136,9 +134,7 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { }), vscode.commands.registerCommand(`code-for-ibmi.sortConnections`, async () => { - const connections = GlobalConfiguration.get(`connections`) || []; - connections.sort((conn1, conn2) => conn1.name.localeCompare(conn2.name)); - await GlobalConfiguration.set(`connections`, connections); + await ConnectionManager.sort(); connectionBrowser.refresh(); }), @@ -160,9 +156,7 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { if (await vscode.window.showWarningMessage(message, { modal: true, detail }, t(`Yes`))) { for (const server of toBeDeleted) { // First remove the connection details - const connections = GlobalConfiguration.get(`connections`) || []; - const newConnections = connections.filter(connection => connection.name !== server.name); - await GlobalConfiguration.set(`connections`, newConnections); + await ConnectionManager.deleteByName(server.name); // Also remove the connection settings const connectionSettings = GlobalConfiguration.get(`connectionSettings`) || []; @@ -173,7 +167,7 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { GlobalStorage.get().deleteServerSettingsCache(server.name); // Then remove the password - await context.secrets.delete(`${server.name}_password`); + await ConnectionManager.deleteStoredPassword(context, server.name); } connectionBrowser.refresh(); @@ -181,10 +175,9 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { } }), vscode.commands.registerCommand(`code-for-ibmi.copyConnection`, async (server: Server) => { - const connections = GlobalConfiguration.get(`connections`) || []; const connectionSettings = GlobalConfiguration.get(`connectionSettings`) || []; - const connection = connections.find(connection => server.name === connection.name); + const connection = ConnectionManager.getByName(server.name); const connectionSetting = connectionSettings.find(connection => server.name === connection.name); if (connection && connectionSetting) { @@ -195,7 +188,7 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { prompt: t("connectionBrowser.copyConnection.prompt", server.name), placeHolder: t('connectionBrowser.copyConnection.placeholder'), value: newConnectionName, - validateInput: value => connections.some(connection => connection.name === value) ? + validateInput: value => ConnectionManager.getByName(value) ? t('connectionBrowser.copyConnection.already.exists', value) : undefined }); @@ -218,10 +211,9 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { } while (newConnectionName && !copyOperations); if (newConnectionName && copyOperations) { - const newConnection = Object.assign({}, connection); + const newConnection = Object.assign({}, connection.data); newConnection.name = newConnectionName; - connections.push(newConnection); - await GlobalConfiguration.set(`connections`, connections); + await ConnectionManager.storeNew(newConnection); const newConnectionSetting = Object.assign({}, connectionSetting); newConnectionSetting.name = newConnectionName; @@ -237,9 +229,9 @@ export function initializeConnectionBrowser(context: vscode.ExtensionContext) { connectionSettings.push(newConnectionSetting); await GlobalConfiguration.set(`connectionSettings`, connectionSettings); - const password = await context.secrets.get(`${server.name}_password`); + const password = await ConnectionManager.getStoredPassword(context, server.name); if (password) { - await context.secrets.store(`${newConnectionName}_password`, password); + await ConnectionManager.setStoredPassword(context, newConnectionName, password); } connectionBrowser.refresh(); @@ -268,7 +260,7 @@ class ConnectionBrowser implements vscode.TreeDataProvider { async getChildren(): Promise { const lastConnection = GlobalStorage.get().getLastConnections()?.[0]; - return (GlobalConfiguration.get(`connections`) || []) + return ConnectionManager.getAll() .map(connection => new ServerItem(connection, connection.name === lastConnection?.name)); } } diff --git a/src/webviews/login/index.ts b/src/webviews/login/index.ts index 41c629913..b1924e58a 100644 --- a/src/webviews/login/index.ts +++ b/src/webviews/login/index.ts @@ -1,5 +1,5 @@ import vscode from "vscode"; -import { ConnectionConfiguration, GlobalConfiguration } from "../../api/Configuration"; +import { ConnectionConfiguration, ConnectionManager, GlobalConfiguration } from "../../api/Configuration"; import { CustomUI, Section } from "../../api/CustomUI"; import IBMi from "../../api/IBMi"; import { disconnect, instance } from "../../instantiate"; @@ -25,8 +25,6 @@ export class Login { if (!disconnect()) return; } - const existingConnections = GlobalConfiguration.get(`connections`) || []; - const connectionTab = new Section() .addInput(`name`, `Connection Name`, undefined, { minlength: 1 }) .addInput(`host`, t(`login.host`), undefined, { minlength: 1 }) @@ -59,35 +57,32 @@ export class Login { data.port = Number(data.port); data.privateKeyPath = data.privateKeyPath?.trim() ? data.privateKeyPath : undefined; if (data.name) { - const existingConnection = existingConnections.find(item => item.name === data.name); + const existingConnection = ConnectionManager.getByName(data.name); if (existingConnection) { vscode.window.showErrorMessage(`Connection with name ${data.name} already exists.`); } else { - const newConnection = (!existingConnections.some(item => item.name === data.name)); - if (newConnection) { - // New connection! - existingConnections.push({ - name: data.name, - host: data.host, - port: data.port, - username: data.username, - privateKeyPath: data.privateKeyPath - }); - - if (data.savePassword) { - await context.secrets.store(`${data.name}_password`, `${data.password}`); - } - - await GlobalConfiguration.set(`connections`, existingConnections); - - const config = await ConnectionConfiguration.load(data.name) - config.tempLibrary = data.tempLibrary; - config.tempDir = data.tempDir; - ConnectionConfiguration.update(config); - vscode.commands.executeCommand(`code-for-ibmi.refreshConnections`); + // New connection! + const newConnection: ConnectionData = { + name: data.name, + host: data.host, + port: data.port, + username: data.username, + privateKeyPath: data.privateKeyPath + }; + + if (data.savePassword && data.password) { + await ConnectionManager.setStoredPassword(context, data.name, data.password); } + await ConnectionManager.storeNew(newConnection); + + const config = await ConnectionConfiguration.load(data.name) + config.tempLibrary = data.tempLibrary; + config.tempDir = data.tempDir; + ConnectionConfiguration.update(config); + vscode.commands.executeCommand(`code-for-ibmi.refreshConnections`); + switch (data.buttons) { case `saveExit`: vscode.window.showInformationMessage(`Connection to ${data.host} saved!`); @@ -141,25 +136,25 @@ export class Login { * @param context */ static async LoginToPrevious(name: string, context: vscode.ExtensionContext, reloadServerSettings?: boolean) { - const connection = instance.getConnection(); - if (connection) { + const existingConnection = instance.getConnection(); + if (existingConnection) { // If the user is already connected and trying to connect to a different system, disconnect them first - if (name !== connection.currentConnectionName) { - vscode.window.showInformationMessage(`Disconnecting from ${connection.currentHost}.`); - if (!disconnect()) return false; + if (name !== existingConnection.currentConnectionName) { + vscode.window.showInformationMessage(`Disconnecting from ${existingConnection.currentHost}.`); + if (!await disconnect()) return false; } } - const existingConnections = GlobalConfiguration.get(`connections`) || []; - let connectionConfig = existingConnections.find(item => item.name === name); - if (connectionConfig) { + const connection = ConnectionManager.getByName(name); + if (connection) { + const connectionConfig = connection.data; if (connectionConfig.privateKeyPath) { // If connecting with a private key, remove the password - await context.secrets.delete(`${connectionConfig.name}_password`); + await ConnectionManager.deleteStoredPassword(context, connectionConfig.name); } else { // Assume connection with a password, but prompt if we don't have one - connectionConfig.password = await context.secrets.get(`${connectionConfig.name}_password`); + connectionConfig.password = await ConnectionManager.getStoredPassword(context, connectionConfig.name); if (!connectionConfig.password) { connectionConfig.password = await vscode.window.showInputBox({ prompt: `Password for ${connectionConfig.name}`, diff --git a/src/webviews/settings/index.ts b/src/webviews/settings/index.ts index e171d40e1..9c1540748 100644 --- a/src/webviews/settings/index.ts +++ b/src/webviews/settings/index.ts @@ -1,5 +1,5 @@ import vscode from "vscode"; -import { ConnectionConfiguration, GlobalConfiguration } from "../../api/Configuration"; +import { ConnectionConfiguration, ConnectionManager, GlobalConfiguration } from "../../api/Configuration"; import { ComplexTab, CustomUI, Section } from "../../api/CustomUI"; import { GlobalStorage } from '../../api/Storage'; import { Tools } from "../../api/Tools"; @@ -10,6 +10,8 @@ import { instance } from "../../instantiate"; import { t } from "../../locale"; import { ConnectionData, Server } from '../../typings'; +const EDITING_CONTEXT = `code-for-ibmi:editingConnection`; + const ENCODINGS = [`37`, `256`, `273`, `277`, `278`, `280`, `284`, `285`, `297`, `500`, `871`, `870`, `905`, `880`, `420`, `875`, `424`, `1026`, `290`, `win37`, `win256`, `win273`, `win277`, `win278`, `win280`, `win284`, `win285`, `win297`, `win500`, `win871`, `win870`, `win905`, `win880`, `win420`, `win875`, `win424`, `win1026`]; const TERMINAL_TYPES = [ @@ -243,169 +245,157 @@ export class SettingsUI { .addHorizontalRule() .addButtons({ id: `save`, label: `Save settings`, requiresValidation: true }); - const page = await ui.loadPage(`Settings: ${config.name}`); - if (page) { - page.panel.dispose(); + await Tools.withContext(EDITING_CONTEXT, async () => { + const page = await ui.loadPage(`Settings: ${config.name}`); + if (page) { + page.panel.dispose(); - if (page.data) { - const data = page.data; - const button = data.buttons; + if (page.data) { + const data = page.data; + const button = data.buttons; - switch (button) { - case `import`: - vscode.commands.executeCommand(`code-for-ibmi.debug.setup.local`); - break; + switch (button) { + case `import`: + vscode.commands.executeCommand(`code-for-ibmi.debug.setup.local`); + break; - case `generate`: - vscode.commands.executeCommand(`code-for-ibmi.debug.setup.remote`); - break; + case `generate`: + vscode.commands.executeCommand(`code-for-ibmi.debug.setup.remote`); + break; - case `clearAllowedExts`: - instance.getStorage()?.revokeAllExtensionAuthorisations(); - break; + case `clearAllowedExts`: + instance.getStorage()?.revokeAllExtensionAuthorisations(); + break; - default: - const data = page.data; - for (const key in data) { - - //In case we need to play with the data - switch (key) { - case `sourceASP`: - if (data[key].trim() === ``) data[key] = null; - break; - case `hideCompileErrors`: - data[key] = String(data[key]).split(`,`) - .map(item => item.toUpperCase().trim()) - .filter(item => item !== ``) - .filter(Tools.distinct); - break; - case `protectedPaths`: - data[key] = String(data[key]).split(`,`) - .map(item => item.trim()) - .map(item => item.startsWith('/') ? item : connection?.upperCaseName(item) || item.toUpperCase()) - .filter(item => item !== ``) - .filter(Tools.distinct); - break; - } + default: + const data = page.data; + for (const key in data) { + + //In case we need to play with the data + switch (key) { + case `sourceASP`: + if (data[key].trim() === ``) data[key] = null; + break; + case `hideCompileErrors`: + data[key] = String(data[key]).split(`,`) + .map(item => item.toUpperCase().trim()) + .filter(item => item !== ``) + .filter(Tools.distinct); + break; + case `protectedPaths`: + data[key] = String(data[key]).split(`,`) + .map(item => item.trim()) + .map(item => item.startsWith('/') ? item : connection?.upperCaseName(item) || item.toUpperCase()) + .filter(item => item !== ``) + .filter(Tools.distinct); + break; + } - //Refresh connection browser if not connected - if (!instance.getConnection()) { - vscode.commands.executeCommand(`code-for-ibmi.refreshConnections`); + //Refresh connection browser if not connected + if (!instance.getConnection()) { + vscode.commands.executeCommand(`code-for-ibmi.refreshConnections`); + } } - } - if (restartFields.some(item => data[item] && data[item] !== config[item])) { - restart = true; - } - - const reloadBrowsers = config.protectedPaths.join(",") !== data.protectedPaths.join(","); - const removeCachedSettings = (!data.quickConnect && data.quickConnect !== config.quickConnect); - - Object.assign(config, data); - await instance.setConfig(config); - if (removeCachedSettings) - GlobalStorage.get().deleteServerSettingsCache(config.name); - - if (connection) { - if (restart) { - vscode.window.showInformationMessage(`Some settings require a restart to take effect. Reload workspace now?`, `Reload`, `No`) - .then(async (value) => { - if (value === `Reload`) { - await vscode.commands.executeCommand(`workbench.action.reloadWindow`); - } - }); + if (restartFields.some(item => data[item] && data[item] !== config[item])) { + restart = true; } - else if (reloadBrowsers) { - vscode.commands.executeCommand("code-for-ibmi.refreshIFSBrowser"); - vscode.commands.executeCommand("code-for-ibmi.refreshObjectBrowser"); + + const reloadBrowsers = config.protectedPaths.join(",") !== data.protectedPaths.join(","); + const removeCachedSettings = (!data.quickConnect && data.quickConnect !== config.quickConnect); + + Object.assign(config, data); + await instance.setConfig(config); + if (removeCachedSettings) + GlobalStorage.get().deleteServerSettingsCache(config.name); + + if (connection) { + if (restart) { + vscode.window.showInformationMessage(`Some settings require a restart to take effect. Reload workspace now?`, `Reload`, `No`) + .then(async (value) => { + if (value === `Reload`) { + await vscode.commands.executeCommand(`workbench.action.reloadWindow`); + } + }); + } + else if (reloadBrowsers) { + vscode.commands.executeCommand("code-for-ibmi.refreshIFSBrowser"); + vscode.commands.executeCommand("code-for-ibmi.refreshObjectBrowser"); + } } - } - break; + break; + } } } - } + }) }), vscode.commands.registerCommand(`code-for-ibmi.showLoginSettings`, async (server?: Server) => { if (server) { - const connections = GlobalConfiguration.get(`connections`); const name = server.name; - if (connections) { - const connectionIdx = connections.findIndex(item => item.name === name); - let connection = connections[connectionIdx]; - const storedPassword = await context.secrets.get(`${name}_password`); + const connection = ConnectionManager.getByName(name); + if (connection) { + const storedPassword = await ConnectionManager.getStoredPassword(context, name); + let { data: stored, index } = connection; - const page = await new CustomUI() - .addInput(`host`, t(`login.host`), undefined, { default: connection.host, minlength: 1 }) - .addInput(`port`, t(`login.port`), undefined, { default: String(connection.port), minlength: 1, maxlength: 5, regexTest: `^\\d+$` }) - .addInput(`username`, t(`username`), undefined, { default: connection.username, minlength: 1 }) + const ui = new CustomUI() + .addInput(`host`, t(`login.host`), undefined, { default: stored.host, minlength: 1 }) + .addInput(`port`, t(`login.port`), undefined, { default: String(stored.port), minlength: 1, maxlength: 5, regexTest: `^\\d+$` }) + .addInput(`username`, t(`username`), undefined, { default: stored.username, minlength: 1 }) .addParagraph(t(`login.authDecision`)) .addPassword(`password`, `${t(`password`)}${storedPassword ? ` (${t(`stored`)})` : ``}`, t(`login.password.label`)) - .addFile(`privateKeyPath`, `${t(`privateKey`)}${connection.privateKeyPath ? ` (${t(`current`)}: ${connection.privateKeyPath})` : ``}`, t(`login.privateKey.label`) + ' ' + t(`login.privateKey.support`)) + .addFile(`privateKeyPath`, `${t(`privateKey`)}${stored.privateKeyPath ? ` (${t(`current`)}: ${stored.privateKeyPath})` : ``}`, t(`login.privateKey.label`) + ' ' + t(`login.privateKey.support`)) .addButtons( { id: `submitButton`, label: t(`save`), requiresValidation: true }, { id: `removeAuth`, label: t(`login.removeAuth`) } - ) - .loadPage(t(`login.title.edit`, name)); + ); - if (page && page.data) { - page.panel.dispose(); + await Tools.withContext(EDITING_CONTEXT, async () => { + const page = await ui.loadPage(t(`login.title.edit`, name)); + if (page && page.data) { + page.panel.dispose(); - const data = page.data; - - let doUpdate = false; - - const chosenButton = data.buttons as "submitButton" | "removeAuth"; + const data = page.data; + const chosenButton = data.buttons as "submitButton" | "removeAuth"; - switch (chosenButton) { - case `submitButton`: - if (data.password) { - // New password was entered, so store the password - // and remove the private key path from the data - await context.secrets.store(`${name}_password`, `${data.password}`); + switch (chosenButton) { + case `removeAuth`: + await ConnectionManager.deleteStoredPassword(context, name); data.privateKeyPath = undefined; - - vscode.window.showInformationMessage(t(`login.password.updated`, name)); - - doUpdate = true; - - } else { - // If no password was entered, but a keypath exists - // then remove the password from the data and - // use the keypath instead - if (data.privateKeyPath?.trim()) { - await context.secrets.delete(`${name}_password`); - + vscode.window.showInformationMessage(t(`login.authRemoved`, name)); + break; + + default: + if (data.password) { + data.privateKeyPath = undefined; + if (data.password !== storedPassword) { + // New password was entered, so store the password + // and remove the private key path from the data + await ConnectionManager.setStoredPassword(context, name, data.password); + vscode.window.showInformationMessage(t(`login.password.updated`, name)); + } + } else if (data.privateKeyPath?.trim()) { + // If no password was entered, but a keypath exists + // then remove the password from the data and + // use the keypath instead + await ConnectionManager.deleteStoredPassword(context, name); vscode.window.showInformationMessage(t(`login.privateKey.updated`, name)); - - doUpdate = true; } - } - break; - - case `removeAuth`: - await context.secrets.delete(`${name}_password`); - data.password = undefined; - data.privateKeyPath = undefined; - - vscode.window.showInformationMessage(t(`login.authRemoved`, name)); - - doUpdate = true; - break; - } - + break; + } - if (doUpdate) { //Fix values before assigning the data data.port = Number(data.port); delete data.password; delete data.buttons; - connections[connectionIdx] = Object.assign(connection, data); - await GlobalConfiguration.set(`connections`, connections); + stored = Object.assign(stored, data); + await ConnectionManager.updateByIndex(index, stored); + vscode.commands.executeCommand(`code-for-ibmi.refreshConnections`); + } - } + }); } } })