Add SSH TOTP authentication support (#350)
* Add SSH TOTP authentication support - Implement keyboard-interactive authentication for SSH connections - Add TOTP dialog component for Terminal and File Manager - Handle TOTP prompts in WebSocket and HTTP connections - Disable Server Stats for TOTP-enabled servers - Add i18n support for TOTP-related messages * Update src/backend/ssh/server-stats.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update src/backend/ssh/file-manager.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --------- Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com> Co-authored-by: Karmaa <88517757+LukeGus@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { toast } from "sonner";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TOTPDialog } from "@/ui/components/TOTPDialog";
|
||||
import {
|
||||
Upload,
|
||||
FolderPlus,
|
||||
@@ -38,6 +39,7 @@ import {
|
||||
renameSSHItem,
|
||||
moveSSHItem,
|
||||
connectSSH,
|
||||
verifySSHTOTP,
|
||||
getSSHStatus,
|
||||
keepSSHAlive,
|
||||
identifySSHSymlink,
|
||||
@@ -98,6 +100,9 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [lastRefreshTime, setLastRefreshTime] = useState<number>(0);
|
||||
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
|
||||
const [totpRequired, setTotpRequired] = useState(false);
|
||||
const [totpSessionId, setTotpSessionId] = useState<string | null>(null);
|
||||
const [totpPrompt, setTotpPrompt] = useState<string>("");
|
||||
const [pinnedFiles, setPinnedFiles] = useState<Set<string>>(new Set());
|
||||
const [sidebarRefreshTrigger, setSidebarRefreshTrigger] = useState(0);
|
||||
const [isClosing, setIsClosing] = useState<boolean>(false);
|
||||
@@ -288,6 +293,14 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
userId: currentHost.userId,
|
||||
});
|
||||
|
||||
if (result?.requires_totp) {
|
||||
setTotpRequired(true);
|
||||
setTotpSessionId(sessionId);
|
||||
setTotpPrompt(result.prompt || "Verification code:");
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setSshSessionId(sessionId);
|
||||
|
||||
try {
|
||||
@@ -1238,6 +1251,47 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
setEditingFile(null);
|
||||
}
|
||||
|
||||
async function handleTotpSubmit(code: string) {
|
||||
if (!totpSessionId || !code) return;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const result = await verifySSHTOTP(totpSessionId, code);
|
||||
|
||||
if (result?.status === "success") {
|
||||
setTotpRequired(false);
|
||||
setTotpPrompt("");
|
||||
setSshSessionId(totpSessionId);
|
||||
setTotpSessionId(null);
|
||||
|
||||
try {
|
||||
const response = await listSSHFiles(totpSessionId, currentPath);
|
||||
const files = Array.isArray(response)
|
||||
? response
|
||||
: response?.files || [];
|
||||
setFiles(files);
|
||||
clearSelection();
|
||||
initialLoadDoneRef.current = true;
|
||||
toast.success(t("fileManager.connectedSuccessfully"));
|
||||
} catch (dirError: any) {
|
||||
console.error("Failed to load initial directory:", dirError);
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("TOTP verification failed:", error);
|
||||
toast.error(t("fileManager.totpVerificationFailed"));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleTotpCancel() {
|
||||
setTotpRequired(false);
|
||||
setTotpPrompt("");
|
||||
setTotpSessionId(null);
|
||||
if (onClose) onClose();
|
||||
}
|
||||
|
||||
function generateUniqueName(
|
||||
baseName: string,
|
||||
type: "file" | "directory",
|
||||
@@ -1806,6 +1860,13 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TOTPDialog
|
||||
isOpen={totpRequired}
|
||||
prompt={totpPrompt}
|
||||
onSubmit={handleTotpSubmit}
|
||||
onCancel={handleTotpCancel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user