[CHORE] Add permissions to Redux (#2914)

* [FIX] Add permissions to Redux store

* add only permissions being used in the app

* add clear permissions reducer

* call RocketChat.hasPermission from reducer

* add server version comparison on getPermissions

* refactor hasPermission function

* refactor hasPermission function

* remove uncomment code

* use Q.experimentalSortBy()

* add coerce function

* Change Rocketchat.hasPermission

* Apply on isReadOnly

* Apply to RoomInfoEditView

* Apply to RoomInfoView and RoomInfoEditView

* canAutoTranslate

* Unnecessary clear permissions

* Revert getUpdatedSince

* Naming fix

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Gung Wah 2021-02-26 00:41:44 +08:00 committed by GitHub
parent 6e32a15cad
commit e98116587d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 297 additions and 153 deletions

View File

@ -70,3 +70,5 @@ export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD']);
export const APP_STATE = createRequestTypes('APP_STATE', ['FOREGROUND', 'BACKGROUND']);
export const ENTERPRISE_MODULES = createRequestTypes('ENTERPRISE_MODULES', ['CLEAR', 'SET']);
export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DECODE_KEY', 'SET', 'SET_BANNER']);
export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET']);

View File

@ -0,0 +1,8 @@
import * as types from './actionsTypes';
export function setPermissions(permissions) {
return {
type: types.PERMISSIONS.SET,
permissions
};
}

View File

@ -34,20 +34,24 @@ const MessageActions = React.memo(forwardRef(({
Message_AllowPinning,
Message_AllowStarring,
Message_Read_Receipt_Store_Users,
isMasterDetail
isMasterDetail,
editMessagePermission,
deleteMessagePermission,
forceDeleteMessagePermission,
pinMessagePermission
}, ref) => {
let permissions = {};
const { showActionSheet, hideActionSheet } = useActionSheet();
const getPermissions = async() => {
try {
const permission = ['edit-message', 'delete-message', 'force-delete-message', 'pin-message'];
const permission = [editMessagePermission, deleteMessagePermission, forceDeleteMessagePermission, pinMessagePermission];
const result = await RocketChat.hasPermission(permission, room.rid);
permissions = {
hasEditPermission: result[permission[0]],
hasDeletePermission: result[permission[1]],
hasForceDeletePermission: result[permission[2]],
hasPinPermission: result[permission[3]]
hasEditPermission: result[0],
hasDeletePermission: result[1],
hasForceDeletePermission: result[2],
hasPinPermission: result[3]
};
} catch {
// Do nothing
@ -440,7 +444,11 @@ MessageActions.propTypes = {
Message_AllowPinning: PropTypes.bool,
Message_AllowStarring: PropTypes.bool,
Message_Read_Receipt_Store_Users: PropTypes.bool,
server: PropTypes.string
server: PropTypes.string,
editMessagePermission: PropTypes.array,
deleteMessagePermission: PropTypes.array,
forceDeleteMessagePermission: PropTypes.array,
pinMessagePermission: PropTypes.array
};
const mapStateToProps = state => ({
@ -452,7 +460,11 @@ const mapStateToProps = state => ({
Message_AllowPinning: state.settings.Message_AllowPinning,
Message_AllowStarring: state.settings.Message_AllowStarring,
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users,
isMasterDetail: state.app.isMasterDetail
isMasterDetail: state.app.isMasterDetail,
editMessagePermission: state.permissions['edit-message'],
deleteMessagePermission: state.permissions['delete-message'],
forceDeleteMessagePermission: state.permissions['force-delete-message'],
pinMessagePermission: state.permissions['pin-message']
});
export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions);

View File

@ -1,11 +1,55 @@
import lt from 'semver/functions/lt';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Q } from '@nozbe/watermelondb';
import coerce from 'semver/functions/coerce';
import orderBy from 'lodash/orderBy';
import database from '../database';
import log from '../../utils/log';
import reduxStore from '../createStore';
import protectedFunction from './helpers/protectedFunction';
import { setPermissions as setPermissionsAction } from '../../actions/permissions';
const PERMISSIONS = [
'add-user-to-any-c-room',
'add-user-to-any-p-room',
'add-user-to-joined-room',
'archive-room',
'auto-translate',
'create-invite-links',
'delete-c',
'delete-message',
'delete-p',
'edit-message',
'edit-room',
'force-delete-message',
'mute-user',
'pin-message',
'post-readonly',
'remove-user',
'set-leader',
'set-moderator',
'set-owner',
'set-react-when-readonly',
'set-readonly',
'toggle-room-e2e-encryption',
'transfer-livechat-guest',
'unarchive-room',
'view-broadcast-member-list',
'view-privileged-setting',
'view-room-administration',
'view-statistics',
'view-user-administration'
];
export async function setPermissions() {
const db = database.active;
const permissionsCollection = db.collections.get('permissions');
const allPermissions = await permissionsCollection.query(Q.where('id', Q.oneOf(PERMISSIONS))).fetch();
const parsed = allPermissions.reduce((acc, item) => ({ ...acc, [item.id]: item.roles }), {});
reduxStore.dispatch(setPermissionsAction(parsed));
}
const getUpdatedSince = (allRecords) => {
try {
@ -64,12 +108,13 @@ const updatePermissions = async({ update = [], remove = [], allRecords }) => {
await db.action(async() => {
await db.batch(...batch);
});
return true;
} catch (e) {
log(e);
}
};
export default function() {
export function getPermissions() {
return new Promise(async(resolve) => {
try {
const serverVersion = reduxStore.getState().server.version;
@ -78,17 +123,20 @@ export default function() {
const allRecords = await permissionsCollection.query().fetch();
// if server version is lower than 0.73.0, fetches from old api
if (serverVersion && lt(serverVersion, '0.73.0')) {
if (serverVersion && lt(coerce(serverVersion), '0.73.0')) {
// RC 0.66.0
const result = await this.sdk.get('permissions.list');
if (!result.success) {
return resolve();
}
await updatePermissions({ update: result.permissions, allRecords });
const changePermissions = await updatePermissions({ update: result.permissions, allRecords });
if (changePermissions) {
setPermissions();
}
return resolve();
} else {
const params = {};
const updatedSince = await getUpdatedSince(allRecords);
const updatedSince = getUpdatedSince(allRecords);
if (updatedSince) {
params.updatedSince = updatedSince;
}
@ -99,7 +147,10 @@ export default function() {
return resolve();
}
await updatePermissions({ update: result.update, remove: result.delete, allRecords });
const changePermissions = await updatePermissions({ update: result.update, remove: result.delete, allRecords });
if (changePermissions) {
setPermissions();
}
return resolve();
}
} catch (e) {

View File

@ -32,7 +32,7 @@ import readMessages from './methods/readMessages';
import getSettings, { getLoginSettings, setSettings } from './methods/getSettings';
import getRooms from './methods/getRooms';
import getPermissions from './methods/getPermissions';
import { setPermissions, getPermissions } from './methods/getPermissions';
import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis';
import {
getEnterpriseModules, setEnterpriseModules, hasLicense, isOmnichannelModuleAvailable
@ -70,7 +70,6 @@ const CERTIFICATE_KEY = 'RC_CERTIFICATE_KEY';
export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY';
export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY';
export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY';
const returnAnArray = obj => obj || [];
const MIN_ROCKETCHAT_VERSION = '0.70.0';
const STATUSES = ['offline', 'online', 'away', 'busy'];
@ -740,6 +739,7 @@ const RocketChat = {
getLoginSettings,
setSettings,
getPermissions,
setPermissions,
getCustomEmojis,
setCustomEmojis,
getEnterpriseModules,
@ -1172,10 +1172,13 @@ const RocketChat = {
// RC 0.65.0
return this.sdk.get(`${ this.roomTypeToApiType(type) }.roles`, { roomId });
},
/**
* Permissions: array of permissions' roles from redux. Example: [['owner', 'admin'], ['leader']]
* Returns an array of boolean for each permission from permissions arg
*/
async hasPermission(permissions, rid) {
const db = database.active;
const subsCollection = db.collections.get('subscriptions');
const permissionsCollection = db.collections.get('permissions');
let roomRoles = [];
try {
// get the room from database
@ -1184,31 +1187,16 @@ const RocketChat = {
roomRoles = room.roles || [];
} catch (error) {
console.log('hasPermission -> Room not found');
return permissions.reduce((result, permission) => {
result[permission] = false;
return result;
}, {});
return permissions.map(() => false);
}
// 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 = (shareUser?.roles || loginUser?.roles) || [];
// merge both roles
const mergedRoles = [...new Set([...roomRoles, ...userRoles])];
// return permissions in object format
// e.g. { 'edit-room': true, 'set-readonly': false }
return permissions.reduce((result, permission) => {
result[permission] = false;
const permissionFound = permissionsFiltered.find(p => p.id === permission);
if (permissionFound) {
result[permission] = returnAnArray(permissionFound.roles).some(r => mergedRoles.includes(r));
}
return result;
}, {});
return permissions.map(permission => permission.some(r => mergedRoles.includes(r) ?? false));
} catch (e) {
log(e);
}
@ -1438,17 +1426,15 @@ const RocketChat = {
query, count, offset, sort
});
},
async canAutoTranslate() {
const db = database.active;
canAutoTranslate() {
try {
const AutoTranslate_Enabled = reduxStore.getState().settings && reduxStore.getState().settings.AutoTranslate_Enabled;
const { AutoTranslate_Enabled } = reduxStore.getState().settings;
if (!AutoTranslate_Enabled) {
return false;
}
const permissionsCollection = db.collections.get('permissions');
const autoTranslatePermission = await permissionsCollection.find('auto-translate');
const userRoles = (reduxStore.getState().login.user && reduxStore.getState().login.user.roles) || [];
return autoTranslatePermission.roles.some(role => userRoles.includes(role));
const autoTranslatePermission = reduxStore.getState().permissions['auto-translate'];
const userRoles = (reduxStore.getState().login?.user?.roles) ?? [];
return autoTranslatePermission?.some(role => userRoles.includes(role));
} catch (e) {
log(e);
return false;

View File

@ -18,6 +18,7 @@ import inviteLinks from './inviteLinks';
import createDiscussion from './createDiscussion';
import enterpriseModules from './enterpriseModules';
import encryption from './encryption';
import permissions from './permissions';
import inquiry from '../ee/omnichannel/reducers/inquiry';
@ -41,5 +42,6 @@ export default combineReducers({
createDiscussion,
inquiry,
enterpriseModules,
encryption
encryption,
permissions
});

View File

@ -0,0 +1,14 @@
import { PERMISSIONS } from '../actions/actionsTypes';
const initialState = {
permissions: {}
};
export default function permissions(state = initialState, action) {
switch (action.type) {
case PERMISSIONS.SET:
return action.permissions;
default:
return state;
}
}

View File

@ -124,6 +124,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
// and block the selectServerSuccess raising multiples errors
RocketChat.setSettings();
RocketChat.setCustomEmojis();
RocketChat.setPermissions();
RocketChat.setEnterpriseModules();
let serverInfo;

View File

@ -1,13 +1,11 @@
import RocketChat from '../lib/rocketchat';
import reduxStore from '../lib/createStore';
const canPost = async({ rid }) => {
try {
const permission = await RocketChat.hasPermission(['post-readonly'], rid);
return permission && permission['post-readonly'];
} catch {
// do nothing
}
return false;
const canPostReadOnly = async({ rid }) => {
// TODO: this is not reactive. If this permission changes, the component won't be updated
const postReadOnlyPermission = reduxStore.getState().permissions['post-readonly'];
const permission = await RocketChat.hasPermission([postReadOnlyPermission], rid);
return permission[0];
};
const isMuted = (room, user) => room && room.muted && room.muted.find && !!room.muted.find(m => m === user.username);
@ -20,7 +18,7 @@ export const isReadOnly = async(room, user) => {
return true;
}
if (room?.ro) {
const allowPost = await canPost(room);
const allowPost = await canPostReadOnly(room);
if (allowPost) {
return false;
}

View File

@ -53,7 +53,15 @@ class RoomActionsView extends React.Component {
closeRoom: PropTypes.func,
theme: PropTypes.string,
fontScale: PropTypes.number,
serverVersion: PropTypes.string
serverVersion: PropTypes.string,
addUserToJoinedRoomPermission: PropTypes.array,
addUserToAnyCRoomPermission: PropTypes.array,
addUserToAnyPRoomPermission: PropTypes.array,
createInviteLinksPermission: PropTypes.array,
editRoomPermission: PropTypes.array,
toggleRoomE2EEncryptionPermission: PropTypes.array,
viewBroadcastMemberListPermission: PropTypes.array,
transferLivechatGuestPermission: PropTypes.array
}
constructor(props) {
@ -118,7 +126,7 @@ class RoomActionsView extends React.Component {
this.updateRoomMember();
}
const canAutoTranslate = await RocketChat.canAutoTranslate();
const canAutoTranslate = RocketChat.canAutoTranslate();
this.setState({ canAutoTranslate });
this.canAddUser();
@ -159,60 +167,62 @@ class RoomActionsView extends React.Component {
canAddUser = async() => {
const { room, joined } = this.state;
const { addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission } = this.props;
const { rid, t } = room;
let canAdd = false;
let canAddUser = false;
const userInRoom = joined;
const permissions = await RocketChat.hasPermission(['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], rid);
const permissions = await RocketChat.hasPermission([addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission], rid);
if (permissions) {
if (userInRoom && permissions['add-user-to-joined-room']) {
canAdd = true;
}
if (t === 'c' && permissions['add-user-to-any-c-room']) {
canAdd = true;
}
if (t === 'p' && permissions['add-user-to-any-p-room']) {
canAdd = true;
}
if (userInRoom && permissions[0]) {
canAddUser = true;
}
this.setState({ canAddUser: canAdd });
if (t === 'c' && permissions[1]) {
canAddUser = true;
}
if (t === 'p' && permissions[2]) {
canAddUser = true;
}
this.setState({ canAddUser });
}
canInviteUser = async() => {
const { room } = this.state;
const { createInviteLinksPermission } = this.props;
const { rid } = room;
const permissions = await RocketChat.hasPermission(['create-invite-links'], rid);
const permissions = await RocketChat.hasPermission([createInviteLinksPermission], rid);
const canInviteUser = permissions && permissions['create-invite-links'];
const canInviteUser = permissions[0];
this.setState({ canInviteUser });
}
canEdit = async() => {
const { room } = this.state;
const { editRoomPermission } = this.props;
const { rid } = room;
const permissions = await RocketChat.hasPermission(['edit-room'], rid);
const permissions = await RocketChat.hasPermission([editRoomPermission], rid);
const canEdit = permissions && permissions['edit-room'];
const canEdit = permissions[0];
this.setState({ canEdit });
}
canToggleEncryption = async() => {
const { room } = this.state;
const { toggleRoomE2EEncryptionPermission } = this.props;
const { rid } = room;
const permissions = await RocketChat.hasPermission(['toggle-room-e2e-encryption'], rid);
const permissions = await RocketChat.hasPermission([toggleRoomE2EEncryptionPermission], rid);
const canToggleEncryption = permissions && permissions['toggle-room-e2e-encryption'];
const canToggleEncryption = permissions[0];
this.setState({ canToggleEncryption });
}
canViewMembers = async() => {
const { room } = this.state;
const { viewBroadcastMemberListPermission } = this.props;
const { rid, t, broadcast } = room;
if (broadcast) {
const viewBroadcastMemberListPermission = 'view-broadcast-member-list';
const permissions = await RocketChat.hasPermission([viewBroadcastMemberListPermission], rid);
if (!permissions[viewBroadcastMemberListPermission]) {
if (!permissions[0]) {
return false;
}
}
@ -226,16 +236,10 @@ class RoomActionsView extends React.Component {
canForwardGuest = async() => {
const { room } = this.state;
const { transferLivechatGuestPermission } = this.props;
const { rid } = room;
let result = true;
const transferLivechatGuest = 'transfer-livechat-guest';
const permissions = await RocketChat.hasPermission([transferLivechatGuest], rid);
if (!permissions[transferLivechatGuest]) {
result = false;
}
this.setState({ canForwardGuest: result });
const permissions = await RocketChat.hasPermission([transferLivechatGuestPermission], rid);
this.setState({ canForwardGuest: permissions[0] });
}
canReturnQueue = async() => {
@ -866,7 +870,15 @@ class RoomActionsView extends React.Component {
const mapStateToProps = state => ({
jitsiEnabled: state.settings.Jitsi_Enabled || false,
encryptionEnabled: state.encryption.enabled,
serverVersion: state.server.version
serverVersion: state.server.version,
addUserToJoinedRoomPermission: state.permissions['add-user-to-joined-room'],
addUserToAnyCRoomPermission: state.permissions['add-user-to-any-c-room'],
addUserToAnyPRoomPermission: state.permissions['add-user-to-any-p-room'],
createInviteLinksPermission: state.permissions['create-invite-links'],
editRoomPermission: state.permissions['edit-room'],
toggleRoomE2EEncryptionPermission: state.permissions['toggle-room-e2e-encryption'],
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
transferLivechatGuestPermission: state.permissions['transfer-livechat-guest']
});
const mapDispatchToProps = dispatch => ({

View File

@ -44,14 +44,6 @@ const PERMISSION_ARCHIVE = 'archive-room';
const PERMISSION_UNARCHIVE = 'unarchive-room';
const PERMISSION_DELETE_C = 'delete-c';
const PERMISSION_DELETE_P = 'delete-p';
const PERMISSIONS_ARRAY = [
PERMISSION_SET_READONLY,
PERMISSION_SET_REACT_WHEN_READONLY,
PERMISSION_ARCHIVE,
PERMISSION_UNARCHIVE,
PERMISSION_DELETE_C,
PERMISSION_DELETE_P
];
class RoomInfoEditView extends React.Component {
static navigationOptions = () => ({
@ -63,7 +55,13 @@ class RoomInfoEditView extends React.Component {
deleteRoom: PropTypes.func,
serverVersion: PropTypes.string,
encryptionEnabled: PropTypes.bool,
theme: PropTypes.string
theme: PropTypes.string,
setReadOnlyPermission: PropTypes.array,
setReactWhenReadOnlyPermission: PropTypes.array,
archiveRoomPermission: PropTypes.array,
unarchiveRoomPermission: PropTypes.array,
deleteCPermission: PropTypes.array,
deletePPermission: PropTypes.array
};
constructor(props) {
@ -108,7 +106,15 @@ class RoomInfoEditView extends React.Component {
// eslint-disable-next-line react/sort-comp
loadRoom = async() => {
const { route } = this.props;
const {
route,
setReadOnlyPermission,
setReactWhenReadOnlyPermission,
archiveRoomPermission,
unarchiveRoomPermission,
deleteCPermission,
deletePPermission
} = this.props;
const rid = route.params?.rid;
if (!rid) {
return;
@ -123,8 +129,25 @@ class RoomInfoEditView extends React.Component {
this.init(this.room);
});
const permissions = await RocketChat.hasPermission(PERMISSIONS_ARRAY, rid);
this.setState({ permissions });
const result = await RocketChat.hasPermission([
setReadOnlyPermission,
setReactWhenReadOnlyPermission,
archiveRoomPermission,
unarchiveRoomPermission,
deleteCPermission,
deletePPermission
], rid);
this.setState({
permissions: {
[PERMISSION_SET_READONLY]: result[0],
[PERMISSION_SET_REACT_WHEN_READONLY]: result[1],
[PERMISSION_ARCHIVE]: result[2],
[PERMISSION_UNARCHIVE]: result[3],
[PERMISSION_DELETE_C]: result[4],
[PERMISSION_DELETE_P]: result[5]
}
});
} catch (e) {
log(e);
}
@ -667,7 +690,13 @@ class RoomInfoEditView extends React.Component {
const mapStateToProps = state => ({
serverVersion: state.server.version,
encryptionEnabled: state.encryption.enabled
encryptionEnabled: state.encryption.enabled,
setReadOnlyPermission: state.permissions[PERMISSION_SET_READONLY],
setReactWhenReadOnlyPermission: state.permissions[PERMISSION_SET_REACT_WHEN_READONLY],
archiveRoomPermission: state.permissions[PERMISSION_ARCHIVE],
unarchiveRoomPermission: state.permissions[PERMISSION_UNARCHIVE],
deleteCPermission: state.permissions[PERMISSION_DELETE_C],
deletePPermission: state.permissions[PERMISSION_DELETE_P]
});
const mapDispatchToProps = dispatch => ({

View File

@ -31,7 +31,6 @@ import SafeAreaView from '../../containers/SafeAreaView';
import { goRoom } from '../../utils/goRoom';
import Navigation from '../../lib/Navigation';
const PERMISSION_EDIT_ROOM = 'edit-room';
const getRoomTitle = (room, type, name, username, statusText, theme) => (type === 'd'
? (
<>
@ -55,7 +54,8 @@ class RoomInfoView extends React.Component {
rooms: PropTypes.array,
theme: PropTypes.string,
isMasterDetail: PropTypes.bool,
jitsiEnabled: PropTypes.bool
jitsiEnabled: PropTypes.bool,
editRoomPermission: PropTypes.array
}
constructor(props) {
@ -193,7 +193,7 @@ class RoomInfoView extends React.Component {
loadRoom = async() => {
const { room: roomState } = this.state;
const { route } = this.props;
const { route, editRoomPermission } = this.props;
let room = route.params?.room;
if (room && room.observe) {
this.roomObservable = room.observe();
@ -213,8 +213,8 @@ class RoomInfoView extends React.Component {
}
}
const permissions = await RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid);
if (permissions[PERMISSION_EDIT_ROOM] && !room.prid) {
const permissions = await RocketChat.hasPermission([editRoomPermission], room.rid);
if (permissions[0] && !room.prid) {
this.setState({ showEdit: true }, () => this.setHeader());
}
}
@ -369,7 +369,8 @@ class RoomInfoView extends React.Component {
const mapStateToProps = state => ({
rooms: state.room.rooms,
isMasterDetail: state.app.isMasterDetail,
jitsiEnabled: state.settings.Jitsi_Enabled || false
jitsiEnabled: state.settings.Jitsi_Enabled || false,
editRoomPermission: state.permissions['edit-room']
});
export default connect(mapStateToProps)(withTheme(RoomInfoView));

View File

@ -28,6 +28,12 @@ import { goRoom } from '../../utils/goRoom';
const PAGE_SIZE = 25;
const PERMISSION_MUTE_USER = 'mute-user';
const PERMISSION_SET_LEADER = 'set-leader';
const PERMISSION_SET_OWNER = 'set-owner';
const PERMISSION_SET_MODERATOR = 'set-moderator';
const PERMISSION_REMOVE_USER = 'remove-user';
class RoomMembersView extends React.Component {
static propTypes = {
navigation: PropTypes.object,
@ -43,7 +49,12 @@ class RoomMembersView extends React.Component {
showActionSheet: PropTypes.func,
theme: PropTypes.string,
isMasterDetail: PropTypes.bool,
useRealName: PropTypes.bool
useRealName: PropTypes.bool,
muteUserPermission: PropTypes.array,
setLeaderPermission: PropTypes.array,
setOwnerPermission: PropTypes.array,
setModeratorPermission: PropTypes.array,
removeUserPermission: PropTypes.array
}
constructor(props) {
@ -81,7 +92,20 @@ class RoomMembersView extends React.Component {
this.fetchMembers();
const { room } = this.state;
this.permissions = await RocketChat.hasPermission(['mute-user', 'set-leader', 'set-owner', 'set-moderator', 'remove-user'], room.rid);
const {
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
} = this.props;
const result = await RocketChat.hasPermission([
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
], room.rid);
this.permissions = {
[PERMISSION_MUTE_USER]: result[0],
[PERMISSION_SET_LEADER]: result[1],
[PERMISSION_SET_OWNER]: result[2],
[PERMISSION_SET_MODERATOR]: result[3],
[PERMISSION_REMOVE_USER]: result[4]
};
const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
if (hasSinglePermission) {
@ -452,7 +476,12 @@ const mapStateToProps = state => ({
baseUrl: state.server.server,
user: getUserSelector(state),
isMasterDetail: state.app.isMasterDetail,
useRealName: state.settings.UI_Use_Real_Name
useRealName: state.settings.UI_Use_Real_Name,
muteUserPermission: state.permissions[PERMISSION_MUTE_USER],
setLeaderPermission: state.permissions[PERMISSION_SET_LEADER],
setOwnerPermission: state.permissions[PERMISSION_SET_OWNER],
setModeratorPermission: state.permissions[PERMISSION_SET_MODERATOR],
removeUserPermission: state.permissions[PERMISSION_REMOVE_USER]
});
export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView)));

View File

@ -436,10 +436,7 @@ class RoomView extends React.Component {
}
}
// We run `canAutoTranslate` again in order to refetch auto translate permission
// in case of a missing connection or poor connection on room open
const canAutoTranslate = await RocketChat.canAutoTranslate();
const canAutoTranslate = RocketChat.canAutoTranslate();
const member = await this.getRoomMember();
this.setState({ canAutoTranslate, member, loading: false });

View File

@ -4,19 +4,16 @@ import {
ScrollView, Text, View, TouchableWithoutFeedback
} from 'react-native';
import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb';
import isEqual from 'react-fast-compare';
import Avatar from '../../containers/Avatar';
import Status from '../../containers/Status/Status';
import log, { logEvent, events } from '../../utils/log';
import { logEvent, events } from '../../utils/log';
import I18n from '../../i18n';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { CustomIcon } from '../../lib/Icons';
import styles from './styles';
import SidebarItem from './SidebarItem';
import { themes } from '../../constants/colors';
import database from '../../lib/database';
import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView';
@ -27,13 +24,6 @@ Separator.propTypes = {
theme: PropTypes.string
};
const permissions = [
'view-statistics',
'view-room-administration',
'view-user-administration',
'view-privileged-setting'
];
class Sidebar extends Component {
static propTypes = {
baseUrl: PropTypes.string,
@ -45,32 +35,24 @@ class Sidebar extends Component {
loadingServer: PropTypes.bool,
useRealName: PropTypes.bool,
allowStatusMessage: PropTypes.bool,
isMasterDetail: PropTypes.bool
isMasterDetail: PropTypes.bool,
viewStatisticsPermission: PropTypes.object,
viewRoomAdministrationPermission: PropTypes.object,
viewUserAdministrationPermission: PropTypes.object,
viewPrivilegedSettingPermission: PropTypes.object
}
constructor(props) {
super(props);
this.state = {
showStatus: false,
isAdmin: false
showStatus: false
};
}
componentDidMount() {
this.setIsAdmin();
}
UNSAFE_componentWillReceiveProps(nextProps) {
const { loadingServer } = this.props;
if (loadingServer && nextProps.loadingServer !== loadingServer) {
this.setIsAdmin();
}
}
shouldComponentUpdate(nextProps, nextState) {
const { showStatus, isAdmin } = this.state;
const {
Site_Name, user, baseUrl, state, isMasterDetail, useRealName, theme
Site_Name, user, baseUrl, state, isMasterDetail, useRealName, theme, viewStatisticsPermission, viewRoomAdministrationPermission, viewUserAdministrationPermission, viewPrivilegedSettingPermission
} = this.props;
// Drawer navigation state
if (state?.index !== nextProps.state?.index) {
@ -103,25 +85,42 @@ class Sidebar extends Component {
if (nextState.isAdmin !== isAdmin) {
return true;
}
if (!isEqual(nextProps.viewStatisticsPermission, viewStatisticsPermission)) {
return true;
}
if (!isEqual(nextProps.viewRoomAdministrationPermission, viewRoomAdministrationPermission)) {
return true;
}
if (!isEqual(nextProps.viewUserAdministrationPermission, viewUserAdministrationPermission)) {
return true;
}
if (!isEqual(nextProps.viewPrivilegedSettingPermission, viewPrivilegedSettingPermission)) {
return true;
}
return false;
}
async setIsAdmin() {
const db = database.active;
const { user } = this.props;
getIsAdmin() {
const {
user, viewStatisticsPermission, viewRoomAdministrationPermission, viewUserAdministrationPermission, viewPrivilegedSettingPermission
} = this.props;
const { roles } = user;
try {
if (roles) {
const permissionsCollection = db.collections.get('permissions');
const permissionsFiltered = await permissionsCollection.query(Q.where('id', Q.oneOf(permissions))).fetch();
const isAdmin = permissionsFiltered.reduce((result, permission) => (
result || permission.roles.some(r => roles.indexOf(r) !== -1)),
false);
this.setState({ isAdmin });
}
} catch (e) {
log(e);
const allPermissions = [viewStatisticsPermission, viewRoomAdministrationPermission, viewUserAdministrationPermission, viewPrivilegedSettingPermission];
let isAdmin = false;
if (roles) {
isAdmin = allPermissions.reduce((result, permission) => {
if (permission) {
return (
result || permission.some(r => roles.indexOf(r) !== -1)
);
}
return result;
},
false);
}
return isAdmin;
}
sidebarNavigate = (route) => {
@ -143,9 +142,8 @@ class Sidebar extends Component {
}
renderAdmin = () => {
const { isAdmin } = this.state;
const { theme, isMasterDetail } = this.props;
if (!isAdmin) {
if (!this.getIsAdmin()) {
return null;
}
const routeName = isMasterDetail ? 'AdminPanelView' : 'AdminPanelStackNavigator';
@ -275,7 +273,11 @@ const mapStateToProps = state => ({
loadingServer: state.server.loading,
useRealName: state.settings.UI_Use_Real_Name,
allowStatusMessage: state.settings.Accounts_AllowUserStatusMessageChange,
isMasterDetail: state.app.isMasterDetail
isMasterDetail: state.app.isMasterDetail,
viewStatisticsPermission: state.permissions['view-statistics'],
viewRoomAdministrationPermission: state.permissions['view-room-administration'],
viewUserAdministrationPermission: state.permissions['view-user-administration'],
viewPrivilegedSettingPermission: state.permissions['view-privileged-setting']
});
export default connect(mapStateToProps)(withTheme(Sidebar));