@webex/webex-core
Version:
Plugin handling for Cisco Webex
975 lines (942 loc) • 37.3 kB
JavaScript
"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 _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',
props: {
validateDomains: ['boolean', false, true],
initFailed: ['boolean', false, false]
},
_catalogs: new _weakMap.default(),
_activeServices: {},
_services: [],
/**
* @private
* Get the current catalog based on the assocaited
* webex instance.
* @returns {IServiceCatalog}
*/
_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 {ServiceGroup} [serviceGroup]
* @returns {string|undefined}
*/
get: function get(name, serviceGroup) {
var catalog = this._getCatalog();
var clusterId = this._activeServices[name];
var urlById = catalog.get(clusterId, serviceGroup);
var urlByName = catalog.get(name, serviceGroup);
// if both are undefined, then we cannot find the service
if (!urlById && !urlByName) {
return undefined;
}
return urlById || urlByName;
},
/**
* 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;
},
/**
* Mark a priority host service url as failed.
* This will mark the service url associated with the
* `ServiceDetail` to be removed from the its
* respective service url array, and then return the next
* viable service url from the `ServiceDetail` service url array.
* @param {string} url
* @returns {string}
*/
markFailedUrl: function markFailedUrl(url) {
var catalog = this._getCatalog();
return catalog.markFailedServiceUrl(url);
},
/**
* saves all the services from the pre and post catalog service
* @param {ActiveServices} activeServices
* @returns {void}
*/
_updateActiveServices: function _updateActiveServices(activeServices) {
this._activeServices = _objectSpread(_objectSpread({}, this._activeServices), activeServices);
},
/**
* saves the hostCatalog object
* @param {Array<Service>} services
* @returns {void}
*/
_updateServices: function _updateServices(services) {
this._services = (0, _lodash.unionBy)(services, this._services, 'id');
},
/**
* 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.updateServiceGroups(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');
// 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 {ServiceGroup} serviceGroup - preauth, signin, postauth
* @param {ServiceHostmap} 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.updateServiceGroups(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 idbrokerUrl = this.get('idbroker');
var identityUrl = this.get('identity');
if (idbrokerUrl && identityUrl) {
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(idbrokerUrl.replace(trailingSlashes, ''), "/idb/oauth2/v1/authorize");
this.webex.setConfig({
credentials: {
idbroker: {
url: idbrokerUrl.replace(trailingSlashes, '') // remove trailing slash
},
identity: {
url: identityUrl.replace(trailingSlashes, '') // remove trailing slash
}
}
});
}
},
/**
* Wait until the service catalog is available,
* or reject afte ra timeout of 60 seconds.
* @param {ServiceGroup} 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) {
var clusterId = this._activeServices[name];
return _promise.default.resolve(this.get(clusterId));
}
var priorityUrl = this.get(name);
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);
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) {
try {
return this.convertUrlToPriorityHostUrl(uri);
} catch (_unused) {
return uri;
}
},
/**
* Formats a host map entry for use in service catalog.
*
* @param {Object} entry - The host map entry to format.
* @param {string} entry.serviceName - i.e. conversation, identity, etc.
* @param {string} entry.id - The unique identifier for the service, usually clusterId.
* @param {Array<IServiceDetail>} entry.serviceUrls - The group to which the service belongs.
* @returns {Object} - The formatted host map entry.
*/
_formatHostMapEntry: function _formatHostMapEntry(_ref9) {
var id = _ref9.id,
serviceName = _ref9.serviceName,
serviceUrls = _ref9.serviceUrls;
var formattedServiceUrls = serviceUrls.map(function (serviceUrl) {
return _objectSpread({
host: new URL(serviceUrl.baseUrl).host
}, serviceUrl);
});
return {
id: id,
serviceName: serviceName,
serviceUrls: formattedServiceUrls
};
},
/**
* @private
* Organize a received hostmap from a service
* @param {ServiceHostmap} serviceHostmap
* catalog endpoint.
* @returns {Array<Service>}
*/
_formatReceivedHostmap: function _formatReceivedHostmap(_ref10) {
var _this7 = this;
var services = _ref10.services,
activeServices = _ref10.activeServices;
var formattedHostmap = services.map(function (service) {
return _this7._formatHostMapEntry(service);
});
this._updateActiveServices(activeServices);
this._updateServices(services);
return formattedHostmap;
},
/**
* Get the clusterId associated with a URL string.
* @param {string} url
* @returns {string | undefined} - 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 {ServiceGroup} [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 _ref11 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref11$cluster = _ref11.cluster,
cluster = _ref11$cluster === void 0 ? 'us' : _ref11$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 _ref12 = this.getServiceFromClusterId({
clusterId: clusterId
}) || {},
url = _ref12.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 default 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().findServiceDetailFromUrl(url);
if (!service) {
return undefined;
}
var priorityUrl = service.get();
var defaultUrl = new URL(service.serviceUrls.find(function (serviceUrl) {
return url.startsWith(serviceUrl.baseUrl);
}).baseUrl).href;
return {
name: service.serviceName,
priorityUrl: priorityUrl,
defaultUrl: defaultUrl
};
},
/**
* 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 _this8 = this;
var _ref13 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
from = _ref13.from,
query = _ref13.query,
token = _ref13.token,
forceRefresh = _ref13.forceRefresh;
var service = 'u2c';
var resource = from ? "/".concat(from, "/catalog") : '/catalog';
var qs = _objectSpread(_objectSpread({}, query || {}), {}, {
format: 'U2CV2'
});
if (forceRefresh) {
qs.timestamp = new Date().getTime();
}
var requestObject = {
method: 'GET',
service: service,
resource: resource,
qs: qs,
headers: {}
};
if (token) {
requestObject.headers = {
authorization: token
};
}
return this.webex.internal.newMetrics.callDiagnosticLatencies.measureLatency(function () {
return _this8.request(requestObject);
}, 'internal.get.u2c.time').then(function (_ref14) {
var body = _ref14.body;
return _this8._formatReceivedHostmap(body);
});
},
/**
* Initialize the discovery services and the whitelisted services.
*
* @returns {void}
*/
initConfig: function initConfig() {
var _this9 = this;
// 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 _this9._formatHostMapEntry({
id: key,
serviceName: key,
serviceUrls: [{
baseUrl: services.discovery[key],
priority: 1
}]
});
});
// Inject formatted discovery services into services catalog.
catalog.updateServiceGroups('discovery', formattedDiscoveryServices);
}
if (services.override) {
// Format the override configuration into an injectable array.
var formattedOverrideServices = (0, _keys.default)(services.override).map(function (key) {
return _this9._formatHostMapEntry({
id: key,
serviceName: key,
serviceUrls: [{
baseUrl: services.override[key],
priority: 1
}]
});
});
// Inject formatted override services into services catalog.
catalog.updateServiceGroups('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 _this10 = 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 _this10.collectPreauthCatalog({
orgId: orgId
});
}).then(function () {
// Validate if the token is authorized.
if (credentials.canAuthorize) {
// Attempt to collect the postauth catalog.
return _this10.updateServices().catch(function () {
_this10.initFailed = true;
_this10.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 _this11 = this;
var catalog = new _serviceCatalog.default();
this._catalogs.set(this.webex, catalog);
// Listen for configuration changes once.
this.listenToOnce(this.webex, 'change:config', function () {
_this11.initConfig();
});
// wait for webex instance to be ready before attempting
// to update the service catalogs
this.listenToOnce(this.webex, 'ready', function () {
var supertoken = _this11.webex.credentials.supertoken;
// Validate if the supertoken exists.
if (supertoken && supertoken.access_token) {
_this11.initServiceCatalogs().then(function () {
catalog.isReady = true;
}).catch(function (error) {
_this11.initFailed = true;
_this11.logger.error("services: failed to init initial services when credentials available, ".concat(error === null || error === void 0 ? void 0 : error.message));
});
} else {
var email = _this11.webex.config.email;
_this11.collectPreauthCatalog(email ? {
email: email
} : undefined).catch(function (error) {
_this11.initFailed = true;
_this11.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-v2.js.map