Prevent updates of specific keys via API (#256)

prevent non-admin from updating settings
prevent password updates in multi-user mdoe
This commit is contained in:
Timothy Carambat 2023-09-29 19:44:40 +02:00 committed by GitHub
parent 61777c837b
commit d5b1f84a4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 11 deletions

View File

@ -239,6 +239,16 @@ function systemEndpoints(app) {
async (request, response) => { async (request, response) => {
try { try {
const body = reqBody(request); const body = reqBody(request);
// Only admins can update the ENV settings.
if (multiUserMode(response)) {
const user = await userFromSession(request, response);
if (!user || user?.role !== "admin") {
response.sendStatus(401).end();
return;
}
}
const { newValues, error } = updateENV(body); const { newValues, error } = updateENV(body);
if (process.env.NODE_ENV === "production") await dumpENV(); if (process.env.NODE_ENV === "production") await dumpENV();
response.status(200).json({ newValues, error }); response.status(200).json({ newValues, error });
@ -254,11 +264,21 @@ function systemEndpoints(app) {
[validatedRequest], [validatedRequest],
async (request, response) => { async (request, response) => {
try { try {
// Cannot update password in multi - user mode.
if (multiUserMode(response)) {
response.sendStatus(401).end();
return;
}
const { usePassword, newPassword } = reqBody(request); const { usePassword, newPassword } = reqBody(request);
const { error } = updateENV({ const { error } = updateENV(
{
AuthToken: usePassword ? newPassword : "", AuthToken: usePassword ? newPassword : "",
JWTSecret: usePassword ? v4() : "", JWTSecret: usePassword ? v4() : "",
}); },
true
);
if (process.env.NODE_ENV === "production") await dumpENV();
response.status(200).json({ success: !error, error }); response.status(200).json({ success: !error, error });
} catch (e) { } catch (e) {
console.log(e.message, e); console.log(e.message, e);
@ -293,8 +313,15 @@ function systemEndpoints(app) {
limit_user_messages: false, limit_user_messages: false,
message_limit: 25, message_limit: 25,
}); });
process.env.AUTH_TOKEN = null;
process.env.JWT_SECRET = process.env.JWT_SECRET ?? v4(); // Make sure JWT_SECRET is set for JWT issuance. updateENV(
{
AuthToken: null,
JWTSecret: process.env.JWT_SECRET ?? v4(),
},
true
);
if (process.env.NODE_ENV === "production") await dumpENV();
await Telemetry.sendTelemetry("enabled_multi_user_mode"); await Telemetry.sendTelemetry("enabled_multi_user_mode");
response.status(200).json({ success: !!user, error }); response.status(200).json({ success: !!user, error });
} catch (e) { } catch (e) {

View File

@ -72,11 +72,11 @@ const KEY_MAPPING = {
// System Settings // System Settings
AuthToken: { AuthToken: {
envKey: "AUTH_TOKEN", envKey: "AUTH_TOKEN",
checks: [], checks: [requiresForceMode],
}, },
JWTSecret: { JWTSecret: {
envKey: "JWT_SECRET", envKey: "JWT_SECRET",
checks: [], checks: [requiresForceMode],
}, },
// Not supported yet. // Not supported yet.
// 'StorageDir': 'STORAGE_DIR', // 'StorageDir': 'STORAGE_DIR',
@ -143,11 +143,15 @@ function validAzureURL(input = "") {
} }
} }
function requiresForceMode(_, forceModeEnabled = false) {
return forceModeEnabled === true ? null : "Cannot set this setting.";
}
// This will force update .env variables which for any which reason were not able to be parsed or // This will force update .env variables which for any which reason were not able to be parsed or
// read from an ENV file as this seems to be a complicating step for many so allowing people to write // read from an ENV file as this seems to be a complicating step for many so allowing people to write
// to the process will at least alleviate that issue. It does not perform comprehensive validity checks or sanity checks // to the process will at least alleviate that issue. It does not perform comprehensive validity checks or sanity checks
// and is simply for debugging when the .env not found issue many come across. // and is simply for debugging when the .env not found issue many come across.
function updateENV(newENVs = {}) { function updateENV(newENVs = {}, force = false) {
let error = ""; let error = "";
const validKeys = Object.keys(KEY_MAPPING); const validKeys = Object.keys(KEY_MAPPING);
const ENV_KEYS = Object.keys(newENVs).filter( const ENV_KEYS = Object.keys(newENVs).filter(
@ -159,7 +163,7 @@ function updateENV(newENVs = {}) {
const { envKey, checks } = KEY_MAPPING[key]; const { envKey, checks } = KEY_MAPPING[key];
const value = newENVs[key]; const value = newENVs[key];
const errors = checks const errors = checks
.map((validityCheck) => validityCheck(value)) .map((validityCheck) => validityCheck(value, force))
.filter((err) => typeof err === "string"); .filter((err) => typeof err === "string");
if (errors.length > 0) { if (errors.length > 0) {

View File

@ -253,7 +253,7 @@ async function migrateTable(tableName, migrateRowFunc) {
// Check table exists // Check table exists
const { count } = await db.get( const { count } = await db.get(
`SELECT COUNT(*) as count FROM sqlite_master WHERE name='${tableName}'` `SELECT COUNT(*) as count FROM sqlite_master WHERE name='${tableName}'`
) );
if (count === 0) { if (count === 0) {
console.log( console.log(
`${tableName} does not exist in legacy DB - nothing to migrate - skipping.` `${tableName} does not exist in legacy DB - nothing to migrate - skipping.`