diff --git a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/ABAC/RoomInfoABACSection.spec.tsx b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/ABAC/RoomInfoABACSection.spec.tsx
new file mode 100644
index 0000000000000..7d3f745f655c7
--- /dev/null
+++ b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/ABAC/RoomInfoABACSection.spec.tsx
@@ -0,0 +1,79 @@
+import type { IRoom } from '@rocket.chat/core-typings';
+import { mockAppRoot } from '@rocket.chat/mock-providers';
+import { render, screen } from '@testing-library/react';
+import { axe } from 'jest-axe';
+
+import RoomInfoABACSection from './RoomInfoABACSection';
+import { createFakeRoom } from '../../../../../../../tests/mocks/data';
+
+type RoomWithABAC = IRoom & {
+ abacAttributes?: {
+ key: string;
+ values: string[];
+ }[];
+};
+
+describe('RoomInfoABACSection', () => {
+ const createRoomWithABAC = (attributes: { key: string; values: string[] }[]): RoomWithABAC => {
+ const room = createFakeRoom();
+ return {
+ ...room,
+ abacAttributes: attributes,
+ } as RoomWithABAC;
+ };
+
+ const appRootWithABACEnabled = mockAppRoot().withSetting('ABAC_Enabled', true).withSetting('ABAC_ShowAttributesInRooms', true).build();
+
+ describe('Conditional rendering', () => {
+ it('should return null when ABAC_Enabled is false', () => {
+ const room = createRoomWithABAC([{ key: 'Test', values: ['Value1'] }]);
+ const appRoot = mockAppRoot().withSetting('ABAC_Enabled', false).withSetting('ABAC_ShowAttributesInRooms', true).build();
+
+ render(, { wrapper: appRoot });
+ expect(screen.queryByText('ABAC_Managed')).not.toBeInTheDocument();
+ });
+
+ it('should return null when ABAC_ShowAttributesInRooms is false', () => {
+ const room = createRoomWithABAC([{ key: 'Test', values: ['Value1'] }]);
+ const appRoot = mockAppRoot().withSetting('ABAC_Enabled', true).withSetting('ABAC_ShowAttributesInRooms', false).build();
+
+ render(, { wrapper: appRoot });
+ expect(screen.queryByText('ABAC_Managed')).not.toBeInTheDocument();
+ });
+
+ it('should return null when abacAttributes is empty', () => {
+ const room = createRoomWithABAC([]);
+ render(, { wrapper: appRootWithABACEnabled });
+ expect(screen.queryByText('ABAC_Managed')).not.toBeInTheDocument();
+ });
+
+ it('should render when all conditions are met', () => {
+ const room = createRoomWithABAC([{ key: 'Test', values: ['Value1'] }]);
+ render(, { wrapper: appRootWithABACEnabled });
+ expect(screen.getByText('ABAC_Managed')).toBeInTheDocument();
+ });
+ });
+
+ describe('Accessibility', () => {
+ it('should have no accessibility violations', async () => {
+ const room = createRoomWithABAC([
+ { key: 'Chat-sensitivity', values: ['Classified', 'Top-Secret'] },
+ { key: 'Country', values: ['US-only'] },
+ ]);
+ const { container } = render(, { wrapper: appRootWithABACEnabled });
+
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+ });
+ });
+ describe('Snapshot', () => {
+ it('should match the snapshot', () => {
+ const room = createRoomWithABAC([
+ { key: 'Chat-sensitivity', values: ['Classified', 'Top-Secret'] },
+ { key: 'Country', values: ['US-only'] },
+ ]);
+ const { baseElement } = render(, { wrapper: appRootWithABACEnabled });
+ expect(baseElement).toMatchSnapshot();
+ });
+ });
+});
diff --git a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/ABAC/RoomInfoABACSection.tsx b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/ABAC/RoomInfoABACSection.tsx
new file mode 100644
index 0000000000000..8c11bdde68f06
--- /dev/null
+++ b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/ABAC/RoomInfoABACSection.tsx
@@ -0,0 +1,66 @@
+import type { IRoom } from '@rocket.chat/core-typings';
+import { Box, Divider, Tag } from '@rocket.chat/fuselage';
+import { useSetting } from '@rocket.chat/ui-contexts';
+import { useTranslation } from 'react-i18next';
+
+import { InfoPanelField, InfoPanelLabel } from '../../../../../../components/InfoPanel';
+import { RoomIcon } from '../../../../../../components/RoomIcon';
+
+// TODO: Remove type union when ABAC is implemented
+type RoomInfoABACSectionProps = {
+ room: IRoom & {
+ abacAttributes?: {
+ key: string;
+ values: string[];
+ }[];
+ };
+};
+
+const RoomInfoABACSection = ({ room }: RoomInfoABACSectionProps) => {
+ const { t } = useTranslation();
+
+ const abacEnabled = useSetting('ABAC_Enabled');
+ const showAttributesInRoom = useSetting('ABAC_ShowAttributesInRooms');
+
+ if (!abacEnabled || !showAttributesInRoom || !room.abacAttributes?.length) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+ {t('ABAC_Managed')}
+
+
+
+
+ {t('ABAC_Managed_description')}
+
+ {t('ABAC_Room_Attributes')}
+
+ {room.abacAttributes.map((attribute) => (
+
+
+ {attribute.key}
+
+
+ {attribute.values.map((value) => (
+
+ {value}
+
+ ))}
+
+
+ ))}
+
+
+ >
+ );
+};
+
+export default RoomInfoABACSection;
diff --git a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/ABAC/__snapshots__/RoomInfoABACSection.spec.tsx.snap b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/ABAC/__snapshots__/RoomInfoABACSection.spec.tsx.snap
new file mode 100644
index 0000000000000..43ec592f3181f
--- /dev/null
+++ b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/ABAC/__snapshots__/RoomInfoABACSection.spec.tsx.snap
@@ -0,0 +1,123 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`RoomInfoABACSection Snapshot should match the snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+ ABAC_Managed
+
+
+
+
+
+ ABAC_Managed_description
+
+ ABAC_Room_Attributes
+
+
+ -
+
+ Chat-sensitivity
+
+
+ -
+
+
+ Classified
+
+
+
+ -
+
+
+ Top-Secret
+
+
+
+
+
+ -
+
+ Country
+
+
+
+
+
+
+
+`;
diff --git a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.stories.tsx b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.stories.tsx
index 3feb078e0c68e..03a8e0a71447f 100644
--- a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.stories.tsx
+++ b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.stories.tsx
@@ -1,7 +1,9 @@
import type { RoomType } from '@rocket.chat/core-typings';
+import { mockAppRoot } from '@rocket.chat/mock-providers';
import type { Meta, StoryFn } from '@storybook/react';
import RoomInfo from './RoomInfo';
+import FakeRoomProvider from '../../../../../../tests/mocks/client/FakeRoomProvider';
import { Contextualbar } from '../../../../../components/Contextualbar';
export default {
@@ -10,7 +12,13 @@ export default {
layout: 'fullscreen',
actions: { argTypesRegex: '^on[A-Z].*' },
},
- decorators: [(fn) => {fn()}],
+ decorators: [
+ (fn) => (
+
+ {fn()}
+
+ ),
+ ],
args: {
icon: 'lock',
},
@@ -61,3 +69,25 @@ Broadcast.args = {
broadcast: true,
},
};
+
+export const ABAC = Template.bind({});
+ABAC.decorators = [
+ mockAppRoot().withSetting('ABAC_Enabled', true).withSetting('ABAC_ShowAttributesInRooms', true).buildStoryDecorator(),
+ (fn) => (
+
+ {fn()}
+
+ ),
+];
+ABAC.args = {
+ ...Default.args,
+ room: {
+ ...roomArgs,
+ // @ts-expect-error - abacAttributes is not yet implemented in Rooms properties
+ abacAttributes: [
+ { name: 'Chat-sensitivity', values: ['Classified', 'Top-Secret'] },
+ { name: 'Country', values: ['US-only'] },
+ { name: 'Project', values: ['Ruminator-2000'] },
+ ],
+ },
+};
diff --git a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx
index ffb0ef73ebdb5..23b2c1eaf2b66 100644
--- a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx
+++ b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx
@@ -29,6 +29,7 @@ import MarkdownText from '../../../../../components/MarkdownText';
import { useRetentionPolicy } from '../../../hooks/useRetentionPolicy';
import { useRoomActions } from '../hooks/useRoomActions';
import { useSplitRoomActions } from '../hooks/useSplitRoomActions';
+import RoomInfoABACSection from './ABAC/RoomInfoABACSection';
type RoomInfoProps = {
room: IRoom;
@@ -128,6 +129,7 @@ const RoomInfo = ({ room, icon, onClickBack, onClickClose, onClickEnterRoom, onC
)}
{retentionPolicy?.isActive && }
+
diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json
index 7991ab6c17650..640ef7f49df6f 100644
--- a/packages/i18n/src/locales/en.i18n.json
+++ b/packages/i18n/src/locales/en.i18n.json
@@ -77,6 +77,9 @@
"A_new_owner_will_be_assigned_automatically_to_those__count__rooms__rooms__": "A new owner will be assigned automatically to those {{count}} rooms:
{{rooms}}.",
"A_secure_and_highly_private_self-managed_solution_for_conference_calls": "A secure and highly private self-managed solution for conference calls.",
"A_workspace_admin_needs_to_install_and_configure_a_conference_call_app": "A workspace admin needs to install and configure a conference call app.",
+ "ABAC_Managed": "ABAC Managed",
+ "ABAC_Managed_description": "Only compliant users have access to attribute-based access controlled rooms. Attributes determine room access.",
+ "ABAC_Room_Attributes": "Room Attributes",
"Accept": "Accept",
"Accept_Call": "Accept Call",
"Accept_incoming_livechat_requests_even_if_there_are_no_online_agents": "Accept incoming omnichannel requests even if there are no online agents",