Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression: Encode registration info as JWT when signing key is provided #24626

Merged
merged 2 commits into from
Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions app/api/server/v1/voip/extensions.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { Match, check } from 'meteor/check';

import { API } from '../../api';
import { hasPermission } from '../../../../authorization/server/index';
import { Users } from '../../../../models/server/raw/index';
import { Voip } from '../../../../../server/sdk';
import { IVoipExtensionBase } from '../../../../../definition/IVoipExtension';
import { generateJWT } from '../../../../utils/server/lib/JWTHelper';
import { settings } from '../../../../settings/server';
import { logger } from './logger';

// Get the connector version and type
API.v1.addRoute(
'connector.getVersion',
{ authRequired: true },
{ authRequired: true, permissionsRequired: ['manage-voip-call-settings'] },
{
async get() {
const version = await Voip.getConnectorVersion();
Expand All @@ -21,7 +23,7 @@ API.v1.addRoute(
// Get the extensions available on the call server
API.v1.addRoute(
'connector.extension.list',
{ authRequired: true },
{ authRequired: true, permissionsRequired: ['manage-voip-call-settings'] },
{
async get() {
const list = await Voip.getExtensionList();
Expand All @@ -37,7 +39,7 @@ API.v1.addRoute(
*/
API.v1.addRoute(
'connector.extension.getDetails',
{ authRequired: true },
{ authRequired: true, permissionsRequired: ['manage-voip-call-settings'] },
{
async get() {
check(
Expand All @@ -57,7 +59,7 @@ API.v1.addRoute(

API.v1.addRoute(
'connector.extension.getRegistrationInfoByExtension',
{ authRequired: true },
{ authRequired: true, permissionsRequired: ['manage-voip-call-settings'] },
{
async get() {
check(
Expand All @@ -67,14 +69,21 @@ API.v1.addRoute(
}),
);
const endpointDetails = await Voip.getRegistrationInfo(this.requestParams());
return API.v1.success({ ...endpointDetails.result });
const encKey = settings.get('VoIP_JWT_Secret');
if (!encKey) {
logger.warn('No JWT keys set. Sending registration info as plain text');
return API.v1.success({ ...endpointDetails.result });
}

const result = generateJWT(endpointDetails.result, encKey);
return API.v1.success({ result });
},
},
);

API.v1.addRoute(
'connector.extension.getRegistrationInfoByUserId',
{ authRequired: true },
{ authRequired: true, permissionsRequired: ['view-agent-extension-association'] },
{
async get() {
check(
Expand All @@ -83,10 +92,12 @@ API.v1.addRoute(
id: String,
}),
);
if (!hasPermission(this.userId, 'view-agent-extension-association')) {
const { id } = this.requestParams();

if (id !== this.userId) {
return API.v1.unauthorized();
}
const { id } = this.requestParams();

const { extension } =
(await Users.getVoipExtensionByUserId(id, {
projection: {
Expand All @@ -99,8 +110,16 @@ API.v1.addRoute(
if (!extension) {
return API.v1.notFound('Extension not found');
}

const endpointDetails = await Voip.getRegistrationInfo({ extension });
return API.v1.success({ ...endpointDetails.result });
const encKey = settings.get('VoIP_JWT_Secret');
if (!encKey) {
logger.warn('No JWT keys set. Sending registration info as plain text');
return API.v1.success({ ...endpointDetails.result });
}

const result = generateJWT(endpointDetails.result, encKey);
return API.v1.success({ result });
},
},
);
11 changes: 9 additions & 2 deletions app/lib/server/startup/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3150,10 +3150,17 @@ settingsRegistry.addGroup('Call_Center', function () {
value: true,
},
});

this.add('VoIP_JWT_Secret', '', {
type: 'password',
i18nDescription: 'VoIP_JWT_Secret_description',
enableQuery: {
_id: 'VoIP_Enabled',
value: true,
},
});
this.section('Server_Configuration', function () {
this.add('VoIP_Server_Host', '', {
type: 'string',
type: 'password',
public: true,
enableQuery: {
_id: 'VoIP_Enabled',
Expand Down
16 changes: 14 additions & 2 deletions client/providers/CallProvider/hooks/useVoipClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useSafely } from '@rocket.chat/fuselage-hooks';
import { KJUR } from 'jsrsasign';
import { useEffect, useState } from 'react';

import { IRegistrationInfo } from '../../../../definition/voip/IRegistrationInfo';
Expand All @@ -23,6 +24,8 @@ export const isUseVoipClientResultError = (result: UseVoipClientResult): result
export const isUseVoipClientResultLoading = (result: UseVoipClientResult): result is UseVoipClientResultLoading =>
!result || !Object.keys(result).length;

const isSignedResponse = (data: any): data is { result: string } => typeof data?.result === 'string';

export const useVoipClient = (): UseVoipClientResult => {
const registrationInfo = useEndpoint('GET', 'connector.extension.getRegistrationInfoByUserId');
const user = useUser();
Expand All @@ -37,16 +40,25 @@ export const useVoipClient = (): UseVoipClientResult => {
}
registrationInfo({ id: user._id }).then(
(data) => {
let parsedData: IRegistrationInfo;
if (isSignedResponse(data)) {
const result = KJUR.jws.JWS.parse(data.result);
parsedData = (result.payloadObj as any)?.context as IRegistrationInfo;
} else {
parsedData = data;
}

const {
extensionDetails: { extension, password },
host,
callServerConfig: { websocketPath },
} = data;
} = parsedData;

let client: VoIPUser;
(async (): Promise<void> => {
try {
client = await SimpleVoipUser.create(extension, password, host, websocketPath, iceServers, 'video');
setResult({ voipClient: client, registrationInfo: data });
setResult({ voipClient: client, registrationInfo: parsedData });
} catch (e) {
setResult({ error: e as Error });
}
Expand Down
2 changes: 1 addition & 1 deletion definition/rest/v1/voip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PaginatedResult } from '../helpers/PaginatedResult';

export type VoipEndpoints = {
'connector.extension.getRegistrationInfoByUserId': {
GET: (params: { id: string }) => IRegistrationInfo;
GET: (params: { id: string }) => IRegistrationInfo | { result: string };
};
'voip/queues.getSummary': {
GET: () => { summary: IQueueSummary[] };
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@
"@types/dompurify": "^2.2.2",
"@types/ejson": "^2.1.3",
"@types/express": "^4.17.12",
"@types/google-libphonenumber": "^7.4.21",
"@types/fibers": "^3.1.1",
"@types/google-libphonenumber": "^7.4.21",
"@types/imap": "^0.8.35",
"@types/jsdom": "^16.2.12",
"@types/jsdom-global": "^3.0.2",
"@types/jsrsasign": "^9.0.1",
"@types/jsrsasign": "^9.0.3",
"@types/ldapjs": "^2.2.1",
"@types/less": "^3.0.2",
"@types/lodash.get": "^4.4.6",
Expand Down
2 changes: 2 additions & 0 deletions packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -4737,6 +4737,8 @@
"Voip_call_ended": "Call ended at",
"Voip_call_ended_unexpectedly": "Call ended unexpectedly: __reason__",
"Voip_call_wrapup": "Call wrapup notes added: __comment__",
"VoIP_JWT_Secret": "VoIP JWT Secret",
"VoIP_JWT_Secret_description": "This allows you to set a secret key for sharing extension details from server to client as JWT instead of plain text. If you don't setup this, extension registration details will be sent as plain text",
"Chat_opened_by_visitor": "Chat opened by the visitor",
"Wait_activation_warning": "Before you can login, your account must be manually activated by an administrator.",
"Waiting_queue": "Waiting queue",
Expand Down