mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-01 18:37:41 +01:00
Update server.js to trust UserAccounts securely (#2447)
* Update server.js to trust UserAccounts securely * Update zh-cn.json btw * Clarify security logic * update logic * Fix filtering of enabled users. * Fix account name logging * More friendly log * Even friendlier message * Revert deleted keys --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
This commit is contained in:
parent
fa983521c0
commit
46c91bec67
@ -334,6 +334,9 @@
|
|||||||
"vLLM API key": "vLLM API 密钥",
|
"vLLM API key": "vLLM API 密钥",
|
||||||
"Example: 127.0.0.1:8000": "例如:http://127.0.0.1:8000",
|
"Example: 127.0.0.1:8000": "例如:http://127.0.0.1:8000",
|
||||||
"vLLM Model": "vLLM 模型",
|
"vLLM Model": "vLLM 模型",
|
||||||
|
"HuggingFace Token": "HuggingFace 代币",
|
||||||
|
"Endpoint URL": "端点 URL",
|
||||||
|
"Example: https://****.endpoints.huggingface.cloud": "例如:https://****.endpoints.huggingface.cloud",
|
||||||
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine(用于OpenAI API的包装器)",
|
"PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine(用于OpenAI API的包装器)",
|
||||||
"Aphrodite API key": "Aphrodite API 密钥",
|
"Aphrodite API key": "Aphrodite API 密钥",
|
||||||
"Aphrodite Model": "Aphrodite 模型",
|
"Aphrodite Model": "Aphrodite 模型",
|
||||||
@ -419,6 +422,8 @@
|
|||||||
"Prompt Post-Processing": "提示词后处理",
|
"Prompt Post-Processing": "提示词后处理",
|
||||||
"Applies additional processing to the prompt before sending it to the API.": "在将提示词发送到 API 之前对其进行额外处理。",
|
"Applies additional processing to the prompt before sending it to the API.": "在将提示词发送到 API 之前对其进行额外处理。",
|
||||||
"prompt_post_processing_none": "未选择",
|
"prompt_post_processing_none": "未选择",
|
||||||
|
"01.AI API Key": "01.AI API密钥",
|
||||||
|
"01.AI Model": "01.AI模型",
|
||||||
"Additional Parameters": "附加参数",
|
"Additional Parameters": "附加参数",
|
||||||
"Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "通过发送简短的测试消息验证您的API连接。请注意,您将因此而消耗额度!",
|
"Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "通过发送简短的测试消息验证您的API连接。请注意,您将因此而消耗额度!",
|
||||||
"Test Message": "发送测试消息",
|
"Test Message": "发送测试消息",
|
||||||
@ -1033,6 +1038,8 @@
|
|||||||
"Sticky": "粘性",
|
"Sticky": "粘性",
|
||||||
"Entries with a cooldown can't be activated N messages after being triggered.": "具有冷却时间的条目在触发后 N 条消息内无法被激活。",
|
"Entries with a cooldown can't be activated N messages after being triggered.": "具有冷却时间的条目在触发后 N 条消息内无法被激活。",
|
||||||
"Cooldown": "冷却",
|
"Cooldown": "冷却",
|
||||||
|
"Entries with a delay can't be activated until there are N messages present in the chat.": "直到聊天中出现 N 条消息时,延迟的条目才能被激活。",
|
||||||
|
"Delay": "延迟",
|
||||||
"Filter to Character(s)": "应用到角色",
|
"Filter to Character(s)": "应用到角色",
|
||||||
"Character Exclusion": "反选角色",
|
"Character Exclusion": "反选角色",
|
||||||
"-- Characters not found --": "-- 未找到角色 --",
|
"-- Characters not found --": "-- 未找到角色 --",
|
||||||
@ -1077,6 +1084,7 @@
|
|||||||
"Move message up": "将消息上移",
|
"Move message up": "将消息上移",
|
||||||
"Move message down": "将消息下移",
|
"Move message down": "将消息下移",
|
||||||
"Enlarge": "放大",
|
"Enlarge": "放大",
|
||||||
|
"Caption": "标题",
|
||||||
"Welcome to SillyTavern!": "欢迎来到 SillyTavern!",
|
"Welcome to SillyTavern!": "欢迎来到 SillyTavern!",
|
||||||
"welcome_message_part_1": "阅读",
|
"welcome_message_part_1": "阅读",
|
||||||
"welcome_message_part_2": "官方文档",
|
"welcome_message_part_2": "官方文档",
|
||||||
@ -1113,10 +1121,6 @@
|
|||||||
"alternate_greetings_hint_2": "按钮即可开始!",
|
"alternate_greetings_hint_2": "按钮即可开始!",
|
||||||
"Alternate Greeting #": "额外问候语 #",
|
"Alternate Greeting #": "额外问候语 #",
|
||||||
"(This will be the first message from the character that starts every chat)": "(这将是角色在每次聊天开始时发送的第一条消息)",
|
"(This will be the first message from the character that starts every chat)": "(这将是角色在每次聊天开始时发送的第一条消息)",
|
||||||
"Forbid Media Override explanation": "当前角色/群组在聊天中使用外部媒体的能力。",
|
|
||||||
"Forbid Media Override subtitle": "媒体:图像、视频、音频。外部:不在本地服务器上托管。",
|
|
||||||
"Always forbidden": "始终禁止",
|
|
||||||
"Always allowed": "始终允许",
|
|
||||||
"View contents": "查看内容",
|
"View contents": "查看内容",
|
||||||
"Remove the file": "删除文件",
|
"Remove the file": "删除文件",
|
||||||
"Unique to this chat": "此聊天独有",
|
"Unique to this chat": "此聊天独有",
|
||||||
@ -1240,6 +1244,7 @@
|
|||||||
"Message Template": "消息模板",
|
"Message Template": "消息模板",
|
||||||
"(use _space": "(使用",
|
"(use _space": "(使用",
|
||||||
"macro)": "宏指令)",
|
"macro)": "宏指令)",
|
||||||
|
"Automatically caption images": "自动为图像添加标题",
|
||||||
"Edit captions before saving": "保存前编辑标题",
|
"Edit captions before saving": "保存前编辑标题",
|
||||||
"Character Expressions": "角色表情",
|
"Character Expressions": "角色表情",
|
||||||
"Translate text to English before classification": "分类之前将文本翻译成英文",
|
"Translate text to English before classification": "分类之前将文本翻译成英文",
|
||||||
@ -1579,6 +1584,10 @@
|
|||||||
"Warning:": "警告:",
|
"Warning:": "警告:",
|
||||||
"This action is irreversible.": "此操作不可逆。",
|
"This action is irreversible.": "此操作不可逆。",
|
||||||
"Type the user's handle below to confirm:": "在下面输入用户的名称以确认:",
|
"Type the user's handle below to confirm:": "在下面输入用户的名称以确认:",
|
||||||
|
"Forbid Media Override explanation": "当前角色/群组在聊天中使用外部媒体的能力。",
|
||||||
|
"Forbid Media Override subtitle": "媒体:图像、视频、音频。外部:不在本地服务器上托管。",
|
||||||
|
"Always forbidden": "始终禁止",
|
||||||
|
"Always allowed": "始终允许",
|
||||||
"help_format_1": "文本格式化命令:",
|
"help_format_1": "文本格式化命令:",
|
||||||
"help_format_2": "*文本*",
|
"help_format_2": "*文本*",
|
||||||
"help_format_3": "显示为",
|
"help_format_3": "显示为",
|
||||||
|
57
server.js
57
server.js
@ -609,10 +609,6 @@ const postSetupTasks = async function () {
|
|||||||
console.warn(color.yellow('Basic Authentication is enabled, but username or password is not set or empty!'));
|
console.warn(color.yellow('Basic Authentication is enabled, but username or password is not set or empty!'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listen && !basicAuthMode && enableAccounts) {
|
|
||||||
await userModule.checkAccountsProtection();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -631,16 +627,6 @@ async function loadPlugins() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listen && !enableWhitelist && !basicAuthMode) {
|
|
||||||
if (getConfigValue('securityOverride', false)) {
|
|
||||||
console.warn(color.red('Security has been overridden. If it\'s not a trusted network, change the settings.'));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error(color.red('Your SillyTavern is currently unsecurely open to the public. Enable whitelisting or basic authentication.'));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the title of the terminal window
|
* Set the title of the terminal window
|
||||||
* @param {string} title Desired title for the window
|
* @param {string} title Desired title for the window
|
||||||
@ -654,10 +640,53 @@ function setWindowTitle(title) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints an error message and exits the process if necessary
|
||||||
|
* @param {string} message The error message to print
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function logSecurityAlert(message) {
|
||||||
|
if (basicAuthMode || enableWhitelist) return; // safe!
|
||||||
|
console.error(color.red(message));
|
||||||
|
if (getConfigValue('securityOverride', false)) {
|
||||||
|
console.warn(color.red('Security has been overridden. If it\'s not a trusted network, change the settings.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifySecuritySettings() {
|
||||||
|
// Skip all security checks as listen is set to false
|
||||||
|
if (!listen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enableAccounts) {
|
||||||
|
logSecurityAlert('Your SillyTavern is currently insecurely open to the public. Enable whitelisting, basic authentication or user accounts.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const users = await userModule.getAllEnabledUsers();
|
||||||
|
const unprotectedUsers = users.filter(x => !x.password);
|
||||||
|
const unprotectedAdminUsers = unprotectedUsers.filter(x => x.admin);
|
||||||
|
|
||||||
|
if (unprotectedUsers.length > 0) {
|
||||||
|
console.warn(color.blue('A friendly reminder that the following users are not password protected:'));
|
||||||
|
unprotectedUsers.map(x => `${color.yellow(x.handle)} ${color.red(x.admin ? '(admin)' : '')}`).forEach(x => console.warn(x));
|
||||||
|
console.log();
|
||||||
|
console.warn(`Consider setting a password in the admin panel or by using the ${color.blue('recover.js')} script.`);
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
if (unprotectedAdminUsers.length > 0) {
|
||||||
|
logSecurityAlert('If you are not using basic authentication or whitelisting, you should set a password for all admin users.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// User storage module needs to be initialized before starting the server
|
// User storage module needs to be initialized before starting the server
|
||||||
userModule.initUserStorage(dataRoot)
|
userModule.initUserStorage(dataRoot)
|
||||||
.then(userModule.ensurePublicDirectoriesExist)
|
.then(userModule.ensurePublicDirectoriesExist)
|
||||||
.then(userModule.migrateUserData)
|
.then(userModule.migrateUserData)
|
||||||
|
.then(verifySecuritySettings)
|
||||||
.then(preSetupTasks)
|
.then(preSetupTasks)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (cliArguments.ssl) {
|
if (cliArguments.ssl) {
|
||||||
|
31
src/users.js
31
src/users.js
@ -681,27 +681,27 @@ async function createBackupArchive(handle, response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if any admin users are not password protected. If so, logs a warning.
|
* Gets all of the users.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<User[]>}
|
||||||
*/
|
*/
|
||||||
async function checkAccountsProtection() {
|
async function getAllUsers() {
|
||||||
if (!ENABLE_ACCOUNTS) {
|
if (!ENABLE_ACCOUNTS) {
|
||||||
return;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {User[]}
|
* @type {User[]}
|
||||||
*/
|
*/
|
||||||
const users = await storage.values();
|
const users = await storage.values();
|
||||||
const unprotectedUsers = users.filter(x => x.enabled && x.admin && !x.password);
|
return users;
|
||||||
if (unprotectedUsers.length > 0) {
|
}
|
||||||
console.warn(color.red('The following admin users are not password protected:'));
|
|
||||||
unprotectedUsers.forEach(x => console.warn(color.yellow(x.handle)));
|
/**
|
||||||
console.log();
|
* Gets all of the enabled users.
|
||||||
console.warn('Please disable them or set a password in the admin panel.');
|
* @returns {Promise<User[]>}
|
||||||
console.log();
|
*/
|
||||||
await delay(3000);
|
async function getAllEnabledUsers() {
|
||||||
}
|
const users = await getAllUsers();
|
||||||
|
return users.filter(x => x.enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -738,6 +738,7 @@ module.exports = {
|
|||||||
shouldRedirectToLogin,
|
shouldRedirectToLogin,
|
||||||
createBackupArchive,
|
createBackupArchive,
|
||||||
tryAutoLogin,
|
tryAutoLogin,
|
||||||
checkAccountsProtection,
|
getAllUsers,
|
||||||
|
getAllEnabledUsers,
|
||||||
router,
|
router,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user