From 404c381ca2cfe87b20856b474a4b0fdcfa2e3b6c Mon Sep 17 00:00:00 2001 From: Djorkaeff Alexandre Date: Mon, 13 Apr 2020 09:51:16 -0300 Subject: [PATCH] [FIX] Read only channel/broadcast (#1951) * [FIX] Read only channel/broadcast * [FIX] Roles missing * [FIX] Check roles to readOnly * [FIX] Can post * [FIX] Respect post-readonly permission * [FIX] Search a room readOnly Co-authored-by: Diego Mello --- app/lib/database/index.js | 3 +- app/lib/methods/subscriptions/rooms.js | 1 + app/lib/rocketchat.js | 30 ++++++++++---- app/utils/room.js | 19 +++++++-- app/views/DirectoryView/index.js | 12 ++++-- app/views/RoomView/index.js | 55 ++++++++++++++++++++------ app/views/RoomsListView/index.js | 5 ++- app/views/ShareView/index.js | 23 ++++++++--- 8 files changed, 113 insertions(+), 35 deletions(-) diff --git a/app/lib/database/index.js b/app/lib/database/index.js index c17b18b21..67f1188ae 100644 --- a/app/lib/database/index.js +++ b/app/lib/database/index.js @@ -79,7 +79,8 @@ class DB { Message, Thread, ThreadMessage, - Upload + Upload, + Permission ], actionsEnabled: true }); diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js index e954e6f14..d80f3dd84 100644 --- a/app/lib/methods/subscriptions/rooms.js +++ b/app/lib/methods/subscriptions/rooms.js @@ -72,6 +72,7 @@ const createOrUpdateSubscription = async(subscription, room) => { autoTranslate: s.autoTranslate, autoTranslateLanguage: s.autoTranslateLanguage, lastMessage: s.lastMessage, + roles: s.roles, usernames: s.usernames, uids: s.uids }; diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index ced274270..3f33071b5 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -289,14 +289,11 @@ const RocketChat = { user = { id: userRecord.id, token: userRecord.token, - username: userRecord.username + username: userRecord.username, + roles: userRecord.roles }; } - reduxStore.dispatch(shareSetUser({ - id: user.id, - token: user.token, - username: user.username - })); + reduxStore.dispatch(shareSetUser(user)); await RocketChat.login({ resume: user.token }); } catch (e) { log(e); @@ -308,6 +305,8 @@ const RocketChat = { this.shareSDK = null; } database.share = null; + + reduxStore.dispatch(shareSetUser(null)); }, updateJitsiTimeout(rid) { @@ -580,6 +579,19 @@ const RocketChat = { } data = data.slice(0, 7); + data = data.map((sub) => { + if (sub.t !== 'd') { + return ({ + rid: sub.rid, + name: sub.name, + fname: sub.fname, + t: sub.t, + search: true + }); + } + return sub; + }); + const usernames = data.map(sub => sub.name); try { if (data.length < 7) { @@ -951,7 +963,7 @@ const RocketChat = { // get the room from database const room = await subsCollection.find(rid); // get room roles - roomRoles = room.roles; + roomRoles = room.roles || []; } catch (error) { console.log('hasPermission -> Room not found'); return permissions.reduce((result, permission) => { @@ -962,8 +974,10 @@ const RocketChat = { // get permissions from database try { const permissionsFiltered = await permissionsCollection.query(Q.where('id', Q.oneOf(permissions))).fetch(); + const shareUser = reduxStore.getState().share.user; + const loginUser = reduxStore.getState().login.user; // get user roles on the server from redux - const userRoles = (reduxStore.getState().login.user && reduxStore.getState().login.user.roles) || []; + const userRoles = (shareUser.roles || loginUser.roles) || []; // merge both roles const mergedRoles = [...new Set([...roomRoles, ...userRoles])]; diff --git a/app/utils/room.js b/app/utils/room.js index 3131a6407..6a58c1e8f 100644 --- a/app/utils/room.js +++ b/app/utils/room.js @@ -1,13 +1,26 @@ import moment from 'moment'; import I18n from '../i18n'; +import RocketChat from '../lib/rocketchat'; -export const isOwner = room => room && room.roles && room.roles.length && !!room.roles.find(role => role === 'owner'); +export const canPost = async({ rid }) => { + try { + const permission = await RocketChat.hasPermission(['post-readonly'], rid); + return permission && permission['post-readonly']; + } catch { + // do nothing + } + return false; +}; export const isMuted = (room, user) => room && room.muted && room.muted.find && !!room.muted.find(m => m === user.username); -export const isReadOnly = (room, user) => { - if (isOwner(room)) { +export const isReadOnly = async(room, user) => { + if (room.archived) { + return true; + } + const allowPost = await canPost(room); + if (allowPost) { return false; } return (room && room.ro) || isMuted(room, user); diff --git a/app/views/DirectoryView/index.js b/app/views/DirectoryView/index.js index 3828a7474..180acde91 100644 --- a/app/views/DirectoryView/index.js +++ b/app/views/DirectoryView/index.js @@ -125,10 +125,14 @@ class DirectoryView extends React.Component { this.setState(({ showOptionsDropdown }) => ({ showOptionsDropdown: !showOptionsDropdown })); } - goRoom = async({ rid, name, t }) => { + goRoom = async({ + rid, name, t, search + }) => { const { navigation } = this.props; await navigation.navigate('RoomsListView'); - navigation.navigate('RoomView', { rid, name, t }); + navigation.navigate('RoomView', { + rid, name, t, search + }); } onPressItem = async(item) => { @@ -139,7 +143,9 @@ class DirectoryView extends React.Component { this.goRoom({ rid: result.room._id, name: item.username, t: 'd' }); } } else { - this.goRoom({ rid: item._id, name: item.name, t: 'c' }); + this.goRoom({ + rid: item._id, name: item.name, t: 'c', search: true + }); } } diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index ce34d46e8..001f33277 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -65,9 +65,10 @@ const stateAttrsUpdate = [ 'editing', 'replying', 'reacting', + 'readOnly', 'member' ]; -const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'muted', 'jitsiTimeout', 'announcement', 'sysMes', 'topic', 'name', 'fname']; +const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'muted', 'jitsiTimeout', 'announcement', 'sysMes', 'topic', 'name', 'fname', 'roles']; class RoomView extends React.Component { static navigationOptions = ({ navigation, screenProps }) => { @@ -164,6 +165,7 @@ class RoomView extends React.Component { const selectedMessage = props.navigation.getParam('message'); const name = props.navigation.getParam('name'); const fname = props.navigation.getParam('fname'); + const search = props.navigation.getParam('search'); const prid = props.navigation.getParam('prid'); this.state = { joined: true, @@ -183,7 +185,7 @@ class RoomView extends React.Component { replying: !!selectedMessage, replyWithMention: false, reacting: false, - announcement: null + readOnly: false }; if (room && room.observe) { @@ -192,6 +194,12 @@ class RoomView extends React.Component { this.findAndObserveRoom(this.rid); } + this.setReadOnly(); + + if (search) { + this.updateRoom(); + } + this.messagebox = React.createRef(); this.list = React.createRef(); this.mounted = false; @@ -278,6 +286,9 @@ class RoomView extends React.Component { if (roomUpdate.topic !== prevState.roomUpdate.topic) { navigation.setParams({ subtitle: roomUpdate.topic }); } + if (!isEqual(prevState.roomUpdate.roles, roomUpdate.roles)) { + this.setReadOnly(); + } } if (((roomUpdate.fname !== prevState.roomUpdate.fname) || (roomUpdate.name !== prevState.roomUpdate.name)) && !this.tmid) { navigation.setParams({ name: this.getRoomTitle(room) }); @@ -346,6 +357,32 @@ class RoomView extends React.Component { }); } + setReadOnly = async() => { + const { room } = this.state; + const { user } = this.props; + const readOnly = await isReadOnly(room, user); + this.setState({ readOnly }); + } + + updateRoom = async() => { + const db = database.active; + + try { + const subCollection = db.collections.get('subscriptions'); + const sub = await subCollection.find(this.rid); + + const { room } = await RocketChat.getRoomInfo(this.rid); + + await db.action(async() => { + await sub.update((s) => { + Object.assign(s, room); + }); + }); + } catch { + // do nothing + } + } + init = async() => { try { this.setState({ loading: true }); @@ -762,12 +799,6 @@ class RoomView extends React.Component { } } - get isReadOnly() { - const { room } = this.state; - const { user } = this.props; - return isReadOnly(room, user); - } - blockAction = ({ actionId, appId, value, blockId, rid, mid }) => RocketChat.triggerBlockAction({ @@ -855,7 +886,7 @@ class RoomView extends React.Component { renderFooter = () => { const { - joined, room, selectedMessage, editing, replying, replyWithMention + joined, room, selectedMessage, editing, replying, replyWithMention, readOnly } = this.state; const { navigation, theme } = this.props; @@ -876,7 +907,7 @@ class RoomView extends React.Component { ); } - if (this.isReadOnly || room.archived) { + if (readOnly) { return ( {I18n.t('This_room_is_read_only')} @@ -914,7 +945,7 @@ class RoomView extends React.Component { renderActions = () => { const { - room, selectedMessage, showActions, showErrorActions, joined + room, selectedMessage, showActions, showErrorActions, joined, readOnly } = this.state; const { user, navigation @@ -935,7 +966,7 @@ class RoomView extends React.Component { editInit={this.onEditInit} replyInit={this.onReplyInit} reactionInit={this.onReactionInit} - isReadOnly={this.isReadOnly} + isReadOnly={readOnly} /> ) : null diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 84aa0027b..9cccdfc5b 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -542,8 +542,9 @@ class RoomsListView extends React.Component { name: this.getRoomTitle(item), t: item.t, prid: item.prid, - roomUserId: this.getUidDirectMessage(item), - room: item + room: item, + search: item.search, + roomUserId: this.getUidDirectMessage(item) }); } diff --git a/app/views/ShareView/index.js b/app/views/ShareView/index.js index f55523319..1582b3524 100644 --- a/app/views/ShareView/index.js +++ b/app/views/ShareView/index.js @@ -69,18 +69,29 @@ class ShareView extends React.Component { fileInfo, room, loading: false, + readOnly: false, file: { name: fileInfo ? fileInfo.name : '', description: '' } }; + + this.setReadOnly(); } componentDidMount() { + const { navigation } = this.props; + navigation.setParams({ sendMessage: this._sendMessage }); + } + + setReadOnly = async() => { const { room } = this.state; const { navigation, user } = this.props; const { username } = user; - navigation.setParams({ sendMessage: this._sendMessage, canSend: !(isReadOnly(room, { username }) || isBlocked(room)) }); + const readOnly = await isReadOnly(room, { username }); + + this.setState({ readOnly }); + navigation.setParams({ canSend: !(readOnly || isBlocked(room)) }); } bytesToSize = bytes => `${ (bytes / 1048576).toFixed(2) }MB`; @@ -237,8 +248,9 @@ class ShareView extends React.Component { renderError = () => { const { room } = this.state; + const { theme } = this.props; return ( - + { isBlocked(room) ? I18n.t('This_room_is_blocked') : I18n.t('This_room_is_read_only') @@ -249,13 +261,12 @@ class ShareView extends React.Component { } render() { - const { user, theme } = this.props; - const { username } = user; + const { theme } = this.props; const { - name, loading, isMedia, room + name, loading, isMedia, room, readOnly } = this.state; - if (isReadOnly(room, { username }) || isBlocked(room)) { + if (readOnly || isBlocked(room)) { return this.renderError(); }