mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
feat: store rdp, vnc and telnet logins in a generic per-protocol table
This commit is contained in:
1 parent
e0c30a4e6f
commit
bba06f7ff6
107 files changed
+18750
-1224
No files matched your search
@@ -74,6 +74,28 @@
|
||||
"accepts": ["button"]
|
||||
}
|
||||
],
|
||||
"protocols": [
|
||||
{
|
||||
"id": "rdp",
|
||||
"credentialFields": [
|
||||
{
|
||||
"key": "domain"
|
||||
}
|
||||
],
|
||||
"defaultPort": 3389,
|
||||
"hostLoginFallback": ["username", "password"]
|
||||
},
|
||||
{
|
||||
"id": "vnc",
|
||||
"defaultPort": 5900,
|
||||
"hostLoginFallback": ["password"]
|
||||
},
|
||||
{
|
||||
"id": "telnet",
|
||||
"defaultPort": 23,
|
||||
"hostLoginFallback": ["password"]
|
||||
}
|
||||
],
|
||||
"guest": true,
|
||||
"permissions": [
|
||||
{
|
||||
|
||||
@@ -141,6 +141,42 @@ export function normalizeImportedHost(
|
||||
return Object.keys(out).length > 0 ? out : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registered as "remote-desktop.hostPayloadLegacy": each protocol's login
|
||||
* in the flat 2.8 shape (rdpUser, rdpDomain, vncAuthType and so on), for
|
||||
* Termix-Mobile until it reads protocolAuth. Built from the payload's own
|
||||
* protocolAuth, which core already reduced for a shared recipient.
|
||||
*/
|
||||
export function hostPayloadLegacy(
|
||||
_values: Record<string, unknown>,
|
||||
host: Record<string, unknown>,
|
||||
): Record<string, unknown> | null {
|
||||
const logins = asObject(host.protocolAuth);
|
||||
if (!logins) return null;
|
||||
const out: Record<string, unknown> = {};
|
||||
for (const protocol of ["rdp", "vnc", "telnet"] as const) {
|
||||
const login = asObject(logins[protocol]);
|
||||
if (!login) continue;
|
||||
out[`${protocol}AuthType`] = login.authType;
|
||||
if (login.credentialId !== undefined) {
|
||||
out[`${protocol}CredentialId`] = login.credentialId;
|
||||
}
|
||||
if (typeof login.username === "string" && login.username) {
|
||||
out[`${protocol}User`] = login.username;
|
||||
}
|
||||
if (typeof login.hasPassword === "boolean") {
|
||||
const name = protocol.charAt(0).toUpperCase() + protocol.slice(1);
|
||||
out[`has${name}Password`] = login.hasPassword;
|
||||
}
|
||||
const domain = asObject(login.fields)?.domain;
|
||||
if (protocol === "rdp" && typeof domain === "string" && domain) {
|
||||
out.rdpDomain = domain;
|
||||
out.domain = domain;
|
||||
}
|
||||
}
|
||||
return Object.keys(out).length > 0 ? out : null;
|
||||
}
|
||||
|
||||
/** User setting key to the guacd parameter it sets. */
|
||||
const USER_DEFAULT_PARAMS: Record<string, string> = {
|
||||
colorDepth: "color-depth",
|
||||
|
||||
@@ -9,6 +9,7 @@ import { resolveGuacdOptions } from "./guacd-config.js";
|
||||
import { registerRoutes } from "./routes.js";
|
||||
import { createLiveSessions } from "./live-sessions.js";
|
||||
import {
|
||||
hostPayloadLegacy,
|
||||
normalizeImportedHost,
|
||||
PORT_KEY,
|
||||
readHostSettings,
|
||||
@@ -107,6 +108,7 @@ export async function activate(ctx: PluginContext) {
|
||||
"remote-desktop.hostImportNormalizer",
|
||||
normalizeImportedHost,
|
||||
);
|
||||
ctx.registry.provide("remote-desktop.hostPayloadLegacy", hostPayloadLegacy);
|
||||
|
||||
// Core's status dot pings these hosts on the protocol's own port.
|
||||
for (const protocol of ["rdp", "vnc", "telnet"] as const) {
|
||||
|
||||
@@ -337,7 +337,7 @@ export function registerRoutes(router: Router, deps: RouteDeps): void {
|
||||
const domain = resolveRdpDomain(
|
||||
target.auth.authType,
|
||||
body.promptedDomain,
|
||||
target.auth.domain,
|
||||
target.auth.fields.domain ?? "",
|
||||
);
|
||||
|
||||
let hostname = target.host.ip;
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
errorMessage,
|
||||
hostRemoteOptions,
|
||||
type Protocol,
|
||||
rdpDomain,
|
||||
type RemoteHostLogin,
|
||||
} from "./host-remote";
|
||||
import { GuacamoleToolbar } from "./GuacamoleToolbar.tsx";
|
||||
@@ -94,9 +95,9 @@ function savedHostConfig(
|
||||
connectionType:
|
||||
protocol ??
|
||||
(options.enableRdp ? "rdp" : options.enableVnc ? "vnc" : "telnet"),
|
||||
domain: host.domain,
|
||||
domain: rdpDomain(host),
|
||||
guacamoleConfig: options.guacamoleConfig,
|
||||
rdpAuthType: host.rdpAuthType,
|
||||
rdpAuthType: host.protocolAuth?.rdp?.authType,
|
||||
authOverrides: host.authOverrides,
|
||||
syncId: host.syncId,
|
||||
connectionOrigin: host.connectionOrigin,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { useTranslation } from "@termix/plugin-sdk/frontend";
|
||||
import {
|
||||
HOST_PROTOCOL_SECRET_KEPT,
|
||||
useTranslation,
|
||||
} from "@termix/plugin-sdk/frontend";
|
||||
import {
|
||||
FakeSwitch,
|
||||
Input,
|
||||
@@ -198,17 +201,17 @@ export function HostEditorRdpTab({
|
||||
<PasswordInput
|
||||
className="h-8 text-xs pr-8"
|
||||
placeholder={
|
||||
form.rdpPassword === "existing_rdp_password"
|
||||
form.rdpPassword === HOST_PROTOCOL_SECRET_KEPT
|
||||
? t("hosts.guac.passwordSaved")
|
||||
: "••••••••"
|
||||
}
|
||||
value={
|
||||
form.rdpPassword === "existing_rdp_password"
|
||||
form.rdpPassword === HOST_PROTOCOL_SECRET_KEPT
|
||||
? ""
|
||||
: form.rdpPassword
|
||||
}
|
||||
onFocus={() => {
|
||||
if (form.rdpPassword === "existing_rdp_password")
|
||||
if (form.rdpPassword === HOST_PROTOCOL_SECRET_KEPT)
|
||||
setField("rdpPassword", "");
|
||||
}}
|
||||
onChange={(e) => setField("rdpPassword", e.target.value)}
|
||||
@@ -1053,17 +1056,17 @@ export function HostEditorVncTab({
|
||||
<PasswordInput
|
||||
className="h-8 text-xs pr-8"
|
||||
placeholder={
|
||||
form.vncPassword === "existing_vnc_password"
|
||||
form.vncPassword === HOST_PROTOCOL_SECRET_KEPT
|
||||
? t("hosts.guac.passwordSaved")
|
||||
: "••••••••"
|
||||
}
|
||||
value={
|
||||
form.vncPassword === "existing_vnc_password"
|
||||
form.vncPassword === HOST_PROTOCOL_SECRET_KEPT
|
||||
? ""
|
||||
: form.vncPassword
|
||||
}
|
||||
onFocus={() => {
|
||||
if (form.vncPassword === "existing_vnc_password")
|
||||
if (form.vncPassword === HOST_PROTOCOL_SECRET_KEPT)
|
||||
setField("vncPassword", "");
|
||||
}}
|
||||
onChange={(e) => setField("vncPassword", e.target.value)}
|
||||
@@ -1544,17 +1547,17 @@ export function HostEditorTelnetTab({
|
||||
<PasswordInput
|
||||
className="h-8 text-xs pr-8"
|
||||
placeholder={
|
||||
form.telnetPassword === "existing_telnet_password"
|
||||
form.telnetPassword === HOST_PROTOCOL_SECRET_KEPT
|
||||
? t("hosts.guac.passwordSaved")
|
||||
: "••••••••"
|
||||
}
|
||||
value={
|
||||
form.telnetPassword === "existing_telnet_password"
|
||||
form.telnetPassword === HOST_PROTOCOL_SECRET_KEPT
|
||||
? ""
|
||||
: form.telnetPassword
|
||||
}
|
||||
onFocus={() => {
|
||||
if (form.telnetPassword === "existing_telnet_password")
|
||||
if (form.telnetPassword === HOST_PROTOCOL_SECRET_KEPT)
|
||||
setField("telnetPassword", "");
|
||||
}}
|
||||
onChange={(e) => setField("telnetPassword", e.target.value)}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { PluginHostRecord } from "@termix/plugin-sdk/frontend";
|
||||
import type {
|
||||
HostProtocolAuthSummary,
|
||||
PluginHostRecord,
|
||||
QuickConnectLogin,
|
||||
} from "@termix/plugin-sdk/frontend";
|
||||
import type { GuacamoleConfig } from "./guacamole-config";
|
||||
|
||||
export const PLUGIN_ID = "remote-desktop";
|
||||
@@ -37,19 +41,18 @@ export const DEFAULT_PORT: Record<Protocol, number> = {
|
||||
telnet: 23,
|
||||
};
|
||||
|
||||
/** The core fields holding a protocol's login, which stay on the host. */
|
||||
/**
|
||||
* The host fields a session reads. Each protocol's login is core's, under
|
||||
* protocolAuth, since this plugin declares its protocols in the manifest.
|
||||
*/
|
||||
export interface RemoteHostLogin {
|
||||
id?: number | string;
|
||||
name?: string | null;
|
||||
ip?: string;
|
||||
domain?: string;
|
||||
syncId?: string | null;
|
||||
connectionOrigin?: "local" | "remote" | null;
|
||||
rdpAuthType?: string;
|
||||
rdpUser?: string;
|
||||
rdpPassword?: string;
|
||||
vncUser?: string;
|
||||
vncPassword?: string;
|
||||
protocolAuth?: Record<string, HostProtocolAuthSummary>;
|
||||
quickConnectLogin?: QuickConnectLogin;
|
||||
authOverrides?: Record<
|
||||
string,
|
||||
{
|
||||
@@ -61,6 +64,11 @@ export interface RemoteHostLogin {
|
||||
pluginSettings?: Record<string, Record<string, unknown>>;
|
||||
}
|
||||
|
||||
/** The RDP domain a host's saved login carries. */
|
||||
export function rdpDomain(host: RemoteHostLogin): string | undefined {
|
||||
return host.protocolAuth?.rdp?.fields?.domain || undefined;
|
||||
}
|
||||
|
||||
function port(value: unknown, fallback: number): number {
|
||||
const parsed = typeof value === "string" ? Number(value) : value;
|
||||
return typeof parsed === "number" && Number.isInteger(parsed) && parsed > 0
|
||||
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
isQuickConnectHost,
|
||||
protocolEnabled,
|
||||
type Protocol,
|
||||
rdpDomain,
|
||||
type RemoteHostLogin,
|
||||
} from "./host-remote";
|
||||
import { remoteDesktopForm } from "./remote-form";
|
||||
@@ -249,8 +250,8 @@ function registerNativeRdp(app: TermixApp): void {
|
||||
void openNativeRdp({
|
||||
host: String(record.ip ?? ""),
|
||||
port: hostRemoteOptions(record).rdpPort,
|
||||
username: record.rdpUser,
|
||||
domain: record.domain,
|
||||
username: record.protocolAuth?.rdp?.username ?? undefined,
|
||||
domain: rdpDomain(record),
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.success) toast.success(app.t("hosts.nativeRdpOpened"));
|
||||
|
||||
@@ -11,14 +11,18 @@ export function quickConnectGuacHost(
|
||||
): GuacamoleQuickHost {
|
||||
const options = hostRemoteOptions(host);
|
||||
const connectionType: Protocol = options.enableVnc ? "vnc" : "rdp";
|
||||
const login =
|
||||
host.quickConnectLogin?.protocol === connectionType
|
||||
? host.quickConnectLogin
|
||||
: undefined;
|
||||
return {
|
||||
name: host.name ?? undefined,
|
||||
ip: host.ip ?? "",
|
||||
connectionType,
|
||||
domain: host.domain,
|
||||
domain: login?.fields?.domain,
|
||||
port: connectionType === "vnc" ? options.vncPort : options.rdpPort,
|
||||
rdpAuthType: host.rdpAuthType,
|
||||
username: connectionType === "vnc" ? host.vncUser : host.rdpUser,
|
||||
password: connectionType === "vnc" ? host.vncPassword : host.rdpPassword,
|
||||
rdpAuthType: "direct",
|
||||
username: login?.username,
|
||||
password: login?.password,
|
||||
};
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
import type { HostEditorSectionProps } from "@termix/plugin-sdk/frontend";
|
||||
import { PLUGIN_ID, remoteOptions } from "./host-remote";
|
||||
import type {
|
||||
HostEditorSectionProps,
|
||||
HostProtocolAuthForm,
|
||||
} from "@termix/plugin-sdk/frontend";
|
||||
import { PLUGIN_ID, remoteOptions, type Protocol } from "./host-remote";
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
/**
|
||||
* The host editor fields the RDP, VNC and Telnet tabs edit. The logins are
|
||||
* core host fields; the options (ports, security, guacd settings) are this
|
||||
* plugin's host settings, presented here under the names the tabs use.
|
||||
* core's, under form.protocolAuth (this plugin declares the protocols in its
|
||||
* manifest); the options (ports, security, guacd settings) are this plugin's
|
||||
* host settings. Both are presented here under the names the tabs use.
|
||||
*/
|
||||
export interface RemoteDesktopForm {
|
||||
domain: string;
|
||||
@@ -46,7 +50,45 @@ const SETTING_FOR_FIELD: Partial<Record<keyof RemoteDesktopForm, string>> = {
|
||||
enableToolbar: "enableToolbar",
|
||||
};
|
||||
|
||||
type LoginPart = "authType" | "credentialId" | "username" | "password";
|
||||
|
||||
/** Tab field name to the protocol login it edits. */
|
||||
const LOGIN_FOR_FIELD: Partial<
|
||||
Record<keyof RemoteDesktopForm, [Protocol, LoginPart | "domain"]>
|
||||
> = {
|
||||
rdpAuthType: ["rdp", "authType"],
|
||||
rdpCredentialId: ["rdp", "credentialId"],
|
||||
rdpUser: ["rdp", "username"],
|
||||
rdpPassword: ["rdp", "password"],
|
||||
domain: ["rdp", "domain"],
|
||||
vncAuthType: ["vnc", "authType"],
|
||||
vncCredentialId: ["vnc", "credentialId"],
|
||||
vncUser: ["vnc", "username"],
|
||||
vncPassword: ["vnc", "password"],
|
||||
telnetAuthType: ["telnet", "authType"],
|
||||
telnetCredentialId: ["telnet", "credentialId"],
|
||||
telnetUser: ["telnet", "username"],
|
||||
telnetPassword: ["telnet", "password"],
|
||||
};
|
||||
|
||||
type PluginSettingsBag = Record<string, Record<string, unknown>>;
|
||||
type LoginBag = Record<string, HostProtocolAuthForm>;
|
||||
|
||||
const EMPTY_LOGIN: HostProtocolAuthForm = {
|
||||
authType: "direct",
|
||||
credentialId: "",
|
||||
username: "",
|
||||
password: "",
|
||||
fields: {},
|
||||
};
|
||||
|
||||
export function loginOf(
|
||||
bag: LoginBag | undefined,
|
||||
protocol: Protocol,
|
||||
): HostProtocolAuthForm {
|
||||
const login = bag?.[protocol];
|
||||
return login ? { ...EMPTY_LOGIN, ...login } : EMPTY_LOGIN;
|
||||
}
|
||||
|
||||
function patchSettings(
|
||||
current: Record<string, unknown>,
|
||||
@@ -59,15 +101,34 @@ function patchSettings(
|
||||
};
|
||||
}
|
||||
|
||||
/** The form patch that sets one part of a protocol's login. */
|
||||
export function patchLogin(
|
||||
current: Record<string, unknown>,
|
||||
protocol: Protocol,
|
||||
part: LoginPart | "domain",
|
||||
value: unknown,
|
||||
): Record<string, unknown> {
|
||||
const bag = (current.protocolAuth as LoginBag | undefined) ?? {};
|
||||
const login = loginOf(bag, protocol);
|
||||
const next: HostProtocolAuthForm =
|
||||
part === "domain"
|
||||
? { ...login, fields: { ...login.fields, domain: String(value ?? "") } }
|
||||
: ({ ...login, [part]: value } as HostProtocolAuthForm);
|
||||
return { protocolAuth: { ...bag, [protocol]: next } };
|
||||
}
|
||||
|
||||
export function remoteDesktopForm(props: HostEditorSectionProps): {
|
||||
form: RemoteDesktopForm;
|
||||
setField: RemoteFormSetField;
|
||||
setGuacField: (key: string, value: unknown) => void;
|
||||
} {
|
||||
const bag = props.form?.pluginSettings as PluginSettingsBag | undefined;
|
||||
const logins = props.form?.protocolAuth as LoginBag | undefined;
|
||||
const options = remoteOptions(bag?.[PLUGIN_ID]);
|
||||
const rdp = loginOf(logins, "rdp");
|
||||
const vnc = loginOf(logins, "vnc");
|
||||
const telnet = loginOf(logins, "telnet");
|
||||
const form: RemoteDesktopForm = {
|
||||
...(props.form as RemoteDesktopForm),
|
||||
rdpPort: options.rdpPort,
|
||||
vncPort: options.vncPort,
|
||||
telnetPort: options.telnetPort,
|
||||
@@ -75,9 +136,29 @@ export function remoteDesktopForm(props: HostEditorSectionProps): {
|
||||
ignoreCert: options.rdpIgnoreCert,
|
||||
guacamoleConfig: options.guacamoleConfig as Record<string, any>,
|
||||
enableToolbar: options.enableToolbar,
|
||||
domain: rdp.fields.domain ?? "",
|
||||
rdpAuthType: rdp.authType,
|
||||
rdpCredentialId: rdp.credentialId,
|
||||
rdpUser: rdp.username,
|
||||
rdpPassword: rdp.password,
|
||||
vncAuthType: vnc.authType === "credential" ? "credential" : "direct",
|
||||
vncCredentialId: vnc.credentialId,
|
||||
vncUser: vnc.username,
|
||||
vncPassword: vnc.password,
|
||||
telnetAuthType: telnet.authType === "credential" ? "credential" : "direct",
|
||||
telnetCredentialId: telnet.credentialId,
|
||||
telnetUser: telnet.username,
|
||||
telnetPassword: telnet.password,
|
||||
};
|
||||
|
||||
const setField: RemoteFormSetField = (key, value) => {
|
||||
const login = LOGIN_FOR_FIELD[key];
|
||||
if (login) {
|
||||
props.updateForm((current) =>
|
||||
patchLogin(current, login[0], login[1], value),
|
||||
);
|
||||
return;
|
||||
}
|
||||
const setting = SETTING_FOR_FIELD[key];
|
||||
if (!setting) {
|
||||
props.setField(key, value);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
hostPayloadLegacy,
|
||||
normalizeImportedHost,
|
||||
userDefaultParams,
|
||||
} from "../../src/backend/host-settings.js";
|
||||
@@ -80,3 +81,38 @@ describe("userDefaultParams", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("hostPayloadLegacy", () => {
|
||||
it("puts each login back in its 2.8 flat shape for older clients", () => {
|
||||
expect(
|
||||
hostPayloadLegacy(
|
||||
{},
|
||||
{
|
||||
protocolAuth: {
|
||||
rdp: {
|
||||
authType: "direct",
|
||||
credentialId: null,
|
||||
username: "admin",
|
||||
fields: { domain: "CORP" },
|
||||
hasPassword: true,
|
||||
},
|
||||
vnc: { authType: "credential", credentialId: 4 },
|
||||
},
|
||||
},
|
||||
),
|
||||
).toEqual({
|
||||
rdpAuthType: "direct",
|
||||
rdpCredentialId: null,
|
||||
rdpUser: "admin",
|
||||
hasRdpPassword: true,
|
||||
rdpDomain: "CORP",
|
||||
domain: "CORP",
|
||||
vncAuthType: "credential",
|
||||
vncCredentialId: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it("adds nothing for a host without logins", () => {
|
||||
expect(hostPayloadLegacy({}, {})).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -37,7 +37,7 @@ function target(
|
||||
authType: "direct",
|
||||
username: "admin",
|
||||
password: "secret",
|
||||
domain: "CORP",
|
||||
fields: { domain: "CORP" },
|
||||
...overrides,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { quickConnectGuacHost } from "../../src/frontend/quick-connect-guac-host";
|
||||
|
||||
// The shape core's Quick Connect builds for a plugin protocol: the switch and
|
||||
// port in this plugin's host settings, the login on the protocol's fields.
|
||||
// port in this plugin's host settings, the unsaved login in quickConnectLogin.
|
||||
function quickHost(protocol: "rdp" | "vnc", extra: Record<string, unknown>) {
|
||||
return {
|
||||
id: "quick-connect-1",
|
||||
@@ -24,9 +24,12 @@ describe("quickConnectGuacHost", () => {
|
||||
expect(
|
||||
quickConnectGuacHost(
|
||||
quickHost("rdp", {
|
||||
rdpUser: "admin",
|
||||
rdpPassword: "pw",
|
||||
domain: "CORP",
|
||||
quickConnectLogin: {
|
||||
protocol: "rdp",
|
||||
username: "admin",
|
||||
password: "pw",
|
||||
fields: { domain: "CORP" },
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toMatchObject({
|
||||
@@ -41,7 +44,11 @@ describe("quickConnectGuacHost", () => {
|
||||
|
||||
it("takes a VNC quick connect's login from the VNC fields", () => {
|
||||
expect(
|
||||
quickConnectGuacHost(quickHost("vnc", { vncPassword: "vncpw" })),
|
||||
quickConnectGuacHost(
|
||||
quickHost("vnc", {
|
||||
quickConnectLogin: { protocol: "vnc", password: "vncpw" },
|
||||
}),
|
||||
),
|
||||
).toMatchObject({
|
||||
connectionType: "vnc",
|
||||
port: 5901,
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { HostEditorSectionProps } from "@termix/plugin-sdk/frontend";
|
||||
import {
|
||||
loginOf,
|
||||
patchLogin,
|
||||
remoteDesktopForm,
|
||||
} from "../../src/frontend/remote-form";
|
||||
|
||||
function sectionProps(form: Record<string, unknown>) {
|
||||
const state = { form: { ...form } };
|
||||
const props = {
|
||||
form: state.form,
|
||||
setField: (key: string, value: unknown) => {
|
||||
state.form = { ...state.form, [key]: value };
|
||||
},
|
||||
updateForm: (
|
||||
patch: (current: Record<string, unknown>) => Record<string, unknown>,
|
||||
) => {
|
||||
state.form = { ...state.form, ...patch(state.form) };
|
||||
},
|
||||
} as unknown as HostEditorSectionProps;
|
||||
return { props, state };
|
||||
}
|
||||
|
||||
describe("remote desktop editor form", () => {
|
||||
it("reads each protocol's login from the host form's protocolAuth", () => {
|
||||
const { props } = sectionProps({
|
||||
protocolAuth: {
|
||||
rdp: {
|
||||
authType: "credential",
|
||||
credentialId: "4",
|
||||
username: "",
|
||||
password: "",
|
||||
fields: { domain: "CORP" },
|
||||
},
|
||||
vnc: {
|
||||
authType: "direct",
|
||||
credentialId: "",
|
||||
username: "",
|
||||
password: "saved",
|
||||
fields: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
const { form } = remoteDesktopForm(props);
|
||||
expect(form).toMatchObject({
|
||||
rdpAuthType: "credential",
|
||||
rdpCredentialId: "4",
|
||||
domain: "CORP",
|
||||
vncPassword: "saved",
|
||||
telnetAuthType: "direct",
|
||||
telnetUser: "",
|
||||
});
|
||||
});
|
||||
|
||||
it("writes the tabs' login fields into protocolAuth", () => {
|
||||
const { props, state } = sectionProps({});
|
||||
const { setField } = remoteDesktopForm(props);
|
||||
setField("rdpUser", "admin");
|
||||
setField("domain", "CORP");
|
||||
setField("telnetAuthType", "credential");
|
||||
|
||||
expect(state.form.protocolAuth).toEqual({
|
||||
rdp: {
|
||||
authType: "direct",
|
||||
credentialId: "",
|
||||
username: "admin",
|
||||
password: "",
|
||||
fields: { domain: "CORP" },
|
||||
},
|
||||
telnet: {
|
||||
authType: "credential",
|
||||
credentialId: "",
|
||||
username: "",
|
||||
password: "",
|
||||
fields: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps other protocols when one changes", () => {
|
||||
const current = {
|
||||
protocolAuth: { vnc: { ...loginOf(undefined, "vnc"), username: "v" } },
|
||||
};
|
||||
const next = patchLogin(current, "rdp", "password", "pw");
|
||||
expect(next.protocolAuth).toMatchObject({
|
||||
vnc: { username: "v" },
|
||||
rdp: { password: "pw" },
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user