From 6b9784c8fc790391b6190de5571c710bc0497104 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Fri, 22 May 2026 16:56:53 -0500 Subject: [PATCH] feat: improve sentinel system --- src/backend/database/routes/credentials.ts | 11 +- src/backend/database/routes/host.ts | 16 +- src/types/index.ts | 7 +- src/types/ui-types.ts | 2 +- src/ui/locales/en.json | 2 +- src/ui/sidebar/HostManager.tsx | 203 ++++++++++----------- 6 files changed, 98 insertions(+), 143 deletions(-) diff --git a/src/backend/database/routes/credentials.ts b/src/backend/database/routes/credentials.ts index 8e5bd024..9e177f01 100644 --- a/src/backend/database/routes/credentials.ts +++ b/src/backend/database/routes/credentials.ts @@ -431,18 +431,11 @@ router.get( if (credential.password) { output.password = credential.password; } - if (credential.key) { - output.key = credential.key; - } - if (credential.privateKey) { - output.privateKey = credential.privateKey; - } + output.hasKey = !!credential.key; + output.hasKeyPassword = !!credential.keyPassword; if (credential.publicKey) { output.publicKey = credential.publicKey; } - if (credential.keyPassword) { - output.keyPassword = credential.keyPassword; - } res.json(output); } catch (err) { diff --git a/src/backend/database/routes/host.ts b/src/backend/database/routes/host.ts index a76008a5..454ef96c 100644 --- a/src/backend/database/routes/host.ts +++ b/src/backend/database/routes/host.ts @@ -246,29 +246,18 @@ function normalizeImportedHost( } const SENSITIVE_FIELDS = [ - "password", "key", "keyPassword", - "sudoPassword", - "autostartPassword", "autostartKey", "autostartKeyPassword", - "socks5Password", - "rdpPassword", - "vncPassword", - "telnetPassword", ]; function stripSensitiveFields( host: Record, ): Record { const result = { ...host }; - result.hasPassword = !!host.password; result.hasKey = !!host.key; - result.hasSudoPassword = !!host.sudoPassword; - result.hasRdpPassword = !!host.rdpPassword; - result.hasVncPassword = !!host.vncPassword; - result.hasTelnetPassword = !!host.telnetPassword; + result.hasKeyPassword = !!host.keyPassword; for (const field of SENSITIVE_FIELDS) { delete result[field]; } @@ -323,9 +312,6 @@ function transformHostResponse( rdpIgnoreCert: !!host.rdpIgnoreCert, vncUser: host.vncUser || undefined, telnetUser: host.telnetUser || undefined, - hasRdpPassword: !!host.rdpPassword, - hasVncPassword: !!host.vncPassword, - hasTelnetPassword: !!host.telnetPassword, tunnelConnections: host.tunnelConnections ? JSON.parse(host.tunnelConnections as string) : [], diff --git a/src/types/index.ts b/src/types/index.ts index fa48ef88..c5acd6d7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -105,16 +105,11 @@ export interface Host { vncUser?: string; telnetUser?: string; telnetPassword?: string; - hasRdpPassword?: boolean; - hasVncPassword?: boolean; - hasTelnetPassword?: boolean; - createdAt: string; updatedAt: string; - hasPassword?: boolean; hasKey?: boolean; - hasSudoPassword?: boolean; + hasKeyPassword?: boolean; isShared?: boolean; permissionLevel?: "view"; diff --git a/src/types/ui-types.ts b/src/types/ui-types.ts index d34ed052..d3a68d18 100644 --- a/src/types/ui-types.ts +++ b/src/types/ui-types.ts @@ -14,8 +14,8 @@ export type Host = { credentialId?: string; overrideCredentialUsername?: boolean; password?: string; - hasPassword?: boolean; hasKey?: boolean; + hasKeyPassword?: boolean; key?: string; keyPassword?: string; keyType?: string; diff --git a/src/ui/locales/en.json b/src/ui/locales/en.json index 67849169..8b8ba430 100644 --- a/src/ui/locales/en.json +++ b/src/ui/locales/en.json @@ -176,7 +176,6 @@ "upload": "Upload", "authentication": "Authentication", "password": "Password", - "passwordSaved": "Password saved, type to change", "key": "Key", "credential": "Credential", "none": "None", @@ -297,6 +296,7 @@ "clearKey": "Clear key", "keySaved": "SSH key saved", "keyReplaceNotice": "paste a new key below to replace it", + "keyPassphraseSaved": "Passphrase saved, type to change", "replaceKey": "Replace key", "forceKeyboardInteractiveLabel": "Force Keyboard Interactive", "forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present", diff --git a/src/ui/sidebar/HostManager.tsx b/src/ui/sidebar/HostManager.tsx index 79575bb9..61c73705 100644 --- a/src/ui/sidebar/HostManager.tsx +++ b/src/ui/sidebar/HostManager.tsx @@ -132,8 +132,8 @@ function sshHostToHost(h: SSHHostWithStatus): Host { tags: h.tags ?? [], authType: h.authType, password: h.password, - hasPassword: !!(h as any).hasPassword || !!h.password, hasKey: !!(h as any).hasKey || !!(typeof h.key === "string" && h.key), + hasKeyPassword: !!(h as any).hasKeyPassword || !!h.keyPassword, key: typeof h.key === "string" ? h.key : undefined, keyPassword: h.keyPassword, keyType: h.keyType, @@ -159,20 +159,14 @@ function sshHostToHost(h: SSHHostWithStatus): Host { vncPort: h.vncPort ?? (h.connectionType === "vnc" ? h.port : 5900), telnetPort: h.telnetPort ?? (h.connectionType === "telnet" ? h.port : 23), rdpUser: h.rdpUser, - rdpPassword: (h as any).hasRdpPassword - ? "existing_password" - : (h.rdpPassword ?? ""), + rdpPassword: h.rdpPassword ?? "", domain: h.rdpDomain, security: h.rdpSecurity, ignoreCert: h.rdpIgnoreCert ?? false, - vncPassword: (h as any).hasVncPassword - ? "existing_password" - : (h.vncPassword ?? ""), + vncPassword: h.vncPassword ?? "", vncUser: h.vncUser, telnetUser: h.telnetUser, - telnetPassword: (h as any).hasTelnetPassword - ? "existing_password" - : (h.telnetPassword ?? ""), + telnetPassword: h.telnetPassword ?? "", quickActions: (h.quickActions ?? []).map((a: any) => ({ name: a.name, snippetId: String(a.snippetId), @@ -870,10 +864,11 @@ function HostEditor({ vncPort: host?.vncPort ?? 5900, telnetPort: host?.telnetPort ?? 23, authType: host?.authType ?? "password", - password: - host?.password ?? (host?.hasPassword ? "existing_password" : ""), + password: host?.password ?? "", key: host?.key ?? (host?.hasKey ? "existing_key" : ""), - keyPassword: host?.keyPassword ?? "", + keyPassword: host?.hasKeyPassword + ? "existing_key_password" + : (host?.keyPassword ?? ""), keyType: host?.keyType ?? "auto", keySubTab: "paste" as "paste" | "upload", credentialId: host?.credentialId ?? "", @@ -951,20 +946,14 @@ function HostEditor({ quickActions: host?.quickActions ?? ([] as { name: string; snippetId: string }[]), rdpUser: host?.rdpUser ?? "", - rdpPassword: (host as any)?.hasRdpPassword - ? "existing_password" - : (host?.rdpPassword ?? ""), + rdpPassword: host?.rdpPassword ?? "", domain: host?.domain ?? "", security: host?.security ?? "", ignoreCert: host?.ignoreCert ?? false, - vncPassword: (host as any)?.hasVncPassword - ? "existing_password" - : (host?.vncPassword ?? ""), + vncPassword: host?.vncPassword ?? "", vncUser: host?.vncUser ?? "", telnetUser: host?.telnetUser ?? "", - telnetPassword: (host as any)?.hasTelnetPassword - ? "existing_password" - : (host?.telnetPassword ?? ""), + telnetPassword: host?.telnetPassword ?? "", guacamoleConfig: host?.guacamoleConfig ?? ({} as Record), statsConfig: host?.statsConfig ?? { statusCheckEnabled: true, @@ -1092,12 +1081,12 @@ function HostEditor({ tags, pin: form.pin, authType: form.authType, - password: - form.password === "existing_password" - ? undefined - : form.password || null, + password: form.password || null, key: form.key === "existing_key" ? undefined : form.key || null, - keyPassword: form.keyPassword || null, + keyPassword: + form.keyPassword === "existing_key_password" + ? undefined + : form.keyPassword || null, keyType: form.keyType !== "auto" ? form.keyType : null, credentialId: form.credentialId ? Number(form.credentialId) : null, overrideCredentialUsername: form.overrideCredentialUsername, @@ -1133,23 +1122,14 @@ function HostEditor({ telnetPort: Number(form.telnetPort), forceKeyboardInteractive: form.forceKeyboardInteractive, rdpUser: form.rdpUser || null, - rdpPassword: - form.rdpPassword === "existing_password" - ? undefined - : form.rdpPassword || null, + rdpPassword: form.rdpPassword || null, rdpDomain: form.domain || null, rdpSecurity: form.security || null, rdpIgnoreCert: form.ignoreCert, - vncPassword: - form.vncPassword === "existing_password" - ? undefined - : form.vncPassword || null, + vncPassword: form.vncPassword || null, vncUser: form.vncUser || null, telnetUser: form.telnetUser || null, - telnetPassword: - form.telnetPassword === "existing_password" - ? undefined - : form.telnetPassword || null, + telnetPassword: form.telnetPassword || null, jumpHosts: form.jumpHosts, portKnockSequence: form.portKnockSequence, tunnelConnections: form.serverTunnels, @@ -1973,20 +1953,8 @@ function HostEditor({ { - if (form.password === "existing_password") - setField("password", ""); - }} + placeholder="••••••••" + value={form.password} onChange={(e) => setField("password", e.target.value)} /> @@ -2077,8 +2045,20 @@ function HostEditor({ { + if (form.keyPassword === "existing_key_password") + setField("keyPassword", ""); + }} onChange={(e) => setField("keyPassword", e.target.value) } @@ -3330,20 +3310,8 @@ function HostEditor({ { - if (form.rdpPassword === "existing_password") - setField("rdpPassword", ""); - }} + placeholder="••••••••" + value={form.rdpPassword} onChange={(e) => setField("rdpPassword", e.target.value)} /> @@ -4118,20 +4086,8 @@ function HostEditor({ { - if (form.vncPassword === "existing_password") - setField("vncPassword", ""); - }} + placeholder="••••••••" + value={form.vncPassword} onChange={(e) => setField("vncPassword", e.target.value)} /> @@ -4514,20 +4470,8 @@ function HostEditor({ { - if (form.telnetPassword === "existing_password") - setField("telnetPassword", ""); - }} + placeholder="••••••••" + value={form.telnetPassword} onChange={(e) => setField("telnetPassword", e.target.value)} /> @@ -5010,9 +4954,19 @@ function CredentialEditorView({ tags: credForm.tags, authType: credForm.type, password: credForm.type === "password" ? credForm.value : null, - key: credForm.type === "key" ? credForm.value : null, + key: + credForm.type === "key" + ? credForm.value === "existing_key" + ? undefined + : credForm.value || null + : null, publicKey: credForm.type === "key" ? credForm.publicKey : null, - keyPassword: credForm.type === "key" ? credForm.passphrase : null, + keyPassword: + credForm.type === "key" + ? credForm.passphrase === "existing_key_password" + ? undefined + : credForm.passphrase || null + : null, }; const saved = credential ? await updateCredential(Number(credential.id), data) @@ -5218,7 +5172,9 @@ function CredentialEditorView({ | "ssh-rsa" | "ecdsa-sha2-nistp256", bits, - credForm.passphrase || undefined, + credForm.passphrase === "existing_key_password" + ? undefined + : credForm.passphrase || undefined, ); if (result.success) { setCredField("value", result.privateKey); @@ -5272,10 +5228,17 @@ function CredentialEditorView({ e.target.value = ""; }} /> + {credForm.value === "existing_key" && ( +
+ {t("hosts.keySaved")} — {t("hosts.keyReplaceNotice")} +
+ )}