mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-17 05:43:36 +00:00
Fix credential auth optional password (#1009)
This commit is contained in:
@@ -25,7 +25,10 @@ import { AuthManager } from "../../utils/auth-manager.js";
|
||||
import { PermissionManager } from "../../utils/permission-manager.js";
|
||||
import { DataCrypto } from "../../utils/data-crypto.js";
|
||||
import { parseSSHKey } from "../../utils/ssh-key-utils.js";
|
||||
import { pickResolvedUsername } from "../../ssh/credential-username.js";
|
||||
import {
|
||||
pickResolvedPassword,
|
||||
pickResolvedUsername,
|
||||
} from "../../ssh/credential-username.js";
|
||||
import {
|
||||
isNonEmptyString,
|
||||
isValidPort,
|
||||
@@ -447,7 +450,12 @@ router.post(
|
||||
sshDataObj.key = key || null;
|
||||
sshDataObj.keyPassword = keyPassword || null;
|
||||
sshDataObj.keyType = keyType;
|
||||
sshDataObj.password = null;
|
||||
sshDataObj.password = password || null;
|
||||
} else if (effectiveAuthType === "credential") {
|
||||
sshDataObj.password = password || null;
|
||||
sshDataObj.key = null;
|
||||
sshDataObj.keyPassword = null;
|
||||
sshDataObj.keyType = null;
|
||||
} else if (effectiveAuthType === "agent") {
|
||||
sshDataObj.password = null;
|
||||
sshDataObj.key = null;
|
||||
@@ -656,7 +664,7 @@ router.post(
|
||||
|
||||
const cred = credentials[0];
|
||||
|
||||
resolvedPassword = cred.password as string | undefined;
|
||||
resolvedPassword = pickResolvedPassword(password, cred.password);
|
||||
resolvedKey = cred.privateKey as string | undefined;
|
||||
resolvedKeyPassword = cred.keyPassword as string | undefined;
|
||||
resolvedKeyType = cred.keyType as string | undefined;
|
||||
@@ -1043,7 +1051,12 @@ router.put(
|
||||
if (keyType) {
|
||||
sshDataObj.keyType = keyType;
|
||||
}
|
||||
sshDataObj.password = null;
|
||||
sshDataObj.password = password || null;
|
||||
} else if (effectiveAuthType === "credential") {
|
||||
sshDataObj.password = password || null;
|
||||
sshDataObj.key = null;
|
||||
sshDataObj.keyPassword = null;
|
||||
sshDataObj.keyType = null;
|
||||
} else if (effectiveAuthType === "agent") {
|
||||
sshDataObj.password = null;
|
||||
sshDataObj.key = null;
|
||||
@@ -2489,7 +2502,7 @@ async function resolveHostCredentials(
|
||||
const credential = credentials[0];
|
||||
const resolvedHost: Record<string, unknown> = {
|
||||
...host,
|
||||
password: credential.password,
|
||||
password: pickResolvedPassword(host.password, credential.password),
|
||||
key: credential.key,
|
||||
keyPassword: credential.keyPassword,
|
||||
keyType: credential.keyType,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import {
|
||||
pickResolvedUsername,
|
||||
pickResolvedPassword,
|
||||
expandOidcUsername,
|
||||
} from "./credential-username.js";
|
||||
|
||||
@@ -30,6 +31,27 @@ describe("pickResolvedUsername", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("pickResolvedPassword", () => {
|
||||
it("keeps the host-specific password ahead of the credential password", () => {
|
||||
expect(pickResolvedPassword("host-pass", "credential-pass")).toBe(
|
||||
"host-pass",
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to the credential password when the host has none", () => {
|
||||
expect(pickResolvedPassword("", "credential-pass")).toBe("credential-pass");
|
||||
expect(pickResolvedPassword(undefined, "credential-pass")).toBe(
|
||||
"credential-pass",
|
||||
);
|
||||
});
|
||||
|
||||
it("treats whitespace-only passwords as empty", () => {
|
||||
expect(pickResolvedPassword(" ", "credential-pass")).toBe(
|
||||
"credential-pass",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("expandOidcUsername", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
|
||||
@@ -21,6 +21,19 @@ export function pickResolvedUsername(
|
||||
return cred;
|
||||
}
|
||||
|
||||
/**
|
||||
* A host can keep a per-host password while using a shared key credential.
|
||||
* Prefer that host-specific value and fall back to the credential password.
|
||||
*/
|
||||
export function pickResolvedPassword(
|
||||
hostPassword: unknown,
|
||||
credentialPassword: unknown,
|
||||
): string | undefined {
|
||||
if (isNonEmptyString(hostPassword)) return hostPassword;
|
||||
if (isNonEmptyString(credentialPassword)) return credentialPassword;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands the `$oidc.preferred_username` placeholder in an SSH username to the
|
||||
* connecting user's OIDC identifier. Returns the username unchanged if it does
|
||||
|
||||
@@ -4,7 +4,10 @@ import { createCorsMiddleware } from "../utils/cors-config.js";
|
||||
import cookieParser from "cookie-parser";
|
||||
import { Client, type ConnectConfig } from "ssh2";
|
||||
import { SSH_ALGORITHMS } from "../utils/ssh-algorithms.js";
|
||||
import { pickResolvedUsername } from "./credential-username.js";
|
||||
import {
|
||||
pickResolvedPassword,
|
||||
pickResolvedUsername,
|
||||
} from "./credential-username.js";
|
||||
import { getDb } from "../database/db/index.js";
|
||||
import { hosts, sshCredentials } from "../database/db/schema.js";
|
||||
import { eq } from "drizzle-orm";
|
||||
@@ -961,9 +964,10 @@ async function resolveHostCredentials(
|
||||
host.overrideCredentialUsername,
|
||||
);
|
||||
|
||||
if (credential.password) {
|
||||
baseHost.password = credential.password;
|
||||
}
|
||||
baseHost.password = pickResolvedPassword(
|
||||
host.password,
|
||||
credential.password,
|
||||
);
|
||||
if (
|
||||
credential.key ||
|
||||
(credential as Record<string, unknown>).privateKey
|
||||
|
||||
@@ -4,6 +4,7 @@ import { eq, and } from "drizzle-orm";
|
||||
import { SimpleDBOps } from "../utils/simple-db-ops.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
import {
|
||||
pickResolvedPassword,
|
||||
pickResolvedUsername,
|
||||
expandOidcUsername,
|
||||
} from "./credential-username.js";
|
||||
@@ -190,7 +191,7 @@ export async function resolveHostById(
|
||||
|
||||
if (credentials.length > 0) {
|
||||
const cred = credentials[0] as Record<string, unknown>;
|
||||
host.password = cred.password;
|
||||
host.password = pickResolvedPassword(host.password, cred.password);
|
||||
// Prefer the normalised private key; fall back to raw key field
|
||||
host.key = (cred.privateKey || cred.key) as string | null;
|
||||
host.keyPassword = cred.keyPassword;
|
||||
|
||||
Reference in New Issue
Block a user