Skip to content

Commit 2d32e52

Browse files
authored
fix: abacAttributes reactivity (RocketChat#40499)
1 parent fa8413f commit 2d32e52

7 files changed

Lines changed: 51 additions & 29 deletions

File tree

.changeset/spicy-phones-breathe.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@rocket.chat/meteor": patch
3+
"@rocket.chat/abac": patch
4+
---
5+
6+
Fixes an issue where some actions made by the abac service were not broadcasting to clients, which affected reactivity

apps/meteor/client/cachedStores/RoomsCachedStore.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { IOmnichannelRoom, IRoom, IRoomWithRetentionPolicy } from '@rocket.chat/core-typings';
2-
import { DEFAULT_SLA_CONFIG, isABACManagedRoom, isRoomNativeFederated, LivechatPriorityWeight } from '@rocket.chat/core-typings';
2+
import { DEFAULT_SLA_CONFIG, isRoomNativeFederated, LivechatPriorityWeight } from '@rocket.chat/core-typings';
33
import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts';
44

55
import { PrivateCachedStore } from '../lib/cachedStores/CachedStore';
@@ -53,9 +53,7 @@ class RoomsCachedStore extends PrivateCachedStore<IRoom> {
5353
source: (room as IOmnichannelRoom | undefined)?.source,
5454
queuedAt: (room as IOmnichannelRoom | undefined)?.queuedAt,
5555
federated: room.federated,
56-
...(isABACManagedRoom(room) && {
57-
abacAttributes: room.abacAttributes,
58-
}),
56+
abacAttributes: room.abacAttributes,
5957
...(isRoomNativeFederated(room) && {
6058
federation: room.federation,
6159
}),

ee/packages/abac/src/helper.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -225,14 +225,8 @@ export function buildRoomNonCompliantConditionsFromSubject(subjectAttributes: IA
225225
return conditions;
226226
}
227227

228-
export async function getAbacRoom(
229-
rid: string,
230-
): Promise<Pick<IRoom, '_id' | 'abacAttributes' | 't' | 'teamMain' | 'teamDefault' | 'default' | 'name'>> {
231-
const room = await Rooms.findOneByIdAndType<
232-
Pick<IRoom, '_id' | 'abacAttributes' | 't' | 'teamMain' | 'teamDefault' | 'default' | 'name'>
233-
>(rid, 'p', {
234-
projection: { abacAttributes: 1, t: 1, teamMain: 1, teamDefault: 1, default: 1, name: 1 },
235-
});
228+
export async function getAbacRoom(rid: string): Promise<IRoom> {
229+
const room = await Rooms.findOneByIdAndType(rid, 'p');
236230
if (!room) {
237231
throw new AbacRoomNotFoundError();
238232
}

ee/packages/abac/src/index.ts

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Room, ServiceClass, Settings } from '@rocket.chat/core-services';
1+
import { api, Room, ServiceClass, Settings } from '@rocket.chat/core-services';
22
import type { AbacActor, IAbacService } from '@rocket.chat/core-services';
33
import { AbacAccessOperation, AbacObjectType } from '@rocket.chat/core-typings';
44
import type {
@@ -428,13 +428,18 @@ export class AbacService extends ServiceClass implements IAbacService {
428428
return Rooms.isAbacAttributeInUse(key, attribute.values || []);
429429
}
430430

431+
private broadcastRoomUpdate(room: IRoom): void {
432+
void api.broadcast('watch.rooms', { clientAction: 'updated', room });
433+
}
434+
431435
async setRoomAbacAttributes(rid: string, attributes: Record<string, string[]>, actor: AbacActor): Promise<void> {
432436
await this.ensurePdpAvailable();
433437
const room = await getAbacRoom(rid);
434438

435439
if (!Object.keys(attributes).length && room.abacAttributes?.length) {
436440
await Rooms.unsetAbacAttributesById(rid);
437441
void Audit.objectAttributesRemoved({ _id: room._id, name: room.name }, room.abacAttributes, actor);
442+
this.broadcastRoomUpdate({ ...room, abacAttributes: undefined });
438443
return;
439444
}
440445

@@ -445,6 +450,10 @@ export class AbacService extends ServiceClass implements IAbacService {
445450
const updated = await Rooms.setAbacAttributesById(rid, normalized);
446451
void Audit.objectAttributeChanged({ _id: room._id, name: room.name }, room.abacAttributes || [], normalized, 'updated', actor);
447452

453+
if (updated) {
454+
this.broadcastRoomUpdate(updated);
455+
}
456+
448457
const previous: IAbacAttributeDefinition[] = room.abacAttributes || [];
449458
if (diffAttributeSets(previous, normalized).added) {
450459
await this.onRoomAttributesChanged(room, updated?.abacAttributes ?? normalized);
@@ -476,6 +485,8 @@ export class AbacService extends ServiceClass implements IAbacService {
476485
);
477486
const next = [...previous, { key, values }];
478487

488+
this.broadcastRoomUpdate({ ...room, abacAttributes: next });
489+
479490
await this.onRoomAttributesChanged(room, next);
480491
return;
481492
}
@@ -486,7 +497,7 @@ export class AbacService extends ServiceClass implements IAbacService {
486497
return;
487498
}
488499

489-
await Rooms.updateAbacAttributeValuesArrayFilteredById(rid, key, values);
500+
const updated = await Rooms.updateAbacAttributeValuesArrayFilteredById(rid, key, values);
490501
void Audit.objectAttributeChanged(
491502
{ _id: room._id, name: room.name },
492503
room.abacAttributes || [],
@@ -495,6 +506,10 @@ export class AbacService extends ServiceClass implements IAbacService {
495506
actor,
496507
);
497508

509+
if (updated) {
510+
this.broadcastRoomUpdate(updated);
511+
}
512+
498513
if (diffAttributeSets([previous[existingIndex]], [{ key, values }]).added) {
499514
const next = previous.map((a, i) => (i === existingIndex ? { key, values } : a));
500515
await this.onRoomAttributesChanged(room, next);
@@ -516,17 +531,16 @@ export class AbacService extends ServiceClass implements IAbacService {
516531
await Rooms.unsetAbacAttributesById(rid);
517532
void Audit.objectAttributesRemoved({ _id: room._id }, previous, actor);
518533

534+
this.broadcastRoomUpdate({ ...room, abacAttributes: undefined });
535+
519536
return;
520537
}
521538

522539
await Rooms.removeAbacAttributeByRoomIdAndKey(rid, key);
523-
void Audit.objectAttributeRemoved(
524-
{ _id: room._id, name: room.name },
525-
previous,
526-
previous.filter((a) => a.key !== key),
527-
'key-removed',
528-
actor,
529-
);
540+
const next = previous.filter((a) => a.key !== key);
541+
void Audit.objectAttributeRemoved({ _id: room._id, name: room.name }, previous, next, 'key-removed', actor);
542+
543+
this.broadcastRoomUpdate({ ...room, abacAttributes: next });
530544
}
531545

532546
async addRoomAbacAttributeByKey(rid: string, key: string, values: string[], actor: AbacActor): Promise<void> {
@@ -549,6 +563,8 @@ export class AbacService extends ServiceClass implements IAbacService {
549563

550564
void Audit.objectAttributeChanged({ _id: room._id, name: room.name }, previous, next, 'key-added', actor);
551565

566+
this.broadcastRoomUpdate({ ...room, abacAttributes: next });
567+
552568
await this.onRoomAttributesChanged(room, next);
553569
}
554570

@@ -570,6 +586,11 @@ export class AbacService extends ServiceClass implements IAbacService {
570586
'key-updated',
571587
actor,
572588
);
589+
590+
if (updated) {
591+
this.broadcastRoomUpdate(updated);
592+
}
593+
573594
if (diffAttributeSets([exists], [{ key, values }]).added) {
574595
await this.onRoomAttributesChanged(room, updated?.abacAttributes || []);
575596
}
@@ -582,13 +603,10 @@ export class AbacService extends ServiceClass implements IAbacService {
582603
}
583604

584605
const updated = await Rooms.insertAbacAttributeIfNotExistsById(rid, key, values);
585-
void Audit.objectAttributeChanged(
586-
{ _id: room._id, name: room.name },
587-
room.abacAttributes || [],
588-
updated?.abacAttributes || [],
589-
'key-added',
590-
actor,
591-
);
606+
const nextAttributes = updated?.abacAttributes || [...(room.abacAttributes || []), { key, values }];
607+
void Audit.objectAttributeChanged({ _id: room._id, name: room.name }, room.abacAttributes || [], nextAttributes, 'key-added', actor);
608+
609+
this.broadcastRoomUpdate({ ...room, abacAttributes: nextAttributes });
592610

593611
await this.onRoomAttributesChanged(room, updated?.abacAttributes || []);
594612
}

ee/packages/abac/src/pdp/VirtruPDP.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ export class VirtruPDP implements IPolicyDecisionPoint {
491491
msg: 'User has no entity key for Virtru PDP evaluation, treating as non-compliant for all ABAC rooms',
492492
userId: user._id,
493493
});
494-
return abacRooms as IRoom[];
494+
return abacRooms;
495495
}
496496

497497
const decisionRequests = abacRooms.map((room) => ({

ee/packages/abac/src/service.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ jest.mock('@rocket.chat/core-services', () => {
6969
Room: {
7070
removeUserFromRoom: jest.fn(),
7171
},
72+
api: {
73+
broadcast: jest.fn(),
74+
},
7275
};
7376
});
7477

ee/packages/abac/src/user-auto-removal.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ jest.mock('@rocket.chat/core-services', () => ({
1717
await Subscriptions.removeByRoomIdAndUserId(roomId, user._id);
1818
},
1919
},
20+
api: {
21+
broadcast: jest.fn(),
22+
},
2023
MeteorError: class extends Error {},
2124
isMeteorError: () => false,
2225
}));

0 commit comments

Comments
 (0)