feat(dialout) check appId for permission to call outbound destination (#12871)

This commit is contained in:
Mihaela Dumitru 2023-02-07 16:18:27 +02:00 committed by GitHub
parent 3e59359563
commit 84221c5c13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 8 deletions

View File

@ -1335,6 +1335,7 @@ var config = {
deploymentInfo deploymentInfo
dialOutAuthUrl dialOutAuthUrl
dialOutCodesUrl dialOutCodesUrl
dialOutRegionUrl
disableRemoteControl disableRemoteControl
displayJids displayJids
externalConnectUrl externalConnectUrl

View File

@ -35,6 +35,11 @@ export type Props = {
*/ */
_dialOutAuthUrl: string, _dialOutAuthUrl: string,
/**
* The URL for validating if an outbound destination is allowed.
*/
_dialOutRegionUrl: string;
/** /**
* Whether or not to show Dial Out functionality. * Whether or not to show Dial Out functionality.
*/ */
@ -235,7 +240,9 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
_query(query = '') { _query(query = '') {
const { const {
_addPeopleEnabled: addPeopleEnabled, _addPeopleEnabled: addPeopleEnabled,
_appId: appId,
_dialOutAuthUrl: dialOutAuthUrl, _dialOutAuthUrl: dialOutAuthUrl,
_dialOutRegionUrl: dialOutRegionUrl,
_dialOutEnabled: dialOutEnabled, _dialOutEnabled: dialOutEnabled,
_jwt: jwt, _jwt: jwt,
_peopleSearchQueryTypes: peopleSearchQueryTypes, _peopleSearchQueryTypes: peopleSearchQueryTypes,
@ -244,8 +251,10 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
} = this.props; } = this.props;
const options = { const options = {
addPeopleEnabled, addPeopleEnabled,
appId,
dialOutAuthUrl, dialOutAuthUrl,
dialOutEnabled, dialOutEnabled,
dialOutRegionUrl,
jwt, jwt,
peopleSearchQueryTypes, peopleSearchQueryTypes,
peopleSearchUrl, peopleSearchUrl,
@ -275,14 +284,17 @@ export function _mapStateToProps(state: Object) {
const { const {
callFlowsEnabled, callFlowsEnabled,
dialOutAuthUrl, dialOutAuthUrl,
dialOutRegionUrl,
peopleSearchQueryTypes, peopleSearchQueryTypes,
peopleSearchUrl peopleSearchUrl
} = state['features/base/config']; } = state['features/base/config'];
return { return {
_addPeopleEnabled: isAddPeopleEnabled(state), _addPeopleEnabled: isAddPeopleEnabled(state),
_appId: state['features/base/jwt']?.tenant,
_callFlowsEnabled: callFlowsEnabled, _callFlowsEnabled: callFlowsEnabled,
_dialOutAuthUrl: dialOutAuthUrl, _dialOutAuthUrl: dialOutAuthUrl,
_dialOutRegionUrl: dialOutRegionUrl,
_dialOutEnabled: isDialOutEnabled(state), _dialOutEnabled: isDialOutEnabled(state),
_jwt: state['features/base/jwt'].jwt, _jwt: state['features/base/jwt'].jwt,
_peopleSearchQueryTypes: peopleSearchQueryTypes, _peopleSearchQueryTypes: peopleSearchQueryTypes,

View File

@ -8,6 +8,7 @@ import { isJwtFeatureEnabled } from '../base/jwt/functions';
import { JitsiRecordingConstants } from '../base/lib-jitsi-meet'; import { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
import { getLocalParticipant, isLocalParticipantModerator } from '../base/participants/functions'; import { getLocalParticipant, isLocalParticipantModerator } from '../base/participants/functions';
import { toState } from '../base/redux/functions'; import { toState } from '../base/redux/functions';
import { doGetJSON } from '../base/util/httpUtils';
import { parseURLParams } from '../base/util/parseURLParams'; import { parseURLParams } from '../base/util/parseURLParams';
import { import {
StatusCode, StatusCode,
@ -55,6 +56,34 @@ export function checkDialNumber(
}); });
} }
/**
* Sends an ajax request to check if the outbound call is permitted.
*
* @param {string} dialOutRegionUrl - The config endpoint.
* @param {string} jwt - The jwt token.
* @param {string} appId - The customer id.
* @param {string} phoneNumber - The destination phone number.
* @returns {Promise} - The promise created by the request.
*/
export function checkOutboundDestination(
dialOutRegionUrl: string,
jwt: string,
appId: string,
phoneNumber: string
): Promise<any> {
return doGetJSON(dialOutRegionUrl, true, {
body: JSON.stringify({
appId,
phoneNumber
}),
method: 'POST',
headers: {
'Authorization': `Bearer ${jwt}`,
'Content-Type': 'application/json'
}
});
}
/** /**
* Removes all non-numeric characters from a string. * Removes all non-numeric characters from a string.
* *
@ -76,6 +105,11 @@ export type GetInviteResultsOptions = {
*/ */
addPeopleEnabled: boolean; addPeopleEnabled: boolean;
/**
* The customer id.
*/
appId: string;
/** /**
* The endpoint to use for checking phone number validity. * The endpoint to use for checking phone number validity.
*/ */
@ -86,6 +120,11 @@ export type GetInviteResultsOptions = {
*/ */
dialOutEnabled: boolean; dialOutEnabled: boolean;
/**
* The endpoint to use for checking dial permission to an outbound destination.
*/
dialOutRegionUrl: string;
/** /**
* The jwt token to pass to the search service. * The jwt token to pass to the search service.
*/ */
@ -123,8 +162,10 @@ export function getInviteResultsForQuery(
const text = query.trim(); const text = query.trim();
const { const {
dialOutAuthUrl,
addPeopleEnabled, addPeopleEnabled,
appId,
dialOutAuthUrl,
dialOutRegionUrl,
dialOutEnabled, dialOutEnabled,
peopleSearchQueryTypes, peopleSearchQueryTypes,
peopleSearchUrl, peopleSearchUrl,
@ -187,7 +228,7 @@ export function getInviteResultsForQuery(
} }
return Promise.all([ peopleSearchPromise, phoneNumberPromise ]) return Promise.all([ peopleSearchPromise, phoneNumberPromise ])
.then(([ peopleResults, phoneResults ]) => { .then(async ([ peopleResults, phoneResults ]) => {
const results: any[] = [ const results: any[] = [
...peopleResults ...peopleResults
]; ];
@ -203,14 +244,26 @@ export function getInviteResultsForQuery(
= peopleResults.find(result => result.type === INVITE_TYPES.PHONE); = peopleResults.find(result => result.type === INVITE_TYPES.PHONE);
if (!hasPhoneResult && typeof phoneResults.allow === 'boolean') { if (!hasPhoneResult && typeof phoneResults.allow === 'boolean') {
results.push({ const result = {
allowed: phoneResults.allow, allowed: phoneResults.allow,
country: phoneResults.country, country: phoneResults.country,
type: INVITE_TYPES.PHONE, type: INVITE_TYPES.PHONE,
number: phoneResults.phone, number: phoneResults.phone,
originalEntry: text, originalEntry: text,
showCountryCodeReminder: !hasCountryCode showCountryCodeReminder: !hasCountryCode
}); };
if (!phoneResults.allow) {
try {
const response = await checkOutboundDestination(dialOutRegionUrl, jwt, appId, text);
result.allowed = response.allowed;
} catch (error) {
logger.error('Error checking permission to dial to outbound destination', error);
}
}
results.push(result);
} }
if (sipInviteEnabled && isASipAddress(text)) { if (sipInviteEnabled && isASipAddress(text)) {

View File

@ -2,9 +2,9 @@
"include": ["react/features/**/*.ts", "react/features/**/*.tsx", "./custom.d.ts", "./globals.native.d.ts"], "include": ["react/features/**/*.ts", "react/features/**/*.tsx", "./custom.d.ts", "./globals.native.d.ts"],
"compilerOptions": { "compilerOptions": {
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"target": "ESNext", "target": "es2020",
"jsx": "react-native", "jsx": "react-native",
"lib": [ "ES2017"], "lib": [ "es2020"],
"types": [ "react-native" ], "types": [ "react-native" ],
"skipLibCheck": true, "skipLibCheck": true,
"moduleResolution": "Node", "moduleResolution": "Node",

View File

@ -2,8 +2,8 @@
"include": ["react/features/**/*.ts", "react/features/**/*.tsx", "./custom.d.ts", "./globals.d.ts"], "include": ["react/features/**/*.ts", "react/features/**/*.tsx", "./custom.d.ts", "./globals.d.ts"],
"compilerOptions": { "compilerOptions": {
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"module": "es6", "module": "es2020",
"target": "es6", "target": "es2020",
"jsx": "react", "jsx": "react",
"lib": [ "webworker", "ES2020", "DOM" ], "lib": [ "webworker", "ES2020", "DOM" ],
"skipLibCheck": true, "skipLibCheck": true,