From a6347dc2831479adf65546bf845708c37bfbcd38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5var=20Aamb=C3=B8=20Fosstveit?= Date: Thu, 2 Apr 2020 00:28:05 +0200 Subject: [PATCH] Expand permissions/role system. Clients are now provisioned with their roles when they join and will have features enabled/disabled based on their permissions. --- app/src/RoomClient.js | 11 +-- app/src/actions/roomActions.js | 12 +++ .../AccessControl/LockDialog/ListLobbyPeer.js | 79 +++------------- app/src/components/Containers/Me.js | 18 +++- app/src/components/Controls/TopBar.js | 9 +- .../MeetingDrawer/Chat/ChatInput.js | 11 ++- .../MeetingDrawer/FileSharing/FileSharing.js | 25 +++++- .../ParticipantList/ParticipantList.js | 7 +- app/src/reducers/me.js | 4 +- app/src/reducers/room.js | 25 +++++- app/src/reducers/userRoles.js | 4 - server/config/config.example.js | 34 +++++-- server/lib/Peer.js | 2 +- server/lib/Room.js | 90 ++++++++++++------- server/server.js | 2 +- server/userRoles.js | 13 ++- 16 files changed, 211 insertions(+), 135 deletions(-) delete mode 100644 app/src/reducers/userRoles.js diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 14194d6..86aaf76 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -2371,10 +2371,8 @@ export default class RoomClient { logger.debug('_joinRoom()'); - const { - displayName, - picture - } = store.getState().settings; + const { displayName } = store.getState().settings; + const { picture } = store.getState().me; try { @@ -2524,7 +2522,7 @@ export default class RoomClient canShareFiles : this._torrentSupport })); - const { roles, peers } = await this.sendRequest( + const { roles, peers, permissionsFromRoles, userRoles } = await this.sendRequest( 'join', { displayName : displayName, @@ -2534,6 +2532,9 @@ export default class RoomClient logger.debug('_joinRoom() joined [peers:"%o", roles:"%o"]', peers, roles); + store.dispatch(roomActions.setUserRoles(userRoles)); + store.dispatch(roomActions.setPermissionsFromRoles(permissionsFromRoles)); + const myRoles = store.getState().me.roles; for (const role of roles) diff --git a/app/src/actions/roomActions.js b/app/src/actions/roomActions.js index 6003b9e..1e93453 100644 --- a/app/src/actions/roomActions.js +++ b/app/src/actions/roomActions.js @@ -127,4 +127,16 @@ export const setCloseMeetingInProgress = (flag) => ({ type : 'CLOSE_MEETING_IN_PROGRESS', payload : { flag } + }); + +export const setUserRoles = (userRoles) => + ({ + type : 'SET_USER_ROLES', + payload : { userRoles } + }); + +export const setPermissionsFromRoles = (permissionsFromRoles) => + ({ + type : 'SET_PERMISSIONS_FROM_ROLES', + payload : { permissionsFromRoles } }); \ No newline at end of file diff --git a/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js b/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js index 94d04d2..97f1ff6 100644 --- a/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js +++ b/app/src/components/AccessControl/LockDialog/ListLobbyPeer.js @@ -7,72 +7,16 @@ import { withRoomContext } from '../../../RoomContext'; import { useIntl } from 'react-intl'; import ListItem from '@material-ui/core/ListItem'; import ListItemText from '@material-ui/core/ListItemText'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; +import IconButton from '@material-ui/core/IconButton'; import ListItemAvatar from '@material-ui/core/ListItemAvatar'; import Avatar from '@material-ui/core/Avatar'; import EmptyAvatar from '../../../images/avatar-empty.jpeg'; import PromoteIcon from '@material-ui/icons/OpenInBrowser'; import Tooltip from '@material-ui/core/Tooltip'; -const styles = (theme) => +const styles = () => ({ root : - { - padding : theme.spacing(1), - width : '100%', - overflow : 'hidden', - cursor : 'auto', - display : 'flex' - }, - avatar : - { - borderRadius : '50%', - height : '2rem' - }, - peerInfo : - { - fontSize : '1rem', - border : 'none', - display : 'flex', - paddingLeft : theme.spacing(1), - flexGrow : 1, - alignItems : 'center' - }, - controls : - { - float : 'right', - display : 'flex', - flexDirection : 'row', - justifyContent : 'flex-start', - alignItems : 'center' - }, - button : - { - flex : '0 0 auto', - margin : '0.3rem', - borderRadius : 2, - backgroundColor : 'rgba(0, 0, 0, 0.5)', - cursor : 'pointer', - transitionProperty : 'opacity, background-color', - transitionDuration : '0.15s', - width : 'var(--media-control-button-size)', - height : 'var(--media-control-button-size)', - opacity : 0.85, - '&:hover' : - { - opacity : 1 - }, - '&.disabled' : - { - pointerEvents : 'none', - backgroundColor : 'var(--media-control-botton-disabled)' - }, - '&.promote' : - { - backgroundColor : 'var(--media-control-botton-on)' - } - }, - ListItem : { alignItems : 'center' } @@ -83,6 +27,7 @@ const ListLobbyPeer = (props) => const { roomClient, peer, + canPromote, classes } = props; @@ -92,7 +37,7 @@ const ListLobbyPeer = (props) => return ( defaultMessage : 'Click to let them in' })} > - { e.stopPropagation(); @@ -120,7 +63,7 @@ const ListLobbyPeer = (props) => }} > - + ); @@ -131,13 +74,17 @@ ListLobbyPeer.propTypes = roomClient : PropTypes.any.isRequired, advancedMode : PropTypes.bool, peer : PropTypes.object.isRequired, + canPromote : PropTypes.bool.isRequired, classes : PropTypes.object.isRequired }; const mapStateToProps = (state, { id }) => { return { - peer : state.lobbyPeers[id] + peer : state.lobbyPeers[id], + canPromote : + state.me.roles.some((role) => + state.room.permissionsFromRoles.PROMOTE_PEER.includes(role)) }; }; @@ -149,6 +96,8 @@ export default withRoomContext(connect( areStatesEqual : (next, prev) => { return ( + prev.room.permissionsFromRoles === next.room.permissionsFromRoles && + prev.me.roles === next.me.roles && prev.lobbyPeers === next.lobbyPeers ); } diff --git a/app/src/components/Containers/Me.js b/app/src/components/Containers/Me.js index 3003f3d..09eb2d6 100644 --- a/app/src/components/Containers/Me.js +++ b/app/src/components/Containers/Me.js @@ -144,6 +144,7 @@ const Me = (props) => micProducer, webcamProducer, screenProducer, + canShareScreen, classes, theme } = props; @@ -396,7 +397,11 @@ const Me = (props) => defaultMessage : 'Start screen sharing' })} className={classes.fab} - disabled={!me.canShareScreen || me.screenShareInProgress} + disabled={ + !canShareScreen || + !me.canShareScreen || + me.screenShareInProgress + } color={screenState === 'on' ? 'primary' : 'default'} size={smallButtons ? 'small' : 'large'} onClick={() => @@ -537,6 +542,7 @@ Me.propTypes = spacing : PropTypes.number, style : PropTypes.object, smallButtons : PropTypes.bool, + canShareScreen : PropTypes.bool.isRequired, classes : PropTypes.object.isRequired, theme : PropTypes.object.isRequired }; @@ -544,10 +550,13 @@ Me.propTypes = const mapStateToProps = (state) => { return { - me : state.me, + me : state.me, ...meProducersSelector(state), - settings : state.settings, - activeSpeaker : state.me.id === state.room.activeSpeakerId + settings : state.settings, + activeSpeaker : state.me.id === state.room.activeSpeakerId, + canShareScreen : + state.me.roles.some((role) => + state.room.permissionsFromRoles.SHARE_SCREEN.includes(role)) }; }; @@ -559,6 +568,7 @@ export default withRoomContext(connect( areStatesEqual : (next, prev) => { return ( + prev.room.permissionsFromRoles === next.room.permissionsFromRoles && prev.me === next.me && prev.producers === next.producers && prev.settings === next.settings && diff --git a/app/src/components/Controls/TopBar.js b/app/src/components/Controls/TopBar.js index 50a20c0..094af23 100644 --- a/app/src/components/Controls/TopBar.js +++ b/app/src/components/Controls/TopBar.js @@ -135,6 +135,7 @@ const TopBar = (props) => toggleToolArea, openUsersTab, unread, + canLock, classes } = props; @@ -271,6 +272,7 @@ const TopBar = (props) => })} className={classes.actionButton} color='inherit' + disabled={!canLock} onClick={() => { if (room.locked) @@ -377,6 +379,7 @@ TopBar.propTypes = toggleToolArea : PropTypes.func.isRequired, openUsersTab : PropTypes.func.isRequired, unread : PropTypes.number.isRequired, + canLock : PropTypes.bool.isRequired, classes : PropTypes.object.isRequired, theme : PropTypes.object.isRequired }; @@ -391,7 +394,10 @@ const mapStateToProps = (state) => loginEnabled : state.me.loginEnabled, myPicture : state.me.picture, unread : state.toolarea.unreadMessages + - state.toolarea.unreadFiles + state.toolarea.unreadFiles, + canLock : + state.me.roles.some((role) => + state.room.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)) }); const mapDispatchToProps = (dispatch) => @@ -434,6 +440,7 @@ export default withRoomContext(connect( prev.me.loggedIn === next.me.loggedIn && prev.me.loginEnabled === next.me.loginEnabled && prev.me.picture === next.me.picture && + prev.me.roles === next.me.roles && prev.toolarea.unreadMessages === next.toolarea.unreadMessages && prev.toolarea.unreadFiles === next.toolarea.unreadFiles ); diff --git a/app/src/components/MeetingDrawer/Chat/ChatInput.js b/app/src/components/MeetingDrawer/Chat/ChatInput.js index 5be44e9..480bb26 100644 --- a/app/src/components/MeetingDrawer/Chat/ChatInput.js +++ b/app/src/components/MeetingDrawer/Chat/ChatInput.js @@ -54,6 +54,7 @@ const ChatInput = (props) => roomClient, displayName, picture, + canChat, classes } = props; @@ -66,6 +67,7 @@ const ChatInput = (props) => defaultMessage : 'Enter chat message...' })} value={message || ''} + disabled={!canChat} onChange={handleChange} onKeyPress={(ev) => { @@ -89,6 +91,7 @@ const ChatInput = (props) => color='primary' className={classes.iconButton} aria-label='Send' + disabled={!canChat} onClick={() => { if (message && message !== '') @@ -112,13 +115,17 @@ ChatInput.propTypes = roomClient : PropTypes.object.isRequired, displayName : PropTypes.string, picture : PropTypes.string, + canChat : PropTypes.bool.isRequired, classes : PropTypes.object.isRequired }; const mapStateToProps = (state) => ({ displayName : state.settings.displayName, - picture : state.me.picture + picture : state.me.picture, + canChat : + state.me.roles.some((role) => + state.room.permissionsFromRoles.SEND_CHAT.includes(role)) }); export default withRoomContext( @@ -130,6 +137,8 @@ export default withRoomContext( areStatesEqual : (next, prev) => { return ( + prev.room.permissionsFromRoles === next.room.permissionsFromRoles && + prev.me.roles === next.me.roles && prev.settings.displayName === next.settings.displayName && prev.me.picture === next.me.picture ); diff --git a/app/src/components/MeetingDrawer/FileSharing/FileSharing.js b/app/src/components/MeetingDrawer/FileSharing/FileSharing.js index 8713134..a3ff80e 100644 --- a/app/src/components/MeetingDrawer/FileSharing/FileSharing.js +++ b/app/src/components/MeetingDrawer/FileSharing/FileSharing.js @@ -41,6 +41,7 @@ const FileSharing = (props) => const { canShareFiles, + canShare, classes } = props; @@ -60,6 +61,7 @@ const FileSharing = (props) => @@ -68,7 +70,7 @@ const FileSharing = (props) => variant='contained' component='span' className={classes.button} - disabled={!canShareFiles} + disabled={!canShareFiles || !canShare} > {buttonDescription} @@ -83,6 +85,7 @@ FileSharing.propTypes = { roomClient : PropTypes.any.isRequired, canShareFiles : PropTypes.bool.isRequired, tabOpen : PropTypes.bool.isRequired, + canShare : PropTypes.bool.isRequired, classes : PropTypes.object.isRequired }; @@ -90,10 +93,26 @@ const mapStateToProps = (state) => { return { canShareFiles : state.me.canShareFiles, - tabOpen : state.toolarea.currentToolTab === 'files' + tabOpen : state.toolarea.currentToolTab === 'files', + canShare : + state.me.roles.some((role) => + state.room.permissionsFromRoles.SHARE_FILE.includes(role)) }; }; export default withRoomContext(connect( - mapStateToProps + mapStateToProps, + null, + null, + { + areStatesEqual : (next, prev) => + { + return ( + prev.room.permissionsFromRoles === next.room.permissionsFromRoles && + prev.me.roles === next.me.roles && + prev.me.canShareFiles === next.me.canShareFiles && + prev.toolarea.currentToolTab === next.toolarea.currentToolTab + ); + } + } )(withStyles(styles)(FileSharing))); diff --git a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js index dbf5491..da6cd61 100644 --- a/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js +++ b/app/src/components/MeetingDrawer/ParticipantList/ParticipantList.js @@ -13,7 +13,6 @@ import ListPeer from './ListPeer'; import ListMe from './ListMe'; import ListModerator from './ListModerator'; import Volume from '../../Containers/Volume'; -import * as userRoles from '../../../reducers/userRoles'; const styles = (theme) => ({ @@ -170,8 +169,9 @@ ParticipantList.propTypes = const mapStateToProps = (state) => { return { - isModerator : state.me.roles.includes(userRoles.MODERATOR) || - state.me.roles.includes(userRoles.ADMIN), + isModerator : + state.me.roles.some((role) => + state.room.permissionsFromRoles.MODERATE_ROOM.includes(role)), passivePeers : passivePeersSelector(state), selectedPeerId : state.room.selectedPeerId, spotlightPeers : spotlightPeersSelector(state) @@ -186,6 +186,7 @@ const ParticipantListContainer = withRoomContext(connect( areStatesEqual : (next, prev) => { return ( + prev.room.permissionsFromRoles === next.room.permissionsFromRoles && prev.me.roles === next.me.roles && prev.peers === next.peers && prev.room.spotlights === next.room.spotlights && diff --git a/app/src/reducers/me.js b/app/src/reducers/me.js index 70bfbfc..9ed18cd 100644 --- a/app/src/reducers/me.js +++ b/app/src/reducers/me.js @@ -1,11 +1,9 @@ -import * as userRoles from './userRoles'; - const initialState = { id : null, picture : null, isMobile : false, - roles : [ userRoles.ALL ], + roles : [ 'normal' ], // Default role canSendMic : false, canSendWebcam : false, canShareScreen : false, diff --git a/app/src/reducers/room.js b/app/src/reducers/room.js index d963a0c..0f735d0 100644 --- a/app/src/reducers/room.js +++ b/app/src/reducers/room.js @@ -21,7 +21,16 @@ const initialState = joined : false, muteAllInProgress : false, stopAllVideoInProgress : false, - closeMeetingInProgress : false + closeMeetingInProgress : false, + userRoles : { NORMAL: 'normal' }, // Default role + permissionsFromRoles : { + CHANGE_ROOM_LOCK : [], + PROMOTE_PEER : [], + SEND_CHAT : [], + SHARE_SCREEN : [], + SHARE_FILE : [], + MODERATE_ROOM : [] + } }; const room = (state = initialState, action) => @@ -175,6 +184,20 @@ const room = (state = initialState, action) => case 'CLOSE_MEETING_IN_PROGRESS': return { ...state, closeMeetingInProgress: action.payload.flag }; + case 'SET_USER_ROLES': + { + const { userRoles } = action.payload; + + return { ...state, userRoles }; + } + + case 'SET_PERMISSIONS_FROM_ROLES': + { + const { permissionsFromRoles } = action.payload; + + return { ...state, permissionsFromRoles }; + } + default: return state; } diff --git a/app/src/reducers/userRoles.js b/app/src/reducers/userRoles.js deleted file mode 100644 index 217a760..0000000 --- a/app/src/reducers/userRoles.js +++ /dev/null @@ -1,4 +0,0 @@ -export const ADMIN = 'admin'; -export const MODERATOR = 'moderator'; -export const AUTHENTICATED = 'authenticated'; -export const ALL = 'normal'; \ No newline at end of file diff --git a/server/config/config.example.js b/server/config/config.example.js index 4b8cfe7..1d8baf8 100644 --- a/server/config/config.example.js +++ b/server/config/config.example.js @@ -194,15 +194,37 @@ module.exports = peer.email = userinfo.email; } }, - // Required roles for Access. All users have the role "ALL" by default. - // Other roles need to be added in the "userMapping" function. This - // is an Array of roles. userRoles.ADMIN have all priveleges and access - // always. + // All users have the role "NORMAL" by default. Other roles need to be + // added in the "userMapping" function. The following accesses and + // permissions are arrays of roles. Roles can be changed in userRoles.js // // Example: // [ userRoles.MODERATOR, userRoles.AUTHENTICATED ] - // This will allow all MODERATOR and AUTHENTICATED users access. - requiredRolesForAccess : [ userRoles.ALL ], + accessFromRoles : { + // The role(s) will gain access to the room + // even if it is locked (!) + BYPASS_ROOM_LOCK : [ userRoles.ADMIN ], + // The role(s) will gain access to the room without + // going into the lobby. If you want to restrict access to your + // server to only directly allow authenticated users, you could + // add the userRoles.AUTHENTICATED to the user in the userMapping + // function, and change to BYPASS_LOBBY : [ userRoles.AUTHENTICATED ] + BYPASS_LOBBY : [ userRoles.NORMAL ] + }, + permissionsFromRoles : { + // The role(s) have permission to lock/unlock a room + CHANGE_ROOM_LOCK : [ userRoles.NORMAL ], + // The role(s) have permission to promote a peer from the lobby + PROMOTE_PEER : [ userRoles.NORMAL ], + // The role(s) have permission to send chat messages + SEND_CHAT : [ userRoles.NORMAL ], + // The role(s) have permission to share screen + SHARE_SCREEN : [ userRoles.NORMAL ], + // The role(s) have permission to share files + SHARE_FILE : [ userRoles.NORMAL ], + // The role(s) have permission to moderate room (e.g. kick user) + MODERATE_ROOM : [ userRoles.MODERATOR ] + }, // When truthy, the room will be open to all users when as long as there // are allready users in the room activateOnHostJoin : true, diff --git a/server/lib/Peer.js b/server/lib/Peer.js index 1878a61..eb7f896 100644 --- a/server/lib/Peer.js +++ b/server/lib/Peer.js @@ -25,7 +25,7 @@ class Peer extends EventEmitter this._inLobby = false; - this._roles = [ userRoles.ALL ]; + this._roles = [ userRoles.NORMAL ]; this._displayName = false; diff --git a/server/lib/Room.js b/server/lib/Room.js index 1afe7a7..4eebb7a 100644 --- a/server/lib/Room.js +++ b/server/lib/Room.js @@ -59,12 +59,6 @@ class Room extends EventEmitter // Locked flag. this._locked = false; - // Required roles to access - this._requiredRoles = [ userRoles.ALL ]; - - if ('requiredRolesForAccess' in config) - this._requiredRoles = config.requiredRolesForAccess; - // if true: accessCode is a possibility to open the room this._joinByAccesCode = true; @@ -157,15 +151,16 @@ class Room extends EventEmitter // Returning user if (returning) this._peerJoining(peer, true); - // Always let ADMIN in, even if locked - else if (peer.roles.includes(userRoles.ADMIN)) + else if ( // Has a role that is allowed to bypass room lock + peer.roles.some((role) => config.accessFromRoles.BYPASS_ROOM_LOCK.includes(role)) + ) this._peerJoining(peer); else if (this._locked) this._parkPeer(peer); else { - // If the user has a role in config.requiredRolesForAccess, let them in - peer.roles.some((role) => this._requiredRoles.includes(role)) ? + // Has a role that is allowed to bypass lobby + peer.roles.some((role) => config.accessFromRoles.BYPASS_LOBBY.includes(role)) ? this._peerJoining(peer) : this._handleGuest(peer); } @@ -200,18 +195,18 @@ class Room extends EventEmitter this._lobby.on('peerRolesChanged', (peer) => { - // Always let admin in, even if locked - if (peer.roles.includes(userRoles.ADMIN)) + if ( // Has a role that is allowed to bypass room lock + peer.roles.some((role) => config.accessFromRoles.BYPASS_ROOM_LOCK.includes(role)) + ) { this._lobby.promotePeer(peer.id); return; } - // If the user has a role in config.requiredRolesForAccess, let them in - if ( + if ( // Has a role that is allowed to bypass lobby !this._locked && - peer.roles.some((role) => this._requiredRoles.includes(role)) + peer.roles.some((role) => config.accessFromRoles.BYPASS_LOBBY.includes(role)) ) { this._lobby.promotePeer(peer.id); @@ -554,8 +549,10 @@ class Room extends EventEmitter .map((joinedPeer) => (joinedPeer.peerInfo)); cb(null, { - roles : peer.roles, - peers : peerInfos + roles : peer.roles, + peers : peerInfos, + permissionsFromRoles : config.permissionsFromRoles, + userRoles : userRoles }); // Mark the new Peer as joined. @@ -682,12 +679,19 @@ class Room extends EventEmitter case 'produce': { + let { appData } = request.data; + + if ( + appData.source === 'screen' && + !peer.roles.some((role) => config.permissionsFromRoles.SHARE_SCREEN.includes(role)) + ) + throw new Error('peer not authorized'); + // Ensure the Peer is joined. if (!peer.joined) throw new Error('Peer not yet joined'); const { transportId, kind, rtpParameters } = request.data; - let { appData } = request.data; const transport = peer.getTransport(transportId); if (!transport) @@ -987,6 +991,11 @@ class Room extends EventEmitter case 'chatMessage': { + if ( + !peer.roles.some((role) => config.permissionsFromRoles.SEND_CHAT.includes(role)) + ) + throw new Error('peer not authorized'); + const { chatMessage } = request.data; this._chatHistory.push(chatMessage); @@ -1025,6 +1034,11 @@ class Room extends EventEmitter case 'lockRoom': { + if ( + !peer.roles.some((role) => config.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)) + ) + throw new Error('peer not authorized'); + this._locked = true; // Spread to others @@ -1040,6 +1054,11 @@ class Room extends EventEmitter case 'unlockRoom': { + if ( + !peer.roles.some((role) => config.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)) + ) + throw new Error('peer not authorized'); + this._locked = false; // Spread to others @@ -1095,6 +1114,11 @@ class Room extends EventEmitter case 'promotePeer': { + if ( + !peer.roles.some((role) => config.permissionsFromRoles.PROMOTE_PEER.includes(role)) + ) + throw new Error('peer not authorized'); + const { peerId } = request.data; this._lobby.promotePeer(peerId); @@ -1107,6 +1131,11 @@ class Room extends EventEmitter case 'promoteAllPeers': { + if ( + !peer.roles.some((role) => config.permissionsFromRoles.PROMOTE_PEER.includes(role)) + ) + throw new Error('peer not authorized'); + this._lobby.promoteAllPeers(); // Return no error @@ -1117,6 +1146,11 @@ class Room extends EventEmitter case 'sendFile': { + if ( + !peer.roles.some((role) => config.permissionsFromRoles.SHARE_FILE.includes(role)) + ) + throw new Error('peer not authorized'); + const { magnetUri } = request.data; this._fileHistory.push({ peerId: peer.id, magnetUri: magnetUri }); @@ -1154,10 +1188,9 @@ class Room extends EventEmitter case 'moderator:muteAll': { if ( - !peer.hasRole(userRoles.MODERATOR) && - !peer.hasRole(userRoles.ADMIN) + !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role)) ) - throw new Error('peer does not have moderator priveleges'); + throw new Error('peer not authorized'); // Spread to others this._notification(peer.socket, 'moderator:mute', { @@ -1172,10 +1205,9 @@ class Room extends EventEmitter case 'moderator:stopAllVideo': { if ( - !peer.hasRole(userRoles.MODERATOR) && - !peer.hasRole(userRoles.ADMIN) + !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role)) ) - throw new Error('peer does not have moderator priveleges'); + throw new Error('peer not authorized'); // Spread to others this._notification(peer.socket, 'moderator:stopVideo', { @@ -1190,10 +1222,9 @@ class Room extends EventEmitter case 'moderator:closeMeeting': { if ( - !peer.hasRole(userRoles.MODERATOR) && - !peer.hasRole(userRoles.ADMIN) + !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role)) ) - throw new Error('peer does not have moderator priveleges'); + throw new Error('peer not authorized'); this._notification( peer.socket, @@ -1213,10 +1244,9 @@ class Room extends EventEmitter case 'moderator:kickPeer': { if ( - !peer.hasRole(userRoles.MODERATOR) && - !peer.hasRole(userRoles.ADMIN) + !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role)) ) - throw new Error('peer does not have moderator priveleges'); + throw new Error('peer not authorized'); const { peerId } = request.data; diff --git a/server/server.js b/server/server.js index 3243280..3775b02 100755 --- a/server/server.js +++ b/server/server.js @@ -327,7 +327,7 @@ async function setupAuth() { for (const role of peer.roles) { - if (role !== userRoles.ALL) + if (role !== userRoles.NORMAL) peer.removeRole(role); } } diff --git a/server/userRoles.js b/server/userRoles.js index c8cf886..b9d0d2b 100644 --- a/server/userRoles.js +++ b/server/userRoles.js @@ -1,12 +1,11 @@ module.exports = { - // Allowed to enter locked rooms + all other priveleges + // These can be changed ADMIN : 'admin', - // Allowed to enter restricted rooms if configured. - // Allowed to moderate users in a room (mute all, - // spotlight video, kick users) MODERATOR : 'moderator', - // Same as MODERATOR, but can't moderate users + PRESENTER : 'presenter', AUTHENTICATED : 'authenticated', - // No priveleges - ALL : 'normal' + // Don't change anything after this point + + // All users have this role by default, do not change or remove this role + NORMAL : 'normal' }; \ No newline at end of file