@wireapp/core 42.19.0 → 42.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Account.d.ts +2 -0
- package/lib/Account.d.ts.map +1 -1
- package/lib/Account.js +4 -1
- package/lib/conversation/SubconversationService/SubconversationService.d.ts +60 -0
- package/lib/conversation/SubconversationService/SubconversationService.d.ts.map +1 -0
- package/lib/conversation/SubconversationService/SubconversationService.js +207 -0
- package/lib/conversation/SubconversationService/SubconversationService.test.d.ts +2 -0
- package/lib/conversation/SubconversationService/SubconversationService.test.d.ts.map +1 -0
- package/lib/conversation/SubconversationService/SubconversationService.test.js +390 -0
- package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts +0 -25
- package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/MLSService/MLSService.js +0 -70
- package/package.json +2 -2
package/lib/Account.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { BroadcastService } from './broadcast/';
|
|
|
9
9
|
import { ClientInfo, ClientService } from './client/';
|
|
10
10
|
import { ConnectionService } from './connection/';
|
|
11
11
|
import { AssetService, ConversationService } from './conversation/';
|
|
12
|
+
import { SubconversationService } from './conversation/SubconversationService/SubconversationService';
|
|
12
13
|
import { GiphyService } from './giphy/';
|
|
13
14
|
import { LinkPreviewService } from './linkPreview';
|
|
14
15
|
import { MLSService } from './messagingProtocols/mls';
|
|
@@ -83,6 +84,7 @@ export declare class Account extends TypedEventEmitter<Events> {
|
|
|
83
84
|
client: ClientService;
|
|
84
85
|
connection: ConnectionService;
|
|
85
86
|
conversation: ConversationService;
|
|
87
|
+
subconversation: SubconversationService;
|
|
86
88
|
giphy: GiphyService;
|
|
87
89
|
linkPreview: LinkPreviewService;
|
|
88
90
|
notification: NotificationService;
|
package/lib/Account.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Account.d.ts","sourceRoot":"","sources":["../src/Account.ts"],"names":[],"mappings":"AAmBA,OAAO,EACL,YAAY,EAGZ,OAAO,EACP,MAAM,EAEN,SAAS,EACV,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAuB,UAAU,EAAE,gBAAgB,EAAC,MAAM,iCAAiC,CAAC;AACnG,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AAOxD,OAAO,EAAC,SAAS,EAAE,eAAe,EAAC,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAC,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAC,UAAU,EAAe,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAAC,cAAc,EAAC,MAAM,YAAY,CAAC;AAE1C,OAAO,EAAC,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAC,UAAU,EAAE,aAAa,EAAC,MAAM,WAAW,CAAC;AACpD,OAAO,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAChD,OAAO,EAAC,YAAY,EAAE,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AAElE,OAAO,EAAC,YAAY,EAAC,MAAM,UAAU,CAAC;AACtC,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACjD,OAAO,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAC,aAAa,EAAE,mBAAmB,EAAO,MAAM,6CAA6C,CAAC;AACrG,OAAO,EAAC,YAAY,EAAE,oBAAoB,EAAC,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAC,SAAS,EAAE,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAAC,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAC,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AAEpC,OAAO,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AACpC,OAAO,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AACpC,OAAO,EAAC,sBAAsB,EAAC,MAAM,+BAA+B,CAAC;AAErE,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAExD,oBAAY,MAAM;IAChB;;;OAGG;IACH,WAAW,gBAAgB;CAC5B;AAED,oBAAY,eAAe;IACzB,8EAA8E;IAC9E,MAAM,WAAW;IACjB,oCAAoC;IACpC,UAAU,eAAe;IACzB,mFAAmF;IACnF,wBAAwB,6BAA6B;IACrD,oGAAoG;IACpG,IAAI,SAAS;CACd;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,SAAS,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;AAEjH,UAAU,cAAc;IACtB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,aAAa,CAAC;IAE5B;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,KAAK,WAAW,GAAG;IACjB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAQF,KAAK,MAAM,GAAG;IACZ,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC;CACjC,CAAC;AAEF,qBAAa,OAAQ,SAAQ,iBAAiB,CAAC,MAAM,CAAC;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAuB;IAC7D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyB;IACtD,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,EAAE,CAAC,CAAe;IAEnB,OAAO,CAAC,EAAE;QACf,GAAG,CAAC,EAAE,UAAU,CAAC;QACjB,WAAW,CAAC,EAAE,mBAAmB,CAAC;QAClC,OAAO,EAAE,cAAc,CAAC;QACxB,OAAO,EAAE,cAAc,CAAC;QACxB,KAAK,EAAE,YAAY,CAAC;QACpB,SAAS,EAAE,gBAAgB,CAAC;QAC5B,MAAM,EAAE,aAAa,CAAC;QACtB,UAAU,EAAE,iBAAiB,CAAC;QAC9B,YAAY,EAAE,mBAAmB,CAAC;QAClC,KAAK,EAAE,YAAY,CAAC;QACpB,WAAW,EAAE,kBAAkB,CAAC;QAChC,YAAY,EAAE,mBAAmB,CAAC;QAClC,IAAI,EAAE,WAAW,CAAC;QAClB,IAAI,EAAE,WAAW,CAAC;QAClB,IAAI,EAAE,WAAW,CAAC;KACnB,CAAC;IACK,eAAe,EAAE,eAAe,CAAC;IACjC,sBAAsB,EAAE,sBAAsB,CAAC;IAEtD;;;OAGG;gBAED,SAAS,GAAE,SAA2B,EACtC,EAAC,WAA6B,EAAE,SAAe,EAAE,oBAAoB,EAAC,GAAE,cAAmB;IAsC7F;;;;;;;;;;;OAWG;IACU,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO;IAMvE,OAAO,CAAC,aAAa;IAKR,UAAU,CACrB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,GAAG,OAAO,CAAC;IA0BnC,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;;;OAKG;IACU,QAAQ,CAAC,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAM3F;;;;OAIG;IACU,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,EAAC,MAAM,EAAC,GAAE,WAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAMvF;;;;;OAKG;IACU,KAAK,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAU1D;;OAEG;IACU,cAAc,CACzB,SAAS,EAAE,SAAS,EACpB,UAAU,GAAE,UAA8B,EAC1C,WAAW,CAAC,EAAE,UAAU,GACvB,OAAO,CAAC,gBAAgB,CAAC;IAsB5B;;;;OAIG;IACU,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAC/D,UAAU,IAAI,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;YAkClD,iBAAiB;IA2B/B;;;;;;OAMG;IACH,qBAAqB,CAAC,YAAY,EAAE,YAAY;IAInC,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"Account.d.ts","sourceRoot":"","sources":["../src/Account.ts"],"names":[],"mappings":"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"}
|
package/lib/Account.js
CHANGED
|
@@ -45,6 +45,7 @@ const client_2 = require("./client/");
|
|
|
45
45
|
const connection_1 = require("./connection/");
|
|
46
46
|
const conversation_1 = require("./conversation/");
|
|
47
47
|
const messageSender_1 = require("./conversation/message/messageSender");
|
|
48
|
+
const SubconversationService_1 = require("./conversation/SubconversationService/SubconversationService");
|
|
48
49
|
const giphy_1 = require("./giphy/");
|
|
49
50
|
const linkPreview_1 = require("./linkPreview");
|
|
50
51
|
const mls_1 = require("./messagingProtocols/mls");
|
|
@@ -245,7 +246,7 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
245
246
|
// initialize scheduler for syncing key packages with backend
|
|
246
247
|
await this.service.mls.schedulePeriodicKeyPackagesBackendSync(validClient.id);
|
|
247
248
|
// leave stale conference subconversations (e.g after a crash)
|
|
248
|
-
await this.service.
|
|
249
|
+
await this.service.subconversation.leaveStaleConferenceSubconversations();
|
|
249
250
|
}
|
|
250
251
|
return validClient;
|
|
251
252
|
}
|
|
@@ -306,6 +307,7 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
306
307
|
const giphyService = new giphy_1.GiphyService(this.apiClient);
|
|
307
308
|
const linkPreviewService = new linkPreview_1.LinkPreviewService(assetService);
|
|
308
309
|
const conversationService = new conversation_1.ConversationService(this.apiClient, proteusService, this.db, mlsService);
|
|
310
|
+
const subconversationService = new SubconversationService_1.SubconversationService(this.apiClient, mlsService);
|
|
309
311
|
const notificationService = new notification_1.NotificationService(this.apiClient, this.storeEngine, conversationService);
|
|
310
312
|
const selfService = new self_1.SelfService(this.apiClient);
|
|
311
313
|
const teamService = new team_1.TeamService(this.apiClient);
|
|
@@ -321,6 +323,7 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
321
323
|
client: clientService,
|
|
322
324
|
connection: connectionService,
|
|
323
325
|
conversation: conversationService,
|
|
326
|
+
subconversation: subconversationService,
|
|
324
327
|
giphy: giphyService,
|
|
325
328
|
linkPreview: linkPreviewService,
|
|
326
329
|
notification: notificationService,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { QualifiedId } from '@wireapp/api-client/lib/user';
|
|
2
|
+
import { APIClient } from '@wireapp/api-client';
|
|
3
|
+
import { TypedEventEmitter } from '@wireapp/commons';
|
|
4
|
+
import { MLSService } from '../../messagingProtocols/mls';
|
|
5
|
+
type Events = {
|
|
6
|
+
MLSConversationRecovered: {
|
|
7
|
+
conversationId: QualifiedId;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export interface SubconversationEpochInfoMember {
|
|
11
|
+
userid: `${string}@${string}`;
|
|
12
|
+
clientid: string;
|
|
13
|
+
in_subconv: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare class SubconversationService extends TypedEventEmitter<Events> {
|
|
16
|
+
private readonly apiClient;
|
|
17
|
+
private readonly _mlsService?;
|
|
18
|
+
private readonly logger;
|
|
19
|
+
constructor(apiClient: APIClient, _mlsService?: MLSService | undefined);
|
|
20
|
+
get mlsService(): MLSService;
|
|
21
|
+
/**
|
|
22
|
+
* Will join or register an mls subconversation for conference calls.
|
|
23
|
+
* Will return the secret key derived from the subconversation
|
|
24
|
+
*
|
|
25
|
+
* @param conversationId Id of the parent conversation in which the call should happen
|
|
26
|
+
*/
|
|
27
|
+
joinConferenceSubconversation(conversationId: QualifiedId, shouldRetry?: boolean): Promise<{
|
|
28
|
+
groupId: string;
|
|
29
|
+
epoch: number;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Will leave conference subconversation if it's known by client and established.
|
|
33
|
+
*
|
|
34
|
+
* @param conversationId Id of the parent conversation which subconversation we want to leave
|
|
35
|
+
*/
|
|
36
|
+
leaveConferenceSubconversation(conversationId: QualifiedId): Promise<void>;
|
|
37
|
+
leaveStaleConferenceSubconversations(): Promise<void>;
|
|
38
|
+
getSubconversationEpochInfo(conversationId: QualifiedId, shouldAdvanceEpoch?: boolean): Promise<{
|
|
39
|
+
members: SubconversationEpochInfoMember[];
|
|
40
|
+
epoch: number;
|
|
41
|
+
secretKey: string;
|
|
42
|
+
keyLength: number;
|
|
43
|
+
} | null>;
|
|
44
|
+
subscribeToEpochUpdates(conversationId: QualifiedId, findConversationByGroupId: (groupId: string) => QualifiedId | undefined, onEpochUpdate: (info: {
|
|
45
|
+
members: SubconversationEpochInfoMember[];
|
|
46
|
+
epoch: number;
|
|
47
|
+
secretKey: string;
|
|
48
|
+
keyLength: number;
|
|
49
|
+
}) => void): Promise<() => void>;
|
|
50
|
+
removeClientFromConferenceSubconversation(conversationId: QualifiedId, clientToRemove: {
|
|
51
|
+
user: QualifiedId;
|
|
52
|
+
clientId: string;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
private joinSubconversationByExternalCommit;
|
|
55
|
+
private getConferenceSubconversation;
|
|
56
|
+
private deleteConferenceSubconversation;
|
|
57
|
+
private generateSubconversationMembers;
|
|
58
|
+
}
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=SubconversationService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubconversationService.d.ts","sourceRoot":"","sources":["../../../src/conversation/SubconversationService/SubconversationService.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAC;AAIzD,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAC,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAC,UAAU,EAAC,MAAM,8BAA8B,CAAC;AAIxD,KAAK,MAAM,GAAG;IACZ,wBAAwB,EAAE;QAAC,cAAc,EAAE,WAAW,CAAA;KAAC,CAAC;CACzD,CAAC;AAEF,MAAM,WAAW,8BAA8B;IAC7C,MAAM,EAAE,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACrB;AAID,qBAAa,sBAAuB,SAAQ,iBAAiB,CAAC,MAAM,CAAC;IAIjE,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAJ/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmD;gBAGvD,SAAS,EAAE,SAAS,EACpB,WAAW,CAAC,wBAAY;IAK3C,IAAI,UAAU,IAAI,UAAU,CAK3B;IAED;;;;;OAKG;IACU,6BAA6B,CACxC,cAAc,EAAE,WAAW,EAC3B,WAAW,UAAO,GACjB,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC;IAiD5C;;;;OAIG;IACU,8BAA8B,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B1E,oCAAoC,IAAI,OAAO,CAAC,IAAI,CAAC;IAUrD,2BAA2B,CACtC,cAAc,EAAE,WAAW,EAC3B,kBAAkB,UAAQ,GACzB,OAAO,CAAC;QACT,OAAO,EAAE,8BAA8B,EAAE,CAAC;QAC1C,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;IAsCI,uBAAuB,CAClC,cAAc,EAAE,WAAW,EAC3B,yBAAyB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,EACvE,aAAa,EAAE,CAAC,IAAI,EAAE;QACpB,OAAO,EAAE,8BAA8B,EAAE,CAAC;QAC1C,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,KAAK,IAAI,GACT,OAAO,CAAC,MAAM,IAAI,CAAC;IAuCT,yCAAyC,CACpD,cAAc,EAAE,WAAW,EAC3B,cAAc,EAAE;QAAC,IAAI,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAC,GACpD,OAAO,CAAC,IAAI,CAAC;YAmCF,mCAAmC;YAMnC,4BAA4B;YAI5B,+BAA+B;YAO/B,8BAA8B;CAqB7C"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Wire
|
|
4
|
+
* Copyright (C) 2023 Wire Swiss GmbH
|
|
5
|
+
*
|
|
6
|
+
* This program is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* This program is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU General Public License
|
|
17
|
+
* along with this program. If not, see http://www.gnu.org/licenses/.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.SubconversationService = void 0;
|
|
25
|
+
const conversation_1 = require("@wireapp/api-client/lib/conversation");
|
|
26
|
+
const TimeUtil_1 = require("@wireapp/commons/lib/util/TimeUtil");
|
|
27
|
+
const logdown_1 = __importDefault(require("logdown"));
|
|
28
|
+
const commons_1 = require("@wireapp/commons");
|
|
29
|
+
const subconversationGroupIdStore_1 = require("../../messagingProtocols/mls/MLSService/stores/subconversationGroupIdStore/subconversationGroupIdStore");
|
|
30
|
+
const fullyQualifiedClientIdUtils_1 = require("../../util/fullyQualifiedClientIdUtils");
|
|
31
|
+
const MLS_CONVERSATION_KEY_LENGTH = 32;
|
|
32
|
+
class SubconversationService extends commons_1.TypedEventEmitter {
|
|
33
|
+
constructor(apiClient, _mlsService) {
|
|
34
|
+
super();
|
|
35
|
+
this.apiClient = apiClient;
|
|
36
|
+
this._mlsService = _mlsService;
|
|
37
|
+
this.logger = (0, logdown_1.default)('@wireapp/core/SubconversationService');
|
|
38
|
+
}
|
|
39
|
+
get mlsService() {
|
|
40
|
+
if (!this._mlsService) {
|
|
41
|
+
throw new Error('MLSService was not initialised!');
|
|
42
|
+
}
|
|
43
|
+
return this._mlsService;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Will join or register an mls subconversation for conference calls.
|
|
47
|
+
* Will return the secret key derived from the subconversation
|
|
48
|
+
*
|
|
49
|
+
* @param conversationId Id of the parent conversation in which the call should happen
|
|
50
|
+
*/
|
|
51
|
+
async joinConferenceSubconversation(conversationId, shouldRetry = true) {
|
|
52
|
+
try {
|
|
53
|
+
const { group_id: subconversationGroupId, epoch: subconversationEpoch, epoch_timestamp: subconversationEpochTimestamp, subconv_id: subconversationId, } = await this.getConferenceSubconversation(conversationId);
|
|
54
|
+
if (subconversationEpoch === 0) {
|
|
55
|
+
const doesConversationExistsLocally = await this.mlsService.conversationExists(subconversationGroupId);
|
|
56
|
+
if (doesConversationExistsLocally) {
|
|
57
|
+
await this.mlsService.wipeConversation(subconversationGroupId);
|
|
58
|
+
}
|
|
59
|
+
// If subconversation is not yet established, create it and upload the commit bundle.
|
|
60
|
+
await this.mlsService.registerConversation(subconversationGroupId, []);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const epochUpdateTime = new Date(subconversationEpochTimestamp).getTime();
|
|
64
|
+
const epochAge = new Date().getTime() - epochUpdateTime;
|
|
65
|
+
if (epochAge > TimeUtil_1.TimeInMillis.DAY) {
|
|
66
|
+
// If subconversation does exist, but it's older than 24h, delete and re-join
|
|
67
|
+
await this.deleteConferenceSubconversation(conversationId, {
|
|
68
|
+
groupId: subconversationGroupId,
|
|
69
|
+
epoch: subconversationEpoch,
|
|
70
|
+
});
|
|
71
|
+
await this.mlsService.wipeConversation(subconversationGroupId);
|
|
72
|
+
return this.joinConferenceSubconversation(conversationId);
|
|
73
|
+
}
|
|
74
|
+
await this.joinSubconversationByExternalCommit(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
75
|
+
}
|
|
76
|
+
const epoch = Number(await this.mlsService.getEpoch(subconversationGroupId));
|
|
77
|
+
// We store the mapping between the subconversation and the parent conversation
|
|
78
|
+
subconversationGroupIdStore_1.subconversationGroupIdStore.storeGroupId(conversationId, subconversationId, subconversationGroupId);
|
|
79
|
+
return { groupId: subconversationGroupId, epoch };
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (shouldRetry) {
|
|
83
|
+
return this.joinConferenceSubconversation(conversationId, false);
|
|
84
|
+
}
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Will leave conference subconversation if it's known by client and established.
|
|
90
|
+
*
|
|
91
|
+
* @param conversationId Id of the parent conversation which subconversation we want to leave
|
|
92
|
+
*/
|
|
93
|
+
async leaveConferenceSubconversation(conversationId) {
|
|
94
|
+
const subconversationGroupId = subconversationGroupIdStore_1.subconversationGroupIdStore.getGroupId(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
95
|
+
if (!subconversationGroupId) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const doesGroupExistLocally = await this.mlsService.conversationExists(subconversationGroupId);
|
|
99
|
+
if (!doesGroupExistLocally) {
|
|
100
|
+
// If the subconversation was known by a client but is does not exist locally, we can remove it from the store.
|
|
101
|
+
return subconversationGroupIdStore_1.subconversationGroupIdStore.removeGroupId(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
await this.apiClient.api.conversation.deleteSubconversationSelf(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
this.logger.error(`Failed to leave conference subconversation:`, error);
|
|
108
|
+
}
|
|
109
|
+
await this.mlsService.wipeConversation(subconversationGroupId);
|
|
110
|
+
// once we've left the subconversation, we can remove it from the store
|
|
111
|
+
subconversationGroupIdStore_1.subconversationGroupIdStore.removeGroupId(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
112
|
+
}
|
|
113
|
+
async leaveStaleConferenceSubconversations() {
|
|
114
|
+
const conversationIds = subconversationGroupIdStore_1.subconversationGroupIdStore.getAllGroupIdsBySubconversationId(conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
115
|
+
for (const { parentConversationId } of conversationIds) {
|
|
116
|
+
await this.leaveConferenceSubconversation(parentConversationId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async getSubconversationEpochInfo(conversationId, shouldAdvanceEpoch = false) {
|
|
120
|
+
const subconversationGroupId = await this.mlsService.getGroupIdFromConversationId(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
121
|
+
const parentGroupId = await this.mlsService.getGroupIdFromConversationId(conversationId);
|
|
122
|
+
// this method should not be called if the subconversation (and its parent conversation) is not established
|
|
123
|
+
if (!subconversationGroupId || !parentGroupId) {
|
|
124
|
+
this.logger.error(`Could not obtain epoch info for conference subconversation of conversation ${JSON.stringify(conversationId)}: parent or subconversation group ID is missing`);
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
//we don't want to react to avs callbacks when conversation was not yet established
|
|
128
|
+
const doesMLSGroupExist = await this.mlsService.conversationExists(subconversationGroupId);
|
|
129
|
+
if (!doesMLSGroupExist) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const members = await this.generateSubconversationMembers(subconversationGroupId, parentGroupId);
|
|
133
|
+
if (shouldAdvanceEpoch) {
|
|
134
|
+
await this.mlsService.renewKeyMaterial(subconversationGroupId);
|
|
135
|
+
}
|
|
136
|
+
const epoch = Number(await this.mlsService.getEpoch(subconversationGroupId));
|
|
137
|
+
const secretKey = await this.mlsService.exportSecretKey(subconversationGroupId, MLS_CONVERSATION_KEY_LENGTH);
|
|
138
|
+
return { members, epoch, keyLength: MLS_CONVERSATION_KEY_LENGTH, secretKey };
|
|
139
|
+
}
|
|
140
|
+
async subscribeToEpochUpdates(conversationId, findConversationByGroupId, onEpochUpdate) {
|
|
141
|
+
const { epoch: initialEpoch, groupId: subconversationGroupId } = await this.joinConferenceSubconversation(conversationId);
|
|
142
|
+
const forwardNewEpoch = async ({ groupId, epoch }) => {
|
|
143
|
+
var _a, _b;
|
|
144
|
+
if (groupId !== subconversationGroupId) {
|
|
145
|
+
// if the epoch update did not happen in the subconversation directly, check if it happened in the parent conversation
|
|
146
|
+
const parentConversationId = findConversationByGroupId(groupId);
|
|
147
|
+
if (!parentConversationId) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const foundSubconversationGroupId = await ((_b = (_a = this.mlsService).getGroupIdFromConversationId) === null || _b === void 0 ? void 0 : _b.call(_a, parentConversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE));
|
|
151
|
+
// if the conference subconversation of parent conversation is not known, ignore the epoch update
|
|
152
|
+
if (foundSubconversationGroupId !== subconversationGroupId) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const subconversationEpochInfo = await this.getSubconversationEpochInfo(conversationId);
|
|
157
|
+
if (!subconversationEpochInfo) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
return onEpochUpdate(Object.assign(Object.assign({}, subconversationEpochInfo), { epoch: Number(epoch) }));
|
|
161
|
+
};
|
|
162
|
+
this.mlsService.on('newEpoch', forwardNewEpoch);
|
|
163
|
+
await forwardNewEpoch({ groupId: subconversationGroupId, epoch: initialEpoch });
|
|
164
|
+
return () => this.mlsService.off('newEpoch', forwardNewEpoch);
|
|
165
|
+
}
|
|
166
|
+
async removeClientFromConferenceSubconversation(conversationId, clientToRemove) {
|
|
167
|
+
const subconversationGroupId = await this.mlsService.getGroupIdFromConversationId(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
168
|
+
if (!subconversationGroupId) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const doesMLSGroupExist = await this.mlsService.conversationExists(subconversationGroupId);
|
|
172
|
+
if (!doesMLSGroupExist) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const { user: { id: userId, domain }, clientId, } = clientToRemove;
|
|
176
|
+
const clientToRemoveQualifiedId = (0, fullyQualifiedClientIdUtils_1.constructFullyQualifiedClientId)(userId, clientId, domain);
|
|
177
|
+
const subconversationMembers = await this.mlsService.getClientIds(subconversationGroupId);
|
|
178
|
+
const isSubconversationMember = subconversationMembers.some(({ userId, clientId, domain }) => (0, fullyQualifiedClientIdUtils_1.constructFullyQualifiedClientId)(userId, clientId, domain) === clientToRemoveQualifiedId);
|
|
179
|
+
if (!isSubconversationMember) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
return void this.mlsService.removeClientsFromConversation(subconversationGroupId, [clientToRemoveQualifiedId]);
|
|
183
|
+
}
|
|
184
|
+
async joinSubconversationByExternalCommit(conversationId, subconversation) {
|
|
185
|
+
await this.mlsService.joinByExternalCommit(() => this.apiClient.api.conversation.getSubconversationGroupInfo(conversationId, subconversation));
|
|
186
|
+
}
|
|
187
|
+
async getConferenceSubconversation(conversationId) {
|
|
188
|
+
return this.apiClient.api.conversation.getSubconversation(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
189
|
+
}
|
|
190
|
+
async deleteConferenceSubconversation(conversationId, data) {
|
|
191
|
+
return this.apiClient.api.conversation.deleteSubconversation(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE, data);
|
|
192
|
+
}
|
|
193
|
+
async generateSubconversationMembers(subconversationGroupId, parentGroupId) {
|
|
194
|
+
const subconversationMemberIds = await this.mlsService.getClientIds(subconversationGroupId);
|
|
195
|
+
const parentMemberIds = await this.mlsService.getClientIds(parentGroupId);
|
|
196
|
+
return parentMemberIds.map(parentMember => {
|
|
197
|
+
const isSubconversationMember = subconversationMemberIds.some(({ userId, clientId, domain }) => (0, fullyQualifiedClientIdUtils_1.constructFullyQualifiedClientId)(userId, clientId, domain) ===
|
|
198
|
+
(0, fullyQualifiedClientIdUtils_1.constructFullyQualifiedClientId)(parentMember.userId, parentMember.clientId, parentMember.domain));
|
|
199
|
+
return {
|
|
200
|
+
userid: `${parentMember.userId}@${parentMember.domain}`,
|
|
201
|
+
clientid: parentMember.clientId,
|
|
202
|
+
in_subconv: isSubconversationMember,
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.SubconversationService = SubconversationService;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubconversationService.test.d.ts","sourceRoot":"","sources":["../../../src/conversation/SubconversationService/SubconversationService.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Wire
|
|
4
|
+
* Copyright (C) 2023 Wire Swiss GmbH
|
|
5
|
+
*
|
|
6
|
+
* This program is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* This program is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU General Public License
|
|
17
|
+
* along with this program. If not, see http://www.gnu.org/licenses/.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
const conversation_1 = require("@wireapp/api-client/lib/conversation");
|
|
22
|
+
const http_1 = require("@wireapp/api-client/lib/http");
|
|
23
|
+
const api_client_1 = require("@wireapp/api-client");
|
|
24
|
+
const SubconversationService_1 = require("./SubconversationService");
|
|
25
|
+
const subconversationGroupIdStore_1 = require("../../messagingProtocols/mls/MLSService/stores/subconversationGroupIdStore/subconversationGroupIdStore");
|
|
26
|
+
const fullyQualifiedClientIdUtils_1 = require("../../util/fullyQualifiedClientIdUtils");
|
|
27
|
+
const getSubconversationResponse = ({ epoch, epochTimestamp, parentConversationId, groupId, members = [], subconversationId = conversation_1.SUBCONVERSATION_ID.CONFERENCE, }) => {
|
|
28
|
+
return {
|
|
29
|
+
cipher_suite: 1,
|
|
30
|
+
epoch,
|
|
31
|
+
parent_qualified_id: parentConversationId,
|
|
32
|
+
group_id: groupId,
|
|
33
|
+
members,
|
|
34
|
+
subconv_id: subconversationId,
|
|
35
|
+
epoch_timestamp: epochTimestamp,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
const buildSubconversationService = () => {
|
|
39
|
+
const apiClient = new api_client_1.APIClient({ urls: api_client_1.APIClient.BACKEND.STAGING });
|
|
40
|
+
const mlsService = {
|
|
41
|
+
conversationExists: jest.fn(),
|
|
42
|
+
wipeConversation: jest.fn(),
|
|
43
|
+
registerConversation: jest.fn(),
|
|
44
|
+
getEpoch: jest.fn(),
|
|
45
|
+
joinByExternalCommit: jest.fn(),
|
|
46
|
+
getGroupIdFromConversationId: jest.fn(),
|
|
47
|
+
exportSecretKey: jest.fn(),
|
|
48
|
+
getClientIds: jest.fn(),
|
|
49
|
+
renewKeyMaterial: jest.fn(),
|
|
50
|
+
on: jest.fn(),
|
|
51
|
+
off: jest.fn(),
|
|
52
|
+
removeClientsFromConversation: jest.fn(),
|
|
53
|
+
};
|
|
54
|
+
const subconversationService = new SubconversationService_1.SubconversationService(apiClient, mlsService);
|
|
55
|
+
return [subconversationService, { apiClient, mlsService }];
|
|
56
|
+
};
|
|
57
|
+
describe('SubconversationService', () => {
|
|
58
|
+
describe('joinConferenceSubconversation', () => {
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
jest.useRealTimers();
|
|
61
|
+
jest.clearAllMocks();
|
|
62
|
+
});
|
|
63
|
+
it('wipes group locally (if it exists) before registering a group if remote epoch is equal 0', async () => {
|
|
64
|
+
const [subconversationService, { apiClient, mlsService }] = buildSubconversationService();
|
|
65
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
66
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
67
|
+
const subconversationResponse = getSubconversationResponse({
|
|
68
|
+
epoch: 0,
|
|
69
|
+
epochTimestamp: '',
|
|
70
|
+
parentConversationId,
|
|
71
|
+
groupId: subconversationGroupId,
|
|
72
|
+
subconversationId: conversation_1.SUBCONVERSATION_ID.CONFERENCE,
|
|
73
|
+
});
|
|
74
|
+
jest.spyOn(apiClient.api.conversation, 'getSubconversation').mockResolvedValueOnce(subconversationResponse);
|
|
75
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(true);
|
|
76
|
+
await subconversationService.joinConferenceSubconversation(parentConversationId);
|
|
77
|
+
expect(mlsService.wipeConversation).toHaveBeenCalledWith(subconversationGroupId);
|
|
78
|
+
expect(mlsService.registerConversation).toHaveBeenCalledWith(subconversationGroupId, []);
|
|
79
|
+
});
|
|
80
|
+
it('registers a group if remote epoch is 0 and group does not exist locally', async () => {
|
|
81
|
+
const [subconversationService, { apiClient, mlsService }] = buildSubconversationService();
|
|
82
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
83
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
84
|
+
const subconversationResponse = getSubconversationResponse({
|
|
85
|
+
epoch: 0,
|
|
86
|
+
epochTimestamp: '',
|
|
87
|
+
parentConversationId,
|
|
88
|
+
groupId: subconversationGroupId,
|
|
89
|
+
subconversationId: conversation_1.SUBCONVERSATION_ID.CONFERENCE,
|
|
90
|
+
});
|
|
91
|
+
jest.spyOn(apiClient.api.conversation, 'getSubconversation').mockResolvedValueOnce(subconversationResponse);
|
|
92
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(false);
|
|
93
|
+
await subconversationService.joinConferenceSubconversation(parentConversationId);
|
|
94
|
+
expect(mlsService.wipeConversation).not.toHaveBeenCalled();
|
|
95
|
+
expect(mlsService.registerConversation).toHaveBeenCalledWith(subconversationGroupId, []);
|
|
96
|
+
});
|
|
97
|
+
it('deletes conference subconversation from backend if group is already established and epoch is older than one day, then rejoins', async () => {
|
|
98
|
+
jest.useFakeTimers();
|
|
99
|
+
const [subconversationService, { apiClient, mlsService }] = buildSubconversationService();
|
|
100
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
101
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
102
|
+
const initialSubconversationEpoch = 1;
|
|
103
|
+
const currentTimeISO = '2023-10-24T12:00:00.000Z';
|
|
104
|
+
jest.setSystemTime(new Date(currentTimeISO));
|
|
105
|
+
jest.spyOn(apiClient.api.conversation, 'deleteSubconversation').mockResolvedValueOnce();
|
|
106
|
+
// epoch time is older than 24h
|
|
107
|
+
const epochTimestamp = '2023-10-23T11:00:00.000Z';
|
|
108
|
+
const subconversationResponse = getSubconversationResponse({
|
|
109
|
+
epoch: initialSubconversationEpoch,
|
|
110
|
+
epochTimestamp: epochTimestamp,
|
|
111
|
+
parentConversationId,
|
|
112
|
+
groupId: subconversationGroupId,
|
|
113
|
+
subconversationId: conversation_1.SUBCONVERSATION_ID.CONFERENCE,
|
|
114
|
+
});
|
|
115
|
+
jest.spyOn(apiClient.api.conversation, 'getSubconversation').mockResolvedValueOnce(subconversationResponse);
|
|
116
|
+
// After deletion, epoch is 0
|
|
117
|
+
const subconversationEpochAfterDeletion = 0;
|
|
118
|
+
const subconversationResponse2 = getSubconversationResponse({
|
|
119
|
+
epoch: subconversationEpochAfterDeletion,
|
|
120
|
+
epochTimestamp: epochTimestamp,
|
|
121
|
+
parentConversationId,
|
|
122
|
+
groupId: subconversationGroupId,
|
|
123
|
+
subconversationId: conversation_1.SUBCONVERSATION_ID.CONFERENCE,
|
|
124
|
+
});
|
|
125
|
+
jest.spyOn(apiClient.api.conversation, 'getSubconversation').mockResolvedValueOnce(subconversationResponse2);
|
|
126
|
+
await subconversationService.joinConferenceSubconversation(parentConversationId);
|
|
127
|
+
expect(apiClient.api.conversation.deleteSubconversation).toHaveBeenCalledWith(parentConversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE, {
|
|
128
|
+
groupId: subconversationGroupId,
|
|
129
|
+
epoch: initialSubconversationEpoch,
|
|
130
|
+
});
|
|
131
|
+
expect(mlsService.registerConversation).toHaveBeenCalledTimes(1);
|
|
132
|
+
expect(mlsService.wipeConversation).toHaveBeenCalledWith(subconversationGroupId);
|
|
133
|
+
});
|
|
134
|
+
it('joins conference subconversation with external commit if group is already established and epoch is younger than one day', async () => {
|
|
135
|
+
jest.useFakeTimers();
|
|
136
|
+
const [subconversationService, { apiClient, mlsService }] = buildSubconversationService();
|
|
137
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
138
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
139
|
+
const subconversationEpoch = 1;
|
|
140
|
+
const currentTimeISO = '2023-10-24T12:00:00.000Z';
|
|
141
|
+
jest.setSystemTime(new Date(currentTimeISO));
|
|
142
|
+
jest.spyOn(apiClient.api.conversation, 'deleteSubconversation').mockResolvedValueOnce();
|
|
143
|
+
// epoch time is younger than 24h
|
|
144
|
+
const epochTimestamp = '2023-10-23T13:00:00.000Z';
|
|
145
|
+
const subconversationResponse = getSubconversationResponse({
|
|
146
|
+
epoch: subconversationEpoch,
|
|
147
|
+
epochTimestamp: epochTimestamp,
|
|
148
|
+
parentConversationId,
|
|
149
|
+
groupId: subconversationGroupId,
|
|
150
|
+
subconversationId: conversation_1.SUBCONVERSATION_ID.CONFERENCE,
|
|
151
|
+
});
|
|
152
|
+
jest.spyOn(apiClient.api.conversation, 'getSubconversation').mockResolvedValueOnce(subconversationResponse);
|
|
153
|
+
await subconversationService.joinConferenceSubconversation(parentConversationId);
|
|
154
|
+
expect(apiClient.api.conversation.deleteSubconversation).not.toHaveBeenCalled();
|
|
155
|
+
expect(mlsService.registerConversation).not.toHaveBeenCalled();
|
|
156
|
+
expect(mlsService.wipeConversation).not.toHaveBeenCalled();
|
|
157
|
+
expect(mlsService.joinByExternalCommit).toHaveBeenCalled();
|
|
158
|
+
});
|
|
159
|
+
it('retries to join if registering a conversations throws an error', async () => {
|
|
160
|
+
const [subconversationService, { apiClient, mlsService }] = buildSubconversationService();
|
|
161
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
162
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
163
|
+
const subconversationResponse = getSubconversationResponse({
|
|
164
|
+
epoch: 0,
|
|
165
|
+
epochTimestamp: '',
|
|
166
|
+
parentConversationId,
|
|
167
|
+
groupId: subconversationGroupId,
|
|
168
|
+
subconversationId: conversation_1.SUBCONVERSATION_ID.CONFERENCE,
|
|
169
|
+
});
|
|
170
|
+
jest.spyOn(apiClient.api.conversation, 'getSubconversation').mockResolvedValue(subconversationResponse);
|
|
171
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(false);
|
|
172
|
+
jest
|
|
173
|
+
.spyOn(mlsService, 'registerConversation')
|
|
174
|
+
.mockRejectedValueOnce(new http_1.BackendError('', http_1.BackendErrorLabel.MLS_STALE_MESSAGE, http_1.StatusCode.CONFLICT));
|
|
175
|
+
await subconversationService.joinConferenceSubconversation(parentConversationId);
|
|
176
|
+
expect(mlsService.wipeConversation).not.toHaveBeenCalled();
|
|
177
|
+
expect(mlsService.registerConversation).toHaveBeenCalledWith(subconversationGroupId, []);
|
|
178
|
+
expect(mlsService.registerConversation).toHaveBeenCalledTimes(2);
|
|
179
|
+
});
|
|
180
|
+
it('returns fresh epoch number after joining the group', async () => {
|
|
181
|
+
const [subconversationService, { apiClient, mlsService }] = buildSubconversationService();
|
|
182
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
183
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
184
|
+
const subconversationResponse = getSubconversationResponse({
|
|
185
|
+
epoch: 0,
|
|
186
|
+
epochTimestamp: '',
|
|
187
|
+
parentConversationId,
|
|
188
|
+
groupId: subconversationGroupId,
|
|
189
|
+
subconversationId: conversation_1.SUBCONVERSATION_ID.CONFERENCE,
|
|
190
|
+
});
|
|
191
|
+
jest.spyOn(apiClient.api.conversation, 'getSubconversation').mockResolvedValueOnce(subconversationResponse);
|
|
192
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(false);
|
|
193
|
+
const updatedEpoch = 1;
|
|
194
|
+
jest.spyOn(mlsService, 'getEpoch').mockResolvedValueOnce(updatedEpoch);
|
|
195
|
+
const response = await subconversationService.joinConferenceSubconversation(parentConversationId);
|
|
196
|
+
expect(mlsService.wipeConversation).not.toHaveBeenCalled();
|
|
197
|
+
expect(mlsService.registerConversation).toHaveBeenCalledWith(subconversationGroupId, []);
|
|
198
|
+
expect(response).toEqual({ epoch: updatedEpoch, groupId: subconversationGroupId });
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
describe('leaveConferenceSubconversation', () => {
|
|
202
|
+
it('does nothing if subconversation id is not found in the store', async () => {
|
|
203
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
204
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
205
|
+
await subconversationService.leaveConferenceSubconversation(parentConversationId);
|
|
206
|
+
expect(mlsService.wipeConversation).not.toHaveBeenCalled();
|
|
207
|
+
});
|
|
208
|
+
it('removes subconversation id from the store if conversations was known but not established locally', async () => {
|
|
209
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
210
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
211
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
212
|
+
subconversationGroupIdStore_1.subconversationGroupIdStore.storeGroupId(parentConversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE, subconversationGroupId);
|
|
213
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(false);
|
|
214
|
+
await subconversationService.leaveConferenceSubconversation(parentConversationId);
|
|
215
|
+
const groupId = subconversationGroupIdStore_1.subconversationGroupIdStore.getGroupId(parentConversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
216
|
+
expect(groupId).toEqual(undefined);
|
|
217
|
+
expect(mlsService.wipeConversation).not.toHaveBeenCalled();
|
|
218
|
+
});
|
|
219
|
+
it('deletes self client from conference subconversation', async () => {
|
|
220
|
+
const [subconversationService, { apiClient, mlsService }] = buildSubconversationService();
|
|
221
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
222
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
223
|
+
subconversationGroupIdStore_1.subconversationGroupIdStore.storeGroupId(parentConversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE, subconversationGroupId);
|
|
224
|
+
jest.spyOn(apiClient.api.conversation, 'deleteSubconversationSelf');
|
|
225
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(true);
|
|
226
|
+
await subconversationService.leaveConferenceSubconversation(parentConversationId);
|
|
227
|
+
const groupId = subconversationGroupIdStore_1.subconversationGroupIdStore.getGroupId(parentConversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
228
|
+
expect(groupId).toEqual(undefined);
|
|
229
|
+
expect(apiClient.api.conversation.deleteSubconversationSelf).toHaveBeenCalledWith(parentConversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
230
|
+
expect(mlsService.wipeConversation).toHaveBeenCalledWith(subconversationGroupId);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe('getSubconversationEpochInfo', () => {
|
|
234
|
+
it('returns null if subconversation id is not known by a client', async () => {
|
|
235
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
236
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
237
|
+
jest.spyOn(mlsService, 'getGroupIdFromConversationId').mockResolvedValue(undefined);
|
|
238
|
+
const response = await subconversationService.getSubconversationEpochInfo(parentConversationId);
|
|
239
|
+
expect(response).toEqual(null);
|
|
240
|
+
});
|
|
241
|
+
it('returns null if MLS group for subconversation does not exist locally', async () => {
|
|
242
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
243
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
244
|
+
const parentConversationGroupId = 'parentConversationGroupId';
|
|
245
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
246
|
+
jest
|
|
247
|
+
.spyOn(mlsService, 'getGroupIdFromConversationId')
|
|
248
|
+
.mockImplementation(async (_conversationId, subconverstionId) => {
|
|
249
|
+
if (subconverstionId) {
|
|
250
|
+
return subconversationGroupId;
|
|
251
|
+
}
|
|
252
|
+
return parentConversationGroupId;
|
|
253
|
+
});
|
|
254
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(false);
|
|
255
|
+
const response = await subconversationService.getSubconversationEpochInfo(parentConversationId);
|
|
256
|
+
expect(response).toEqual(null);
|
|
257
|
+
});
|
|
258
|
+
it('returns epoch info and advances epoch number', async () => {
|
|
259
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
260
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
261
|
+
const parentConversationGroupId = 'parentConversationGroupId';
|
|
262
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
263
|
+
jest
|
|
264
|
+
.spyOn(mlsService, 'getGroupIdFromConversationId')
|
|
265
|
+
.mockImplementation(async (_conversationId, subconverstionId) => {
|
|
266
|
+
if (subconverstionId) {
|
|
267
|
+
return subconversationGroupId;
|
|
268
|
+
}
|
|
269
|
+
return parentConversationGroupId;
|
|
270
|
+
});
|
|
271
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(true);
|
|
272
|
+
const mockedEpoch = 2;
|
|
273
|
+
jest.spyOn(mlsService, 'getEpoch').mockResolvedValueOnce(mockedEpoch);
|
|
274
|
+
const mockedSecretKey = 'mockedSecretKey';
|
|
275
|
+
jest.spyOn(mlsService, 'exportSecretKey').mockResolvedValueOnce(mockedSecretKey);
|
|
276
|
+
const subconversationMemberIds = [{ userId: 'userId1', clientId: 'clientId1', domain: 'domain' }];
|
|
277
|
+
const parentConversationMemberIds = [
|
|
278
|
+
{ userId: 'userId1', clientId: 'clientId1', domain: 'domain' },
|
|
279
|
+
{ userId: 'userId2', clientId: 'clientId2', domain: 'domain' },
|
|
280
|
+
];
|
|
281
|
+
jest.spyOn(mlsService, 'getClientIds').mockImplementation(async (groupId) => {
|
|
282
|
+
if (groupId === parentConversationGroupId) {
|
|
283
|
+
return parentConversationMemberIds;
|
|
284
|
+
}
|
|
285
|
+
return subconversationMemberIds;
|
|
286
|
+
});
|
|
287
|
+
const response = await subconversationService.getSubconversationEpochInfo(parentConversationId, true);
|
|
288
|
+
const expected = {
|
|
289
|
+
epoch: mockedEpoch,
|
|
290
|
+
keyLength: 32,
|
|
291
|
+
members: [
|
|
292
|
+
{ clientid: 'clientId1', in_subconv: true, userid: 'userId1@domain' },
|
|
293
|
+
{ clientid: 'clientId2', in_subconv: false, userid: 'userId2@domain' },
|
|
294
|
+
],
|
|
295
|
+
secretKey: mockedSecretKey,
|
|
296
|
+
};
|
|
297
|
+
expect(response).toEqual(expected);
|
|
298
|
+
expect(mlsService.renewKeyMaterial).toHaveBeenCalledWith(subconversationGroupId);
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
describe('subscribeToEpochUpdates', () => {
|
|
302
|
+
it('should subscribe to newEpoch event', async () => {
|
|
303
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
304
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
305
|
+
const parentConversationGroupId = 'parentConversationGroupId';
|
|
306
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
307
|
+
const mockedInitialEpoch = 1;
|
|
308
|
+
jest.spyOn(mlsService, 'getGroupIdFromConversationId').mockResolvedValue(subconversationGroupId);
|
|
309
|
+
const mockedEpochInfo = { epoch: mockedInitialEpoch, keyLength: 32, members: [], secretKey: '' };
|
|
310
|
+
jest.spyOn(subconversationService, 'getSubconversationEpochInfo').mockResolvedValueOnce(mockedEpochInfo);
|
|
311
|
+
jest
|
|
312
|
+
.spyOn(subconversationService, 'joinConferenceSubconversation')
|
|
313
|
+
.mockResolvedValue({ epoch: mockedInitialEpoch, groupId: subconversationGroupId });
|
|
314
|
+
const findConversationByGroupId = (groupId) => {
|
|
315
|
+
if (groupId === parentConversationGroupId) {
|
|
316
|
+
return parentConversationId;
|
|
317
|
+
}
|
|
318
|
+
return undefined;
|
|
319
|
+
};
|
|
320
|
+
const onEpochUpdateCallback = jest.fn();
|
|
321
|
+
const unsubscribe = await subconversationService.subscribeToEpochUpdates(parentConversationId, findConversationByGroupId, onEpochUpdateCallback);
|
|
322
|
+
expect(mlsService.on).toHaveBeenCalledWith('newEpoch', expect.any(Function));
|
|
323
|
+
expect(subconversationService.getSubconversationEpochInfo).toHaveBeenCalledWith(parentConversationId);
|
|
324
|
+
expect(onEpochUpdateCallback).toHaveBeenCalledWith(mockedEpochInfo);
|
|
325
|
+
unsubscribe();
|
|
326
|
+
expect(mlsService.off).toHaveBeenCalledWith('newEpoch', expect.any(Function));
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
describe('removeClientFromConferenceSubconversation', () => {
|
|
330
|
+
it('does nothing if subconversation group is not known', async () => {
|
|
331
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
332
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
333
|
+
jest.spyOn(mlsService, 'getGroupIdFromConversationId').mockResolvedValue(undefined);
|
|
334
|
+
const user = { id: 'userId', domain: 'domain' };
|
|
335
|
+
const clientId = 'clientId';
|
|
336
|
+
const clientToRemove = { user, clientId };
|
|
337
|
+
await subconversationService.removeClientFromConferenceSubconversation(parentConversationId, clientToRemove);
|
|
338
|
+
expect(mlsService.removeClientsFromConversation).not.toHaveBeenCalled();
|
|
339
|
+
});
|
|
340
|
+
it('does nothing if subconversation group is not established', async () => {
|
|
341
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
342
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
343
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
344
|
+
jest.spyOn(mlsService, 'getGroupIdFromConversationId').mockResolvedValue(subconversationGroupId);
|
|
345
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValue(false);
|
|
346
|
+
const user = { id: 'userId', domain: 'domain' };
|
|
347
|
+
const clientId = 'clientId';
|
|
348
|
+
const clientToRemove = { user, clientId };
|
|
349
|
+
await subconversationService.removeClientFromConferenceSubconversation(parentConversationId, clientToRemove);
|
|
350
|
+
expect(mlsService.removeClientsFromConversation).not.toHaveBeenCalled();
|
|
351
|
+
});
|
|
352
|
+
it('does nothing if client is not a subconversation group member', async () => {
|
|
353
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
354
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
355
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
356
|
+
jest.spyOn(mlsService, 'getGroupIdFromConversationId').mockResolvedValue(subconversationGroupId);
|
|
357
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValue(true);
|
|
358
|
+
const subconversationMemberIds = [
|
|
359
|
+
{ userId: 'userId1', clientId: 'clientId1', domain: 'domain' },
|
|
360
|
+
{ userId: 'userId2', clientId: 'clientId2', domain: 'domain2' },
|
|
361
|
+
];
|
|
362
|
+
jest.spyOn(mlsService, 'getClientIds').mockResolvedValueOnce(subconversationMemberIds);
|
|
363
|
+
const user = { id: 'userId3', domain: 'domain3' };
|
|
364
|
+
const clientId = 'clientId3';
|
|
365
|
+
const clientToRemove = { user, clientId };
|
|
366
|
+
await subconversationService.removeClientFromConferenceSubconversation(parentConversationId, clientToRemove);
|
|
367
|
+
expect(mlsService.removeClientsFromConversation).not.toHaveBeenCalled();
|
|
368
|
+
});
|
|
369
|
+
it('removes client from subconversation group', async () => {
|
|
370
|
+
const [subconversationService, { mlsService }] = buildSubconversationService();
|
|
371
|
+
const parentConversationId = { id: 'parentConversationId', domain: 'domain' };
|
|
372
|
+
const subconversationGroupId = 'subconversationGroupId';
|
|
373
|
+
jest.spyOn(mlsService, 'getGroupIdFromConversationId').mockResolvedValue(subconversationGroupId);
|
|
374
|
+
jest.spyOn(mlsService, 'conversationExists').mockResolvedValue(true);
|
|
375
|
+
const user = { id: 'userId3', domain: 'domain3' };
|
|
376
|
+
const clientId = 'clientId3';
|
|
377
|
+
const clientToRemove = { user, clientId };
|
|
378
|
+
const subconversationMemberIds = [
|
|
379
|
+
{ userId: 'userId1', clientId: 'clientId1', domain: 'domain' },
|
|
380
|
+
{ userId: 'userId2', clientId: 'clientId2', domain: 'domain2' },
|
|
381
|
+
{ userId: clientToRemove.user.id, clientId: clientToRemove.clientId, domain: clientToRemove.user.domain },
|
|
382
|
+
];
|
|
383
|
+
jest.spyOn(mlsService, 'getClientIds').mockResolvedValueOnce(subconversationMemberIds);
|
|
384
|
+
await subconversationService.removeClientFromConferenceSubconversation(parentConversationId, clientToRemove);
|
|
385
|
+
expect(mlsService.removeClientsFromConversation).toHaveBeenCalledWith(subconversationGroupId, [
|
|
386
|
+
(0, fullyQualifiedClientIdUtils_1.constructFullyQualifiedClientId)(clientToRemove.user.id, clientToRemove.clientId, clientToRemove.user.domain),
|
|
387
|
+
]);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { RegisteredClient } from '@wireapp/api-client/lib/client';
|
|
2
2
|
import { PostMlsMessageResponse, SUBCONVERSATION_ID } from '@wireapp/api-client/lib/conversation';
|
|
3
|
-
import { Subconversation } from '@wireapp/api-client/lib/conversation/Subconversation';
|
|
4
3
|
import { ConversationMLSMessageAddEvent, ConversationMLSWelcomeEvent } from '@wireapp/api-client/lib/event';
|
|
5
4
|
import { QualifiedId } from '@wireapp/api-client/lib/user';
|
|
6
5
|
import logdown from 'logdown';
|
|
@@ -20,11 +19,6 @@ interface LocalMLSServiceConfig extends MLSServiceConfig {
|
|
|
20
19
|
*/
|
|
21
20
|
minRequiredNumberOfAvailableKeyPackages: number;
|
|
22
21
|
}
|
|
23
|
-
export interface SubconversationEpochInfoMember {
|
|
24
|
-
userid: string;
|
|
25
|
-
clientid: ClientId;
|
|
26
|
-
in_subconv: boolean;
|
|
27
|
-
}
|
|
28
22
|
type Events = {
|
|
29
23
|
newEpoch: {
|
|
30
24
|
epoch: number;
|
|
@@ -60,25 +54,6 @@ export declare class MLSService extends TypedEventEmitter<Events> {
|
|
|
60
54
|
getEpoch(groupId: string | Uint8Array): Promise<number>;
|
|
61
55
|
newProposal(proposalType: ProposalType, args: ProposalArgs | AddProposalArgs | RemoveProposalArgs): Promise<import("@wireapp/core-crypto").ProposalBundle>;
|
|
62
56
|
joinByExternalCommit(getGroupInfo: () => Promise<Uint8Array>): Promise<PostMlsMessageResponse>;
|
|
63
|
-
getConferenceSubconversation(conversationId: QualifiedId): Promise<Subconversation>;
|
|
64
|
-
private deleteConferenceSubconversation;
|
|
65
|
-
/**
|
|
66
|
-
* Will leave conference subconversation if it's known by client and established.
|
|
67
|
-
*
|
|
68
|
-
* @param conversationId Id of the parent conversation which subconversation we want to leave
|
|
69
|
-
*/
|
|
70
|
-
leaveConferenceSubconversation(conversationId: QualifiedId): Promise<void>;
|
|
71
|
-
leaveStaleConferenceSubconversations(): Promise<void>;
|
|
72
|
-
/**
|
|
73
|
-
* Will join or register an mls subconversation for conference calls.
|
|
74
|
-
* Will return the secret key derived from the subconversation
|
|
75
|
-
*
|
|
76
|
-
* @param conversationId Id of the parent conversation in which the call should happen
|
|
77
|
-
*/
|
|
78
|
-
joinConferenceSubconversation(conversationId: QualifiedId): Promise<{
|
|
79
|
-
groupId: string;
|
|
80
|
-
epoch: number;
|
|
81
|
-
}>;
|
|
82
57
|
exportSecretKey(groupId: string, keyLength: number): Promise<string>;
|
|
83
58
|
newExternalProposal(externalProposalType: ExternalProposalType, args: ExternalAddProposalArgs): Promise<Uint8Array>;
|
|
84
59
|
processWelcomeMessage(welcomeMessage: Uint8Array): Promise<ConversationId>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MLSService.d.ts","sourceRoot":"","sources":["../../../../src/messagingProtocols/mls/MLSService/MLSService.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAqB,gBAAgB,EAAC,MAAM,gCAAgC,CAAC;AACzF,OAAO,EAAC,sBAAsB,EAAE,kBAAkB,EAAC,MAAM,sCAAsC,CAAC;AAChG,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"MLSService.d.ts","sourceRoot":"","sources":["../../../../src/messagingProtocols/mls/MLSService/MLSService.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,9 +33,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
35
|
exports.MLSService = exports.optionalToUint8Array = void 0;
|
|
36
|
-
const conversation_1 = require("@wireapp/api-client/lib/conversation");
|
|
37
36
|
const http_1 = require("@wireapp/api-client/lib/http");
|
|
38
|
-
const TimeUtil_1 = require("@wireapp/commons/lib/util/TimeUtil");
|
|
39
37
|
const bazinga64_1 = require("bazinga64");
|
|
40
38
|
const logdown_1 = __importDefault(require("logdown"));
|
|
41
39
|
const commons_1 = require("@wireapp/commons");
|
|
@@ -239,74 +237,6 @@ class MLSService extends commons_1.TypedEventEmitter {
|
|
|
239
237
|
}
|
|
240
238
|
return mlsResponse;
|
|
241
239
|
}
|
|
242
|
-
async getConferenceSubconversation(conversationId) {
|
|
243
|
-
return this.apiClient.api.conversation.getSubconversation(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
244
|
-
}
|
|
245
|
-
async deleteConferenceSubconversation(conversationId, data) {
|
|
246
|
-
return this.apiClient.api.conversation.deleteSubconversation(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE, data);
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Will leave conference subconversation if it's known by client and established.
|
|
250
|
-
*
|
|
251
|
-
* @param conversationId Id of the parent conversation which subconversation we want to leave
|
|
252
|
-
*/
|
|
253
|
-
async leaveConferenceSubconversation(conversationId) {
|
|
254
|
-
const subconversationGroupId = subconversationGroupIdStore_1.subconversationGroupIdStore.getGroupId(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
255
|
-
if (!subconversationGroupId) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
const isSubconversationEstablished = await this.conversationExists(subconversationGroupId);
|
|
259
|
-
if (!isSubconversationEstablished) {
|
|
260
|
-
// if the subconversation was known by a client but is not established anymore, we can remove it from the store
|
|
261
|
-
return subconversationGroupIdStore_1.subconversationGroupIdStore.removeGroupId(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
262
|
-
}
|
|
263
|
-
try {
|
|
264
|
-
await this.apiClient.api.conversation.deleteSubconversationSelf(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
265
|
-
}
|
|
266
|
-
catch (error) {
|
|
267
|
-
this.logger.error(`Failed to leave conference subconversation:`, error);
|
|
268
|
-
}
|
|
269
|
-
await this.wipeConversation(subconversationGroupId);
|
|
270
|
-
// once we've left the subconversation, we can remove it from the store
|
|
271
|
-
subconversationGroupIdStore_1.subconversationGroupIdStore.removeGroupId(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
272
|
-
}
|
|
273
|
-
async leaveStaleConferenceSubconversations() {
|
|
274
|
-
const conversationIds = subconversationGroupIdStore_1.subconversationGroupIdStore.getAllGroupIdsBySubconversationId(conversation_1.SUBCONVERSATION_ID.CONFERENCE);
|
|
275
|
-
for (const { parentConversationId } of conversationIds) {
|
|
276
|
-
await this.leaveConferenceSubconversation(parentConversationId);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Will join or register an mls subconversation for conference calls.
|
|
281
|
-
* Will return the secret key derived from the subconversation
|
|
282
|
-
*
|
|
283
|
-
* @param conversationId Id of the parent conversation in which the call should happen
|
|
284
|
-
*/
|
|
285
|
-
async joinConferenceSubconversation(conversationId) {
|
|
286
|
-
const subconversation = await this.getConferenceSubconversation(conversationId);
|
|
287
|
-
if (subconversation.epoch === 0) {
|
|
288
|
-
// if subconversation is not yet established, create it
|
|
289
|
-
await this.registerConversation(subconversation.group_id, []);
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
const epochUpdateTime = new Date(subconversation.epoch_timestamp).getTime();
|
|
293
|
-
const epochAge = new Date().getTime() - epochUpdateTime;
|
|
294
|
-
if (epochAge > TimeUtil_1.TimeInMillis.DAY) {
|
|
295
|
-
// if subconversation does exist, but it's older than 24h, delete and re-join
|
|
296
|
-
await this.deleteConferenceSubconversation(conversationId, {
|
|
297
|
-
groupId: subconversation.group_id,
|
|
298
|
-
epoch: subconversation.epoch,
|
|
299
|
-
});
|
|
300
|
-
await this.wipeConversation(subconversation.group_id);
|
|
301
|
-
return this.joinConferenceSubconversation(conversationId);
|
|
302
|
-
}
|
|
303
|
-
await this.joinByExternalCommit(() => this.apiClient.api.conversation.getSubconversationGroupInfo(conversationId, conversation_1.SUBCONVERSATION_ID.CONFERENCE));
|
|
304
|
-
}
|
|
305
|
-
const epoch = Number(await this.getEpoch(subconversation.group_id));
|
|
306
|
-
// We store the mapping between the subconversation and the parent conversation
|
|
307
|
-
subconversationGroupIdStore_1.subconversationGroupIdStore.storeGroupId(conversationId, subconversation.subconv_id, subconversation.group_id);
|
|
308
|
-
return { groupId: subconversation.group_id, epoch };
|
|
309
|
-
}
|
|
310
240
|
async exportSecretKey(groupId, keyLength) {
|
|
311
241
|
const groupIdBytes = bazinga64_1.Decoder.fromBase64(groupId).asBytes;
|
|
312
242
|
const key = await this.coreCryptoClient.exportSecretKey(groupIdBytes, keyLength);
|
package/package.json
CHANGED