diff --git a/packages/@webex/plugin-meetings/src/meetings/index.ts b/packages/@webex/plugin-meetings/src/meetings/index.ts index 3188c825f36..6b6f9159113 100644 --- a/packages/@webex/plugin-meetings/src/meetings/index.ts +++ b/packages/@webex/plugin-meetings/src/meetings/index.ts @@ -1,5 +1,5 @@ /* eslint no-shadow: ["error", { "allow": ["eventType"] }] */ - +import {union} from 'lodash'; import '@webex/internal-plugin-mercury'; import '@webex/internal-plugin-conversation'; import '@webex/internal-plugin-metrics'; @@ -1001,7 +1001,10 @@ export default class Meetings extends WebexPlugin { fetchUserPreferredWebexSite() { return this.request.getMeetingPreferences().then((res) => { if (res) { - this.preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res); + const preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res); + this.preferredWebexSite = preferredWebexSite; + // @ts-ignore + this.webex.internal.services._getCatalog().addAllowedDomains([preferredWebexSite]); } // fall back to getting the preferred site from the user information @@ -1014,6 +1017,8 @@ export default class Meetings extends WebexPlugin { user?.userPreferences?.userPreferencesItems?.preferredWebExSite; if (preferredWebexSite) { this.preferredWebexSite = preferredWebexSite; + // @ts-ignore + this.webex.internal.services._getCatalog().addAllowedDomains([preferredWebexSite]); } else { throw new Error('site not found'); } diff --git a/packages/@webex/plugin-meetings/test/unit/spec/meetings/index.js b/packages/@webex/plugin-meetings/test/unit/spec/meetings/index.js index ab86cd085aa..1d171f41a80 100644 --- a/packages/@webex/plugin-meetings/test/unit/spec/meetings/index.js +++ b/packages/@webex/plugin-meetings/test/unit/spec/meetings/index.js @@ -18,6 +18,7 @@ import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy'; import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config'; import Meeting, {CallStateForMetrics} from '@webex/plugin-meetings/src/meeting'; +import {Services} from '@webex/webex-core'; import MeetingUtil from '@webex/plugin-meetings/src/meeting/util'; import Meetings from '@webex/plugin-meetings/src/meetings'; import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection'; @@ -75,6 +76,8 @@ describe('plugin-meetings', () => { let test1; let test2; let locusInfo; + let services; + let catalog; describe('meetings index', () => { beforeEach(() => { @@ -93,9 +96,13 @@ describe('plugin-meetings', () => { device: Device, mercury: Mercury, meetings: Meetings, + services: Services, }, }); + services = webex.internal.services; + catalog = services._getCatalog(); + Object.assign(webex, { logging: logger, }); @@ -161,6 +168,7 @@ describe('plugin-meetings', () => { ], }) ), + _getCatalog: sinon.stub().returns(catalog), fetchClientRegionInfo: sinon.stub().returns(Promise.resolve()), }, metrics: { @@ -1917,34 +1925,34 @@ describe('plugin-meetings', () => { let loggerProxySpy; it('should call request.getMeetingPreferences to get the preferred webex site ', async () => { + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), []); assert.isDefined(webex.meetings.preferredWebexSite); await webex.meetings.fetchUserPreferredWebexSite(); assert.equal(webex.meetings.preferredWebexSite, 'go.webex.com'); + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), [ + 'go.webex.com', + ]); }); const setup = ({user} = {}) => { loggerProxySpy = sinon.spy(LoggerProxy.logger, 'error'); + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), []); Object.assign(webex.internal, { - services: { - getMeetingPreferences: sinon.stub().returns(Promise.resolve({})), - }, user: { get: sinon.stub().returns(Promise.resolve(user)), }, }); + + Object.assign(webex.internal.services, { + getMeetingPreferences: sinon.stub().returns(Promise.resolve({})), + }); }; it('should not fail if UserPreferred info is not fetched ', async () => { setup(); - Object.assign(webex.internal, { - services: { - getMeetingPreferences: sinon.stub().returns(Promise.resolve({})), - }, - }); - await webex.meetings.fetchUserPreferredWebexSite().then(() => { assert.equal(webex.meetings.preferredWebexSite, ''); }); @@ -1952,6 +1960,7 @@ describe('plugin-meetings', () => { loggerProxySpy, 'Failed to fetch preferred site from user - no site will be set' ); + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']); }); it('should fall back to fetching the site from the user', async () => { @@ -1968,6 +1977,10 @@ describe('plugin-meetings', () => { await webex.meetings.fetchUserPreferredWebexSite(); assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com'); + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), [ + '', + 'site.webex.com', + ]); assert.notCalled(loggerProxySpy); }); @@ -1989,6 +2002,7 @@ describe('plugin-meetings', () => { loggerProxySpy, 'Failed to fetch preferred site from user - no site will be set' ); + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']); }); } ); @@ -2005,6 +2019,7 @@ describe('plugin-meetings', () => { loggerProxySpy, 'Failed to fetch preferred site from user - no site will be set' ); + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']); }); it('should fall back to fetching the site from the user', async () => { @@ -2022,6 +2037,10 @@ describe('plugin-meetings', () => { assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com'); assert.notCalled(loggerProxySpy); + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), [ + '', + 'site.webex.com', + ]); }); forEach( @@ -2042,6 +2061,7 @@ describe('plugin-meetings', () => { loggerProxySpy, 'Failed to fetch preferred site from user - no site will be set' ); + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']); }); } ); @@ -2058,6 +2078,7 @@ describe('plugin-meetings', () => { loggerProxySpy, 'Failed to fetch preferred site from user - no site will be set' ); + assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']); }); }); }); @@ -2344,12 +2365,14 @@ describe('plugin-meetings', () => { sessionType: 'MAIN', }; newLocus.self.state = 'JOINED'; - newLocus.self.devices = [{ - intent: { - reason: 'ON_HOLD_LOBBY', - type: 'WAIT', - } - }]; + newLocus.self.devices = [ + { + intent: { + reason: 'ON_HOLD_LOBBY', + type: 'WAIT', + }, + }, + ]; LoggerProxy.logger.log = sinon.stub(); const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus); assert.equal(result, true); diff --git a/packages/@webex/webex-core/src/config.js b/packages/@webex/webex-core/src/config.js index 42a8bfe3c27..88893b114ef 100644 --- a/packages/@webex/webex-core/src/config.js +++ b/packages/@webex/webex-core/src/config.js @@ -58,16 +58,7 @@ export default { * * @type {Array} */ - allowedDomains: [ - 'wbx2.com', - 'ciscospark.com', - 'webex.com', - 'webexapis.com', - 'broadcloudpbx.com', - 'broadcloud.eu', - 'broadcloud.com.au', - 'broadcloudpbx.net', - ], + allowedDomains: [], }, device: { preDiscoveryServices: { diff --git a/packages/@webex/webex-core/src/lib/services/constants.js b/packages/@webex/webex-core/src/lib/services/constants.js index 3f53f26e019..4f5da831e07 100644 --- a/packages/@webex/webex-core/src/lib/services/constants.js +++ b/packages/@webex/webex-core/src/lib/services/constants.js @@ -6,4 +6,16 @@ const SERVICE_CATALOGS_ENUM_TYPES = { NUMBER: 'SERVICE_CATALOGS_ENUM_TYPES_NUMBER', }; -export {SERVICE_CATALOGS_ENUM_TYPES, NAMESPACE, SERVICE_CATALOGS}; +// The default allowed domains that SDK can make requests to outside of service catalog +const COMMERCIAL_ALLOWED_DOMAINS = [ + 'wbx2.com', + 'ciscospark.com', + 'webex.com', + 'webexapis.com', + 'broadcloudpbx.com', + 'broadcloud.eu', + 'broadcloud.com.au', + 'broadcloudpbx.net', +]; + +export {SERVICE_CATALOGS_ENUM_TYPES, NAMESPACE, SERVICE_CATALOGS, COMMERCIAL_ALLOWED_DOMAINS}; diff --git a/packages/@webex/webex-core/src/lib/services/service-catalog.js b/packages/@webex/webex-core/src/lib/services/service-catalog.js index 8d209e14c09..f4f44eba8b9 100644 --- a/packages/@webex/webex-core/src/lib/services/service-catalog.js +++ b/packages/@webex/webex-core/src/lib/services/service-catalog.js @@ -2,6 +2,7 @@ import Url from 'url'; import AmpState from 'ampersand-state'; +import {union} from 'lodash'; import ServiceUrl from './service-url'; /* eslint-disable no-underscore-dangle */ @@ -361,6 +362,15 @@ const ServiceCatalog = AmpState.extend({ this.allowedDomains = [...allowedDomains]; }, + /** + * + * @param {Array} newAllowedDomains - new allowed domains to add to existing set of allowed domains + * @returns {void} + */ + addAllowedDomains(newAllowedDomains) { + this.allowedDomains = union(this.allowedDomains, newAllowedDomains); + }, + /** * Update the current list of `ServiceUrl`s against a provided * service hostmap. diff --git a/packages/@webex/webex-core/src/lib/services/services.js b/packages/@webex/webex-core/src/lib/services/services.js index 060aef75b42..a63b26ff945 100644 --- a/packages/@webex/webex-core/src/lib/services/services.js +++ b/packages/@webex/webex-core/src/lib/services/services.js @@ -2,6 +2,7 @@ import Url from 'url'; import sha256 from 'crypto-js/sha256'; +import {union} from 'lodash'; import WebexPlugin from '../webex-plugin'; import METRICS from './metrics'; @@ -9,6 +10,7 @@ import ServiceCatalog from './service-catalog'; import ServiceRegistry from './service-registry'; import ServiceState from './service-state'; import fedRampServices from './service-fed-ramp'; +import {COMMERCIAL_ALLOWED_DOMAINS} from './constants'; const trailingSlashes = /(?:^\/)|(?:\/$)/; @@ -941,6 +943,11 @@ const Services = WebexPlugin.extend({ catalog.updateServiceUrls('override', formattedOverrideServices); } + // if not fedramp, append on the commercialAllowedDomains + if (!fedramp) { + services.allowedDomains = union(services.allowedDomains, COMMERCIAL_ALLOWED_DOMAINS); + } + // Check for allowed host domains. if (services.allowedDomains) { // Store the allowed domains as a property of the catalog. diff --git a/packages/@webex/webex-core/test/integration/spec/services/services.js b/packages/@webex/webex-core/test/integration/spec/services/services.js index 809d214edec..c0943d34286 100644 --- a/packages/@webex/webex-core/test/integration/spec/services/services.js +++ b/packages/@webex/webex-core/test/integration/spec/services/services.js @@ -11,6 +11,7 @@ import WebexCore, { ServiceRegistry, ServiceState, ServiceUrl, + serviceConstants, } from '@webex/webex-core'; import testUsers from '@webex/test-helper-test-users'; import uuid from 'uuid'; @@ -363,7 +364,9 @@ describe('webex-core', () => { services.initConfig(); - assert.deepEqual(allowedDomains, services._getCatalog().allowedDomains); + const expectedResult = [...allowedDomains, ...serviceConstants.COMMERCIAL_ALLOWED_DOMAINS]; + + assert.deepEqual(expectedResult, services._getCatalog().allowedDomains); }); }); diff --git a/packages/@webex/webex-core/test/unit/spec/interceptors/auth.js b/packages/@webex/webex-core/test/unit/spec/interceptors/auth.js index be1cf46942e..877e115c0e3 100644 --- a/packages/@webex/webex-core/test/unit/spec/interceptors/auth.js +++ b/packages/@webex/webex-core/test/unit/spec/interceptors/auth.js @@ -10,7 +10,14 @@ import sinon from 'sinon'; import {browserOnly, nodeOnly} from '@webex/test-helper-mocha'; import Logger from '@webex/plugin-logger'; import MockWebex from '@webex/test-helper-mock-webex'; -import {AuthInterceptor, config, Credentials, WebexHttpError, Token} from '@webex/webex-core'; +import { + AuthInterceptor, + config, + Credentials, + WebexHttpError, + Token, + serviceConstants, +} from '@webex/webex-core'; import {cloneDeep, merge} from 'lodash'; import Metrics from '@webex/internal-plugin-metrics'; @@ -122,7 +129,7 @@ describe('webex-core', () => { hasService: (service) => Object.keys(services).includes(service), hasAllowedDomains: () => true, isAllowedDomainUrl: (uri) => - !!config.services.allowedDomains.find((host) => uri.includes(host)), + !!serviceConstants.COMMERCIAL_ALLOWED_DOMAINS.find((host) => uri.includes(host)), getServiceFromUrl: (uri) => { let targetKey; @@ -249,7 +256,7 @@ describe('webex-core', () => { hasService: (service) => Object.keys(services).includes(service), hasAllowedDomains: () => true, isAllowedDomainUrl: (uri) => - !!config.services.allowedDomains.find((host) => uri.includes(host)), + !!serviceConstants.COMMERCIAL_ALLOWED_DOMAINS.find((host) => uri.includes(host)), validateDomains: true, }; @@ -323,7 +330,7 @@ describe('webex-core', () => { it('resolves to true with an allowed domain uri', () => interceptor .requiresCredentials({ - uri: `https://${config.services.allowedDomains[0]}/resource`, + uri: `https://${serviceConstants.COMMERCIAL_ALLOWED_DOMAINS[0]}/resource`, }) .then((response) => assert.isTrue(response))); @@ -339,7 +346,7 @@ describe('webex-core', () => { const {isAllowedDomainUrl} = webex.internal.services; const result = isAllowedDomainUrl( - `https://${config.services.allowedDomains[0]}/resource` + `https://${serviceConstants.COMMERCIAL_ALLOWED_DOMAINS[0]}/resource` ); assert.equal(result, true); @@ -350,7 +357,7 @@ describe('webex-core', () => { return interceptor .requiresCredentials({ - uri: `https://${config.services.allowedDomains[0]}/resource`, + uri: `https://${serviceConstants.COMMERCIAL_ALLOWED_DOMAINS[0]}/resource`, }) .then((res) => { assert.equal(res, true); @@ -361,7 +368,9 @@ describe('webex-core', () => { webex.internal.services.waitForService = sinon.stub(); const {waitForService} = webex.internal.services; - waitForService.resolves(`https://${config.services.allowedDomains[0]}/resource`); + waitForService.resolves( + `https://${serviceConstants.COMMERCIAL_ALLOWED_DOMAINS[0]}/resource` + ); return interceptor .requiresCredentials({ diff --git a/packages/@webex/webex-core/test/unit/spec/services/service-catalog.js b/packages/@webex/webex-core/test/unit/spec/services/service-catalog.js index 89f81689984..3cf4f52fb04 100644 --- a/packages/@webex/webex-core/test/unit/spec/services/service-catalog.js +++ b/packages/@webex/webex-core/test/unit/spec/services/service-catalog.js @@ -101,11 +101,7 @@ describe('webex-core', () => { const domains = []; beforeEach(() => { - domains.push( - 'example-a', - 'example-b', - 'example-c' - ); + domains.push('example-a', 'example-b', 'example-c'); catalog.setAllowedDomains(domains); }); @@ -125,11 +121,7 @@ describe('webex-core', () => { const domains = []; beforeEach(() => { - domains.push( - 'example-a', - 'example-b', - 'example-c' - ); + domains.push('example-a', 'example-b', 'example-c'); catalog.setAllowedDomains(domains); }); @@ -168,11 +160,7 @@ describe('webex-core', () => { const domains = []; beforeEach(() => { - domains.push( - 'example-a', - 'example-b', - 'example-c' - ); + domains.push('example-a', 'example-b', 'example-c'); catalog.setAllowedDomains(domains); }); @@ -189,6 +177,30 @@ describe('webex-core', () => { assert.notDeepInclude(domains, newValues); }); }); + + describe('#addAllowedDomains()', () => { + const domains = []; + + beforeEach(() => { + domains.push('example-a', 'example-b', 'example-c'); + + catalog.setAllowedDomains(domains); + }); + + afterEach(() => { + domains.length = 0; + }); + + it('merge the allowed domain entries with new values', () => { + const newValues = ['example-c', 'example-e', 'example-f']; + + catalog.addAllowedDomains(newValues); + + const list = catalog.getAllowedDomains(); + + assert.match(['example-a', 'example-b', 'example-c', 'example-e', 'example-f'], list); + }); + }); }); }); /* eslint-enable no-underscore-dangle */