Fix credential auth optional password (#1009)

This commit is contained in:
ZacharyZcR
2026-07-10 08:03:14 +08:00
committed by GitHub
parent 37560fa133
commit 6b98b86969
8 changed files with 79 additions and 14 deletions
@@ -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();
+13
View File
@@ -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
+8 -4
View File
@@ -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
+2 -1
View File
@@ -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;