diff --git a/ts/groups.ts b/ts/groups.ts index 32c5f42ec69b..fd37803bb897 100644 --- a/ts/groups.ts +++ b/ts/groups.ts @@ -1472,12 +1472,43 @@ export async function modifyGroupV2({ let refreshedCredentials = false; + const profileFetchQueue = new PQueue({ + concurrency: 3, + }); + for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) { log.info(`modifyGroupV2/${logId}: Starting attempt ${attempt}`); try { // eslint-disable-next-line no-await-in-loop await window.waitForEmptyEventQueue(); + // Fetch profiles for contacts that do not have credentials (or have + // expired credentials) + { + const membersMissingCredentials = usingCredentialsFrom.filter(member => + member.hasProfileKeyCredentialExpired() + ); + const logIds = membersMissingCredentials.map(member => + member.idForLogging() + ); + + if (logIds.length !== 0) { + log.info(`modifyGroupV2/${logId}: Fetching profiles for ${logIds}`); + } + + for (const member of membersMissingCredentials) { + member.set({ + profileKeyCredential: null, + profileKeyCredentialExpiration: null, + }); + } + + // eslint-disable-next-line no-await-in-loop + await profileFetchQueue.addAll( + membersMissingCredentials.map(member => () => member.getProfiles()) + ); + } + log.info(`modifyGroupV2/${logId}: Queuing attempt ${attempt}`); // eslint-disable-next-line no-await-in-loop @@ -1557,10 +1588,12 @@ export async function modifyGroupV2({ const logIds = usingCredentialsFrom.map(member => member.idForLogging() ); - log.warn( - `modifyGroupV2/${logId}: Profile key credentials were not ` + - `up-to-date. Updating profiles for ${logIds} and retrying` - ); + if (logIds.length !== 0) { + log.warn( + `modifyGroupV2/${logId}: Profile key credentials were not ` + + `up-to-date. Updating profiles for ${logIds} and retrying` + ); + } for (const member of usingCredentialsFrom) { member.set({ @@ -1569,9 +1602,6 @@ export async function modifyGroupV2({ }); } - const profileFetchQueue = new PQueue({ - concurrency: 3, - }); // eslint-disable-next-line no-await-in-loop await profileFetchQueue.addAll( usingCredentialsFrom.map(member => () => member.getProfiles()) @@ -1741,6 +1771,17 @@ export async function createGroupV2({ const ourACI = window.storage.user.getCheckedUuid(UUIDKind.ACI).toString(); + const ourConversation = + window.ConversationController.getOurConversationOrThrow(); + if (ourConversation.hasProfileKeyCredentialExpired()) { + log.info(`createGroupV2/${logId}: fetching our own credentials`); + ourConversation.set({ + profileKeyCredential: null, + profileKeyCredentialExpiration: null, + }); + await ourConversation.getProfiles(); + } + const membersV2: Array = [ { uuid: ourACI, @@ -1770,7 +1811,7 @@ export async function createGroupV2({ } // Refresh our local data to be sure - if (!contact.get('profileKey') || !contact.get('profileKeyCredential')) { + if (contact.hasProfileKeyCredentialExpired()) { await contact.getProfiles(); } diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index 41d68f3dc12e..0927559fdf2c 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -2056,6 +2056,8 @@ export class ConversationModel extends window.Backbone UUIDKind.ACI ); const ourPNI = window.textsecure.storage.user.getUuid(UUIDKind.PNI); + const ourConversation = + window.ConversationController.getOurConversationOrThrow(); if ( isGroupV1(this.attributes) || @@ -2068,7 +2070,7 @@ export class ConversationModel extends window.Backbone ) { await this.modifyGroupV2({ name: 'promotePendingMember', - usingCredentialsFrom: [], + usingCredentialsFrom: [ourConversation], createGroupChange: () => this.promotePendingMember(UUIDKind.ACI), }); } else if ( @@ -2078,7 +2080,7 @@ export class ConversationModel extends window.Backbone ) { await this.modifyGroupV2({ name: 'promotePendingMember', - usingCredentialsFrom: [], + usingCredentialsFrom: [ourConversation], createGroupChange: () => this.promotePendingMember(UUIDKind.PNI), }); } else if (isGroupV2(this.attributes) && this.isMember(ourACI)) { @@ -2170,18 +2172,20 @@ export class ConversationModel extends window.Backbone approvalRequired: boolean; }): Promise { const ourACI = window.textsecure.storage.user.getCheckedUuid(); + const ourConversation = + window.ConversationController.getOurConversationOrThrow(); try { if (approvalRequired) { await this.modifyGroupV2({ name: 'requestToJoin', - usingCredentialsFrom: [], + usingCredentialsFrom: [ourConversation], inviteLinkPassword, createGroupChange: () => this.addPendingApprovalRequest(), }); } else { await this.modifyGroupV2({ name: 'joinGroup', - usingCredentialsFrom: [], + usingCredentialsFrom: [ourConversation], inviteLinkPassword, createGroupChange: () => this.addMember(ourACI), }); @@ -2284,6 +2288,8 @@ export class ConversationModel extends window.Backbone const ourACI = window.textsecure.storage.user.getCheckedUuid(UUIDKind.ACI); const ourPNI = window.textsecure.storage.user.getUuid(UUIDKind.PNI); + const ourConversation = + window.ConversationController.getOurConversationOrThrow(); if (this.isMemberPending(ourACI)) { await this.modifyGroupV2({ @@ -2294,7 +2300,7 @@ export class ConversationModel extends window.Backbone } else if (this.isMember(ourACI)) { await this.modifyGroupV2({ name: 'delete', - usingCredentialsFrom: [], + usingCredentialsFrom: [ourConversation], createGroupChange: () => this.removeMember(ourACI), }); // Keep PNI in pending if ACI was a member. @@ -2418,9 +2424,7 @@ export class ConversationModel extends window.Backbone }); await this.modifyGroupV2({ name: 'removePendingMember', - usingCredentialsFrom: conversationIds - .map(id => window.ConversationController.get(id)) - .filter(isNotNil), + usingCredentialsFrom: [], createGroupChange: () => this.removePendingMember(uuids), extraConversationsForSend: conversationIds, }); @@ -2444,14 +2448,14 @@ export class ConversationModel extends window.Backbone if (this.isMemberRequestingToJoin(uuid)) { await this.modifyGroupV2({ name: 'denyPendingApprovalRequest', - usingCredentialsFrom: [pendingMember], + usingCredentialsFrom: [], createGroupChange: () => this.denyPendingApprovalRequest(uuid), extraConversationsForSend: [conversationId], }); } else if (this.isMemberPending(uuid)) { await this.modifyGroupV2({ name: 'removePendingMember', - usingCredentialsFrom: [pendingMember], + usingCredentialsFrom: [], createGroupChange: () => this.removePendingMember([uuid]), extraConversationsForSend: [conversationId], }); @@ -2476,14 +2480,14 @@ export class ConversationModel extends window.Backbone if (this.isMemberRequestingToJoin(uuid)) { await this.modifyGroupV2({ name: 'denyPendingApprovalRequest', - usingCredentialsFrom: [pendingMember], + usingCredentialsFrom: [], createGroupChange: () => this.denyPendingApprovalRequest(uuid), extraConversationsForSend: [conversationId], }); } else if (this.isMemberPending(uuid)) { await this.modifyGroupV2({ name: 'removePendingMember', - usingCredentialsFrom: [pendingMember], + usingCredentialsFrom: [], createGroupChange: () => this.removePendingMember([uuid]), extraConversationsForSend: [conversationId], }); @@ -4783,13 +4787,18 @@ export class ConversationModel extends window.Backbone } hasProfileKeyCredentialExpired(): boolean { + const profileKey = this.get('profileKey'); + if (!profileKey) { + return false; + } + const profileKeyCredential = this.get('profileKeyCredential'); const profileKeyCredentialExpiration = this.get( 'profileKeyCredentialExpiration' ); if (!profileKeyCredential) { - return false; + return true; } if (!isNumber(profileKeyCredentialExpiration)) {