fix: None auth issues and macOS build failure and rename files for consistency
This commit is contained in:
2
.github/workflows/electron.yml
vendored
2
.github/workflows/electron.yml
vendored
@@ -403,7 +403,6 @@ jobs:
|
||||
- name: Build macOS DMG
|
||||
env:
|
||||
ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES: true
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
@@ -412,6 +411,7 @@ jobs:
|
||||
if [ "${{ steps.check_certs.outputs.has_certs }}" != "true" ]; then
|
||||
npm run build
|
||||
fi
|
||||
export GH_TOKEN="${{ secrets.GITHUB_TOKEN }}"
|
||||
npx electron-builder --mac dmg --universal --x64 --arm64 --publish never
|
||||
|
||||
- name: List release directory
|
||||
|
||||
@@ -1039,26 +1039,19 @@ router.post("/logout", authenticateJWT, async (req, res) => {
|
||||
// Route: Get current user's info using JWT
|
||||
// GET /users/me
|
||||
router.get("/me", authenticateJWT, async (req: Request, res: Response) => {
|
||||
console.log("=== /users/me CALLED ===");
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
console.log("User ID from JWT:", userId);
|
||||
|
||||
if (!isNonEmptyString(userId)) {
|
||||
console.log("ERROR: Invalid userId");
|
||||
authLogger.warn("Invalid userId in JWT for /users/me");
|
||||
return res.status(401).json({ error: "Invalid userId" });
|
||||
}
|
||||
try {
|
||||
const user = await db.select().from(users).where(eq(users.id, userId));
|
||||
console.log("User found:", user.length > 0 ? "YES" : "NO");
|
||||
|
||||
if (!user || user.length === 0) {
|
||||
console.log("ERROR: User not found in database");
|
||||
authLogger.warn(`User not found for /users/me: ${userId}`);
|
||||
return res.status(401).json({ error: "User not found" });
|
||||
}
|
||||
|
||||
console.log("SUCCESS: Returning user info");
|
||||
res.json({
|
||||
userId: user[0].id,
|
||||
username: user[0].username,
|
||||
@@ -1067,7 +1060,6 @@ router.get("/me", authenticateJWT, async (req: Request, res: Response) => {
|
||||
totp_enabled: !!user[0].totp_enabled,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("ERROR: Exception thrown:", err);
|
||||
authLogger.error("Failed to get username", err);
|
||||
res.status(500).json({ error: "Failed to get username" });
|
||||
}
|
||||
|
||||
@@ -459,6 +459,18 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
|
||||
"The server does not support keyboard-interactive authentication. Please provide credentials.",
|
||||
reason: "no_keyboard",
|
||||
});
|
||||
} else if (
|
||||
resolvedCredentials.authType === "none" &&
|
||||
(err.message.includes("All configured authentication methods failed") ||
|
||||
err.message.includes("No supported authentication methods available") ||
|
||||
err.message.includes("authentication methods failed"))
|
||||
) {
|
||||
res.status(200).json({
|
||||
status: "auth_required",
|
||||
message:
|
||||
"The server does not support keyboard-interactive authentication. Please provide credentials.",
|
||||
reason: "no_keyboard",
|
||||
});
|
||||
} else {
|
||||
fileLogger.error("SSH connection failed for file manager", {
|
||||
operation: "file_connect",
|
||||
|
||||
@@ -485,6 +485,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
}, 60000);
|
||||
|
||||
let resolvedCredentials = { password, key, keyPassword, keyType, authType };
|
||||
let authMethodNotAvailable = false;
|
||||
if (credentialId && id && hostConfig.userId) {
|
||||
try {
|
||||
const credentials = await SimpleDBOps.select(
|
||||
@@ -707,6 +708,23 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
|
||||
sshConn.on("error", (err: Error) => {
|
||||
clearTimeout(connectionTimeout);
|
||||
|
||||
if (
|
||||
(authMethodNotAvailable && resolvedCredentials.authType === "none") ||
|
||||
(resolvedCredentials.authType === "none" &&
|
||||
err.message.includes("All configured authentication methods failed"))
|
||||
) {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "auth_method_not_available",
|
||||
message:
|
||||
"The server does not support keyboard-interactive authentication. Please provide credentials.",
|
||||
}),
|
||||
);
|
||||
cleanupSSH(connectionTimeout);
|
||||
return;
|
||||
}
|
||||
|
||||
sshLogger.error("SSH connection error", err, {
|
||||
operation: "ssh_connect",
|
||||
hostId: id,
|
||||
@@ -880,7 +898,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
host: ip,
|
||||
port,
|
||||
username,
|
||||
tryKeyboard: true,
|
||||
tryKeyboard: resolvedCredentials.authType === "none",
|
||||
keepaliveInterval: 30000,
|
||||
keepaliveCountMax: 3,
|
||||
readyTimeout: 60000,
|
||||
@@ -955,14 +973,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
if (methodsLeft.includes("keyboard-interactive")) {
|
||||
callback("keyboard-interactive");
|
||||
} else {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "auth_method_not_available",
|
||||
message:
|
||||
"The server does not support keyboard-interactive authentication. Please provide credentials.",
|
||||
methodsAvailable: methodsLeft,
|
||||
}),
|
||||
);
|
||||
authMethodNotAvailable = true;
|
||||
callback(false);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -311,6 +311,8 @@
|
||||
"next": "Weiter",
|
||||
"previous": "Vorherige",
|
||||
"refresh": "Aktualisieren",
|
||||
"connect": "Verbinden",
|
||||
"connecting": "Verbinde...",
|
||||
"settings": "Einstellungen",
|
||||
"profile": "Profil",
|
||||
"help": "Hilfe",
|
||||
@@ -1173,7 +1175,17 @@
|
||||
"passwordResetSuccess": "Erfolgreich!",
|
||||
"passwordResetSuccessDesc": "Ihr Passwort wurde erfolgreich zurückgesetzt! Sie können sich jetzt mit Ihrem neuen Passwort anmelden.",
|
||||
"signUp": "Registrierung",
|
||||
"dataLossWarning": "Wenn Sie Ihr Passwort auf diese Weise zurücksetzen, werden alle Ihre gespeicherten SSH-Hosts, Anmeldeinformationen und andere verschlüsselte Daten gelöscht. Diese Aktion kann nicht rückgängig gemacht werden. Verwenden Sie diese Option nur, wenn Sie Ihr Passwort vergessen haben und nicht angemeldet sind."
|
||||
"dataLossWarning": "Wenn Sie Ihr Passwort auf diese Weise zurücksetzen, werden alle Ihre gespeicherten SSH-Hosts, Anmeldeinformationen und andere verschlüsselte Daten gelöscht. Diese Aktion kann nicht rückgängig gemacht werden. Verwenden Sie diese Option nur, wenn Sie Ihr Passwort vergessen haben und nicht angemeldet sind.",
|
||||
"sshAuthenticationRequired": "SSH-Authentifizierung erforderlich",
|
||||
"sshNoKeyboardInteractive": "Keyboard-Interactive-Authentifizierung nicht verfügbar",
|
||||
"sshAuthenticationFailed": "Authentifizierung fehlgeschlagen",
|
||||
"sshAuthenticationTimeout": "Authentifizierungs-Timeout",
|
||||
"sshNoKeyboardInteractiveDescription": "Der Server unterstützt keine Keyboard-Interactive-Authentifizierung. Bitte geben Sie Ihr Passwort oder Ihren SSH-Schlüssel ein.",
|
||||
"sshAuthFailedDescription": "Die angegebenen Anmeldeinformationen waren falsch. Bitte versuchen Sie es erneut mit gültigen Anmeldeinformationen.",
|
||||
"sshTimeoutDescription": "Der Authentifizierungsversuch ist abgelaufen. Bitte versuchen Sie es erneut.",
|
||||
"sshProvideCredentialsDescription": "Bitte geben Sie Ihre SSH-Anmeldeinformationen ein, um eine Verbindung zu diesem Server herzustellen.",
|
||||
"sshPasswordDescription": "Geben Sie das Passwort für diese SSH-Verbindung ein.",
|
||||
"sshKeyPasswordDescription": "Wenn Ihr SSH-Schlüssel verschlüsselt ist, geben Sie hier die Passphrase ein."
|
||||
},
|
||||
"errors": {
|
||||
"notFound": "Seite nicht gefunden",
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"sshPrivateKey": "SSH Private Key",
|
||||
"upload": "Upload",
|
||||
"updateKey": "Update Key",
|
||||
"keyPassword": "Key Password (optional)",
|
||||
"keyPassword": "Key Password",
|
||||
"keyType": "Key Type",
|
||||
"keyTypeRSA": "RSA",
|
||||
"keyTypeECDSA": "ECDSA",
|
||||
@@ -287,6 +287,8 @@
|
||||
"loading": "Loading",
|
||||
"required": "Required",
|
||||
"optional": "Optional",
|
||||
"connect": "Connect",
|
||||
"connecting": "Connecting...",
|
||||
"clear": "Clear",
|
||||
"toggleSidebar": "Toggle Sidebar",
|
||||
"sidebar": "Sidebar",
|
||||
@@ -778,7 +780,7 @@
|
||||
"terminalCustomizationNotice": "Note: Terminal customizations only work on desktop (website and Electron app). Mobile apps and mobile website use system default terminal settings.",
|
||||
"noneAuthTitle": "Keyboard-Interactive Authentication",
|
||||
"noneAuthDescription": "This authentication method will use keyboard-interactive authentication when connecting to the SSH server.",
|
||||
"noneAuthDetails": "Keyboard-interactive authentication allows the server to prompt you for credentials during connection. This is useful for servers that require multi-factor authentication or dynamic password entry."
|
||||
"noneAuthDetails": "Keyboard-interactive authentication allows the server to prompt you for credentials during connection. This is useful for servers that require multi-factor authentication or if you do not want to save credentials locally."
|
||||
},
|
||||
"terminal": {
|
||||
"title": "Terminal",
|
||||
@@ -1275,6 +1277,16 @@
|
||||
"yourBackupCodes": "Your Backup Codes",
|
||||
"download": "Download",
|
||||
"setupTwoFactorTitle": "Set Up Two-Factor Authentication",
|
||||
"sshAuthenticationRequired": "SSH Authentication Required",
|
||||
"sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable",
|
||||
"sshAuthenticationFailed": "Authentication Failed",
|
||||
"sshAuthenticationTimeout": "Authentication Timeout",
|
||||
"sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.",
|
||||
"sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.",
|
||||
"sshTimeoutDescription": "The authentication attempt timed out. Please try again.",
|
||||
"sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.",
|
||||
"sshPasswordDescription": "Enter the password for this SSH connection.",
|
||||
"sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.",
|
||||
"step1ScanQR": "Step 1: Scan the QR code with your authenticator app",
|
||||
"manualEntryCode": "Manual Entry Code",
|
||||
"cannotScanQRText": "If you can't scan the QR code, enter this code manually in your authenticator app",
|
||||
|
||||
@@ -326,6 +326,8 @@
|
||||
"next": "Próximo",
|
||||
"previous": "Anterior",
|
||||
"refresh": "Atualizar",
|
||||
"connect": "Conectar",
|
||||
"connecting": "Conectando...",
|
||||
"settings": "Configurações",
|
||||
"profile": "Perfil",
|
||||
"help": "Ajuda",
|
||||
@@ -1220,7 +1222,17 @@
|
||||
"enterNewPassword": "Digite sua nova senha para o usuário:",
|
||||
"passwordResetSuccess": "Sucesso!",
|
||||
"signUp": "Cadastrar",
|
||||
"dataLossWarning": "Redefinir sua senha desta forma excluirá todos os seus hosts SSH salvos, credenciais e outros dados criptografados. Esta ação não pode ser desfeita. Use isso apenas se você esqueceu sua senha e não está logado."
|
||||
"dataLossWarning": "Redefinir sua senha desta forma excluirá todos os seus hosts SSH salvos, credenciais e outros dados criptografados. Esta ação não pode ser desfeita. Use isso apenas se você esqueceu sua senha e não está logado.",
|
||||
"sshAuthenticationRequired": "Autenticação SSH Necessária",
|
||||
"sshNoKeyboardInteractive": "Autenticação Interativa por Teclado Indisponível",
|
||||
"sshAuthenticationFailed": "Falha na Autenticação",
|
||||
"sshAuthenticationTimeout": "Tempo Limite de Autenticação",
|
||||
"sshNoKeyboardInteractiveDescription": "O servidor não suporta autenticação interativa por teclado. Por favor, forneça sua senha ou chave SSH.",
|
||||
"sshAuthFailedDescription": "As credenciais fornecidas estavam incorretas. Por favor, tente novamente com credenciais válidas.",
|
||||
"sshTimeoutDescription": "A tentativa de autenticação expirou. Por favor, tente novamente.",
|
||||
"sshProvideCredentialsDescription": "Por favor, forneça suas credenciais SSH para conectar a este servidor.",
|
||||
"sshPasswordDescription": "Digite a senha para esta conexão SSH.",
|
||||
"sshKeyPasswordDescription": "Se sua chave SSH estiver criptografada, digite a senha aqui."
|
||||
},
|
||||
"errors": {
|
||||
"notFound": "Página não encontrada",
|
||||
|
||||
@@ -345,6 +345,8 @@
|
||||
"settingUp": "设置中...",
|
||||
"next": "下一步",
|
||||
"previous": "上一步",
|
||||
"connect": "连接",
|
||||
"connecting": "连接中...",
|
||||
"refresh": "刷新",
|
||||
"settings": "设置",
|
||||
"profile": "个人资料",
|
||||
@@ -1280,7 +1282,17 @@
|
||||
"passwordResetSuccess": "成功!",
|
||||
"passwordResetSuccessDesc": "您的密码已成功重置!您现在可以使用新密码登录。",
|
||||
"signUp": "注册",
|
||||
"dataLossWarning": "以这种方式重置密码将删除所有已保存的 SSH 主机、凭据和其他加密数据。此操作无法撤销。仅当您忘记密码且未登录时才使用此功能。"
|
||||
"dataLossWarning": "以这种方式重置密码将删除所有已保存的 SSH 主机、凭据和其他加密数据。此操作无法撤销。仅当您忘记密码且未登录时才使用此功能。",
|
||||
"sshAuthenticationRequired": "需要 SSH 身份验证",
|
||||
"sshNoKeyboardInteractive": "键盘交互式身份验证不可用",
|
||||
"sshAuthenticationFailed": "身份验证失败",
|
||||
"sshAuthenticationTimeout": "身份验证超时",
|
||||
"sshNoKeyboardInteractiveDescription": "服务器不支持键盘交互式身份验证。请提供您的密码或 SSH 密钥。",
|
||||
"sshAuthFailedDescription": "提供的凭据不正确。请使用有效凭据重试。",
|
||||
"sshTimeoutDescription": "身份验证尝试超时。请重试。",
|
||||
"sshProvideCredentialsDescription": "请提供您的 SSH 凭据以连接到此服务器。",
|
||||
"sshPasswordDescription": "输入此 SSH 连接的密码。",
|
||||
"sshKeyPasswordDescription": "如果您的 SSH 密钥已加密,请在此处输入密码。"
|
||||
},
|
||||
"errors": {
|
||||
"notFound": "页面未找到",
|
||||
|
||||
@@ -250,6 +250,9 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
data: {
|
||||
cols: terminal.cols,
|
||||
rows: terminal.rows,
|
||||
password: credentials.password,
|
||||
sshKey: credentials.sshKey,
|
||||
keyPassword: credentials.keyPassword,
|
||||
hostConfig: {
|
||||
...hostConfig,
|
||||
password: credentials.password,
|
||||
@@ -525,26 +528,6 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
} else if (msg.type === "error") {
|
||||
const errorMessage = msg.message || t("terminal.unknownError");
|
||||
|
||||
if (
|
||||
errorMessage.toLowerCase().includes("auth") ||
|
||||
errorMessage.toLowerCase().includes("password") ||
|
||||
errorMessage.toLowerCase().includes("permission") ||
|
||||
errorMessage.toLowerCase().includes("denied") ||
|
||||
errorMessage.toLowerCase().includes("invalid") ||
|
||||
errorMessage.toLowerCase().includes("failed") ||
|
||||
errorMessage.toLowerCase().includes("incorrect")
|
||||
) {
|
||||
toast.error(t("terminal.authError", { message: errorMessage }));
|
||||
shouldNotReconnectRef.current = true;
|
||||
if (webSocketRef.current) {
|
||||
webSocketRef.current.close();
|
||||
}
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
errorMessage.toLowerCase().includes("connection") ||
|
||||
errorMessage.toLowerCase().includes("timeout") ||
|
||||
@@ -563,6 +546,26 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(errorMessage.toLowerCase().includes("auth") &&
|
||||
errorMessage.toLowerCase().includes("failed")) ||
|
||||
errorMessage.toLowerCase().includes("permission denied") ||
|
||||
(errorMessage.toLowerCase().includes("invalid") &&
|
||||
(errorMessage.toLowerCase().includes("password") ||
|
||||
errorMessage.toLowerCase().includes("key"))) ||
|
||||
errorMessage.toLowerCase().includes("incorrect password")
|
||||
) {
|
||||
toast.error(t("terminal.authError", { message: errorMessage }));
|
||||
shouldNotReconnectRef.current = true;
|
||||
if (webSocketRef.current) {
|
||||
webSocketRef.current.close();
|
||||
}
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
toast.error(t("terminal.error", { message: errorMessage }));
|
||||
} else if (msg.type === "connected") {
|
||||
setIsConnected(true);
|
||||
|
||||
@@ -25,7 +25,7 @@ interface SshToolsSidebarProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function SshToolsSidebar({
|
||||
export function SSHToolsSidebar({
|
||||
isOpen,
|
||||
onClose,
|
||||
}: SshToolsSidebarProps): React.ReactElement | null {
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useRef, useState, useMemo } from "react";
|
||||
import { Terminal } from "@/ui/desktop/apps/terminal/Terminal.tsx";
|
||||
import { Server as ServerView } from "@/ui/desktop/apps/server/Server.tsx";
|
||||
import { FileManager } from "@/ui/desktop/apps/file manager/FileManager.tsx";
|
||||
import { FileManager } from "@/ui/desktop/apps/file-manager/FileManager.tsx";
|
||||
import { useTabs } from "@/ui/desktop/navigation/tabs/TabContext.tsx";
|
||||
import {
|
||||
ResizablePanelGroup,
|
||||
|
||||
@@ -137,7 +137,7 @@ export function SSHAuthDialog({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm"
|
||||
className="absolute inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm"
|
||||
style={{ backgroundColor: `${backgroundColor}dd` }}
|
||||
>
|
||||
<Card className="w-full max-w-2xl mx-4 shadow-2xl">
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useTabs } from "@/ui/desktop/navigation/tabs/TabContext.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TabDropdown } from "@/ui/desktop/navigation/tabs/TabDropdown.tsx";
|
||||
import { SnippetsSidebar } from "@/ui/desktop/apps/terminal/SnippetsSidebar.tsx";
|
||||
import { SshToolsSidebar } from "@/ui/desktop/apps/tools/SshToolsSidebar.tsx";
|
||||
import { SSHToolsSidebar } from "@/ui/desktop/apps/tools/SSHToolsSidebar.tsx";
|
||||
import { ToolsMenu } from "@/ui/desktop/apps/tools/ToolsMenu.tsx";
|
||||
|
||||
interface TabData {
|
||||
@@ -497,7 +497,7 @@ export function TopNavbar({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SshToolsSidebar
|
||||
<SSHToolsSidebar
|
||||
isOpen={toolsSheetOpen}
|
||||
onClose={() => setToolsSheetOpen(false)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user