import React from "react"; import { ChevronDown, ChevronRight, ChevronUp, Eye, EyeOff, FileKey, FolderOpen, Globe, Key, Link2, MoreHorizontal, Plus, Shield, TerminalSquare, Trash2, Variable, X } from "lucide-react"; import { AlgorithmOverridesPanel } from "./host-details/AlgorithmOverridesPanel"; import { Badge } from "./ui/badge"; import { Button } from "./ui/button"; import { HostDetailsSection, HostDetailsSettingRow } from "./host-details"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible"; import { Combobox } from "./ui/combobox"; import { Dropdown, DropdownContent, DropdownTrigger } from "./ui/dropdown"; import { Input } from "./ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; import { Switch } from "./ui/switch"; import { Textarea } from "./ui/textarea"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; // eslint-disable-next-line @typescript-eslint/no-explicit-any type GroupSshSettingsSectionProps = Record; const ToggleRow: React.FC<{ label: string; hint?: React.ReactNode; enabled: boolean; onToggle: () => void }> = ({ label, hint, enabled, onToggle }) => { return ( onToggle()} /> ); }; export const GroupSshSettingsSection: React.FC = ({ sshEnabled, t, removeSsh, form, update, showPassword, setShowPassword, availableKeys, identities, identityOptions, updateSshIdentity, effectiveSshIdentityId, setSelectedCredentialType, selectedCredentialType, credentialPopoverOpen, setCredentialPopoverOpen, keysByCategory, newKeyFilePath, setNewKeyFilePath, inheritedLegacyAlgorithms, inheritedSkipEcdsaHostKey, inheritedStartupCommandRunMode, showAlgorithmOverrides, setShowAlgorithmOverrides, inheritedAlgorithmOverrides, proxySummaryLabel, setActiveSubPanel, chainedHosts, }) => { if (!sshEnabled) return null; const startupCommandRunModeFallback = inheritedStartupCommandRunMode ?? "paste"; const effectiveStartupCommandRunMode = form.startupCommandRunMode ?? startupCommandRunModeFallback; return ( } title={t("vault.groups.details.ssh")} className="overflow-hidden" action={ } >
SSH on
update("port", e.target.value ? Number(e.target.value) : undefined) } className="h-8 flex-1 min-w-0 text-center" /> {t("hostDetails.port")}
{(identities.length > 0 || effectiveSshIdentityId) && ( )} {!effectiveSshIdentityId && (<> update("username", e.target.value || undefined)} className="h-10" />
update("password", e.target.value || undefined)} className="h-10 pr-10" />
)} {/* Selected credential display */} {!effectiveSshIdentityId && form.identityFileId && (
{form.authMethod === "certificate" ? ( ) : ( )} {availableKeys.find((k) => k.id === form.identityFileId)?.label || "Key"}
)} {/* Local key file paths display */} {!effectiveSshIdentityId && !form.identityFileId && form.identityFilePaths && form.identityFilePaths.length > 0 && (
{form.identityFilePaths.map((keyPath, idx) => (
{keyPath}
))}
)} {/* Credential type selection with inline popover - hidden when credential is selected */} {!effectiveSshIdentityId && !form.identityFileId && !selectedCredentialType && (
)} {/* Key selection combobox - appears after selecting "Key" type */} {selectedCredentialType === "key" && !effectiveSshIdentityId && !form.identityFileId && (
({ value: k.id, label: k.label, sublabel: `${k.type}${k.keySize ? ` ${k.keySize}` : ""}`, icon: , }))} value={form.identityFileId} onValueChange={(val) => { update("identityFileId", val); update("authMethod", "key"); setSelectedCredentialType(null); }} placeholder={t("hostDetails.keys.search")} emptyText={t("hostDetails.keys.empty")} icon={} className="flex-1" />
)} {/* Certificate selection combobox - appears after selecting "Certificate" type */} {selectedCredentialType === "certificate" && !effectiveSshIdentityId && !form.identityFileId && (
({ value: k.id, label: k.label, icon: ( ), }))} value={form.identityFileId} onValueChange={(val) => { update("identityFileId", val); update("authMethod", "certificate"); setSelectedCredentialType(null); }} placeholder={t("hostDetails.certs.search")} emptyText={t("hostDetails.certs.empty")} icon={ } className="flex-1" />
)} {/* Local key file path input - appears after selecting "Local Key File" type */} {!effectiveSshIdentityId && !form.identityFileId && selectedCredentialType === "localKeyFile" && (
setNewKeyFilePath(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" && newKeyFilePath.trim()) { e.preventDefault(); const paths = [...(form.identityFilePaths || []), newKeyFilePath.trim()]; update("identityFilePaths", paths); update("identityFileId", undefined); update("authMethod", "key"); setNewKeyFilePath(""); } }} /> {t("hostDetails.credential.browseKeyFile")}
)} update("agentForwarding", !form.agentForwarding)} /> {/* Startup Command — Textarea so multi-line sequences are typeable here just like on the per-host details panel (#1083 follow-up). */}