mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-30 15:15:24 +00:00
133 lines
3.9 KiB
TypeScript
133 lines
3.9 KiB
TypeScript
/**
|
|
* Every SSH auth type the server knows how to connect with.
|
|
*
|
|
* Core registers password, key, credential, agent and none. The rest come from
|
|
* plugins through ctx.auth.registerSshAuthProvider. A type with no provider fails the
|
|
* connect with a message naming the plugin that would provide it.
|
|
*/
|
|
|
|
import type {
|
|
KeyboardInteractiveInterceptor,
|
|
SshAuthProvider,
|
|
} from "./types.js";
|
|
|
|
const providers = new Map<string, SshAuthProvider>();
|
|
const interceptors = new Map<string, KeyboardInteractiveInterceptor>();
|
|
|
|
export interface SshAuthTypeOwner {
|
|
pluginId: string;
|
|
pluginName: string;
|
|
}
|
|
|
|
/**
|
|
* Which plugin declares which auth type, from every manifest on disk
|
|
* including disabled ones. Set by the plugin runtime.
|
|
*/
|
|
let ownerSource: () => Array<SshAuthTypeOwner & { type: string }> = () => [];
|
|
|
|
export function setSshAuthTypeOwnerSource(
|
|
source: () => Array<SshAuthTypeOwner & { type: string }>,
|
|
): void {
|
|
ownerSource = source;
|
|
}
|
|
|
|
export function listSshAuthTypeOwners(): Array<
|
|
SshAuthTypeOwner & { type: string }
|
|
> {
|
|
return ownerSource();
|
|
}
|
|
|
|
function findSshAuthTypeOwner(type: string): SshAuthTypeOwner | null {
|
|
const owner = ownerSource().find((entry) => entry.type === type);
|
|
return owner
|
|
? { pluginId: owner.pluginId, pluginName: owner.pluginName }
|
|
: null;
|
|
}
|
|
|
|
export class SshAuthProviderMissingError extends Error {
|
|
readonly code = "SSH_AUTH_PROVIDER_MISSING";
|
|
constructor(
|
|
readonly authType: string,
|
|
readonly owner: SshAuthTypeOwner | null,
|
|
) {
|
|
super(
|
|
owner
|
|
? `This host uses ${authType}, which needs the ${owner.pluginName} plugin`
|
|
: `This host uses ${authType}, which no enabled plugin provides`,
|
|
);
|
|
this.name = "SshAuthProviderMissingError";
|
|
}
|
|
}
|
|
|
|
export function registerSshAuthProvider(provider: SshAuthProvider): () => void {
|
|
const existing = providers.get(provider.type);
|
|
if (existing && existing.pluginId !== provider.pluginId) {
|
|
throw new Error(
|
|
`SSH auth type "${provider.type}" is already provided by ${existing.pluginId}`,
|
|
);
|
|
}
|
|
providers.set(provider.type, provider);
|
|
return () => {
|
|
if (providers.get(provider.type) === provider) {
|
|
providers.delete(provider.type);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function getSshAuthProvider(type: string): SshAuthProvider | undefined {
|
|
return providers.get(type);
|
|
}
|
|
|
|
/** Throws SshAuthProviderMissingError when nothing provides the type. */
|
|
export function requireSshAuthProvider(type: string): SshAuthProvider {
|
|
const provider = providers.get(type);
|
|
if (!provider) {
|
|
throw new SshAuthProviderMissingError(type, findSshAuthTypeOwner(type));
|
|
}
|
|
return provider;
|
|
}
|
|
|
|
export function listSshAuthProviders(): SshAuthProvider[] {
|
|
return [...providers.values()];
|
|
}
|
|
|
|
/** Whether a registered provider works for a host that is never saved. */
|
|
export function isQuickConnectAuthType(type: string): boolean {
|
|
return providers.get(type)?.quickConnect === true;
|
|
}
|
|
|
|
/** Types a stored credential may use. */
|
|
export function listCredentialTypes(): string[] {
|
|
return listSshAuthProviders()
|
|
.filter((provider) => provider.credentialType)
|
|
.map((provider) => provider.type);
|
|
}
|
|
|
|
export function registerKeyboardInteractiveInterceptor(
|
|
interceptor: KeyboardInteractiveInterceptor,
|
|
): () => void {
|
|
const existing = interceptors.get(interceptor.id);
|
|
if (existing && existing.pluginId !== interceptor.pluginId) {
|
|
throw new Error(
|
|
`Keyboard-interactive handler "${interceptor.id}" is already provided by ${existing.pluginId}`,
|
|
);
|
|
}
|
|
interceptors.set(interceptor.id, interceptor);
|
|
return () => {
|
|
if (interceptors.get(interceptor.id) === interceptor) {
|
|
interceptors.delete(interceptor.id);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function listKeyboardInteractiveInterceptors(): KeyboardInteractiveInterceptor[] {
|
|
return [...interceptors.values()];
|
|
}
|
|
|
|
/** Test helper. */
|
|
export function resetSshAuthRegistryForTests(): void {
|
|
providers.clear();
|
|
interceptors.clear();
|
|
ownerSource = () => [];
|
|
}
|