1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, describe, it } from "@effect/vitest";
3
4import * as Effect from "effect/Effect";
5import * as FileSystem from "effect/FileSystem";
6import * as Layer from "effect/Layer";
7import * as Path from "effect/Path";
8import * as HostProcess from "@t3tools/shared/HostProcess";
9
10import {
11 buildSshAskpassHelperDescriptor,
12 buildSshChildEnvironment,
13 isSshAuthFailure,
14} from "./auth.ts";
15
16describe("ssh auth", () => {
17 it.effect("detects ssh auth failures from common permission denied messages", () =>
18 Effect.sync(() => {
19 assert.equal(
20 isSshAuthFailure(
21 new Error(
22 "julius@100.65.180.100: Permission denied (publickey,password,keyboard-interactive).",
23 ),
24 ),
25 true,
26 );
27 assert.equal(isSshAuthFailure(new Error("Permission denied (publickey).")), true);
28 assert.equal(isSshAuthFailure(new Error("Connection timed out")), false);
29 assert.equal(isSshAuthFailure(new Error("mkdir: Permission denied")), false);
30 }),
31 );
32
33 it.effect("creates askpass env for cached password prompts", () =>
34 Effect.gen(function* () {
35 const fs = yield* FileSystem.FileSystem;
36 const path = yield* Path.Path;
37 const directory = yield* fs.makeTempDirectoryScoped({ prefix: "t3-ssh-askpass-test-" });
38 const env = yield* buildSshChildEnvironment({
39 authSecret: "super-secret",
40 interactiveAuth: true,
41 askpassDirectory: directory,
42 baseEnv: {},
43 });
44
45 const askpassPath = path.join(directory, "ssh-askpass.sh");
46 assert.equal(env.SSH_ASKPASS, askpassPath);
47 assert.equal(env.SSH_ASKPASS_REQUIRE, "force");
48 assert.equal(env.T3_SSH_AUTH_SECRET, "super-secret");
49 assert.equal(env.DISPLAY, "t3code");
50 assert.equal(yield* fs.exists(askpassPath), true);
51 assert.include(yield* fs.readFileString(askpassPath), 'printf "%s\\n" "$T3_SSH_AUTH_SECRET"');
52 }).pipe(
53 Effect.provide(Layer.merge(NodeServices.layer, Layer.succeed(HostProcess.Platform, "linux"))),
54 Effect.scoped,
55 ),
56 );
57
58 it.effect("builds a windows askpass launcher pair", () =>
59 Effect.gen(function* () {
60 const descriptor = yield* buildSshAskpassHelperDescriptor({
61 directory: "C:\\temp\\t3code-ssh-askpass",
62 }).pipe(
63 Effect.provide(
64 Layer.merge(NodeServices.layer, Layer.succeed(HostProcess.Platform, "win32")),
65 ),
66 );
67
68 assert.equal(descriptor.launcherPath, "C:\\temp\\t3code-ssh-askpass\\ssh-askpass.cmd");
69 assert.deepEqual(
70 descriptor.files.map((file) => file.path.split("\\").at(-1)),
71 ["ssh-askpass.cmd", "ssh-askpass.ps1"],
72 );
73 }),
74 );
75});