@wireapp/core 29.2.4 → 30.0.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/CHANGELOG.md +62 -0
- package/package.json +3 -3
- package/src/main/Account.js +5 -1
- package/src/main/connection/ConnectionService.d.ts +2 -3
- package/src/main/connection/ConnectionService.js +1 -4
- package/src/main/conversation/ConversationService/ConversationService.d.ts +10 -4
- package/src/main/conversation/ConversationService/ConversationService.js +61 -10
- package/src/main/conversation/ConversationService/ConversationService.types.d.ts +5 -0
- package/src/main/conversation/content/AssetContent.d.ts +3 -3
- package/src/main/conversation/content/ContentType.d.ts +2 -2
- package/src/main/conversation/content/ConversationContent.d.ts +2 -2
- package/src/main/conversation/content/EditedTextContent.d.ts +1 -1
- package/src/main/conversation/content/LinkPreviewContent.d.ts +1 -1
- package/src/main/conversation/content/LocationContent.d.ts +1 -1
- package/src/main/conversation/content/QuoteContent.d.ts +1 -1
- package/src/main/conversation/content/ReactionContent.d.ts +1 -1
- package/src/main/conversation/content/TextContent.d.ts +1 -1
- package/src/main/cryptography/CryptographyDatabaseRepository.d.ts +2 -1
- package/src/main/cryptography/CryptographyDatabaseRepository.js +2 -8
- package/src/main/notification/NotificationDatabaseRepository.d.ts +23 -7
- package/src/main/notification/NotificationDatabaseRepository.js +42 -9
- package/src/main/notification/NotificationService.d.ts +28 -0
- package/src/main/notification/NotificationService.js +95 -6
- package/src/main/notification/types.d.ts +17 -0
- package/src/main/notification/types.js +21 -0
- package/src/main/user/UserService.d.ts +1 -18
- package/src/main/user/UserService.js +1 -42
- package/src/main/util/TaskScheduler/TaskScheduler.d.ts +10 -0
- package/src/main/util/TaskScheduler/TaskScheduler.js +70 -0
- package/src/main/util/mapQualifiedUserClientIdsToFullyQualifiedClientIds.d.ts +8 -0
- package/src/main/util/mapQualifiedUserClientIdsToFullyQualifiedClientIds.js +32 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,68 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [30.0.1](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@30.0.0...@wireapp/core@30.0.1) (2022-08-29)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @wireapp/core
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [30.0.0](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@29.3.0...@wireapp/core@30.0.0) (2022-08-25)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* remove user from MLS conversation (#FS-561) ([#4366](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/issues/4366)) ([89da444](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/commit/89da4449fa3caa0cbe41fbd595326f157e7e4011)), closes [#FS-561](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/issues/FS-561) [#4367](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/issues/4367)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### BREAKING CHANGES
|
|
23
|
+
|
|
24
|
+
* renamed removeUser function to removeUserFromProteusConversation in @wireapp/core and
|
|
25
|
+
changed the types of conversation and user ids in @wireapp/bot-api MessageHandler from string to QualifiedId.
|
|
26
|
+
|
|
27
|
+
* chore: initial work on remove user from mls convo
|
|
28
|
+
|
|
29
|
+
* fix: use FQCI ids for client ids
|
|
30
|
+
|
|
31
|
+
* fix: use FQCI ids for client ids
|
|
32
|
+
|
|
33
|
+
* runfix: encode client id straight to byte array
|
|
34
|
+
|
|
35
|
+
* runfix: encode client id straight to byte array
|
|
36
|
+
|
|
37
|
+
* feat: send client-removal commit messages
|
|
38
|
+
|
|
39
|
+
* runfix: remove event from remove user from proteus convo func
|
|
40
|
+
|
|
41
|
+
* refactor: rename proteus remove func
|
|
42
|
+
|
|
43
|
+
* test: add test for fully qualified ids mapping function
|
|
44
|
+
|
|
45
|
+
* refactor: more precise name for remove from proteus convo
|
|
46
|
+
|
|
47
|
+
* refactor: apply CR suggestions
|
|
48
|
+
|
|
49
|
+
* runfix: don't convert messages twice to uint8arr
|
|
50
|
+
|
|
51
|
+
* runfix: wrapp commits into uint8arr
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# [29.3.0](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@29.2.4...@wireapp/core@29.3.0) (2022-08-25)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### Features
|
|
61
|
+
|
|
62
|
+
* periodically commit proposals (fs-690) ([#4362](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/issues/4362)) ([d821f9e](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/commit/d821f9e43d35b4cb151588f5a7b18aa503e39410))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
6
68
|
## [29.2.4](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@29.2.3...@wireapp/core@29.2.4) (2022-08-23)
|
|
7
69
|
|
|
8
70
|
**Note:** Version bump only for package @wireapp/core
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"@otak/core-crypto": "0.3.0-es2017",
|
|
8
8
|
"@types/long": "4.0.1",
|
|
9
9
|
"@types/node": "~14",
|
|
10
|
-
"@wireapp/api-client": "
|
|
10
|
+
"@wireapp/api-client": "20.0.1",
|
|
11
11
|
"@wireapp/commons": "4.3.0",
|
|
12
12
|
"@wireapp/cryptobox": "12.8.0",
|
|
13
13
|
"@wireapp/store-engine-dexie": "1.6.10",
|
|
@@ -77,6 +77,6 @@
|
|
|
77
77
|
"test:node": "nyc jasmine --config=jasmine.json",
|
|
78
78
|
"watch": "tsc ---watch"
|
|
79
79
|
},
|
|
80
|
-
"version": "
|
|
81
|
-
"gitHead": "
|
|
80
|
+
"version": "30.0.1",
|
|
81
|
+
"gitHead": "5cdbd6fd089dd82b5df42480a967a6044b673951"
|
|
82
82
|
}
|
package/src/main/Account.js
CHANGED
|
@@ -137,10 +137,14 @@ class Account extends events_1.EventEmitter {
|
|
|
137
137
|
* @param cookie The cookie to identify the user against backend (will use the browser's one if not given)
|
|
138
138
|
*/
|
|
139
139
|
async init(clientType, cookie, initClient = true) {
|
|
140
|
+
var _a;
|
|
140
141
|
const context = await this.apiClient.init(clientType, cookie);
|
|
141
142
|
await this.initServices(context);
|
|
143
|
+
// Assumption: client gets only initialized once
|
|
142
144
|
if (initClient) {
|
|
143
145
|
await this.initClient({ clientType });
|
|
146
|
+
// initialize schedulers for pending mls proposals once client is initialized
|
|
147
|
+
await ((_a = this.service) === null || _a === void 0 ? void 0 : _a.notification.checkExistingPendingProposals());
|
|
144
148
|
}
|
|
145
149
|
return context;
|
|
146
150
|
}
|
|
@@ -237,7 +241,7 @@ class Account extends events_1.EventEmitter {
|
|
|
237
241
|
const selfService = new self_1.SelfService(this.apiClient);
|
|
238
242
|
const teamService = new team_1.TeamService(this.apiClient);
|
|
239
243
|
const broadcastService = new broadcast_1.BroadcastService(this.apiClient, cryptographyService);
|
|
240
|
-
const userService = new user_1.UserService(this.apiClient
|
|
244
|
+
const userService = new user_1.UserService(this.apiClient);
|
|
241
245
|
this.service = {
|
|
242
246
|
account: accountService,
|
|
243
247
|
asset: assetService,
|
|
@@ -4,8 +4,7 @@ import { QualifiedId } from '@wireapp/api-client/src/user';
|
|
|
4
4
|
export declare class ConnectionService {
|
|
5
5
|
private readonly apiClient;
|
|
6
6
|
constructor(apiClient: APIClient);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ignoreConnection(userId: string): Promise<Connection>;
|
|
7
|
+
acceptConnection(userId: QualifiedId): Promise<Connection>;
|
|
8
|
+
ignoreConnection(userId: QualifiedId): Promise<Connection>;
|
|
10
9
|
createConnection(userId: QualifiedId): Promise<Connection>;
|
|
11
10
|
}
|
|
@@ -24,9 +24,6 @@ class ConnectionService {
|
|
|
24
24
|
constructor(apiClient) {
|
|
25
25
|
this.apiClient = apiClient;
|
|
26
26
|
}
|
|
27
|
-
getConnections() {
|
|
28
|
-
return this.apiClient.api.connection.getAllConnections();
|
|
29
|
-
}
|
|
30
27
|
acceptConnection(userId) {
|
|
31
28
|
return this.apiClient.api.connection.putConnection(userId, {
|
|
32
29
|
status: connection_1.ConnectionStatus.ACCEPTED,
|
|
@@ -38,7 +35,7 @@ class ConnectionService {
|
|
|
38
35
|
});
|
|
39
36
|
}
|
|
40
37
|
createConnection(userId) {
|
|
41
|
-
return this.apiClient.api.connection.postConnection(userId
|
|
38
|
+
return this.apiClient.api.connection.postConnection(userId);
|
|
42
39
|
}
|
|
43
40
|
}
|
|
44
41
|
exports.ConnectionService = ConnectionService;
|
|
@@ -3,8 +3,8 @@ import type { APIClient } from '@wireapp/api-client';
|
|
|
3
3
|
import { MessageSendingStatus, Conversation, DefaultConversationRoleName, MutedStatus, NewConversation, QualifiedUserClients, UserClients, ClientMismatch } from '@wireapp/api-client/src/conversation';
|
|
4
4
|
import type { ConversationMemberLeaveEvent } from '@wireapp/api-client/src/event';
|
|
5
5
|
import type { QualifiedId, UserPreKeyBundleMap } from '@wireapp/api-client/src/user';
|
|
6
|
-
import { MessageTimer } from '../../conversation/';
|
|
7
|
-
import type { RemoteData } from '
|
|
6
|
+
import { MessageTimer, RemoveUsersParams } from '../../conversation/';
|
|
7
|
+
import type { RemoteData } from '../content';
|
|
8
8
|
import type { CryptographyService } from '../../cryptography/';
|
|
9
9
|
import type { ClearConversationMessage, DeleteMessage, HideMessage, OtrMessage } from '../message/OtrMessage';
|
|
10
10
|
import { XOR } from '@wireapp/commons/src/main/util/TypeUtil';
|
|
@@ -102,7 +102,11 @@ export declare class ConversationService {
|
|
|
102
102
|
getAllParticipantsClients(conversationId: string, conversationDomain?: string): Promise<UserClients | QualifiedUserClients>;
|
|
103
103
|
deleteMessageLocal(conversationId: string, messageIdToHide: string, sendAsProtobuf?: boolean, conversationDomain?: string): Promise<HideMessage>;
|
|
104
104
|
deleteMessageEveryone(conversationId: string, messageIdToDelete: string, userIds?: string[] | QualifiedId[] | UserClients | QualifiedUserClients, sendAsProtobuf?: boolean, conversationDomain?: string, callbacks?: MessageSendingCallbacks): Promise<DeleteMessage>;
|
|
105
|
-
leaveConversation(conversationId:
|
|
105
|
+
leaveConversation(conversationId: QualifiedId): Promise<ConversationMemberLeaveEvent>;
|
|
106
|
+
leaveMLSConversation(conversationId: QualifiedId): Promise<ConversationMemberLeaveEvent>;
|
|
107
|
+
/**
|
|
108
|
+
* @depricated seems not to be used and is outdated. use leaveConversation instead
|
|
109
|
+
*/
|
|
106
110
|
leaveConversations(conversationIds?: string[]): Promise<ConversationMemberLeaveEvent[]>;
|
|
107
111
|
/**
|
|
108
112
|
* Create a group conversation.
|
|
@@ -127,7 +131,7 @@ export declare class ConversationService {
|
|
|
127
131
|
getAsset({ assetId, assetToken, otrKey, sha256 }: RemoteData): Promise<Uint8Array>;
|
|
128
132
|
getUnencryptedAsset(assetId: string, assetToken?: string): Promise<ArrayBuffer>;
|
|
129
133
|
addUsersToProteusConversation({ conversationId, qualifiedUserIds }: Omit<AddUsersParams, 'groupId'>): Promise<import("@wireapp/api-client/src/event").ConversationMemberJoinEvent>;
|
|
130
|
-
|
|
134
|
+
removeUserFromProteusConversation(conversationId: QualifiedId, userId: QualifiedId): Promise<ConversationMemberLeaveEvent>;
|
|
131
135
|
private sendProteusMessage;
|
|
132
136
|
/**
|
|
133
137
|
* Sends a message to a conversation
|
|
@@ -151,4 +155,6 @@ export declare class ConversationService {
|
|
|
151
155
|
createMLSConversation(conversationData: NewConversation): Promise<MLSReturnType>;
|
|
152
156
|
private sendMLSMessage;
|
|
153
157
|
addUsersToMLSConversation({ qualifiedUserIds, groupId, conversationId, }: Required<AddUsersParams>): Promise<MLSReturnType>;
|
|
158
|
+
private sendCommitBundleRemovalMessages;
|
|
159
|
+
removeUsersFromMLSConversation({ groupId, conversationId, qualifiedUserIds, }: RemoveUsersParams): Promise<MLSReturnType>;
|
|
154
160
|
}
|
|
@@ -30,6 +30,12 @@ const MessageService_1 = require("../message/MessageService");
|
|
|
30
30
|
const MessageToProtoMapper_1 = require("../message/MessageToProtoMapper");
|
|
31
31
|
const ConversationService_types_1 = require("./ConversationService.types");
|
|
32
32
|
const bazinga64_1 = require("bazinga64");
|
|
33
|
+
const mapQualifiedUserClientIdsToFullyQualifiedClientIds_1 = require("../../util/mapQualifiedUserClientIdsToFullyQualifiedClientIds");
|
|
34
|
+
//@todo: this function is temporary, we wait for the update from core-crypto side
|
|
35
|
+
//they are returning regular array instead of Uint8Array for commit and welcome messages
|
|
36
|
+
const optionalToUint8Array = (array) => {
|
|
37
|
+
return Array.isArray(array) ? Uint8Array.from(array) : array;
|
|
38
|
+
};
|
|
33
39
|
class ConversationService {
|
|
34
40
|
constructor(apiClient, cryptographyService, config, coreCryptoClientProvider, notificationService) {
|
|
35
41
|
this.apiClient = apiClient;
|
|
@@ -598,8 +604,20 @@ class ConversationService {
|
|
|
598
604
|
};
|
|
599
605
|
}
|
|
600
606
|
leaveConversation(conversationId) {
|
|
601
|
-
|
|
607
|
+
if (!this.apiClient.context || !this.apiClient.context.userId || !this.apiClient.context.domain) {
|
|
608
|
+
throw new Error('Cannot leave conversation without a userId and domain');
|
|
609
|
+
}
|
|
610
|
+
return this.apiClient.api.conversation.deleteMember(conversationId, {
|
|
611
|
+
domain: this.apiClient.context.domain,
|
|
612
|
+
id: this.apiClient.context.userId,
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
leaveMLSConversation(conversationId) {
|
|
616
|
+
return this.leaveConversation(conversationId);
|
|
602
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* @depricated seems not to be used and is outdated. use leaveConversation instead
|
|
620
|
+
*/
|
|
603
621
|
async leaveConversations(conversationIds) {
|
|
604
622
|
if (!conversationIds) {
|
|
605
623
|
const conversation = await this.getConversations();
|
|
@@ -607,7 +625,7 @@ class ConversationService {
|
|
|
607
625
|
.filter(conversation => conversation.type === conversation_1.CONVERSATION_TYPE.REGULAR)
|
|
608
626
|
.map(conversation => conversation.id);
|
|
609
627
|
}
|
|
610
|
-
return Promise.all(conversationIds.map(conversationId => this.leaveConversation(conversationId)));
|
|
628
|
+
return Promise.all(conversationIds.map(conversationId => { var _a, _b; return this.leaveConversation({ id: conversationId, domain: (_b = (_a = this.apiClient.context) === null || _a === void 0 ? void 0 : _a.domain) !== null && _b !== void 0 ? _b : '' }); }));
|
|
611
629
|
}
|
|
612
630
|
createProteusConversation(conversationData, otherUserIds) {
|
|
613
631
|
let payload;
|
|
@@ -647,12 +665,10 @@ class ConversationService {
|
|
|
647
665
|
return (await request.response).buffer;
|
|
648
666
|
}
|
|
649
667
|
async addUsersToProteusConversation({ conversationId, qualifiedUserIds }) {
|
|
650
|
-
|
|
651
|
-
return response;
|
|
668
|
+
return this.apiClient.api.conversation.postMembers(conversationId, qualifiedUserIds);
|
|
652
669
|
}
|
|
653
|
-
async
|
|
654
|
-
|
|
655
|
-
return userId;
|
|
670
|
+
async removeUserFromProteusConversation(conversationId, userId) {
|
|
671
|
+
return this.apiClient.api.conversation.deleteMember(conversationId, userId);
|
|
656
672
|
}
|
|
657
673
|
async sendProteusMessage(params, genericMessage, content) {
|
|
658
674
|
var _a;
|
|
@@ -822,10 +838,13 @@ class ConversationService {
|
|
|
822
838
|
const coreCryptoClient = this.coreCryptoClientProvider();
|
|
823
839
|
const memberAddedMessages = await coreCryptoClient.addClientsToConversation(groupIdDecodedFromBase64, invitee);
|
|
824
840
|
if (memberAddedMessages === null || memberAddedMessages === void 0 ? void 0 : memberAddedMessages.welcome) {
|
|
825
|
-
|
|
841
|
+
//@todo: it's temporary - we wait for core-crypto fix to return the actual Uint8Array instead of regular array
|
|
842
|
+
await this.apiClient.api.conversation.postMlsWelcomeMessage(optionalToUint8Array(memberAddedMessages.welcome));
|
|
826
843
|
}
|
|
827
844
|
if (memberAddedMessages === null || memberAddedMessages === void 0 ? void 0 : memberAddedMessages.commit) {
|
|
828
|
-
const messageResponse = await this.apiClient.api.conversation.postMlsMessage(
|
|
845
|
+
const messageResponse = await this.apiClient.api.conversation.postMlsMessage(
|
|
846
|
+
//@todo: it's temporary - we wait for core-crypto fix to return the actual Uint8Array instead of regular array
|
|
847
|
+
optionalToUint8Array(memberAddedMessages.commit));
|
|
829
848
|
await coreCryptoClient.commitAccepted(groupIdDecodedFromBase64);
|
|
830
849
|
return messageResponse;
|
|
831
850
|
}
|
|
@@ -839,6 +858,9 @@ class ConversationService {
|
|
|
839
858
|
*/
|
|
840
859
|
const newConversation = await this.apiClient.api.conversation.postConversation(Object.assign(Object.assign({}, conversationData), { users: undefined, qualified_users: undefined }));
|
|
841
860
|
const { group_id: groupId, qualified_id: qualifiedId } = newConversation;
|
|
861
|
+
if (!groupId) {
|
|
862
|
+
throw new Error('No group_id found in response which is required for creating MLS conversations.');
|
|
863
|
+
}
|
|
842
864
|
const groupIdDecodedFromBase64 = bazinga64_1.Decoder.fromBase64(groupId).asBytes;
|
|
843
865
|
const { qualified_users: qualifiedUsers = [], selfUserId } = conversationData;
|
|
844
866
|
if (!selfUserId) {
|
|
@@ -871,6 +893,8 @@ class ConversationService {
|
|
|
871
893
|
var _a, _b;
|
|
872
894
|
const { groupId, onSuccess, payload } = params;
|
|
873
895
|
const groupIdBytes = bazinga64_1.Decoder.fromBase64(groupId).asBytes;
|
|
896
|
+
// immediately execute pending commits before sending the message
|
|
897
|
+
await this.notificationService.commitPendingProposals({ groupId });
|
|
874
898
|
const coreCryptoClient = this.coreCryptoClientProvider();
|
|
875
899
|
const encrypted = await coreCryptoClient.encryptMessage(groupIdBytes, protocol_messaging_1.GenericMessage.encode(genericMessage).finish());
|
|
876
900
|
try {
|
|
@@ -886,13 +910,40 @@ class ConversationService {
|
|
|
886
910
|
const groupIdDecodedFromBase64 = bazinga64_1.Decoder.fromBase64(groupId).asBytes;
|
|
887
911
|
const coreCryptoKeyPackagesPayload = await this.getCoreCryptoKeyPackagesPayload([...qualifiedUserIds]);
|
|
888
912
|
const response = await this.addUsersToExistingMLSConversation(groupIdDecodedFromBase64, coreCryptoKeyPackagesPayload);
|
|
889
|
-
console.info('addUsersToMLSGroup', conversationId, qualifiedUserIds, groupIdDecodedFromBase64, response);
|
|
890
913
|
const conversation = await this.getConversations(conversationId.id);
|
|
891
914
|
return {
|
|
892
915
|
events: (response === null || response === void 0 ? void 0 : response.events) || [],
|
|
893
916
|
conversation,
|
|
894
917
|
};
|
|
895
918
|
}
|
|
919
|
+
async sendCommitBundleRemovalMessages(groupIdDecodedFromBase64, commitBundle) {
|
|
920
|
+
const coreCryptoClient = this.coreCryptoClientProvider();
|
|
921
|
+
if (commitBundle === null || commitBundle === void 0 ? void 0 : commitBundle.welcome) {
|
|
922
|
+
//@todo: it's temporary - we wait for core-crypto fix to return the actual Uint8Array instead of regular array
|
|
923
|
+
await this.apiClient.api.conversation.postMlsWelcomeMessage(optionalToUint8Array(commitBundle.welcome));
|
|
924
|
+
}
|
|
925
|
+
if (commitBundle === null || commitBundle === void 0 ? void 0 : commitBundle.commit) {
|
|
926
|
+
const messageResponse = await this.apiClient.api.conversation.postMlsMessage(
|
|
927
|
+
//@todo: it's temporary - we wait for core-crypto fix to return the actual Uint8Array instead of regular array
|
|
928
|
+
optionalToUint8Array(commitBundle.commit));
|
|
929
|
+
await coreCryptoClient.commitAccepted(groupIdDecodedFromBase64);
|
|
930
|
+
return messageResponse;
|
|
931
|
+
}
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
async removeUsersFromMLSConversation({ groupId, conversationId, qualifiedUserIds, }) {
|
|
935
|
+
const coreCryptoClient = this.coreCryptoClientProvider();
|
|
936
|
+
const groupIdDecodedFromBase64 = bazinga64_1.Decoder.fromBase64(groupId).asBytes;
|
|
937
|
+
const clientsToRemove = await this.apiClient.api.user.postListClients({ qualified_users: qualifiedUserIds });
|
|
938
|
+
const fullyQualifiedClientIds = (0, mapQualifiedUserClientIdsToFullyQualifiedClientIds_1.mapQualifiedUserClientIdsToFullyQualifiedClientIds)(clientsToRemove.qualified_user_map);
|
|
939
|
+
const commitBundle = await coreCryptoClient.removeClientsFromConversation(groupIdDecodedFromBase64, fullyQualifiedClientIds);
|
|
940
|
+
const messageResponse = await this.sendCommitBundleRemovalMessages(groupIdDecodedFromBase64, commitBundle);
|
|
941
|
+
const conversation = await this.getConversations(conversationId.id);
|
|
942
|
+
return {
|
|
943
|
+
events: (messageResponse === null || messageResponse === void 0 ? void 0 : messageResponse.events) || [],
|
|
944
|
+
conversation,
|
|
945
|
+
};
|
|
946
|
+
}
|
|
896
947
|
}
|
|
897
948
|
exports.ConversationService = ConversationService;
|
|
898
949
|
//# sourceMappingURL=ConversationService.js.map
|
|
@@ -97,6 +97,11 @@ export declare type AddUsersParams = {
|
|
|
97
97
|
qualifiedUserIds: QualifiedId[];
|
|
98
98
|
groupId?: string;
|
|
99
99
|
};
|
|
100
|
+
export declare type RemoveUsersParams = {
|
|
101
|
+
conversationId: QualifiedId;
|
|
102
|
+
qualifiedUserIds: QualifiedId[];
|
|
103
|
+
groupId: string;
|
|
104
|
+
};
|
|
100
105
|
export declare type MLSReturnType = {
|
|
101
106
|
events: MlsEvent[];
|
|
102
107
|
conversation: Conversation;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import type { Asset } from '@wireapp/protocol-messaging';
|
|
3
|
-
import type { AbortReason, AssetTransferState } from '
|
|
4
|
-
import type { FileContent, FileMetaDataContent, ImageContent, LegalHoldStatus } from '
|
|
5
|
-
import type { EncryptedAssetUploaded } from '../../cryptography
|
|
3
|
+
import type { AbortReason, AssetTransferState } from '..';
|
|
4
|
+
import type { FileContent, FileMetaDataContent, ImageContent, LegalHoldStatus } from '.';
|
|
5
|
+
import type { EncryptedAssetUploaded } from '../../cryptography';
|
|
6
6
|
export declare type ImageMetaData = Asset.IImageMetaData;
|
|
7
7
|
export declare type VideoMetaData = Asset.IVideoMetaData;
|
|
8
8
|
export declare type Preview = Asset.IPreview;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Connection } from '@wireapp/api-client/src/connection/';
|
|
2
|
-
import type { ClientActionType } from '
|
|
3
|
-
import type { AssetContent, ClearedContent, ClientActionContent, ConfirmationContent, ConversationContent, DeletedContent, EditedTextContent, FileAssetAbortContent, FileAssetContent, FileAssetMetaDataContent, HiddenContent, ImageAssetContent, ImageContent, LocationContent, ReactionContent, TextContent } from '
|
|
2
|
+
import type { ClientActionType } from '..';
|
|
3
|
+
import type { AssetContent, ClearedContent, ClientActionContent, ConfirmationContent, ConversationContent, DeletedContent, EditedTextContent, FileAssetAbortContent, FileAssetContent, FileAssetMetaDataContent, HiddenContent, ImageAssetContent, ImageContent, LocationContent, ReactionContent, TextContent } from '.';
|
|
4
4
|
export declare function isAbortedAssetContent(content: ConversationContent): content is AssetContent;
|
|
5
5
|
export declare function isAssetContent(content: ConversationContent): content is AssetContent;
|
|
6
6
|
export declare function isClearedContent(content: ConversationContent): content is ClearedContent;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { ClientActionType } from '
|
|
2
|
-
import type { AssetContent, ButtonActionContent, ButtonActionConfirmationContent, CallingContent, ClearedContent, ClientActionContent, ClientAddContent, ClientRemoveContent, CompositeContent, ConfirmationContent, ConnectionContent, DeletedContent, EditedTextContent, FileAssetAbortContent, FileAssetContent, FileAssetMetaDataContent, HiddenContent, ImageAssetContent, ImageContent, KnockContent, LocationContent, ReactionContent, TextContent } from '
|
|
1
|
+
import type { ClientActionType } from '..';
|
|
2
|
+
import type { AssetContent, ButtonActionContent, ButtonActionConfirmationContent, CallingContent, ClearedContent, ClientActionContent, ClientAddContent, ClientRemoveContent, CompositeContent, ConfirmationContent, ConnectionContent, DeletedContent, EditedTextContent, FileAssetAbortContent, FileAssetContent, FileAssetMetaDataContent, HiddenContent, ImageAssetContent, ImageContent, KnockContent, LocationContent, ReactionContent, TextContent } from '.';
|
|
3
3
|
export declare type ConversationContent = AssetContent | ButtonActionContent | ButtonActionConfirmationContent | CallingContent | ClearedContent | ClientActionContent | ClientActionType | ClientAddContent | ClientRemoveContent | CompositeContent | ConfirmationContent | ConnectionContent | DeletedContent | EditedTextContent | FileAssetAbortContent | FileAssetContent | FileAssetMetaDataContent | HiddenContent | ImageAssetContent | ImageContent | KnockContent | LocationContent | ReactionContent | TextContent;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ILinkPreview } from '@wireapp/protocol-messaging';
|
|
2
|
-
import type { ImageAssetContent, ImageContent, LegalHoldStatus } from '
|
|
2
|
+
import type { ImageAssetContent, ImageContent, LegalHoldStatus } from '.';
|
|
3
3
|
export interface LinkPreviewContent extends Omit<ILinkPreview, 'image'> {
|
|
4
4
|
expectsReadConfirmation?: boolean;
|
|
5
5
|
legalHoldStatus?: LegalHoldStatus;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IQuote } from '@wireapp/protocol-messaging';
|
|
2
|
-
import type { AssetContent, LocationContent, TextContent } from '
|
|
2
|
+
import type { AssetContent, LocationContent, TextContent } from '.';
|
|
3
3
|
export { IQuote as QuoteContent };
|
|
4
4
|
export interface QuoteMessageContent {
|
|
5
5
|
content: AssetContent | LocationContent | TextContent;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LegalHoldStatus, LinkPreviewUploadedContent, MentionContent, QuoteContent } from '
|
|
1
|
+
import type { LegalHoldStatus, LinkPreviewUploadedContent, MentionContent, QuoteContent } from '.';
|
|
2
2
|
export interface TextContent {
|
|
3
3
|
expectsReadConfirmation?: boolean;
|
|
4
4
|
legalHoldStatus?: LegalHoldStatus;
|
|
@@ -5,7 +5,8 @@ export declare enum DatabaseStores {
|
|
|
5
5
|
KEYS = "keys",
|
|
6
6
|
PRE_KEYS = "prekeys",
|
|
7
7
|
SESSIONS = "sessions",
|
|
8
|
-
GROUP_IDS = "group_ids"
|
|
8
|
+
GROUP_IDS = "group_ids",
|
|
9
|
+
PENDING_PROPOSALS = "pending_proposals"
|
|
9
10
|
}
|
|
10
11
|
export declare class CryptographyDatabaseRepository {
|
|
11
12
|
private readonly storeEngine;
|
|
@@ -27,20 +27,14 @@ var DatabaseStores;
|
|
|
27
27
|
DatabaseStores["PRE_KEYS"] = "prekeys";
|
|
28
28
|
DatabaseStores["SESSIONS"] = "sessions";
|
|
29
29
|
DatabaseStores["GROUP_IDS"] = "group_ids";
|
|
30
|
+
DatabaseStores["PENDING_PROPOSALS"] = "pending_proposals";
|
|
30
31
|
})(DatabaseStores = exports.DatabaseStores || (exports.DatabaseStores = {}));
|
|
31
32
|
class CryptographyDatabaseRepository {
|
|
32
33
|
constructor(storeEngine) {
|
|
33
34
|
this.storeEngine = storeEngine;
|
|
34
35
|
}
|
|
35
36
|
deleteStores() {
|
|
36
|
-
return Promise.all(
|
|
37
|
-
this.storeEngine.deleteAll(CryptographyDatabaseRepository.STORES.AMPLIFY),
|
|
38
|
-
this.storeEngine.deleteAll(CryptographyDatabaseRepository.STORES.CLIENTS),
|
|
39
|
-
this.storeEngine.deleteAll(CryptographyDatabaseRepository.STORES.KEYS),
|
|
40
|
-
this.storeEngine.deleteAll(CryptographyDatabaseRepository.STORES.SESSIONS),
|
|
41
|
-
this.storeEngine.deleteAll(CryptographyDatabaseRepository.STORES.PRE_KEYS),
|
|
42
|
-
this.storeEngine.deleteAll(CryptographyDatabaseRepository.STORES.GROUP_IDS),
|
|
43
|
-
]);
|
|
37
|
+
return Promise.all(Object.keys(CryptographyDatabaseRepository.STORES).map(store => this.storeEngine.deleteAll(store)));
|
|
44
38
|
}
|
|
45
39
|
}
|
|
46
40
|
exports.CryptographyDatabaseRepository = CryptographyDatabaseRepository;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { BackendEvent } from '@wireapp/api-client/src/event';
|
|
2
2
|
import type { Notification } from '@wireapp/api-client/src/notification/';
|
|
3
3
|
import type { CRUDEngine } from '@wireapp/store-engine';
|
|
4
|
-
|
|
5
|
-
groupId: string;
|
|
6
|
-
conversationId: string;
|
|
7
|
-
conversationDomain: string;
|
|
8
|
-
};
|
|
4
|
+
import { CommonMLS, CompoundGroupIdParams, StorePendingProposalsParams } from './types';
|
|
9
5
|
export declare enum DatabaseStores {
|
|
10
6
|
EVENTS = "events"
|
|
11
7
|
}
|
|
@@ -23,7 +19,27 @@ export declare class NotificationDatabaseRepository {
|
|
|
23
19
|
getLastNotificationId(): Promise<string>;
|
|
24
20
|
updateLastNotificationId(lastNotification: Notification): Promise<string>;
|
|
25
21
|
private generateCompoundGroupIdPrimaryKey;
|
|
26
|
-
addCompoundGroupId(params: CompoundGroupIdParams): Promise<
|
|
22
|
+
addCompoundGroupId(params: CompoundGroupIdParams): Promise<CompoundGroupIdParams>;
|
|
27
23
|
getCompoundGroupId(params: Omit<CompoundGroupIdParams, 'groupId'>): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* ## MLS only ##
|
|
26
|
+
* Store groupIds with pending proposals and a delay in the DB until the proposals get committed.
|
|
27
|
+
*
|
|
28
|
+
* @param groupId groupId of the mls conversation
|
|
29
|
+
* @param firingDate date when the pending proposals should be committed
|
|
30
|
+
*/
|
|
31
|
+
storePendingProposal(params: StorePendingProposalsParams): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* ## MLS only ##
|
|
34
|
+
* Delete stored entries for pending proposals that have been committed.
|
|
35
|
+
*
|
|
36
|
+
* @param groupId groupId of the mls conversation
|
|
37
|
+
*/
|
|
38
|
+
deletePendingProposal({ groupId }: CommonMLS): Promise<boolean>;
|
|
39
|
+
/**
|
|
40
|
+
* ## MLS only ##
|
|
41
|
+
* Get all stored entries for pending proposals.
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
44
|
+
getStoredPendingProposals(): Promise<StorePendingProposalsParams[]>;
|
|
28
45
|
}
|
|
29
|
-
export {};
|
|
@@ -29,8 +29,7 @@ var DatabaseKeys;
|
|
|
29
29
|
DatabaseKeys["PRIMARY_KEY_LAST_EVENT"] = "z.storage.StorageKey.EVENT.LAST_DATE";
|
|
30
30
|
DatabaseKeys["PRIMARY_KEY_LAST_NOTIFICATION"] = "z.storage.StorageKey.NOTIFICATION.LAST_ID";
|
|
31
31
|
})(DatabaseKeys = exports.DatabaseKeys || (exports.DatabaseKeys = {}));
|
|
32
|
-
const
|
|
33
|
-
const STORE_GROUPIDS = CryptographyDatabaseRepository_1.CryptographyDatabaseRepository.STORES.GROUP_IDS;
|
|
32
|
+
const STORES = Object.assign({}, CryptographyDatabaseRepository_1.CryptographyDatabaseRepository.STORES);
|
|
34
33
|
class NotificationDatabaseRepository {
|
|
35
34
|
constructor(storeEngine) {
|
|
36
35
|
this.storeEngine = storeEngine;
|
|
@@ -39,23 +38,27 @@ class NotificationDatabaseRepository {
|
|
|
39
38
|
return this.storeEngine.readAll(DatabaseStores.EVENTS);
|
|
40
39
|
}
|
|
41
40
|
async getLastEventDate() {
|
|
42
|
-
const { value } = await this.storeEngine.read(
|
|
41
|
+
const { value } = await this.storeEngine.read(STORES.AMPLIFY, DatabaseKeys.PRIMARY_KEY_LAST_EVENT);
|
|
43
42
|
return new Date(value);
|
|
44
43
|
}
|
|
45
44
|
async updateLastEventDate(eventDate) {
|
|
46
|
-
await this.storeEngine.update(
|
|
45
|
+
await this.storeEngine.update(STORES.AMPLIFY, DatabaseKeys.PRIMARY_KEY_LAST_EVENT, {
|
|
46
|
+
value: eventDate.toISOString(),
|
|
47
|
+
});
|
|
47
48
|
return eventDate;
|
|
48
49
|
}
|
|
49
50
|
async createLastEventDate(eventDate) {
|
|
50
|
-
await this.storeEngine.create(
|
|
51
|
+
await this.storeEngine.create(STORES.AMPLIFY, DatabaseKeys.PRIMARY_KEY_LAST_EVENT, {
|
|
52
|
+
value: eventDate.toISOString(),
|
|
53
|
+
});
|
|
51
54
|
return eventDate;
|
|
52
55
|
}
|
|
53
56
|
async getLastNotificationId() {
|
|
54
|
-
const { value } = await this.storeEngine.read(
|
|
57
|
+
const { value } = await this.storeEngine.read(STORES.AMPLIFY, DatabaseKeys.PRIMARY_KEY_LAST_NOTIFICATION);
|
|
55
58
|
return value;
|
|
56
59
|
}
|
|
57
60
|
async updateLastNotificationId(lastNotification) {
|
|
58
|
-
await this.storeEngine.updateOrCreate(
|
|
61
|
+
await this.storeEngine.updateOrCreate(STORES.AMPLIFY, DatabaseKeys.PRIMARY_KEY_LAST_NOTIFICATION, {
|
|
59
62
|
value: lastNotification.id,
|
|
60
63
|
});
|
|
61
64
|
return lastNotification.id;
|
|
@@ -64,10 +67,40 @@ class NotificationDatabaseRepository {
|
|
|
64
67
|
return `${conversationId}@${conversationDomain}`;
|
|
65
68
|
}
|
|
66
69
|
async addCompoundGroupId(params) {
|
|
67
|
-
await this.storeEngine.updateOrCreate(
|
|
70
|
+
await this.storeEngine.updateOrCreate(STORES.GROUP_IDS, this.generateCompoundGroupIdPrimaryKey(params), params.groupId);
|
|
71
|
+
return params;
|
|
68
72
|
}
|
|
69
73
|
async getCompoundGroupId(params) {
|
|
70
|
-
return this.storeEngine.read(
|
|
74
|
+
return this.storeEngine.read(STORES.GROUP_IDS, this.generateCompoundGroupIdPrimaryKey(params));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* ## MLS only ##
|
|
78
|
+
* Store groupIds with pending proposals and a delay in the DB until the proposals get committed.
|
|
79
|
+
*
|
|
80
|
+
* @param groupId groupId of the mls conversation
|
|
81
|
+
* @param firingDate date when the pending proposals should be committed
|
|
82
|
+
*/
|
|
83
|
+
async storePendingProposal(params) {
|
|
84
|
+
await this.storeEngine.updateOrCreate(STORES.PENDING_PROPOSALS, `${params.groupId}`, params);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* ## MLS only ##
|
|
89
|
+
* Delete stored entries for pending proposals that have been committed.
|
|
90
|
+
*
|
|
91
|
+
* @param groupId groupId of the mls conversation
|
|
92
|
+
*/
|
|
93
|
+
async deletePendingProposal({ groupId }) {
|
|
94
|
+
await this.storeEngine.delete(STORES.PENDING_PROPOSALS, `${groupId}`);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* ## MLS only ##
|
|
99
|
+
* Get all stored entries for pending proposals.
|
|
100
|
+
*
|
|
101
|
+
*/
|
|
102
|
+
async getStoredPendingProposals() {
|
|
103
|
+
return this.storeEngine.readAll(STORES.PENDING_PROPOSALS);
|
|
71
104
|
}
|
|
72
105
|
}
|
|
73
106
|
exports.NotificationDatabaseRepository = NotificationDatabaseRepository;
|
|
@@ -12,6 +12,7 @@ import { AbortHandler } from '@wireapp/api-client/src/tcp';
|
|
|
12
12
|
import type { CoreCrypto } from '@otak/core-crypto/platforms/web/corecrypto';
|
|
13
13
|
import { QualifiedId } from '@wireapp/api-client/src/user';
|
|
14
14
|
import { Conversation } from '@wireapp/api-client/src/conversation';
|
|
15
|
+
import { CommitPendingProposalsParams } from './types';
|
|
15
16
|
export declare type HandledEventPayload = {
|
|
16
17
|
event: Events.BackendEvent;
|
|
17
18
|
mappedEvent?: PayloadBundle;
|
|
@@ -61,6 +62,7 @@ export declare class NotificationService extends EventEmitter {
|
|
|
61
62
|
private cleanupPayloadBundle;
|
|
62
63
|
private handleEvent;
|
|
63
64
|
/**
|
|
65
|
+
* ## MLS only ##
|
|
64
66
|
* If there is a groupId in the conversation, we need to store the conversationId => groupId pair
|
|
65
67
|
* in order to find the groupId when decrypting messages
|
|
66
68
|
* This is a bit hacky but since mls messages do not embed the groupId we need to keep a mapping of those
|
|
@@ -69,11 +71,37 @@ export declare class NotificationService extends EventEmitter {
|
|
|
69
71
|
*/
|
|
70
72
|
saveConversationGroupId(conversation: Conversation): Promise<void>;
|
|
71
73
|
/**
|
|
74
|
+
* ## MLS only ##
|
|
72
75
|
* If there is a matching conversationId => groupId pair in the database,
|
|
73
76
|
* we can find the groupId and return it as a Uint8Array
|
|
74
77
|
*
|
|
75
78
|
* @param conversationQualifiedId
|
|
76
79
|
*/
|
|
77
80
|
getUint8ArrayFromConversationGroupId(conversationQualifiedId: QualifiedId): Promise<Uint8Array>;
|
|
81
|
+
/**
|
|
82
|
+
* ## MLS only ##
|
|
83
|
+
* If there are pending proposals, we need to either process them,
|
|
84
|
+
* or save them in the database for later processing
|
|
85
|
+
*
|
|
86
|
+
* @param groupId groupId of the mls conversation
|
|
87
|
+
* @param delayInMs delay in ms before processing proposals
|
|
88
|
+
* @param eventTime time of the event that had the proposals
|
|
89
|
+
*/
|
|
90
|
+
private handlePendingProposals;
|
|
91
|
+
/**
|
|
92
|
+
* ## MLS only ##
|
|
93
|
+
* Commit all pending proposals for a given groupId
|
|
94
|
+
*
|
|
95
|
+
* @param groupId groupId of the conversation
|
|
96
|
+
* @param skipDelete if true, do not delete the pending proposals from the database
|
|
97
|
+
*/
|
|
98
|
+
commitPendingProposals({ groupId, skipDelete }: CommitPendingProposalsParams): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* ## MLS only ##
|
|
101
|
+
* Get all pending proposals from the database and schedule them
|
|
102
|
+
* Function must only be called once, after application start
|
|
103
|
+
*
|
|
104
|
+
*/
|
|
105
|
+
checkExistingPendingProposals(): Promise<void>;
|
|
78
106
|
}
|
|
79
107
|
export {};
|
|
@@ -65,6 +65,7 @@ const NotificationBackendRepository_1 = require("./NotificationBackendRepository
|
|
|
65
65
|
const NotificationDatabaseRepository_1 = require("./NotificationDatabaseRepository");
|
|
66
66
|
const protocol_messaging_1 = require("@wireapp/protocol-messaging");
|
|
67
67
|
const bazinga64_1 = require("bazinga64");
|
|
68
|
+
const TaskScheduler_1 = require("../util/TaskScheduler/TaskScheduler");
|
|
68
69
|
var TOPIC;
|
|
69
70
|
(function (TOPIC) {
|
|
70
71
|
TOPIC["NOTIFICATION_ERROR"] = "NotificationService.TOPIC.NOTIFICATION_ERROR";
|
|
@@ -209,7 +210,7 @@ class NotificationService extends events_1.EventEmitter {
|
|
|
209
210
|
}
|
|
210
211
|
}
|
|
211
212
|
async handleEvent(event, source, dryRun = false) {
|
|
212
|
-
var _a;
|
|
213
|
+
var _a, _b;
|
|
213
214
|
const coreCryptoClient = this.coreCryptoClientProvider();
|
|
214
215
|
if (!coreCryptoClient) {
|
|
215
216
|
throw new Error('Unable to access core crypto client');
|
|
@@ -227,12 +228,20 @@ class NotificationService extends events_1.EventEmitter {
|
|
|
227
228
|
};
|
|
228
229
|
case Events.CONVERSATION_EVENT.MLS_MESSAGE_ADD:
|
|
229
230
|
const encryptedData = bazinga64_1.Decoder.fromBase64(event.data).asBytes;
|
|
230
|
-
const groupId = await this.getUint8ArrayFromConversationGroupId(event.qualified_conversation
|
|
231
|
-
|
|
232
|
-
|
|
231
|
+
const groupId = await this.getUint8ArrayFromConversationGroupId((_a = event.qualified_conversation) !== null && _a !== void 0 ? _a : { id: event.conversation, domain: '' });
|
|
232
|
+
// Check if the message includes proposals
|
|
233
|
+
const { proposals, commitDelay, message } = await coreCryptoClient.decryptMessage(groupId, encryptedData);
|
|
234
|
+
if (proposals.length > 0) {
|
|
235
|
+
await this.handlePendingProposals({
|
|
236
|
+
groupId: groupId.toString(),
|
|
237
|
+
delayInMs: commitDelay !== null && commitDelay !== void 0 ? commitDelay : 0,
|
|
238
|
+
eventTime: event.time,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
if (!message) {
|
|
233
242
|
throw new Error(`MLS message received from ${source} was empty`);
|
|
234
243
|
}
|
|
235
|
-
const decryptedData = protocol_messaging_1.GenericMessage.decode(
|
|
244
|
+
const decryptedData = protocol_messaging_1.GenericMessage.decode(message);
|
|
236
245
|
/**
|
|
237
246
|
* @todo Find a proper solution to add mappedEvent to this return
|
|
238
247
|
* otherwise event.data will be base64 raw data of the received event
|
|
@@ -261,7 +270,7 @@ class NotificationService extends events_1.EventEmitter {
|
|
|
261
270
|
case Events.CONVERSATION_EVENT.MEMBER_JOIN:
|
|
262
271
|
// As of today (07/07/2022) the backend sends `WELCOME` message to the user's own conversation (not the actual conversation that the welcome should be part of)
|
|
263
272
|
// So in order to map conversation Ids and groupId together, we need to first fetch the conversation and get the groupId linked to it.
|
|
264
|
-
const conversation = await this.apiClient.api.conversation.getConversation((
|
|
273
|
+
const conversation = await this.apiClient.api.conversation.getConversation((_b = event.qualified_conversation) !== null && _b !== void 0 ? _b : { id: event.conversation, domain: '' });
|
|
265
274
|
if (!conversation) {
|
|
266
275
|
throw new Error('no conv');
|
|
267
276
|
}
|
|
@@ -284,6 +293,7 @@ class NotificationService extends events_1.EventEmitter {
|
|
|
284
293
|
return { event };
|
|
285
294
|
}
|
|
286
295
|
/**
|
|
296
|
+
* ## MLS only ##
|
|
287
297
|
* If there is a groupId in the conversation, we need to store the conversationId => groupId pair
|
|
288
298
|
* in order to find the groupId when decrypting messages
|
|
289
299
|
* This is a bit hacky but since mls messages do not embed the groupId we need to keep a mapping of those
|
|
@@ -297,6 +307,7 @@ class NotificationService extends events_1.EventEmitter {
|
|
|
297
307
|
}
|
|
298
308
|
}
|
|
299
309
|
/**
|
|
310
|
+
* ## MLS only ##
|
|
300
311
|
* If there is a matching conversationId => groupId pair in the database,
|
|
301
312
|
* we can find the groupId and return it as a Uint8Array
|
|
302
313
|
*
|
|
@@ -313,6 +324,84 @@ class NotificationService extends events_1.EventEmitter {
|
|
|
313
324
|
}
|
|
314
325
|
return bazinga64_1.Decoder.fromBase64(groupId).asBytes;
|
|
315
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* ## MLS only ##
|
|
329
|
+
* If there are pending proposals, we need to either process them,
|
|
330
|
+
* or save them in the database for later processing
|
|
331
|
+
*
|
|
332
|
+
* @param groupId groupId of the mls conversation
|
|
333
|
+
* @param delayInMs delay in ms before processing proposals
|
|
334
|
+
* @param eventTime time of the event that had the proposals
|
|
335
|
+
*/
|
|
336
|
+
async handlePendingProposals({ delayInMs, groupId, eventTime }) {
|
|
337
|
+
if (delayInMs > 0) {
|
|
338
|
+
const eventDate = new Date(eventTime);
|
|
339
|
+
const firingDate = eventDate.setTime(eventDate.getTime() + delayInMs);
|
|
340
|
+
try {
|
|
341
|
+
await this.database.storePendingProposal({
|
|
342
|
+
groupId,
|
|
343
|
+
firingDate,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
this.logger.error('Could not store pending proposal', error);
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
TaskScheduler_1.TaskScheduler.addTask({
|
|
351
|
+
task: () => this.commitPendingProposals({ groupId }),
|
|
352
|
+
firingDate,
|
|
353
|
+
key: groupId,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
await this.commitPendingProposals({ groupId, skipDelete: true });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* ## MLS only ##
|
|
363
|
+
* Commit all pending proposals for a given groupId
|
|
364
|
+
*
|
|
365
|
+
* @param groupId groupId of the conversation
|
|
366
|
+
* @param skipDelete if true, do not delete the pending proposals from the database
|
|
367
|
+
*/
|
|
368
|
+
async commitPendingProposals({ groupId, skipDelete = false }) {
|
|
369
|
+
const coreCryptoClient = this.coreCryptoClientProvider();
|
|
370
|
+
if (!coreCryptoClient) {
|
|
371
|
+
throw new Error('Could not get coreCryptoClient');
|
|
372
|
+
}
|
|
373
|
+
try {
|
|
374
|
+
await coreCryptoClient.commitPendingProposals(bazinga64_1.Decoder.fromBase64(groupId).asBytes);
|
|
375
|
+
if (!skipDelete) {
|
|
376
|
+
TaskScheduler_1.TaskScheduler.cancelTask(groupId);
|
|
377
|
+
await this.database.deletePendingProposal({ groupId });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
this.logger.error(`Error while committing pending proposals for groupId ${groupId}`, error);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* ## MLS only ##
|
|
386
|
+
* Get all pending proposals from the database and schedule them
|
|
387
|
+
* Function must only be called once, after application start
|
|
388
|
+
*
|
|
389
|
+
*/
|
|
390
|
+
async checkExistingPendingProposals() {
|
|
391
|
+
try {
|
|
392
|
+
const pendingProposals = await this.database.getStoredPendingProposals();
|
|
393
|
+
if (pendingProposals.length > 0) {
|
|
394
|
+
pendingProposals.forEach(({ groupId, firingDate }) => TaskScheduler_1.TaskScheduler.addTask({
|
|
395
|
+
task: () => this.commitPendingProposals({ groupId }),
|
|
396
|
+
firingDate,
|
|
397
|
+
key: groupId,
|
|
398
|
+
}));
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
this.logger.error('Could not get pending proposals', error);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
316
405
|
}
|
|
317
406
|
exports.NotificationService = NotificationService;
|
|
318
407
|
NotificationService.TOPIC = TOPIC;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare type CommonMLS = {
|
|
2
|
+
groupId: string;
|
|
3
|
+
};
|
|
4
|
+
export declare type CompoundGroupIdParams = {
|
|
5
|
+
conversationId: string;
|
|
6
|
+
conversationDomain: string;
|
|
7
|
+
} & CommonMLS;
|
|
8
|
+
export declare type HandlePendingProposalsParams = {
|
|
9
|
+
delayInMs: number;
|
|
10
|
+
eventTime: string;
|
|
11
|
+
} & CommonMLS;
|
|
12
|
+
export declare type CommitPendingProposalsParams = {
|
|
13
|
+
skipDelete?: boolean;
|
|
14
|
+
} & CommonMLS;
|
|
15
|
+
export declare type StorePendingProposalsParams = {
|
|
16
|
+
firingDate: number;
|
|
17
|
+
} & CommonMLS;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Wire
|
|
4
|
+
* Copyright (C) 2022 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
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -1,25 +1,8 @@
|
|
|
1
1
|
import type { APIClient } from '@wireapp/api-client';
|
|
2
2
|
import type { QualifiedId, User } from '@wireapp/api-client/src/user/';
|
|
3
|
-
import type { AvailabilityType, BroadcastService } from '../broadcast/';
|
|
4
|
-
import { ConnectionService } from '../connection';
|
|
5
|
-
import { ConversationService } from '../conversation';
|
|
6
3
|
export declare class UserService {
|
|
7
4
|
private readonly apiClient;
|
|
8
|
-
|
|
9
|
-
private readonly connectionService;
|
|
10
|
-
private readonly conversationService;
|
|
11
|
-
constructor(apiClient: APIClient, broadcastService: BroadcastService, conversationService: ConversationService, connectionService: ConnectionService);
|
|
5
|
+
constructor(apiClient: APIClient);
|
|
12
6
|
getUser(userId: string | QualifiedId): Promise<User>;
|
|
13
7
|
getUsers(userIds: string[] | QualifiedId[]): Promise<User[]>;
|
|
14
|
-
/**
|
|
15
|
-
* Sends a availability update to members of the same team
|
|
16
|
-
* @param teamId
|
|
17
|
-
* @param type
|
|
18
|
-
* @param options.sendAll=false will broadcast the message to all the members of the team (instead of just direct connections). Should be avoided in a big team
|
|
19
|
-
* @param options.sendAsProtobuf=false
|
|
20
|
-
*/
|
|
21
|
-
setAvailability(teamId: string, type: AvailabilityType, { sendAll, sendAsProtobuf }?: {
|
|
22
|
-
sendAll?: boolean | undefined;
|
|
23
|
-
sendAsProtobuf?: boolean | undefined;
|
|
24
|
-
}): Promise<void>;
|
|
25
8
|
}
|
|
@@ -17,21 +17,12 @@
|
|
|
17
17
|
* along with this program. If not, see http://www.gnu.org/licenses/.
|
|
18
18
|
*
|
|
19
19
|
*/
|
|
20
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
-
};
|
|
23
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
21
|
exports.UserService = void 0;
|
|
25
|
-
const protocol_messaging_1 = require("@wireapp/protocol-messaging");
|
|
26
|
-
const uuidjs_1 = __importDefault(require("uuidjs"));
|
|
27
22
|
const TypePredicateUtil_1 = require("../util/TypePredicateUtil");
|
|
28
|
-
const connection_1 = require("@wireapp/api-client/src/connection");
|
|
29
23
|
class UserService {
|
|
30
|
-
constructor(apiClient
|
|
24
|
+
constructor(apiClient) {
|
|
31
25
|
this.apiClient = apiClient;
|
|
32
|
-
this.broadcastService = broadcastService;
|
|
33
|
-
this.connectionService = connectionService;
|
|
34
|
-
this.conversationService = conversationService;
|
|
35
26
|
}
|
|
36
27
|
getUser(userId) {
|
|
37
28
|
return this.apiClient.api.user.getUser(userId);
|
|
@@ -44,38 +35,6 @@ class UserService {
|
|
|
44
35
|
? this.apiClient.api.user.postListUsers({ qualified_ids: userIds })
|
|
45
36
|
: this.apiClient.api.user.getUsers({ ids: userIds });
|
|
46
37
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Sends a availability update to members of the same team
|
|
49
|
-
* @param teamId
|
|
50
|
-
* @param type
|
|
51
|
-
* @param options.sendAll=false will broadcast the message to all the members of the team (instead of just direct connections). Should be avoided in a big team
|
|
52
|
-
* @param options.sendAsProtobuf=false
|
|
53
|
-
*/
|
|
54
|
-
async setAvailability(teamId, type, { sendAll = false, sendAsProtobuf = false } = {}) {
|
|
55
|
-
// Get pre-key bundles for members of your own team
|
|
56
|
-
const preKeyBundlesFromTeam = await this.broadcastService.getPreKeyBundlesFromTeam(teamId, false, !sendAll);
|
|
57
|
-
// Get pre-key bundles for all of your other 1:1 connections
|
|
58
|
-
const connections = await this.connectionService.getConnections();
|
|
59
|
-
const acceptedConnections = connections.filter(connection => connection.status === connection_1.ConnectionStatus.ACCEPTED);
|
|
60
|
-
const preKeyBundlePromises = acceptedConnections.map(connection => {
|
|
61
|
-
const mappedConnection = {
|
|
62
|
-
userId: connection.to,
|
|
63
|
-
conversationId: connection.conversation,
|
|
64
|
-
};
|
|
65
|
-
return this.conversationService.getPreKeyBundleMap(mappedConnection.conversationId, [mappedConnection.userId]);
|
|
66
|
-
});
|
|
67
|
-
const preKeyBundlesFromConnections = await Promise.all(preKeyBundlePromises);
|
|
68
|
-
// Merge pre-key bundles
|
|
69
|
-
const allPreKeyBundles = preKeyBundlesFromConnections.reduce((accumulator, preKeyBundleMap) => {
|
|
70
|
-
return Object.assign(Object.assign({}, accumulator), preKeyBundleMap);
|
|
71
|
-
}, preKeyBundlesFromTeam);
|
|
72
|
-
const genericMessage = protocol_messaging_1.GenericMessage.create({
|
|
73
|
-
availability: new protocol_messaging_1.Availability({ type }),
|
|
74
|
-
messageId: uuidjs_1.default.genV4().toString(),
|
|
75
|
-
});
|
|
76
|
-
// Broadcast availability status to your team members & external 1:1 connections
|
|
77
|
-
await this.broadcastService.broadcastGenericMessage(genericMessage, allPreKeyBundles, sendAsProtobuf);
|
|
78
|
-
}
|
|
79
38
|
}
|
|
80
39
|
exports.UserService = UserService;
|
|
81
40
|
//# sourceMappingURL=UserService.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare type ScheduleTaskParams = {
|
|
2
|
+
task: () => Promise<void>;
|
|
3
|
+
firingDate: number;
|
|
4
|
+
key: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const TaskScheduler: {
|
|
7
|
+
addTask: ({ task, firingDate, key }: ScheduleTaskParams) => void;
|
|
8
|
+
cancelTask: (key: string) => void;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Wire
|
|
4
|
+
* Copyright (C) 2022 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.TaskScheduler = void 0;
|
|
25
|
+
const logdown_1 = __importDefault(require("logdown"));
|
|
26
|
+
const logger = (0, logdown_1.default)('@wireapp/core/util/TaskScheduler/TaskScheduler', {
|
|
27
|
+
logger: console,
|
|
28
|
+
markdown: false,
|
|
29
|
+
});
|
|
30
|
+
const activeTimeouts = {};
|
|
31
|
+
/**
|
|
32
|
+
* Execute a task at a given time.
|
|
33
|
+
*
|
|
34
|
+
* @param task function to be executed
|
|
35
|
+
* @param firingDate execution date
|
|
36
|
+
* @param key unique key for the task
|
|
37
|
+
*/
|
|
38
|
+
const addTask = ({ task, firingDate, key }) => {
|
|
39
|
+
const now = new Date();
|
|
40
|
+
const execute = new Date(firingDate);
|
|
41
|
+
const delay = execute.getTime() - now.getTime();
|
|
42
|
+
if (activeTimeouts[key]) {
|
|
43
|
+
cancelTask(key);
|
|
44
|
+
}
|
|
45
|
+
const timeout = setTimeout(async () => {
|
|
46
|
+
await task();
|
|
47
|
+
delete activeTimeouts[key];
|
|
48
|
+
}, delay > 0 ? delay : 0);
|
|
49
|
+
// add the task to the list of active tasks
|
|
50
|
+
activeTimeouts[key] = timeout;
|
|
51
|
+
logger.info(`New scheduled task to be executed at "${execute}" with key "${key}"`);
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Cancel a scheduled task early
|
|
55
|
+
*
|
|
56
|
+
* @param key unique key for the task
|
|
57
|
+
*/
|
|
58
|
+
const cancelTask = (key) => {
|
|
59
|
+
const timeout = activeTimeouts[key];
|
|
60
|
+
if (timeout) {
|
|
61
|
+
clearTimeout(timeout);
|
|
62
|
+
delete activeTimeouts[key];
|
|
63
|
+
logger.info(`Scheduled task with key "${key}" prematurely cleared`);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
exports.TaskScheduler = {
|
|
67
|
+
addTask,
|
|
68
|
+
cancelTask,
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=TaskScheduler.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { QualifiedUserClientMap } from '@wireapp/api-client/src/client';
|
|
2
|
+
declare type UserId = string;
|
|
3
|
+
declare type ClientId = string;
|
|
4
|
+
declare type Domain = string;
|
|
5
|
+
export declare type ClientIdStringType = `${UserId}:${ClientId}@${Domain}`;
|
|
6
|
+
export declare const constructFullyQualifiedClientId: (userId: UserId, clientId: ClientId, domain: Domain) => ClientIdStringType;
|
|
7
|
+
export declare const mapQualifiedUserClientIdsToFullyQualifiedClientIds: (qualifiedUserMap: QualifiedUserClientMap) => Uint8Array[];
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Wire
|
|
4
|
+
* Copyright (C) 2022 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
|
+
exports.mapQualifiedUserClientIdsToFullyQualifiedClientIds = exports.constructFullyQualifiedClientId = void 0;
|
|
22
|
+
const constructFullyQualifiedClientId = (userId, clientId, domain) => `${userId}:${clientId}@${domain}`;
|
|
23
|
+
exports.constructFullyQualifiedClientId = constructFullyQualifiedClientId;
|
|
24
|
+
const mapQualifiedUserClientIdsToFullyQualifiedClientIds = (qualifiedUserMap) => {
|
|
25
|
+
const encoder = new TextEncoder();
|
|
26
|
+
return Object.entries(qualifiedUserMap).flatMap(([domain, users]) => {
|
|
27
|
+
const clients = Object.entries(users);
|
|
28
|
+
return clients.flatMap(([userId, clients]) => clients.map(client => encoder.encode((0, exports.constructFullyQualifiedClientId)(userId, client.id, domain))));
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
exports.mapQualifiedUserClientIdsToFullyQualifiedClientIds = mapQualifiedUserClientIdsToFullyQualifiedClientIds;
|
|
32
|
+
//# sourceMappingURL=mapQualifiedUserClientIdsToFullyQualifiedClientIds.js.map
|