@webex/webex-core 3.0.0-beta.31 → 3.0.0-beta.310

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.
@@ -11,6 +11,7 @@ import {inBrowser} from '@webex/common';
11
11
  import FakeTimers from '@sinonjs/fake-timers';
12
12
  import {skipInBrowser} from '@webex/test-helper-mocha';
13
13
  import Logger from '@webex/plugin-logger';
14
+ import Metrics, {config} from '@webex/internal-plugin-metrics';
14
15
 
15
16
  /* eslint camelcase: [0] */
16
17
 
@@ -59,6 +60,34 @@ describe('webex-core', () => {
59
60
  });
60
61
  });
61
62
 
63
+ describe('#isUnverifiedGuest', () => {
64
+ let credentials;
65
+ let webex;
66
+ beforeEach('generate the webex instance', () => {
67
+ webex = new MockWebex();
68
+ credentials = new Credentials(undefined, {parent: webex});
69
+ });
70
+
71
+ it('should have #isUnverifiedGuest', () => {
72
+ assert.exists(credentials.isUnverifiedGuest);
73
+ });
74
+
75
+ it('should get the user status and return as a boolean', () => {
76
+ credentials.set('supertoken', 'AT');
77
+ assert.isFalse(credentials.isUnverifiedGuest);
78
+ });
79
+
80
+ it('should get guest user ', () => {
81
+ credentials.set('supertoken', 'eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX3R5cGUiOiJndWVzdCJ9');
82
+ assert.isTrue(credentials.isUnverifiedGuest);
83
+ });
84
+
85
+ it('should get login user ', () => {
86
+ credentials.set('supertoken', 'dGhpc2lzbm90YXJlYWx1c2VydG9rZW4=');
87
+ assert.isFalse(credentials.isUnverifiedGuest);
88
+ });
89
+ });
90
+
62
91
  describe('#canAuthorize', () => {
63
92
  it('indicates if the current state has enough information to populate an auth header, even if a token refresh or token downscope is required', () => {
64
93
  const webex = new MockWebex();
@@ -417,7 +446,11 @@ describe('webex-core', () => {
417
446
  });
418
447
 
419
448
  it('schedules a refreshTimer', () => {
420
- const webex = new MockWebex();
449
+ const webex = new MockWebex({
450
+ children: {
451
+ metrics: Metrics,
452
+ },
453
+ });
421
454
  const supertoken = makeToken(webex, {
422
455
  access_token: 'ST',
423
456
  refresh_token: 'RT',
@@ -430,6 +463,7 @@ describe('webex-core', () => {
430
463
  });
431
464
 
432
465
  sinon.stub(supertoken, 'refresh').returns(Promise.resolve(supertoken2));
466
+ sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
433
467
  const credentials = new Credentials(supertoken, {parent: webex});
434
468
 
435
469
  webex.trigger('change:config');
@@ -464,7 +498,19 @@ describe('webex-core', () => {
464
498
  });
465
499
 
466
500
  describe('#getUserToken()', () => {
467
- it('resolves with the supertoken if the supertoken matches the requested scopes');
501
+ it('resolves with the supertoken if the supertoken matches the requested scopes', () => {
502
+ const webex = new MockWebex();
503
+ const credentials = new Credentials(undefined, {parent: webex});
504
+
505
+ webex.trigger('change:config');
506
+ const st = makeToken(webex, {access_token: 'ST', scope: 'scope1'});
507
+
508
+ credentials.set({
509
+ supertoken: st,
510
+ });
511
+
512
+ return credentials.getUserToken('scope1').then((result) => assert.deepEqual(result, st));
513
+ });
468
514
 
469
515
  it('resolves with the token identified by the specified scopes', () => {
470
516
  const webex = new MockWebex();
@@ -492,6 +538,25 @@ describe('webex-core', () => {
492
538
  ]);
493
539
  });
494
540
 
541
+ it('uses the supertoken.scope instead of the config.scope for downscope', () => {
542
+ const webex = new MockWebex();
543
+ const credentials = new Credentials(undefined, {parent: webex});
544
+
545
+ webex.trigger('change:config');
546
+ const st = makeToken(webex, {access_token: 'ST', scope: 'scope1 spark:kms'});
547
+
548
+ credentials.set({
549
+ supertoken: st,
550
+ scope: 'invalidScope scope1',
551
+ });
552
+
553
+ sinon.stub(credentials, 'downscope').returns(Promise.resolve());
554
+
555
+ return credentials.getUserToken().then(() => {
556
+ assert.calledWith(credentials.downscope, 'scope1');
557
+ });
558
+ });
559
+
495
560
  describe('when no matching token is found', () => {
496
561
  it('downscopes the supertoken', () => {
497
562
  const webex = new MockWebex();
@@ -529,13 +594,13 @@ describe('webex-core', () => {
529
594
  it('resolves with a token containing all but the kms scopes', () => {
530
595
  const webex = new MockWebex();
531
596
 
532
- webex.config.credentials.scope = 'scope1 spark:kms';
533
597
  const credentials = new Credentials(undefined, {parent: webex});
534
598
 
535
599
  webex.trigger('change:config');
536
600
 
537
601
  credentials.supertoken = makeToken(webex, {
538
602
  access_token: 'ST',
603
+ scope: 'scope1 spark:kms',
539
604
  });
540
605
 
541
606
  // const t2 = makeToken(webex, {
@@ -562,9 +627,11 @@ describe('webex-core', () => {
562
627
  const webex = new MockWebex({
563
628
  children: {
564
629
  logger: Logger,
630
+ metrics: Metrics,
565
631
  },
566
632
  });
567
633
 
634
+ webex.config.metrics = config.metrics;
568
635
  webex.config.credentials.scope = 'scope1 spark:kms';
569
636
  const credentials = new Credentials(undefined, {parent: webex});
570
637
 
@@ -574,9 +641,11 @@ describe('webex-core', () => {
574
641
  access_token: 'ST',
575
642
  });
576
643
 
577
- sinon
578
- .stub(credentials.supertoken, 'downscope')
579
- .returns(Promise.reject(new Error('downscope failed')));
644
+ const failReason = 'downscope failed';
645
+ sinon.stub(credentials.supertoken, 'downscope').returns(Promise.reject(failReason));
646
+
647
+ sinon.stub(credentials.logger, 'warn').callsFake(() => {});
648
+ sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
580
649
 
581
650
  const t1 = makeToken(webex, {
582
651
  access_token: 'AT1',
@@ -587,14 +656,27 @@ describe('webex-core', () => {
587
656
  userTokens: [t1],
588
657
  });
589
658
 
590
- return credentials
591
- .getUserToken('scope2')
592
- .then((t) => assert.equal(t.access_token, credentials.supertoken.access_token));
659
+ return credentials.getUserToken('scope2').then((t) => {
660
+ assert.equal(t.access_token, credentials.supertoken.access_token);
661
+ assert.calledWith(
662
+ credentials.logger.warn,
663
+ 'credentials: failed to downscope supertoken to "scope2"'
664
+ );
665
+ assert.calledWith(
666
+ webex.internal.metrics.submitClientMetrics,
667
+ 'JS_SDK_CREDENTIALS_DOWNSCOPE_FAILED',
668
+ {fields: {failReason, requestedScope: 'scope2'}}
669
+ );
670
+ });
593
671
  });
594
672
  });
595
673
 
596
674
  it('is blocked while a token refresh is inflight', () => {
597
- const webex = new MockWebex();
675
+ const webex = new MockWebex({
676
+ children: {
677
+ metrics: Metrics,
678
+ },
679
+ });
598
680
 
599
681
  webex.config.credentials.scope = 'scope1 spark:kms';
600
682
  const credentials = new Credentials(undefined, {parent: webex});
@@ -620,6 +702,7 @@ describe('webex-core', () => {
620
702
  const at2 = makeToken(webex, {access_token: 'ST2ATD'});
621
703
 
622
704
  sinon.stub(supertoken2, 'downscope').returns(Promise.resolve(at2));
705
+ sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
623
706
 
624
707
  return Promise.all([
625
708
  credentials.refresh(),
@@ -750,18 +833,24 @@ describe('webex-core', () => {
750
833
 
751
834
  describe('#refresh()', () => {
752
835
  it('refreshes and downscopes the supertoken, and revokes previous tokens', () => {
753
- const webex = new MockWebex();
836
+ const webex = new MockWebex({
837
+ children: {
838
+ metrics: Metrics,
839
+ },
840
+ });
754
841
  const credentials = new Credentials(undefined, {parent: webex});
755
842
 
756
843
  webex.trigger('change:config');
757
844
  const st = makeToken(webex, {
758
845
  access_token: 'ST',
759
846
  refresh_token: 'RT',
847
+ scope: 'scope1 scope2',
760
848
  });
761
849
 
762
850
  const st2 = makeToken(webex, {
763
851
  access_token: 'ST2',
764
852
  refresh_token: 'RT2',
853
+ scope: 'scope1 scope2',
765
854
  });
766
855
 
767
856
  const t1 = makeToken(webex, {
@@ -778,6 +867,7 @@ describe('webex-core', () => {
778
867
  sinon.stub(st, 'refresh').returns(Promise.resolve(st2));
779
868
  sinon.stub(t1, 'revoke').returns(Promise.resolve());
780
869
  sinon.spy(credentials, 'scheduleRefresh');
870
+ sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
781
871
 
782
872
  credentials.set({
783
873
  supertoken: st,
@@ -797,7 +887,11 @@ describe('webex-core', () => {
797
887
  });
798
888
 
799
889
  it('refreshes and downscopes the supertoken even if revocation of previous token fails', () => {
800
- const webex = new MockWebex();
890
+ const webex = new MockWebex({
891
+ children: {
892
+ metrics: Metrics,
893
+ },
894
+ });
801
895
  const credentials = new Credentials(undefined, {parent: webex});
802
896
 
803
897
  webex.trigger('change:config');
@@ -809,6 +903,7 @@ describe('webex-core', () => {
809
903
  const st2 = makeToken(webex, {
810
904
  access_token: 'ST2',
811
905
  refresh_token: 'RT2',
906
+ scope: 'scope1 scope2',
812
907
  });
813
908
 
814
909
  const t1 = makeToken(webex, {
@@ -825,6 +920,7 @@ describe('webex-core', () => {
825
920
  sinon.stub(st, 'refresh').returns(Promise.resolve(st2));
826
921
  sinon.stub(t1, 'revoke').returns(Promise.reject());
827
922
  sinon.spy(credentials, 'scheduleRefresh');
923
+ sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
828
924
 
829
925
  credentials.set({
830
926
  supertoken: st,
@@ -847,6 +943,7 @@ describe('webex-core', () => {
847
943
  const webex = new MockWebex({
848
944
  children: {
849
945
  logger: Logger,
946
+ metrics: Metrics,
850
947
  },
851
948
  });
852
949
  const credentials = new Credentials(undefined, {parent: webex});
@@ -855,9 +952,11 @@ describe('webex-core', () => {
855
952
  const st = makeToken(webex, {
856
953
  access_token: 'ST',
857
954
  refresh_token: 'RT',
955
+ scope: '',
858
956
  });
859
957
 
860
958
  sinon.stub(st, 'refresh').returns(Promise.resolve(makeToken(webex, {access_token: 'ST2'})));
959
+ sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
861
960
 
862
961
  const t1 = makeToken(webex, {
863
962
  access_token: 'AT1',
@@ -873,7 +972,7 @@ describe('webex-core', () => {
873
972
  });
874
973
 
875
974
  it('allows #getUserToken() to be revoked, but #getUserToken() promises will not resolve until the suport token has been refreshed', () => {
876
- const webex = new MockWebex();
975
+ const webex = new MockWebex({children: {metrics: Metrics}});
877
976
  const credentials = new Credentials(undefined, {parent: webex});
878
977
 
879
978
  webex.trigger('change:config');
@@ -899,6 +998,7 @@ describe('webex-core', () => {
899
998
 
900
999
  sinon.stub(st1, 'refresh').returns(Promise.resolve(st2));
901
1000
  sinon.stub(st2, 'downscope').returns(Promise.resolve(t2));
1001
+ sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
902
1002
 
903
1003
  credentials.set({
904
1004
  supertoken: st1,
@@ -955,6 +1055,61 @@ describe('webex-core', () => {
955
1055
  assert.calledWith(triggerSpy, sinon.match('client:InvalidRequestError'));
956
1056
  });
957
1057
  });
1058
+
1059
+ it('exclude invalid scopes from user token, log and call metrics when fetched supertoken scope mismatch with the configured scope', () => {
1060
+ const webex = new MockWebex({
1061
+ children: {
1062
+ logger: Logger,
1063
+ metrics: Metrics,
1064
+ },
1065
+ });
1066
+ const credentials = new Credentials(undefined, {parent: webex});
1067
+
1068
+ webex.trigger('change:config');
1069
+ const st = makeToken(webex, {
1070
+ access_token: 'ST',
1071
+ refresh_token: 'RT',
1072
+ });
1073
+
1074
+ const st2 = makeToken(webex, {
1075
+ access_token: 'ST2',
1076
+ refresh_token: 'RT2',
1077
+ scope: 'scope1',
1078
+ });
1079
+
1080
+ const userToken = makeToken(webex, {
1081
+ access_token: 'AT1',
1082
+ scope: 'scope1 invalidScope1',
1083
+ });
1084
+
1085
+ credentials.set({
1086
+ supertoken: st,
1087
+ userTokens: [userToken],
1088
+ });
1089
+ const invalidScopes = 'invalidScope1 invalidScope2';
1090
+ credentials.config.scope = `scope1 ${invalidScopes}`;
1091
+
1092
+ sinon.stub(st2, 'downscope').returns(Promise.resolve());
1093
+ sinon.stub(st, 'refresh').returns(Promise.resolve(st2));
1094
+ sinon.spy(credentials, 'downscope');
1095
+ sinon.spy(credentials, 'scheduleRefresh');
1096
+
1097
+ sinon.stub(credentials.logger, 'warn').callsFake(() => {});
1098
+ sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
1099
+
1100
+ return credentials.refresh().then(() => {
1101
+ assert.calledWith(
1102
+ credentials.logger.warn,
1103
+ `credentials: "${invalidScopes}" scope(s) are invalid because not listed in the supertoken, they will be excluded from user token requests.`
1104
+ );
1105
+ assert.calledWith(
1106
+ webex.internal.metrics.submitClientMetrics,
1107
+ 'JS_SDK_CREDENTIALS_TOKEN_REFRESH_SCOPE_MISMATCH',
1108
+ {fields: {invalidScopes}}
1109
+ );
1110
+ assert.calledWith(credentials.downscope, 'scope1');
1111
+ });
1112
+ });
958
1113
  });
959
1114
 
960
1115
  describe('#scheduleRefresh()', () => {
@@ -0,0 +1,55 @@
1
+ import {assert} from '@webex/test-helper-chai';
2
+ import {sortScope, filterScope, diffScopes} from '@webex/webex-core/src/lib/credentials/scope';
3
+
4
+ describe('webex-core', () => {
5
+ describe('scope utils', () => {
6
+ describe('sortScope', () => {
7
+ it('should sort scopes alphabetically', () => {
8
+ assert.equal(sortScope(undefined), '');
9
+ assert.equal(sortScope(''), '');
10
+ assert.equal(sortScope('a'), 'a');
11
+ assert.equal(sortScope('b c a'), 'a b c');
12
+ });
13
+ });
14
+
15
+ describe('filterScope', () => {
16
+ it('should filter out one scope from the original scope and sort the result', () => {
17
+ assert.equal(filterScope('a', undefined), '');
18
+ assert.equal(filterScope('a', ''), '');
19
+ assert.equal(filterScope('a', 'a'), '');
20
+ assert.equal(filterScope('a', 'a b c'), 'b c');
21
+ assert.equal(filterScope('c', 'a c b'), 'a b');
22
+ });
23
+
24
+ it('should filter out a list of scopes from the original scope and sort the result', () => {
25
+ assert.equal(filterScope([], 'a'), 'a');
26
+ assert.equal(filterScope(['a', 'b'], undefined), '');
27
+ assert.equal(filterScope(['a', 'b'], ''), '');
28
+ assert.equal(filterScope(['a', 'b'], 'a'), '');
29
+ assert.equal(filterScope(['a', 'b'], 'a b c'), 'c');
30
+ assert.equal(filterScope(['a', 'd'], 'a c a b'), 'b c');
31
+ });
32
+ });
33
+
34
+ describe('diffScopes', () => {
35
+ it('should return an empty string, if all items in the first scope are contained in the second scope', () => {
36
+ assert.deepEqual(diffScopes(undefined, undefined), '');
37
+ assert.deepEqual(diffScopes(undefined, ''), '');
38
+ assert.deepEqual(diffScopes('', undefined), '');
39
+ assert.deepEqual(diffScopes('', ''), '');
40
+ assert.deepEqual(diffScopes('a', 'a'), '');
41
+ assert.deepEqual(diffScopes('a b c', 'a b c'), '');
42
+ assert.deepEqual(diffScopes(undefined, 'a b c'), '');
43
+ assert.deepEqual(diffScopes('a b c', 'a b c d'), '');
44
+ });
45
+
46
+ it('should return a string containing all items in the first scope that are not in the second scope', () => {
47
+ assert.deepEqual(diffScopes('a', undefined), 'a');
48
+ assert.deepEqual(diffScopes('a', 'b'), 'a');
49
+ assert.deepEqual(diffScopes('a b c', 'a b'), 'c');
50
+ assert.deepEqual(diffScopes('a b c d', 'a b c'), 'd');
51
+ assert.deepEqual(diffScopes('a b c', undefined), 'a b c');
52
+ });
53
+ });
54
+ });
55
+ });
@@ -12,6 +12,7 @@ import Logger from '@webex/plugin-logger';
12
12
  import MockWebex from '@webex/test-helper-mock-webex';
13
13
  import {AuthInterceptor, config, Credentials, WebexHttpError, Token} from '@webex/webex-core';
14
14
  import {cloneDeep, merge} from 'lodash';
15
+ import Metrics from '@webex/internal-plugin-metrics';
15
16
 
16
17
  const {assert} = chai;
17
18
 
@@ -28,6 +29,7 @@ describe('webex-core', () => {
28
29
  children: {
29
30
  credentials: Credentials,
30
31
  logger: Logger,
32
+ metrics: Metrics,
31
33
  },
32
34
  config: merge(cloneDeep(config), {credentials: {client_secret: 'fake'}}),
33
35
  });
@@ -41,6 +43,7 @@ describe('webex-core', () => {
41
43
  );
42
44
 
43
45
  interceptor = Reflect.apply(AuthInterceptor.create, webex, []);
46
+ sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
44
47
  });
45
48
 
46
49
  describe('#onRequest()', () => {
@@ -5,6 +5,7 @@ import chai from 'chai';
5
5
  import chaiAsPromised from 'chai-as-promised';
6
6
  import sinon from 'sinon';
7
7
  import {ServiceInterceptor} from '@webex/webex-core';
8
+ import CONFIG from '../../../../../src/config';
8
9
 
9
10
  const {assert} = chai;
10
11
 
@@ -26,6 +27,7 @@ describe('webex-core', () => {
26
27
  service: 'example',
27
28
  serviceUrl: 'https://www.example-service.com/',
28
29
  uri: 'https://www.example-uri.com/',
30
+ waitForServiceTimeout: 11,
29
31
  };
30
32
 
31
33
  options = {};
@@ -107,6 +109,7 @@ describe('webex-core', () => {
107
109
 
108
110
  options.service = fixture.service;
109
111
  options.resource = fixture.resource;
112
+ options.timeout = fixture.waitForServiceTimeout;
110
113
  });
111
114
 
112
115
  it('should normalize the options', () =>
@@ -116,9 +119,12 @@ describe('webex-core', () => {
116
119
  interceptor.onRequest(options).then(() => assert.called(interceptor.validateOptions)));
117
120
 
118
121
  it('should attempt to collect the service url', () =>
119
- interceptor
120
- .onRequest(options)
121
- .then(() => assert.calledWith(waitForService, {name: options.service})));
122
+ interceptor.onRequest(options).then(
123
+ assert.calledWith(waitForService, {
124
+ name: options.service,
125
+ timeout: options.waitForServiceTimeout,
126
+ })
127
+ ));
122
128
 
123
129
  describe('when the service url was collected successfully', () => {
124
130
  beforeEach('generate additional mocks', () => {});
@@ -54,6 +54,18 @@ describe('Webex', () => {
54
54
  });
55
55
  });
56
56
 
57
+ describe('#request', () => {
58
+ it('exists', () => {
59
+ assert.property(webex, 'request');
60
+ });
61
+ });
62
+
63
+ describe('#prepareFetchOptions', () => {
64
+ it('exists', () => {
65
+ assert.property(webex, 'prepareFetchOptions');
66
+ });
67
+ });
68
+
57
69
  describe('#initialize()', () => {
58
70
  it('initializes without arguments', () => {
59
71
  let webex;