mirror of https://github.com/vector-im/riot-web
Offer to unban user during invite if inviter has sufficient permissions (#11256)
* Offer to unban user during invite if inviter has sufficient permissions * Improve unban check in MultiInviter * Improve coverage * Update src/utils/MultiInviter.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>pull/28788/head^2
parent
86d3ec8154
commit
63bdd84c94
|
@ -735,6 +735,8 @@
|
||||||
"Not a valid %(brand)s keyfile": "Not a valid %(brand)s keyfile",
|
"Not a valid %(brand)s keyfile": "Not a valid %(brand)s keyfile",
|
||||||
"Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?",
|
"Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?",
|
||||||
"Unrecognised address": "Unrecognised address",
|
"Unrecognised address": "Unrecognised address",
|
||||||
|
"Unban": "Unban",
|
||||||
|
"User cannot be invited until they are unbanned": "User cannot be invited until they are unbanned",
|
||||||
"You do not have permission to invite people to this space.": "You do not have permission to invite people to this space.",
|
"You do not have permission to invite people to this space.": "You do not have permission to invite people to this space.",
|
||||||
"You do not have permission to invite people to this room.": "You do not have permission to invite people to this room.",
|
"You do not have permission to invite people to this room.": "You do not have permission to invite people to this room.",
|
||||||
"User is already invited to the space": "User is already invited to the space",
|
"User is already invited to the space": "User is already invited to the space",
|
||||||
|
@ -1706,7 +1708,6 @@
|
||||||
"Upload custom sound": "Upload custom sound",
|
"Upload custom sound": "Upload custom sound",
|
||||||
"Browse": "Browse",
|
"Browse": "Browse",
|
||||||
"Failed to unban": "Failed to unban",
|
"Failed to unban": "Failed to unban",
|
||||||
"Unban": "Unban",
|
|
||||||
"Banned by %(displayName)s": "Banned by %(displayName)s",
|
"Banned by %(displayName)s": "Banned by %(displayName)s",
|
||||||
"Reason": "Reason",
|
"Reason": "Reason",
|
||||||
"Error changing power level requirement": "Error changing power level requirement",
|
"Error changing power level requirement": "Error changing power level requirement",
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { _t } from "../languageHandler";
|
||||||
import Modal from "../Modal";
|
import Modal from "../Modal";
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import AskInviteAnywayDialog from "../components/views/dialogs/AskInviteAnywayDialog";
|
import AskInviteAnywayDialog from "../components/views/dialogs/AskInviteAnywayDialog";
|
||||||
|
import ConfirmUserActionDialog from "../components/views/dialogs/ConfirmUserActionDialog";
|
||||||
|
|
||||||
export enum InviteState {
|
export enum InviteState {
|
||||||
Invited = "invited",
|
Invited = "invited",
|
||||||
|
@ -48,6 +49,7 @@ export type CompletionStates = Record<string, InviteState>;
|
||||||
|
|
||||||
const USER_ALREADY_JOINED = "IO.ELEMENT.ALREADY_JOINED";
|
const USER_ALREADY_JOINED = "IO.ELEMENT.ALREADY_JOINED";
|
||||||
const USER_ALREADY_INVITED = "IO.ELEMENT.ALREADY_INVITED";
|
const USER_ALREADY_INVITED = "IO.ELEMENT.ALREADY_INVITED";
|
||||||
|
const USER_BANNED = "IO.ELEMENT.BANNED";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invites multiple addresses to a room, handling rate limiting from the server
|
* Invites multiple addresses to a room, handling rate limiting from the server
|
||||||
|
@ -170,6 +172,34 @@ export default class MultiInviter {
|
||||||
errcode: USER_ALREADY_INVITED,
|
errcode: USER_ALREADY_INVITED,
|
||||||
error: "Member already invited",
|
error: "Member already invited",
|
||||||
});
|
});
|
||||||
|
} else if (member?.membership === "ban") {
|
||||||
|
let proceed = false;
|
||||||
|
// Check if we can unban the invitee.
|
||||||
|
// See https://spec.matrix.org/v1.7/rooms/v10/#authorization-rules, particularly 4.5.3 and 4.5.4.
|
||||||
|
const ourMember = room.getMember(this.matrixClient.getSafeUserId());
|
||||||
|
if (
|
||||||
|
!!ourMember &&
|
||||||
|
member.powerLevel < ourMember.powerLevel &&
|
||||||
|
room.currentState.hasSufficientPowerLevelFor("ban", ourMember.powerLevel) &&
|
||||||
|
room.currentState.hasSufficientPowerLevelFor("kick", ourMember.powerLevel)
|
||||||
|
) {
|
||||||
|
const { finished } = Modal.createDialog(ConfirmUserActionDialog, {
|
||||||
|
member,
|
||||||
|
action: _t("Unban"),
|
||||||
|
title: _t("User cannot be invited until they are unbanned"),
|
||||||
|
});
|
||||||
|
[proceed = false] = await finished;
|
||||||
|
if (proceed) {
|
||||||
|
await this.matrixClient.unban(roomId, member.userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!proceed) {
|
||||||
|
throw new MatrixError({
|
||||||
|
errcode: USER_BANNED,
|
||||||
|
error: "Member is banned",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) {
|
if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) {
|
||||||
|
@ -268,6 +298,7 @@ export default class MultiInviter {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "M_BAD_STATE":
|
case "M_BAD_STATE":
|
||||||
|
case USER_BANNED:
|
||||||
errorText = _t("The user must be unbanned before they can be invited.");
|
errorText = _t("The user must be unbanned before they can be invited.");
|
||||||
break;
|
break;
|
||||||
case "M_UNSUPPORTED_ROOM_VERSION":
|
case "M_UNSUPPORTED_ROOM_VERSION":
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
import { MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, MatrixError, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
|
||||||
import Modal, { ComponentType, ComponentProps } from "../../src/Modal";
|
import Modal, { ComponentType, ComponentProps } from "../../src/Modal";
|
||||||
|
@ -23,6 +23,7 @@ import SettingsStore from "../../src/settings/SettingsStore";
|
||||||
import MultiInviter, { CompletionStates } from "../../src/utils/MultiInviter";
|
import MultiInviter, { CompletionStates } from "../../src/utils/MultiInviter";
|
||||||
import * as TestUtilsMatrix from "../test-utils";
|
import * as TestUtilsMatrix from "../test-utils";
|
||||||
import AskInviteAnywayDialog from "../../src/components/views/dialogs/AskInviteAnywayDialog";
|
import AskInviteAnywayDialog from "../../src/components/views/dialogs/AskInviteAnywayDialog";
|
||||||
|
import ConfirmUserActionDialog from "../../src/components/views/dialogs/ConfirmUserActionDialog";
|
||||||
|
|
||||||
const ROOMID = "!room:server";
|
const ROOMID = "!room:server";
|
||||||
|
|
||||||
|
@ -89,6 +90,7 @@ describe("MultiInviter", () => {
|
||||||
client.getProfileInfo.mockImplementation((userId: string) => {
|
client.getProfileInfo.mockImplementation((userId: string) => {
|
||||||
return MXID_PROFILE_STATES[userId] || Promise.reject();
|
return MXID_PROFILE_STATES[userId] || Promise.reject();
|
||||||
});
|
});
|
||||||
|
client.unban = jest.fn();
|
||||||
|
|
||||||
inviter = new MultiInviter(client, ROOMID);
|
inviter = new MultiInviter(client, ROOMID);
|
||||||
});
|
});
|
||||||
|
@ -154,5 +156,36 @@ describe("MultiInviter", () => {
|
||||||
`"Cannot invite user by email without an identity server. You can connect to one under "Settings"."`,
|
`"Cannot invite user by email without an identity server. You can connect to one under "Settings"."`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should ask if user wants to unban user if they have permission", async () => {
|
||||||
|
mocked(Modal.createDialog).mockImplementation(
|
||||||
|
(Element: ComponentType, props?: ComponentProps<ComponentType>): any => {
|
||||||
|
// We stub out the modal with an immediate affirmative (proceed) return
|
||||||
|
return { finished: Promise.resolve([true]) };
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const room = new Room(ROOMID, client, client.getSafeUserId());
|
||||||
|
mocked(client.getRoom).mockReturnValue(room);
|
||||||
|
const ourMember = new RoomMember(ROOMID, client.getSafeUserId());
|
||||||
|
ourMember.membership = "join";
|
||||||
|
ourMember.powerLevel = 100;
|
||||||
|
const member = new RoomMember(ROOMID, MXID1);
|
||||||
|
member.membership = "ban";
|
||||||
|
member.powerLevel = 0;
|
||||||
|
room.getMember = (userId: string) => {
|
||||||
|
if (userId === client.getSafeUserId()) return ourMember;
|
||||||
|
if (userId === MXID1) return member;
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
await inviter.invite([MXID1]);
|
||||||
|
expect(Modal.createDialog).toHaveBeenCalledWith(ConfirmUserActionDialog, {
|
||||||
|
member,
|
||||||
|
title: "User cannot be invited until they are unbanned",
|
||||||
|
action: "Unban",
|
||||||
|
});
|
||||||
|
expect(client.unban).toHaveBeenCalledWith(ROOMID, MXID1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue