Public: anyone can read this; the crew can change it.

Behind an edge, the terminal's sign-up cannot take a name the edge will later vouch for #2830

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/2829-behind-an-edge-the-terminal-s-sign-up-ca → main 1 commit fork

  • Merged. scout/2829-behind-an-edge-the-terminal-s-sign-up-ca was merged into main as 445e9b5.

Review

Sign in to review this change.
2 files changed +68 −3
src/tui/screens/signin.ts +10 −3
@@ -42,6 +42,9 @@ const CREATE: Field[] = [
4242 const INVITE_BLANK_MESSAGE = "Paste the token from your invite link.";
4343 const INVITE: Field = { label: "Invite", secret: true, hint: "The token from your invite link." };
4444
45 /** Behind an edge the terminal cannot know the login the edge will vouch for. */
46 const EDGE_SIGNUP_MESSAGE = "Sign up on the page: accounts here are tied to the login the edge vouches for.";
47
4548 export function signInScreen(f: Session, ctx: Ctx, onDone: () => void): Screen {
4649 // "Is this forge empty?" is a existence question, not a count: `COUNT(*)`
4750 // walks the whole user table on the one screen every session paints.
@@ -50,7 +53,11 @@ export function signInScreen(f: Session, ctx: Ctx, onDone: () => void): Screen {
5053 // With signup = invite the form is offered, and asks for the invite.
5154 // Both are read where they are used, so the screen follows the live setting.
5255 const invited = (): boolean => getSignup(f.db) === "invite";
53 const closed = (): boolean => !invited() && signupClosed(f.app);
56 // Behind an edge no account is made here: it would be one the edge may later
57 // sign a platform user of the same name into. Sign-in is unchanged.
58 const edge = f.app.config.edgeToken !== undefined;
59 const closed = (): boolean => edge || (!invited() && signupClosed(f.app));
60 const closedText = (): string => (edge ? EDGE_SIGNUP_MESSAGE : SIGNUP_CLOSED_MESSAGE);
5461 let mode: "signin" | "create" = first && !closed() ? "create" : "signin";
5562 let values = ["", "", "", ""];
5663 let focus = 0;
@@ -62,7 +69,7 @@ export function signInScreen(f: Session, ctx: Ctx, onDone: () => void): Screen {
6269 const submit = (): void => {
6370 if (busy) return;
6471 if (closed() && mode === "create") {
65 error = SIGNUP_CLOSED_MESSAGE;
72 error = closedText();
6673 return;
6774 }
6875 const needsInvite = mode === "create" && invited();
@@ -141,7 +148,7 @@ export function signInScreen(f: Session, ctx: Ctx, onDone: () => void): Screen {
141148 head!.y + 1,
142149 truncate(
143150 closed()
144 ? SIGNUP_CLOSED_MESSAGE
151 ? closedText()
145152 : first
146153 ? "This forge has no accounts yet. The operator makes the admin."
147154 : `Connected from ${f.identity.remoteAddress} with ${f.identity.fingerprint ?? "no key"}`,
test/tui-signup-edge.test.ts added +58
@@ -0,0 +1,58 @@
1 /**
2 * Behind an edge the terminal cannot know the login the edge will vouch for,
3 * so its create-account mode makes no row and points at the page.
4 */
5 import { afterAll, beforeAll, expect, test } from "bun:test";
6 import { mkdtemp, rm } from "node:fs/promises";
7 import { tmpdir } from "node:os";
8 import { join } from "node:path";
9 import { createApp, type App as Forge } from "../src/app.ts";
10 import { getUserByName } from "../src/core/users.ts";
11 import { createForgeApp } from "../src/tui/forge.ts";
12 import type { TuiIdentity } from "../src/tui/session.ts";
13 import { headlessTerminal } from "../src/tui/term/terminal.ts";
14 import { quietLogs } from "./helpers/globals.ts";
15 import { settleApp } from "./helpers/tui.ts";
16
17 quietLogs();
18
19 const KEY =
20 "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB2S7bF0R4NmuTUvVJXPPjdI1AF8XHXeYSNfr8DJ5eDo terminal-test";
21
22 let dir: string;
23 let forge: Forge;
24
25 beforeAll(async () => {
26 dir = await mkdtemp(join(tmpdir(), "forge-tui-edge-signup-"));
27 forge = createApp({ dataDir: dir, port: 0, edgeToken: "edge-secret", disk: () => ({ freeBytes: 95, totalBytes: 100 }) });
28 });
29 afterAll(async () => {
30 forge.close();
31 await rm(dir, { recursive: true, force: true });
32 });
33
34 test("with an edge token the terminal sign-up makes no row", async () => {
35 const identity: TuiIdentity = { sshUsername: "git", remoteAddress: "198.51.100.7", fingerprint: "SHA256:x", publicKey: KEY };
36 const { app } = createForgeApp(forge, identity, { terminal: headlessTerminal(120, 32), exitHooks: false });
37 app.start();
38 const key = (k: string, ctrl = false): void =>
39 app.dispatch({ type: "key", key: k, ctrl, alt: false, shift: false });
40 const type = (t: string): void => [...t].forEach((c) => key(c));
41 try {
42 await settleApp(app);
43 key("s");
44 await settleApp(app);
45 key("t", true); // no create mode to switch to
46 await settleApp(app);
47 expect(app.screen.toText()).not.toContain("Email");
48 expect(app.screen.toText()).toContain("Sign up on the page");
49 type("mallory");
50 key("tab");
51 type("a-long-passphrase");
52 key("enter");
53 await settleApp(app);
54 expect(getUserByName(forge.db, "mallory")).toBeFalsy();
55 } finally {
56 app.stop();
57 }
58 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs