feat: introduce PASSWORD_HASH and deprecate PASSWORD

This commit is contained in:
Robert Heim 2024-06-18 20:17:00 +02:00
parent 72fbf1baf6
commit eaa4b1ebaa
3 changed files with 42 additions and 10 deletions

View file

@ -29,11 +29,14 @@ const {
WEBUI_HOST,
RELEASE,
PASSWORD,
PASSWORD_HASH,
LANG,
UI_TRAFFIC_STATS,
UI_CHART_TYPE,
} = require('../config');
const requiresPassword = !!PASSWORD || !!PASSWORD_HASH;
module.exports = class Server {
constructor() {
@ -72,7 +75,6 @@ module.exports = class Server {
// Authentication
.get('/api/session', defineEventHandler((event) => {
const requiresPassword = !!process.env.PASSWORD;
const authenticated = requiresPassword
? !!(event.node.req.session && event.node.req.session.authenticated)
: true;
@ -85,19 +87,21 @@ module.exports = class Server {
.post('/api/session', defineEventHandler(async (event) => {
const { password } = await readBody(event);
if (typeof password !== 'string') {
if (!requiresPassword) {
// if no password is required, the API should never be called.
// Do not automatically authenticate the user.
throw createError({
status: 401,
message: 'Missing: Password',
message: 'Invalid state',
});
}
if (password !== PASSWORD) {
if (!isPasswordValid(password)) {
throw createError({
status: 401,
message: 'Incorrect Password',
});
}
};
event.node.req.session.authenticated = true;
event.node.req.session.save();
@ -110,7 +114,7 @@ module.exports = class Server {
// WireGuard
app.use(
fromNodeMiddleware((req, res, next) => {
if (!PASSWORD || !req.url.startsWith('/api/')) {
if (!requiresPassword || !req.url.startsWith('/api/')) {
return next();
}
@ -119,7 +123,7 @@ module.exports = class Server {
}
if (req.url.startsWith('/api/') && req.headers['authorization']) {
if (bcrypt.compareSync(req.headers['authorization'], bcrypt.hashSync(PASSWORD, 10))) {
if (isPasswordValid(req.headers['authorization'])) {
return next();
}
return res.status(401).json({
@ -235,6 +239,32 @@ module.exports = class Server {
});
};
/**
* Checks if `password` matches the PASSWORD_HASH.
*
* For backward compatibility it also allows `password` to match the clear text PASSWORD,
* but only if no PASSWORD_HASH is provided.
*
* If both enviornment variables are not set, the password is always invalid.
*
* @param {string} password String to test
* @returns {boolean} true if matching environment, otherwise false
*/
const isPasswordValid = (password) => {
if (typeof password !== 'string') {
return false;
}
if (!!PASSWORD_HASH) {
return bcrypt.compareSync(password, PASSWORD_HASH);
}
if (!!PASSWORD) {
return password == PASSWORD;
}
return false;
}
// Static assets
const publicDir = '/app/www';
app.use(