Fix SSH encryption and add file download functionality

- Fix SSH authentication by ensuring all database operations use EncryptedDBOperations for automatic encryption/decryption
- Resolve SSH connection failures caused by encrypted password data being passed to authentication
- Add comprehensive file download functionality for SSH file manager (Issue #228)
- Update database migration to add require_password column for SSH sessions
- Enhance debugging and logging for SSH connection troubleshooting

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZacharyZcR
2025-09-16 13:24:25 +08:00
parent 182b60a428
commit 957bc5e41b
10 changed files with 426 additions and 30 deletions

View File

@@ -358,6 +358,17 @@ async function resolveHostCredentials(
host: any,
): Promise<SSHHostWithCredentials | undefined> {
try {
statsLogger.debug(`Resolving credentials for host ${host.id}`, {
operation: 'credential_resolve',
hostId: host.id,
authType: host.authType,
credentialId: host.credentialId,
hasPassword: !!host.password,
hasKey: !!host.key,
passwordLength: host.password?.length || 0,
keyLength: host.key?.length || 0
});
const baseHost: any = {
id: host.id,
name: host.name,
@@ -397,6 +408,16 @@ async function resolveHostCredentials(
if (credentials.length > 0) {
const credential = credentials[0];
statsLogger.debug(`Using credential ${credential.id} for host ${host.id}`, {
operation: 'credential_resolve',
credentialId: credential.id,
authType: credential.authType,
hasPassword: !!credential.password,
hasKey: !!credential.key,
passwordLength: credential.password?.length || 0,
keyLength: credential.key?.length || 0
});
baseHost.credentialId = credential.id;
baseHost.username = credential.username;
baseHost.authType = credential.authType;
@@ -426,9 +447,25 @@ async function resolveHostCredentials(
addLegacyCredentials(baseHost, host);
}
} else {
statsLogger.debug(`Using legacy credentials for host ${host.id}`, {
operation: 'credential_resolve',
hasPassword: !!host.password,
hasKey: !!host.key,
passwordLength: host.password?.length || 0,
keyLength: host.key?.length || 0
});
addLegacyCredentials(baseHost, host);
}
statsLogger.debug(`Final resolved host ${host.id}`, {
operation: 'credential_resolve',
authType: baseHost.authType,
hasPassword: !!baseHost.password,
hasKey: !!baseHost.key,
passwordLength: baseHost.password?.length || 0,
keyLength: baseHost.key?.length || 0
});
return baseHost;
} catch (error) {
statsLogger.error(
@@ -446,6 +483,18 @@ function addLegacyCredentials(baseHost: any, host: any): void {
}
function buildSshConfig(host: SSHHostWithCredentials): ConnectConfig {
statsLogger.debug(`Building SSH config for host ${host.ip}`, {
operation: 'ssh_config',
authType: host.authType,
hasPassword: !!host.password,
hasKey: !!host.key,
username: host.username,
passwordLength: host.password?.length || 0,
keyLength: host.key?.length || 0,
passwordType: typeof host.password,
passwordRaw: host.password ? JSON.stringify(host.password.substring(0, 20)) : null
});
const base: ConnectConfig = {
host: host.ip,
port: host.port || 22,
@@ -458,12 +507,26 @@ function buildSshConfig(host: SSHHostWithCredentials): ConnectConfig {
if (!host.password) {
throw new Error(`No password available for host ${host.ip}`);
}
statsLogger.debug(`Using password auth for ${host.ip}`, {
operation: 'ssh_config',
passwordLength: host.password.length,
passwordFirst3: host.password.substring(0, 3),
passwordLast3: host.password.substring(host.password.length - 3),
passwordType: typeof host.password,
passwordIsString: typeof host.password === 'string'
});
(base as any).password = host.password;
} else if (host.authType === "key") {
if (!host.key) {
throw new Error(`No SSH key available for host ${host.ip}`);
}
statsLogger.debug(`Using key auth for ${host.ip}`, {
operation: 'ssh_config',
keyPreview: host.key.substring(0, Math.min(50, host.key.length)) + '...',
hasPassphrase: !!host.keyPassword
});
try {
if (!host.key.includes("-----BEGIN") || !host.key.includes("-----END")) {
throw new Error("Invalid private key format");