Expand permissions/role system. Clients are now provisioned with their roles when they join and will have features enabled/disabled based on their permissions.

auto_join_3.3
Håvar Aambø Fosstveit 2020-04-02 00:28:05 +02:00
parent 197156e6f6
commit a6347dc283
16 changed files with 211 additions and 135 deletions

View File

@ -2371,10 +2371,8 @@ export default class RoomClient
{ {
logger.debug('_joinRoom()'); logger.debug('_joinRoom()');
const { const { displayName } = store.getState().settings;
displayName, const { picture } = store.getState().me;
picture
} = store.getState().settings;
try try
{ {
@ -2524,7 +2522,7 @@ export default class RoomClient
canShareFiles : this._torrentSupport canShareFiles : this._torrentSupport
})); }));
const { roles, peers } = await this.sendRequest( const { roles, peers, permissionsFromRoles, userRoles } = await this.sendRequest(
'join', 'join',
{ {
displayName : displayName, displayName : displayName,
@ -2534,6 +2532,9 @@ export default class RoomClient
logger.debug('_joinRoom() joined [peers:"%o", roles:"%o"]', peers, roles); 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; const myRoles = store.getState().me.roles;
for (const role of roles) for (const role of roles)

View File

@ -128,3 +128,15 @@ export const setCloseMeetingInProgress = (flag) =>
type : 'CLOSE_MEETING_IN_PROGRESS', type : 'CLOSE_MEETING_IN_PROGRESS',
payload : { flag } payload : { flag }
}); });
export const setUserRoles = (userRoles) =>
({
type : 'SET_USER_ROLES',
payload : { userRoles }
});
export const setPermissionsFromRoles = (permissionsFromRoles) =>
({
type : 'SET_PERMISSIONS_FROM_ROLES',
payload : { permissionsFromRoles }
});

View File

@ -7,72 +7,16 @@ import { withRoomContext } from '../../../RoomContext';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import ListItem from '@material-ui/core/ListItem'; import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText'; 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 ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar'; import Avatar from '@material-ui/core/Avatar';
import EmptyAvatar from '../../../images/avatar-empty.jpeg'; import EmptyAvatar from '../../../images/avatar-empty.jpeg';
import PromoteIcon from '@material-ui/icons/OpenInBrowser'; import PromoteIcon from '@material-ui/icons/OpenInBrowser';
import Tooltip from '@material-ui/core/Tooltip'; import Tooltip from '@material-ui/core/Tooltip';
const styles = (theme) => const styles = () =>
({ ({
root : 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' alignItems : 'center'
} }
@ -83,6 +27,7 @@ const ListLobbyPeer = (props) =>
const { const {
roomClient, roomClient,
peer, peer,
canPromote,
classes classes
} = props; } = props;
@ -92,7 +37,7 @@ const ListLobbyPeer = (props) =>
return ( return (
<ListItem <ListItem
className={classnames(classes.ListItem)} className={classnames(classes.root)}
key={peer.peerId} key={peer.peerId}
button button
alignItems='flex-start' alignItems='flex-start'
@ -109,10 +54,8 @@ const ListLobbyPeer = (props) =>
defaultMessage : 'Click to let them in' defaultMessage : 'Click to let them in'
})} })}
> >
<ListItemIcon <IconButton
className={classnames(classes.button, 'promote', { disabled={!canPromote || peer.promotionInProgress}
disabled : peer.promotionInProgress
})}
onClick={(e) => onClick={(e) =>
{ {
e.stopPropagation(); e.stopPropagation();
@ -120,7 +63,7 @@ const ListLobbyPeer = (props) =>
}} }}
> >
<PromoteIcon /> <PromoteIcon />
</ListItemIcon> </IconButton>
</Tooltip> </Tooltip>
</ListItem> </ListItem>
); );
@ -131,13 +74,17 @@ ListLobbyPeer.propTypes =
roomClient : PropTypes.any.isRequired, roomClient : PropTypes.any.isRequired,
advancedMode : PropTypes.bool, advancedMode : PropTypes.bool,
peer : PropTypes.object.isRequired, peer : PropTypes.object.isRequired,
canPromote : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired classes : PropTypes.object.isRequired
}; };
const mapStateToProps = (state, { id }) => const mapStateToProps = (state, { id }) =>
{ {
return { 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) => areStatesEqual : (next, prev) =>
{ {
return ( return (
prev.room.permissionsFromRoles === next.room.permissionsFromRoles &&
prev.me.roles === next.me.roles &&
prev.lobbyPeers === next.lobbyPeers prev.lobbyPeers === next.lobbyPeers
); );
} }

View File

@ -144,6 +144,7 @@ const Me = (props) =>
micProducer, micProducer,
webcamProducer, webcamProducer,
screenProducer, screenProducer,
canShareScreen,
classes, classes,
theme theme
} = props; } = props;
@ -396,7 +397,11 @@ const Me = (props) =>
defaultMessage : 'Start screen sharing' defaultMessage : 'Start screen sharing'
})} })}
className={classes.fab} className={classes.fab}
disabled={!me.canShareScreen || me.screenShareInProgress} disabled={
!canShareScreen ||
!me.canShareScreen ||
me.screenShareInProgress
}
color={screenState === 'on' ? 'primary' : 'default'} color={screenState === 'on' ? 'primary' : 'default'}
size={smallButtons ? 'small' : 'large'} size={smallButtons ? 'small' : 'large'}
onClick={() => onClick={() =>
@ -537,6 +542,7 @@ Me.propTypes =
spacing : PropTypes.number, spacing : PropTypes.number,
style : PropTypes.object, style : PropTypes.object,
smallButtons : PropTypes.bool, smallButtons : PropTypes.bool,
canShareScreen : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired, classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired theme : PropTypes.object.isRequired
}; };
@ -547,7 +553,10 @@ const mapStateToProps = (state) =>
me : state.me, me : state.me,
...meProducersSelector(state), ...meProducersSelector(state),
settings : state.settings, settings : state.settings,
activeSpeaker : state.me.id === state.room.activeSpeakerId 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) => areStatesEqual : (next, prev) =>
{ {
return ( return (
prev.room.permissionsFromRoles === next.room.permissionsFromRoles &&
prev.me === next.me && prev.me === next.me &&
prev.producers === next.producers && prev.producers === next.producers &&
prev.settings === next.settings && prev.settings === next.settings &&

View File

@ -135,6 +135,7 @@ const TopBar = (props) =>
toggleToolArea, toggleToolArea,
openUsersTab, openUsersTab,
unread, unread,
canLock,
classes classes
} = props; } = props;
@ -271,6 +272,7 @@ const TopBar = (props) =>
})} })}
className={classes.actionButton} className={classes.actionButton}
color='inherit' color='inherit'
disabled={!canLock}
onClick={() => onClick={() =>
{ {
if (room.locked) if (room.locked)
@ -377,6 +379,7 @@ TopBar.propTypes =
toggleToolArea : PropTypes.func.isRequired, toggleToolArea : PropTypes.func.isRequired,
openUsersTab : PropTypes.func.isRequired, openUsersTab : PropTypes.func.isRequired,
unread : PropTypes.number.isRequired, unread : PropTypes.number.isRequired,
canLock : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired, classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired theme : PropTypes.object.isRequired
}; };
@ -391,7 +394,10 @@ const mapStateToProps = (state) =>
loginEnabled : state.me.loginEnabled, loginEnabled : state.me.loginEnabled,
myPicture : state.me.picture, myPicture : state.me.picture,
unread : state.toolarea.unreadMessages + 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) => const mapDispatchToProps = (dispatch) =>
@ -434,6 +440,7 @@ export default withRoomContext(connect(
prev.me.loggedIn === next.me.loggedIn && prev.me.loggedIn === next.me.loggedIn &&
prev.me.loginEnabled === next.me.loginEnabled && prev.me.loginEnabled === next.me.loginEnabled &&
prev.me.picture === next.me.picture && prev.me.picture === next.me.picture &&
prev.me.roles === next.me.roles &&
prev.toolarea.unreadMessages === next.toolarea.unreadMessages && prev.toolarea.unreadMessages === next.toolarea.unreadMessages &&
prev.toolarea.unreadFiles === next.toolarea.unreadFiles prev.toolarea.unreadFiles === next.toolarea.unreadFiles
); );

View File

@ -54,6 +54,7 @@ const ChatInput = (props) =>
roomClient, roomClient,
displayName, displayName,
picture, picture,
canChat,
classes classes
} = props; } = props;
@ -66,6 +67,7 @@ const ChatInput = (props) =>
defaultMessage : 'Enter chat message...' defaultMessage : 'Enter chat message...'
})} })}
value={message || ''} value={message || ''}
disabled={!canChat}
onChange={handleChange} onChange={handleChange}
onKeyPress={(ev) => onKeyPress={(ev) =>
{ {
@ -89,6 +91,7 @@ const ChatInput = (props) =>
color='primary' color='primary'
className={classes.iconButton} className={classes.iconButton}
aria-label='Send' aria-label='Send'
disabled={!canChat}
onClick={() => onClick={() =>
{ {
if (message && message !== '') if (message && message !== '')
@ -112,13 +115,17 @@ ChatInput.propTypes =
roomClient : PropTypes.object.isRequired, roomClient : PropTypes.object.isRequired,
displayName : PropTypes.string, displayName : PropTypes.string,
picture : PropTypes.string, picture : PropTypes.string,
canChat : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired classes : PropTypes.object.isRequired
}; };
const mapStateToProps = (state) => const mapStateToProps = (state) =>
({ ({
displayName : state.settings.displayName, 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( export default withRoomContext(
@ -130,6 +137,8 @@ export default withRoomContext(
areStatesEqual : (next, prev) => areStatesEqual : (next, prev) =>
{ {
return ( return (
prev.room.permissionsFromRoles === next.room.permissionsFromRoles &&
prev.me.roles === next.me.roles &&
prev.settings.displayName === next.settings.displayName && prev.settings.displayName === next.settings.displayName &&
prev.me.picture === next.me.picture prev.me.picture === next.me.picture
); );

View File

@ -41,6 +41,7 @@ const FileSharing = (props) =>
const { const {
canShareFiles, canShareFiles,
canShare,
classes classes
} = props; } = props;
@ -60,6 +61,7 @@ const FileSharing = (props) =>
<input <input
className={classes.input} className={classes.input}
type='file' type='file'
disabled={!canShare}
onChange={handleFileChange} onChange={handleFileChange}
id='share-files-button' id='share-files-button'
/> />
@ -68,7 +70,7 @@ const FileSharing = (props) =>
variant='contained' variant='contained'
component='span' component='span'
className={classes.button} className={classes.button}
disabled={!canShareFiles} disabled={!canShareFiles || !canShare}
> >
{buttonDescription} {buttonDescription}
</Button> </Button>
@ -83,6 +85,7 @@ FileSharing.propTypes = {
roomClient : PropTypes.any.isRequired, roomClient : PropTypes.any.isRequired,
canShareFiles : PropTypes.bool.isRequired, canShareFiles : PropTypes.bool.isRequired,
tabOpen : PropTypes.bool.isRequired, tabOpen : PropTypes.bool.isRequired,
canShare : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired classes : PropTypes.object.isRequired
}; };
@ -90,10 +93,26 @@ const mapStateToProps = (state) =>
{ {
return { return {
canShareFiles : state.me.canShareFiles, 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( 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))); )(withStyles(styles)(FileSharing)));

View File

@ -13,7 +13,6 @@ import ListPeer from './ListPeer';
import ListMe from './ListMe'; import ListMe from './ListMe';
import ListModerator from './ListModerator'; import ListModerator from './ListModerator';
import Volume from '../../Containers/Volume'; import Volume from '../../Containers/Volume';
import * as userRoles from '../../../reducers/userRoles';
const styles = (theme) => const styles = (theme) =>
({ ({
@ -170,8 +169,9 @@ ParticipantList.propTypes =
const mapStateToProps = (state) => const mapStateToProps = (state) =>
{ {
return { return {
isModerator : state.me.roles.includes(userRoles.MODERATOR) || isModerator :
state.me.roles.includes(userRoles.ADMIN), state.me.roles.some((role) =>
state.room.permissionsFromRoles.MODERATE_ROOM.includes(role)),
passivePeers : passivePeersSelector(state), passivePeers : passivePeersSelector(state),
selectedPeerId : state.room.selectedPeerId, selectedPeerId : state.room.selectedPeerId,
spotlightPeers : spotlightPeersSelector(state) spotlightPeers : spotlightPeersSelector(state)
@ -186,6 +186,7 @@ const ParticipantListContainer = withRoomContext(connect(
areStatesEqual : (next, prev) => areStatesEqual : (next, prev) =>
{ {
return ( return (
prev.room.permissionsFromRoles === next.room.permissionsFromRoles &&
prev.me.roles === next.me.roles && prev.me.roles === next.me.roles &&
prev.peers === next.peers && prev.peers === next.peers &&
prev.room.spotlights === next.room.spotlights && prev.room.spotlights === next.room.spotlights &&

View File

@ -1,11 +1,9 @@
import * as userRoles from './userRoles';
const initialState = const initialState =
{ {
id : null, id : null,
picture : null, picture : null,
isMobile : false, isMobile : false,
roles : [ userRoles.ALL ], roles : [ 'normal' ], // Default role
canSendMic : false, canSendMic : false,
canSendWebcam : false, canSendWebcam : false,
canShareScreen : false, canShareScreen : false,

View File

@ -21,7 +21,16 @@ const initialState =
joined : false, joined : false,
muteAllInProgress : false, muteAllInProgress : false,
stopAllVideoInProgress : 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) => const room = (state = initialState, action) =>
@ -175,6 +184,20 @@ const room = (state = initialState, action) =>
case 'CLOSE_MEETING_IN_PROGRESS': case 'CLOSE_MEETING_IN_PROGRESS':
return { ...state, closeMeetingInProgress: action.payload.flag }; 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: default:
return state; return state;
} }

View File

@ -1,4 +0,0 @@
export const ADMIN = 'admin';
export const MODERATOR = 'moderator';
export const AUTHENTICATED = 'authenticated';
export const ALL = 'normal';

View File

@ -194,15 +194,37 @@ module.exports =
peer.email = userinfo.email; peer.email = userinfo.email;
} }
}, },
// Required roles for Access. All users have the role "ALL" by default. // All users have the role "NORMAL" by default. Other roles need to be
// Other roles need to be added in the "userMapping" function. This // added in the "userMapping" function. The following accesses and
// is an Array of roles. userRoles.ADMIN have all priveleges and access // permissions are arrays of roles. Roles can be changed in userRoles.js
// always.
// //
// Example: // Example:
// [ userRoles.MODERATOR, userRoles.AUTHENTICATED ] // [ userRoles.MODERATOR, userRoles.AUTHENTICATED ]
// This will allow all MODERATOR and AUTHENTICATED users access. accessFromRoles : {
requiredRolesForAccess : [ userRoles.ALL ], // 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 // When truthy, the room will be open to all users when as long as there
// are allready users in the room // are allready users in the room
activateOnHostJoin : true, activateOnHostJoin : true,

View File

@ -25,7 +25,7 @@ class Peer extends EventEmitter
this._inLobby = false; this._inLobby = false;
this._roles = [ userRoles.ALL ]; this._roles = [ userRoles.NORMAL ];
this._displayName = false; this._displayName = false;

View File

@ -59,12 +59,6 @@ class Room extends EventEmitter
// Locked flag. // Locked flag.
this._locked = false; 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 // if true: accessCode is a possibility to open the room
this._joinByAccesCode = true; this._joinByAccesCode = true;
@ -157,15 +151,16 @@ class Room extends EventEmitter
// Returning user // Returning user
if (returning) if (returning)
this._peerJoining(peer, true); this._peerJoining(peer, true);
// Always let ADMIN in, even if locked else if ( // Has a role that is allowed to bypass room lock
else if (peer.roles.includes(userRoles.ADMIN)) peer.roles.some((role) => config.accessFromRoles.BYPASS_ROOM_LOCK.includes(role))
)
this._peerJoining(peer); this._peerJoining(peer);
else if (this._locked) else if (this._locked)
this._parkPeer(peer); this._parkPeer(peer);
else else
{ {
// If the user has a role in config.requiredRolesForAccess, let them in // Has a role that is allowed to bypass lobby
peer.roles.some((role) => this._requiredRoles.includes(role)) ? peer.roles.some((role) => config.accessFromRoles.BYPASS_LOBBY.includes(role)) ?
this._peerJoining(peer) : this._peerJoining(peer) :
this._handleGuest(peer); this._handleGuest(peer);
} }
@ -200,18 +195,18 @@ class Room extends EventEmitter
this._lobby.on('peerRolesChanged', (peer) => this._lobby.on('peerRolesChanged', (peer) =>
{ {
// Always let admin in, even if locked if ( // Has a role that is allowed to bypass room lock
if (peer.roles.includes(userRoles.ADMIN)) peer.roles.some((role) => config.accessFromRoles.BYPASS_ROOM_LOCK.includes(role))
)
{ {
this._lobby.promotePeer(peer.id); this._lobby.promotePeer(peer.id);
return; return;
} }
// If the user has a role in config.requiredRolesForAccess, let them in if ( // Has a role that is allowed to bypass lobby
if (
!this._locked && !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); this._lobby.promotePeer(peer.id);
@ -555,7 +550,9 @@ class Room extends EventEmitter
cb(null, { cb(null, {
roles : peer.roles, roles : peer.roles,
peers : peerInfos peers : peerInfos,
permissionsFromRoles : config.permissionsFromRoles,
userRoles : userRoles
}); });
// Mark the new Peer as joined. // Mark the new Peer as joined.
@ -682,12 +679,19 @@ class Room extends EventEmitter
case 'produce': 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. // Ensure the Peer is joined.
if (!peer.joined) if (!peer.joined)
throw new Error('Peer not yet joined'); throw new Error('Peer not yet joined');
const { transportId, kind, rtpParameters } = request.data; const { transportId, kind, rtpParameters } = request.data;
let { appData } = request.data;
const transport = peer.getTransport(transportId); const transport = peer.getTransport(transportId);
if (!transport) if (!transport)
@ -987,6 +991,11 @@ class Room extends EventEmitter
case 'chatMessage': case 'chatMessage':
{ {
if (
!peer.roles.some((role) => config.permissionsFromRoles.SEND_CHAT.includes(role))
)
throw new Error('peer not authorized');
const { chatMessage } = request.data; const { chatMessage } = request.data;
this._chatHistory.push(chatMessage); this._chatHistory.push(chatMessage);
@ -1025,6 +1034,11 @@ class Room extends EventEmitter
case 'lockRoom': case 'lockRoom':
{ {
if (
!peer.roles.some((role) => config.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role))
)
throw new Error('peer not authorized');
this._locked = true; this._locked = true;
// Spread to others // Spread to others
@ -1040,6 +1054,11 @@ class Room extends EventEmitter
case 'unlockRoom': case 'unlockRoom':
{ {
if (
!peer.roles.some((role) => config.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role))
)
throw new Error('peer not authorized');
this._locked = false; this._locked = false;
// Spread to others // Spread to others
@ -1095,6 +1114,11 @@ class Room extends EventEmitter
case 'promotePeer': case 'promotePeer':
{ {
if (
!peer.roles.some((role) => config.permissionsFromRoles.PROMOTE_PEER.includes(role))
)
throw new Error('peer not authorized');
const { peerId } = request.data; const { peerId } = request.data;
this._lobby.promotePeer(peerId); this._lobby.promotePeer(peerId);
@ -1107,6 +1131,11 @@ class Room extends EventEmitter
case 'promoteAllPeers': case 'promoteAllPeers':
{ {
if (
!peer.roles.some((role) => config.permissionsFromRoles.PROMOTE_PEER.includes(role))
)
throw new Error('peer not authorized');
this._lobby.promoteAllPeers(); this._lobby.promoteAllPeers();
// Return no error // Return no error
@ -1117,6 +1146,11 @@ class Room extends EventEmitter
case 'sendFile': case 'sendFile':
{ {
if (
!peer.roles.some((role) => config.permissionsFromRoles.SHARE_FILE.includes(role))
)
throw new Error('peer not authorized');
const { magnetUri } = request.data; const { magnetUri } = request.data;
this._fileHistory.push({ peerId: peer.id, magnetUri: magnetUri }); this._fileHistory.push({ peerId: peer.id, magnetUri: magnetUri });
@ -1154,10 +1188,9 @@ class Room extends EventEmitter
case 'moderator:muteAll': case 'moderator:muteAll':
{ {
if ( if (
!peer.hasRole(userRoles.MODERATOR) && !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role))
!peer.hasRole(userRoles.ADMIN)
) )
throw new Error('peer does not have moderator priveleges'); throw new Error('peer not authorized');
// Spread to others // Spread to others
this._notification(peer.socket, 'moderator:mute', { this._notification(peer.socket, 'moderator:mute', {
@ -1172,10 +1205,9 @@ class Room extends EventEmitter
case 'moderator:stopAllVideo': case 'moderator:stopAllVideo':
{ {
if ( if (
!peer.hasRole(userRoles.MODERATOR) && !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role))
!peer.hasRole(userRoles.ADMIN)
) )
throw new Error('peer does not have moderator priveleges'); throw new Error('peer not authorized');
// Spread to others // Spread to others
this._notification(peer.socket, 'moderator:stopVideo', { this._notification(peer.socket, 'moderator:stopVideo', {
@ -1190,10 +1222,9 @@ class Room extends EventEmitter
case 'moderator:closeMeeting': case 'moderator:closeMeeting':
{ {
if ( if (
!peer.hasRole(userRoles.MODERATOR) && !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role))
!peer.hasRole(userRoles.ADMIN)
) )
throw new Error('peer does not have moderator priveleges'); throw new Error('peer not authorized');
this._notification( this._notification(
peer.socket, peer.socket,
@ -1213,10 +1244,9 @@ class Room extends EventEmitter
case 'moderator:kickPeer': case 'moderator:kickPeer':
{ {
if ( if (
!peer.hasRole(userRoles.MODERATOR) && !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role))
!peer.hasRole(userRoles.ADMIN)
) )
throw new Error('peer does not have moderator priveleges'); throw new Error('peer not authorized');
const { peerId } = request.data; const { peerId } = request.data;

View File

@ -327,7 +327,7 @@ async function setupAuth()
{ {
for (const role of peer.roles) for (const role of peer.roles)
{ {
if (role !== userRoles.ALL) if (role !== userRoles.NORMAL)
peer.removeRole(role); peer.removeRole(role);
} }
} }

View File

@ -1,12 +1,11 @@
module.exports = { module.exports = {
// Allowed to enter locked rooms + all other priveleges // These can be changed
ADMIN : 'admin', ADMIN : 'admin',
// Allowed to enter restricted rooms if configured.
// Allowed to moderate users in a room (mute all,
// spotlight video, kick users)
MODERATOR : 'moderator', MODERATOR : 'moderator',
// Same as MODERATOR, but can't moderate users PRESENTER : 'presenter',
AUTHENTICATED : 'authenticated', AUTHENTICATED : 'authenticated',
// No priveleges // Don't change anything after this point
ALL : 'normal'
// All users have this role by default, do not change or remove this role
NORMAL : 'normal'
}; };