fix: prompt shared RDP users for credentials (#1345)

This commit is contained in:
ZacharyZcR
2026-08-27 06:38:56 +08:00
committed by GitHub
parent f848dee343
commit 6406c3a923
8 changed files with 129 additions and 8 deletions
@@ -346,6 +346,7 @@ const CONNECT_LEVEL_FIELDS = new Set([
"enableTelnet",
"sshPort",
"rdpPort",
"rdpAuthType",
"vncPort",
"telnetPort",
"defaultPath",
@@ -34,3 +34,31 @@ export function resolveRdpDomain(
? promptedDomain
: storedDomain;
}
type SharedRdpAuthResolution =
| { source: "personal-override" }
| { source: "owner-shared"; authType: string }
| { source: "secretless" }
| { source: "required" }
| null;
export function resolveRdpAuthTypeForConnect({
storedAuthType,
credentialId,
sharedResolution,
}: {
storedAuthType?: string | null;
credentialId?: number | null;
sharedResolution?: SharedRdpAuthResolution;
}): string {
if (sharedResolution === undefined) {
return storedAuthType || (credentialId ? "credential" : "direct");
}
if (sharedResolution?.source === "personal-override") return "credential";
if (sharedResolution?.source === "owner-shared") {
return sharedResolution.authType;
}
return sharedResolution?.source === "secretless" && storedAuthType === "none"
? "none"
: "direct";
}
+16 -6
View File
@@ -6,7 +6,10 @@ import { withDriveSettings } from "./drive-settings.js";
import { guacLogger } from "../../utils/logger.js";
import { AuthManager } from "../../utils/auth-manager.js";
import { PermissionManager } from "../../utils/permission-manager.js";
import { resolveRecipientSharedHostAuthentication } from "../../utils/shared-host-auth-resolver.js";
import {
resolveRecipientSharedHostAuthentication,
type RecipientSharedHostAuthResolution,
} from "../../utils/shared-host-auth-resolver.js";
import type { AuthOverrideProtocol } from "../../../types/auth-protocols.js";
import net from "net";
import crypto from "crypto";
@@ -25,7 +28,11 @@ import {
getRequestMeta,
} from "../../utils/audit-logger.js";
import { resolveJumpTunnelEndpoint } from "./jump-tunnel-endpoint.js";
import { buildRdpSettings, resolveRdpDomain } from "./rdp-settings.js";
import {
buildRdpSettings,
resolveRdpAuthTypeForConnect,
resolveRdpDomain,
} from "./rdp-settings.js";
import { createMacosVncCompatibilityProxy } from "./macos-vnc-proxy.js";
const router = express.Router();
@@ -322,6 +329,7 @@ router.post(
const hostRecord = host as Record<string, unknown>;
const hostRepository = hostResolutionRepository;
const isSharedConnection = host.userId !== userId;
let sharedAuthResolution: RecipientSharedHostAuthResolution | null = null;
if (isSharedConnection) {
// Recipients never read the owner's raw secrets; wipe them and use
@@ -341,6 +349,7 @@ router.post(
userId,
connectionType as AuthOverrideProtocol,
);
sharedAuthResolution = resolution;
const auth =
resolution.source === "personal-override"
? { ...resolution.credential, domain: null }
@@ -448,10 +457,11 @@ router.post(
let username: string;
let password: string;
const rdpAuthTypeForConnect = isSharedConnection
? null
: (host.rdpAuthType as string) ||
(host.rdpCredentialId ? "credential" : "direct");
const rdpAuthTypeForConnect = resolveRdpAuthTypeForConnect({
storedAuthType: host.rdpAuthType as string | null,
credentialId: host.rdpCredentialId as number | null,
sharedResolution: isSharedConnection ? sharedAuthResolution : undefined,
});
switch (connectionType) {
case "rdp":
@@ -394,6 +394,7 @@ describe("sanitizeHostForRecipient", () => {
{
...sharedHost,
permissionLevel: "connect",
rdpAuthType: "none",
authOverrides: {
ssh: {
credentialId: 9,
@@ -408,6 +409,7 @@ describe("sanitizeHostForRecipient", () => {
expect(result.ip).toBe("10.0.0.42");
expect(result.enableRdp).toBe(true);
expect(result.rdpPort).toBe(3389);
expect(result.rdpAuthType).toBe("none");
expect(result.permissionLevel).toBe("connect");
expect(result.shareSshAuth).toBe(true);
expect(result.authOverrides).toEqual({
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import {
buildRdpSettings,
resolveRdpAuthTypeForConnect,
resolveRdpDomain,
} from "../../../hosts/guacamole/rdp-settings.js";
@@ -52,3 +53,32 @@ describe("buildRdpSettings", () => {
expect(resolveRdpDomain("none", undefined, "SAVED")).toBe("SAVED");
});
});
describe("resolveRdpAuthTypeForConnect", () => {
it("keeps prompt-on-connect authentication for a secretless recipient", () => {
expect(
resolveRdpAuthTypeForConnect({
storedAuthType: "none",
sharedResolution: { source: "secretless" },
}),
).toBe("none");
});
it("uses a recipient override instead of prompting", () => {
expect(
resolveRdpAuthTypeForConnect({
storedAuthType: "none",
sharedResolution: { source: "personal-override" },
}),
).toBe("credential");
});
it("uses authentication shared by the owner", () => {
expect(
resolveRdpAuthTypeForConnect({
storedAuthType: "credential",
sharedResolution: { source: "owner-shared", authType: "credential" },
}),
).toBe("credential");
});
});