Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
8a2a37e
abac license module
KevLehman Sep 29, 2025
518e5b9
abac global enable setting
KevLehman Sep 29, 2025
eab8a9b
abac package and endpoints
KevLehman Sep 29, 2025
dc3741f
abac model
KevLehman Sep 29, 2025
7105d52
permission
KevLehman Sep 29, 2025
02f6aca
lint
KevLehman Sep 29, 2025
9e18534
abac flag
KevLehman Sep 30, 2025
b3fab5f
model
KevLehman Sep 30, 2025
852afdf
abac service
KevLehman Sep 30, 2025
40225c2
translations
KevLehman Sep 30, 2025
ff00949
tests
KevLehman Sep 30, 2025
8e84b6c
attributes on room
KevLehman Sep 30, 2025
277a2e4
create an abac attribute
KevLehman Sep 30, 2025
c6af020
Tests for other function
KevLehman Oct 1, 2025
16d01e6
list attirbutes endpoint
KevLehman Oct 1, 2025
5050a1b
update abac attributes
KevLehman Oct 1, 2025
d784d93
remove abac toggle endpoint
KevLehman Oct 1, 2025
25ae969
update abac attribute or values
KevLehman Oct 1, 2025
6f35def
delete
KevLehman Oct 1, 2025
c472c4d
move check to model
KevLehman Oct 1, 2025
eb6c45d
check setting
KevLehman Oct 1, 2025
754a7ac
delete
KevLehman Oct 1, 2025
114e6a0
attribute by id
KevLehman Oct 1, 2025
6eaf317
check if attribute is in use by key
KevLehman Oct 2, 2025
754bed2
abac set attributes
KevLehman Oct 2, 2025
60070df
remove comments
KevLehman Oct 2, 2025
2b280f9
update by key
KevLehman Oct 2, 2025
73f4707
tests
KevLehman Oct 2, 2025
bf29c61
remove abac attr
KevLehman Oct 3, 2025
5b4f686
endpoints
KevLehman Oct 3, 2025
b919fa7
minor changes to index
KevLehman Oct 3, 2025
1aa331b
smol issues with request validators
KevLehman Oct 3, 2025
2577b47
schemas again
KevLehman Oct 3, 2025
114742e
endpoints
KevLehman Oct 6, 2025
cc70695
void insted of error
KevLehman Oct 6, 2025
766cbd7
fix index usage
KevLehman Oct 6, 2025
777f6c9
tests
KevLehman Oct 6, 2025
27ad8b2
delete all attributes from room
KevLehman Oct 6, 2025
25eaf33
ts
KevLehman Oct 6, 2025
fe03e23
test'
KevLehman Oct 6, 2025
ef11083
api
KevLehman Oct 7, 2025
76b52c4
ts
KevLehman Oct 7, 2025
9d1dd36
test
KevLehman Oct 7, 2025
2d60839
test
KevLehman Oct 7, 2025
21fb934
svc
KevLehman Oct 7, 2025
5121bd9
test
KevLehman Oct 7, 2025
152aa27
?
KevLehman Oct 7, 2025
5dac393
I'm dum dum
KevLehman Oct 8, 2025
0cd92a9
rollback this change
KevLehman Oct 8, 2025
f272b44
fix: Pagination not working on `abac/attributes` endpoint (#37189)
KevLehman Oct 10, 2025
31e87c3
fix: `abac/attributes` endpoint filtering (#37225)
KevLehman Oct 15, 2025
c4dea9a
chore: Remove unused expect-error directives (#37234)
KevLehman Oct 15, 2025
14d059b
chore: Replace findOne with findOneById and use removeById (#37240)
KevLehman Oct 15, 2025
26e7ad7
fix: FindOneById used where key was provided
KevLehman Oct 15, 2025
e6a602b
fix: Unit tests
KevLehman Oct 15, 2025
acf89ea
restore
KevLehman Oct 17, 2025
b6769c7
feat: Remove users from room when new attributes are added to the roo…
KevLehman Oct 20, 2025
4591ab9
chore: Prevent default room & team channels from becoming abac manage…
KevLehman Oct 22, 2025
d92dea1
feat: Prevent LDAP sync from adding users to abac rooms/teams (#37299)
KevLehman Oct 24, 2025
ac010a6
chore: Unset attributes instead of leaving empty array (#37301)
KevLehman Oct 24, 2025
f10bafe
chore: Re-route endpoints from `abac/room/*` to `abac/rooms/*`
tassoevan Oct 25, 2025
070b66c
feat: Prevent ABAC managed rooms becoming public while ABAC is active…
KevLehman Oct 27, 2025
c4ce543
fix: tests using room instead of rooms
KevLehman Oct 28, 2025
73289b1
feat: Prevent invite links from being generated on abac rooms (#37325)
KevLehman Oct 29, 2025
ca6d156
feat: Register abac service inside authz (#37333)
KevLehman Oct 30, 2025
ea53216
feat: Update add/join methods to use abac rules (#37339)
KevLehman Nov 4, 2025
f2d0032
feat: add ABAC admin settings (#37139)
MartinSchoeler Nov 6, 2025
e5be967
fix: GET/DELETE abac endpoints not allowing actions with setting disa…
KevLehman Nov 10, 2025
e49e9a8
feat: Access Checks for ABAC managed rooms (#37423)
KevLehman Nov 11, 2025
16464fa
feat: New setting for showing ABAC attributes in rooms (#37465)
KevLehman Nov 11, 2025
c290776
fix type
KevLehman Nov 12, 2025
76a5d7d
test: ABAC tests unit (#37495)
KevLehman Nov 12, 2025
fc2bef3
feat: ABAC Attributes LDAP integration (#37379)
KevLehman Nov 12, 2025
7fdf37b
feat: ABAC Rooms attributes tab (#37408)
MartinSchoeler Nov 17, 2025
98660a4
feat: New endpoint for showing ABAC managed rooms (#37536)
KevLehman Nov 18, 2025
a1b3382
feat: Force LDAP sync for specific users via endpoint (#37542)
KevLehman Nov 19, 2025
de02c9f
feat: Remove users from rooms when subject attributes no longer match…
KevLehman Nov 19, 2025
b2d25d2
chore: Run ABAC LDAP sync on license/setting changes (#37562)
KevLehman Nov 20, 2025
5cb1f48
fix: Add `abacAttributes` to adminRooms projection (#37576)
KevLehman Nov 21, 2025
b34e62d
chore: fix types
KevLehman Nov 24, 2025
eb1f71f
fix: useHasLicenseModule return change (#37596)
MartinSchoeler Nov 24, 2025
080feb5
chore: ABAC statistics (#37606)
KevLehman Nov 26, 2025
8e82114
feat: Audit ABAC actions (#37565)
KevLehman Nov 28, 2025
1ca9399
fix: Actor type
KevLehman Nov 28, 2025
17fadf4
test: ABAC + LDAP (#37615)
KevLehman Dec 1, 2025
c3c14e6
chore: Add name prop to object's audit (#37655)
KevLehman Dec 1, 2025
5c16351
test: Fix abac tests (#37658)
KevLehman Dec 1, 2025
56583bb
move helpers to helper file
KevLehman Dec 2, 2025
789c1b2
chore: Action performed audit log to happen after action
KevLehman Dec 2, 2025
96a16e4
fix: broken contextualbar export (#37685)
MartinSchoeler Dec 3, 2025
e23eb0e
feat: ABAC Logs Tab (#37633)
MartinSchoeler Dec 4, 2025
4f72d65
feat: admin ABAC rooms tab (#37557)
MartinSchoeler Dec 4, 2025
ed46fda
chore: update link values for ABAC (#37525)
MartinSchoeler Dec 5, 2025
8559a73
chore: ABAC QoL (#37676)
KevLehman Dec 5, 2025
a647992
chore: Override model index
KevLehman Dec 8, 2025
56d6d75
fix: properly filter abacAttributes from user enpoint return
MartinSchoeler Dec 8, 2025
58b5fcd
test: snapshot fix
MartinSchoeler Dec 8, 2025
588db8d
chore: Reorganize ABAC UI folders and files (#37700)
MartinSchoeler Dec 9, 2025
8140430
fix: Missing abacAttributes from login call
KevLehman Dec 9, 2025
b811628
fix: Prevent invite when license/setting is disabled and room is abac…
KevLehman Dec 10, 2025
d830de3
fix: Changes because of conflict resolution because of a refactor
KevLehman Dec 10, 2025
b90a407
refactor: More polish (#37736)
KevLehman Dec 10, 2025
eae2823
regression(ABAC): Display correct values on user info (#37748)
MartinSchoeler Dec 10, 2025
7aded97
regression(ABAC): Wrong default date range in ABAC Logs page (#37749)
MartinSchoeler Dec 10, 2025
526ba34
test: Restructure subject attr tests
KevLehman Dec 10, 2025
77c2b3b
fix: Coderabbit review (#37774)
KevLehman Dec 11, 2025
64ab36f
fix: Cannot add more than 10 attribute values when creating/udpating …
KevLehman Dec 11, 2025
565c2bf
regression(ABAC): missing header icon (#37779)
MartinSchoeler Dec 11, 2025
0d840ee
fix: Remove ui limitation of saving 10 attribute values
KevLehman Dec 11, 2025
c816655
fix: Prevent submit when form is just opened and has no changes (#37781)
KevLehman Dec 12, 2025
5fc2deb
regression(ABAC): Missing settings from abac -> Settings tab (#37766)
MartinSchoeler Dec 12, 2025
d2693a1
regression: ABAC table & form fixes (#37782)
MartinSchoeler Dec 15, 2025
c9c6aa3
chore: Coderabbit review (#37814)
KevLehman Dec 15, 2025
202176b
chore: use correct imports
MartinSchoeler Dec 15, 2025
32baee0
regression: truncate logs page cells
MartinSchoeler Dec 15, 2025
953f56a
regression: missing link on callout
MartinSchoeler Dec 15, 2025
1d36359
fix: Prevent audit messages from reading abac rooms (#37820)
KevLehman Dec 16, 2025
976a0e6
fix: Add audit log for subject attribute changes in AbacService
KevLehman Dec 16, 2025
ed96c2a
fix: RoomForm returning only private rooms and not teams (#37836)
KevLehman Dec 16, 2025
3cd5393
chore(ABAC): refactor room form autocompletes (#37817)
MartinSchoeler Dec 16, 2025
21e92d4
feat(ABAC): add new column to ABAC logs page (#37837)
MartinSchoeler Dec 17, 2025
1479924
regression(ABAC): Settings not being saved in ABAC screen (#37838)
MartinSchoeler Dec 17, 2025
bd28882
fix: Wrong invite error message (#37851)
KevLehman Dec 17, 2025
94e3da0
fix: Add `name` to `object` being audited on access grant (#37856)
KevLehman Dec 17, 2025
0f3d776
chore: merge fixes
MartinSchoeler Dec 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/ninety-dodos-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@rocket.chat/authorization-service': minor
'@rocket.chat/core-services': minor
'@rocket.chat/message-types': minor
'@rocket.chat/model-typings': minor
'@rocket.chat/core-typings': minor
'@rocket.chat/apps-engine': minor
'@rocket.chat/abac': minor
'@rocket.chat/models': minor
'@rocket.chat/i18n': minor
'@rocket.chat/jwt': minor
'@rocket.chat/meteor': minor
---

Adds Attribute Based Access Control (ABAC) for private channels & private teams.
142 changes: 107 additions & 35 deletions apps/meteor/app/api/server/v1/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import { Meteor } from 'meteor/meteor';

import { isTruthy } from '../../../../lib/isTruthy';
import { adminFields } from '../../../../lib/rooms/adminFields';
import { omit } from '../../../../lib/utils/omit';
import * as dataExport from '../../../../server/lib/dataExport';
import { eraseRoom } from '../../../../server/lib/eraseRoom';
Expand Down Expand Up @@ -1032,49 +1033,120 @@ const isRoomGetRolesPropsSchema = {
additionalProperties: false,
required: ['rid'],
};
export const roomEndpoints = API.v1.get(
'rooms.roles',
{
authRequired: true,
query: ajv.compile<{
rid: string;
}>(isRoomGetRolesPropsSchema),
response: {
200: ajv.compile<{
roles: RoomRoles[];
}>({
type: 'object',
properties: {
roles: {
type: 'array',
items: {
type: 'object',
properties: {
rid: { type: 'string' },
u: {
type: 'object',
properties: { _id: { type: 'string' }, username: { type: 'string' } },
required: ['_id', 'username'],
export const roomEndpoints = API.v1
.get(
'rooms.roles',
{
authRequired: true,
query: ajv.compile<{
rid: string;
}>(isRoomGetRolesPropsSchema),
response: {
200: ajv.compile<{
roles: RoomRoles[];
}>({
type: 'object',
properties: {
roles: {
type: 'array',
items: {
type: 'object',
properties: {
rid: { type: 'string' },
u: {
type: 'object',
properties: { _id: { type: 'string' }, username: { type: 'string' } },
required: ['_id', 'username'],
},
roles: { type: 'array', items: { type: 'string' } },
},
roles: { type: 'array', items: { type: 'string' } },
required: ['rid', 'u', 'roles'],
},
required: ['rid', 'u', 'roles'],
},
},
required: ['roles'],
}),
},
},
async function () {
const { rid } = this.queryParams;
const roles = await executeGetRoomRoles(rid, this.userId);

return API.v1.success({
roles,
});
},
)
.get(
'rooms.adminRooms.privateRooms',
{
authRequired: true,
permissionsRequired: ['view-room-administration'],
query: ajv.compile<{
filter?: string;
offset?: number;
count?: number;
sort?: string;
}>({
type: 'object',
properties: {
filter: { type: 'string' },
offset: { type: 'number' },
count: { type: 'number' },
sort: { type: 'string' },
},
required: ['roles'],
additionalProperties: true,
}),
response: {
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateUnauthorizedErrorResponse,
200: ajv.compile<{
rooms: IRoom[];
count: number;
offset: number;
total: number;
}>({
type: 'object',
properties: {
rooms: {
type: 'array',
items: { type: 'object' },
},
count: { type: 'number' },
offset: { type: 'number' },
total: { type: 'number' },
success: { type: 'boolean', enum: [true] },
},
required: ['rooms', 'count', 'offset', 'total', 'success'],
additionalProperties: false,
}),
},
},
},
async function () {
const { rid } = this.queryParams;
const roles = await executeGetRoomRoles(rid, this.userId);
async function action() {
const { offset, count } = await getPaginationItems(this.queryParams);
const { sort } = await this.parseJsonQuery();
const { filter } = this.queryParams;

return API.v1.success({
roles,
});
},
);
const name = (filter || '').trim();

const { cursor, totalCount } = Rooms.findPrivateRoomsAndTeamsPaginated(name, {
skip: offset,
limit: count,
sort: sort || { default: -1, name: 1 },
projection: adminFields,
});

const [rooms, total] = await Promise.all([cursor.toArray(), totalCount]);

return API.v1.success({
rooms,
count: rooms.length,
offset,
total,
});
},
);

const roomInviteEndpoints = API.v1.post(
'rooms.invite',
Expand Down
8 changes: 8 additions & 0 deletions apps/meteor/app/api/server/v1/teams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { eraseRoom } from '../../../../server/lib/eraseRoom';
import { canAccessRoomAsync } from '../../../authorization/server';
import { hasPermissionAsync, hasAtLeastOnePermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { removeUserFromRoom } from '../../../lib/server/functions/removeUserFromRoom';
import { settings } from '../../../settings/server';
import { API } from '../api';
import { getPaginationItems } from '../helpers/getPaginationItems';
import { eraseTeam } from '../lib/eraseTeam';
Expand Down Expand Up @@ -235,6 +236,13 @@ API.v1.addRoute(
}
const canUpdateAny = !!(await hasPermissionAsync(this.userId, 'view-all-team-channels', team.roomId));

if (settings.get('ABAC_Enabled') && isDefault) {
const room = await Rooms.findOneByIdAndType(roomId, 'p', { projection: { abacAttributes: 1 } });
if (room?.abacAttributes?.length) {
return API.v1.failure('error-room-is-abac-managed');
}
}

const room = await Team.updateRoom(this.userId, roomId, isDefault, canUpdateAny);

return API.v1.success({ room });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Team } from '@rocket.chat/core-services';
import type { IRoom, IRoomWithRetentionPolicy, IUser, MessageTypesValues } from '@rocket.chat/core-typings';
import type { IRoom, IRoomWithRetentionPolicy, IUser, MessageTypesValues, ITeam } from '@rocket.chat/core-typings';
import { TEAM_TYPE } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Rooms, Users } from '@rocket.chat/models';
Expand All @@ -11,6 +11,7 @@ import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { setRoomAvatar } from '../../../lib/server/functions/setRoomAvatar';
import { notifyOnRoomChangedById } from '../../../lib/server/lib/notifyListener';
import { settings } from '../../../settings/server';
import { saveReactWhenReadOnly } from '../functions/saveReactWhenReadOnly';
import { saveRoomAnnouncement } from '../functions/saveRoomAnnouncement';
import { saveRoomCustomFields } from '../functions/saveRoomCustomFields';
Expand Down Expand Up @@ -61,14 +62,33 @@ type RoomSettingsValidators = {
const hasRetentionPolicy = (room: IRoom & { retention?: any }): room is IRoomWithRetentionPolicy =>
'retention' in room && room.retention !== undefined;

const isAbacManagedRoom = (room: IRoom): boolean => {
return room.t === 'p' && settings.get<boolean>('ABAC_Enabled') && Array.isArray(room?.abacAttributes) && room.abacAttributes.length > 0;
};

const isAbacManagedTeam = (team: Partial<ITeam> | null, teamRoom: IRoom): boolean => {
return (
team?.type === TEAM_TYPE.PRIVATE &&
settings.get<boolean>('ABAC_Enabled') &&
Array.isArray(teamRoom?.abacAttributes) &&
teamRoom.abacAttributes.length > 0
);
};

const validators: RoomSettingsValidators = {
async default({ userId }) {
async default({ userId, room, value }) {
if (!(await hasPermissionAsync(userId, 'view-room-administration'))) {
throw new Meteor.Error('error-action-not-allowed', 'Viewing room administration is not allowed', {
method: 'saveRoomSettings',
action: 'Viewing_room_administration',
});
}
if (isAbacManagedRoom(room) && value) {
throw new Meteor.Error('error-action-not-allowed', 'Setting an ABAC managed room as default is not allowed', {
method: 'saveRoomSettings',
action: 'Viewing_room_administration',
});
}
},
async featured({ userId }) {
if (!(await hasPermissionAsync(userId, 'view-room-administration'))) {
Expand Down Expand Up @@ -98,6 +118,13 @@ const validators: RoomSettingsValidators = {
});
}

if (isAbacManagedRoom(room) && value !== 'p') {
throw new Meteor.Error('error-action-not-allowed', 'Changing an ABAC managed private room to public is not allowed', {
method: 'saveRoomSettings',
action: 'Change_Room_Type',
});
}

if (!room.teamId) {
return;
}
Expand All @@ -116,6 +143,13 @@ const validators: RoomSettingsValidators = {
action: 'Change_Room_Type',
});
}

if (isAbacManagedTeam(team, room) && value !== 'p') {
throw new Meteor.Error('error-action-not-allowed', 'Changing an ABAC managed private team room to public is not allowed', {
method: 'saveRoomSettings',
action: 'Change_Room_Type',
});
}
},
async encrypted({ userId, value, room, rid }) {
if (value !== room.encrypted) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ export const findOrCreateInvite = async (userId: string, invite: Pick<IInvite, '
});
}

if (settings.get('ABAC_Enabled') && room?.abacAttributes?.length) {
throw new Meteor.Error('error-invalid-room', 'Room is ABAC managed', {
method: 'findOrCreateInvite',
field: 'rid',
});
}

if (!(await roomCoordinator.getRoomDirectives(room.t).allowMemberAction(room, RoomMemberActions.INVITE, userId))) {
throw new Meteor.Error('error-room-type-not-allowed', 'Cannot create invite links for this room type', {
method: 'findOrCreateInvite',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Invites, Rooms } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';

import { settings } from '../../../settings/server';

export const validateInviteToken = async (token: string) => {
if (!token || typeof token !== 'string') {
throw new Meteor.Error('error-invalid-token', 'The invite token is invalid.', {
Expand All @@ -25,6 +27,13 @@ export const validateInviteToken = async (token: string) => {
});
}

if (settings.get('ABAC_Enabled') && room?.abacAttributes?.length) {
throw new Meteor.Error('error-invalid-room', 'Room is ABAC managed', {
method: 'validateInviteToken',
field: 'rid',
});
}

if (inviteData.expires && new Date(inviteData.expires).getTime() <= Date.now()) {
throw new Meteor.Error('error-invite-expired', 'The invite token has expired.', {
method: 'validateInviteToken',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Subscriptions } from '@rocket.chat/models';
import { getDefaultChannels } from './getDefaultChannels';
import { callbacks } from '../../../../server/lib/callbacks';
import { getSubscriptionAutotranslateDefaultConfig } from '../../../../server/lib/getSubscriptionAutotranslateDefaultConfig';
import { settings } from '../../../settings/server';
import { getDefaultSubscriptionPref } from '../../../utils/lib/getDefaultSubscriptionPref';
import { notifyOnSubscriptionChangedById } from '../lib/notifyListener';

Expand All @@ -13,6 +14,10 @@ export const addUserToDefaultChannels = async function (user: IUser, silenced?:
const defaultRooms = await getDefaultChannels();

for await (const room of defaultRooms) {
if (settings.get('ABAC_Enabled') && room?.abacAttributes?.length) {
continue;
}

if (!(await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { projection: { _id: 1 } }))) {
const autoTranslateConfig = getSubscriptionAutotranslateDefaultConfig(user);

Expand Down
6 changes: 5 additions & 1 deletion apps/meteor/app/lib/server/functions/addUserToRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { callbacks } from '../../../../server/lib/callbacks';
import { beforeAddUserToRoom } from '../../../../server/lib/callbacks/beforeAddUserToRoom';
import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator';
import { settings } from '../../../settings/server';
import { beforeAddUserToRoom as beforeAddUserToRoomPatch } from '../lib/beforeAddUserToRoom';
import { notifyOnRoomChangedById } from '../lib/notifyListener';

/**
Expand Down Expand Up @@ -61,7 +62,10 @@ export const addUserToRoom = async (
}

try {
await beforeAddUserToRoom.run({ user: userToBeAdded, inviter: (inviter && (await Users.findOneById(inviter._id))) || undefined }, room);
const inviterUser = inviter && ((await Users.findOneById(inviter._id)) || undefined);
// Not "duplicated": we're moving away from callbacks so this is a patch function. We should migrate the next one to be a patch or use this same patch, instead of calling both
await beforeAddUserToRoomPatch([userToBeAdded.username!], room, inviterUser);
await beforeAddUserToRoom.run({ user: userToBeAdded, inviter: inviterUser }, room);
} catch (error) {
throw new Meteor.Error((error as any)?.message);
}
Expand Down
1 change: 1 addition & 0 deletions apps/meteor/app/lib/server/functions/getFullUserData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const defaultFields = {
extension: 1,
federated: 1,
statusLivechat: 1,
abacAttributes: 1,
} as const;

const fullFields = {
Expand Down
Loading
Loading