diff --git a/.changeset/odd-pigs-hang.md b/.changeset/odd-pigs-hang.md new file mode 100644 index 0000000000000..b3db5fdbdafaf --- /dev/null +++ b/.changeset/odd-pigs-hang.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes an issue where iframe external commands sent via `window.postMessage` were not being handled correctly when Rocket.Chat was embedded inside an iframe. diff --git a/apps/meteor/client/startup/index.ts b/apps/meteor/client/startup/index.ts index 689d5cbe33aa0..ad31ce8030c63 100644 --- a/apps/meteor/client/startup/index.ts +++ b/apps/meteor/client/startup/index.ts @@ -3,7 +3,6 @@ import './appRoot'; import './audit'; import './callbacks'; import './deviceManagement'; -import './iframeCommands'; import './incomingMessages'; import './roles'; import './routes'; diff --git a/apps/meteor/client/views/root/AppLayout.tsx b/apps/meteor/client/views/root/AppLayout.tsx index 33b0714ba3d30..6d44b557a2b31 100644 --- a/apps/meteor/client/views/root/AppLayout.tsx +++ b/apps/meteor/client/views/root/AppLayout.tsx @@ -21,6 +21,7 @@ import { useDesktopTitle } from './hooks/useDesktopTitle'; import { useEmojiOne } from './hooks/useEmojiOne'; import { useEscapeKeyStroke } from './hooks/useEscapeKeyStroke'; import { useGoogleTagManager } from './hooks/useGoogleTagManager'; +import { useIframeCommands } from './hooks/useIframeCommands'; import { useIframeLoginListener } from './hooks/useIframeLoginListener'; import { useLivechatEnterprise } from './hooks/useLivechatEnterprise'; import { useLoadMissedMessages } from './hooks/useLoadMissedMessages'; @@ -71,6 +72,7 @@ const AppLayout = () => { useDesktopFavicon(); useDesktopTitle(); useStartupEvent(); + useIframeCommands(); const layout = useSyncExternalStore(appLayout.subscribe, appLayout.getSnapshot); diff --git a/apps/meteor/client/startup/iframeCommands.ts b/apps/meteor/client/views/root/hooks/useIframeCommands.ts similarity index 60% rename from apps/meteor/client/startup/iframeCommands.ts rename to apps/meteor/client/views/root/hooks/useIframeCommands.ts index 500487b816f0a..88154dca9fc63 100644 --- a/apps/meteor/client/startup/iframeCommands.ts +++ b/apps/meteor/client/views/root/hooks/useIframeCommands.ts @@ -1,17 +1,18 @@ import type { UserStatus, IUser } from '@rocket.chat/core-typings'; import { escapeRegExp } from '@rocket.chat/string-helpers'; import { afterLogoutCleanUpCallback } from '@rocket.chat/ui-client'; -import type { LocationPathname } from '@rocket.chat/ui-contexts'; +import { type LocationPathname, useSetting } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; +import { useEffect } from 'react'; -import { AccountBox } from '../../app/ui-utils/client/lib/AccountBox'; -import { sdk } from '../../app/utils/client/lib/SDKClient'; -import { capitalize, ltrim, rtrim } from '../../lib/utils/stringUtils'; -import { baseURI } from '../lib/baseURI'; -import { loginServices } from '../lib/loginServices'; -import { settings } from '../lib/settings'; -import { getUser } from '../lib/user'; -import { router } from '../providers/RouterProvider'; +import { AccountBox } from '../../../../app/ui-utils/client/lib/AccountBox'; +import { sdk } from '../../../../app/utils/client/lib/SDKClient'; +import { capitalize, ltrim, rtrim } from '../../../../lib/utils/stringUtils'; +import { baseURI } from '../../../lib/baseURI'; +import { loginServices } from '../../../lib/loginServices'; +import { settings } from '../../../lib/settings'; +import { getUser } from '../../../lib/user'; +import { router } from '../../../providers/RouterProvider'; const commands = { 'go'(data: { path: string }) { @@ -93,27 +94,37 @@ type CommandMessage[0]; -window.addEventListener('message', (e: MessageEvent) => { - if (!settings.peek('Iframe_Integration_receive_enable')) { - return; - } +export const useIframeCommands = () => { + const iframeReceiveEnabled = useSetting('Iframe_Integration_receive_enable'); + const iframeReceiveOrigin = useSetting('Iframe_Integration_receive_origin', '*'); - if (typeof e.data !== 'object' || typeof e.data.externalCommand !== 'string') { - return; - } + useEffect(() => { + if (!iframeReceiveEnabled) { + return; + } + const messageListener = (event: MessageEvent) => { + if (typeof event.data !== 'object' || typeof event.data.externalCommand !== 'string') { + return; + } - const origins = settings.peek('Iframe_Integration_receive_origin') ?? '*'; + if (iframeReceiveOrigin !== '*' && iframeReceiveOrigin.split(',').indexOf(event.origin) === -1) { + console.error('Origin not allowed', event.origin); + return; + } - if (origins !== '*' && origins.split(',').indexOf(e.origin) === -1) { - console.error('Origin not allowed', e.origin); - return; - } + if (!(event.data.externalCommand in commands)) { + console.error('Command not allowed', event.data.externalCommand); + return; + } - if (!(e.data.externalCommand in commands)) { - console.error('Command not allowed', e.data.externalCommand); - return; - } + const command: (data: MessageEvent['data'], event: MessageEvent) => void = commands[event.data.externalCommand]; + command(event.data, event); + }; + + window.addEventListener('message', messageListener); - const command: (data: any, event: MessageEvent) => void = commands[e.data.externalCommand]; - command(e.data, e); -}); + return () => { + window.removeEventListener('message', messageListener); + }; + }, [iframeReceiveEnabled, iframeReceiveOrigin]); +};