#!/usr/bin/env node import { createInterface } from "node:readline"; import { parseArgs } from "node:util"; const { values, positionals } = parseArgs({ allowPositionals: true, options: { policy: { type: "string", multiple: true, default: [] }, "token-ttl": { type: "string", default: "15m" }, "token-max-ttl": { type: "string", default: "1h" }, }, }); const [username] = positionals; const policies = values.policy; const address = process.env.VAULT_ADDR; const token = process.env.VAULT_TOKEN; if (!username || policies.length === 0 || !address || !token) { console.error( "Usage: VAULT_ADDR=... VAULT_TOKEN=... vault_create_user.mjs --policy POLICY [--policy POLICY...] [--token-ttl TTL] [--token-max-ttl TTL] USERNAME", ); process.exit(1); } // Without an output stream readline does not echo the typed characters. const rl = createInterface({ input: process.stdin, terminal: process.stdin.isTTY }); const lines = rl[Symbol.asyncIterator](); const ask = async (prompt) => { process.stdout.write(prompt); const { value } = await lines.next(); process.stdout.write("\n"); return value ?? ""; }; const password = await ask("Password: "); const confirmation = await ask("Repeat the password: "); rl.close(); if (password === "" || password !== confirmation) { console.error("The passwords are empty or do not match"); process.exit(1); } const response = await fetch(`${address}/v1/auth/userpass/users/${username}`, { method: "POST", headers: { "X-Vault-Token": token, "Content-Type": "application/json" }, body: JSON.stringify({ password, token_policies: policies, token_ttl: values["token-ttl"], token_max_ttl: values["token-max-ttl"], }), }); if (!response.ok) { console.error(`Vault returned ${response.status}: ${await response.text()}`); process.exit(1); } console.log(`The user ${username} was created with the policies: ${policies.join(", ")}`);