Add sanity checks to prevent users from ignoring themselves (#30079)
* Fix missing state * Throw error if membership event changes * Write test * Fix broken tests * Cache inviter when room is loaded * Translate error message for dialog
This commit is contained in:
@@ -370,6 +370,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
private unmounted = false;
|
||||
private permalinkCreators: Record<string, RoomPermalinkCreator> = {};
|
||||
|
||||
// The userId from which we received this invite.
|
||||
// Only populated if the membership of our user is invite.
|
||||
private inviter?: string;
|
||||
|
||||
private roomView = createRef<HTMLDivElement>();
|
||||
private searchResultsPanel = createRef<ScrollPanel>();
|
||||
private messagePanel: TimelinePanel | null = null;
|
||||
@@ -1350,6 +1354,11 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
// after a successful peek, or after we join the room).
|
||||
private onRoomLoaded = (room: Room): void => {
|
||||
if (this.unmounted) return;
|
||||
|
||||
// Store the inviter so that we can know who invited us to this room even if
|
||||
// the membership event changes.
|
||||
this.inviter = this.getInviterFromRoom(room);
|
||||
|
||||
// Attach a widget store listener only when we get a room
|
||||
this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange);
|
||||
|
||||
@@ -1729,8 +1738,20 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
});
|
||||
};
|
||||
|
||||
private getInviterFromRoom(room: Room): string | undefined {
|
||||
const ownUserId = this.context.client?.getSafeUserId();
|
||||
if (!ownUserId) return;
|
||||
|
||||
const myMember = room.getMember(ownUserId);
|
||||
const memberEvent = myMember?.events.member;
|
||||
const senderId = memberEvent?.getSender();
|
||||
|
||||
if (memberEvent?.getContent().membership === KnownMembership.Invite) return senderId;
|
||||
}
|
||||
|
||||
private onDeclineAndBlockButtonClicked = async (): Promise<void> => {
|
||||
if (!this.state.room || !this.context.client) return;
|
||||
|
||||
const [shouldReject, ignoreUser, reportRoom] = await Modal.createDialog(DeclineAndBlockInviteDialog, {
|
||||
roomName: this.state.room.name,
|
||||
}).finished;
|
||||
@@ -1745,11 +1766,20 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
const actions: Promise<unknown>[] = [];
|
||||
|
||||
if (ignoreUser) {
|
||||
const myMember = this.state.room.getMember(this.context.client!.getSafeUserId());
|
||||
const inviteEvent = myMember!.events.member;
|
||||
const ignoredUsers = this.context.client.getIgnoredUsers();
|
||||
ignoredUsers.push(inviteEvent!.getSender()!); // de-duped internally in the js-sdk
|
||||
actions.push(this.context.client.setIgnoredUsers(ignoredUsers));
|
||||
const doIgnore = async (): Promise<void> => {
|
||||
const ownUserId = this.context.client!.getSafeUserId();
|
||||
if (!this.inviter || this.inviter === ownUserId) {
|
||||
// This is unlikely to happen since we cache the inviter as early as possible.
|
||||
// However, we still do this check here to be double sure.
|
||||
throw new CannotDetermineUserError(
|
||||
"Cannot determine which user to ignore since the member event has changed.",
|
||||
);
|
||||
}
|
||||
const ignoredUsers = this.context.client!.getIgnoredUsers();
|
||||
ignoredUsers.push(this.inviter); // de-duped internally in the js-sdk
|
||||
await this.context.client!.setIgnoredUsers(ignoredUsers);
|
||||
};
|
||||
actions.push(doIgnore());
|
||||
}
|
||||
|
||||
if (reportRoom !== false) {
|
||||
@@ -1766,7 +1796,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
} catch (error) {
|
||||
logger.error(`Failed to reject invite: ${error}`);
|
||||
|
||||
const msg = error instanceof Error ? error.message : JSON.stringify(error);
|
||||
let msg: string = "";
|
||||
if (error instanceof CannotDetermineUserError) {
|
||||
msg = _t("room|failed_determine_user");
|
||||
} else if (error instanceof Error) {
|
||||
msg = error.message;
|
||||
} else {
|
||||
msg = JSON.stringify(error);
|
||||
}
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: _t("room|failed_reject_invite"),
|
||||
description: msg,
|
||||
@@ -1783,6 +1820,9 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.setState({
|
||||
rejecting: true,
|
||||
});
|
||||
await this.context.client.leave(this.state.room.roomId);
|
||||
defaultDispatcher.dispatch({ action: Action.ViewHomePage });
|
||||
this.setState({
|
||||
@@ -2612,3 +2652,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CannotDetermineUserError extends Error {
|
||||
public name = "CannotDetermineUserError";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user