New option for handling permissions in rooms. Set allowWhenRoleMissing to permit actions before a peer with that permission joins. Ref #303
parent
220d4dd99d
commit
a49258e840
|
|
@ -2847,8 +2847,8 @@ export default class RoomClient
|
||||||
roles,
|
roles,
|
||||||
peers,
|
peers,
|
||||||
tracker,
|
tracker,
|
||||||
permissionsFromRoles,
|
roomPermissions,
|
||||||
userRoles,
|
allowWhenRoleMissing,
|
||||||
chatHistory,
|
chatHistory,
|
||||||
fileHistory,
|
fileHistory,
|
||||||
lastNHistory,
|
lastNHistory,
|
||||||
|
|
@ -2874,8 +2874,10 @@ export default class RoomClient
|
||||||
|
|
||||||
store.dispatch(meActions.loggedIn(authenticated));
|
store.dispatch(meActions.loggedIn(authenticated));
|
||||||
|
|
||||||
store.dispatch(roomActions.setUserRoles(userRoles));
|
store.dispatch(roomActions.setRoomPermissions(roomPermissions));
|
||||||
store.dispatch(roomActions.setPermissionsFromRoles(permissionsFromRoles));
|
|
||||||
|
if (allowWhenRoleMissing)
|
||||||
|
store.dispatch(roomActions.setAllowWhenRoleMissing(allowWhenRoleMissing));
|
||||||
|
|
||||||
const myRoles = store.getState().me.roles;
|
const myRoles = store.getState().me.roles;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,14 +177,14 @@ export const setClearFileSharingInProgress = (flag) =>
|
||||||
payload : { flag }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setUserRoles = (userRoles) =>
|
export const setRoomPermissions = (roomPermissions) =>
|
||||||
({
|
({
|
||||||
type : 'SET_USER_ROLES',
|
type : 'SET_ROOM_PERMISSIONS',
|
||||||
payload : { userRoles }
|
payload : { roomPermissions }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setPermissionsFromRoles = (permissionsFromRoles) =>
|
export const setAllowWhenRoleMissing = (allowWhenRoleMissing) =>
|
||||||
({
|
({
|
||||||
type : 'SET_PERMISSIONS_FROM_ROLES',
|
type : 'SET_ALLOW_WHEN_ROLE_MISSING',
|
||||||
payload : { permissionsFromRoles }
|
payload : { allowWhenRoleMissing }
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
|
import { makePermissionSelector } from '../../Selectors';
|
||||||
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 IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
|
@ -85,28 +87,32 @@ ListLobbyPeer.propTypes =
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state, { id }) =>
|
const makeMapStateToProps = (initialState, { id }) =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.PROMOTE_PEER);
|
||||||
peer : state.lobbyPeers[id],
|
|
||||||
promotionInProgress : state.room.lobbyPeersPromotionInProgress,
|
const mapStateToProps = (state) =>
|
||||||
canPromote :
|
{
|
||||||
state.me.roles.some((role) =>
|
return {
|
||||||
state.room.permissionsFromRoles.PROMOTE_PEER.includes(role))
|
peer : state.lobbyPeers[id],
|
||||||
|
promotionInProgress : state.room.lobbyPeersPromotionInProgress,
|
||||||
|
canPromote : hasPermission(state)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room.permissionsFromRoles === next.room.permissionsFromRoles &&
|
prev.room === next.room &&
|
||||||
prev.room.lobbyPeersPromotionInProgress ===
|
prev.peers === next.peers && // For checking permissions
|
||||||
next.room.lobbyPeersPromotionInProgress &&
|
|
||||||
prev.me.roles === next.me.roles &&
|
prev.me.roles === next.me.roles &&
|
||||||
prev.lobbyPeers === next.lobbyPeers
|
prev.lobbyPeers === next.lobbyPeers
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
lobbyPeersKeySelector
|
lobbyPeersKeySelector,
|
||||||
|
makePermissionSelector
|
||||||
} from '../../Selectors';
|
} from '../../Selectors';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
import * as appPropTypes from '../../appPropTypes';
|
import * as appPropTypes from '../../appPropTypes';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
|
|
@ -140,15 +142,20 @@ LockDialog.propTypes =
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.PROMOTE_PEER);
|
||||||
room : state.room,
|
|
||||||
lobbyPeers : lobbyPeersKeySelector(state),
|
const mapStateToProps = (state) =>
|
||||||
canPromote :
|
{
|
||||||
state.me.roles.some((role) =>
|
return {
|
||||||
state.room.permissionsFromRoles.PROMOTE_PEER.includes(role))
|
room : state.room,
|
||||||
|
lobbyPeers : lobbyPeersKeySelector(state),
|
||||||
|
canPromote : hasPermission(state)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
|
|
@ -157,7 +164,7 @@ const mapDispatchToProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
|
|
@ -166,6 +173,7 @@ export default withRoomContext(connect(
|
||||||
return (
|
return (
|
||||||
prev.room === next.room &&
|
prev.room === next.room &&
|
||||||
prev.me.roles === next.me.roles &&
|
prev.me.roles === next.me.roles &&
|
||||||
|
prev.peers === next.peers &&
|
||||||
prev.lobbyPeers === next.lobbyPeers
|
prev.lobbyPeers === next.lobbyPeers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { meProducersSelector } from '../Selectors';
|
import {
|
||||||
|
meProducersSelector,
|
||||||
|
makePermissionSelector
|
||||||
|
} from '../Selectors';
|
||||||
|
import { permissions } from '../../permissions';
|
||||||
import { withRoomContext } from '../../RoomContext';
|
import { withRoomContext } from '../../RoomContext';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
@ -807,32 +811,37 @@ Me.propTypes =
|
||||||
theme : PropTypes.object.isRequired
|
theme : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.SHARE_SCREEN);
|
||||||
me : state.me,
|
|
||||||
...meProducersSelector(state),
|
const mapStateToProps = (state) =>
|
||||||
settings : state.settings,
|
{
|
||||||
activeSpeaker : state.me.id === state.room.activeSpeakerId,
|
return {
|
||||||
canShareScreen :
|
me : state.me,
|
||||||
state.me.roles.some((role) =>
|
...meProducersSelector(state),
|
||||||
state.room.permissionsFromRoles.SHARE_SCREEN.includes(role))
|
settings : state.settings,
|
||||||
|
activeSpeaker : state.me.id === state.room.activeSpeakerId,
|
||||||
|
canShareScreen : hasPermission(state)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room.permissionsFromRoles === next.room.permissionsFromRoles &&
|
prev.room === next.room &&
|
||||||
prev.me === next.me &&
|
prev.me === next.me &&
|
||||||
|
prev.peers === next.peers &&
|
||||||
prev.producers === next.producers &&
|
prev.producers === next.producers &&
|
||||||
prev.settings === next.settings &&
|
prev.settings === next.settings
|
||||||
prev.room.activeSpeakerId === next.room.activeSpeakerId
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
lobbyPeersKeySelector,
|
lobbyPeersKeySelector,
|
||||||
peersLengthSelector,
|
peersLengthSelector,
|
||||||
raisedHandsSelector
|
raisedHandsSelector,
|
||||||
|
makePermissionSelector
|
||||||
} from '../Selectors';
|
} from '../Selectors';
|
||||||
|
import { permissions } from '../../permissions';
|
||||||
import * as appPropTypes from '../appPropTypes';
|
import * as appPropTypes from '../appPropTypes';
|
||||||
import { withRoomContext } from '../../RoomContext';
|
import { withRoomContext } from '../../RoomContext';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
|
@ -751,27 +753,35 @@ TopBar.propTypes =
|
||||||
theme : PropTypes.object.isRequired
|
theme : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
({
|
{
|
||||||
room : state.room,
|
const hasExtraVideoPermission =
|
||||||
peersLength : peersLengthSelector(state),
|
makePermissionSelector(permissions.EXTRA_VIDEO);
|
||||||
lobbyPeers : lobbyPeersKeySelector(state),
|
|
||||||
permanentTopBar : state.settings.permanentTopBar,
|
const hasLockPermission =
|
||||||
loggedIn : state.me.loggedIn,
|
makePermissionSelector(permissions.CHANGE_ROOM_LOCK);
|
||||||
loginEnabled : state.me.loginEnabled,
|
|
||||||
myPicture : state.me.picture,
|
const hasPromotionPermission =
|
||||||
unread : state.toolarea.unreadMessages +
|
makePermissionSelector(permissions.PROMOTE_PEER);
|
||||||
state.toolarea.unreadFiles + raisedHandsSelector(state),
|
|
||||||
canProduceExtraVideo :
|
const mapStateToProps = (state) =>
|
||||||
state.me.roles.some((role) =>
|
({
|
||||||
state.room.permissionsFromRoles.EXTRA_VIDEO.includes(role)),
|
room : state.room,
|
||||||
canLock :
|
peersLength : peersLengthSelector(state),
|
||||||
state.me.roles.some((role) =>
|
lobbyPeers : lobbyPeersKeySelector(state),
|
||||||
state.room.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)),
|
permanentTopBar : state.settings.permanentTopBar,
|
||||||
canPromote :
|
loggedIn : state.me.loggedIn,
|
||||||
state.me.roles.some((role) =>
|
loginEnabled : state.me.loginEnabled,
|
||||||
state.room.permissionsFromRoles.PROMOTE_PEER.includes(role))
|
myPicture : state.me.picture,
|
||||||
});
|
unread : state.toolarea.unreadMessages +
|
||||||
|
state.toolarea.unreadFiles + raisedHandsSelector(state),
|
||||||
|
canProduceExtraVideo : hasExtraVideoPermission(state),
|
||||||
|
canLock : hasLockPermission(state),
|
||||||
|
canPromote : hasPromotionPermission(state)
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) =>
|
const mapDispatchToProps = (dispatch) =>
|
||||||
({
|
({
|
||||||
|
|
@ -811,7 +821,7 @@ const mapDispatchToProps = (dispatch) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import PropTypes from 'prop-types';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
|
import { makePermissionSelector } from '../../Selectors';
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
import InputBase from '@material-ui/core/InputBase';
|
import InputBase from '@material-ui/core/InputBase';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
|
@ -119,26 +121,32 @@ ChatInput.propTypes =
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
({
|
{
|
||||||
displayName : state.settings.displayName,
|
const hasPermission = makePermissionSelector(permissions.SEND_CHAT);
|
||||||
picture : state.me.picture,
|
|
||||||
canChat :
|
const mapStateToProps = (state) =>
|
||||||
state.me.roles.some((role) =>
|
({
|
||||||
state.room.permissionsFromRoles.SEND_CHAT.includes(role))
|
displayName : state.settings.displayName,
|
||||||
});
|
picture : state.me.picture,
|
||||||
|
canChat : hasPermission(state)
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
export default withRoomContext(
|
export default withRoomContext(
|
||||||
connect(
|
connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room.permissionsFromRoles === next.room.permissionsFromRoles &&
|
prev.room === next.room &&
|
||||||
prev.me.roles === next.me.roles &&
|
prev.me.roles === next.me.roles &&
|
||||||
|
prev.peers === next.peers &&
|
||||||
prev.settings.displayName === next.settings.displayName &&
|
prev.settings.displayName === next.settings.displayName &&
|
||||||
prev.me.picture === next.me.picture
|
prev.me.picture === next.me.picture
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import PropTypes from 'prop-types';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
|
import { makePermissionSelector } from '../../Selectors';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
|
|
@ -76,16 +78,21 @@ ChatModerator.propTypes =
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
({
|
{
|
||||||
isChatModerator :
|
const hasPermission = makePermissionSelector(permissions.MODERATE_CHAT);
|
||||||
state.me.roles.some((role) =>
|
|
||||||
state.room.permissionsFromRoles.MODERATE_CHAT.includes(role)),
|
const mapStateToProps = (state) =>
|
||||||
room : state.room
|
({
|
||||||
});
|
isChatModerator : hasPermission(state),
|
||||||
|
room : state.room
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
|
|
@ -93,7 +100,8 @@ export default withRoomContext(connect(
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room === next.room &&
|
prev.room === next.room &&
|
||||||
prev.me === next.me
|
prev.me === next.me &&
|
||||||
|
prev.peers === next.peers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import { connect } from 'react-redux';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
|
import { makePermissionSelector } from '../../Selectors';
|
||||||
import FileList from './FileList';
|
import FileList from './FileList';
|
||||||
import FileSharingModerator from './FileSharingModerator';
|
import FileSharingModerator from './FileSharingModerator';
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
|
|
@ -131,30 +133,36 @@ FileSharing.propTypes = {
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.SHARE_FILE);
|
||||||
canShareFiles : state.me.canShareFiles,
|
|
||||||
browser : state.me.browser,
|
const mapStateToProps = (state) =>
|
||||||
tabOpen : state.toolarea.currentToolTab === 'files',
|
{
|
||||||
canShare :
|
return {
|
||||||
state.me.roles.some((role) =>
|
canShareFiles : state.me.canShareFiles,
|
||||||
state.room.permissionsFromRoles.SHARE_FILE.includes(role))
|
browser : state.me.browser,
|
||||||
|
tabOpen : state.toolarea.currentToolTab === 'files',
|
||||||
|
canShare : hasPermission(state)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room.permissionsFromRoles === next.room.permissionsFromRoles &&
|
prev.room === next.room &&
|
||||||
prev.me.browser === next.me.browser &&
|
prev.me.browser === next.me.browser &&
|
||||||
prev.me.roles === next.me.roles &&
|
prev.me.roles === next.me.roles &&
|
||||||
prev.me.canShareFiles === next.me.canShareFiles &&
|
prev.me.canShareFiles === next.me.canShareFiles &&
|
||||||
|
prev.peers === next.peers &&
|
||||||
prev.toolarea.currentToolTab === next.toolarea.currentToolTab
|
prev.toolarea.currentToolTab === next.toolarea.currentToolTab
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import PropTypes from 'prop-types';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
|
import { makePermissionSelector } from '../../Selectors';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
|
|
@ -76,16 +78,21 @@ FileSharingModerator.propTypes =
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
({
|
{
|
||||||
isFileSharingModerator :
|
const hasPermission = makePermissionSelector(permissions.MODERATE_FILES);
|
||||||
state.me.roles.some((role) =>
|
|
||||||
state.room.permissionsFromRoles.MODERATE_FILES.includes(role)),
|
const mapStateToProps = (state) =>
|
||||||
room : state.room
|
({
|
||||||
});
|
isFileSharingModerator : hasPermission(state),
|
||||||
|
room : state.room
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
export default withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
|
|
@ -93,7 +100,8 @@ export default withRoomContext(connect(
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room === next.room &&
|
prev.room === next.room &&
|
||||||
prev.me === next.me
|
prev.me === next.me &&
|
||||||
|
prev.peers === next.peers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
participantListSelector
|
participantListSelector,
|
||||||
|
makePermissionSelector
|
||||||
} from '../../Selectors';
|
} from '../../Selectors';
|
||||||
|
import { permissions } from '../../../permissions';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../../../RoomContext';
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
|
|
@ -160,31 +162,34 @@ ParticipantList.propTypes =
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const makeMapStateToProps = () =>
|
||||||
{
|
{
|
||||||
return {
|
const hasPermission = makePermissionSelector(permissions.MODERATE_ROOM);
|
||||||
isModerator :
|
|
||||||
state.me.roles.some((role) =>
|
const mapStateToProps = (state) =>
|
||||||
state.room.permissionsFromRoles.MODERATE_ROOM.includes(role)),
|
{
|
||||||
participants : participantListSelector(state),
|
return {
|
||||||
spotlights : state.room.spotlights,
|
isModerator : hasPermission(state),
|
||||||
selectedPeerId : state.room.selectedPeerId
|
participants : participantListSelector(state),
|
||||||
|
spotlights : state.room.spotlights,
|
||||||
|
selectedPeerId : state.room.selectedPeerId
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParticipantListContainer = withRoomContext(connect(
|
const ParticipantListContainer = withRoomContext(connect(
|
||||||
mapStateToProps,
|
makeMapStateToProps,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.room.permissionsFromRoles === next.room.permissionsFromRoles &&
|
prev.room === next.room &&
|
||||||
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.selectedPeerId === next.room.selectedPeerId
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const meRolesSelect = (state) => state.me.roles;
|
||||||
|
const roomPermissionsSelect = (state) => state.room.roomPermissions;
|
||||||
|
const roomAllowWhenRoleMissing = (state) => state.room.allowWhenRoleMissing;
|
||||||
const producersSelect = (state) => state.producers;
|
const producersSelect = (state) => state.producers;
|
||||||
const consumersSelect = (state) => state.consumers;
|
const consumersSelect = (state) => state.consumers;
|
||||||
const spotlightsSelector = (state) => state.room.spotlights;
|
const spotlightsSelector = (state) => state.room.spotlights;
|
||||||
|
|
@ -217,3 +220,53 @@ export const makePeerConsumerSelector = () =>
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Very important that the Components that use this
|
||||||
|
// selector need to check at least these state changes:
|
||||||
|
//
|
||||||
|
// areStatesEqual : (next, prev) =>
|
||||||
|
// {
|
||||||
|
// return (
|
||||||
|
// prev.room.roomPermissions === next.room.roomPermissions &&
|
||||||
|
// prev.room.allowWhenRoleMissing === next.room.allowWhenRoleMissing &&
|
||||||
|
// prev.peers === next.peers &&
|
||||||
|
// prev.me.roles === next.me.roles
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
export const makePermissionSelector = (permission) =>
|
||||||
|
{
|
||||||
|
return createSelector(
|
||||||
|
meRolesSelect,
|
||||||
|
roomPermissionsSelect,
|
||||||
|
roomAllowWhenRoleMissing,
|
||||||
|
peersValueSelector,
|
||||||
|
(roles, roomPermissions, allowWhenRoleMissing, peers) =>
|
||||||
|
{
|
||||||
|
if (!roomPermissions)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const permitted = roles.some((role) =>
|
||||||
|
roomPermissions[permission].includes(role)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (permitted)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!allowWhenRoleMissing)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Allow if config is set, and no one is present
|
||||||
|
if (allowWhenRoleMissing.includes(permission) &&
|
||||||
|
peers.filter(
|
||||||
|
(peer) =>
|
||||||
|
peer.roles.some(
|
||||||
|
(role) => roomPermissions[permission].includes(role)
|
||||||
|
)
|
||||||
|
).length === 0
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export const permissions = {
|
||||||
|
// The role(s) have permission to lock/unlock a room
|
||||||
|
CHANGE_ROOM_LOCK : 'CHANGE_ROOM_LOCK',
|
||||||
|
// The role(s) have permission to promote a peer from the lobby
|
||||||
|
PROMOTE_PEER : 'PROMOTE_PEER',
|
||||||
|
// The role(s) have permission to send chat messages
|
||||||
|
SEND_CHAT : 'SEND_CHAT',
|
||||||
|
// The role(s) have permission to moderate chat
|
||||||
|
MODERATE_CHAT : 'MODERATE_CHAT',
|
||||||
|
// The role(s) have permission to share screen
|
||||||
|
SHARE_SCREEN : 'SHARE_SCREEN',
|
||||||
|
// The role(s) have permission to produce extra video
|
||||||
|
EXTRA_VIDEO : 'EXTRA_VIDEO',
|
||||||
|
// The role(s) have permission to share files
|
||||||
|
SHARE_FILE : 'SHARE_FILE',
|
||||||
|
// The role(s) have permission to moderate files
|
||||||
|
MODERATE_FILES : 'MODERATE_FILES',
|
||||||
|
// The role(s) have permission to moderate room (e.g. kick user)
|
||||||
|
MODERATE_ROOM : 'MODERATE_ROOM'
|
||||||
|
};
|
||||||
|
|
@ -33,18 +33,8 @@ const initialState =
|
||||||
closeMeetingInProgress : false,
|
closeMeetingInProgress : false,
|
||||||
clearChatInProgress : false,
|
clearChatInProgress : false,
|
||||||
clearFileSharingInProgress : false,
|
clearFileSharingInProgress : false,
|
||||||
userRoles : { NORMAL: 'normal' }, // Default role
|
roomPermissions : null,
|
||||||
permissionsFromRoles : {
|
allowWhenRoleMissing : null
|
||||||
CHANGE_ROOM_LOCK : [],
|
|
||||||
PROMOTE_PEER : [],
|
|
||||||
SEND_CHAT : [],
|
|
||||||
MODERATE_CHAT : [],
|
|
||||||
SHARE_SCREEN : [],
|
|
||||||
EXTRA_VIDEO : [],
|
|
||||||
SHARE_FILE : [],
|
|
||||||
MODERATE_FILES : [],
|
|
||||||
MODERATE_ROOM : []
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const room = (state = initialState, action) =>
|
const room = (state = initialState, action) =>
|
||||||
|
|
@ -240,18 +230,18 @@ const room = (state = initialState, action) =>
|
||||||
case 'CLEAR_FILE_SHARING_IN_PROGRESS':
|
case 'CLEAR_FILE_SHARING_IN_PROGRESS':
|
||||||
return { ...state, clearFileSharingInProgress: action.payload.flag };
|
return { ...state, clearFileSharingInProgress: action.payload.flag };
|
||||||
|
|
||||||
case 'SET_USER_ROLES':
|
case 'SET_ROOM_PERMISSIONS':
|
||||||
{
|
{
|
||||||
const { userRoles } = action.payload;
|
const { roomPermissions } = action.payload;
|
||||||
|
|
||||||
return { ...state, userRoles };
|
return { ...state, roomPermissions };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_PERMISSIONS_FROM_ROLES':
|
case 'SET_ALLOW_WHEN_ROLE_MISSING':
|
||||||
{
|
{
|
||||||
const { permissionsFromRoles } = action.payload;
|
const { allowWhenRoleMissing } = action.payload;
|
||||||
|
|
||||||
return { ...state, permissionsFromRoles };
|
return { ...state, allowWhenRoleMissing };
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
module.exports = {
|
||||||
|
// The role(s) will gain access to the room
|
||||||
|
// even if it is locked (!)
|
||||||
|
BYPASS_ROOM_LOCK : 'BYPASS_ROOM_LOCK',
|
||||||
|
// 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 : 'BYPASS_LOBBY'
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,23 @@
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const userRoles = require('../userRoles');
|
const userRoles = require('../userRoles');
|
||||||
|
|
||||||
|
const {
|
||||||
|
BYPASS_ROOM_LOCK,
|
||||||
|
BYPASS_LOBBY
|
||||||
|
} = require('../access');
|
||||||
|
|
||||||
|
const {
|
||||||
|
CHANGE_ROOM_LOCK,
|
||||||
|
PROMOTE_PEER,
|
||||||
|
SEND_CHAT,
|
||||||
|
MODERATE_CHAT,
|
||||||
|
SHARE_SCREEN,
|
||||||
|
EXTRA_VIDEO,
|
||||||
|
SHARE_FILE,
|
||||||
|
MODERATE_FILES,
|
||||||
|
MODERATE_ROOM
|
||||||
|
} = require('../permissions');
|
||||||
|
|
||||||
// const AwaitQueue = require('awaitqueue');
|
// const AwaitQueue = require('awaitqueue');
|
||||||
// const axios = require('axios');
|
// const axios = require('axios');
|
||||||
|
|
||||||
|
|
@ -216,44 +234,50 @@ module.exports =
|
||||||
accessFromRoles : {
|
accessFromRoles : {
|
||||||
// The role(s) will gain access to the room
|
// The role(s) will gain access to the room
|
||||||
// even if it is locked (!)
|
// even if it is locked (!)
|
||||||
BYPASS_ROOM_LOCK : [ userRoles.ADMIN ],
|
[BYPASS_ROOM_LOCK] : [ userRoles.ADMIN ],
|
||||||
// The role(s) will gain access to the room without
|
// The role(s) will gain access to the room without
|
||||||
// going into the lobby. If you want to restrict access to your
|
// going into the lobby. If you want to restrict access to your
|
||||||
// server to only directly allow authenticated users, you could
|
// server to only directly allow authenticated users, you could
|
||||||
// add the userRoles.AUTHENTICATED to the user in the userMapping
|
// add the userRoles.AUTHENTICATED to the user in the userMapping
|
||||||
// function, and change to BYPASS_LOBBY : [ userRoles.AUTHENTICATED ]
|
// function, and change to BYPASS_LOBBY : [ userRoles.AUTHENTICATED ]
|
||||||
BYPASS_LOBBY : [ userRoles.NORMAL ]
|
[BYPASS_LOBBY] : [ userRoles.NORMAL ]
|
||||||
},
|
},
|
||||||
permissionsFromRoles : {
|
permissionsFromRoles : {
|
||||||
// The role(s) have permission to lock/unlock a room
|
// The role(s) have permission to lock/unlock a room
|
||||||
CHANGE_ROOM_LOCK : [ userRoles.NORMAL ],
|
[CHANGE_ROOM_LOCK] : [ userRoles.MODERATOR ],
|
||||||
// The role(s) have permission to promote a peer from the lobby
|
// The role(s) have permission to promote a peer from the lobby
|
||||||
PROMOTE_PEER : [ userRoles.NORMAL ],
|
[PROMOTE_PEER] : [ userRoles.NORMAL ],
|
||||||
// The role(s) have permission to send chat messages
|
// The role(s) have permission to send chat messages
|
||||||
SEND_CHAT : [ userRoles.NORMAL ],
|
[SEND_CHAT] : [ userRoles.NORMAL ],
|
||||||
// The role(s) have permission to moderate chat
|
// The role(s) have permission to moderate chat
|
||||||
MODERATE_CHAT : [ userRoles.MODERATOR ],
|
[MODERATE_CHAT] : [ userRoles.MODERATOR ],
|
||||||
// The role(s) have permission to share screen
|
// The role(s) have permission to share screen
|
||||||
SHARE_SCREEN : [ userRoles.NORMAL ],
|
[SHARE_SCREEN] : [ userRoles.NORMAL ],
|
||||||
// The role(s) have permission to produce extra video
|
// The role(s) have permission to produce extra video
|
||||||
EXTRA_VIDEO : [ userRoles.NORMAL ],
|
[EXTRA_VIDEO] : [ userRoles.NORMAL ],
|
||||||
// The role(s) have permission to share files
|
// The role(s) have permission to share files
|
||||||
SHARE_FILE : [ userRoles.NORMAL ],
|
[SHARE_FILE] : [ userRoles.NORMAL ],
|
||||||
// The role(s) have permission to moderate files
|
// The role(s) have permission to moderate files
|
||||||
MODERATE_FILES : [ userRoles.MODERATOR ],
|
[MODERATE_FILES] : [ userRoles.MODERATOR ],
|
||||||
// The role(s) have permission to moderate room (e.g. kick user)
|
// The role(s) have permission to moderate room (e.g. kick user)
|
||||||
MODERATE_ROOM : [ userRoles.MODERATOR ]
|
[MODERATE_ROOM] : [ userRoles.MODERATOR ]
|
||||||
},
|
},
|
||||||
|
// Array of permissions. If no peer with the permission in question
|
||||||
|
// is in the room, all peers are permitted to do the action. The peers
|
||||||
|
// that are allowed because of this rule will not be able to do this
|
||||||
|
// action as soon as a peer with the permission joins. In this example
|
||||||
|
// everyone will be able to lock/unlock room until a MODERATOR joins.
|
||||||
|
allowWhenRoleMissing : [ CHANGE_ROOM_LOCK ],
|
||||||
// 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,
|
||||||
// When set, maxUsersPerRoom defines how many users can join
|
// When set, maxUsersPerRoom defines how many users can join
|
||||||
// a single room. If not set, there is no limit.
|
// a single room. If not set, there is no limit.
|
||||||
// maxUsersPerRoom : 20,
|
// maxUsersPerRoom : 20,
|
||||||
// Room size before spreading to new router
|
// Room size before spreading to new router
|
||||||
routerScaleSize : 40,
|
routerScaleSize : 40,
|
||||||
// Mediasoup settings
|
// Mediasoup settings
|
||||||
mediasoup :
|
mediasoup :
|
||||||
{
|
{
|
||||||
numWorkers : Object.keys(os.cpus()).length,
|
numWorkers : Object.keys(os.cpus()).length,
|
||||||
// mediasoup Worker settings.
|
// mediasoup Worker settings.
|
||||||
|
|
|
||||||
|
|
@ -5,29 +5,47 @@ const Lobby = require('./Lobby');
|
||||||
const { v4: uuidv4 } = require('uuid');
|
const { v4: uuidv4 } = require('uuid');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const userRoles = require('../userRoles');
|
const userRoles = require('../userRoles');
|
||||||
|
|
||||||
|
const {
|
||||||
|
BYPASS_ROOM_LOCK,
|
||||||
|
BYPASS_LOBBY
|
||||||
|
} = require('../access');
|
||||||
|
|
||||||
|
const permissions = require('../permissions'), {
|
||||||
|
CHANGE_ROOM_LOCK,
|
||||||
|
PROMOTE_PEER,
|
||||||
|
SEND_CHAT,
|
||||||
|
MODERATE_CHAT,
|
||||||
|
SHARE_SCREEN,
|
||||||
|
EXTRA_VIDEO,
|
||||||
|
SHARE_FILE,
|
||||||
|
MODERATE_FILES,
|
||||||
|
MODERATE_ROOM
|
||||||
|
} = permissions;
|
||||||
|
|
||||||
const config = require('../config/config');
|
const config = require('../config/config');
|
||||||
|
|
||||||
const logger = new Logger('Room');
|
const logger = new Logger('Room');
|
||||||
|
|
||||||
// In case they are not configured properly
|
// In case they are not configured properly
|
||||||
const accessFromRoles =
|
const roomAccess =
|
||||||
{
|
{
|
||||||
BYPASS_ROOM_LOCK : [ userRoles.ADMIN ],
|
[BYPASS_ROOM_LOCK] : [ userRoles.ADMIN ],
|
||||||
BYPASS_LOBBY : [ userRoles.NORMAL ],
|
[BYPASS_LOBBY] : [ userRoles.NORMAL ],
|
||||||
...config.accessFromRoles
|
...config.accessFromRoles
|
||||||
};
|
};
|
||||||
|
|
||||||
const permissionsFromRoles =
|
const roomPermissions =
|
||||||
{
|
{
|
||||||
CHANGE_ROOM_LOCK : [ userRoles.NORMAL ],
|
[CHANGE_ROOM_LOCK] : [ userRoles.NORMAL ],
|
||||||
PROMOTE_PEER : [ userRoles.NORMAL ],
|
[PROMOTE_PEER] : [ userRoles.NORMAL ],
|
||||||
SEND_CHAT : [ userRoles.NORMAL ],
|
[SEND_CHAT] : [ userRoles.NORMAL ],
|
||||||
MODERATE_CHAT : [ userRoles.MODERATOR ],
|
[MODERATE_CHAT] : [ userRoles.MODERATOR ],
|
||||||
SHARE_SCREEN : [ userRoles.NORMAL ],
|
[SHARE_SCREEN] : [ userRoles.NORMAL ],
|
||||||
EXTRA_VIDEO : [ userRoles.NORMAL ],
|
[EXTRA_VIDEO] : [ userRoles.NORMAL ],
|
||||||
SHARE_FILE : [ userRoles.NORMAL ],
|
[SHARE_FILE] : [ userRoles.NORMAL ],
|
||||||
MODERATE_FILES : [ userRoles.MODERATOR ],
|
[MODERATE_FILES] : [ userRoles.MODERATOR ],
|
||||||
MODERATE_ROOM : [ userRoles.MODERATOR ],
|
[MODERATE_ROOM] : [ userRoles.MODERATOR ],
|
||||||
...config.permissionsFromRoles
|
...config.permissionsFromRoles
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -221,9 +239,8 @@ class Room extends EventEmitter
|
||||||
// Returning user
|
// Returning user
|
||||||
if (returning)
|
if (returning)
|
||||||
this._peerJoining(peer, true);
|
this._peerJoining(peer, true);
|
||||||
else if ( // Has a role that is allowed to bypass room lock
|
// Has a role that is allowed to bypass room lock
|
||||||
peer.roles.some((role) => accessFromRoles.BYPASS_ROOM_LOCK.includes(role))
|
else if (this._hasAccess(peer, BYPASS_ROOM_LOCK))
|
||||||
)
|
|
||||||
this._peerJoining(peer);
|
this._peerJoining(peer);
|
||||||
else if (
|
else if (
|
||||||
'maxUsersPerRoom' in config &&
|
'maxUsersPerRoom' in config &&
|
||||||
|
|
@ -239,7 +256,7 @@ class Room extends EventEmitter
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Has a role that is allowed to bypass lobby
|
// Has a role that is allowed to bypass lobby
|
||||||
peer.roles.some((role) => accessFromRoles.BYPASS_LOBBY.includes(role)) ?
|
this._hasAccess(peer, BYPASS_LOBBY) ?
|
||||||
this._peerJoining(peer) :
|
this._peerJoining(peer) :
|
||||||
this._handleGuest(peer);
|
this._handleGuest(peer);
|
||||||
}
|
}
|
||||||
|
|
@ -271,11 +288,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
this._peerJoining(promotedPeer);
|
this._peerJoining(promotedPeer);
|
||||||
|
|
||||||
for (
|
for (const peer of this._getPeersWithPermission(PROMOTE_PEER))
|
||||||
const peer of this._getPeersWithPermission({
|
|
||||||
permission : permissionsFromRoles.PROMOTE_PEER
|
|
||||||
})
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
this._notification(peer.socket, 'lobby:promotedPeer', { peerId: id });
|
this._notification(peer.socket, 'lobby:promotedPeer', { peerId: id });
|
||||||
}
|
}
|
||||||
|
|
@ -283,9 +296,8 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
this._lobby.on('peerRolesChanged', (peer) =>
|
this._lobby.on('peerRolesChanged', (peer) =>
|
||||||
{
|
{
|
||||||
if ( // Has a role that is allowed to bypass room lock
|
// Has a role that is allowed to bypass room lock
|
||||||
peer.roles.some((role) => accessFromRoles.BYPASS_ROOM_LOCK.includes(role))
|
if (this._hasAccess(peer, BYPASS_ROOM_LOCK))
|
||||||
)
|
|
||||||
{
|
{
|
||||||
this._lobby.promotePeer(peer.id);
|
this._lobby.promotePeer(peer.id);
|
||||||
|
|
||||||
|
|
@ -294,7 +306,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
if ( // Has a role that is allowed to bypass lobby
|
if ( // Has a role that is allowed to bypass lobby
|
||||||
!this._locked &&
|
!this._locked &&
|
||||||
peer.roles.some((role) => accessFromRoles.BYPASS_LOBBY.includes(role))
|
this._hasAccess(peer, BYPASS_LOBBY)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this._lobby.promotePeer(peer.id);
|
this._lobby.promotePeer(peer.id);
|
||||||
|
|
@ -307,11 +319,7 @@ class Room extends EventEmitter
|
||||||
{
|
{
|
||||||
const { id, displayName } = changedPeer;
|
const { id, displayName } = changedPeer;
|
||||||
|
|
||||||
for (
|
for (const peer of this._getPeersWithPermission(PROMOTE_PEER))
|
||||||
const peer of this._getPeersWithPermission({
|
|
||||||
permission : permissionsFromRoles.PROMOTE_PEER
|
|
||||||
})
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
this._notification(peer.socket, 'lobby:changeDisplayName', { peerId: id, displayName });
|
this._notification(peer.socket, 'lobby:changeDisplayName', { peerId: id, displayName });
|
||||||
}
|
}
|
||||||
|
|
@ -321,11 +329,7 @@ class Room extends EventEmitter
|
||||||
{
|
{
|
||||||
const { id, picture } = changedPeer;
|
const { id, picture } = changedPeer;
|
||||||
|
|
||||||
for (
|
for (const peer of this._getPeersWithPermission(PROMOTE_PEER))
|
||||||
const peer of this._getPeersWithPermission({
|
|
||||||
permission : permissionsFromRoles.PROMOTE_PEER
|
|
||||||
})
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
this._notification(peer.socket, 'lobby:changePicture', { peerId: id, picture });
|
this._notification(peer.socket, 'lobby:changePicture', { peerId: id, picture });
|
||||||
}
|
}
|
||||||
|
|
@ -337,11 +341,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
const { id } = closedPeer;
|
const { id } = closedPeer;
|
||||||
|
|
||||||
for (
|
for (const peer of this._getPeersWithPermission(PROMOTE_PEER))
|
||||||
const peer of this._getPeersWithPermission({
|
|
||||||
permission : permissionsFromRoles.PROMOTE_PEER
|
|
||||||
})
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
this._notification(peer.socket, 'lobby:peerClosed', { peerId: id });
|
this._notification(peer.socket, 'lobby:peerClosed', { peerId: id });
|
||||||
}
|
}
|
||||||
|
|
@ -401,7 +401,7 @@ class Room extends EventEmitter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async dump()
|
dump()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
roomId : this._roomId,
|
roomId : this._roomId,
|
||||||
|
|
@ -447,11 +447,7 @@ class Room extends EventEmitter
|
||||||
{
|
{
|
||||||
this._lobby.parkPeer(parkPeer);
|
this._lobby.parkPeer(parkPeer);
|
||||||
|
|
||||||
for (
|
for (const peer of this._getPeersWithPermission(PROMOTE_PEER))
|
||||||
const peer of this._getPeersWithPermission({
|
|
||||||
permission : permissionsFromRoles.PROMOTE_PEER
|
|
||||||
})
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
this._notification(peer.socket, 'parkedPeer', { peerId: parkPeer.id });
|
this._notification(peer.socket, 'parkedPeer', { peerId: parkPeer.id });
|
||||||
}
|
}
|
||||||
|
|
@ -602,7 +598,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
// Got permission to promote peers, notify peer of
|
// Got permission to promote peers, notify peer of
|
||||||
// peers in lobby
|
// peers in lobby
|
||||||
if (permissionsFromRoles.PROMOTE_PEER.includes(newRole))
|
if (roomPermissions.PROMOTE_PEER.includes(newRole))
|
||||||
{
|
{
|
||||||
const lobbyPeers = this._lobby.peerList();
|
const lobbyPeers = this._lobby.peerList();
|
||||||
|
|
||||||
|
|
@ -671,11 +667,8 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
let lobbyPeers = [];
|
let lobbyPeers = [];
|
||||||
|
|
||||||
if ( // Allowed to promote peers, notify about lobbypeers
|
// Allowed to promote peers, notify about lobbypeers
|
||||||
peer.roles.some((role) =>
|
if (this._hasPermission(peer, PROMOTE_PEER))
|
||||||
permissionsFromRoles.PROMOTE_PEER.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
lobbyPeers = this._lobby.peerList();
|
lobbyPeers = this._lobby.peerList();
|
||||||
|
|
||||||
cb(null, {
|
cb(null, {
|
||||||
|
|
@ -683,8 +676,9 @@ class Room extends EventEmitter
|
||||||
peers : peerInfos,
|
peers : peerInfos,
|
||||||
tracker : config.fileTracker,
|
tracker : config.fileTracker,
|
||||||
authenticated : peer.authenticated,
|
authenticated : peer.authenticated,
|
||||||
permissionsFromRoles : permissionsFromRoles,
|
roomPermissions : roomPermissions,
|
||||||
userRoles : userRoles,
|
userRoles : userRoles,
|
||||||
|
allowWhenRoleMissing : config.allowWhenRoleMissing,
|
||||||
chatHistory : this._chatHistory,
|
chatHistory : this._chatHistory,
|
||||||
fileHistory : this._fileHistory,
|
fileHistory : this._fileHistory,
|
||||||
lastNHistory : this._lastN,
|
lastNHistory : this._lastN,
|
||||||
|
|
@ -711,7 +705,7 @@ class Room extends EventEmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the new Peer to all other Peers.
|
// Notify the new Peer to all other Peers.
|
||||||
for (const otherPeer of this._getJoinedPeers({ excludePeer: peer }))
|
for (const otherPeer of this._getJoinedPeers(peer))
|
||||||
{
|
{
|
||||||
this._notification(
|
this._notification(
|
||||||
otherPeer.socket,
|
otherPeer.socket,
|
||||||
|
|
@ -821,15 +815,13 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
if (
|
if (
|
||||||
appData.source === 'screen' &&
|
appData.source === 'screen' &&
|
||||||
!peer.roles.some(
|
!this._hasPermission(peer, SHARE_SCREEN)
|
||||||
(role) => permissionsFromRoles.SHARE_SCREEN.includes(role))
|
|
||||||
)
|
)
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
if (
|
if (
|
||||||
appData.source === 'extravideo' &&
|
appData.source === 'extravideo' &&
|
||||||
!peer.roles.some(
|
!this._hasPermission(peer, EXTRA_VIDEO)
|
||||||
(role) => permissionsFromRoles.EXTRA_VIDEO.includes(role))
|
|
||||||
)
|
)
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
|
|
@ -882,7 +874,7 @@ class Room extends EventEmitter
|
||||||
cb(null, { id: producer.id });
|
cb(null, { id: producer.id });
|
||||||
|
|
||||||
// Optimization: Create a server-side Consumer for each Peer.
|
// Optimization: Create a server-side Consumer for each Peer.
|
||||||
for (const otherPeer of this._getJoinedPeers({ excludePeer: peer }))
|
for (const otherPeer of this._getJoinedPeers(peer))
|
||||||
{
|
{
|
||||||
this._createConsumer(
|
this._createConsumer(
|
||||||
{
|
{
|
||||||
|
|
@ -1144,9 +1136,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'chatMessage':
|
case 'chatMessage':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, SEND_CHAT))
|
||||||
!peer.roles.some((role) => permissionsFromRoles.SEND_CHAT.includes(role))
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
const { chatMessage } = request.data;
|
const { chatMessage } = request.data;
|
||||||
|
|
@ -1167,11 +1157,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'moderator:clearChat':
|
case 'moderator:clearChat':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, MODERATE_CHAT))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.MODERATE_CHAT.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
this._chatHistory = [];
|
this._chatHistory = [];
|
||||||
|
|
@ -1187,11 +1173,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'lockRoom':
|
case 'lockRoom':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, CHANGE_ROOM_LOCK))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
this._locked = true;
|
this._locked = true;
|
||||||
|
|
@ -1209,11 +1191,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'unlockRoom':
|
case 'unlockRoom':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, CHANGE_ROOM_LOCK))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
this._locked = false;
|
this._locked = false;
|
||||||
|
|
@ -1271,11 +1249,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'promotePeer':
|
case 'promotePeer':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, PROMOTE_PEER))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.PROMOTE_PEER.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
const { peerId } = request.data;
|
const { peerId } = request.data;
|
||||||
|
|
@ -1290,11 +1264,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'promoteAllPeers':
|
case 'promoteAllPeers':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, PROMOTE_PEER))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.PROMOTE_PEER.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
this._lobby.promoteAllPeers();
|
this._lobby.promoteAllPeers();
|
||||||
|
|
@ -1307,11 +1277,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'sendFile':
|
case 'sendFile':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, SHARE_FILE))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.SHARE_FILE.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
const { magnetUri } = request.data;
|
const { magnetUri } = request.data;
|
||||||
|
|
@ -1332,11 +1298,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'moderator:clearFileSharing':
|
case 'moderator:clearFileSharing':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, MODERATE_FILES))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.MODERATE_FILES.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
this._fileHistory = [];
|
this._fileHistory = [];
|
||||||
|
|
@ -1371,11 +1333,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'moderator:mute':
|
case 'moderator:mute':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, MODERATE_ROOM))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
const { peerId } = request.data;
|
const { peerId } = request.data;
|
||||||
|
|
@ -1394,11 +1352,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'moderator:muteAll':
|
case 'moderator:muteAll':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, MODERATE_ROOM))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
// Spread to others
|
// Spread to others
|
||||||
|
|
@ -1411,11 +1365,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'moderator:stopVideo':
|
case 'moderator:stopVideo':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, MODERATE_ROOM))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
const { peerId } = request.data;
|
const { peerId } = request.data;
|
||||||
|
|
@ -1434,11 +1384,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'moderator:stopAllVideo':
|
case 'moderator:stopAllVideo':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, MODERATE_ROOM))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
// Spread to others
|
// Spread to others
|
||||||
|
|
@ -1451,11 +1397,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'moderator:closeMeeting':
|
case 'moderator:closeMeeting':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, MODERATE_ROOM))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
this._notification(peer.socket, 'moderator:kick', null, true);
|
this._notification(peer.socket, 'moderator:kick', null, true);
|
||||||
|
|
@ -1470,11 +1412,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'moderator:kickPeer':
|
case 'moderator:kickPeer':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, MODERATE_ROOM))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
const { peerId } = request.data;
|
const { peerId } = request.data;
|
||||||
|
|
@ -1495,11 +1433,7 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'moderator:lowerHand':
|
case 'moderator:lowerHand':
|
||||||
{
|
{
|
||||||
if (
|
if (!this._hasPermission(peer, MODERATE_ROOM))
|
||||||
!peer.roles.some(
|
|
||||||
(role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new Error('peer not authorized');
|
throw new Error('peer not authorized');
|
||||||
|
|
||||||
const { peerId } = request.data;
|
const { peerId } = request.data;
|
||||||
|
|
@ -1677,16 +1611,41 @@ class Room extends EventEmitter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_hasPermission(peer, permission)
|
||||||
|
{
|
||||||
|
const hasPermission = peer.roles.some((role) =>
|
||||||
|
roomPermissions[permission].includes(role)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasPermission)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Allow if config is set, and no one is present
|
||||||
|
if (
|
||||||
|
'allowWhenRoleMissing' in config &&
|
||||||
|
config.allowWhenRoleMissing.includes(permission) &&
|
||||||
|
this._getPeersWithPermission(permission).length === 0
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hasAccess(peer, access)
|
||||||
|
{
|
||||||
|
return peer.roles.some((role) => roomAccess[access].includes(role));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to get the list of joined peers.
|
* Helper to get the list of joined peers.
|
||||||
*/
|
*/
|
||||||
_getJoinedPeers({ excludePeer = undefined } = {})
|
_getJoinedPeers(excludePeer = undefined)
|
||||||
{
|
{
|
||||||
return Object.values(this._peers)
|
return Object.values(this._peers)
|
||||||
.filter((peer) => peer.joined && peer !== excludePeer);
|
.filter((peer) => peer.joined && peer !== excludePeer);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getPeersWithPermission({ permission = null, excludePeer = undefined, joined = true })
|
_getPeersWithPermission(permission = null, excludePeer = undefined, joined = true)
|
||||||
{
|
{
|
||||||
return Object.values(this._peers)
|
return Object.values(this._peers)
|
||||||
.filter(
|
.filter(
|
||||||
|
|
@ -1694,7 +1653,7 @@ class Room extends EventEmitter
|
||||||
peer.joined === joined &&
|
peer.joined === joined &&
|
||||||
peer !== excludePeer &&
|
peer !== excludePeer &&
|
||||||
peer.roles.some(
|
peer.roles.some(
|
||||||
(role) => permission.includes(role)
|
(role) => roomPermissions[permission].includes(role)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
module.exports = {
|
||||||
|
// The role(s) have permission to lock/unlock a room
|
||||||
|
CHANGE_ROOM_LOCK : 'CHANGE_ROOM_LOCK',
|
||||||
|
// The role(s) have permission to promote a peer from the lobby
|
||||||
|
PROMOTE_PEER : 'PROMOTE_PEER',
|
||||||
|
// The role(s) have permission to send chat messages
|
||||||
|
SEND_CHAT : 'SEND_CHAT',
|
||||||
|
// The role(s) have permission to moderate chat
|
||||||
|
MODERATE_CHAT : 'MODERATE_CHAT',
|
||||||
|
// The role(s) have permission to share screen
|
||||||
|
SHARE_SCREEN : 'SHARE_SCREEN',
|
||||||
|
// The role(s) have permission to produce extra video
|
||||||
|
EXTRA_VIDEO : 'EXTRA_VIDEO',
|
||||||
|
// The role(s) have permission to share files
|
||||||
|
SHARE_FILE : 'SHARE_FILE',
|
||||||
|
// The role(s) have permission to moderate files
|
||||||
|
MODERATE_FILES : 'MODERATE_FILES',
|
||||||
|
// The role(s) have permission to moderate room (e.g. kick user)
|
||||||
|
MODERATE_ROOM : 'MODERATE_ROOM'
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue