Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e2760e3
[FIX] Add permissions to Redux store
kresnaputra Feb 17, 2021
514193c
add only permissions being used in the app
kresnaputra Feb 22, 2021
ce83ed8
add clear permissions reducer
kresnaputra Feb 22, 2021
ebfcbbb
call RocketChat.hasPermission from reducer
kresnaputra Feb 23, 2021
7371f00
add server version comparison on getPermissions
kresnaputra Feb 23, 2021
d2464ae
Merge branch 'develop' into fix.add-permissions-to-redux-store
kresnaputra Feb 23, 2021
35e90a6
Merge branch 'develop' into fix.add-permissions-to-redux-store
diegolmello Feb 23, 2021
c4f78a4
refactor hasPermission function
kresnaputra Feb 24, 2021
e714d44
refactor hasPermission function
kresnaputra Feb 24, 2021
35c19bf
remove uncomment code
kresnaputra Feb 24, 2021
61bd15a
use Q.experimentalSortBy()
kresnaputra Feb 24, 2021
a3af55a
Merge branch 'fix.add-permissions-to-redux-store' of github.com:Rocke…
kresnaputra Feb 24, 2021
87dde11
add coerce function
kresnaputra Feb 24, 2021
4fe0ab1
Merge branch 'develop' into fix.add-permissions-to-redux-store
diegolmello Feb 24, 2021
80ebeba
Change Rocketchat.hasPermission
diegolmello Feb 24, 2021
f27d4a6
Apply on isReadOnly
diegolmello Feb 24, 2021
8060b58
Apply to RoomInfoEditView
diegolmello Feb 24, 2021
6329bdf
Apply to RoomInfoView and RoomInfoEditView
diegolmello Feb 24, 2021
966264f
canAutoTranslate
diegolmello Feb 24, 2021
1f3adeb
Unnecessary clear permissions
diegolmello Feb 24, 2021
9a5be0d
Revert getUpdatedSince
diegolmello Feb 24, 2021
d0ecfc7
Naming fix
diegolmello Feb 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/actions/actionsTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
8 changes: 8 additions & 0 deletions app/actions/permissions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as types from './actionsTypes';

export function setPermissions(permissions) {
return {
type: types.PERMISSIONS.SET,
permissions
};
}
28 changes: 20 additions & 8 deletions app/containers/MessageActions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 => ({
Expand All @@ -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);
61 changes: 56 additions & 5 deletions app/lib/methods/getPermissions.js
Original file line number Diff line number Diff line change
@@ -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 {
Comment thread
diegolmello marked this conversation as resolved.
Expand Down Expand Up @@ -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;
Expand All @@ -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 {
Comment thread
diegolmello marked this conversation as resolved.
const params = {};
const updatedSince = await getUpdatedSince(allRecords);
const updatedSince = getUpdatedSince(allRecords);
if (updatedSince) {
params.updatedSince = updatedSince;
}
Expand All @@ -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) {
Expand Down
42 changes: 14 additions & 28 deletions app/lib/rocketchat.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'];
Expand Down Expand Up @@ -740,6 +739,7 @@ const RocketChat = {
getLoginSettings,
setSettings,
getPermissions,
setPermissions,
getCustomEmojis,
setCustomEmojis,
getEnterpriseModules,
Expand Down Expand Up @@ -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) {
Comment thread
diegolmello marked this conversation as resolved.
const db = database.active;
const subsCollection = db.collections.get('subscriptions');
const permissionsCollection = db.collections.get('permissions');
let roomRoles = [];
try {
// get the room from database
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion app/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -41,5 +42,6 @@ export default combineReducers({
createDiscussion,
inquiry,
enterpriseModules,
encryption
encryption,
permissions
});
14 changes: 14 additions & 0 deletions app/reducers/permissions.js
Original file line number Diff line number Diff line change
@@ -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;
}
}
1 change: 1 addition & 0 deletions app/sagas/selectServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 7 additions & 9 deletions app/utils/isReadOnly.js
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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;
}
Expand Down
Loading