clusterflux-public/scripts/cli-browser-login-flow-smoke.js
Disasmer release dry run 20c72e6066 Public dry run dryrun-1714a9eedd5b
Source commit: 1714a9eedd5b1e30c6c9aceb7a999f2f695ba83c

Public tree identity: sha256:e5daffad23fa7c7f1822adccd3c3b4ee0bc7f1a45e0d321eb9a6fb8282b269db
2026-07-03 17:31:04 +02:00

206 lines
6.3 KiB
JavaScript

#!/usr/bin/env node
const assert = require("assert");
const cp = require("child_process");
const fs = require("fs");
const http = require("http");
const net = require("net");
const path = require("path");
const repo = path.resolve(__dirname, "..");
const tmp = path.join(repo, "target", "acceptance", "tmp", "cli-browser-login-flow");
fs.mkdirSync(tmp, { recursive: true });
function writeOpener() {
const opener = path.join(tmp, "browser-opener.js");
const trace = path.join(tmp, "browser-opener.log");
fs.writeFileSync(
opener,
`#!/usr/bin/env node
const fs = require("fs");
const http = require("http");
const trace = ${JSON.stringify(trace)};
fs.appendFileSync(trace, "started " + process.argv.slice(2).join(" ") + "\\n");
const loginUrl = new URL(process.argv[2]);
const state = loginUrl.searchParams.get("state");
const redirect = new URL(loginUrl.searchParams.get("redirect_uri"));
if (!state || !redirect) {
fs.appendFileSync(trace, "missing state or redirect\\n");
console.error("browser opener did not receive state and redirect_uri");
process.exit(1);
}
redirect.searchParams.set("code", "browser-smoke-code");
redirect.searchParams.set("state", state);
fs.appendFileSync(trace, "callback " + redirect.toString() + "\\n");
http.get(redirect, (response) => {
response.resume();
response.on("end", () => {
fs.appendFileSync(trace, "status " + response.statusCode + "\\n");
process.exit(response.statusCode === 200 ? 0 : 1);
});
}).on("error", (error) => {
fs.appendFileSync(trace, "error " + (error.stack || error.message) + "\\n");
console.error(error.stack || error.message);
process.exit(1);
});
`
);
fs.chmodSync(opener, 0o755);
return opener;
}
function listen(server, host = "127.0.0.1") {
return new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, host, () => {
server.off("error", reject);
resolve(server.address());
});
});
}
async function startCoordinator() {
const requests = [];
const server = net.createServer((socket) => {
let buffered = "";
socket.on("data", (chunk) => {
buffered += chunk.toString("utf8");
if (!buffered.includes("\n")) return;
const [line] = buffered.split(/\r?\n/);
const request = JSON.parse(line);
requests.push(request);
assert.strictEqual(request.type, "oidc_browser_login");
assert.strictEqual(request.authorization_code, "browser-smoke-code");
assert.strictEqual(request.issuer_url, "http://127.0.0.1:1");
assert.strictEqual(request.client_id, "disasmer-smoke");
assert.match(request.redirect_path, /^http:\/\/127\.0\.0\.1:45173\/callback$/);
assert.match(request.state, /^sha256:[a-f0-9]{64}$/);
socket.end(
JSON.stringify({
type: "oidc_browser_session",
session: {
tenant: request.tenant,
project: request.project,
user: request.user,
browser_credential_kind: "BrowserSession",
cli_session_credential_kind: "CliDeviceSession",
provider_tokens_sent_to_nodes: false,
flow: {
authorization_url: "http://127.0.0.1:1/application/o/authorize/",
callback_path: request.redirect_path,
state: request.state,
},
oidc_token_exchange: {
token_endpoint: "http://127.0.0.1:1/application/o/token/",
token_type: "Bearer",
received_access_token: true,
received_id_token: true,
retained_provider_tokens: false,
},
},
}) + "\n"
);
server.close();
});
});
const address = await listen(server);
return {
url: `${address.address}:${address.port}`,
requests,
close: () => new Promise((resolve) => server.close(() => resolve())),
};
}
function runDisasmer(args, env) {
return new Promise((resolve, reject) => {
const child = cp.spawn("cargo", args, {
cwd: repo,
env,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
const timeout = setTimeout(() => {
child.kill();
reject(new Error(`disasmer command timed out\n${stderr}`));
}, 30000);
child.stdout.on("data", (chunk) => {
stdout += chunk.toString("utf8");
});
child.stderr.on("data", (chunk) => {
stderr += chunk.toString("utf8");
});
child.on("error", (error) => {
clearTimeout(timeout);
reject(error);
});
child.on("close", (code, signal) => {
clearTimeout(timeout);
if (code !== 0) {
reject(
new Error(
`disasmer command failed with ${signal || code}\nSTDERR:\n${stderr}\nSTDOUT:\n${stdout}`
)
);
} else {
resolve(stdout);
}
});
});
}
(async () => {
const opener = writeOpener();
const coordinator = await startCoordinator();
try {
const stdout = await runDisasmer(
[
"run",
"-q",
"-p",
"disasmer-cli",
"--bin",
"disasmer",
"--",
"login",
"--browser",
"--json",
"--coordinator",
coordinator.url,
"--oidc-issuer-url",
"http://127.0.0.1:1",
"--oidc-client-id",
"disasmer-smoke",
"--tenant",
"tenant-smoke",
"--project-id",
"project-smoke",
"--user",
"user-smoke",
],
{
...process.env,
DISASMER_BROWSER_OPEN_COMMAND: opener,
DISASMER_BROWSER_LOGIN_TIMEOUT_SECONDS: "5",
}
);
const report = JSON.parse(stdout);
assert.strictEqual(report.plan.coordinator, coordinator.url);
assert.strictEqual(report.boundary.cli_contacted_coordinator, true);
assert.strictEqual(report.boundary.scoped_cli_session_received, true);
assert.strictEqual(report.boundary.provider_tokens_exposed_to_cli, false);
assert.strictEqual(report.boundary.provider_tokens_sent_to_nodes, false);
assert.strictEqual(report.boundary.coordinator_session_requests, 1);
assert.strictEqual(coordinator.requests.length, 1);
} finally {
if (coordinator.requests.length === 0) {
await coordinator.close().catch(() => {});
}
}
console.log("CLI browser login flow smoke passed");
})().catch((error) => {
console.error(error.stack || error.message);
process.exit(1);
});