v_2.3
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
'use strict';
|
||||
const express = require('express');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const { db } = require('../db');
|
||||
const { log } = require('../lib/audit');
|
||||
const { normaliseEmail, validateEmail, validateUsername, validatePassword } = require('../lib/validate');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/** True while the instance has no users at all (i.e. needs first-boot setup). */
|
||||
function needsSetup() {
|
||||
return db.prepare('SELECT COUNT(*) c FROM users').get().c === 0;
|
||||
}
|
||||
|
||||
// Lets the front-end know whether the wizard should be shown.
|
||||
router.get('/status', (req, res) => {
|
||||
res.json({ needs_setup: needsSetup() });
|
||||
});
|
||||
|
||||
// Create the very first administrator. Only ever available while no users exist.
|
||||
router.post('/', (req, res) => {
|
||||
if (!needsSetup()) {
|
||||
return res.status(409).json({ error: 'Setup has already been completed.' });
|
||||
}
|
||||
|
||||
const username = String((req.body && req.body.username) || '').trim();
|
||||
const email = normaliseEmail(req.body && req.body.email); // mandatory
|
||||
const password = String((req.body && req.body.password) || '');
|
||||
const confirm = String((req.body && req.body.confirm) || '');
|
||||
|
||||
// --- validation (same rules as the Users tab) -------------------------
|
||||
const err = validateUsername(username) || validateEmail(email) || validatePassword(password);
|
||||
if (err) return res.status(400).json({ error: err });
|
||||
if (password !== confirm) {
|
||||
return res.status(400).json({ error: 'The two passwords do not match.' });
|
||||
}
|
||||
|
||||
// --- create -----------------------------------------------------------
|
||||
// Guard against two browsers racing through the wizard simultaneously:
|
||||
// the transaction re-checks the user count before inserting.
|
||||
let created;
|
||||
try {
|
||||
created = db.transaction(() => {
|
||||
if (db.prepare('SELECT COUNT(*) c FROM users').get().c !== 0) {
|
||||
const e = new Error('Setup has already been completed.');
|
||||
e.status = 409;
|
||||
throw e;
|
||||
}
|
||||
const hash = bcrypt.hashSync(password, 12);
|
||||
const info = db
|
||||
.prepare('INSERT INTO users (username, email, password_hash, role) VALUES (?, ?, ?, ?)')
|
||||
.run(username, email, hash, 'admin');
|
||||
return db.prepare('SELECT * FROM users WHERE id = ?').get(info.lastInsertRowid);
|
||||
})();
|
||||
} catch (err) {
|
||||
if (err && err.status === 409) return res.status(409).json({ error: err.message });
|
||||
if (String(err.message || '').includes('UNIQUE')) {
|
||||
return res.status(409).json({ error: 'That username or email is already in use.' });
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
log(req, 'SETUP_COMPLETED', 'Setup', `First administrator "${created.username}" created`);
|
||||
|
||||
// Sign the new administrator straight in — no need to re-type credentials.
|
||||
req.session.user = {
|
||||
id: created.id,
|
||||
username: created.username,
|
||||
email: created.email,
|
||||
role: created.role,
|
||||
mfa_enabled: false,
|
||||
};
|
||||
log(req, 'LOGIN', 'Login', `${created.username} signed in`);
|
||||
|
||||
res.json({ ok: true, user: req.session.user });
|
||||
});
|
||||
|
||||
module.exports = { router, needsSetup };
|
||||
Reference in New Issue
Block a user