diff --git a/apps/meteor/app/lib/server/lib/notifyListener.ts b/apps/meteor/app/lib/server/lib/notifyListener.ts index 776b51997745..93e2b68e01ba 100644 --- a/apps/meteor/app/lib/server/lib/notifyListener.ts +++ b/apps/meteor/app/lib/server/lib/notifyListener.ts @@ -1,6 +1,16 @@ import { api, dbWatchersDisabled } from '@rocket.chat/core-services'; -import type { IPermission, IRocketChatRecord, IRoom, ISetting, IPbxEvent, IRole, IIntegration } from '@rocket.chat/core-typings'; -import { Rooms, Permissions, Settings, PbxEvents, Roles, Integrations } from '@rocket.chat/models'; +import type { + IRocketChatRecord, + IRoom, + ILoginServiceConfiguration, + ISetting, + IRole, + IPermission, + IIntegration, + IPbxEvent, + LoginServiceConfiguration as LoginServiceConfigurationData, +} from '@rocket.chat/core-typings'; +import { Rooms, Permissions, Settings, PbxEvents, Roles, Integrations, LoginServiceConfiguration } from '@rocket.chat/models'; type ClientAction = 'inserted' | 'updated' | 'removed'; @@ -28,6 +38,7 @@ export async function notifyOnRoomChangedById( } const eligibleIds = Array.isArray(ids) ? ids : [ids]; + const items = Rooms.findByIds(eligibleIds); for await (const item of items) { @@ -112,10 +123,11 @@ export async function notifyOnPbxEventChangedById( } const item = await PbxEvents.findOneById(id); - - if (item) { - void api.broadcast('watch.pbxevents', { clientAction, id, data: item }); + if (!item) { + return; } + + void api.broadcast('watch.pbxevents', { clientAction, id, data: item }); } export async function notifyOnRoleChanged(role: T, clientAction: 'removed' | 'changed' = 'changed'): Promise { @@ -142,6 +154,39 @@ export async function notifyOnRoleChangedById( void notifyOnRoleChanged(role, clientAction); } +export async function notifyOnLoginServiceConfigurationChanged( + service: Partial & Pick, + clientAction: ClientAction = 'updated', +): Promise { + if (!dbWatchersDisabled) { + return; + } + + void api.broadcast('watch.loginServiceConfiguration', { + clientAction, + id: service._id, + data: service, + }); +} + +export async function notifyOnLoginServiceConfigurationChangedByService( + service: T['service'], + clientAction: ClientAction = 'updated', +): Promise { + if (!dbWatchersDisabled) { + return; + } + + const item = await LoginServiceConfiguration.findOneByService>(service, { + projection: { secret: 0 }, + }); + if (!item) { + return; + } + + void notifyOnLoginServiceConfigurationChanged(item, clientAction); +} + export async function notifyOnIntegrationChanged(data: T, clientAction: ClientAction = 'updated'): Promise { if (!dbWatchersDisabled) { return; @@ -159,10 +204,11 @@ export async function notifyOnIntegrationChangedById( } const item = await Integrations.findOneById(id); - - if (item) { - void api.broadcast('watch.integrations', { clientAction, id: item._id, data: item }); + if (!item) { + return; } + + void api.broadcast('watch.integrations', { clientAction, id: item._id, data: item }); } export async function notifyOnIntegrationChangedByUserId( 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 8f6791c36302..bb9567260337 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts @@ -3,6 +3,10 @@ import { LoginServiceConfiguration } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { + notifyOnLoginServiceConfigurationChanged, + notifyOnLoginServiceConfigurationChangedByService, +} from '../../../lib/server/lib/notifyListener'; import { settings, settingsRegistry } from '../../../settings/server'; import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; import { SAMLUtils } from './Utils'; @@ -117,9 +121,22 @@ export const loadSamlServiceProviders = async function (): Promise { const samlConfigs = getSamlConfigs(key); SAMLUtils.log(key); await LoginServiceConfiguration.createOrUpdateService(serviceName, samlConfigs); + void notifyOnLoginServiceConfigurationChangedByService(serviceName); return configureSamlService(samlConfigs); } - await LoginServiceConfiguration.removeService(serviceName); + + const service = await LoginServiceConfiguration.findOneByService(serviceName, { projection: { _id: 1 } }); + if (!service?._id) { + return false; + } + + const { deletedCount } = await LoginServiceConfiguration.removeService(service._id); + if (!deletedCount) { + return false; + } + + void notifyOnLoginServiceConfigurationChanged({ _id: service._id }, 'removed'); + return false; }), ) diff --git a/apps/meteor/server/database/watchCollections.ts b/apps/meteor/server/database/watchCollections.ts index 93df616df637..67fa3b1cfc4e 100644 --- a/apps/meteor/server/database/watchCollections.ts +++ b/apps/meteor/server/database/watchCollections.ts @@ -33,7 +33,6 @@ export function getWatchCollections(): string[] { Users.getCollectionName(), LivechatInquiry.getCollectionName(), LivechatDepartmentAgents.getCollectionName(), - LoginServiceConfiguration.getCollectionName(), InstanceStatus.getCollectionName(), IntegrationHistory.getCollectionName(), EmailInbox.getCollectionName(), @@ -50,6 +49,7 @@ export function getWatchCollections(): string[] { collections.push(PbxEvents.getCollectionName()); collections.push(Integrations.getCollectionName()); collections.push(Permissions.getCollectionName()); + collections.push(LoginServiceConfiguration.getCollectionName()); } if (onlyCollections.length > 0) { diff --git a/apps/meteor/server/lib/oauth/updateOAuthServices.ts b/apps/meteor/server/lib/oauth/updateOAuthServices.ts index ed0ae5977d0d..f1df9fbb7aa9 100644 --- a/apps/meteor/server/lib/oauth/updateOAuthServices.ts +++ b/apps/meteor/server/lib/oauth/updateOAuthServices.ts @@ -8,6 +8,10 @@ import type { import { LoginServiceConfiguration } from '@rocket.chat/models'; import { CustomOAuth } from '../../../app/custom-oauth/server/custom_oauth_server'; +import { + notifyOnLoginServiceConfigurationChanged, + notifyOnLoginServiceConfigurationChangedByService, +} from '../../../app/lib/server/lib/notifyListener'; import { settings } from '../../../app/settings/server/cached'; import { logger } from './logger'; @@ -113,8 +117,15 @@ export async function updateOAuthServices(): Promise { } await LoginServiceConfiguration.createOrUpdateService(serviceKey, data); + void notifyOnLoginServiceConfigurationChangedByService(serviceKey); } else { - await LoginServiceConfiguration.removeService(serviceKey); + const service = await LoginServiceConfiguration.findOneByService(serviceName, { projection: { _id: 1 } }); + if (service?._id) { + const { deletedCount } = await LoginServiceConfiguration.removeService(service._id); + if (deletedCount > 0) { + void notifyOnLoginServiceConfigurationChanged({ _id: service._id }, 'removed'); + } + } } } } diff --git a/apps/meteor/server/models/raw/LoginServiceConfiguration.ts b/apps/meteor/server/models/raw/LoginServiceConfiguration.ts index 98b14d2c1947..06d0c5753f91 100644 --- a/apps/meteor/server/models/raw/LoginServiceConfiguration.ts +++ b/apps/meteor/server/models/raw/LoginServiceConfiguration.ts @@ -1,6 +1,6 @@ import type { LoginServiceConfiguration, RocketChatRecordDeleted } from '@rocket.chat/core-typings'; import type { ILoginServiceConfigurationModel } from '@rocket.chat/model-typings'; -import type { Collection, Db, DeleteResult } from 'mongodb'; +import type { Collection, Db, DeleteResult, Document, FindOptions } from 'mongodb'; import { BaseRaw } from './BaseRaw'; @@ -15,7 +15,7 @@ export class LoginServiceConfigurationRaw extends BaseRaw, ): Promise { const service = serviceName.toLowerCase(); @@ -44,9 +44,14 @@ export class LoginServiceConfigurationRaw extends BaseRaw { - const service = serviceName.toLowerCase(); + async removeService(_id: LoginServiceConfiguration['_id']): Promise { + return this.deleteOne({ _id }); + } - return this.deleteOne({ service }); + async findOneByService

( + serviceName: LoginServiceConfiguration['service'], + options?: FindOptions

, + ): Promise

{ + return this.findOne({ service: serviceName.toLowerCase() }, options); } } diff --git a/packages/core-services/src/events/Events.ts b/packages/core-services/src/events/Events.ts index d3491ab001dc..4fbbcf6af28e 100644 --- a/packages/core-services/src/events/Events.ts +++ b/packages/core-services/src/events/Events.ts @@ -50,7 +50,7 @@ type LoginServiceConfigurationEvent = { } | { clientAction: Omit; - data: Partial; + data: Omit, 'secret'> & { secret?: never }; } ); diff --git a/packages/model-typings/src/models/ILoginServiceConfigurationModel.ts b/packages/model-typings/src/models/ILoginServiceConfigurationModel.ts index 5a26607eda06..ce71854b70ab 100644 --- a/packages/model-typings/src/models/ILoginServiceConfigurationModel.ts +++ b/packages/model-typings/src/models/ILoginServiceConfigurationModel.ts @@ -1,10 +1,16 @@ import type { LoginServiceConfiguration } from '@rocket.chat/core-typings'; -import type { DeleteResult } from 'mongodb'; +import type { DeleteResult, Document, FindOptions } from 'mongodb'; import type { IBaseModel } from './IBaseModel'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ILoginServiceConfigurationModel extends IBaseModel { - createOrUpdateService(serviceName: string, serviceData: Partial): Promise; - removeService(serviceName: string): Promise; + createOrUpdateService( + serviceName: LoginServiceConfiguration['service'], + serviceData: Partial, + ): Promise; + removeService(_id: LoginServiceConfiguration['_id']): Promise; + findOneByService

( + serviceName: LoginServiceConfiguration['service'], + options?: FindOptions

, + ): Promise

; }