From f56e0ddb5f6bf7546be9d508b6926c060a406f1c Mon Sep 17 00:00:00 2001 From: td Date: Mon, 1 Aug 2022 14:27:10 +0530 Subject: [PATCH] fix: check for m.call permissions in groupCallEnabled --- lib/src/client.dart | 1 + lib/src/room.dart | 38 +++++++++++++-- test/room_test.dart | 111 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 140 insertions(+), 10 deletions(-) diff --git a/lib/src/client.dart b/lib/src/client.dart index e3312a7b..dcd01af5 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -641,6 +641,7 @@ class Client extends MatrixApi { powerLevelContentOverride ??= {}; powerLevelContentOverride['events'] = { 'org.matrix.msc3401.call.member': 0, + 'org.matrix.msc3401.call': 0, }; } final roomId = await createRoom( diff --git a/lib/src/room.dart b/lib/src/room.dart index 207c6be5..2db9ffd1 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -1660,14 +1660,39 @@ class Room { /// The level required to ban a user. bool get canBan => _hasPermissionFor('ban'); + /// returns if user can change a particular state event by comparing `ownPowerLevel` + /// with possible overrides in `events`, if not present compares `ownPowerLevel` + /// with state_default + bool canChangeStateEvent(String action) { + return powerForChangingStateEvent(action) <= ownPowerLevel; + } + + /// returns the powerlevel required for chaning the `action` defaults to + /// state_default if `action` isn't specified in events override + int powerForChangingStateEvent(String action) { + final powerLevelMap = getState(EventTypes.RoomPowerLevels)?.content; + return powerLevelMap! + .tryGetMap('events') + ?.tryGet(action) ?? + powerLevelMap.tryGet('state_default') ?? + 50; + } + + bool get canCreateGroupCall => + canChangeStateEvent('org.matrix.msc3401.call') && groupCallsEnabled; + bool get canJoinGroupCall => + canChangeStateEvent('org.matrix.msc3401.call.member') && + groupCallsEnabled; + /// if returned value is not null `org.matrix.msc3401.call.member` is present /// and group calls can be used bool get groupCallsEnabled { final powerLevelMap = getState(EventTypes.RoomPowerLevels)?.content; - final groupCallPowerLevel = - powerLevelMap?.tryGetMap('events')?['org.matrix.msc3401.call.member']; - return groupCallPowerLevel != null && - groupCallPowerLevel <= getDefaultPowerLevel(powerLevelMap!); + if (powerLevelMap == null) return false; + return powerForChangingStateEvent('org.matrix.msc3401.call.member') <= + getDefaultPowerLevel(powerLevelMap) && + powerForChangingStateEvent('org.matrix.msc3401.call') <= + getDefaultPowerLevel(powerLevelMap); } /// sets the `org.matrix.msc3401.call.member` power level to users default for @@ -1677,10 +1702,13 @@ class Room { final currentPowerLevelsMap = getState(EventTypes.RoomPowerLevels)?.content; if (currentPowerLevelsMap != null) { final newPowerLevelMap = currentPowerLevelsMap; - newPowerLevelMap['events'].addAll({ + final eventsMap = newPowerLevelMap['events'] ?? {}; + eventsMap.addAll({ + 'org.matrix.msc3401.call': getDefaultPowerLevel(currentPowerLevelsMap), 'org.matrix.msc3401.call.member': getDefaultPowerLevel(currentPowerLevelsMap) }); + newPowerLevelMap.addAll({'events': eventsMap}); await client.setRoomStateWithKey( id, EventTypes.RoomPowerLevels, diff --git a/test/room_test.dart b/test/room_test.dart index a466ad22..a9149bcc 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -420,6 +420,33 @@ void main() { expect(room.canSendEvent('m.room.member'), true); expect(room.powerLevels, room.getState('m.room.power_levels')?.content['users']); + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.power_levels', + room: room, + eventId: '123', + content: { + 'ban': 50, + 'events': { + 'm.room.name': 'lannaForcedMeToTestThis', + 'm.room.power_levels': 100, + }, + 'events_default': 0, + 'invite': 50, + 'kick': 50, + 'notifications': {'room': 20}, + 'redact': 50, + 'state_default': 60, + 'users': {'@test:fakeServer.notExisting': 100}, + 'users_default': 10 + }, + originServerTs: DateTime.now(), + stateKey: ''), + ); + expect(room.powerForChangingStateEvent('m.room.name'), 60); + expect(room.powerForChangingStateEvent('m.room.power_levels'), 100); + expect(room.powerForChangingStateEvent('m.room.nonExisting'), 60); room.setState( Event( @@ -461,6 +488,8 @@ void main() { test('Enabling group calls', () async { expect(room.groupCallsEnabled, false); + + // users default is 0 and so group calls not enabled room.setState( Event( senderId: '@test:example.com', @@ -470,7 +499,6 @@ void main() { content: { 'events': {'org.matrix.msc3401.call.member': 100}, 'state_default': 50, - 'users': {'@test:fakeServer.notExisting': 100}, 'users_default': 0 }, originServerTs: DateTime.now(), @@ -478,6 +506,8 @@ void main() { ), ); expect(room.groupCallsEnabled, false); + + // one of the group call permissions is unspecified in events override room.setState( Event( senderId: '@test:example.com', @@ -487,14 +517,62 @@ void main() { content: { 'events': {'org.matrix.msc3401.call.member': 27}, 'state_default': 50, - 'users': {'@test:fakeServer.notExisting': 100}, - 'users_default': 100 + 'users_default': 49 + }, + originServerTs: DateTime.now(), + stateKey: '', + ), + ); + expect(room.groupCallsEnabled, false); + + // only override one of the group calls permission, other one still less + // than users_default and state_default + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.power_levels', + room: room, + eventId: '123a', + content: { + 'events': { + 'org.matrix.msc3401.call.member': 27, + 'org.matrix.msc3401.call': 0 + }, + 'state_default': 50, + 'users_default': 2 + }, + originServerTs: DateTime.now(), + stateKey: '', + ), + ); + expect(room.groupCallsEnabled, false); + expect(room.canJoinGroupCall, false); + expect(room.canCreateGroupCall, false); + + // state_default 50 and user_default 26, but override evnents present + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.power_levels', + room: room, + eventId: '123a', + content: { + 'events': { + 'org.matrix.msc3401.call.member': 25, + 'org.matrix.msc3401.call': 25 + }, + 'state_default': 50, + 'users_default': 26 }, originServerTs: DateTime.now(), stateKey: '', ), ); expect(room.groupCallsEnabled, true); + expect(room.canJoinGroupCall, true); + expect(room.canCreateGroupCall, true); + + // state_default 50 and user_default 0, use enableGroupCall room.setState( Event( senderId: '@test:example.com', @@ -502,7 +580,6 @@ void main() { room: room, eventId: '123', content: { - 'events': {}, 'state_default': 50, 'users': {'@test:fakeServer.notExisting': 100}, 'users_default': 0 @@ -511,8 +588,12 @@ void main() { stateKey: ''), ); expect(room.groupCallsEnabled, false); + expect(room.canJoinGroupCall, false); + expect(room.canCreateGroupCall, false); await room.enableGroupCalls(); expect(room.groupCallsEnabled, true); + + // state_default 50 and user_default unspecified, use enableGroupCall room.setState( Event( senderId: '@test:example.com', @@ -520,7 +601,6 @@ void main() { room: room, eventId: '123', content: { - 'events': {}, 'state_default': 50, 'users': {'@test:fakeServer.notExisting': 100}, }, @@ -530,6 +610,27 @@ void main() { ); await room.enableGroupCalls(); expect(room.groupCallsEnabled, true); + expect(room.canJoinGroupCall, true); + expect(room.canCreateGroupCall, true); + + // state_default is 0 so users should be able to send state events + room.setState( + Event( + senderId: '@test:example.com', + type: 'm.room.power_levels', + room: room, + eventId: '123', + content: { + 'state_default': 0, + 'users': {'@test:fakeServer.notExisting': 100}, + }, + originServerTs: DateTime.now(), + stateKey: '', + ), + ); + expect(room.groupCallsEnabled, true); + expect(room.canJoinGroupCall, true); + expect(room.canCreateGroupCall, true); room.setState( Event( senderId: '@test:example.com',