@wireapp/core 30.11.2 → 30.13.0

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 CHANGED
@@ -3,6 +3,28 @@
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.13.0](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@30.12.0...@wireapp/core@30.13.0) (2022-09-20)
7
+
8
+
9
+ ### Features
10
+
11
+ * Handle external proposals ([#4397](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/issues/4397)) ([54c5f97](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/commit/54c5f979eb5e5bd4e864dad1b9acb3c330ddac75))
12
+
13
+
14
+
15
+
16
+
17
+ # [30.12.0](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@30.11.2...@wireapp/core@30.12.0) (2022-09-20)
18
+
19
+
20
+ ### Features
21
+
22
+ * leave mls convo - refactor (FS-683) ([#4401](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/issues/4401)) ([909e142](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/commit/909e142753035fb54c8b4f19076a1dec646f6b97))
23
+
24
+
25
+
26
+
27
+
6
28
  ## [30.11.2](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@30.11.1...@wireapp/core@30.11.2) (2022-09-19)
7
29
 
8
30
  **Note:** Version bump only for package @wireapp/core
package/package.json CHANGED
@@ -75,6 +75,6 @@
75
75
  "test": "jest",
76
76
  "watch": "tsc ---watch"
77
77
  },
78
- "version": "30.11.2",
79
- "gitHead": "4e82d31760bc2f55bc345027b3e01d3ecbd2ee07"
78
+ "version": "30.13.0",
79
+ "gitHead": "b88023577debb092f6bb4a0125f0cd64ce14d200"
80
80
  }
@@ -23,7 +23,7 @@ import { AccountService } from './account/';
23
23
  import { LinkPreviewService } from './linkPreview';
24
24
  import { WEBSOCKET_STATE } from '@wireapp/api-client/src/tcp/ReconnectingWebsocket';
25
25
  import { MLSService } from './mls';
26
- import type { MLSConfig } from './mls/types';
26
+ import type { MLSCallbacks, MLSConfig } from './mls/types';
27
27
  export declare type ProcessedEventPayload = HandledEventPayload;
28
28
  declare enum TOPIC {
29
29
  ERROR = "Account.TOPIC.ERROR"
@@ -147,6 +147,14 @@ export declare class Account<T = any> extends EventEmitter {
147
147
  isNewClient: boolean;
148
148
  localClient: RegisteredClient;
149
149
  }>;
150
+ /**
151
+ * In order to be able to send MLS messages, the core needs a few information from the consumer.
152
+ * Namely:
153
+ * - is the current user allowed to administrate a specific conversation
154
+ * - what is the groupId of a conversation
155
+ * @param mlsCallbacks
156
+ */
157
+ configureMLSCallbacks(mlsCallbacks: MLSCallbacks): void;
150
158
  initServices(context: Context): Promise<void>;
151
159
  loadAndValidateLocalClient(entropyData?: Uint8Array): Promise<RegisteredClient>;
152
160
  private createMLSClient;
@@ -232,6 +232,17 @@ class Account extends events_1.EventEmitter {
232
232
  throw error;
233
233
  }
234
234
  }
235
+ /**
236
+ * In order to be able to send MLS messages, the core needs a few information from the consumer.
237
+ * Namely:
238
+ * - is the current user allowed to administrate a specific conversation
239
+ * - what is the groupId of a conversation
240
+ * @param mlsCallbacks
241
+ */
242
+ configureMLSCallbacks(mlsCallbacks) {
243
+ var _a;
244
+ (_a = this.service) === null || _a === void 0 ? void 0 : _a.mls.configureMLSCallbacks(mlsCallbacks);
245
+ }
235
246
  async initServices(context) {
236
247
  this.storeEngine = await this.initEngine(context);
237
248
  const accountService = new account_1.AccountService(this.apiClient);
@@ -110,11 +110,6 @@ export declare class ConversationService {
110
110
  fetchAllParticipantsClients(conversationId: string, conversationDomain?: string): Promise<UserClients | QualifiedUserClients>;
111
111
  deleteMessageLocal(conversationId: string, messageIdToHide: string, sendAsProtobuf?: boolean, conversationDomain?: string): Promise<HideMessage>;
112
112
  deleteMessageEveryone(conversationId: string, messageIdToDelete: string, userIds?: string[] | QualifiedId[] | UserClients | QualifiedUserClients, sendAsProtobuf?: boolean, conversationDomain?: string, callbacks?: MessageSendingCallbacks): Promise<DeleteMessage>;
113
- leaveConversation(conversationId: QualifiedId): Promise<ConversationMemberLeaveEvent>;
114
- /**
115
- * @depricated seems not to be used and is outdated. use leaveConversation instead
116
- */
117
- leaveConversations(conversationIds?: string[]): Promise<ConversationMemberLeaveEvent[]>;
118
113
  /**
119
114
  * Create a group conversation.
120
115
  * @param {string} name
@@ -138,7 +133,7 @@ export declare class ConversationService {
138
133
  getAsset({ assetId, assetToken, otrKey, sha256 }: RemoteData): Promise<Uint8Array>;
139
134
  getUnencryptedAsset(assetId: string, assetToken?: string): Promise<ArrayBuffer>;
140
135
  addUsersToProteusConversation({ conversationId, qualifiedUserIds }: Omit<AddUsersParams, 'groupId'>): Promise<import("@wireapp/api-client/src/event").ConversationMemberJoinEvent>;
141
- removeUserFromProteusConversation(conversationId: QualifiedId, userId: QualifiedId): Promise<ConversationMemberLeaveEvent>;
136
+ removeUserFromConversation(conversationId: QualifiedId, userId: QualifiedId): Promise<ConversationMemberLeaveEvent>;
142
137
  private sendProteusMessage;
143
138
  /**
144
139
  * Sends a message to a conversation
@@ -31,7 +31,7 @@ const MessageService_1 = require("../message/MessageService");
31
31
  const MessageToProtoMapper_1 = require("../message/MessageToProtoMapper");
32
32
  const ConversationService_types_1 = require("./ConversationService.types");
33
33
  const bazinga64_1 = require("bazinga64");
34
- const mapQualifiedUserClientIdsToFullyQualifiedClientIds_1 = require("../../util/mapQualifiedUserClientIdsToFullyQualifiedClientIds");
34
+ const fullyQualifiedClientIdUtils_1 = require("../../util/fullyQualifiedClientIdUtils");
35
35
  const mls_1 = require("../../mls");
36
36
  const messageSender_1 = require("../message/messageSender");
37
37
  class ConversationService {
@@ -617,27 +617,6 @@ class ConversationService {
617
617
  type: conversation_2.PayloadBundleType.MESSAGE_DELETE,
618
618
  };
619
619
  }
620
- leaveConversation(conversationId) {
621
- if (!this.apiClient.context || !this.apiClient.context.userId || !this.apiClient.context.domain) {
622
- throw new Error('Cannot leave conversation without a userId and domain');
623
- }
624
- return this.apiClient.api.conversation.deleteMember(conversationId, {
625
- domain: this.apiClient.context.domain,
626
- id: this.apiClient.context.userId,
627
- });
628
- }
629
- /**
630
- * @depricated seems not to be used and is outdated. use leaveConversation instead
631
- */
632
- async leaveConversations(conversationIds) {
633
- if (!conversationIds) {
634
- const conversation = await this.getConversations();
635
- conversationIds = conversation
636
- .filter(conversation => conversation.type === conversation_1.CONVERSATION_TYPE.REGULAR)
637
- .map(conversation => conversation.id);
638
- }
639
- 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 : '' }); }));
640
- }
641
620
  createProteusConversation(conversationData, otherUserIds) {
642
621
  let payload;
643
622
  if (typeof conversationData === 'string') {
@@ -678,7 +657,7 @@ class ConversationService {
678
657
  async addUsersToProteusConversation({ conversationId, qualifiedUserIds }) {
679
658
  return this.apiClient.api.conversation.postMembers(conversationId, qualifiedUserIds);
680
659
  }
681
- async removeUserFromProteusConversation(conversationId, userId) {
660
+ async removeUserFromConversation(conversationId, userId) {
682
661
  return this.apiClient.api.conversation.deleteMember(conversationId, userId);
683
662
  }
684
663
  async sendProteusMessage(params, genericMessage, content) {
@@ -851,7 +830,6 @@ class ConversationService {
851
830
  ...qualifiedUsers,
852
831
  ]);
853
832
  const response = await this.mlsService.addUsersToExistingConversation(groupIdDecodedFromBase64, coreCryptoKeyPackagesPayload);
854
- await this.notificationService.saveConversationGroupId(newConversation);
855
833
  //We store the info when conversation (along with key material) was created, so we will know when to renew it
856
834
  const groupCreationTimeStamp = new Date().getTime();
857
835
  await this.notificationService.storeLastKeyMaterialUpdateDate({
@@ -898,7 +876,7 @@ class ConversationService {
898
876
  async removeUsersFromMLSConversation({ groupId, conversationId, qualifiedUserIds, }) {
899
877
  const groupIdDecodedFromBase64 = bazinga64_1.Decoder.fromBase64(groupId).asBytes;
900
878
  const clientsToRemove = await this.apiClient.api.user.postListClients({ qualified_users: qualifiedUserIds });
901
- const fullyQualifiedClientIds = (0, mapQualifiedUserClientIdsToFullyQualifiedClientIds_1.mapQualifiedUserClientIdsToFullyQualifiedClientIds)(clientsToRemove.qualified_user_map);
879
+ const fullyQualifiedClientIds = (0, fullyQualifiedClientIdUtils_1.mapQualifiedUserClientIdsToFullyQualifiedClientIds)(clientsToRemove.qualified_user_map);
902
880
  const messageResponse = await this.mlsService.removeClientsFromConversation(groupIdDecodedFromBase64, fullyQualifiedClientIds);
903
881
  //key material gets updated after removing a user from the group, so we can reset last key update time value in the store
904
882
  await this.storeLastKeyMaterialUpdateDateWithCurrentTime(groupId);
@@ -1,17 +1,19 @@
1
- import { AddProposalArgs, CommitBundle, ConversationConfiguration, ConversationId, CoreCrypto, DecryptedMessage, ExternalProposalArgs, ExternalProposalType, ExternalRemoveProposalArgs, Invitee, ProposalArgs, ProposalType, RemoveProposalArgs } from '@wireapp/core-crypto';
1
+ import { AddProposalArgs, ConversationConfiguration, ConversationId, CoreCrypto, DecryptedMessage, ExternalProposalArgs, ExternalProposalType, ExternalRemoveProposalArgs, Invitee, ProposalArgs, ProposalType, RemoveProposalArgs } from '@wireapp/core-crypto';
2
2
  import { APIClient } from '@wireapp/api-client';
3
3
  import { QualifiedUsers } from '../../conversation';
4
- import type { MLSConfig } from '../types';
4
+ import type { MLSCallbacks, MLSConfig } from '../types';
5
5
  import { PostMlsMessageResponse } from '@wireapp/api-client/src/conversation';
6
6
  export declare const optionalToUint8Array: (array: Uint8Array | []) => Uint8Array;
7
7
  export declare class MLSService {
8
8
  readonly config: MLSConfig | undefined;
9
9
  private readonly apiClient;
10
10
  private readonly coreCryptoClientProvider;
11
+ groupIdFromConversationId?: MLSCallbacks['groupIdFromConversationId'];
11
12
  constructor(config: MLSConfig | undefined, apiClient: APIClient, coreCryptoClientProvider: () => CoreCrypto | undefined);
12
13
  private getCoreCryptoClient;
13
14
  private uploadCommitBundle;
14
15
  addUsersToExistingConversation(groupId: Uint8Array, invitee: Invitee[]): Promise<PostMlsMessageResponse | null>;
16
+ configureMLSCallbacks({ groupIdFromConversationId, ...coreCryptoCallbacks }: MLSCallbacks): void;
15
17
  getKeyPackagesPayload(qualifiedUsers: QualifiedUsers[]): Promise<Invitee[]>;
16
18
  newProposal(proposalType: ProposalType, args: ProposalArgs | AddProposalArgs | RemoveProposalArgs): Promise<import("@wireapp/core-crypto").ProposalBundle>;
17
19
  newExternalProposal(externalProposalType: ExternalProposalType, args: ExternalProposalArgs | ExternalRemoveProposalArgs): Promise<Uint8Array>;
@@ -21,7 +23,7 @@ export declare class MLSService {
21
23
  updateKeyingMaterial(conversationId: ConversationId): Promise<PostMlsMessageResponse | null>;
22
24
  createConversation(conversationId: ConversationId, configuration?: ConversationConfiguration): Promise<any>;
23
25
  removeClientsFromConversation(conversationId: ConversationId, clientIds: Uint8Array[]): Promise<PostMlsMessageResponse | null>;
24
- commitPendingProposals(conversationId: ConversationId): Promise<CommitBundle | undefined>;
26
+ commitPendingProposals(groupId: ConversationId): Promise<void>;
25
27
  conversationExists(conversationId: ConversationId): Promise<boolean>;
26
28
  clientValidKeypackagesCount(): Promise<number>;
27
29
  clientKeypackages(amountRequested: number): Promise<Uint8Array[]>;
@@ -17,10 +17,22 @@
17
17
  * along with this program. If not, see http://www.gnu.org/licenses/.
18
18
  *
19
19
  */
20
+ var __rest = (this && this.__rest) || function (s, e) {
21
+ var t = {};
22
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
23
+ t[p] = s[p];
24
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
25
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
26
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
27
+ t[p[i]] = s[p[i]];
28
+ }
29
+ return t;
30
+ };
20
31
  Object.defineProperty(exports, "__esModule", { value: true });
21
32
  exports.MLSService = exports.optionalToUint8Array = void 0;
22
33
  const bazinga64_1 = require("bazinga64");
23
34
  const messageSender_1 = require("../../conversation/message/messageSender");
35
+ const fullyQualifiedClientIdUtils_1 = require("../../util/fullyQualifiedClientIdUtils");
24
36
  //@todo: this function is temporary, we wait for the update from core-crypto side
25
37
  //they are returning regular array instead of Uint8Array for commit and welcome messages
26
38
  const optionalToUint8Array = (array) => {
@@ -40,23 +52,23 @@ class MLSService {
40
52
  }
41
53
  return client;
42
54
  }
43
- async uploadCommitBundle(groupId, commitBundle) {
55
+ async uploadCommitBundle(groupId, { commit, welcome }) {
44
56
  const coreCryptoClient = this.getCoreCryptoClient();
45
- if (commitBundle.welcome) {
46
- //@todo: it's temporary - we wait for core-crypto fix to return the actual Uint8Array instead of regular array
47
- await this.apiClient.api.conversation.postMlsWelcomeMessage((0, exports.optionalToUint8Array)(commitBundle.welcome));
48
- }
49
- if (commitBundle.commit) {
57
+ if (commit) {
50
58
  try {
51
59
  const messageResponse = await this.apiClient.api.conversation.postMlsMessage(
52
60
  //@todo: it's temporary - we wait for core-crypto fix to return the actual Uint8Array instead of regular array
53
- (0, exports.optionalToUint8Array)(commitBundle.commit));
61
+ (0, exports.optionalToUint8Array)(commit));
54
62
  await coreCryptoClient.commitAccepted(groupId);
63
+ if (welcome) {
64
+ // If the commit went well, we can send the Welcome
65
+ //@todo: it's temporary - we wait for core-crypto fix to return the actual Uint8Array instead of regular array
66
+ await this.apiClient.api.conversation.postMlsWelcomeMessage((0, exports.optionalToUint8Array)(welcome));
67
+ }
55
68
  return messageResponse;
56
69
  }
57
70
  catch (error) {
58
71
  await coreCryptoClient.clearPendingCommit(groupId);
59
- return null;
60
72
  }
61
73
  }
62
74
  return null;
@@ -68,6 +80,18 @@ class MLSService {
68
80
  return this.uploadCommitBundle(groupId, memberAddedMessages);
69
81
  });
70
82
  }
83
+ configureMLSCallbacks(_a) {
84
+ var { groupIdFromConversationId } = _a, coreCryptoCallbacks = __rest(_a, ["groupIdFromConversationId"]);
85
+ this.getCoreCryptoClient().registerCallbacks(Object.assign(Object.assign({}, coreCryptoCallbacks), { clientIdBelongsToOneOf: (client, otherClients) => {
86
+ const decoder = new TextDecoder();
87
+ const { user } = (0, fullyQualifiedClientIdUtils_1.parseFullQualifiedClientId)(decoder.decode(client));
88
+ return otherClients.some(client => {
89
+ const { user: otherUser } = (0, fullyQualifiedClientIdUtils_1.parseFullQualifiedClientId)(decoder.decode(client));
90
+ return otherUser === user;
91
+ });
92
+ } }));
93
+ this.groupIdFromConversationId = groupIdFromConversationId;
94
+ }
71
95
  async getKeyPackagesPayload(qualifiedUsers) {
72
96
  /**
73
97
  * @note We need to fetch key packages for all the users
@@ -122,8 +146,9 @@ class MLSService {
122
146
  return this.uploadCommitBundle(conversationId, commitBundle);
123
147
  });
124
148
  }
125
- async commitPendingProposals(conversationId) {
126
- return this.getCoreCryptoClient().commitPendingProposals(conversationId);
149
+ async commitPendingProposals(groupId) {
150
+ const commitBundle = await this.getCoreCryptoClient().commitPendingProposals(groupId);
151
+ return commitBundle ? void this.uploadCommitBundle(groupId, commitBundle) : undefined;
127
152
  }
128
153
  async conversationExists(conversationId) {
129
154
  return this.getCoreCryptoClient().conversationExists(conversationId);
@@ -1,7 +1,18 @@
1
+ import { QualifiedId } from '@wireapp/api-client/src/user';
2
+ import { CoreCryptoCallbacks } from '@wireapp/core-crypto';
1
3
  declare type SecretCrypto<T> = {
2
4
  encrypt: (value: Uint8Array) => Promise<T>;
3
5
  decrypt: (payload: T) => Promise<Uint8Array>;
4
6
  };
7
+ export interface MLSCallbacks extends Pick<CoreCryptoCallbacks, 'authorize'> {
8
+ /**
9
+ * Should return a groupId corresponding to the conversation ID given
10
+ * Used for the core to know what core-crypto conversation we are dealing with when receiving events
11
+ * @param conversationId
12
+ * @returns the bytes of the groupId corresponding to the conversation ID
13
+ */
14
+ groupIdFromConversationId: (conversationId: QualifiedId) => Promise<string | undefined>;
15
+ }
5
16
  export interface MLSConfig<T = any> {
6
17
  /**
7
18
  * encrypt/decrypt function pair that will be called before storing/fetching secrets in the secrets database.
@@ -1,7 +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
- import { CommonMLS, CompoundGroupIdParams, StorePendingProposalsParams } from './types';
4
+ import { CommonMLS, StorePendingProposalsParams } from './types';
5
5
  export declare enum DatabaseStores {
6
6
  EVENTS = "events"
7
7
  }
@@ -18,9 +18,6 @@ export declare class NotificationDatabaseRepository {
18
18
  createLastEventDate(eventDate: Date): Promise<Date>;
19
19
  getLastNotificationId(): Promise<string>;
20
20
  updateLastNotificationId(lastNotification: Notification): Promise<string>;
21
- private generateCompoundGroupIdPrimaryKey;
22
- addCompoundGroupId(params: CompoundGroupIdParams): Promise<CompoundGroupIdParams>;
23
- getCompoundGroupId(params: Omit<CompoundGroupIdParams, 'groupId'>): Promise<string>;
24
21
  /**
25
22
  * ## MLS only ##
26
23
  * Store groupIds with pending proposals and a delay in the DB until the proposals get committed.
@@ -63,16 +63,6 @@ class NotificationDatabaseRepository {
63
63
  });
64
64
  return lastNotification.id;
65
65
  }
66
- generateCompoundGroupIdPrimaryKey({ conversationId, conversationDomain, }) {
67
- return `${conversationId}@${conversationDomain}`;
68
- }
69
- async addCompoundGroupId(params) {
70
- await this.storeEngine.updateOrCreate(STORES.GROUP_IDS, this.generateCompoundGroupIdPrimaryKey(params), params.groupId);
71
- return params;
72
- }
73
- async getCompoundGroupId(params) {
74
- return this.storeEngine.read(STORES.GROUP_IDS, this.generateCompoundGroupIdPrimaryKey(params));
75
- }
76
66
  /**
77
67
  * ## MLS only ##
78
68
  * Store groupIds with pending proposals and a delay in the DB until the proposals get committed.
@@ -9,8 +9,6 @@ import { NotificationError } from '../CoreError';
9
9
  import type { CryptographyService } from '../cryptography';
10
10
  import { GenericMessage } from '@wireapp/protocol-messaging';
11
11
  import { AbortHandler } from '@wireapp/api-client/src/tcp';
12
- import { QualifiedId } from '@wireapp/api-client/src/user';
13
- import { Conversation } from '@wireapp/api-client/src/conversation';
14
12
  import { CommitPendingProposalsParams, LastKeyMaterialUpdateParams } from './types';
15
13
  import type { MLSService } from '../mls';
16
14
  export declare type HandledEventPayload = {
@@ -61,23 +59,14 @@ export declare class NotificationService extends EventEmitter {
61
59
  handleNotification(notification: Notification, source: PayloadBundleSource, dryRun?: boolean): AsyncGenerator<HandledEventPayload>;
62
60
  private cleanupPayloadBundle;
63
61
  private handleEvent;
64
- /**
65
- * ## MLS only ##
66
- * If there is a groupId in the conversation, we need to store the conversationId => groupId pair
67
- * in order to find the groupId when decrypting messages
68
- * This is a bit hacky but since mls messages do not embed the groupId we need to keep a mapping of those
69
- *
70
- * @param conversation conversation with group_id
71
- */
72
- saveConversationGroupId(conversation: Conversation): Promise<void>;
73
62
  /**
74
63
  * ## MLS only ##
75
64
  * If there is a matching conversationId => groupId pair in the database,
76
- * we can find the groupId and return it as a Uint8Array
65
+ * we can find the groupId and return it as a string
77
66
  *
78
67
  * @param conversationQualifiedId
79
68
  */
80
- getUint8ArrayFromConversationGroupId(conversationQualifiedId: QualifiedId): Promise<Uint8Array>;
69
+ private getGroupIdFromConversationId;
81
70
  /**
82
71
  * ## MLS only ##
83
72
  * If there are pending proposals, we need to either process them,
@@ -229,20 +229,25 @@ class NotificationService extends events_1.EventEmitter {
229
229
  const groupIdStr = bazinga64_1.Encoder.toBase64(newGroupId).asString;
230
230
  // The groupId can then be sent back to the consumer
231
231
  return {
232
- event,
232
+ event: Object.assign(Object.assign({}, event), { data: groupIdStr }),
233
233
  mappedEvent: ConversationMapper_1.ConversationMapper.mapConversationEvent(Object.assign(Object.assign({}, event), { data: groupIdStr }), source),
234
234
  };
235
235
  case Events.CONVERSATION_EVENT.MLS_MESSAGE_ADD:
236
236
  const encryptedData = bazinga64_1.Decoder.fromBase64(event.data).asBytes;
237
- const groupId = await this.getUint8ArrayFromConversationGroupId((_a = event.qualified_conversation) !== null && _a !== void 0 ? _a : { id: event.conversation, domain: '' });
237
+ const groupId = await this.getGroupIdFromConversationId((_a = event.qualified_conversation) !== null && _a !== void 0 ? _a : { id: event.conversation, domain: '' });
238
+ const groupIdBytes = bazinga64_1.Decoder.fromBase64(groupId).asBytes;
238
239
  // Check if the message includes proposals
239
- const { proposals, commitDelay, message } = await this.mlsService.decryptMessage(groupId, encryptedData);
240
- if (proposals.length > 0) {
240
+ const { proposals, commitDelay, message } = await this.mlsService.decryptMessage(groupIdBytes, encryptedData);
241
+ if (typeof commitDelay === 'number' || proposals.length > 0) {
242
+ // we are dealing with a proposal, add a task to process this proposal later on
243
+ // Those proposals are stored inside of coreCrypto and will be handled after a timeout
241
244
  await this.handlePendingProposals({
242
- groupId: groupId.toString(),
245
+ groupId,
243
246
  delayInMs: commitDelay !== null && commitDelay !== void 0 ? commitDelay : 0,
244
247
  eventTime: event.time,
245
248
  });
249
+ // This is not a text message, there is nothing more to do
250
+ return { event };
246
251
  }
247
252
  if (!message) {
248
253
  throw new Error(`MLS message received from ${source} was empty`);
@@ -280,7 +285,6 @@ class NotificationService extends events_1.EventEmitter {
280
285
  if (!conversation) {
281
286
  throw new Error('no conv');
282
287
  }
283
- await this.saveConversationGroupId(conversation);
284
288
  case Events.CONVERSATION_EVENT.MESSAGE_TIMER_UPDATE:
285
289
  case Events.CONVERSATION_EVENT.RENAME:
286
290
  case Events.CONVERSATION_EVENT.TYPING: {
@@ -298,37 +302,21 @@ class NotificationService extends events_1.EventEmitter {
298
302
  }
299
303
  return { event };
300
304
  }
301
- /**
302
- * ## MLS only ##
303
- * If there is a groupId in the conversation, we need to store the conversationId => groupId pair
304
- * in order to find the groupId when decrypting messages
305
- * This is a bit hacky but since mls messages do not embed the groupId we need to keep a mapping of those
306
- *
307
- * @param conversation conversation with group_id
308
- */
309
- async saveConversationGroupId(conversation) {
310
- if (conversation.group_id) {
311
- const { group_id: groupId, qualified_id: { id: conversationId, domain: conversationDomain }, } = conversation;
312
- await this.database.addCompoundGroupId({ conversationDomain, conversationId, groupId });
313
- }
314
- }
315
305
  /**
316
306
  * ## MLS only ##
317
307
  * If there is a matching conversationId => groupId pair in the database,
318
- * we can find the groupId and return it as a Uint8Array
308
+ * we can find the groupId and return it as a string
319
309
  *
320
310
  * @param conversationQualifiedId
321
311
  */
322
- async getUint8ArrayFromConversationGroupId(conversationQualifiedId) {
312
+ async getGroupIdFromConversationId(conversationQualifiedId) {
313
+ var _a, _b;
323
314
  const { id: conversationId, domain: conversationDomain } = conversationQualifiedId;
324
- const groupId = await this.database.getCompoundGroupId({
325
- conversationId,
326
- conversationDomain,
327
- });
315
+ const groupId = await ((_b = (_a = this.mlsService).groupIdFromConversationId) === null || _b === void 0 ? void 0 : _b.call(_a, conversationQualifiedId));
328
316
  if (!groupId) {
329
317
  throw new Error(`Could not find a group_id for conversation ${conversationId}@${conversationDomain}`);
330
318
  }
331
- return bazinga64_1.Decoder.fromBase64(groupId).asBytes;
319
+ return groupId;
332
320
  }
333
321
  /**
334
322
  * ## MLS only ##
@@ -1,10 +1,6 @@
1
1
  export declare type CommonMLS = {
2
2
  groupId: string;
3
3
  };
4
- export declare type CompoundGroupIdParams = {
5
- conversationId: string;
6
- conversationDomain: string;
7
- } & CommonMLS;
8
4
  export declare type HandlePendingProposalsParams = {
9
5
  delayInMs: number;
10
6
  eventTime: string;
@@ -4,5 +4,10 @@ declare type ClientId = string;
4
4
  declare type Domain = string;
5
5
  export declare type ClientIdStringType = `${UserId}:${ClientId}@${Domain}`;
6
6
  export declare const constructFullyQualifiedClientId: (userId: UserId, clientId: ClientId, domain: Domain) => ClientIdStringType;
7
+ export declare const parseFullQualifiedClientId: (qualifiedId: string) => {
8
+ user: UserId;
9
+ client: ClientId;
10
+ domain: Domain;
11
+ };
7
12
  export declare const mapQualifiedUserClientIdsToFullyQualifiedClientIds: (qualifiedUserMap: QualifiedUserClientMap) => Uint8Array[];
8
13
  export {};
@@ -18,9 +18,19 @@
18
18
  *
19
19
  */
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.mapQualifiedUserClientIdsToFullyQualifiedClientIds = exports.constructFullyQualifiedClientId = void 0;
21
+ exports.mapQualifiedUserClientIdsToFullyQualifiedClientIds = exports.parseFullQualifiedClientId = exports.constructFullyQualifiedClientId = void 0;
22
22
  const constructFullyQualifiedClientId = (userId, clientId, domain) => `${userId}:${clientId}@${domain}`;
23
23
  exports.constructFullyQualifiedClientId = constructFullyQualifiedClientId;
24
+ const parseFullQualifiedClientId = (qualifiedId) => {
25
+ var _a;
26
+ const regexp = /([a-zA-Z0-9\-]+):([a-zA-Z0-9\-]+)@([a-zA-Z0-9\-.]+)/;
27
+ const [, user, client, domain] = (_a = qualifiedId.match(regexp)) !== null && _a !== void 0 ? _a : [];
28
+ if (!user || !client || !domain) {
29
+ throw new Error(`given client fully qualified ID is corrupted (${qualifiedId})`);
30
+ }
31
+ return { user, client, domain };
32
+ };
33
+ exports.parseFullQualifiedClientId = parseFullQualifiedClientId;
24
34
  const mapQualifiedUserClientIdsToFullyQualifiedClientIds = (qualifiedUserMap) => {
25
35
  const encoder = new TextEncoder();
26
36
  return Object.entries(qualifiedUserMap).flatMap(([domain, users]) => {
@@ -29,4 +39,4 @@ const mapQualifiedUserClientIdsToFullyQualifiedClientIds = (qualifiedUserMap) =>
29
39
  });
30
40
  };
31
41
  exports.mapQualifiedUserClientIdsToFullyQualifiedClientIds = mapQualifiedUserClientIdsToFullyQualifiedClientIds;
32
- //# sourceMappingURL=mapQualifiedUserClientIdsToFullyQualifiedClientIds.js.map
42
+ //# sourceMappingURL=fullyQualifiedClientIdUtils.js.map