Merge branch 'feat-roles-authorization' into develop
commit
b5754bbf96
|
|
@ -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,13 @@ export default class RoomClient
|
||||||
canShareFiles : this._torrentSupport
|
canShareFiles : this._torrentSupport
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { authenticated, roles, peers } = await this.sendRequest(
|
const {
|
||||||
|
authenticated,
|
||||||
|
roles,
|
||||||
|
peers,
|
||||||
|
permissionsFromRoles,
|
||||||
|
userRoles
|
||||||
|
} = await this.sendRequest(
|
||||||
'join',
|
'join',
|
||||||
{
|
{
|
||||||
displayName : displayName,
|
displayName : displayName,
|
||||||
|
|
@ -2541,6 +2545,9 @@ export default class RoomClient
|
||||||
|
|
||||||
store.dispatch(meActions.loggedIn(authenticated));
|
store.dispatch(meActions.loggedIn(authenticated));
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
|
||||||
|
|
@ -127,4 +127,16 @@ 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 }
|
||||||
});
|
});
|
||||||
|
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
@ -544,10 +550,13 @@ Me.propTypes =
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
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 &&
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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)));
|
||||||
|
|
|
||||||
|
|
@ -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) =>
|
||||||
({
|
({
|
||||||
|
|
@ -174,8 +173,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 : spotlightSortedPeersSelector(state)
|
spotlightPeers : spotlightSortedPeersSelector(state)
|
||||||
|
|
@ -190,6 +190,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 &&
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
export const ADMIN = 'admin';
|
|
||||||
export const MODERATOR = 'moderator';
|
|
||||||
export const AUTHENTICATED = 'authenticated';
|
|
||||||
export const ALL = 'normal';
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class Peer extends EventEmitter
|
||||||
|
|
||||||
this._authenticated = false;
|
this._authenticated = false;
|
||||||
|
|
||||||
this._roles = [ userRoles.ALL ];
|
this._roles = [ userRoles.NORMAL ];
|
||||||
|
|
||||||
this._displayName = false;
|
this._displayName = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -554,9 +549,11 @@ class Room extends EventEmitter
|
||||||
.map((joinedPeer) => (joinedPeer.peerInfo));
|
.map((joinedPeer) => (joinedPeer.peerInfo));
|
||||||
|
|
||||||
cb(null, {
|
cb(null, {
|
||||||
roles : peer.roles,
|
roles : peer.roles,
|
||||||
peers : peerInfos,
|
peers : peerInfos,
|
||||||
authenticated : peer.authenticated
|
authenticated : peer.authenticated,
|
||||||
|
permissionsFromRoles : config.permissionsFromRoles,
|
||||||
|
userRoles : userRoles
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mark the new Peer as joined.
|
// Mark the new Peer as joined.
|
||||||
|
|
@ -683,12 +680,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)
|
||||||
|
|
@ -982,6 +986,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);
|
||||||
|
|
@ -1020,6 +1029,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
|
||||||
|
|
@ -1035,6 +1049,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
|
||||||
|
|
@ -1090,6 +1109,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);
|
||||||
|
|
@ -1102,6 +1126,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
|
||||||
|
|
@ -1112,6 +1141,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 });
|
||||||
|
|
@ -1149,10 +1183,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', {
|
||||||
|
|
@ -1167,10 +1200,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', {
|
||||||
|
|
@ -1185,10 +1217,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,
|
||||||
|
|
@ -1208,10 +1239,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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue