feat(jwt) deprecate and remove enableFeaturesBasedOnToken

The new behavior is as follows:

IF a user has a token and `features` is not set, we treat it as if the
feature was enabled.

IF a user has a token and `features` is set, we check if the feature
name has a value of "true".

`isJwtFeatureEnabled` also provides a way to specify the default value
in case there is no token.
This commit is contained in:
Saúl Ibarra Corretgé 2022-08-16 14:53:53 +02:00 committed by Saúl Ibarra Corretgé
parent bf222c5094
commit 77d687952d
12 changed files with 50 additions and 193 deletions

View File

@ -618,9 +618,6 @@ var config = {
// Hides the email section under profile settings.
// hideEmailInSettings: false,
// Whether or not some features are checked based on token.
// enableFeaturesBasedOnToken: false,
// When enabled the password used for locking a room is restricted to up to the number of digits specified
// default: roomPasswordNumberOfDigits: false,
// roomPasswordNumberOfDigits: 10,

View File

@ -11,7 +11,6 @@
"defaultEmail": "Your Default Email",
"disabled": "You can't invite people.",
"failedToAdd": "Failed to add participants",
"footerText": "Dialing out is disabled.",
"googleEmail": "Google Email",
"inviteMoreHeader": "You are the only one in the meeting",
"inviteMoreMailSubject": "Join {{appName}} meeting",
@ -286,7 +285,6 @@
"linkMeetingTitle": "Link meeting to Salesforce",
"liveStreaming": "Live Streaming",
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Not possible while recording is active",
"liveStreamingDisabledTooltip": "Start live stream disabled.",
"localUserControls": "Local user controls",
"lockMessage": "Failed to lock the conference.",
"lockRoom": "Add meeting $t(lockRoomPassword)",
@ -341,7 +339,6 @@
"recentlyUsedObjects": "Your recently used objects",
"recording": "Recording",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Not possible while a live stream is active",
"recordingDisabledTooltip": "Start recording disabled.",
"rejoinNow": "Rejoin now",
"remoteControlAllowedMessage": "{{user}} accepted your remote control request!",
"remoteControlDeniedMessage": "{{user}} rejected your remote control request!",

View File

@ -260,7 +260,6 @@ export interface IConfig {
enableDisplayNameInStats?: boolean;
enableEmailInStats?: boolean;
enableEncodedTransformSupport?: boolean;
enableFeaturesBasedOnToken?: boolean;
enableForcedReload?: boolean;
enableIceRestart?: boolean;
enableInsecureRoomNameWarning?: boolean;

View File

@ -2,6 +2,7 @@
import jwtDecode from 'jwt-decode';
import { getLocalParticipant } from '../participants/functions';
import { parseURLParams } from '../util';
import { MEET_FEATURES } from './constants';
@ -31,6 +32,31 @@ export function getJwtName(state: Object) {
return user?.name;
}
/**
* Check if the given JWT feature is enabled.
*
* @param {Object} state - The app state.
* @param {string} feature - The feature we want to check.
* @param {boolean} ifNoToken - Default value if there is no token.
* @returns {string}
*/
export function isJwtFeatureEnabled(state: Object, feature: string, ifNoToken: boolean = false) {
const { jwt } = state['features/base/jwt'];
if (!jwt) {
return ifNoToken;
}
const { features } = getLocalParticipant(state) || {};
// If `features` is undefined, act as if everything is enabled.
if (typeof features === 'undefined') {
return true;
}
return String(features[feature]) === 'true';
}
/**
* Checks whether a given timestamp is a valid UNIX timestamp in seconds.
* We convert to miliseconds during the check since `Date` works with miliseconds for UNIX timestamp values.

View File

@ -402,17 +402,6 @@ export function getParticipantPresenceStatus(
return participantById.presence;
}
/**
* Returns true if there is at least 1 participant with screen sharing feature and false otherwise.
*
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state.
* @returns {boolean}
*/
export function haveParticipantWithScreenSharingFeature(stateful: IStore | Function) {
return toState(stateful)['features/base/participants'].haveParticipantWithScreenSharingFeature;
}
/**
* Selectors for getting all remote participants.
*

View File

@ -101,7 +101,6 @@ const DEFAULT_STATE = {
dominantSpeaker: undefined,
everyoneIsModerator: false,
fakeParticipants: new Map(),
haveParticipantWithScreenSharingFeature: false,
local: undefined,
localScreenShare: undefined,
overwrittenNameList: {},
@ -118,7 +117,6 @@ export interface IParticipantsState {
dominantSpeaker?: string;
everyoneIsModerator: boolean;
fakeParticipants: Map<string, Participant>;
haveParticipantWithScreenSharingFeature: boolean;
local?: LocalParticipant;
localScreenShare?: Participant;
overwrittenNameList: Object;
@ -255,14 +253,6 @@ ReducerRegistry.register('features/base/participants', (state: IParticipantsStat
} else if (!state.everyoneIsModerator && isModerator) {
state.everyoneIsModerator = _isEveryoneModerator(state);
}
// haveParticipantWithScreenSharingFeature calculation:
const { features = {} } = participant;
// Currently we use only PARTICIPANT_UPDATED to set a feature to enabled and we never disable it.
if (String(features['screen-sharing']) === 'true') {
state.haveParticipantWithScreenSharingFeature = true;
}
}
return {
@ -401,26 +391,6 @@ ReducerRegistry.register('features/base/participants', (state: IParticipantsStat
state.everyoneIsModerator = _isEveryoneModerator(state);
}
const { features = {} } = oldParticipant || {};
if (state.haveParticipantWithScreenSharingFeature && String(features['screen-sharing']) === 'true') {
const { features: localFeatures = {} } = state.local || {};
if (String(localFeatures['screen-sharing']) !== 'true') {
state.haveParticipantWithScreenSharingFeature = false;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const [ key, participant ] of state.remote) {
const { features: f = {} } = participant;
if (String(f['screen-sharing']) === 'true') {
state.haveParticipantWithScreenSharingFeature = true;
break;
}
}
}
}
if (dominantSpeaker === id) {
state.dominantSpeaker = undefined;
}

View File

@ -5,9 +5,8 @@ import React from 'react';
import type { Dispatch } from 'redux';
import { Avatar } from '../../../../base/avatar';
import { translate, translateToHTML } from '../../../../base/i18n';
import { translate } from '../../../../base/i18n';
import { Icon, IconPhone } from '../../../../base/icons';
import { getLocalParticipant } from '../../../../base/participants';
import { MultiSelectAutocomplete } from '../../../../base/react';
import { connect } from '../../../../base/redux';
import { isVpaasMeeting } from '../../../../jaas/functions';
@ -28,11 +27,6 @@ type Props = AbstractProps & {
*/
_conference: Object,
/**
* Whether to show a footer text after the search results as a last element.
*/
_footerTextEnabled: boolean,
/**
* Whether the meeting belongs to JaaS user.
*/
@ -83,7 +77,6 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
this._onSubmitKeyPress = this._onSubmitKeyPress.bind(this);
this._parseQueryResults = this._parseQueryResults.bind(this);
this._setMultiSelectElement = this._setMultiSelectElement.bind(this);
this._renderFooterText = this._renderFooterText.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._resourceClient = {
@ -135,7 +128,6 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
_sipInviteEnabled,
t
} = this.props;
const footerText = this._renderFooterText();
let isMultiSelectDisabled = this.state.addToCallInProgress;
const loadingMessage = 'addPeople.searching';
const noMatches = 'addPeople.noResults';
@ -163,7 +155,6 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
onKeyDown = { this._onKeyDown }>
{ this._renderErrorMessage() }
<MultiSelectAutocomplete
footer = { footerText }
isDisabled = { isMultiSelectDisabled }
loadingMessage = { t(loadingMessage) }
noMatchesFound = { t(noMatches) }
@ -404,33 +395,6 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
_query: (string) => Promise<Array<Object>>;
_renderFooterText: () => Object;
/**
* Sets up the rendering of the footer text, if enabled.
*
* @returns {Object | undefined}
*/
_renderFooterText() {
const { _footerTextEnabled, t } = this.props;
let footerText;
if (_footerTextEnabled) {
footerText = {
content: <div className = 'footer-text-wrap'>
<div>
<span className = 'footer-telephone-icon'>
<Icon src = { IconPhone } />
</span>
</div>
{ translateToHTML(t, 'addPeople.footerText') }
</div>
};
}
return footerText;
}
_onClearItems: () => void;
/**
@ -580,20 +544,8 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
* @returns {Props}
*/
function _mapStateToProps(state) {
const { enableFeaturesBasedOnToken } = state['features/base/config'];
let footerTextEnabled = false;
if (enableFeaturesBasedOnToken) {
const { features = {} } = getLocalParticipant(state);
if (String(features['outbound-call']) !== 'true') {
footerTextEnabled = true;
}
}
return {
..._abstractMapStateToProps(state),
_footerTextEnabled: footerTextEnabled,
_isVpaas: isVpaasMeeting(state)
};
}

View File

@ -5,6 +5,7 @@ import { getRoomName } from '../base/conference';
import { getInviteURL } from '../base/connection';
import { isIosMobileBrowser } from '../base/environment/utils';
import { i18next } from '../base/i18n';
import { isJwtFeatureEnabled } from '../base/jwt/functions';
import { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
import { getLocalParticipant, isLocalParticipantModerator } from '../base/participants';
import { toState } from '../base/redux';
@ -410,11 +411,8 @@ export function isDialOutEnabled(state: Object): boolean {
*/
export function isSipInviteEnabled(state: Object): boolean {
const { sipInviteUrl } = state['features/base/config'];
const { features = {} } = getLocalParticipant(state) || {};
return state['features/base/jwt'].jwt
&& Boolean(sipInviteUrl)
&& String(features['sip-outbound-call']) === 'true';
return isJwtFeatureEnabled(state, 'sip-outbound-call') && Boolean(sipInviteUrl);
}
/**

View File

@ -1,11 +1,9 @@
// @flow
import { IconLiveStreaming } from '../../../base/icons';
import { isJwtFeatureEnabled } from '../../../base/jwt/functions';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import {
getLocalParticipant,
isLocalParticipantModerator
} from '../../../base/participants';
import { isLocalParticipantModerator } from '../../../base/participants';
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
import { isInBreakoutRoom } from '../../../breakout-rooms/functions';
import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
@ -134,36 +132,22 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
// A button can be disabled/enabled only if enableFeaturesBasedOnToken
// is on or if the recording is running.
let _disabled;
let _disabled = false;
let _tooltip = '';
if (typeof visible === 'undefined') {
// If the containing component provides the visible prop, that is one
// above all, but if not, the button should be autonomus and decide on
// above all, but if not, the button should be autonomous and decide on
// its own to be visible or not.
const isModerator = isLocalParticipantModerator(state);
const {
enableFeaturesBasedOnToken
} = state['features/base/config'];
const liveStreaming = getLiveStreaming(state);
const { features = {} } = getLocalParticipant(state);
visible = isModerator && liveStreaming.enabled;
if (enableFeaturesBasedOnToken) {
visible = visible && String(features.livestreaming) === 'true';
_disabled = String(features.livestreaming) === 'disabled';
if (!visible && !_disabled) {
_disabled = true;
visible = true;
_tooltip = 'dialog.liveStreamingDisabledTooltip';
}
}
visible = isJwtFeatureEnabled(state, 'livestreaming', visible);
}
// disable the button if the recording is running.
if (getActiveSession(state, JitsiRecordingConstants.mode.FILE)) {
if (visible && getActiveSession(state, JitsiRecordingConstants.mode.FILE)) {
_disabled = true;
_tooltip = 'dialog.liveStreamingDisabledBecauseOfActiveRecordingTooltip';
}
@ -176,8 +160,7 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
return {
_disabled,
_isLiveStreamRunning: Boolean(
getActiveSession(state, JitsiRecordingConstants.mode.STREAM)),
_isLiveStreamRunning: Boolean(getActiveSession(state, JitsiRecordingConstants.mode.STREAM)),
_tooltip,
visible
};

View File

@ -1,6 +1,7 @@
// @flow
import { isMobileBrowser } from '../base/environment/utils';
import { isJwtFeatureEnabled } from '../base/jwt/functions';
import { JitsiRecordingConstants, browser } from '../base/lib-jitsi-meet';
import { getLocalParticipant, getRemoteParticipants, isLocalParticipantModerator } from '../base/participants';
import { isInBreakoutRoom } from '../breakout-rooms/functions';
@ -153,7 +154,7 @@ export function getRecordButtonProps(state: Object): ?string {
// a button can be disabled/enabled if enableFeaturesBasedOnToken
// is on or if the livestreaming is running.
let disabled;
let disabled = false;
let tooltip = '';
// If the containing component provides the visible prop, that is one
@ -161,29 +162,18 @@ export function getRecordButtonProps(state: Object): ?string {
// its own to be visible or not.
const isModerator = isLocalParticipantModerator(state);
const {
enableFeaturesBasedOnToken,
recordingService,
localRecording
} = state['features/base/config'];
const { features = {} } = getLocalParticipant(state);
const localRecordingEnabled = !localRecording?.disable && supportsLocalRecording();
const dropboxEnabled = isDropboxEnabled(state);
visible = isModerator && (recordingService?.enabled || localRecordingEnabled || dropboxEnabled);
if (enableFeaturesBasedOnToken) {
visible = visible && String(features.recording) === 'true';
disabled = String(features.recording) === 'disabled';
if (!visible && !disabled) {
disabled = true;
visible = true;
tooltip = 'dialog.recordingDisabledTooltip';
}
}
visible = isJwtFeatureEnabled(state, 'recording', visible);
// disable the button if the livestreaming is running.
if (getActiveSession(state, JitsiRecordingConstants.mode.STREAM)) {
if (visible && getActiveSession(state, JitsiRecordingConstants.mode.STREAM)) {
disabled = true;
tooltip = 'dialog.recordingDisabledBecauseOfActiveLiveStreamingTooltip';
}

View File

@ -10,26 +10,20 @@ import { isDesktopShareButtonDisabled } from '../../functions';
type Props = AbstractButtonProps & {
/**
/**
* Whether or not screensharing is initialized.
*/
_desktopSharingEnabled: boolean,
/**
* The tooltip key to use when screensharing is disabled. Or undefined
* if non to be shown and the button to be hidden.
*/
_desktopSharingDisabledTooltipKey: string,
_desktopSharingEnabled: boolean,
/**
* Whether or not the local participant is screensharing.
*/
_screensharing: boolean,
_screensharing: boolean,
/**
* The redux {@code dispatch} function.
*/
dispatch: Function,
dispatch: Function,
};
/**
@ -46,7 +40,7 @@ class ShareDesktopButton extends AbstractButton<Props, *> {
* Retrieves tooltip dynamically.
*/
get tooltip() {
const { _desktopSharingDisabledTooltipKey, _desktopSharingEnabled, _screensharing } = this.props;
const { _desktopSharingEnabled, _screensharing } = this.props;
if (_desktopSharingEnabled) {
if (_screensharing) {
@ -56,7 +50,7 @@ class ShareDesktopButton extends AbstractButton<Props, *> {
return 'toolbar.startScreenSharing';
}
return _desktopSharingDisabledTooltipKey;
return 'dialog.shareYourScreenDisabled';
}
/**
@ -98,23 +92,12 @@ class ShareDesktopButton extends AbstractButton<Props, *> {
* @returns {Object}
*/
const mapStateToProps = state => {
let desktopSharingEnabled = JitsiMeetJS.isDesktopSharingEnabled();
const { enableFeaturesBasedOnToken } = state['features/base/config'];
let desktopSharingDisabledTooltipKey;
if (enableFeaturesBasedOnToken) {
// we enable desktop sharing if any participant already have this
// feature enabled
desktopSharingEnabled = state['features/base/participants'].haveParticipantWithScreenSharingFeature;
desktopSharingDisabledTooltipKey = 'dialog.shareYourScreenDisabled';
}
// Disable the screenshare button if the video sender limit is reached and there is no video or media share in
// progress.
desktopSharingEnabled = desktopSharingEnabled && !isDesktopShareButtonDisabled(state);
const desktopSharingEnabled
= JitsiMeetJS.isDesktopSharingEnabled() && !isDesktopShareButtonDisabled(state);
return {
_desktopSharingDisabledTooltipKey: desktopSharingDisabledTooltipKey,
_desktopSharingEnabled: desktopSharingEnabled,
_screensharing: isScreenVideoShared(state)
};

View File

@ -21,7 +21,6 @@ import JitsiMeetJS from '../../../base/lib-jitsi-meet';
import {
getLocalParticipant,
hasRaisedHand,
haveParticipantWithScreenSharingFeature,
raiseHand
} from '../../../base/participants';
import { connect } from '../../../base/redux';
@ -133,12 +132,6 @@ type Props = {
*/
_desktopSharingButtonDisabled: boolean,
/**
* The tooltip key to use when screensharing is disabled. Or undefined
* if non to be shown and the button to be hidden.
*/
_desktopSharingDisabledTooltipKey: boolean,
/**
* Whether or not screensharing is initialized.
*/
@ -1274,12 +1267,7 @@ class Toolbox extends Component<Props> {
* @returns {boolean}
*/
_showDesktopSharingButton() {
const {
_desktopSharingEnabled,
_desktopSharingDisabledTooltipKey
} = this.props;
return _desktopSharingEnabled || _desktopSharingDisabledTooltipKey;
return this.props._desktopSharingEnabled;
}
/**
@ -1408,12 +1396,10 @@ class Toolbox extends Component<Props> {
*/
function _mapStateToProps(state, ownProps) {
const { conference } = state['features/base/conference'];
let desktopSharingEnabled = JitsiMeetJS.isDesktopSharingEnabled();
const {
buttonsWithNotifyClick,
callStatsID,
disableProfile,
enableFeaturesBasedOnToken,
iAmRecorder,
iAmSipGateway
} = state['features/base/config'];
@ -1425,18 +1411,6 @@ function _mapStateToProps(state, ownProps) {
const localParticipant = getLocalParticipant(state);
const localVideo = getLocalVideoTrack(state['features/base/tracks']);
const { clientWidth } = state['features/base/responsive-ui'];
let desktopSharingDisabledTooltipKey;
if (enableFeaturesBasedOnToken) {
if (desktopSharingEnabled) {
// we enable desktop sharing if any participant already have this
// feature enabled and if the user supports it.
desktopSharingEnabled = haveParticipantWithScreenSharingFeature(state);
desktopSharingDisabledTooltipKey = 'dialog.shareYourScreenDisabled';
}
}
const toolbarButtons = ownProps.toolbarButtons || getToolbarButtons(state);
return {
@ -1445,9 +1419,8 @@ function _mapStateToProps(state, ownProps) {
_chatOpen: state['features/chat'].isOpen,
_clientWidth: clientWidth,
_conference: conference,
_desktopSharingEnabled: desktopSharingEnabled,
_desktopSharingEnabled: JitsiMeetJS.isDesktopSharingEnabled(),
_desktopSharingButtonDisabled: isDesktopShareButtonDisabled(state),
_desktopSharingDisabledTooltipKey: desktopSharingDisabledTooltipKey,
_dialog: Boolean(state['features/base/dialog'].component),
_disabled: Boolean(iAmRecorder || iAmSipGateway),
_feedbackConfigured: Boolean(callStatsID),