snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
44
apps/meteor/server/configuration/accounts_meld.js
Normal file
44
apps/meteor/server/configuration/accounts_meld.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Users } from '@rocket.chat/models';
|
||||
import { Accounts } from 'meteor/accounts-base';
|
||||
import _ from 'underscore';
|
||||
|
||||
export async function configureAccounts() {
|
||||
const orig_updateOrCreateUserFromExternalService = Accounts.updateOrCreateUserFromExternalService;
|
||||
Accounts.updateOrCreateUserFromExternalService = async function (serviceName, serviceData = {}, ...args /* , options*/) {
|
||||
const services = ['facebook', 'github', 'gitlab', 'google', 'meteor-developer', 'linkedin', 'twitter', 'apple'];
|
||||
|
||||
if (services.includes(serviceName) === false && serviceData._OAuthCustom !== true) {
|
||||
return orig_updateOrCreateUserFromExternalService.apply(this, [serviceName, serviceData, ...args]);
|
||||
}
|
||||
|
||||
if (serviceName === 'meteor-developer') {
|
||||
if (Array.isArray(serviceData.emails)) {
|
||||
const primaryEmail = serviceData.emails.sort((a) => a.primary !== true).filter((item) => item.verified === true)[0];
|
||||
serviceData.email = primaryEmail && primaryEmail.address;
|
||||
}
|
||||
}
|
||||
|
||||
if (serviceData.email) {
|
||||
const user = await Users.findOneByEmailAddress(serviceData.email);
|
||||
if (user != null && user.services?.[serviceName]?.id !== serviceData.id) {
|
||||
const findQuery = {
|
||||
address: serviceData.email,
|
||||
verified: true,
|
||||
};
|
||||
|
||||
if (user.services?.password && !_.findWhere(user.emails, findQuery)) {
|
||||
await Users.resetPasswordAndSetRequirePasswordChange(
|
||||
user._id,
|
||||
true,
|
||||
'This_email_has_already_been_used_and_has_not_been_verified__Please_change_your_password',
|
||||
);
|
||||
}
|
||||
|
||||
await Users.setServiceId(user._id, serviceName, serviceData.id);
|
||||
await Users.setEmailVerified(user._id, serviceData.email);
|
||||
}
|
||||
}
|
||||
|
||||
return orig_updateOrCreateUserFromExternalService.apply(this, [serviceName, serviceData, ...args]);
|
||||
};
|
||||
}
|
||||
37
apps/meteor/server/configuration/cas.ts
Normal file
37
apps/meteor/server/configuration/cas.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import debounce from 'lodash.debounce';
|
||||
import { Accounts } from 'meteor/accounts-base';
|
||||
import { RoutePolicy } from 'meteor/routepolicy';
|
||||
import { WebApp } from 'meteor/webapp';
|
||||
|
||||
import { loginHandlerCAS } from '../lib/cas/loginHandler';
|
||||
import { middlewareCAS } from '../lib/cas/middleware';
|
||||
import { updateCasServices } from '../lib/cas/updateCasService';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureCAS(settings: ICachedSettings) {
|
||||
const _updateCasServices = debounce(updateCasServices, 2000);
|
||||
|
||||
settings.watchByRegex(/^CAS_.+/, async () => {
|
||||
await _updateCasServices();
|
||||
});
|
||||
|
||||
RoutePolicy.declare('/_cas/', 'network');
|
||||
|
||||
// Listen to incoming OAuth http requests
|
||||
WebApp.connectHandlers.use((req, res, next) => {
|
||||
middlewareCAS(req, res, next);
|
||||
});
|
||||
|
||||
/*
|
||||
* Register a server-side login handler.
|
||||
* It is called after Accounts.callLoginMethod() is called from client.
|
||||
*
|
||||
*/
|
||||
Accounts.registerLoginHandler('cas', (options) => {
|
||||
const promise = loginHandlerCAS(options);
|
||||
|
||||
// Pretend the promise has been awaited so the types will match -
|
||||
// #TODO: Fix registerLoginHandler's type definitions (it accepts promises)
|
||||
return promise as unknown as Awaited<typeof promise>;
|
||||
});
|
||||
}
|
||||
6
apps/meteor/server/configuration/configureAssets.ts
Normal file
6
apps/meteor/server/configuration/configureAssets.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { RocketChatAssets } from '../lib/media/assets';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureAssets(settings: ICachedSettings): Promise<void> {
|
||||
settings.watchByRegex(/^Assets_/, (key, value) => RocketChatAssets.processAsset(key, value));
|
||||
}
|
||||
61
apps/meteor/server/configuration/configureBoilerplate.ts
Normal file
61
apps/meteor/server/configuration/configureBoilerplate.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { WebApp, WebAppInternals } from 'meteor/webapp';
|
||||
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
const webAppHashes: Record<string, string> = {};
|
||||
|
||||
export function getWebAppHash(arch: string): string | undefined {
|
||||
if (!webAppHashes[arch]) {
|
||||
const program = WebApp.clientPrograms[arch] as (typeof WebApp.clientPrograms)[string] & {
|
||||
meteorRuntimeConfig: string;
|
||||
};
|
||||
webAppHashes[arch] = createHash('sha1')
|
||||
.update(JSON.stringify(encodeURIComponent(program.meteorRuntimeConfig)))
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
return webAppHashes[arch];
|
||||
}
|
||||
|
||||
const { generateBoilerplate } = WebAppInternals;
|
||||
|
||||
WebAppInternals.generateBoilerplate = function (...args: Parameters<typeof generateBoilerplate>) {
|
||||
for (const arch of Object.keys(WebApp.clientPrograms)) {
|
||||
delete webAppHashes[arch];
|
||||
}
|
||||
return generateBoilerplate.apply(this, args);
|
||||
};
|
||||
|
||||
export function configureBoilerplate(settings: ICachedSettings): void {
|
||||
settings.watch<string>(
|
||||
'Site_Url',
|
||||
// Needed as WebAppInternals.generateBoilerplate needs to be called in a fiber
|
||||
Meteor.bindEnvironment(async (value) => {
|
||||
if (value == null || value.trim() === '') {
|
||||
return;
|
||||
}
|
||||
let host = value.replace(/\/$/, '');
|
||||
// let prefix = '';
|
||||
const match = value.match(/([^\/]+\/{2}[^\/]+)(\/.+)/);
|
||||
if (match != null) {
|
||||
host = match[1];
|
||||
// prefix = match[2].replace(/\/$/, '');
|
||||
}
|
||||
(global as any).__meteor_runtime_config__.ROOT_URL = value;
|
||||
|
||||
if (Meteor.absoluteUrl.defaultOptions?.rootUrl) {
|
||||
Meteor.absoluteUrl.defaultOptions.rootUrl = value;
|
||||
}
|
||||
|
||||
// hostname = host.replace(/^https?:\/\//, '');
|
||||
process.env.MOBILE_ROOT_URL = host;
|
||||
process.env.MOBILE_DDP_URL = host;
|
||||
if (typeof WebAppInternals !== 'undefined' && WebAppInternals.generateBoilerplate) {
|
||||
await WebAppInternals.generateBoilerplate();
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
32
apps/meteor/server/configuration/configureCDN.ts
Normal file
32
apps/meteor/server/configuration/configureCDN.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { WebAppInternals } from 'meteor/webapp';
|
||||
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureCDN(settings: ICachedSettings): Promise<void> {
|
||||
settings.change<string>('CDN_PREFIX', (value) => {
|
||||
const useForAll = settings.get('CDN_PREFIX_ALL');
|
||||
if (value && useForAll) {
|
||||
WebAppInternals.setBundledJsCssPrefix(value);
|
||||
}
|
||||
});
|
||||
|
||||
settings.change<string>('CDN_JSCSS_PREFIX', (value) => {
|
||||
const useForAll = settings.get('CDN_PREFIX_ALL');
|
||||
if (value && typeof value === 'string' && value.trim() && !useForAll) {
|
||||
WebAppInternals.setBundledJsCssPrefix(value);
|
||||
}
|
||||
});
|
||||
|
||||
const cdnValue = settings.get('CDN_PREFIX');
|
||||
const useForAll = settings.get('CDN_PREFIX_ALL');
|
||||
const cdnJsCss = settings.get('CDN_JSCSS_PREFIX');
|
||||
if (cdnValue && typeof cdnValue === 'string' && cdnValue.trim()) {
|
||||
if (useForAll) {
|
||||
WebAppInternals.setBundledJsCssPrefix(cdnValue);
|
||||
return;
|
||||
}
|
||||
if (cdnJsCss && typeof cdnJsCss === 'string' && cdnJsCss.trim()) {
|
||||
WebAppInternals.setBundledJsCssPrefix(cdnJsCss);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
apps/meteor/server/configuration/configureCORS.ts
Normal file
11
apps/meteor/server/configuration/configureCORS.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { setCachingVersion, setInlineScriptsAllowed } from '../lib/cors/cors';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureCORS(settings: ICachedSettings): Promise<void> {
|
||||
settings.watch<boolean>('Enable_CSP', async (enabled) => {
|
||||
await setInlineScriptsAllowed(!enabled);
|
||||
});
|
||||
settings.watch<string>('Troubleshoot_Force_Caching_Version', (value) => {
|
||||
setCachingVersion(value);
|
||||
});
|
||||
}
|
||||
45
apps/meteor/server/configuration/configureDirectReply.ts
Normal file
45
apps/meteor/server/configuration/configureDirectReply.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import _ from 'underscore';
|
||||
|
||||
import { logger } from '../features/EmailInbox/logger';
|
||||
import { DirectReplyIMAPInterceptor, POP3Helper } from '../lib/notifications/interceptDirectReplyEmails';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureDirectReply(settings: ICachedSettings): Promise<void> {
|
||||
let client: DirectReplyIMAPInterceptor | POP3Helper | undefined;
|
||||
const startEmailInterceptor = _.debounce(async () => {
|
||||
logger.info('Email Interceptor...');
|
||||
const protocol = settings.get('Direct_Reply_Protocol');
|
||||
|
||||
const isEnabled =
|
||||
settings.get('Direct_Reply_Enable') &&
|
||||
protocol &&
|
||||
settings.get('Direct_Reply_Host') &&
|
||||
settings.get('Direct_Reply_Port') &&
|
||||
settings.get('Direct_Reply_Username') &&
|
||||
settings.get('Direct_Reply_Password');
|
||||
|
||||
if (client) {
|
||||
await client.stop();
|
||||
}
|
||||
|
||||
if (!isEnabled) {
|
||||
logger.info('Email Interceptor Stopped...');
|
||||
return;
|
||||
}
|
||||
logger.info('Starting Email Interceptor...');
|
||||
|
||||
if (protocol === 'IMAP') {
|
||||
client = new DirectReplyIMAPInterceptor();
|
||||
await client.start();
|
||||
}
|
||||
|
||||
if (protocol === 'POP') {
|
||||
client = new POP3Helper(settings.get('Direct_Reply_Frequency'));
|
||||
client.start();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
settings.watchByRegex(/^Direct_Reply_.+/, startEmailInterceptor);
|
||||
|
||||
void startEmailInterceptor();
|
||||
}
|
||||
28
apps/meteor/server/configuration/configureIRC.ts
Normal file
28
apps/meteor/server/configuration/configureIRC.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import Bridge from '../bridges/irc/irc-bridge';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureIRC(settings: ICachedSettings): Promise<void> {
|
||||
if (!settings.get('IRC_Enabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize the config values
|
||||
const config = {
|
||||
server: {
|
||||
protocol: settings.get('IRC_Protocol'),
|
||||
host: settings.get('IRC_Host'),
|
||||
port: settings.get('IRC_Port'),
|
||||
name: settings.get('IRC_Name'),
|
||||
description: settings.get('IRC_Description'),
|
||||
},
|
||||
passwords: {
|
||||
local: settings.get('IRC_Local_Password'),
|
||||
peer: settings.get('IRC_Peer_Password'),
|
||||
},
|
||||
};
|
||||
|
||||
Meteor.ircBridge = new Bridge(config);
|
||||
await Meteor.ircBridge.init();
|
||||
}
|
||||
17
apps/meteor/server/configuration/configureLogLevel.ts
Normal file
17
apps/meteor/server/configuration/configureLogLevel.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { logLevel, type LogLevelSetting } from '@rocket.chat/logger';
|
||||
import { Settings } from '@rocket.chat/models';
|
||||
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureLogLevel(settings: ICachedSettings) {
|
||||
const LogLevel = await Settings.getValueById('Log_Level');
|
||||
if (LogLevel) {
|
||||
logLevel.emit('changed', LogLevel as LogLevelSetting);
|
||||
}
|
||||
|
||||
settings.watch('Log_Level', (value) => {
|
||||
if (value != null) {
|
||||
logLevel.emit('changed', String(value) as LogLevelSetting);
|
||||
}
|
||||
});
|
||||
}
|
||||
104
apps/meteor/server/configuration/configurePassport.ts
Normal file
104
apps/meteor/server/configuration/configurePassport.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { Users } from '@rocket.chat/models';
|
||||
import bodyParser from 'body-parser';
|
||||
import MongoStore from 'connect-mongo';
|
||||
import express from 'express';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import session from 'express-session';
|
||||
import { MongoInternals } from 'meteor/mongo';
|
||||
import { WebApp } from 'meteor/webapp';
|
||||
import passport from 'passport';
|
||||
|
||||
import { configureOAuthServices } from '../lib/oauth/configureOAuthServices';
|
||||
import { createOAuthServiceConfig } from '../lib/oauth/createOAuthServiceConfig';
|
||||
import { getOAuthServices } from '../lib/oauth/getOAuthServices';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
const oAuthPaths = ['/oauth', '/_oauth'];
|
||||
|
||||
const { Router: router } = express;
|
||||
|
||||
export const oAuthRouter = router();
|
||||
|
||||
const oAuthApp = express();
|
||||
oAuthApp.set('trust proxy', 1);
|
||||
|
||||
export const configurePassport = (settings: ICachedSettings) => {
|
||||
const { client } = MongoInternals.defaultRemoteCollectionDriver().mongo;
|
||||
|
||||
oAuthApp.use(
|
||||
oAuthPaths,
|
||||
session({
|
||||
name: 'oauth',
|
||||
secret: settings.get<string>('Accounts_OAuth_Session_Secret'),
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
proxy: true,
|
||||
store: MongoStore.create({
|
||||
client,
|
||||
collectionName: 'rocketchat_oauth_sessions',
|
||||
ttl: 5 * 60,
|
||||
autoRemove: 'native',
|
||||
}),
|
||||
cookie: {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
maxAge: 5 * 60 * 1000, // 5 minutes
|
||||
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
oAuthApp.use(oAuthPaths, passport.initialize());
|
||||
oAuthApp.use(oAuthPaths, passport.session());
|
||||
oAuthApp.use(oAuthPaths, bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
const createOAuthRateLimiter = () =>
|
||||
rateLimit({
|
||||
windowMs: settings.get<number>('API_Enable_Rate_Limiter_Limit_Time_Default'),
|
||||
max: settings.get<number>('API_Enable_Rate_Limiter_Limit_Calls_Default'),
|
||||
skip: () =>
|
||||
process.env.TEST_MODE === 'true' ||
|
||||
process.env.TEST_MODE === 'api' ||
|
||||
settings.get<boolean>('API_Enable_Rate_Limiter') !== true ||
|
||||
(process.env.NODE_ENV === 'development' && settings.get<boolean>('API_Enable_Rate_Limiter_Dev') !== true),
|
||||
handler: (_req, res) => {
|
||||
res.status(429).json({
|
||||
success: false,
|
||||
error: 'Too many requests. Please try again later.',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
let oauthRateLimiter = createOAuthRateLimiter();
|
||||
|
||||
settings.watchMultiple(['API_Enable_Rate_Limiter_Limit_Time_Default', 'API_Enable_Rate_Limiter_Limit_Calls_Default'], () => {
|
||||
oauthRateLimiter = createOAuthRateLimiter();
|
||||
});
|
||||
|
||||
oAuthRouter.use(oAuthPaths, (req, res, next) => oauthRateLimiter(req, res, next));
|
||||
|
||||
// Register OAuth Routes
|
||||
oAuthApp.use(oAuthRouter);
|
||||
|
||||
passport.serializeUser((user: any, done) => {
|
||||
done(null, user._id);
|
||||
});
|
||||
|
||||
passport.deserializeUser(async (id, done) => {
|
||||
const user = await Users.findOneById(id as string, { projection: { __rooms: 0 } });
|
||||
// we don’t actually use this user later
|
||||
done(null, user);
|
||||
});
|
||||
|
||||
settings.watchByRegex(/^(Accounts_OAuth_[a-z0-9_]+|API_GitHub_Enterprise_URL)$/i, () => {
|
||||
if (!settings.get<boolean>('Accounts_OAuth_Use_Modern_Flow')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const services = getOAuthServices(settings);
|
||||
const oauthServiceConfigs = createOAuthServiceConfig(settings, services);
|
||||
configureOAuthServices(oauthServiceConfigs, settings);
|
||||
});
|
||||
|
||||
WebApp.rawConnectHandlers.use(oAuthApp);
|
||||
};
|
||||
34
apps/meteor/server/configuration/configureSMTP.ts
Normal file
34
apps/meteor/server/configuration/configureSMTP.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { SystemLogger } from '../lib/logger/system';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export function configureSMTP(settings: ICachedSettings): void {
|
||||
settings.watchMultiple(
|
||||
['SMTP_Host', 'SMTP_Port', 'SMTP_Username', 'SMTP_Password', 'SMTP_Protocol', 'SMTP_Pool', 'SMTP_IgnoreTLS'],
|
||||
() => {
|
||||
SystemLogger.info('Updating process.env.MAIL_URL');
|
||||
|
||||
if (!settings.get('SMTP_Host')) {
|
||||
delete process.env.MAIL_URL;
|
||||
return;
|
||||
}
|
||||
|
||||
process.env.MAIL_URL = `${settings.get('SMTP_Protocol')}://`;
|
||||
|
||||
if (settings.get('SMTP_Username') && settings.get('SMTP_Password')) {
|
||||
process.env.MAIL_URL += `${encodeURIComponent(settings.get('SMTP_Username'))}:${encodeURIComponent(settings.get('SMTP_Password'))}@`;
|
||||
}
|
||||
|
||||
process.env.MAIL_URL += encodeURIComponent(settings.get('SMTP_Host'));
|
||||
|
||||
if (settings.get('SMTP_Port')) {
|
||||
process.env.MAIL_URL += `:${parseInt(settings.get('SMTP_Port'))}`;
|
||||
}
|
||||
|
||||
process.env.MAIL_URL += `?pool=${settings.get('SMTP_Pool')}`;
|
||||
|
||||
if (settings.get('SMTP_Protocol') === 'smtp' && settings.get('SMTP_IgnoreTLS')) {
|
||||
process.env.MAIL_URL += '&secure=false&ignoreTLS=true';
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
34
apps/meteor/server/configuration/index.ts
Normal file
34
apps/meteor/server/configuration/index.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { configureAccounts } from './accounts_meld';
|
||||
import { configureCAS } from './cas';
|
||||
import { configureAssets } from './configureAssets';
|
||||
import { configureBoilerplate } from './configureBoilerplate';
|
||||
import { configureCDN } from './configureCDN';
|
||||
import { configureCORS } from './configureCORS';
|
||||
import { configureDirectReply } from './configureDirectReply';
|
||||
import { configureIRC } from './configureIRC';
|
||||
import { configureLogLevel } from './configureLogLevel';
|
||||
import { configurePassport } from './configurePassport';
|
||||
import { configureSMTP } from './configureSMTP';
|
||||
import { configureLDAP } from './ldap';
|
||||
import { configureOAuth } from './oauth';
|
||||
import { configurePushNotifications } from './pushNotification';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureServer(settings: ICachedSettings) {
|
||||
await Promise.all([
|
||||
configureLogLevel(settings),
|
||||
configureAccounts(),
|
||||
configureCAS(settings),
|
||||
configureLDAP(settings),
|
||||
configureOAuth(settings),
|
||||
configureAssets(settings),
|
||||
configureCORS(settings),
|
||||
configureCDN(settings),
|
||||
configurePushNotifications(settings),
|
||||
configureBoilerplate(settings),
|
||||
configureDirectReply(settings),
|
||||
configureSMTP(settings),
|
||||
configureIRC(settings),
|
||||
configurePassport(settings),
|
||||
]);
|
||||
}
|
||||
55
apps/meteor/server/configuration/ldap.ts
Normal file
55
apps/meteor/server/configuration/ldap.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { LDAP } from '@rocket.chat/core-services';
|
||||
import { Accounts } from 'meteor/accounts-base';
|
||||
|
||||
import { callbacks } from '../lib/callbacks';
|
||||
import { warnUnlicensedAuthService } from '../lib/premiumAuthDeprecation';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureLDAP(settings: ICachedSettings): Promise<void> {
|
||||
// Register ldap login handler
|
||||
Accounts.registerLoginHandler('ldap', async (loginRequest: Record<string, any>) => {
|
||||
if (!loginRequest.ldap || !loginRequest.ldapOptions) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
warnUnlicensedAuthService('LDAP', 'ldap-enterprise');
|
||||
|
||||
return LDAP.loginRequest(loginRequest.username, loginRequest.ldapPass);
|
||||
});
|
||||
|
||||
// Prevent password logins by LDAP users when LDAP is enabled
|
||||
let ldapEnabled: boolean;
|
||||
settings.watch('LDAP_Enable', (value) => {
|
||||
if (ldapEnabled === value) {
|
||||
return;
|
||||
}
|
||||
ldapEnabled = value as boolean;
|
||||
|
||||
if (!value) {
|
||||
return callbacks.remove('beforeValidateLogin', 'validateLdapLoginFallback');
|
||||
}
|
||||
|
||||
callbacks.add(
|
||||
'beforeValidateLogin',
|
||||
(login: Record<string, any>) => {
|
||||
if (!login.allowed) {
|
||||
return login;
|
||||
}
|
||||
|
||||
// The fallback setting should only block password logins, so users that have other login services can continue using them
|
||||
if (login.type !== 'password') {
|
||||
return login;
|
||||
}
|
||||
|
||||
// LDAP users can still login locally when login fallback is enabled
|
||||
if (login.user.services?.ldap?.id) {
|
||||
login.allowed = settings.get<boolean>('LDAP_Login_Fallback') ?? false;
|
||||
}
|
||||
|
||||
return login;
|
||||
},
|
||||
callbacks.priority.MEDIUM,
|
||||
'validateLdapLoginFallback',
|
||||
);
|
||||
});
|
||||
}
|
||||
21
apps/meteor/server/configuration/oauth.ts
Normal file
21
apps/meteor/server/configuration/oauth.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
import { initCustomOAuthServices } from '../lib/oauth/initCustomOAuthServices';
|
||||
import { removeOAuthService } from '../lib/oauth/removeOAuthService';
|
||||
import { updateOAuthServices } from '../lib/oauth/updateOAuthServices';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configureOAuth(settings: ICachedSettings): Promise<void> {
|
||||
const _updateOAuthServices = debounce(updateOAuthServices, 2000);
|
||||
settings.watchByRegex(/^Accounts_OAuth_.+/, () => {
|
||||
return _updateOAuthServices();
|
||||
});
|
||||
|
||||
settings.watchByRegex(/^Accounts_OAuth_Custom-[a-z0-9_]+/, (key, value) => {
|
||||
if (!value) {
|
||||
return removeOAuthService(key);
|
||||
}
|
||||
});
|
||||
|
||||
await initCustomOAuthServices();
|
||||
}
|
||||
73
apps/meteor/server/configuration/pushNotification.ts
Normal file
73
apps/meteor/server/configuration/pushNotification.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { getWorkspaceAccessToken } from '../lib/cloud';
|
||||
import { Push } from '../lib/notifications/push';
|
||||
import type { ICachedSettings } from '../settings/CachedSettings';
|
||||
|
||||
export async function configurePushNotifications(settings: ICachedSettings): Promise<void> {
|
||||
settings.watch<boolean>('Push_enable', async (enabled) => {
|
||||
if (!enabled) {
|
||||
await Push.unconfigure();
|
||||
return;
|
||||
}
|
||||
const gateways =
|
||||
settings.get('Push_enable_gateway') && settings.get('Register_Server') && settings.get('Cloud_Service_Agree_PrivacyTerms')
|
||||
? settings.get<string>('Push_gateway').split('\n')
|
||||
: undefined;
|
||||
|
||||
let apn:
|
||||
| {
|
||||
passphrase: string;
|
||||
key: string;
|
||||
cert: string;
|
||||
gateway?: string;
|
||||
}
|
||||
| undefined;
|
||||
let gcm:
|
||||
| {
|
||||
apiKey: string;
|
||||
projectNumber: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
// TODO: this part of the code should be refactored as the deprecated GCM methods are no longer being used and FCM is preferred.
|
||||
if (!gateways) {
|
||||
gcm = {
|
||||
apiKey: 'TO_BE_REFACTORED',
|
||||
projectNumber: 'TO_BE_REFACTORED',
|
||||
};
|
||||
|
||||
apn = {
|
||||
passphrase: settings.get('Push_apn_passphrase'),
|
||||
key: settings.get('Push_apn_key'),
|
||||
cert: settings.get('Push_apn_cert'),
|
||||
};
|
||||
|
||||
if (settings.get('Push_production') !== true) {
|
||||
apn = {
|
||||
passphrase: settings.get('Push_apn_dev_passphrase'),
|
||||
key: settings.get('Push_apn_dev_key'),
|
||||
cert: settings.get('Push_apn_dev_cert'),
|
||||
gateway: 'gateway.sandbox.push.apple.com',
|
||||
};
|
||||
}
|
||||
|
||||
if (!apn.key || apn.key.trim() === '' || !apn.cert || apn.cert.trim() === '') {
|
||||
apn = undefined;
|
||||
}
|
||||
|
||||
if (!gcm.apiKey || gcm.apiKey.trim() === '' || !gcm.projectNumber || gcm.projectNumber.trim() === '') {
|
||||
gcm = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Push.configure({
|
||||
apn,
|
||||
gcm,
|
||||
production: settings.get('Push_production'),
|
||||
gateways,
|
||||
uniqueId: settings.get('uniqueID'),
|
||||
async getAuthorization() {
|
||||
return `Bearer ${await getWorkspaceAccessToken()}`;
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user