diff --git a/.changeset/tall-timers-swim.md b/.changeset/tall-timers-swim.md new file mode 100644 index 0000000000000..a161740545b83 --- /dev/null +++ b/.changeset/tall-timers-swim.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes workspace statistics deployment data not updating on server version changes. diff --git a/.changeset/tiny-candles-hang.md b/.changeset/tiny-candles-hang.md new file mode 100644 index 0000000000000..977ffc3764e9d --- /dev/null +++ b/.changeset/tiny-candles-hang.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Ensures SAML respects blank custom Authn Context diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/AuthorizeRequest.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/AuthorizeRequest.ts index 9971750e3df0a..27d0100c0b77b 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/generators/AuthorizeRequest.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/generators/AuthorizeRequest.ts @@ -10,6 +10,13 @@ import { defaultAuthnContextTemplate, } from '../constants'; +function resolveCustomAuthnContext(serviceProviderOptions: IServiceProviderOptions): string | undefined; +function resolveCustomAuthnContext(serviceProviderOptions: IServiceProviderOptions, defaultValue: string): string; +function resolveCustomAuthnContext(serviceProviderOptions: IServiceProviderOptions, defaultValue?: string): string | undefined { + const value = serviceProviderOptions.customAuthnContext.trim(); + return value.length > 0 ? value : defaultValue; +} + /* An Authorize Request is used to show the Identity Provider login form when the user clicks on the Rocket.Chat SAML login button */ @@ -46,7 +53,7 @@ export class AuthorizeRequest { } private static authnContextTagTemplate(serviceProviderOptions: IServiceProviderOptions): string { - if (!serviceProviderOptions.customAuthnContext) { + if (!resolveCustomAuthnContext(serviceProviderOptions)) { return ''; } @@ -68,7 +75,7 @@ export class AuthorizeRequest { issuer: serviceProviderOptions.issuer, identifierFormat: serviceProviderOptions.identifierFormat || defaultIdentifierFormat, authnContextComparison: serviceProviderOptions.authnContextComparison || 'exact', - authnContext: serviceProviderOptions.customAuthnContext || defaultAuthnContext, + authnContext: resolveCustomAuthnContext(serviceProviderOptions, defaultAuthnContext), }; } } diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts index 02fcdd129b26b..917032c54306e 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts @@ -72,6 +72,9 @@ const getSamlConfigs = function (service: string): SAMLConfiguration { const configureSamlService = function (samlConfigs: Record): IServiceProviderOptions { let privateCert = null; let privateKey = null; + const rawCustomAuthnContext = samlConfigs.customAuthnContext; + const normalizedCustomAuthnContext = typeof rawCustomAuthnContext === 'string' ? rawCustomAuthnContext.trim() : rawCustomAuthnContext; + const customAuthnContext = typeof normalizedCustomAuthnContext === 'string' ? normalizedCustomAuthnContext : defaultAuthnContext; if (samlConfigs.secret.privateKey && samlConfigs.secret.publicCert) { privateKey = samlConfigs.secret.privateKey; @@ -91,7 +94,7 @@ const configureSamlService = function (samlConfigs: Record): IServi privateCert, privateKey, signatureAlgorithm: samlConfigs.secret.algorithm, - customAuthnContext: samlConfigs.customAuthnContext, + customAuthnContext, authnContextComparison: samlConfigs.authnContextComparison, defaultUserRole: samlConfigs.defaultUserRole, allowedClockDrift: parseInt(samlConfigs.allowedClockDrift) || 0, diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index 29f09fef5386f..08711e20bfbe1 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -38,6 +38,7 @@ import { getImporterStatistics } from './getImporterStatistics'; import { getServicesStatistics } from './getServicesStatistics'; import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred'; import { isRunningMs } from '../../../../server/lib/isRunningMs'; +import { SystemLogger } from '../../../../server/lib/logger/system'; import { getControl } from '../../../../server/lib/migrations'; import { getSettingsStatistics } from '../../../../server/lib/statistics/getSettingsStatistics'; import { getMatrixFederationStatistics } from '../../../../server/services/federation/infrastructure/rocket-chat/adapters/Statistics'; @@ -599,4 +600,58 @@ export const statistics = { return rcStatistics; }, + async updateDeploymentData(): Promise { + try { + const lastStatistics = await Statistics.findLast(); + + if (!lastStatistics) { + return; + } + + const { mongoVersion, mongoStorageEngine } = await getMongoInfo(); + + const deploymentInfo: Partial = { + os: { + type: os.type(), + platform: os.platform(), + arch: os.arch(), + release: os.release(), + uptime: os.uptime(), + loadavg: os.loadavg(), + totalmem: os.totalmem(), + freemem: os.freemem(), + cpus: os.cpus(), + }, + process: { + nodeVersion: process.version, + pid: process.pid, + uptime: process.uptime(), + }, + deploy: { + method: process.env.DEPLOY_METHOD || 'tar', + platform: process.env.DEPLOY_PLATFORM || 'selfinstall', + }, + msEnabled: isRunningMs(), + mongoVersion, + mongoStorageEngine: mongoStorageEngine || '', + migration: await getControl(), + }; + + if (Info) { + deploymentInfo.version = Info.version; + } + + const { _id, ...baseStatistics } = lastStatistics; + + const newStatistics: Omit = { + ...baseStatistics, + ...deploymentInfo, + createdAt: new Date(), + }; + + await Statistics.insertOne(newStatistics); + } catch (error) { + SystemLogger.error({ msg: 'Error saving statistics with new deployment data', err: error }); + } + }, }; diff --git a/apps/meteor/ee/server/configuration/saml.ts b/apps/meteor/ee/server/configuration/saml.ts index 34430b4418b90..6ab5f10926233 100644 --- a/apps/meteor/ee/server/configuration/saml.ts +++ b/apps/meteor/ee/server/configuration/saml.ts @@ -30,8 +30,11 @@ await License.onLicense('saml-enterprise', () => { SAMLUtils.events.on('loadConfigs', (service: string, configs: Record): void => { // Include ee settings on the configs object so that they can be copied to the login service too + const rawCustomAuthnContext = settings.get(`${service}_custom_authn_context`); + const customAuthnContext = typeof rawCustomAuthnContext === 'string' ? rawCustomAuthnContext.trim() : undefined; + Object.assign(configs, { - customAuthnContext: settings.get(`${service}_custom_authn_context`), + ...(customAuthnContext !== undefined && { customAuthnContext }), authnContextComparison: settings.get(`${service}_authn_context_comparison`), identifierFormat: settings.get(`${service}_identifier_format`), nameIDPolicyTemplate: settings.get(`${service}_NameId_template`), diff --git a/apps/meteor/server/startup/migrations/xrun.ts b/apps/meteor/server/startup/migrations/xrun.ts index 0344649f99935..fcf6bf8b5137b 100644 --- a/apps/meteor/server/startup/migrations/xrun.ts +++ b/apps/meteor/server/startup/migrations/xrun.ts @@ -3,6 +3,7 @@ import type { UpdateResult } from 'mongodb'; import { upsertPermissions } from '../../../app/authorization/server/functions/upsertPermissions'; import { settings } from '../../../app/settings/server'; +import { statistics } from '../../../app/statistics/server/lib/statistics'; import { migrateDatabase, onServerVersionChange } from '../../lib/migrations'; import { ensureCloudWorkspaceRegistered } from '../cloudRegistration'; @@ -62,5 +63,6 @@ export const performMigrationProcedure = async (): Promise => { await upsertPermissions(); await ensureCloudWorkspaceRegistered(); await moveRetentionSetting(); + await statistics.updateDeploymentData(); }); }; diff --git a/apps/meteor/tests/unit/app/meteor-accounts-saml/server.tests.ts b/apps/meteor/tests/unit/app/meteor-accounts-saml/server.tests.ts index bdd4717d5ab16..effb3bee3a00f 100644 --- a/apps/meteor/tests/unit/app/meteor-accounts-saml/server.tests.ts +++ b/apps/meteor/tests/unit/app/meteor-accounts-saml/server.tests.ts @@ -83,6 +83,28 @@ describe('SAML', () => { expect(authorizeRequest.id).to.be.equal(credentialToken); expect(authorizeRequest.request).to.be.equal('[callback-url] [entry-point] [issuer]'); }); + + it('should omit the authn context block when the customAuthnContext setting is empty', () => { + const credentialToken = '__credentialToken__'; + const customOptions = { + ...serviceProviderOptions, + customAuthnContext: '', + }; + + const authorizeRequest = AuthorizeRequest.generate(customOptions, credentialToken); + expect(authorizeRequest.request).to.not.include(' { + const credentialToken = '__credentialToken__'; + const customOptions = { + ...serviceProviderOptions, + customAuthnContext: ' ', + }; + + const authorizeRequest = AuthorizeRequest.generate(customOptions, credentialToken); + expect(authorizeRequest.request).to.not.include('