Source commit: 750f29790fe2046599fbe5b5d06fdb63d92fabff Public tree identity: sha256:02326e08850bb287585789733ea5f3f153e1f7f3fd88afcc24e249107df91506
206 lines
6.3 KiB
JavaScript
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);
|
|
});
|