Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Share e2ee keys when using /invite SlashCommand #7655

Merged
merged 4 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/RoomInvite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
progressCallback?: () => void,
): Promise<IInviteResult> {
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 {
Expand Down Expand Up @@ -110,8 +113,13 @@ export function isValid3pidInvite(event: MatrixEvent): boolean {
return true;
}

export function inviteUsersToRoom(roomId: string, userIds: string[], progressCallback?: () => void): Promise<void> {
return inviteMultipleToRoom(roomId, userIds, progressCallback).then((result) => {
export function inviteUsersToRoom(
roomId: string,
userIds: string[],
sendSharedHistoryKeys = false,
progressCallback?: () => void,
): Promise<void> {
return inviteMultipleToRoom(roomId, userIds, sendSharedHistoryKeys, progressCallback).then((result) => {
const room = MatrixClientPeg.get().getRoom(roomId);
showAnyInviteErrors(result.states, room, result.inviter);
}).catch((err) => {
Expand Down
6 changes: 3 additions & 3 deletions src/SlashCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ const CreateSpaceFromCommunityDialog: React.FC<IProps> = ({ 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));
Expand Down
22 changes: 1 addition & 21 deletions src/components/views/dialogs/InviteDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -761,31 +761,11 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
}

try {
const result = await inviteMultipleToRoom(this.props.roomId, targetIds);
const result = await inviteMultipleToRoom(this.props.roomId, targetIds, true);
CountlyAnalytics.instance.trackSendInvite(startTime, this.props.roomId, targetIds.length);
if (!this.shouldAbortAfterInviteError(result, room)) { // handles setting error message too
this.props.onFinished();
}

if (cli.isRoomEncrypted(this.props.roomId)) {
const visibilityEvent = room.currentState.getStateEvents(
"m.room.history_visibility", "",
);
const visibility = visibilityEvent && visibilityEvent.getContent() &&
visibilityEvent.getContent().history_visibility;
if (visibility == "world_readable" || visibility == "shared") {
const invitedUsers = [];
for (const [addr, state] of Object.entries(result.states)) {
if (state === "invited" && getAddressType(addr) === "mx-user-id") {
invitedUsers.push(addr);
}
}
logger.log("Sharing history with", invitedUsers);
cli.sendSharedHistoryKeys(
this.props.roomId, invitedUsers,
);
}
}
} catch (err) {
logger.error(err);
this.setState({
Expand Down
42 changes: 36 additions & 6 deletions src/utils/MultiInviter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ limitations under the License.
import { MatrixError } from "matrix-js-sdk/src/http-api";
import { defer, IDeferred } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { HistoryVisibility } from "matrix-js-sdk/src/@types/partials";

import { MatrixClientPeg } from '../MatrixClientPeg';
import { AddressType, getAddressType } from '../UserAddress';
Expand Down Expand Up @@ -49,6 +52,7 @@ const USER_ALREADY_INVITED = "IO.ELEMENT.ALREADY_INVITED";
export default class MultiInviter {
private readonly roomId?: string;
private readonly groupId?: string;
private readonly matrixClient: MatrixClient;

private canceled = false;
private addresses: string[] = [];
Expand All @@ -71,6 +75,8 @@ export default class MultiInviter {
this.roomId = targetId;
this.groupId = null;
}

this.matrixClient = MatrixClientPeg.get();
}

public get fatal() {
Expand All @@ -83,9 +89,10 @@ export default class MultiInviter {
*
* @param {array} addresses Array of addresses to invite
* @param {string} reason Reason for inviting (optional)
* @param {boolean} sendSharedHistoryKeys whether to share e2ee keys with the invitees if applicable.
* @returns {Promise} Resolved when all invitations in the queue are complete
*/
public invite(addresses, reason?: string): Promise<CompletionStates> {
public invite(addresses, reason?: string, sendSharedHistoryKeys = false): Promise<CompletionStates> {
if (this.addresses.length > 0) {
throw new Error("Already inviting/invited");
}
Expand All @@ -104,7 +111,30 @@ export default class MultiInviter {
this.deferred = defer<CompletionStates>();
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;
});
}

/**
Expand All @@ -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);
Expand All @@ -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');
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils/RoomUpgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down