From 4026ae09bb4d38a0fe445ccda3e5f4244ce11766 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 4 Sep 2018 15:30:16 +0200 Subject: [PATCH] check power levels without relying on membership as this might not be known for the syncing user. instead, add a method to room which always knows the syncing user's membership --- CHANGELOG.md | 1 + spec/unit/room-state.spec.js | 15 --------------- spec/unit/room.spec.js | 15 ++++++++++++++- src/models/room-state.js | 22 +++++++++++++--------- src/models/room.js | 11 +++++++++++ 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7a80983411..5298d7f35b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ BREAKING CHANGE --------------- * `MatrixClient::startClient` now returns a Promise. No method should be called on the client before that promise resolves. Before this method didn't return anything. + * `RoomState::maySendEvent('m.room.message', userId)` & `RoomState::maySendMessage(userId)` do not check the membership of the user anymore, only the power level. To check if the syncing user is allowed to write in a room, use `Room::maySendMessage()` as `RoomState` is not always aware of the syncing user's membership anymore, in case lazy loading of members is enabled. Changes in [0.11.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.0) (TDB) ================================================================================================== diff --git a/spec/unit/room-state.spec.js b/spec/unit/room-state.spec.js index 24f94018490..6619c64a7ce 100644 --- a/spec/unit/room-state.spec.js +++ b/spec/unit/room-state.spec.js @@ -447,13 +447,6 @@ describe("RoomState", function() { }); describe("maySendStateEvent", function() { - it("should say non-joined members may not send state", - function() { - expect(state.maySendStateEvent( - 'm.room.name', "@nobody:nowhere", - )).toEqual(false); - }); - it("should say any member may send state with no power level event", function() { expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true); @@ -640,14 +633,6 @@ describe("RoomState", function() { }); describe("maySendEvent", function() { - it("should say non-joined members may not send events", - function() { - expect(state.maySendEvent( - 'm.room.message', "@nobody:nowhere", - )).toEqual(false); - expect(state.maySendMessage("@nobody:nowhere")).toEqual(false); - }); - it("should say any member may send events with no power level event", function() { expect(state.maySendEvent('m.room.message', userA)).toEqual(true); diff --git a/spec/unit/room.spec.js b/spec/unit/room.spec.js index b98559ecf5a..d20da76c947 100644 --- a/spec/unit/room.spec.js +++ b/spec/unit/room.spec.js @@ -1400,7 +1400,7 @@ describe("Room", function() { describe("getMyMembership", function() { it("should return synced membership if membership isn't available yet", - async function() { + function() { const room = new Room(roomId, null, userA); room.setSyncedMembership("invite"); expect(room.getMyMembership()).toEqual("invite"); @@ -1411,4 +1411,17 @@ describe("Room", function() { expect(room.getMyMembership()).toEqual("join"); }); }); + + describe("maySendMessage", function() { + it("should return false if synced membership not join", + function() { + const room = new Room(roomId, null, userA); + room.setSyncedMembership("invite"); + expect(room.maySendMessage()).toEqual(false); + room.setSyncedMembership("leave"); + expect(room.maySendMessage()).toEqual(false); + room.setSyncedMembership("join"); + expect(room.maySendMessage()).toEqual(true); + }); + }); }); diff --git a/src/models/room-state.js b/src/models/room-state.js index fb049daa9dd..b64bb39ec3d 100644 --- a/src/models/room-state.js +++ b/src/models/room-state.js @@ -651,11 +651,6 @@ RoomState.prototype.maySendStateEvent = function(stateEventType, userId) { * according to the room's state. */ RoomState.prototype._maySendEventOfType = function(eventType, userId, state) { - const member = this.getMember(userId); - if (!member || member.membership == 'leave') { - return false; - } - const power_levels_event = this.getStateEvents('m.room.power_levels', ''); let power_levels; @@ -663,25 +658,34 @@ RoomState.prototype._maySendEventOfType = function(eventType, userId, state) { let state_default = 0; let events_default = 0; + let powerLevel = 0; if (power_levels_event) { power_levels = power_levels_event.getContent(); events_levels = power_levels.events || {}; - if (utils.isNumber(power_levels.state_default)) { + if (Number.isFinite(power_levels.state_default)) { state_default = power_levels.state_default; } else { state_default = 50; } - if (utils.isNumber(power_levels.events_default)) { + + const userPowerLevel = power_levels.users && power_levels.users[userId]; + if (Number.isFinite(userPowerLevel)) { + powerLevel = userPowerLevel; + } else if(Number.isFinite(power_levels.users_default)) { + powerLevel = power_levels.users_default; + } + + if (Number.isFinite(power_levels.events_default)) { events_default = power_levels.events_default; } } let required_level = state ? state_default : events_default; - if (utils.isNumber(events_levels[eventType])) { + if (Number.isFinite(events_levels[eventType])) { required_level = events_levels[eventType]; } - return member.powerLevel >= required_level; + return powerLevel >= required_level; }; /** diff --git a/src/models/room.js b/src/models/room.js index ce7054493e5..f78e2b02640 100644 --- a/src/models/room.js +++ b/src/models/room.js @@ -1471,6 +1471,17 @@ Room.prototype.getAccountData = function(type) { return this.accountData[type]; }; + +/** + * Returns wheter the syncing user has permission to send a message in the room + * @return {boolean} true if the user should be permitted to send + * message events into the room. + */ +Room.prototype.maySendMessage = function() { + return this.getMyMembership() === 'join' && + this.currentState.maySendEvent('m.room.message', this.myUserId); +}; + /** * This is an internal method. Calculates the name of the room from the current * room state.