Skip to content

Commit 68426bd

Browse files
authored
fix: GDPR for contacts (#36228)
1 parent b0be3e5 commit 68426bd

File tree

26 files changed

+179
-75
lines changed

26 files changed

+179
-75
lines changed

.changeset/tough-points-know.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@rocket.chat/meteor": patch
3+
"@rocket.chat/model-typings": patch
4+
"@rocket.chat/models": patch
5+
---
6+
7+
Fixes GDPR contact information removal for Omnichannel.

apps/meteor/app/apps/server/converters/contacts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { transformMappedData } from './transformMappedData';
66

77
export class AppContactsConverter implements IAppContactsConverter {
88
async convertById(contactId: ILivechatContact['_id']): Promise<IAppsLivechatContact | undefined> {
9-
const contact = await LivechatContacts.findOneById(contactId);
9+
const contact = await LivechatContacts.findOneEnabledById(contactId);
1010
if (!contact) {
1111
return;
1212
}

apps/meteor/app/apps/server/converters/rooms.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export class AppRoomsConverter {
105105
if (!contact?._id) {
106106
return;
107107
}
108-
const contactFromDb = await LivechatContacts.findOneById(contact._id, { projection: { _id: 1 } });
108+
const contactFromDb = await LivechatContacts.findOneEnabledById(contact._id, { projection: { _id: 1 } });
109109
return contactFromDb?._id;
110110
}
111111

apps/meteor/app/livechat/server/api/v1/contact.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ API.v1.addRoute(
154154
return API.v1.notFound();
155155
}
156156

157-
const contact = await LivechatContacts.findOneById(contactId);
157+
const contact = await LivechatContacts.findOneEnabledById(contactId);
158158

159159
if (!contact) {
160160
return API.v1.notFound();

apps/meteor/app/livechat/server/api/v1/visitor.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { callbacks } from '../../../../../lib/callbacks';
77
import { API } from '../../../../api/server';
88
import { settings } from '../../../../settings/server';
99
import { setMultipleVisitorCustomFields } from '../../lib/custom-fields';
10-
import { registerGuest, removeGuest, notifyGuestStatusChanged } from '../../lib/guests';
10+
import { registerGuest, notifyGuestStatusChanged, removeContactsByVisitorId } from '../../lib/guests';
11+
import { livechatLogger } from '../../lib/logger';
1112
import { saveRoomInfo } from '../../lib/rooms';
1213
import { updateCallStatus } from '../../lib/utils';
1314
import { findGuest, normalizeHttpHeaderData } from '../lib/livechat';
@@ -140,18 +141,19 @@ API.v1.addRoute('livechat/visitor/:token', {
140141
throw new Meteor.Error('visitor-has-open-rooms', 'Cannot remove visitors with opened rooms');
141142
}
142143

143-
const { _id, token } = visitor;
144-
const result = await removeGuest({ _id, token });
145-
if (!result.modifiedCount) {
144+
const { _id } = visitor;
145+
try {
146+
await removeContactsByVisitorId({ _id });
147+
return API.v1.success({
148+
visitor: {
149+
_id,
150+
ts: new Date().toISOString(),
151+
},
152+
});
153+
} catch (e) {
154+
livechatLogger.error(e);
146155
throw new Meteor.Error('error-removing-visitor', 'An error ocurred while deleting visitor');
147156
}
148-
149-
return API.v1.success({
150-
visitor: {
151-
_id,
152-
ts: new Date().toISOString(),
153-
},
154-
});
155157
},
156158
});
157159

apps/meteor/app/livechat/server/lib/Helper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export const prepareLivechatRoom = async (
9898
const contactId = await migrateVisitorIfMissingContact(_id, source);
9999
const contact =
100100
contactId &&
101-
(await LivechatContacts.findOneById<Pick<ILivechatContact, '_id' | 'name' | 'channels' | 'activity'>>(contactId, {
101+
(await LivechatContacts.findOneEnabledById<Pick<ILivechatContact, '_id' | 'name' | 'channels' | 'activity'>>(contactId, {
102102
projection: { name: 1, channels: 1, activity: 1 },
103103
}));
104104
if (!contact) {

apps/meteor/app/livechat/server/lib/QueueManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ export class QueueManager {
219219
return false;
220220
}
221221

222-
const contact = await LivechatContacts.findOneById(room.contactId, { projection: { channels: 1 } });
222+
const contact = await LivechatContacts.findOneEnabledById(room.contactId, { projection: { channels: 1 } });
223223
if (!contact) {
224224
return false;
225225
}

apps/meteor/app/livechat/server/lib/contacts/getContactChannelsGrouped.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { ILivechatContact, ILivechatContactChannel } from '@rocket.chat/cor
22
import { LivechatContacts } from '@rocket.chat/models';
33

44
export async function getContactChannelsGrouped(contactId: string): Promise<ILivechatContactChannel[]> {
5-
const contact = await LivechatContacts.findOneById<Pick<ILivechatContact, 'channels'>>(contactId, { projection: { channels: 1 } });
5+
const contact = await LivechatContacts.findOneEnabledById<Pick<ILivechatContact, 'channels'>>(contactId, { projection: { channels: 1 } });
66

77
if (!contact?.channels) {
88
return [];

apps/meteor/app/livechat/server/lib/contacts/getContactHistory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const getContactHistory = makeFunction(
3232
async (params: GetContactHistoryParams): Promise<PaginatedResult<{ history: VisitorSearchChatsResult[] }>> => {
3333
const { contactId, count, offset, sort } = params;
3434

35-
const contact = await LivechatContacts.findOneById<Pick<ILivechatContact, '_id'>>(contactId, { projection: { _id: 1 } });
35+
const contact = await LivechatContacts.findOneEnabledById<Pick<ILivechatContact, '_id'>>(contactId, { projection: { _id: 1 } });
3636

3737
if (!contact) {
3838
throw new Error('error-contact-not-found');

apps/meteor/app/livechat/server/lib/contacts/resolveContactConflicts.spec.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import sinon from 'sinon';
55

66
const modelsMock = {
77
LivechatContacts: {
8-
findOneById: sinon.stub(),
8+
findOneEnabledById: sinon.stub(),
99
updateContact: sinon.stub(),
1010
},
1111
Settings: {
@@ -24,13 +24,13 @@ const { resolveContactConflicts } = proxyquire.noCallThru().load('./resolveConta
2424

2525
describe('resolveContactConflicts', () => {
2626
beforeEach(() => {
27-
modelsMock.LivechatContacts.findOneById.reset();
27+
modelsMock.LivechatContacts.findOneEnabledById.reset();
2828
modelsMock.Settings.incrementValueById.reset();
2929
modelsMock.LivechatContacts.updateContact.reset();
3030
});
3131

3232
it('should update the contact with the resolved custom field', async () => {
33-
modelsMock.LivechatContacts.findOneById.resolves({
33+
modelsMock.LivechatContacts.findOneEnabledById.resolves({
3434
_id: 'contactId',
3535
customFields: { customField: 'newValue' },
3636
conflictingFields: [{ field: 'customFields.customField', value: 'oldValue' }],
@@ -44,7 +44,7 @@ describe('resolveContactConflicts', () => {
4444

4545
const result = await resolveContactConflicts({ contactId: 'contactId', customField: { customField: 'newValue' } });
4646

47-
expect(modelsMock.LivechatContacts.findOneById.getCall(0).args[0]).to.be.equal('contactId');
47+
expect(modelsMock.LivechatContacts.findOneEnabledById.getCall(0).args[0]).to.be.equal('contactId');
4848

4949
expect(modelsMock.Settings.incrementValueById.getCall(0).args[0]).to.be.equal('Livechat_conflicting_fields_counter');
5050
expect(modelsMock.Settings.incrementValueById.getCall(0).args[1]).to.be.equal(1);
@@ -59,7 +59,7 @@ describe('resolveContactConflicts', () => {
5959
});
6060

6161
it('should update the contact with the resolved name', async () => {
62-
modelsMock.LivechatContacts.findOneById.resolves({
62+
modelsMock.LivechatContacts.findOneEnabledById.resolves({
6363
_id: 'contactId',
6464
name: 'Old Name',
6565
customFields: { customField: 'newValue' },
@@ -75,7 +75,7 @@ describe('resolveContactConflicts', () => {
7575

7676
const result = await resolveContactConflicts({ contactId: 'contactId', name: 'New Name' });
7777

78-
expect(modelsMock.LivechatContacts.findOneById.getCall(0).args[0]).to.be.equal('contactId');
78+
expect(modelsMock.LivechatContacts.findOneEnabledById.getCall(0).args[0]).to.be.equal('contactId');
7979

8080
expect(modelsMock.Settings.incrementValueById.getCall(0).args[0]).to.be.equal('Livechat_conflicting_fields_counter');
8181
expect(modelsMock.Settings.incrementValueById.getCall(0).args[1]).to.be.equal(1);
@@ -91,7 +91,7 @@ describe('resolveContactConflicts', () => {
9191
});
9292

9393
it('should update the contact with the resolved contact manager', async () => {
94-
modelsMock.LivechatContacts.findOneById.resolves({
94+
modelsMock.LivechatContacts.findOneEnabledById.resolves({
9595
_id: 'contactId',
9696
name: 'Name',
9797
contactManager: 'contactManagerId',
@@ -109,7 +109,7 @@ describe('resolveContactConflicts', () => {
109109

110110
const result = await resolveContactConflicts({ contactId: 'contactId', name: 'New Name' });
111111

112-
expect(modelsMock.LivechatContacts.findOneById.getCall(0).args[0]).to.be.equal('contactId');
112+
expect(modelsMock.LivechatContacts.findOneEnabledById.getCall(0).args[0]).to.be.equal('contactId');
113113

114114
expect(modelsMock.Settings.incrementValueById.getCall(0).args[0]).to.be.equal('Livechat_conflicting_fields_counter');
115115
expect(modelsMock.Settings.incrementValueById.getCall(0).args[1]).to.be.equal(1);
@@ -126,7 +126,7 @@ describe('resolveContactConflicts', () => {
126126

127127
it('should wipe conflicts if wipeConflicts = true', async () => {
128128
it('should update the contact with the resolved name', async () => {
129-
modelsMock.LivechatContacts.findOneById.resolves({
129+
modelsMock.LivechatContacts.findOneEnabledById.resolves({
130130
_id: 'contactId',
131131
name: 'Name',
132132
customFields: { customField: 'newValue' },
@@ -145,7 +145,7 @@ describe('resolveContactConflicts', () => {
145145

146146
const result = await resolveContactConflicts({ contactId: 'contactId', name: 'New Name', wipeConflicts: true });
147147

148-
expect(modelsMock.LivechatContacts.findOneById.getCall(0).args[0]).to.be.equal('contactId');
148+
expect(modelsMock.LivechatContacts.findOneEnabledById.getCall(0).args[0]).to.be.equal('contactId');
149149

150150
expect(modelsMock.Settings.incrementValueById.getCall(0).args[0]).to.be.equal('Livechat_conflicting_fields_counter');
151151
expect(modelsMock.Settings.incrementValueById.getCall(0).args[1]).to.be.equal(2);
@@ -163,7 +163,7 @@ describe('resolveContactConflicts', () => {
163163

164164
it('should wipe conflicts if wipeConflicts = true', async () => {
165165
it('should update the contact with the resolved name', async () => {
166-
modelsMock.LivechatContacts.findOneById.resolves({
166+
modelsMock.LivechatContacts.findOneEnabledById.resolves({
167167
_id: 'contactId',
168168
name: 'Name',
169169
customFields: { customField: 'newValue' },
@@ -182,7 +182,7 @@ describe('resolveContactConflicts', () => {
182182

183183
const result = await resolveContactConflicts({ contactId: 'contactId', name: 'New Name', wipeConflicts: false });
184184

185-
expect(modelsMock.LivechatContacts.findOneById.getCall(0).args[0]).to.be.equal('contactId');
185+
expect(modelsMock.LivechatContacts.findOneEnabledById.getCall(0).args[0]).to.be.equal('contactId');
186186

187187
expect(modelsMock.Settings.incrementValueById.getCall(0).args[0]).to.be.equal('Livechat_conflicting_fields_counter');
188188
expect(modelsMock.Settings.incrementValueById.getCall(0).args[1]).to.be.equal(1);
@@ -199,15 +199,15 @@ describe('resolveContactConflicts', () => {
199199
});
200200

201201
it('should throw an error if the contact does not exist', async () => {
202-
modelsMock.LivechatContacts.findOneById.resolves(undefined);
202+
modelsMock.LivechatContacts.findOneEnabledById.resolves(undefined);
203203
await expect(resolveContactConflicts({ contactId: 'id', customField: { customField: 'newValue' } })).to.be.rejectedWith(
204204
'error-contact-not-found',
205205
);
206206
expect(modelsMock.LivechatContacts.updateContact.getCall(0)).to.be.null;
207207
});
208208

209209
it('should throw an error if the contact has no conflicting fields', async () => {
210-
modelsMock.LivechatContacts.findOneById.resolves({
210+
modelsMock.LivechatContacts.findOneEnabledById.resolves({
211211
_id: 'contactId',
212212
name: 'Name',
213213
contactManager: 'contactManagerId',
@@ -221,7 +221,7 @@ describe('resolveContactConflicts', () => {
221221
});
222222

223223
it('should throw an error if the contact manager is invalid', async () => {
224-
modelsMock.LivechatContacts.findOneById.resolves({
224+
modelsMock.LivechatContacts.findOneEnabledById.resolves({
225225
_id: 'contactId',
226226
name: 'Name',
227227
contactManager: 'contactManagerId',

0 commit comments

Comments
 (0)