mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 14:45:24 +00:00
feat: move opkssh and warpgate into plugins (C3)
This commit is contained in:
1 parent
925da4a661
commit
98d367d7e2
336 files changed
+22380
-4746
No files matched your search
@@ -0,0 +1,37 @@
|
||||
import type {
|
||||
PluginKeyboardInteractiveDetection,
|
||||
PluginKeyboardInteractivePrompt,
|
||||
} from "@termix/plugin-sdk/backend";
|
||||
|
||||
const WARPGATE_PATTERN = /warpgate\s+authentication/i;
|
||||
const URL_PATTERN = /https?:\/\/[^\s\n]+/i;
|
||||
const SECURITY_KEY_PATTERN =
|
||||
/security key[:\s]+([a-z0-9](?:\s+[a-z0-9]){3}|[a-z0-9]{4})/i;
|
||||
|
||||
/**
|
||||
* Warpgate asks for its browser approval as a keyboard-interactive round
|
||||
* that names itself and carries the sign-in URL and a security key.
|
||||
*/
|
||||
export function detectWarpgateRound(round: {
|
||||
name: string;
|
||||
instructions: string;
|
||||
prompts: PluginKeyboardInteractivePrompt[];
|
||||
}): PluginKeyboardInteractiveDetection | null {
|
||||
const texts = round.prompts.map((prompt) => prompt.prompt);
|
||||
const isWarpgate =
|
||||
WARPGATE_PATTERN.test(round.name) ||
|
||||
WARPGATE_PATTERN.test(round.instructions) ||
|
||||
texts.some((text) => WARPGATE_PATTERN.test(text));
|
||||
if (!isWarpgate) return null;
|
||||
|
||||
const fullText = `${round.name}\n${round.instructions}\n${texts.join("\n")}`;
|
||||
const url = fullText.match(URL_PATTERN);
|
||||
if (!url) return null;
|
||||
const key = fullText.match(SECURITY_KEY_PATTERN);
|
||||
return {
|
||||
kind: "browser",
|
||||
url: url[0],
|
||||
code: key ? key[1] : "N/A",
|
||||
instructions: round.instructions,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Registered as "warpgate.hostImportNormalizer" so core's host import keeps
|
||||
* the flag. Reads a Termix export's pluginSettings.warpgate first, then the
|
||||
* useWarpgate host field exports had before 2.9.0.
|
||||
*/
|
||||
export function hostImportNormalizer(
|
||||
raw: Record<string, unknown>,
|
||||
): Record<string, unknown> | null {
|
||||
const pluginSettings = raw.pluginSettings as
|
||||
Record<string, Record<string, unknown> | undefined> | undefined;
|
||||
const value = pluginSettings?.warpgate?.useWarpgate ?? raw.useWarpgate;
|
||||
if (value === undefined || value === null) return null;
|
||||
return { useWarpgate: value === true || value === 1 || value === "true" };
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { PluginContext } from "@termix/plugin-sdk/backend";
|
||||
import { detectWarpgateRound } from "./detect.js";
|
||||
import { hostImportNormalizer } from "./host-import.js";
|
||||
|
||||
export const HANDLER_ID = "warpgate";
|
||||
|
||||
export async function activate(ctx: PluginContext) {
|
||||
ctx.auth.registerKeyboardInteractiveHandler({
|
||||
id: HANDLER_ID,
|
||||
label: "Warpgate",
|
||||
detect: (round) => detectWarpgateRound(round),
|
||||
// Warpgate asks for the password before its browser round; answer it
|
||||
// silently so the only thing the user sees is the sign-in dialog.
|
||||
autoAnswerPasswords: (_host, settings) => settings.useWarpgate === true,
|
||||
});
|
||||
|
||||
ctx.registry.provide("warpgate.hostImportNormalizer", hostImportNormalizer);
|
||||
}
|
||||
|
||||
export async function deactivate() {
|
||||
// Registered through ctx, so core removes it.
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { TermixApp } from "@termix/plugin-sdk/frontend";
|
||||
|
||||
// The host setting is drawn by core's schema form and the sign-in dialog is
|
||||
// the transports' own, so there is nothing to register.
|
||||
export function activate(_app: TermixApp): void {}
|
||||
Reference in new issue
Block a user