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>
Signed| @@ -334,6 +334,9 @@ | ||
| 334 | 334 | "vLLM API key": "vLLM API 密钥", |
| 335 | 335 | "Example: 127.0.0.1:8000": "例如:http://127.0.0.1:8000", |
| 336 | 336 | "vLLM Model": "vLLM 模型", |
| 337 | + "HuggingFace Token": "HuggingFace 代币", | |
| 338 | + "Endpoint URL": "端点 URL", | |
| 339 | + "Example: https://****.endpoints.huggingface.cloud": "例如:https://****.endpoints.huggingface.cloud", | |
| 337 | 340 | "PygmalionAI/aphrodite-engine": "PygmalionAI/aphrodite-engine(用于OpenAI API的包装器)", |
| 338 | 341 | "Aphrodite API key": "Aphrodite API 密钥", |
| 339 | 342 | "Aphrodite Model": "Aphrodite 模型", |
| @@ -419,6 +422,8 @@ | ||
| 419 | 422 | "Prompt Post-Processing": "提示词后处理", |
| 420 | 423 | "Applies additional processing to the prompt before sending it to the API.": "在将提示词发送到 API 之前对其进行额外处理。", |
| 421 | 424 | "prompt_post_processing_none": "未选择", |
| 425 | + "01.AI API Key": "01.AI API密钥", | |
| 426 | + "01.AI Model": "01.AI模型", | |
| 422 | 427 | "Additional Parameters": "附加参数", |
| 423 | 428 | "Verifies your API connection by sending a short test message. Be aware that you'll be credited for it!": "通过发送简短的测试消息验证您的API连接。请注意,您将因此而消耗额度!", |
| 424 | 429 | "Test Message": "发送测试消息", |
| @@ -1033,6 +1038,8 @@ | ||
| 1033 | 1038 | "Sticky": "粘性", |
| 1034 | 1039 | "Entries with a cooldown can't be activated N messages after being triggered.": "具有冷却时间的条目在触发后 N 条消息内无法被激活。", |
| 1035 | 1040 | "Cooldown": "冷却", |
| 1041 | + "Entries with a delay can't be activated until there are N messages present in the chat.": "直到聊天中出现 N 条消息时,延迟的条目才能被激活。", | |
| 1042 | + "Delay": "延迟", | |
| 1036 | 1043 | "Filter to Character(s)": "应用到角色", |
| 1037 | 1044 | "Character Exclusion": "反选角色", |
| 1038 | 1045 | "-- Characters not found --": "-- 未找到角色 --", |
| @@ -1077,6 +1084,7 @@ | ||
| 1077 | 1084 | "Move message up": "将消息上移", |
| 1078 | 1085 | "Move message down": "将消息下移", |
| 1079 | 1086 | "Enlarge": "放大", |
| 1087 | + "Caption": "标题", | |
| 1080 | 1088 | "Welcome to SillyTavern!": "欢迎来到 SillyTavern!", |
| 1081 | 1089 | "welcome_message_part_1": "阅读", |
| 1082 | 1090 | "welcome_message_part_2": "官方文档", |
| @@ -1113,10 +1121,6 @@ | ||
| 1113 | 1121 | "alternate_greetings_hint_2": "按钮即可开始!", |
| 1114 | 1122 | "Alternate Greeting #": "额外问候语 #", |
| 1115 | 1123 | "(This will be the first message from the character that starts every chat)": "(这将是角色在每次聊天开始时发送的第一条消息)", |
| 1116 | - "Forbid Media Override explanation": "当前角色/群组在聊天中使用外部媒体的能力。", | |
| 1117 | - "Forbid Media Override subtitle": "媒体:图像、视频、音频。外部:不在本地服务器上托管。", | |
| 1118 | - "Always forbidden": "始终禁止", | |
| 1119 | - "Always allowed": "始终允许", | |
| 1120 | 1124 | "View contents": "查看内容", |
| 1121 | 1125 | "Remove the file": "删除文件", |
| 1122 | 1126 | "Unique to this chat": "此聊天独有", |
| @@ -1240,6 +1244,7 @@ | ||
| 1240 | 1244 | "Message Template": "消息模板", |
| 1241 | 1245 | "(use _space": "(使用", |
| 1242 | 1246 | "macro)": "宏指令)", |
| 1247 | + "Automatically caption images": "自动为图像添加标题", | |
| 1243 | 1248 | "Edit captions before saving": "保存前编辑标题", |
| 1244 | 1249 | "Character Expressions": "角色表情", |
| 1245 | 1250 | "Translate text to English before classification": "分类之前将文本翻译成英文", |
| @@ -1579,6 +1584,10 @@ | ||
| 1579 | 1584 | "Warning:": "警告:", |
| 1580 | 1585 | "This action is irreversible.": "此操作不可逆。", |
| 1581 | 1586 | "Type the user's handle below to confirm:": "在下面输入用户的名称以确认:", |
| 1587 | + "Forbid Media Override explanation": "当前角色/群组在聊天中使用外部媒体的能力。", | |
| 1588 | + "Forbid Media Override subtitle": "媒体:图像、视频、音频。外部:不在本地服务器上托管。", | |
| 1589 | + "Always forbidden": "始终禁止", | |
| 1590 | + "Always allowed": "始终允许", | |
| 1582 | 1591 | "help_format_1": "文本格式化命令:", |
| 1583 | 1592 | "help_format_2": "*文本*", |
| 1584 | 1593 | "help_format_3": "显示为", |
| @@ -609,10 +609,6 @@ const postSetupTasks = async function () { | ||
| 609 | 609 | console.warn(color.yellow('Basic Authentication is enabled, but username or password is not set or empty!')); |
| 610 | 610 | } |
| 611 | 611 | } |
| 612 | - | |
| 613 | - if (listen && !basicAuthMode && enableAccounts) { | |
| 614 | - await userModule.checkAccountsProtection(); | |
| 615 | - } | |
| 616 | 612 | }; |
| 617 | 613 | |
| 618 | 614 | /** |
| @@ -631,16 +627,6 @@ async function loadPlugins() { | ||
| 631 | 627 | } |
| 632 | 628 | } |
| 633 | 629 | |
| 634 | -if (listen && !enableWhitelist && !basicAuthMode) { | |
| 635 | - if (getConfigValue('securityOverride', false)) { | |
| 636 | - console.warn(color.red('Security has been overridden. If it\'s not a trusted network, change the settings.')); | |
| 637 | - } | |
| 638 | - else { | |
| 639 | - console.error(color.red('Your SillyTavern is currently unsecurely open to the public. Enable whitelisting or basic authentication.')); | |
| 640 | - process.exit(1); | |
| 641 | - } | |
| 642 | -} | |
| 643 | - | |
| 644 | 630 | /** |
| 645 | 631 | * Set the title of the terminal window |
| 646 | 632 | * @param {string} title Desired title for the window |
| @@ -654,10 +640,53 @@ function setWindowTitle(title) { | ||
| 654 | 640 | } |
| 655 | 641 | } |
| 656 | 642 | |
| 643 | +/** | |
| 644 | + * Prints an error message and exits the process if necessary | |
| 645 | + * @param {string} message The error message to print | |
| 646 | + * @returns {void} | |
| 647 | + */ | |
| 648 | +function logSecurityAlert(message) { | |
| 649 | + if (basicAuthMode || enableWhitelist) return; // safe! | |
| 650 | + console.error(color.red(message)); | |
| 651 | + if (getConfigValue('securityOverride', false)) { | |
| 652 | + console.warn(color.red('Security has been overridden. If it\'s not a trusted network, change the settings.')); | |
| 653 | + return; | |
| 654 | + } | |
| 655 | + process.exit(1); | |
| 656 | +} | |
| 657 | + | |
| 658 | +async function verifySecuritySettings() { | |
| 659 | + // Skip all security checks as listen is set to false | |
| 660 | + if (!listen) { | |
| 661 | + return; | |
| 662 | + } | |
| 663 | + | |
| 664 | + if (!enableAccounts) { | |
| 665 | + logSecurityAlert('Your SillyTavern is currently insecurely open to the public. Enable whitelisting, basic authentication or user accounts.'); | |
| 666 | + } | |
| 667 | + | |
| 668 | + const users = await userModule.getAllEnabledUsers(); | |
| 669 | + const unprotectedUsers = users.filter(x => !x.password); | |
| 670 | + const unprotectedAdminUsers = unprotectedUsers.filter(x => x.admin); | |
| 671 | + | |
| 672 | + if (unprotectedUsers.length > 0) { | |
| 673 | + console.warn(color.blue('A friendly reminder that the following users are not password protected:')); | |
| 674 | + unprotectedUsers.map(x => `${color.yellow(x.handle)} ${color.red(x.admin ? '(admin)' : '')}`).forEach(x => console.warn(x)); | |
| 675 | + console.log(); | |
| 676 | + console.warn(`Consider setting a password in the admin panel or by using the ${color.blue('recover.js')} script.`); | |
| 677 | + console.log(); | |
| 678 | + | |
| 679 | + if (unprotectedAdminUsers.length > 0) { | |
| 680 | + logSecurityAlert('If you are not using basic authentication or whitelisting, you should set a password for all admin users.'); | |
| 681 | + } | |
| 682 | + } | |
| 683 | +} | |
| 684 | + | |
| 657 | 685 | // User storage module needs to be initialized before starting the server |
| 658 | 686 | userModule.initUserStorage(dataRoot) |
| 659 | 687 | .then(userModule.ensurePublicDirectoriesExist) |
| 660 | 688 | .then(userModule.migrateUserData) |
| 689 | + .then(verifySecuritySettings) | |
| 661 | 690 | .then(preSetupTasks) |
| 662 | 691 | .finally(() => { |
| 663 | 692 | if (cliArguments.ssl) { |
| @@ -681,27 +681,27 @@ async function createBackupArchive(handle, response) { | ||
| 681 | 681 | } |
| 682 | 682 | |
| 683 | 683 | /** |
| 684 | - * Checks if any admin users are not password protected. If so, logs a warning. | |
| 684 | + * Gets all of the users. | |
| 685 | 685 | * @returns {Promise<voidUser[]>} |
| 686 | 686 | */ |
| 687 | 687 | async function checkAccountsProtectiongetAllUsers() { |
| 688 | 688 | if (!ENABLE_ACCOUNTS) { |
| 689 | 689 | return []; |
| 690 | 690 | } |
| 691 | - | |
| 692 | 691 | /** |
| 693 | 692 | * @type {User[]} |
| 694 | 693 | */ |
| 695 | 694 | const users = await storage.values(); |
| 696 | - const unprotectedUsers = users.filter(x => x.enabled && x.admin && !x.password); | |
| 695 | + return users; | |
| 697 | - if (unprotectedUsers.length > 0) { | |
| 698 | - console.warn(color.red('The following admin users are not password protected:')); | |
| 699 | - unprotectedUsers.forEach(x => console.warn(color.yellow(x.handle))); | |
| 700 | - console.log(); | |
| 701 | - console.warn('Please disable them or set a password in the admin panel.'); | |
| 702 | - console.log(); | |
| 703 | - await delay(3000); | |
| 704 | 696 | } |
| 697 | + | |
| 698 | +/** | |
| 699 | + * Gets all of the enabled users. | |
| 700 | + * @returns {Promise<User[]>} | |
| 701 | + */ | |
| 702 | +async function getAllEnabledUsers() { | |
| 703 | + const users = await getAllUsers(); | |
| 704 | + return users.filter(x => x.enabled); | |
| 705 | 705 | } |
| 706 | 706 | |
| 707 | 707 | /** |
| @@ -738,6 +738,7 @@ module.exports = { | ||
| 738 | 738 | shouldRedirectToLogin, |
| 739 | 739 | createBackupArchive, |
| 740 | 740 | tryAutoLogin, |
| 741 | 741 | checkAccountsProtectiongetAllUsers, |
| 742 | + getAllEnabledUsers, | |
| 742 | 743 | router, |
| 743 | 744 | }; |