|
|
|
|
|
|
|
|
|
import { Buffer } from 'node:buffer'; |
|
import storage from 'node-persist'; |
|
import { getAllUserHandles, toKey, getPasswordHash } from '../users.js'; |
|
import { getConfigValue, safeReadFileSync } from '../util.js'; |
|
|
|
const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false, 'boolean'); |
|
const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false, 'boolean'); |
|
|
|
const basicAuthMiddleware = async function (request, response, callback) { |
|
const unauthorizedWebpage = safeReadFileSync('./public/error/unauthorized.html') ?? ''; |
|
const unauthorizedResponse = (res) => { |
|
res.set('WWW-Authenticate', 'Basic realm="SillyTavern", charset="UTF-8"'); |
|
return res.status(401).send(unauthorizedWebpage); |
|
}; |
|
|
|
const basicAuthUserName = getConfigValue('basicAuthUser.username'); |
|
const basicAuthUserPassword = getConfigValue('basicAuthUser.password'); |
|
const authHeader = request.headers.authorization; |
|
|
|
if (!authHeader) { |
|
return unauthorizedResponse(response); |
|
} |
|
|
|
const [scheme, credentials] = authHeader.split(' '); |
|
|
|
if (scheme !== 'Basic' || !credentials) { |
|
return unauthorizedResponse(response); |
|
} |
|
|
|
const usePerUserAuth = PER_USER_BASIC_AUTH && ENABLE_ACCOUNTS; |
|
const [username, password] = Buffer.from(credentials, 'base64') |
|
.toString('utf8') |
|
.split(':'); |
|
|
|
if (!usePerUserAuth && username === basicAuthUserName && password === basicAuthUserPassword) { |
|
return callback(); |
|
} else if (usePerUserAuth) { |
|
const userHandles = await getAllUserHandles(); |
|
for (const userHandle of userHandles) { |
|
if (username === userHandle) { |
|
const user = await storage.getItem(toKey(userHandle)); |
|
if (user && user.enabled && (user.password && user.password === getPasswordHash(password, user.salt))) { |
|
return callback(); |
|
} |
|
} |
|
} |
|
} |
|
return unauthorizedResponse(response); |
|
}; |
|
|
|
export default basicAuthMiddleware; |
|
|