UNPKG

@webex/webex-core

Version:

Plugin handling for Cisco Webex

1,075 lines (1,038 loc) • 40.6 kB
"use strict"; var _Object$keys2 = require("@babel/runtime-corejs2/core-js/object/keys"); var _Object$getOwnPropertySymbols = require("@babel/runtime-corejs2/core-js/object/get-own-property-symbols"); var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor"); var _Object$getOwnPropertyDescriptors = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptors"); var _Object$defineProperties = require("@babel/runtime-corejs2/core-js/object/define-properties"); var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property"); var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault"); _Object$defineProperty(exports, "__esModule", { value: true }); exports.default = exports.DEFAULT_CLUSTER_SERVICE = exports.DEFAULT_CLUSTER = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty")); var _weakMap = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/weak-map")); var _keys = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/keys")); var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise")); var _sha = _interopRequireDefault(require("crypto-js/sha256")); var _lodash = require("lodash"); var _webexPlugin = _interopRequireDefault(require("../webex-plugin")); var _metrics = _interopRequireDefault(require("../metrics")); var _serviceCatalog = _interopRequireDefault(require("./service-catalog")); var _serviceRegistry = _interopRequireDefault(require("./service-registry")); var _serviceState = _interopRequireDefault(require("./service-state")); var _serviceFedRamp = _interopRequireDefault(require("./service-fed-ramp")); var _constants = require("../constants"); function ownKeys(e, r) { var t = _Object$keys2(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } var trailingSlashes = /(?:^\/)|(?:\/$)/; // The default cluster when one is not provided (usually as 'US' from hydra) var DEFAULT_CLUSTER = exports.DEFAULT_CLUSTER = 'urn:TEAM:us-east-2_a'; // The default service name for convo (currently identityLookup due to some weird CSB issue) var DEFAULT_CLUSTER_SERVICE = exports.DEFAULT_CLUSTER_SERVICE = 'identityLookup'; var CLUSTER_SERVICE = process.env.WEBEX_CONVERSATION_CLUSTER_SERVICE || DEFAULT_CLUSTER_SERVICE; var DEFAULT_CLUSTER_IDENTIFIER = process.env.WEBEX_CONVERSATION_DEFAULT_CLUSTER || "".concat(DEFAULT_CLUSTER, ":").concat(CLUSTER_SERVICE); /* eslint-disable no-underscore-dangle */ /** * @class */ var Services = _webexPlugin.default.extend({ namespace: 'Services', /** * The {@link WeakMap} of {@link ServiceRegistry} class instances that are * keyed with WebexCore instances. * * @instance * @type {WeakMap<WebexCore, ServiceRegistry>} * @private * @memberof Services */ registries: new _weakMap.default(), /** * The {@link WeakMap} of {@link ServiceState} class instances that are * keyed with WebexCore instances. * * @instance * @type {WeakMap<WebexCore, ServiceState>} * @private * @memberof Services */ states: new _weakMap.default(), props: { validateDomains: ['boolean', false, true], initFailed: ['boolean', false, false] }, _catalogs: new _weakMap.default(), _serviceUrls: null, _hostCatalog: null, /** * Get the registry associated with this webex instance. * * @private * @memberof Services * @returns {ServiceRegistry} - The associated {@link ServiceRegistry}. */ getRegistry: function getRegistry() { return this.registries.get(this.webex); }, /** * Get the state associated with this webex instance. * * @private * @memberof Services * @returns {ServiceState} - The associated {@link ServiceState}. */ getState: function getState() { return this.states.get(this.webex); }, /** * @private * Get the current catalog based on the assocaited * webex instance. * @returns {ServiceCatalog} */ _getCatalog: function _getCatalog() { return this._catalogs.get(this.webex); }, /** * Get a service url from the current services list by name * from the associated instance catalog. * @param {string} name * @param {boolean} [priorityHost] * @param {string} [serviceGroup] * @returns {string|undefined} */ get: function get(name, priorityHost, serviceGroup) { var catalog = this._getCatalog(); return catalog.get(name, priorityHost, serviceGroup); }, /** * Determine if the catalog contains a specific service * * @param {string} serviceName - The service name to validate. * @returns {boolean} - True if the service exists. */ hasService: function hasService(serviceName) { return !!this.get(serviceName); }, /** * Determine if a whilelist exists in the service catalog. * * @returns {boolean} - True if a allowed domains list exists. */ hasAllowedDomains: function hasAllowedDomains() { var catalog = this._getCatalog(); return catalog.getAllowedDomains().length > 0; }, /** * Generate a service catalog as an object from * the associated instance catalog. * @param {boolean} [priorityHost] - use highest priority host if set to `true` * @param {string} [serviceGroup] * @returns {Record<string, string>} */ list: function list(priorityHost, serviceGroup) { var catalog = this._getCatalog(); return catalog.list(priorityHost, serviceGroup); }, /** * Mark a priority host service url as failed. * This will mark the host associated with the * `ServiceUrl` to be removed from the its * respective host array, and then return the next * viable host from the `ServiceUrls` host array, * or the `ServiceUrls` default url if no other priority * hosts are available, or if `noPriorityHosts` is set to * `true`. * @param {string} url * @param {boolean} noPriorityHosts * @returns {string} */ markFailedUrl: function markFailedUrl(url, noPriorityHosts) { var catalog = this._getCatalog(); return catalog.markFailedUrl(url, noPriorityHosts); }, /** * saves all the services from the pre and post catalog service * @param {Object} serviceUrls * @returns {void} */ _updateServiceUrls: function _updateServiceUrls(serviceUrls) { this._serviceUrls = _objectSpread(_objectSpread({}, this._serviceUrls), serviceUrls); }, /** * saves the hostCatalog object * @param {Object} hostCatalog * @returns {void} */ _updateHostCatalog: function _updateHostCatalog(hostCatalog) { this._hostCatalog = _objectSpread(_objectSpread({}, this._hostCatalog), hostCatalog); }, /** * Update a list of `serviceUrls` to the most current * catalog via the defined `discoveryUrl` then returns the current * list of services. * @param {object} [param] * @param {string} [param.from] - This accepts `limited` or `signin` * @param {object} [param.query] - This accepts `email`, `orgId` or `userId` key values * @param {string} [param.query.email] - must be a standard-format email * @param {string} [param.query.orgId] - must be an organization id * @param {string} [param.query.userId] - must be a user id * @param {string} [param.token] - used for signin catalog * @returns {Promise<object>} */ updateServices: function updateServices() { var _this = this; var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, from = _ref.from, query = _ref.query, token = _ref.token, forceRefresh = _ref.forceRefresh; var catalog = this._getCatalog(); var formattedQuery; var serviceGroup; // map catalog name to service group name. switch (from) { case 'limited': serviceGroup = 'preauth'; break; case 'signin': serviceGroup = 'signin'; break; default: serviceGroup = 'postauth'; break; } // confirm catalog update for group is not in progress. if (catalog.status[serviceGroup].collecting) { return this.waitForCatalog(serviceGroup); } catalog.status[serviceGroup].collecting = true; if (serviceGroup === 'preauth') { var queryKey = query && (0, _keys.default)(query)[0]; if (!['email', 'emailhash', 'userId', 'orgId', 'mode'].includes(queryKey)) { return _promise.default.reject(new Error('a query param of email, emailhash, userId, orgId, or mode is required')); } } // encode email when query key is email if (serviceGroup === 'preauth' || serviceGroup === 'signin') { var _queryKey = (0, _keys.default)(query)[0]; formattedQuery = {}; if (_queryKey === 'email' && query.email) { formattedQuery.emailhash = (0, _sha.default)(query.email.toLowerCase()).toString(); } else { formattedQuery[_queryKey] = query[_queryKey]; } } return this._fetchNewServiceHostmap({ from: from, token: token, query: formattedQuery, forceRefresh: forceRefresh }).then(function (serviceHostMap) { catalog.updateServiceUrls(serviceGroup, serviceHostMap); _this.updateCredentialsConfig(); catalog.status[serviceGroup].collecting = false; }).catch(function (error) { catalog.status[serviceGroup].collecting = false; return _promise.default.reject(error); }); }, /** * User validation parameter transfer object for {@link validateUser}. * @param {object} ValidateUserPTO * @property {string} ValidateUserPTO.email - The email of the user. * @property {string} [ValidateUserPTO.reqId] - The activation requester. * @property {object} [ValidateUserPTO.activationOptions] - Extra options to pass when sending the activation * @property {object} [ValidateUserPTO.preloginUserId] - The prelogin user id to set when sending the activation. */ /** * User validation return transfer object for {@link validateUser}. * @param {object} ValidateUserRTO * @property {boolean} ValidateUserRTO.activated - If the user is activated. * @property {boolean} ValidateUserRTO.exists - If the user exists. * @property {string} ValidateUserRTO.details - A descriptive status message. * @property {object} ValidateUserRTO.user - **License** service user object. */ /** * Validate if a user is activated and update the service catalogs as needed * based on the user's activation status. * * @param {ValidateUserPTO} - The parameter transfer object. * @returns {ValidateUserRTO} - The return transfer object. */ validateUser: function validateUser(_ref2) { var _this2 = this; var email = _ref2.email, _ref2$reqId = _ref2.reqId, reqId = _ref2$reqId === void 0 ? 'WEBCLIENT' : _ref2$reqId, _ref2$forceRefresh = _ref2.forceRefresh, forceRefresh = _ref2$forceRefresh === void 0 ? false : _ref2$forceRefresh, _ref2$activationOptio = _ref2.activationOptions, activationOptions = _ref2$activationOptio === void 0 ? {} : _ref2$activationOptio, preloginUserId = _ref2.preloginUserId; this.logger.info('services: validating a user'); // Validate that an email parameter key was provided. if (!email) { return _promise.default.reject(new Error('`email` is required')); } // Destructure the credentials object. var canAuthorize = this.webex.credentials.canAuthorize; // Validate that the user is already authorized. if (canAuthorize) { return this.updateServices({ forceRefresh: forceRefresh }).then(function () { return _this2.webex.credentials.getUserToken(); }).then(function (token) { return _this2.sendUserActivation({ email: email, reqId: reqId, token: token.toString(), activationOptions: activationOptions, preloginUserId: preloginUserId }); }).then(function (userObj) { return { activated: true, exists: true, details: 'user is authorized via a user token', user: userObj }; }); } // Destructure the client authorization details. /* eslint-disable camelcase */ var _this$webex$credentia = this.webex.credentials.config, client_id = _this$webex$credentia.client_id, client_secret = _this$webex$credentia.client_secret; // Validate that client authentication details exist. if (!client_id || !client_secret) { return _promise.default.reject(new Error('client authentication details are not available')); } /* eslint-enable camelcase */ // Declare a class-memeber-scoped token for usage within the promise chain. var token; // Begin client authentication user validation. return this.collectPreauthCatalog({ email: email }).then(function () { // Retrieve the service url from the updated catalog. This is required // since `WebexCore` is usually not fully initialized at the time this // request completes. var idbrokerService = _this2.get('idbroker', true); // Collect the client auth token. return _this2.webex.credentials.getClientToken({ uri: "".concat(idbrokerService, "idb/oauth2/v1/access_token"), scope: 'webexsquare:admin webexsquare:get_conversation Identity:SCIM' }); }).then(function (tokenObj) { // Generate the token string. token = tokenObj.toString(); // Collect the signin catalog using the client auth information. return _this2.collectSigninCatalog({ email: email, token: token, forceRefresh: forceRefresh }); }) // Validate if collecting the signin catalog failed and populate the RTO // with the appropriate content. .catch(function (error) { return { exists: error.name !== 'NotFound', activated: false, details: error.name !== 'NotFound' ? 'user exists but is not activated' : 'user does not exist and is not activated' }; }) // Validate if the previous promise resolved with an RTO and populate the // new RTO accordingly. .then(function (rto) { return _promise.default.all([rto || { activated: true, exists: true, details: 'user exists and is activated' }, _this2.sendUserActivation({ email: email, reqId: reqId, token: token, activationOptions: activationOptions, preloginUserId: preloginUserId })]); }).then(function (_ref3) { var _ref4 = (0, _slicedToArray2.default)(_ref3, 2), rto = _ref4[0], user = _ref4[1]; return _objectSpread(_objectSpread({}, rto), {}, { user: user }); }).catch(function (error) { var response = { statusCode: error.statusCode, responseText: error.body && error.body.message, body: error.body }; return _promise.default.reject(response); }); }, /** * Get user meeting preferences (preferred webex site). * * @returns {object} - User Information including user preferrences . */ getMeetingPreferences: function getMeetingPreferences() { var _this3 = this; return this.request({ method: 'GET', service: 'hydra', resource: 'meetingPreferences' }).then(function (res) { _this3.logger.info('services: received user region info'); return res.body; }).catch(function (err) { _this3.logger.info('services: was not able to fetch user login information', err); // resolve successfully even if request failed }); }, /** * Fetches client region info such as countryCode and timezone. * * @returns {object} - The region info object. */ fetchClientRegionInfo: function fetchClientRegionInfo() { var _this4 = this; var services = this.webex.config.services; return this.request({ uri: services.discovery.sqdiscovery, addAuthHeader: false, headers: { 'spark-user-agent': null }, timeout: 5000 }).then(function (res) { _this4.logger.info('services: received user region info'); return res.body; }).catch(function (err) { _this4.logger.info('services: was not able to get user region info', err); // resolve successfully even if request failed }); }, /** * User activation parameter transfer object for {@link sendUserActivation}. * @typedef {object} SendUserActivationPTO * @property {string} SendUserActivationPTO.email - The email of the user. * @property {string} SendUserActivationPTO.reqId - The activation requester. * @property {string} SendUserActivationPTO.token - The client auth token. * @property {object} SendUserActivationPTO.activationOptions - Extra options to pass when sending the activation. * @property {object} SendUserActivationPTO.preloginUserId - The prelogin user id to set when sending the activation. */ /** * Send a request to activate a user using a client token. * * @param {SendUserActivationPTO} - The Parameter transfer object. * @returns {LicenseDTO} - The DTO returned from the **License** service. */ sendUserActivation: function sendUserActivation(_ref5) { var _this5 = this; var email = _ref5.email, reqId = _ref5.reqId, token = _ref5.token, activationOptions = _ref5.activationOptions, preloginUserId = _ref5.preloginUserId; this.logger.info('services: sending user activation request'); var countryCode; var timezone; // try to fetch client region info first return this.fetchClientRegionInfo().then(function (clientRegionInfo) { if (clientRegionInfo) { countryCode = clientRegionInfo.countryCode; timezone = clientRegionInfo.timezone; } // Send the user activation request to the **License** service. return _this5.request({ service: 'license', resource: 'users/activations', method: 'POST', headers: { accept: 'application/json', authorization: token, 'x-prelogin-userid': preloginUserId }, body: _objectSpread({ email: email, reqId: reqId, countryCode: countryCode, timeZone: timezone }, activationOptions), shouldRefreshAccessToken: false }); }) // On success, return the **License** user object. .then(function (_ref6) { var body = _ref6.body; return body; }) // On failure, reject with error from **License**. .catch(function (error) { return _promise.default.reject(error); }); }, /** * Updates a given service group i.e. preauth, signin, postauth with a new hostmap. * @param {string} serviceGroup - preauth, signin, postauth * @param {object} hostMap - The new hostmap to update the service group with. * @returns {Promise<void>} */ updateCatalog: function updateCatalog(serviceGroup, hostMap) { var catalog = this._getCatalog(); var serviceHostMap = this._formatReceivedHostmap(hostMap); return catalog.updateServiceUrls(serviceGroup, serviceHostMap); }, /** * simplified method to update the preauth catalog via email * * @param {object} query * @param {string} query.email - A standard format email. * @param {string} query.orgId - The user's OrgId. * @param {boolean} forceRefresh - Boolean to bypass u2c cache control header * @returns {Promise<void>} */ collectPreauthCatalog: function collectPreauthCatalog(query) { var forceRefresh = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (!query) { return this.updateServices({ from: 'limited', query: { mode: 'DEFAULT_BY_PROXIMITY' }, forceRefresh: forceRefresh }); } return this.updateServices({ from: 'limited', query: query, forceRefresh: forceRefresh }); }, /** * simplified method to update the signin catalog via email and token * @param {object} param * @param {string} param.email - must be a standard-format email * @param {string} param.token - must be a client token * @returns {Promise<void>} */ collectSigninCatalog: function collectSigninCatalog() { var _ref7 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, email = _ref7.email, token = _ref7.token, forceRefresh = _ref7.forceRefresh; if (!email) { return _promise.default.reject(new Error('`email` is required')); } if (!token) { return _promise.default.reject(new Error('`token` is required')); } return this.updateServices({ from: 'signin', query: { email: email }, token: token, forceRefresh: forceRefresh }); }, /** * Updates credentials config to utilize u2c catalog * urls. * @returns {void} */ updateCredentialsConfig: function updateCredentialsConfig() { var _this$list = this.list(true), idbroker = _this$list.idbroker, identity = _this$list.identity; if (idbroker && identity) { var _this$webex$config$cr = this.webex.config.credentials, authorizationString = _this$webex$config$cr.authorizationString, authorizeUrl = _this$webex$config$cr.authorizeUrl; // This must be set outside of the setConfig method used to assign the // idbroker and identity url values. this.webex.config.credentials.authorizeUrl = authorizationString ? authorizeUrl : "".concat(idbroker.replace(trailingSlashes, ''), "/idb/oauth2/v1/authorize"); this.webex.setConfig({ credentials: { idbroker: { url: idbroker.replace(trailingSlashes, '') // remove trailing slash }, identity: { url: identity.replace(trailingSlashes, '') // remove trailing slash } } }); } }, /** * Wait until the service catalog is available, * or reject afte ra timeout of 60 seconds. * @param {string} serviceGroup * @param {number} [timeout] - in seconds * @returns {Promise<void>} */ waitForCatalog: function waitForCatalog(serviceGroup, timeout) { var catalog = this._getCatalog(); var supertoken = this.webex.credentials.supertoken; if (serviceGroup === 'postauth' && supertoken && supertoken.access_token && !catalog.status.postauth.collecting && !catalog.status.postauth.ready) { if (!catalog.status.preauth.ready) { return this.initServiceCatalogs(); } return this.updateServices(); } return catalog.waitForCatalog(serviceGroup, timeout); }, /** * Service waiting parameter transfer object for {@link waitForService}. * * @typedef {object} WaitForServicePTO * @property {string} [WaitForServicePTO.name] - The service name. * @property {string} [WaitForServicePTO.url] - The service url. * @property {string} [WaitForServicePTO.timeout] - wait duration in seconds. */ /** * Wait until the service has been ammended to any service catalog. This * method prioritizes the service name over the service url when searching. * * @param {WaitForServicePTO} - The parameter transfer object. * @returns {Promise<string>} - Resolves to the priority host of a service. */ waitForService: function waitForService(_ref8) { var _this6 = this; var name = _ref8.name, _ref8$timeout = _ref8.timeout, timeout = _ref8$timeout === void 0 ? 5 : _ref8$timeout, url = _ref8.url; var services = this.webex.config.services; // Save memory by grabbing the catalog after there isn't a priortyURL var catalog = this._getCatalog(); var fetchFromServiceUrl = services.servicesNotNeedValidation.find(function (service) { return service === name; }); if (fetchFromServiceUrl) { return _promise.default.resolve(this._serviceUrls[name]); } var priorityUrl = this.get(name, true); var priorityUrlObj = this.getServiceFromUrl(url); if (priorityUrl || priorityUrlObj) { return _promise.default.resolve(priorityUrl || priorityUrlObj.priorityUrl); } if (catalog.isReady) { if (url) { return _promise.default.resolve(url); } this.webex.internal.metrics.submitClientMetrics(_metrics.default.JS_SDK_SERVICE_NOT_FOUND, { fields: { service_name: name } }); return _promise.default.reject(new Error("services: service '".concat(name, "' was not found in any of the catalogs"))); } return new _promise.default(function (resolve, reject) { var groupsToCheck = ['preauth', 'signin', 'postauth']; var checkCatalog = function checkCatalog(catalogGroup) { return catalog.waitForCatalog(catalogGroup, timeout).then(function () { var scopedPriorityUrl = _this6.get(name, true); var scopedPrioriryUrlObj = _this6.getServiceFromUrl(url); if (scopedPriorityUrl || scopedPrioriryUrlObj) { resolve(scopedPriorityUrl || scopedPrioriryUrlObj.priorityUrl); } }).catch(function () { return undefined; }); }; _promise.default.all(groupsToCheck.map(function (group) { return checkCatalog(group); })).then(function () { _this6.webex.internal.metrics.submitClientMetrics(_metrics.default.JS_SDK_SERVICE_NOT_FOUND, { fields: { service_name: name } }); reject(new Error("services: service '".concat(name, "' was not found after waiting"))); }); }); }, /** * Looks up the hostname in the host catalog * and replaces it with the first host if it finds it * @param {string} uri * @returns {string} uri with the host replaced */ replaceHostFromHostmap: function replaceHostFromHostmap(uri) { var url = new URL(uri); var hostCatalog = this._hostCatalog; if (!hostCatalog) { return uri; } var host = hostCatalog[url.host]; if (host && host[0]) { var newHost = host[0].host; url.host = newHost; return url.toString(); } return uri; }, /** * @private * Organize a received hostmap from a service * catalog endpoint. * @param {object} serviceHostmap * @returns {object} */ _formatReceivedHostmap: function _formatReceivedHostmap(serviceHostmap) { this._updateHostCatalog(serviceHostmap.hostCatalog); var extractId = function extractId(entry) { return entry.id.split(':')[3]; }; var formattedHostmap = []; // for each of the services in the serviceLinks, find the matching host in the catalog (0, _keys.default)(serviceHostmap.serviceLinks).forEach(function (serviceName) { var _formattedHost$hosts; var serviceUrl = serviceHostmap.serviceLinks[serviceName]; var host; try { host = new URL(serviceUrl).host; } catch (e) { return; } var matchingCatalogEntry = serviceHostmap.hostCatalog[host]; var formattedHost = { name: serviceName, defaultUrl: serviceUrl, defaultHost: host, hosts: [] }; formattedHostmap.push(formattedHost); // If the catalog does not have any hosts we will be unable to find the service ID // so can't search for other hosts if (!matchingCatalogEntry || !matchingCatalogEntry[0]) { return; } var serviceId = extractId(matchingCatalogEntry[0]); (0, _lodash.forEach)(matchingCatalogEntry, function (entry) { // The ids for all hosts within a hostCatalog entry should be the same // but for safety, only add host entries that have the same id as the first one if (extractId(entry) === serviceId) { formattedHost.hosts.push(_objectSpread(_objectSpread({}, entry), {}, { homeCluster: true })); } }); var otherHosts = []; // find the services in the host catalog that have the same id // and add them to the otherHosts (0, _lodash.forEach)(serviceHostmap.hostCatalog, function (entry) { // exclude the matching catalog entry as we have already added that if (entry === matchingCatalogEntry) { return; } (0, _lodash.forEach)(entry, function (entryHost) { // only add hosts that have the correct id if (extractId(entryHost) === serviceId) { otherHosts.push(_objectSpread(_objectSpread({}, entryHost), {}, { homeCluster: false })); } }); }); (_formattedHost$hosts = formattedHost.hosts).push.apply(_formattedHost$hosts, otherHosts); }); // update all the service urls in the host catalog this._updateServiceUrls(serviceHostmap.serviceLinks); this._updateHostCatalog(serviceHostmap.hostCatalog); return formattedHostmap; }, /** * Get the clusterId associated with a URL string. * @param {string} url * @returns {string} - Cluster ID of url provided */ getClusterId: function getClusterId(url) { var catalog = this._getCatalog(); return catalog.findClusterId(url); }, /** * Get a service value from a provided clusterId. This method will * return an object containing both the name and url of a found service. * @param {object} params * @param {string} params.clusterId - clusterId of found service * @param {boolean} [params.priorityHost] - returns priority host url if true * @param {string} [params.serviceGroup] - specify service group * @returns {object} service * @returns {string} service.name * @returns {string} service.url */ getServiceFromClusterId: function getServiceFromClusterId(params) { var catalog = this._getCatalog(); return catalog.findServiceFromClusterId(params); }, /** * @param {String} cluster the cluster containing the id * @param {UUID} [id] the id of the conversation. * If empty, just return the base URL. * @returns {String} url of the service */ getServiceUrlFromClusterId: function getServiceUrlFromClusterId() { var _ref9 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref9$cluster = _ref9.cluster, cluster = _ref9$cluster === void 0 ? 'us' : _ref9$cluster; var clusterId = cluster === 'us' ? DEFAULT_CLUSTER_IDENTIFIER : cluster; // Determine if cluster has service name (non-US clusters from hydra do not) if (clusterId.split(':').length < 4) { // Add Service to cluster identifier clusterId = "".concat(cluster, ":").concat(CLUSTER_SERVICE); } var _ref10 = this.getServiceFromClusterId({ clusterId: clusterId }) || {}, url = _ref10.url; if (!url) { throw Error("Could not find service for cluster [".concat(cluster, "]")); } return url; }, /** * Get a service object from a service url if the service url exists in the * catalog. * * @param {string} url - The url to be validated. * @returns {object} - Service object. * @returns {object.name} - The name of the service found. * @returns {object.priorityUrl} - The priority url of the found service. * @returns {object.defaultUrl} - The default url of the found service. */ getServiceFromUrl: function getServiceFromUrl() { var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var service = this._getCatalog().findServiceUrlFromUrl(url); if (!service) { return undefined; } return { name: service.name, priorityUrl: service.get(true), defaultUrl: service.get() }; }, /** * Verify that a provided url exists in the service * catalog. * @param {string} url * @returns {boolean} - true if exists, false otherwise */ isServiceUrl: function isServiceUrl(url) { var catalog = this._getCatalog(); return !!catalog.findServiceUrlFromUrl(url); }, /** * Determine if a provided url is in the catalog's allowed domains. * * @param {string} url - The url to match allowed domains against. * @returns {boolean} - True if the url provided is allowed. */ isAllowedDomainUrl: function isAllowedDomainUrl(url) { var catalog = this._getCatalog(); return !!catalog.findAllowedDomain(url); }, /** * Converts the host portion of the url from default host * to a priority host * * @param {string} url a service url that contains a default host * @returns {string} a service url that contains the top priority host. * @throws if url isn't a service url */ convertUrlToPriorityHostUrl: function convertUrlToPriorityHostUrl() { var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var data = this.getServiceFromUrl(url); if (!data) { throw Error("No service associated with url: [".concat(url, "]")); } return url.replace(data.defaultUrl, data.priorityUrl); }, /** * @private * Simplified method wrapper for sending a request to get * an updated service hostmap. * @param {object} [param] * @param {string} [param.from] - This accepts `limited` or `signin` * @param {object} [param.query] - This accepts `email`, `orgId` or `userId` key values * @param {string} [param.query.email] - must be a standard-format email * @param {string} [param.query.orgId] - must be an organization id * @param {string} [param.query.userId] - must be a user id * @param {string} [param.token] - used for signin catalog * @returns {Promise<object>} */ _fetchNewServiceHostmap: function _fetchNewServiceHostmap() { var _this7 = this; var _ref11 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, from = _ref11.from, query = _ref11.query, token = _ref11.token, forceRefresh = _ref11.forceRefresh; var service = 'u2c'; var resource = from ? "/".concat(from, "/catalog") : '/catalog'; var qs = _objectSpread(_objectSpread({}, query), {}, { format: 'hostmap' }); if (forceRefresh) { qs.timestamp = new Date().getTime(); } var requestObject = { method: 'GET', service: service, resource: resource, qs: qs }; if (token) { requestObject.headers = { authorization: token }; } return this.webex.internal.newMetrics.callDiagnosticLatencies.measureLatency(function () { return _this7.request(requestObject); }, 'internal.get.u2c.time').then(function (_ref12) { var body = _ref12.body; return _this7._formatReceivedHostmap(body); }); }, /** * Initialize the discovery services and the whitelisted services. * * @returns {void} */ initConfig: function initConfig() { // Get the catalog and destructure the services config. var catalog = this._getCatalog(); var _this$webex$config = this.webex.config, services = _this$webex$config.services, fedramp = _this$webex$config.fedramp; // Validate that the services configuration exists. if (services) { if (fedramp) { services.discovery = _serviceFedRamp.default; } // Check for discovery services. if (services.discovery) { // Format the discovery configuration into an injectable array. var formattedDiscoveryServices = (0, _keys.default)(services.discovery).map(function (key) { return { name: key, defaultUrl: services.discovery[key] }; }); // Inject formatted discovery services into services catalog. catalog.updateServiceUrls('discovery', formattedDiscoveryServices); } if (services.override) { // Format the override configuration into an injectable array. var formattedOverrideServices = (0, _keys.default)(services.override).map(function (key) { return { name: key, defaultUrl: services.override[key] }; }); // Inject formatted override services into services catalog. catalog.updateServiceUrls('override', formattedOverrideServices); } // if not fedramp, append on the commercialAllowedDomains if (!fedramp) { services.allowedDomains = (0, _lodash.union)(services.allowedDomains, _constants.COMMERCIAL_ALLOWED_DOMAINS); } // Check for allowed host domains. if (services.allowedDomains) { // Store the allowed domains as a property of the catalog. catalog.setAllowedDomains(services.allowedDomains); } // Set `validateDomains` property to match configuration this.validateDomains = services.validateDomains; } }, /** * Make the initial requests to collect the root catalogs. * * @returns {Promise<void, Error>} - Errors if the token is unavailable. */ initServiceCatalogs: function initServiceCatalogs() { var _this8 = this; this.logger.info('services: initializing initial service catalogs'); // Destructure the credentials plugin. var credentials = this.webex.credentials; // Init a promise chain. Must be done as a Promise.resolve() to allow // credentials#getOrgId() to properly throw. return _promise.default.resolve() // Get the user's OrgId. .then(function () { return credentials.getOrgId(); }) // Begin collecting the preauth/limited catalog. .then(function (orgId) { return _this8.collectPreauthCatalog({ orgId: orgId }); }).then(function () { // Validate if the token is authorized. if (credentials.canAuthorize) { // Attempt to collect the postauth catalog. return _this8.updateServices().catch(function () { _this8.initFailed = true; _this8.logger.warn('services: cannot retrieve postauth catalog'); }); } // Return a resolved promise for consistent return value. return _promise.default.resolve(); }); }, /** * Initializer * * @instance * @memberof Services * @returns {Services} */ initialize: function initialize() { var _this9 = this; var catalog = new _serviceCatalog.default(); var registry = new _serviceRegistry.default(); var state = new _serviceState.default(); this._catalogs.set(this.webex, catalog); this.registries.set(this.webex, registry); this.states.set(this.webex, state); // Listen for configuration changes once. this.listenToOnce(this.webex, 'change:config', function () { _this9.initConfig(); }); // wait for webex instance to be ready before attempting // to update the service catalogs this.listenToOnce(this.webex, 'ready', function () { var supertoken = _this9.webex.credentials.supertoken; // Validate if the supertoken exists. if (supertoken && supertoken.access_token) { _this9.initServiceCatalogs().then(function () { catalog.isReady = true; }).catch(function (error) { _this9.initFailed = true; _this9.logger.error("services: failed to init initial services when credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message)); }); } else { var email = _this9.webex.config.email; _this9.collectPreauthCatalog(email ? { email: email } : undefined).catch(function (error) { _this9.initFailed = true; _this9.logger.error("services: failed to init initial services when no credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message)); }); } }); }, version: "3.9.0" }); /* eslint-enable no-underscore-dangle */ var _default = exports.default = Services; //# sourceMappingURL=services.js.map