@webex/webex-core 3.0.0-bnr.5 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +6 -0
- package/babel.config.js +3 -0
- package/dist/config.js +1 -2
- package/dist/config.js.map +1 -1
- package/dist/credentials-config.js +1 -2
- package/dist/credentials-config.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/interceptors/auth.js +4 -3
- package/dist/interceptors/auth.js.map +1 -1
- package/dist/interceptors/default-options.js +4 -3
- package/dist/interceptors/default-options.js.map +1 -1
- package/dist/interceptors/embargo.js +4 -3
- package/dist/interceptors/embargo.js.map +1 -1
- package/dist/interceptors/network-timing.js +4 -3
- package/dist/interceptors/network-timing.js.map +1 -1
- package/dist/interceptors/payload-transformer.js +4 -3
- package/dist/interceptors/payload-transformer.js.map +1 -1
- package/dist/interceptors/rate-limit.js +4 -3
- package/dist/interceptors/rate-limit.js.map +1 -1
- package/dist/interceptors/redirect.js +7 -6
- package/dist/interceptors/redirect.js.map +1 -1
- package/dist/interceptors/request-event.js +9 -8
- package/dist/interceptors/request-event.js.map +1 -1
- package/dist/interceptors/request-logger.js +12 -15
- package/dist/interceptors/request-logger.js.map +1 -1
- package/dist/interceptors/request-timing.js +4 -3
- package/dist/interceptors/request-timing.js.map +1 -1
- package/dist/interceptors/response-logger.js +10 -10
- package/dist/interceptors/response-logger.js.map +1 -1
- package/dist/interceptors/user-agent.js +8 -7
- package/dist/interceptors/user-agent.js.map +1 -1
- package/dist/interceptors/webex-tracking-id.js +4 -3
- package/dist/interceptors/webex-tracking-id.js.map +1 -1
- package/dist/interceptors/webex-user-agent.js +9 -8
- package/dist/interceptors/webex-user-agent.js.map +1 -1
- package/dist/lib/batcher.js +8 -5
- package/dist/lib/batcher.js.map +1 -1
- package/dist/lib/constants.js +13 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/credentials/credentials.js +73 -35
- package/dist/lib/credentials/credentials.js.map +1 -1
- package/dist/lib/credentials/grant-errors.js +5 -5
- package/dist/lib/credentials/grant-errors.js.map +1 -1
- package/dist/lib/credentials/index.js.map +1 -1
- package/dist/lib/credentials/scope.js +26 -5
- package/dist/lib/credentials/scope.js.map +1 -1
- package/dist/lib/credentials/token-collection.js +1 -2
- package/dist/lib/credentials/token-collection.js.map +1 -1
- package/dist/lib/credentials/token.js +14 -10
- package/dist/lib/credentials/token.js.map +1 -1
- package/dist/lib/page.js +1 -2
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/services/constants.js +3 -6
- package/dist/lib/services/constants.js.map +1 -1
- package/dist/lib/services/index.js +2 -2
- package/dist/lib/services/index.js.map +1 -1
- package/dist/lib/services/interceptors/server-error.js +6 -5
- package/dist/lib/services/interceptors/server-error.js.map +1 -1
- package/dist/lib/services/interceptors/service.js +8 -5
- package/dist/lib/services/interceptors/service.js.map +1 -1
- package/dist/lib/services/metrics.js +1 -2
- package/dist/lib/services/metrics.js.map +1 -1
- package/dist/lib/services/service-catalog.js +5 -5
- package/dist/lib/services/service-catalog.js.map +1 -1
- package/dist/lib/services/service-fed-ramp.js +1 -2
- package/dist/lib/services/service-fed-ramp.js.map +1 -1
- package/dist/lib/services/service-host.js +1 -2
- package/dist/lib/services/service-host.js.map +1 -1
- package/dist/lib/services/service-registry.js +3 -4
- package/dist/lib/services/service-registry.js.map +1 -1
- package/dist/lib/services/service-state.js +1 -2
- package/dist/lib/services/service-state.js.map +1 -1
- package/dist/lib/services/service-url.js +1 -2
- package/dist/lib/services/service-url.js.map +1 -1
- package/dist/lib/services/services.js +56 -13
- package/dist/lib/services/services.js.map +1 -1
- package/dist/lib/stateless-webex-plugin.js +1 -2
- package/dist/lib/stateless-webex-plugin.js.map +1 -1
- package/dist/lib/storage/decorators.js +18 -16
- package/dist/lib/storage/decorators.js.map +1 -1
- package/dist/lib/storage/errors.js +5 -5
- package/dist/lib/storage/errors.js.map +1 -1
- package/dist/lib/storage/index.js.map +1 -1
- package/dist/lib/storage/make-webex-plugin-store.js +8 -10
- package/dist/lib/storage/make-webex-plugin-store.js.map +1 -1
- package/dist/lib/storage/make-webex-store.js.map +1 -1
- package/dist/lib/storage/memory-store-adapter.js +1 -2
- package/dist/lib/storage/memory-store-adapter.js.map +1 -1
- package/dist/lib/webex-core-plugin-mixin.js +13 -14
- package/dist/lib/webex-core-plugin-mixin.js.map +1 -1
- package/dist/lib/webex-http-error.js +4 -3
- package/dist/lib/webex-http-error.js.map +1 -1
- package/dist/lib/webex-internal-core-plugin-mixin.js +13 -14
- package/dist/lib/webex-internal-core-plugin-mixin.js.map +1 -1
- package/dist/lib/webex-plugin.js +5 -8
- package/dist/lib/webex-plugin.js.map +1 -1
- package/dist/plugins/logger.js +2 -3
- package/dist/plugins/logger.js.map +1 -1
- package/dist/webex-core.js +37 -38
- package/dist/webex-core.js.map +1 -1
- package/dist/webex-internal-core.js +1 -2
- package/dist/webex-internal-core.js.map +1 -1
- package/jest.config.js +3 -0
- package/package.json +33 -17
- package/process +1 -0
- package/src/lib/constants.js +6 -0
- package/src/lib/credentials/credentials.js +82 -40
- package/src/lib/credentials/scope.js +24 -5
- package/src/lib/credentials/token.js +9 -1
- package/src/lib/services/interceptors/server-error.js +1 -1
- package/src/lib/services/interceptors/service.js +2 -2
- package/src/lib/services/service-catalog.js +3 -1
- package/src/lib/services/services.js +46 -0
- package/src/webex-core.js +13 -1
- package/test/integration/spec/credentials/credentials.js +6 -3
- package/test/integration/spec/unit-browser/auth.js +93 -0
- package/test/integration/spec/unit-browser/token.js +122 -0
- package/test/unit/spec/_setup.js +6 -0
- package/test/unit/spec/credentials/credentials.js +181 -24
- package/test/unit/spec/credentials/scope.js +80 -0
- package/test/unit/spec/credentials/token.js +11 -1
- package/test/unit/spec/interceptors/auth.js +4 -1
- package/test/unit/spec/interceptors/embargo.js +8 -8
- package/test/unit/spec/interceptors/webex-user-agent.js +6 -6
- package/test/unit/spec/lib/page.js +3 -3
- package/test/unit/spec/services/interceptors/server-error.js +5 -5
- package/test/unit/spec/services/interceptors/service.js +21 -14
- package/test/unit/spec/services/service-catalog.js +24 -12
- package/test/unit/spec/services/service-host.js +3 -3
- package/test/unit/spec/services/service-registry.js +34 -44
- package/test/unit/spec/services/service-state.js +1 -1
- package/test/unit/spec/services/service-url.js +2 -2
- package/test/unit/spec/services/services.js +10 -4
- package/test/unit/spec/webex-core.js +12 -2
- package/test/unit/spec/webex-internal-core.js +0 -10
- package/dist/types/config.d.ts +0 -39
- package/dist/types/credentials-config.d.ts +0 -2
- package/dist/types/index.d.ts +0 -24
- package/dist/types/interceptors/auth.d.ts +0 -43
- package/dist/types/interceptors/default-options.d.ts +0 -31
- package/dist/types/interceptors/embargo.d.ts +0 -16
- package/dist/types/interceptors/network-timing.d.ts +0 -30
- package/dist/types/interceptors/payload-transformer.d.ts +0 -30
- package/dist/types/interceptors/rate-limit.d.ts +0 -61
- package/dist/types/interceptors/redirect.d.ts +0 -22
- package/dist/types/interceptors/request-event.d.ts +0 -36
- package/dist/types/interceptors/request-logger.d.ts +0 -22
- package/dist/types/interceptors/request-timing.d.ts +0 -36
- package/dist/types/interceptors/response-logger.d.ts +0 -30
- package/dist/types/interceptors/user-agent.d.ts +0 -38
- package/dist/types/interceptors/webex-tracking-id.d.ts +0 -26
- package/dist/types/interceptors/webex-user-agent.d.ts +0 -22
- package/dist/types/lib/batcher.d.ts +0 -6
- package/dist/types/lib/credentials/credentials.d.ts +0 -5
- package/dist/types/lib/credentials/grant-errors.d.ts +0 -57
- package/dist/types/lib/credentials/index.d.ts +0 -4
- package/dist/types/lib/credentials/scope.d.ts +0 -16
- package/dist/types/lib/credentials/token-collection.d.ts +0 -2
- package/dist/types/lib/credentials/token.d.ts +0 -5
- package/dist/types/lib/page.d.ts +0 -76
- package/dist/types/lib/services/constants.d.ts +0 -6
- package/dist/types/lib/services/index.d.ts +0 -10
- package/dist/types/lib/services/interceptors/server-error.d.ts +0 -16
- package/dist/types/lib/services/interceptors/service.d.ts +0 -37
- package/dist/types/lib/services/metrics.d.ts +0 -4
- package/dist/types/lib/services/service-catalog.d.ts +0 -5
- package/dist/types/lib/services/service-fed-ramp.d.ts +0 -5
- package/dist/types/lib/services/service-host.d.ts +0 -217
- package/dist/types/lib/services/service-registry.d.ts +0 -271
- package/dist/types/lib/services/service-state.d.ts +0 -46
- package/dist/types/lib/services/service-url.d.ts +0 -5
- package/dist/types/lib/services/services.d.ts +0 -5
- package/dist/types/lib/stateless-webex-plugin.d.ts +0 -48
- package/dist/types/lib/storage/decorators.d.ts +0 -15
- package/dist/types/lib/storage/errors.d.ts +0 -10
- package/dist/types/lib/storage/index.d.ts +0 -5
- package/dist/types/lib/storage/make-webex-plugin-store.d.ts +0 -8
- package/dist/types/lib/storage/make-webex-store.d.ts +0 -48
- package/dist/types/lib/storage/memory-store-adapter.d.ts +0 -18
- package/dist/types/lib/webex-core-plugin-mixin.d.ts +0 -8
- package/dist/types/lib/webex-http-error.d.ts +0 -12
- package/dist/types/lib/webex-internal-core-plugin-mixin.d.ts +0 -8
- package/dist/types/lib/webex-plugin.d.ts +0 -5
- package/dist/types/plugins/logger.d.ts +0 -2
- package/dist/types/webex-core.d.ts +0 -31
- package/dist/types/webex-internal-core.d.ts +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/webex-core",
|
|
3
|
-
"version": "3.0.0-bnr.5",
|
|
4
3
|
"description": "Plugin handling for Cisco Webex",
|
|
5
4
|
"license": "MIT",
|
|
6
5
|
"contributors": [
|
|
@@ -30,32 +29,49 @@
|
|
|
30
29
|
]
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
32
|
+
"@babel/core": "^7.17.10",
|
|
33
33
|
"@sinonjs/fake-timers": "^6.0.1",
|
|
34
|
-
"@webex/
|
|
35
|
-
"@webex/
|
|
36
|
-
"@webex/
|
|
37
|
-
"@webex/
|
|
38
|
-
"@webex/test-helper-
|
|
39
|
-
"@webex/test-helper-
|
|
34
|
+
"@webex/babel-config-legacy": "0.0.0",
|
|
35
|
+
"@webex/eslint-config-legacy": "0.0.0",
|
|
36
|
+
"@webex/jest-config-legacy": "0.0.0",
|
|
37
|
+
"@webex/legacy-tools": "0.0.0",
|
|
38
|
+
"@webex/test-helper-chai": "3.0.0",
|
|
39
|
+
"@webex/test-helper-make-local-url": "3.0.0",
|
|
40
|
+
"@webex/test-helper-mocha": "3.0.0",
|
|
41
|
+
"@webex/test-helper-mock-webex": "3.0.0",
|
|
42
|
+
"@webex/test-helper-refresh-callback": "3.0.0",
|
|
43
|
+
"@webex/test-helper-test-users": "3.0.0",
|
|
40
44
|
"chai": "^4.3.4",
|
|
41
45
|
"chai-as-promised": "^7.1.1",
|
|
46
|
+
"eslint": "^8.24.0",
|
|
47
|
+
"prettier": "^2.7.1",
|
|
42
48
|
"sinon": "^9.2.4"
|
|
43
49
|
},
|
|
44
50
|
"dependencies": {
|
|
45
|
-
"@webex/common": "3.0.0
|
|
46
|
-
"@webex/common-timers": "3.0.0
|
|
47
|
-
"@webex/http-core": "3.0.0
|
|
48
|
-
"@webex/internal-plugin-device": "3.0.0
|
|
49
|
-
"@webex/plugin-logger": "3.0.0
|
|
50
|
-
"@webex/storage-adapter-spec": "3.0.0
|
|
51
|
-
"@webex/webex-core": "3.0.0-bnr.5",
|
|
51
|
+
"@webex/common": "3.0.0",
|
|
52
|
+
"@webex/common-timers": "3.0.0",
|
|
53
|
+
"@webex/http-core": "3.0.0",
|
|
54
|
+
"@webex/internal-plugin-device": "3.0.0",
|
|
55
|
+
"@webex/plugin-logger": "3.0.0",
|
|
56
|
+
"@webex/storage-adapter-spec": "3.0.0",
|
|
52
57
|
"ampersand-collection": "^2.0.2",
|
|
53
58
|
"ampersand-events": "^2.0.2",
|
|
54
59
|
"ampersand-state": "^5.0.3",
|
|
55
60
|
"core-decorators": "^0.20.0",
|
|
56
61
|
"crypto-js": "^4.1.1",
|
|
57
|
-
"jsonwebtoken": "^
|
|
62
|
+
"jsonwebtoken": "^9.0.0",
|
|
58
63
|
"lodash": "^4.17.21",
|
|
59
64
|
"uuid": "^3.3.2"
|
|
60
|
-
}
|
|
61
|
-
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "yarn build:src",
|
|
68
|
+
"build:src": "webex-legacy-tools build -dest \"./dist\" -src \"./src\" -js -ts -maps",
|
|
69
|
+
"deploy:npm": "yarn npm publish",
|
|
70
|
+
"test": "yarn test:style && yarn test:unit && yarn test:integration && yarn test:browser",
|
|
71
|
+
"test:browser": "webex-legacy-tools test --integration --runner karma",
|
|
72
|
+
"test:integration": "webex-legacy-tools test --integration --runner mocha",
|
|
73
|
+
"test:style": "eslint ./src/**/*.*",
|
|
74
|
+
"test:unit": "webex-legacy-tools test --unit --runner jest"
|
|
75
|
+
},
|
|
76
|
+
"version": "3.0.0"
|
|
77
|
+
}
|
package/process
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {browser: true};
|
|
@@ -13,10 +13,11 @@ import {clone, cloneDeep, isObject, isEmpty} from 'lodash';
|
|
|
13
13
|
import WebexPlugin from '../webex-plugin';
|
|
14
14
|
import {persist, waitForValue} from '../storage/decorators';
|
|
15
15
|
|
|
16
|
-
import grantErrors from './grant-errors';
|
|
17
|
-
import {filterScope, sortScope} from './scope';
|
|
16
|
+
import grantErrors, {OAuthError} from './grant-errors';
|
|
17
|
+
import {filterScope, diffScopes, sortScope} from './scope';
|
|
18
18
|
import Token from './token';
|
|
19
19
|
import TokenCollection from './token-collection';
|
|
20
|
+
import {METRICS} from '../constants';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* @class
|
|
@@ -48,6 +49,25 @@ const Credentials = WebexPlugin.extend({
|
|
|
48
49
|
return Boolean(this.supertoken && this.supertoken.canRefresh);
|
|
49
50
|
},
|
|
50
51
|
},
|
|
52
|
+
isUnverifiedGuest: {
|
|
53
|
+
deps: ['supertoken'],
|
|
54
|
+
/**
|
|
55
|
+
* Returns true if the user is an unverified guest
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
fn() {
|
|
59
|
+
let isGuest = false;
|
|
60
|
+
try {
|
|
61
|
+
isGuest =
|
|
62
|
+
JSON.parse(base64.decode(this.supertoken.access_token.split('.')[1])).user_type ===
|
|
63
|
+
'guest';
|
|
64
|
+
} catch {
|
|
65
|
+
/* the non-guest token is formatted differently so catch is expected */
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return isGuest;
|
|
69
|
+
},
|
|
70
|
+
},
|
|
51
71
|
},
|
|
52
72
|
|
|
53
73
|
props: {
|
|
@@ -240,8 +260,15 @@ const Credentials = WebexPlugin.extend({
|
|
|
240
260
|
*/
|
|
241
261
|
downscope(scope) {
|
|
242
262
|
return this.supertoken.downscope(scope).catch((reason) => {
|
|
243
|
-
|
|
263
|
+
const failReason = reason?.body ?? reason;
|
|
264
|
+
this.logger.warn(`credentials: failed to downscope supertoken to "${scope}"`, failReason);
|
|
244
265
|
this.logger.trace(`credentials: falling back to supertoken for ${scope}`);
|
|
266
|
+
this.webex.internal.metrics.submitClientMetrics(METRICS.JS_SDK_CREDENTIALS_DOWNSCOPE_FAILED, {
|
|
267
|
+
fields: {
|
|
268
|
+
requestedScope: scope,
|
|
269
|
+
failReason,
|
|
270
|
+
},
|
|
271
|
+
});
|
|
245
272
|
|
|
246
273
|
return Promise.resolve(new Token({scope, ...this.supertoken.serialize()}), {
|
|
247
274
|
parent: this,
|
|
@@ -322,12 +349,12 @@ const Credentials = WebexPlugin.extend({
|
|
|
322
349
|
}
|
|
323
350
|
|
|
324
351
|
if (!scope) {
|
|
325
|
-
scope = filterScope('spark:kms', this.
|
|
352
|
+
scope = filterScope('spark:kms', this.supertoken.scope);
|
|
326
353
|
}
|
|
327
354
|
|
|
328
355
|
scope = sortScope(scope);
|
|
329
356
|
|
|
330
|
-
if (scope === sortScope(this.
|
|
357
|
+
if (scope === sortScope(this.supertoken.scope)) {
|
|
331
358
|
return Promise.resolve(this.supertoken);
|
|
332
359
|
}
|
|
333
360
|
|
|
@@ -477,42 +504,10 @@ const Credentials = WebexPlugin.extend({
|
|
|
477
504
|
|
|
478
505
|
return supertoken
|
|
479
506
|
.refresh()
|
|
480
|
-
.then((st) => {
|
|
481
|
-
// clear refresh timer
|
|
482
|
-
if (this.refreshTimer) {
|
|
483
|
-
clearTimeout(this.refreshTimer);
|
|
484
|
-
this.unset('refreshTimer');
|
|
485
|
-
}
|
|
486
|
-
this.supertoken = st;
|
|
487
|
-
|
|
488
|
-
return Promise.all(
|
|
489
|
-
tokens.map((token) =>
|
|
490
|
-
this.downscope(token.scope)
|
|
491
|
-
// eslint-disable-next-line max-nested-callbacks
|
|
492
|
-
.then((t) => {
|
|
493
|
-
this.logger.info(`credentials: revoking token for ${token.scope}`);
|
|
494
|
-
|
|
495
|
-
return token
|
|
496
|
-
.revoke()
|
|
497
|
-
.catch((err) => {
|
|
498
|
-
this.logger.warn('credentials: failed to revoke user token', err);
|
|
499
|
-
})
|
|
500
|
-
.then(() => {
|
|
501
|
-
this.userTokens.remove(token.scope);
|
|
502
|
-
this.userTokens.add(t);
|
|
503
|
-
});
|
|
504
|
-
})
|
|
505
|
-
)
|
|
506
|
-
);
|
|
507
|
-
})
|
|
508
|
-
.then(() => {
|
|
509
|
-
this.scheduleRefresh(this.supertoken.expires);
|
|
510
|
-
})
|
|
511
507
|
.catch((error) => {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
// Error: The refresh token provided is expired, revoked, malformed, or invalid. Hence emit an event to the client, an opportunity to logout.
|
|
508
|
+
if (error instanceof OAuthError) {
|
|
509
|
+
// Error: super token refresh failed with 400 status code.
|
|
510
|
+
// Hence emit an event to the client, an opportunity to logout.
|
|
516
511
|
this.unset('supertoken');
|
|
517
512
|
while (this.userTokens.models.length) {
|
|
518
513
|
try {
|
|
@@ -525,6 +520,53 @@ const Credentials = WebexPlugin.extend({
|
|
|
525
520
|
}
|
|
526
521
|
|
|
527
522
|
return Promise.reject(error);
|
|
523
|
+
})
|
|
524
|
+
.then((st) => {
|
|
525
|
+
// clear refresh timer
|
|
526
|
+
if (this.refreshTimer) {
|
|
527
|
+
clearTimeout(this.refreshTimer);
|
|
528
|
+
this.unset('refreshTimer');
|
|
529
|
+
}
|
|
530
|
+
this.supertoken = st;
|
|
531
|
+
|
|
532
|
+
const invalidScopes = diffScopes(this.config.scope, st.scope);
|
|
533
|
+
|
|
534
|
+
if (invalidScopes !== '') {
|
|
535
|
+
this.logger.warn(
|
|
536
|
+
`credentials: "${invalidScopes}" scope(s) are invalid because not listed in the supertoken, they will be excluded from user token requests.`
|
|
537
|
+
);
|
|
538
|
+
this.webex.internal.metrics.submitClientMetrics(
|
|
539
|
+
METRICS.JS_SDK_CREDENTIALS_TOKEN_REFRESH_SCOPE_MISMATCH,
|
|
540
|
+
{fields: {invalidScopes}}
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return Promise.all(
|
|
545
|
+
tokens.map((token) => {
|
|
546
|
+
const tokenScope = filterScope(diffScopes(token.scope, st.scope), token.scope);
|
|
547
|
+
|
|
548
|
+
return (
|
|
549
|
+
this.downscope(tokenScope)
|
|
550
|
+
// eslint-disable-next-line max-nested-callbacks
|
|
551
|
+
.then((t) => {
|
|
552
|
+
this.logger.info(`credentials: revoking token for ${token.scope}`);
|
|
553
|
+
|
|
554
|
+
return token
|
|
555
|
+
.revoke()
|
|
556
|
+
.catch((err) => {
|
|
557
|
+
this.logger.warn('credentials: failed to revoke user token', err);
|
|
558
|
+
})
|
|
559
|
+
.then(() => {
|
|
560
|
+
this.userTokens.remove(token.scope);
|
|
561
|
+
this.userTokens.add(t);
|
|
562
|
+
});
|
|
563
|
+
})
|
|
564
|
+
);
|
|
565
|
+
})
|
|
566
|
+
);
|
|
567
|
+
})
|
|
568
|
+
.then(() => {
|
|
569
|
+
this.scheduleRefresh(this.supertoken.expires);
|
|
528
570
|
});
|
|
529
571
|
},
|
|
530
572
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import {difference} from 'lodash';
|
|
6
|
+
|
|
7
|
+
const SCOPE_SEPARATOR = ' ';
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
10
|
* sorts a list of scopes
|
|
7
11
|
* @param {string} scope
|
|
@@ -12,12 +16,12 @@ export function sortScope(scope) {
|
|
|
12
16
|
return '';
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
return scope.split(
|
|
19
|
+
return scope.split(SCOPE_SEPARATOR).sort().join(SCOPE_SEPARATOR);
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
* sorts a list of scopes and filters the specified scope
|
|
20
|
-
* @param {string} toFilter
|
|
24
|
+
* @param {string|string[]} toFilter
|
|
21
25
|
* @param {string} scope
|
|
22
26
|
* @returns {string}
|
|
23
27
|
*/
|
|
@@ -25,10 +29,25 @@ export function filterScope(toFilter, scope) {
|
|
|
25
29
|
if (!scope) {
|
|
26
30
|
return '';
|
|
27
31
|
}
|
|
32
|
+
const toFilterArr = Array.isArray(toFilter) ? toFilter : [toFilter];
|
|
28
33
|
|
|
29
34
|
return scope
|
|
30
|
-
.split(
|
|
31
|
-
.filter((item) => item
|
|
35
|
+
.split(SCOPE_SEPARATOR)
|
|
36
|
+
.filter((item) => !toFilterArr.includes(item))
|
|
32
37
|
.sort()
|
|
33
|
-
.join(
|
|
38
|
+
.join(SCOPE_SEPARATOR);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns a string containing all items in scopeA that are not in scopeB, or an empty string if there are none.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} scopeA
|
|
45
|
+
* @param {string} scopeB
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
export function diffScopes(scopeA, scopeB) {
|
|
49
|
+
const a = scopeA?.split(SCOPE_SEPARATOR) ?? [];
|
|
50
|
+
const b = scopeB?.split(SCOPE_SEPARATOR) ?? [];
|
|
51
|
+
|
|
52
|
+
return difference(a, b).sort().join(SCOPE_SEPARATOR);
|
|
34
53
|
}
|
|
@@ -9,7 +9,7 @@ import {safeSetTimeout} from '@webex/common-timers';
|
|
|
9
9
|
import WebexHttpError from '../webex-http-error';
|
|
10
10
|
import WebexPlugin from '../webex-plugin';
|
|
11
11
|
|
|
12
|
-
import {sortScope} from './scope';
|
|
12
|
+
import {sortScope, diffScopes} from './scope';
|
|
13
13
|
import grantErrors, {OAuthError} from './grant-errors';
|
|
14
14
|
|
|
15
15
|
/* eslint-disable camelcase */
|
|
@@ -259,6 +259,14 @@ const Token = WebexPlugin.extend({
|
|
|
259
259
|
return Promise.reject(new Error('cannot downscope access token'));
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
if (diffScopes(scope, this.config.scope) !== '') {
|
|
263
|
+
return Promise.reject(
|
|
264
|
+
new Error(
|
|
265
|
+
`new scope (${scope}) is not subset of the available scopes (${this.config.scope})`
|
|
266
|
+
)
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
262
270
|
// Since we're going to use scope as the index in our token collection, it's
|
|
263
271
|
// important scopes are always deterministically specified.
|
|
264
272
|
if (scope) {
|
|
@@ -36,11 +36,11 @@ export default class ServiceInterceptor extends Interceptor {
|
|
|
36
36
|
|
|
37
37
|
// Destructure commonly referenced namespaces.
|
|
38
38
|
const {services} = this.webex.internal;
|
|
39
|
-
const {service, resource} = options;
|
|
39
|
+
const {service, resource, waitForServiceTimeout} = options;
|
|
40
40
|
|
|
41
41
|
// Attempt to collect the service url.
|
|
42
42
|
return services
|
|
43
|
-
.waitForService({name: service})
|
|
43
|
+
.waitForService({name: service, timeout: waitForServiceTimeout})
|
|
44
44
|
.then((serviceUrl) => {
|
|
45
45
|
// Generate the combined service url and resource.
|
|
46
46
|
options.uri = this.generateUri(serviceUrl, resource);
|
|
@@ -413,6 +413,8 @@ const ServiceCatalog = AmpState.extend({
|
|
|
413
413
|
resolve();
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
+
const validatedTimeout = typeof timeout === 'number' && timeout >= 0 ? timeout : 60;
|
|
417
|
+
|
|
416
418
|
const timeoutTimer = setTimeout(
|
|
417
419
|
() =>
|
|
418
420
|
reject(
|
|
@@ -420,7 +422,7 @@ const ServiceCatalog = AmpState.extend({
|
|
|
420
422
|
`services: timeout occured while waiting for '${serviceGroup}' catalog to populate`
|
|
421
423
|
)
|
|
422
424
|
),
|
|
423
|
-
|
|
425
|
+
validatedTimeout * 1000
|
|
424
426
|
);
|
|
425
427
|
|
|
426
428
|
this.once(serviceGroup, () => {
|
|
@@ -12,6 +12,15 @@ import fedRampServices from './service-fed-ramp';
|
|
|
12
12
|
|
|
13
13
|
const trailingSlashes = /(?:^\/)|(?:\/$)/;
|
|
14
14
|
|
|
15
|
+
// The default cluster when one is not provided (usually as 'US' from hydra)
|
|
16
|
+
export const DEFAULT_CLUSTER = 'urn:TEAM:us-east-2_a';
|
|
17
|
+
// The default service name for convo (currently identityLookup due to some weird CSB issue)
|
|
18
|
+
export const DEFAULT_CLUSTER_SERVICE = 'identityLookup';
|
|
19
|
+
|
|
20
|
+
const CLUSTER_SERVICE = process.env.WEBEX_CONVERSATION_CLUSTER_SERVICE || DEFAULT_CLUSTER_SERVICE;
|
|
21
|
+
const DEFAULT_CLUSTER_IDENTIFIER =
|
|
22
|
+
process.env.WEBEX_CONVERSATION_DEFAULT_CLUSTER || `${DEFAULT_CLUSTER}:${CLUSTER_SERVICE}`;
|
|
23
|
+
|
|
15
24
|
/* eslint-disable no-underscore-dangle */
|
|
16
25
|
/**
|
|
17
26
|
* @class
|
|
@@ -49,6 +58,8 @@ const Services = WebexPlugin.extend({
|
|
|
49
58
|
|
|
50
59
|
_serviceUrls: null,
|
|
51
60
|
|
|
61
|
+
_hostCatalog: null,
|
|
62
|
+
|
|
52
63
|
/**
|
|
53
64
|
* Get the registry associated with this webex instance.
|
|
54
65
|
*
|
|
@@ -157,6 +168,15 @@ const Services = WebexPlugin.extend({
|
|
|
157
168
|
this._serviceUrls = {...this._serviceUrls, ...serviceUrls};
|
|
158
169
|
},
|
|
159
170
|
|
|
171
|
+
/**
|
|
172
|
+
* saves the hostCatalog object
|
|
173
|
+
* @param {Object} hostCatalog
|
|
174
|
+
* @returns {void}
|
|
175
|
+
*/
|
|
176
|
+
_updateHostCatalog(hostCatalog) {
|
|
177
|
+
this._hostCatalog = {...this._hostCatalog, ...hostCatalog};
|
|
178
|
+
},
|
|
179
|
+
|
|
160
180
|
/**
|
|
161
181
|
* Update a list of `serviceUrls` to the most current
|
|
162
182
|
* catalog via the defined `discoveryUrl` then returns the current
|
|
@@ -662,6 +682,7 @@ const Services = WebexPlugin.extend({
|
|
|
662
682
|
* @returns {object}
|
|
663
683
|
*/
|
|
664
684
|
_formatReceivedHostmap(serviceHostmap) {
|
|
685
|
+
this._updateHostCatalog(serviceHostmap.hostCatalog);
|
|
665
686
|
// map the host catalog items to a formatted hostmap
|
|
666
687
|
const formattedHostmap = Object.keys(serviceHostmap.hostCatalog).reduce((accumulator, key) => {
|
|
667
688
|
if (serviceHostmap.hostCatalog[key].length === 0) {
|
|
@@ -720,6 +741,7 @@ const Services = WebexPlugin.extend({
|
|
|
720
741
|
// update all the service urls in the host catalog
|
|
721
742
|
|
|
722
743
|
this._updateServiceUrls(serviceHostmap.serviceLinks);
|
|
744
|
+
this._updateHostCatalog(serviceHostmap.hostCatalog);
|
|
723
745
|
|
|
724
746
|
return formattedHostmap;
|
|
725
747
|
},
|
|
@@ -752,6 +774,30 @@ const Services = WebexPlugin.extend({
|
|
|
752
774
|
return catalog.findServiceFromClusterId(params);
|
|
753
775
|
},
|
|
754
776
|
|
|
777
|
+
/**
|
|
778
|
+
* @param {String} cluster the cluster containing the id
|
|
779
|
+
* @param {UUID} [id] the id of the conversation.
|
|
780
|
+
* If empty, just return the base URL.
|
|
781
|
+
* @returns {String} url of the service
|
|
782
|
+
*/
|
|
783
|
+
getServiceUrlFromClusterId({cluster = 'us'} = {}) {
|
|
784
|
+
let clusterId = cluster === 'us' ? DEFAULT_CLUSTER_IDENTIFIER : cluster;
|
|
785
|
+
|
|
786
|
+
// Determine if cluster has service name (non-US clusters from hydra do not)
|
|
787
|
+
if (clusterId.split(':').length < 4) {
|
|
788
|
+
// Add Service to cluster identifier
|
|
789
|
+
clusterId = `${cluster}:${CLUSTER_SERVICE}`;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const {url} = this.getServiceFromClusterId({clusterId}) || {};
|
|
793
|
+
|
|
794
|
+
if (!url) {
|
|
795
|
+
throw Error(`Could not find service for cluster [${cluster}]`);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
return url;
|
|
799
|
+
},
|
|
800
|
+
|
|
755
801
|
/**
|
|
756
802
|
* Get a service object from a service url if the service url exists in the
|
|
757
803
|
* catalog.
|
package/src/webex-core.js
CHANGED
|
@@ -6,7 +6,12 @@ import {EventEmitter} from 'events';
|
|
|
6
6
|
import util from 'util';
|
|
7
7
|
|
|
8
8
|
import {proxyEvents, retry, transferEvents} from '@webex/common';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
HttpStatusInterceptor,
|
|
11
|
+
defaults as requestDefaults,
|
|
12
|
+
protoprepareFetchOptions as prepareFetchOptions,
|
|
13
|
+
setTimingsAndFetch as _setTimingsAndFetch,
|
|
14
|
+
} from '@webex/http-core';
|
|
10
15
|
import {defaultsDeep, get, isFunction, isString, last, merge, omit, set, unset} from 'lodash';
|
|
11
16
|
import AmpState from 'ampersand-state';
|
|
12
17
|
import uuid from 'uuid';
|
|
@@ -402,6 +407,13 @@ const WebexCore = AmpState.extend({
|
|
|
402
407
|
interceptors: ints,
|
|
403
408
|
});
|
|
404
409
|
|
|
410
|
+
this.prepareFetchOptions = prepareFetchOptions({
|
|
411
|
+
json: true,
|
|
412
|
+
interceptors: ints,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
this.setTimingsAndFetch = _setTimingsAndFetch;
|
|
416
|
+
|
|
405
417
|
let sessionId = `${get(this, 'config.trackingIdPrefix', 'webex-js-sdk')}_${get(
|
|
406
418
|
this,
|
|
407
419
|
'config.trackingIdBase',
|
|
@@ -110,12 +110,15 @@ describe('webex-core', () => {
|
|
|
110
110
|
});
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
-
browserOnly(it)('throws without a refresh callback', () => {
|
|
113
|
+
browserOnly(it)('throws without a refresh callback', async () => {
|
|
114
114
|
const webex = new WebexCore({
|
|
115
115
|
credentials: user.token,
|
|
116
116
|
});
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
await webex.credentials.refresh().then(() => {
|
|
118
|
+
assert(false, 'resolved, should have thrown');
|
|
119
|
+
}).catch((err) => {
|
|
120
|
+
assert(false);
|
|
121
|
+
});
|
|
119
122
|
});
|
|
120
123
|
|
|
121
124
|
browserOnly(it)('refreshes with a refresh callback', () => {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* eslint-disable camelcase */
|
|
6
|
+
|
|
7
|
+
import chai from 'chai';
|
|
8
|
+
import chaiAsPromised from 'chai-as-promised';
|
|
9
|
+
import sinon from 'sinon';
|
|
10
|
+
import {browserOnly, nodeOnly} from '@webex/test-helper-mocha';
|
|
11
|
+
import Logger from '@webex/plugin-logger';
|
|
12
|
+
import MockWebex from '@webex/test-helper-mock-webex';
|
|
13
|
+
import {AuthInterceptor, config, Credentials, WebexHttpError, Token} from '@webex/webex-core';
|
|
14
|
+
import {cloneDeep, merge} from 'lodash';
|
|
15
|
+
import Metrics from '@webex/internal-plugin-metrics';
|
|
16
|
+
|
|
17
|
+
const {assert} = chai;
|
|
18
|
+
|
|
19
|
+
chai.use(chaiAsPromised);
|
|
20
|
+
sinon.assert.expose(chai.assert, {prefix: ''});
|
|
21
|
+
|
|
22
|
+
describe('webex-core', () => {
|
|
23
|
+
describe('Interceptors', () => {
|
|
24
|
+
describe('AuthInterceptor', () => {
|
|
25
|
+
let interceptor, webex;
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
webex = new MockWebex({
|
|
29
|
+
children: {
|
|
30
|
+
credentials: Credentials,
|
|
31
|
+
logger: Logger,
|
|
32
|
+
metrics: Metrics,
|
|
33
|
+
},
|
|
34
|
+
config: merge(cloneDeep(config), {credentials: {client_secret: 'fake'}}),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
webex.credentials.supertoken = new Token(
|
|
38
|
+
{
|
|
39
|
+
access_token: 'ST1',
|
|
40
|
+
token_type: 'Bearer',
|
|
41
|
+
},
|
|
42
|
+
{parent: webex}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
interceptor = Reflect.apply(AuthInterceptor.create, webex, []);
|
|
46
|
+
sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
describe('#onResponseError()', () => {
|
|
51
|
+
describe('when the server responds with 401', () => {
|
|
52
|
+
browserOnly(it)('refreshes the access token and replays the request', () => {
|
|
53
|
+
webex.config.credentials.refreshCallback = sinon.stub().returns(
|
|
54
|
+
Promise.resolve({
|
|
55
|
+
access_token: 'ST2',
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
webex.credentials.supertoken = new Token(
|
|
60
|
+
{
|
|
61
|
+
access_token: 'ST1',
|
|
62
|
+
refresh_token: 'RT1',
|
|
63
|
+
},
|
|
64
|
+
{parent: webex}
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const err = new WebexHttpError.Unauthorized({
|
|
68
|
+
statusCode: 401,
|
|
69
|
+
options: {
|
|
70
|
+
headers: {
|
|
71
|
+
trackingid: 'blarg',
|
|
72
|
+
},
|
|
73
|
+
uri: `${config.services.discovery.hydra}/ping`,
|
|
74
|
+
},
|
|
75
|
+
body: {
|
|
76
|
+
error: 'fake error',
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
assert.notCalled(webex.request);
|
|
81
|
+
|
|
82
|
+
return interceptor.onResponseError(err.options, err).then(() => {
|
|
83
|
+
// once for replay
|
|
84
|
+
assert.calledOnce(webex.request);
|
|
85
|
+
assert.equal(webex.credentials.supertoken.access_token, 'ST2');
|
|
86
|
+
assert.equal(webex.request.args[0][0].replayCount, 1);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
});
|