diff --git a/api/server/middleware/checkDomainAllowed.js b/api/server/middleware/checkDomainAllowed.js index f9af7558cb..b8dafffcce 100644 --- a/api/server/middleware/checkDomainAllowed.js +++ b/api/server/middleware/checkDomainAllowed.js @@ -1,5 +1,6 @@ +const { logger } = require('@librechat/data-schemas'); const { isEmailDomainAllowed } = require('~/server/services/domains'); -const { logger } = require('~/config'); +const { getAppConfig } = require('~/server/services/Config'); /** * Checks the domain's social login is allowed @@ -14,7 +15,10 @@ const { logger } = require('~/config'); */ const checkDomainAllowed = async (req, res, next = () => {}) => { const email = req?.user?.email; - if (email && !(await isEmailDomainAllowed(email))) { + const appConfig = getAppConfig({ + role: req?.user?.role, + }); + if (email && !(await isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains))) { logger.error(`[Social Login] [Social Login not allowed] [Email: ${email}]`); return res.redirect('/login'); } else { diff --git a/api/server/routes/agents/actions.js b/api/server/routes/agents/actions.js index c3a41bc771..e6f6fe064f 100644 --- a/api/server/routes/agents/actions.js +++ b/api/server/routes/agents/actions.js @@ -16,6 +16,7 @@ const { getAgent, updateAgent, getListAgentsByAccess } = require('~/models/Agent const { updateAction, getActions, deleteAction } = require('~/models/Action'); const { isActionDomainAllowed } = require('~/server/services/domains'); const { canAccessAgentResource } = require('~/server/middleware'); +const { getAppConfig } = require('~/server/services/Config/app'); const { getRoleByName } = require('~/models/Role'); const router = express.Router(); @@ -83,7 +84,11 @@ router.post( } let metadata = await encryptMetadata(removeNullishValues(_metadata, true)); - const isDomainAllowed = await isActionDomainAllowed(metadata.domain); + const appConfig = await getAppConfig({ role: req.user.role }); + const isDomainAllowed = await isActionDomainAllowed( + metadata.domain, + appConfig?.registration?.allowedDomains, + ); if (!isDomainAllowed) { return res.status(400).json({ message: 'Domain not allowed' }); } diff --git a/api/server/routes/assistants/actions.js b/api/server/routes/assistants/actions.js index 77122a5ecf..01486c8f75 100644 --- a/api/server/routes/assistants/actions.js +++ b/api/server/routes/assistants/actions.js @@ -21,8 +21,8 @@ const router = express.Router(); * @returns {Object} 200 - success response - application/json */ router.post('/:assistant_id', async (req, res) => { - const appConfig = await getAppConfig({ role: req.user?.role }); try { + const appConfig = await getAppConfig({ role: req.user?.role }); const { assistant_id } = req.params; /** @type {{ functions: FunctionTool[], action_id: string, metadata: ActionMetadata }} */ @@ -32,7 +32,10 @@ router.post('/:assistant_id', async (req, res) => { } let metadata = await encryptMetadata(removeNullishValues(_metadata, true)); - const isDomainAllowed = await isActionDomainAllowed(metadata.domain); + const isDomainAllowed = await isActionDomainAllowed( + metadata.domain, + appConfig?.registration?.allowedDomains, + ); if (!isDomainAllowed) { return res.status(400).json({ message: 'Domain not allowed' }); } diff --git a/api/server/services/AppService.js b/api/server/services/AppService.js index 84adb0e96d..2c67f7de4d 100644 --- a/api/server/services/AppService.js +++ b/api/server/services/AppService.js @@ -84,33 +84,32 @@ const AppService = async () => { // Store MCP config for later initialization const mcpConfig = config.mcpServers || null; - const socialLogins = - config?.registration?.socialLogins ?? configDefaults?.registration?.socialLogins; + const registration = config.registration ?? configDefaults.registration; const interfaceConfig = await loadDefaultInterface(config, configDefaults); const turnstileConfig = loadTurnstileConfig(config, configDefaults); - const defaultLocals = { - config, + const defaultConfig = { ocr, paths, + config, memory, + balance, + mcpConfig, webSearch, fileStrategy, - socialLogins, + registration, filteredTools, includedTools, imageOutputType, interfaceConfig, turnstileConfig, - balance, - mcpConfig, }; const agentsDefaults = agentsConfigSetup(config); if (!Object.keys(config).length) { const appConfig = { - ...defaultLocals, + ...defaultConfig, [EModelEndpoint.agents]: agentsDefaults, }; await setAppConfig(appConfig); @@ -169,7 +168,7 @@ const AppService = async () => { } const appConfig = { - ...defaultLocals, + ...defaultConfig, fileConfig: config?.fileConfig, secureImageLinks: config?.secureImageLinks, modelSpecs: processModelSpecs(endpoints, config.modelSpecs, interfaceConfig), diff --git a/api/server/services/AuthService.js b/api/server/services/AuthService.js index 8c7cbf7d92..c35bb9bd54 100644 --- a/api/server/services/AuthService.js +++ b/api/server/services/AuthService.js @@ -20,9 +20,9 @@ const { deleteUserById, generateRefreshToken, } = require('~/models'); +const { getBalanceConfig, getAppConfig } = require('~/server/services/Config'); const { isEmailDomainAllowed } = require('~/server/services/domains'); const { checkEmailConfig, sendEmail } = require('~/server/utils'); -const { getBalanceConfig } = require('~/server/services/Config'); const { registerSchema } = require('~/strategies/validators'); const domains = { @@ -195,7 +195,8 @@ const registerUser = async (user, additionalData = {}) => { return { status: 200, message: genericVerificationMessage }; } - if (!(await isEmailDomainAllowed(email))) { + const appConfig = await getAppConfig({ role: user.role }); + if (!(await isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains))) { const errorMessage = 'The email address provided cannot be used. Please use a different email address.'; logger.error(`[registerUser] [Registration not allowed] [Email: ${user.email}]`); diff --git a/api/server/services/Config/app.js b/api/server/services/Config/app.js index 7731e8713a..03c90293da 100644 --- a/api/server/services/Config/app.js +++ b/api/server/services/Config/app.js @@ -2,37 +2,6 @@ const { logger } = require('@librechat/data-schemas'); const { CacheKeys } = require('librechat-data-provider'); const getLogStores = require('~/cache/getLogStores'); -/** - * @typedef {Object} AppConfig - * @property {import('librechat-data-provider').TCustomConfig} config - The main custom configuration - * @property {import('librechat-data-provider').TCustomConfig['ocr']} ocr - OCR configuration - * @property {Object} paths - File paths configuration - * @property {import('librechat-data-provider').TMemoryConfig | undefined} memory - Memory configuration - * @property {import('librechat-data-provider').TCustomConfig['webSearch']} webSearch - Web search configuration - * @property {string} fileStrategy - File storage strategy ('local', 's3', 'firebase', 'azure_blob') - * @property {Array} socialLogins - Social login configurations - * @property {string[]} [filteredTools] - Admin-filtered tools - * @property {string[]} [includedTools] - Admin-included tools - * @property {string} imageOutputType - Image output type configuration - * @property {import('librechat-data-provider').TCustomConfig['interface']} interfaceConfig - Interface configuration - * @property {import('librechat-data-provider').TCustomConfig['registration']} turnstileConfig - Turnstile configuration - * @property {import('librechat-data-provider').TCustomConfig['balance']} balance - Balance configuration - * @property {import('librechat-data-provider').TCustomConfig['mcpServers'] | null} mcpConfig - MCP server configuration - * @property {import('librechat-data-provider').TCustomConfig['fileConfig']} [fileConfig] - File configuration - * @property {import('librechat-data-provider').TCustomConfig['secureImageLinks']} [secureImageLinks] - Secure image links configuration - * @property {import('librechat-data-provider').TCustomConfig['modelSpecs'] | undefined} [modelSpecs] - Processed model specifications - * @property {import('librechat-data-provider').TEndpoint} [openAI] - OpenAI endpoint configuration - * @property {import('librechat-data-provider').TEndpoint} [google] - Google endpoint configuration - * @property {import('librechat-data-provider').TEndpoint} [bedrock] - Bedrock endpoint configuration - * @property {import('librechat-data-provider').TEndpoint} [anthropic] - Anthropic endpoint configuration - * @property {import('librechat-data-provider').TEndpoint} [gptPlugins] - GPT plugins endpoint configuration - * @property {import('librechat-data-provider').TEndpoint} [azureOpenAI] - Azure OpenAI endpoint configuration - * @property {import('librechat-data-provider').TEndpoint} [assistants] - Assistants endpoint configuration - * @property {import('librechat-data-provider').TEndpoint} [azureAssistants] - Azure assistants endpoint configuration - * @property {import('librechat-data-provider').TEndpoint} [agents] - Agents endpoint configuration - * @property {import('librechat-data-provider').TEndpoint} [all] - Global endpoint configuration - */ - /** * Get the app configuration based on user context * @param {Object} [options] diff --git a/api/server/services/ToolService.js b/api/server/services/ToolService.js index a2d29a4dbf..382df9bfab 100644 --- a/api/server/services/ToolService.js +++ b/api/server/services/ToolService.js @@ -364,8 +364,10 @@ async function processRequiredActions(client, requiredActions) { const domain = await domainParser(action.metadata.domain, true); domainMap.set(domain, action); - // Check if domain is allowed - const isDomainAllowed = await isActionDomainAllowed(action.metadata.domain); + const isDomainAllowed = await isActionDomainAllowed( + action.metadata.domain, + appConfig?.registration?.allowedDomains, + ); if (!isDomainAllowed) { continue; } @@ -631,7 +633,10 @@ async function loadAgentTools({ req, res, agent, tool_resources, openAIApiKey }) domainMap.set(domain, action); // Check if domain is allowed (do this once per action set) - const isDomainAllowed = await isActionDomainAllowed(action.metadata.domain); + const isDomainAllowed = await isActionDomainAllowed( + action.metadata.domain, + appConfig?.registration?.allowedDomains, + ); if (!isDomainAllowed) { continue; } diff --git a/api/server/services/domains.js b/api/server/services/domains.js index 50e625c3d6..787e23d682 100644 --- a/api/server/services/domains.js +++ b/api/server/services/domains.js @@ -1,10 +1,9 @@ -const { getCustomConfig } = require('~/server/services/Config'); - /** * @param {string} email + * @param {string[]} [allowedDomains] * @returns {Promise} */ -async function isEmailDomainAllowed(email) { +async function isEmailDomainAllowed(email, allowedDomains) { if (!email) { return false; } @@ -15,14 +14,13 @@ async function isEmailDomainAllowed(email) { return false; } - const customConfig = await getCustomConfig(); - if (!customConfig) { + if (!allowedDomains) { return true; - } else if (!customConfig?.registration?.allowedDomains) { + } else if (!Array.isArray(allowedDomains) || !allowedDomains.length) { return true; } - return customConfig.registration.allowedDomains.includes(domain); + return allowedDomains.includes(domain); } /** @@ -65,16 +63,14 @@ function normalizeDomain(domain) { /** * Checks if the given domain is allowed. If no restrictions are set, allows all domains. * @param {string} [domain] + * @param {string[]} [allowedDomains] * @returns {Promise} */ -async function isActionDomainAllowed(domain) { +async function isActionDomainAllowed(domain, allowedDomains) { if (!domain || typeof domain !== 'string') { return false; } - const customConfig = await getCustomConfig(); - const allowedDomains = customConfig?.actions?.allowedDomains; - if (!Array.isArray(allowedDomains) || !allowedDomains.length) { return true; }