mirror of https://github.com/vector-im/riot-web
Share e2ee keys when using /invite SlashCommand (#7655)
parent
15276ea3b4
commit
cbc671b19f
|
@ -43,16 +43,19 @@ export interface IInviteResult {
|
||||||
*
|
*
|
||||||
* @param {string} roomId The ID of the room to invite to
|
* @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 {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.
|
* @param {function} progressCallback optional callback, fired after each invite.
|
||||||
* @returns {Promise} Promise
|
* @returns {Promise} Promise
|
||||||
*/
|
*/
|
||||||
export function inviteMultipleToRoom(
|
export function inviteMultipleToRoom(
|
||||||
roomId: string,
|
roomId: string,
|
||||||
addresses: string[],
|
addresses: string[],
|
||||||
|
sendSharedHistoryKeys = false,
|
||||||
progressCallback?: () => void,
|
progressCallback?: () => void,
|
||||||
): Promise<IInviteResult> {
|
): Promise<IInviteResult> {
|
||||||
const inviter = new MultiInviter(roomId, progressCallback);
|
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 {
|
export function showStartChatInviteDialog(initialText = ""): void {
|
||||||
|
@ -110,8 +113,13 @@ export function isValid3pidInvite(event: MatrixEvent): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function inviteUsersToRoom(roomId: string, userIds: string[], progressCallback?: () => void): Promise<void> {
|
export function inviteUsersToRoom(
|
||||||
return inviteMultipleToRoom(roomId, userIds, progressCallback).then((result) => {
|
roomId: string,
|
||||||
|
userIds: string[],
|
||||||
|
sendSharedHistoryKeys = false,
|
||||||
|
progressCallback?: () => void,
|
||||||
|
): Promise<void> {
|
||||||
|
return inviteMultipleToRoom(roomId, userIds, sendSharedHistoryKeys, progressCallback).then((result) => {
|
||||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
showAnyInviteErrors(result.states, room, result.inviter);
|
showAnyInviteErrors(result.states, room, result.inviter);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
|
|
@ -35,7 +35,7 @@ import { linkifyAndSanitizeHtml } from './HtmlUtils';
|
||||||
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
|
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
|
||||||
import WidgetUtils from "./utils/WidgetUtils";
|
import WidgetUtils from "./utils/WidgetUtils";
|
||||||
import { textToHtmlRainbow } from "./utils/colour";
|
import { textToHtmlRainbow } from "./utils/colour";
|
||||||
import { getAddressType } from './UserAddress';
|
import { AddressType, getAddressType } from './UserAddress';
|
||||||
import { abbreviateUrl } from './utils/UrlUtils';
|
import { abbreviateUrl } from './utils/UrlUtils';
|
||||||
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils';
|
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils';
|
||||||
import { isPermalinkHost, parsePermalink } from "./utils/permalinks/Permalinks";
|
import { isPermalinkHost, parsePermalink } from "./utils/permalinks/Permalinks";
|
||||||
|
@ -500,7 +500,7 @@ export const Commands = [
|
||||||
// meaningful.
|
// meaningful.
|
||||||
let prom = Promise.resolve();
|
let prom = Promise.resolve();
|
||||||
if (
|
if (
|
||||||
getAddressType(address) === 'email' &&
|
getAddressType(address) === AddressType.Email &&
|
||||||
!MatrixClientPeg.get().getIdentityServerUrl()
|
!MatrixClientPeg.get().getIdentityServerUrl()
|
||||||
) {
|
) {
|
||||||
const defaultIdentityServerUrl = getDefaultIdentityServerUrl();
|
const defaultIdentityServerUrl = getDefaultIdentityServerUrl();
|
||||||
|
@ -541,7 +541,7 @@ export const Commands = [
|
||||||
}
|
}
|
||||||
const inviter = new MultiInviter(roomId);
|
const inviter = new MultiInviter(roomId);
|
||||||
return success(prom.then(() => {
|
return success(prom.then(() => {
|
||||||
return inviter.invite([address], reason);
|
return inviter.invite([address], reason, true);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if (inviter.getCompletionState(address) !== "invited") {
|
if (inviter.getCompletionState(address) !== "invited") {
|
||||||
throw new Error(inviter.getErrorText(address));
|
throw new Error(inviter.getErrorText(address));
|
||||||
|
|
|
@ -208,7 +208,7 @@ const CreateSpaceFromCommunityDialog: React.FC<IProps> = ({ matrixClient: cli, g
|
||||||
setProgress(Progress.InvitingUsers);
|
setProgress(Progress.InvitingUsers);
|
||||||
|
|
||||||
const userIds = [...members, ...invitedMembers].map(m => m.userId).filter(m => m !== cli.getUserId());
|
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
|
// eagerly remove it from the community panel
|
||||||
dis.dispatch(TagOrderActions.removeTag(cli, groupId));
|
dis.dispatch(TagOrderActions.removeTag(cli, groupId));
|
||||||
|
|
|
@ -761,31 +761,11 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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);
|
CountlyAnalytics.instance.trackSendInvite(startTime, this.props.roomId, targetIds.length);
|
||||||
if (!this.shouldAbortAfterInviteError(result, room)) { // handles setting error message too
|
if (!this.shouldAbortAfterInviteError(result, room)) { // handles setting error message too
|
||||||
this.props.onFinished();
|
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) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|
|
@ -17,6 +17,9 @@ limitations under the License.
|
||||||
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
||||||
import { defer, IDeferred } from "matrix-js-sdk/src/utils";
|
import { defer, IDeferred } from "matrix-js-sdk/src/utils";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
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 { MatrixClientPeg } from '../MatrixClientPeg';
|
||||||
import { AddressType, getAddressType } from '../UserAddress';
|
import { AddressType, getAddressType } from '../UserAddress';
|
||||||
|
@ -49,6 +52,7 @@ const USER_ALREADY_INVITED = "IO.ELEMENT.ALREADY_INVITED";
|
||||||
export default class MultiInviter {
|
export default class MultiInviter {
|
||||||
private readonly roomId?: string;
|
private readonly roomId?: string;
|
||||||
private readonly groupId?: string;
|
private readonly groupId?: string;
|
||||||
|
private readonly matrixClient: MatrixClient;
|
||||||
|
|
||||||
private canceled = false;
|
private canceled = false;
|
||||||
private addresses: string[] = [];
|
private addresses: string[] = [];
|
||||||
|
@ -71,6 +75,8 @@ export default class MultiInviter {
|
||||||
this.roomId = targetId;
|
this.roomId = targetId;
|
||||||
this.groupId = null;
|
this.groupId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.matrixClient = MatrixClientPeg.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get fatal() {
|
public get fatal() {
|
||||||
|
@ -83,9 +89,10 @@ export default class MultiInviter {
|
||||||
*
|
*
|
||||||
* @param {array} addresses Array of addresses to invite
|
* @param {array} addresses Array of addresses to invite
|
||||||
* @param {string} reason Reason for inviting (optional)
|
* @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
|
* @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) {
|
if (this.addresses.length > 0) {
|
||||||
throw new Error("Already inviting/invited");
|
throw new Error("Already inviting/invited");
|
||||||
}
|
}
|
||||||
|
@ -104,7 +111,30 @@ export default class MultiInviter {
|
||||||
this.deferred = defer<CompletionStates>();
|
this.deferred = defer<CompletionStates>();
|
||||||
this.inviteMore(0);
|
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);
|
const addrType = getAddressType(addr);
|
||||||
|
|
||||||
if (addrType === AddressType.Email) {
|
if (addrType === AddressType.Email) {
|
||||||
return MatrixClientPeg.get().inviteByEmail(roomId, addr);
|
return this.matrixClient.inviteByEmail(roomId, addr);
|
||||||
} else if (addrType === AddressType.MatrixUserId) {
|
} 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");
|
if (!room) throw new Error("Room not found");
|
||||||
|
|
||||||
const member = room.getMember(addr);
|
const member = room.getMember(addr);
|
||||||
|
@ -148,14 +178,14 @@ export default class MultiInviter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) {
|
if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) {
|
||||||
const profile = await MatrixClientPeg.get().getProfileInfo(addr);
|
const profile = await this.matrixClient.getProfileInfo(addr);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
// noinspection ExceptionCaughtLocallyJS
|
// noinspection ExceptionCaughtLocallyJS
|
||||||
throw new Error("User has no profile");
|
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 {
|
} else {
|
||||||
throw new Error('Unsupported address');
|
throw new Error('Unsupported address');
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ export async function upgradeRoom(
|
||||||
|
|
||||||
if (toInvite.length > 0) {
|
if (toInvite.length > 0) {
|
||||||
// Errors are handled internally to this function
|
// Errors are handled internally to this function
|
||||||
await inviteUsersToRoom(newRoomId, toInvite, () => {
|
await inviteUsersToRoom(newRoomId, toInvite, false, () => {
|
||||||
progress.inviteUsersProgress++;
|
progress.inviteUsersProgress++;
|
||||||
progressCallback?.(progress);
|
progressCallback?.(progress);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue