From cbc671b19f6cc6e3054f9daa10ffd86f106b87af Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 28 Jan 2022 10:02:37 +0000 Subject: [PATCH] Share e2ee keys when using /invite SlashCommand (#7655) --- src/RoomInvite.tsx | 14 +++++-- src/SlashCommands.tsx | 6 +-- .../CreateSpaceFromCommunityDialog.tsx | 2 +- src/components/views/dialogs/InviteDialog.tsx | 22 +--------- src/utils/MultiInviter.ts | 42 ++++++++++++++++--- src/utils/RoomUpgrade.ts | 2 +- 6 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/RoomInvite.tsx b/src/RoomInvite.tsx index 17058d2bc8..a6eefaeb81 100644 --- a/src/RoomInvite.tsx +++ b/src/RoomInvite.tsx @@ -43,16 +43,19 @@ export interface IInviteResult { * * @param {string} roomId The ID of the room to invite to * @param {string[]} addresses Array of strings of addresses to invite. May be matrix IDs or 3pids. + * @param {boolean} sendSharedHistoryKeys whether to share e2ee keys with the invitees if applicable. * @param {function} progressCallback optional callback, fired after each invite. * @returns {Promise} Promise */ export function inviteMultipleToRoom( roomId: string, addresses: string[], + sendSharedHistoryKeys = false, progressCallback?: () => void, ): Promise { const inviter = new MultiInviter(roomId, progressCallback); - return inviter.invite(addresses).then(states => Promise.resolve({ states, inviter })); + return inviter.invite(addresses, undefined, sendSharedHistoryKeys) + .then(states => Promise.resolve({ states, inviter })); } export function showStartChatInviteDialog(initialText = ""): void { @@ -110,8 +113,13 @@ export function isValid3pidInvite(event: MatrixEvent): boolean { return true; } -export function inviteUsersToRoom(roomId: string, userIds: string[], progressCallback?: () => void): Promise { - return inviteMultipleToRoom(roomId, userIds, progressCallback).then((result) => { +export function inviteUsersToRoom( + roomId: string, + userIds: string[], + sendSharedHistoryKeys = false, + progressCallback?: () => void, +): Promise { + return inviteMultipleToRoom(roomId, userIds, sendSharedHistoryKeys, progressCallback).then((result) => { const room = MatrixClientPeg.get().getRoom(roomId); showAnyInviteErrors(result.states, room, result.inviter); }).catch((err) => { diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 40fe36679c..8250e30251 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -35,7 +35,7 @@ import { linkifyAndSanitizeHtml } from './HtmlUtils'; import QuestionDialog from "./components/views/dialogs/QuestionDialog"; import WidgetUtils from "./utils/WidgetUtils"; import { textToHtmlRainbow } from "./utils/colour"; -import { getAddressType } from './UserAddress'; +import { AddressType, getAddressType } from './UserAddress'; import { abbreviateUrl } from './utils/UrlUtils'; import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils'; import { isPermalinkHost, parsePermalink } from "./utils/permalinks/Permalinks"; @@ -500,7 +500,7 @@ export const Commands = [ // meaningful. let prom = Promise.resolve(); if ( - getAddressType(address) === 'email' && + getAddressType(address) === AddressType.Email && !MatrixClientPeg.get().getIdentityServerUrl() ) { const defaultIdentityServerUrl = getDefaultIdentityServerUrl(); @@ -541,7 +541,7 @@ export const Commands = [ } const inviter = new MultiInviter(roomId); return success(prom.then(() => { - return inviter.invite([address], reason); + return inviter.invite([address], reason, true); }).then(() => { if (inviter.getCompletionState(address) !== "invited") { throw new Error(inviter.getErrorText(address)); diff --git a/src/components/views/dialogs/CreateSpaceFromCommunityDialog.tsx b/src/components/views/dialogs/CreateSpaceFromCommunityDialog.tsx index 9674054e69..1c91a624a5 100644 --- a/src/components/views/dialogs/CreateSpaceFromCommunityDialog.tsx +++ b/src/components/views/dialogs/CreateSpaceFromCommunityDialog.tsx @@ -208,7 +208,7 @@ const CreateSpaceFromCommunityDialog: React.FC = ({ matrixClient: cli, g setProgress(Progress.InvitingUsers); const userIds = [...members, ...invitedMembers].map(m => m.userId).filter(m => m !== cli.getUserId()); - await inviteUsersToRoom(roomId, userIds, () => setProgress(p => p + 1)); + await inviteUsersToRoom(roomId, userIds, false, () => setProgress(p => p + 1)); // eagerly remove it from the community panel dis.dispatch(TagOrderActions.removeTag(cli, groupId)); diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index e2ad08162a..4cfc532703 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -761,31 +761,11 @@ export default class InviteDialog extends React.PureComponent { + public invite(addresses, reason?: string, sendSharedHistoryKeys = false): Promise { if (this.addresses.length > 0) { throw new Error("Already inviting/invited"); } @@ -104,7 +111,30 @@ export default class MultiInviter { this.deferred = defer(); this.inviteMore(0); - return this.deferred.promise; + if (!sendSharedHistoryKeys || !this.roomId || !this.matrixClient.isRoomEncrypted(this.roomId)) { + return this.deferred.promise; + } + + const room = this.matrixClient.getRoom(this.roomId); + const visibilityEvent = room?.currentState.getStateEvents(EventType.RoomHistoryVisibility, ""); + const visibility = visibilityEvent?.getContent().history_visibility; + + if (visibility !== HistoryVisibility.WorldReadable && visibility !== HistoryVisibility.Shared) { + return this.deferred.promise; + } + + return this.deferred.promise.then(async states => { + const invitedUsers = []; + for (const [addr, state] of Object.entries(states)) { + if (state === InviteState.Invited && getAddressType(addr) === AddressType.MatrixUserId) { + invitedUsers.push(addr); + } + } + logger.log("Sharing history with", invitedUsers); + await this.matrixClient.sendSharedHistoryKeys(this.roomId, invitedUsers); + + return states; + }); } /** @@ -129,9 +159,9 @@ export default class MultiInviter { const addrType = getAddressType(addr); if (addrType === AddressType.Email) { - return MatrixClientPeg.get().inviteByEmail(roomId, addr); + return this.matrixClient.inviteByEmail(roomId, addr); } else if (addrType === AddressType.MatrixUserId) { - const room = MatrixClientPeg.get().getRoom(roomId); + const room = this.matrixClient.getRoom(roomId); if (!room) throw new Error("Room not found"); const member = room.getMember(addr); @@ -148,14 +178,14 @@ export default class MultiInviter { } if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) { - const profile = await MatrixClientPeg.get().getProfileInfo(addr); + const profile = await this.matrixClient.getProfileInfo(addr); if (!profile) { // noinspection ExceptionCaughtLocallyJS throw new Error("User has no profile"); } } - return MatrixClientPeg.get().invite(roomId, addr, undefined, this.reason); + return this.matrixClient.invite(roomId, addr, undefined, this.reason); } else { throw new Error('Unsupported address'); } diff --git a/src/utils/RoomUpgrade.ts b/src/utils/RoomUpgrade.ts index e8189b0c0f..3f3369a920 100644 --- a/src/utils/RoomUpgrade.ts +++ b/src/utils/RoomUpgrade.ts @@ -117,7 +117,7 @@ export async function upgradeRoom( if (toInvite.length > 0) { // Errors are handled internally to this function - await inviteUsersToRoom(newRoomId, toInvite, () => { + await inviteUsersToRoom(newRoomId, toInvite, false, () => { progress.inviteUsersProgress++; progressCallback?.(progress); });