mirror of
https://github.com/guyg1474/hcrb.git
synced 2026-10-16 07:13:28 +02:00
90 lines
3.4 KiB
JavaScript
90 lines
3.4 KiB
JavaScript
// Benutzerverwaltung für den Login
|
||
//
|
||
// npm run user -- list
|
||
// npm run user -- add <name> Benutzer anlegen (Passwort wird abgefragt)
|
||
// npm run user -- passwd <name> Passwort ändern (meldet alle Sitzungen des Benutzers ab)
|
||
// npm run user -- remove <name> Benutzer löschen
|
||
|
||
import { createInterface } from 'node:readline';
|
||
import { hashPassword, readUsers, writeUsers, normalizeName, MIN_PASSWORD, USERS_FILE } from '../auth.js';
|
||
|
||
// Passwort verdeckt einlesen (bei umgeleiteter Eingabe zeilenweise)
|
||
let pipedLines;
|
||
function askPassword(prompt) {
|
||
return new Promise((resolve, reject) => {
|
||
const { stdin, stdout } = process;
|
||
stdout.write(prompt);
|
||
if (!stdin.isTTY) {
|
||
pipedLines ??= createInterface({ input: stdin })[Symbol.asyncIterator]();
|
||
pipedLines.next().then(({ value }) => { stdout.write('\n'); resolve(value ?? ''); }, reject);
|
||
return;
|
||
}
|
||
let input = '';
|
||
stdin.setRawMode(true);
|
||
stdin.setEncoding('utf8');
|
||
stdin.resume();
|
||
const onData = (chunk) => {
|
||
for (const ch of chunk) {
|
||
if (ch === '\r' || ch === '\n') {
|
||
stdin.setRawMode(false);
|
||
stdin.pause();
|
||
stdin.off('data', onData);
|
||
stdout.write('\n');
|
||
return resolve(input);
|
||
}
|
||
if (ch === '\u0003') { // Strg+C
|
||
stdin.setRawMode(false);
|
||
stdout.write('\n');
|
||
return reject(new Error('Abgebrochen.'));
|
||
}
|
||
if (ch === '\u007f' || ch === '\b') input = input.slice(0, -1);
|
||
else input += ch;
|
||
}
|
||
};
|
||
stdin.on('data', onData);
|
||
});
|
||
}
|
||
|
||
async function newPassword() {
|
||
const pw = await askPassword('Neues Passwort: ');
|
||
if (pw.length < MIN_PASSWORD) throw new Error(`Das Passwort muss mindestens ${MIN_PASSWORD} Zeichen haben.`);
|
||
if (await askPassword('Passwort wiederholen: ') !== pw) throw new Error('Die Passwörter stimmen nicht überein.');
|
||
return hashPassword(pw);
|
||
}
|
||
|
||
const [cmd, rawName] = process.argv.slice(2);
|
||
const name = normalizeName(rawName);
|
||
const users = readUsers();
|
||
const user = users.find((u) => u.name === name);
|
||
|
||
try {
|
||
if (cmd === 'list') {
|
||
console.log(users.length ? users.map((u) => u.name).join('\n') : 'Noch keine Benutzer angelegt.');
|
||
} else if (cmd === 'add' && name) {
|
||
if (!/^[a-z0-9._-]{2,32}$/.test(name)) {
|
||
throw new Error('Benutzername: 2–32 Zeichen, nur a–z, 0–9, Punkt, Minus, Unterstrich.');
|
||
}
|
||
if (user) throw new Error(`Benutzer „${name}“ gibt es schon.`);
|
||
users.push({ name, hash: await newPassword(), changedAt: Date.now() });
|
||
writeUsers(users);
|
||
console.log(`Benutzer „${name}“ angelegt.`);
|
||
} else if (cmd === 'passwd' && name) {
|
||
if (!user) throw new Error(`Benutzer „${name}“ gibt es nicht.`);
|
||
user.hash = await newPassword();
|
||
user.changedAt = Date.now();
|
||
writeUsers(users);
|
||
console.log(`Passwort von „${name}“ geändert. Bestehende Anmeldungen sind abgemeldet.`);
|
||
} else if (cmd === 'remove' && name) {
|
||
if (!user) throw new Error(`Benutzer „${name}“ gibt es nicht.`);
|
||
writeUsers(users.filter((u) => u !== user));
|
||
console.log(`Benutzer „${name}“ gelöscht.`);
|
||
} else {
|
||
console.log('Benutzung: npm run user -- list | add <name> | passwd <name> | remove <name>');
|
||
console.log(`Benutzerdatei: ${USERS_FILE}`);
|
||
process.exitCode = cmd ? 1 : 0;
|
||
}
|
||
} catch (err) {
|
||
console.error(err.message);
|
||
process.exitCode = 1;
|
||
}
|