@microsoft/windows-admin-center-sdk 6.0.0 → 6.1.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.
Files changed (176) hide show
  1. package/3rdPartyDisclosure.html +10 -13
  2. package/README.md +110 -33
  3. package/angular/fesm2022/msft-sme-angular.mjs +1235 -1213
  4. package/angular/fesm2022/msft-sme-angular.mjs.map +1 -1
  5. package/angular/index.d.ts +29 -14
  6. package/angular/package.json +1 -1
  7. package/core/azure/azure-manager.d.ts +15 -3
  8. package/core/azure/azure-manager.d.ts.map +1 -1
  9. package/core/azure/azure-manager.js +29 -10
  10. package/core/azure/azure-manager.js.map +1 -1
  11. package/core/azure/models/aadAppConfig-data.d.ts +16 -0
  12. package/core/azure/models/aadAppConfig-data.d.ts.map +1 -1
  13. package/core/azure/models/aadAppConfig-data.js.map +1 -1
  14. package/core/base/environment.d.ts +1 -1
  15. package/core/base/environment.d.ts.map +1 -1
  16. package/core/base/environment.js.map +1 -1
  17. package/core/base/utilities.d.ts +10 -2
  18. package/core/base/utilities.d.ts.map +1 -1
  19. package/core/base/utilities.js +45 -25
  20. package/core/base/utilities.js.map +1 -1
  21. package/core/data/gateway-connection.d.ts.map +1 -1
  22. package/core/data/gateway-connection.js +1 -0
  23. package/core/data/gateway-connection.js.map +1 -1
  24. package/core/diagnostics/sme-web-telemetry.d.ts +17 -1
  25. package/core/diagnostics/sme-web-telemetry.d.ts.map +1 -1
  26. package/core/diagnostics/sme-web-telemetry.js +16 -0
  27. package/core/diagnostics/sme-web-telemetry.js.map +1 -1
  28. package/core/enterprise/enterprise-models.d.ts +10 -4
  29. package/core/enterprise/enterprise-models.d.ts.map +1 -1
  30. package/core/enterprise/enterprise-models.js +9 -4
  31. package/core/enterprise/enterprise-models.js.map +1 -1
  32. package/core/index.d.ts +5 -3
  33. package/core/index.d.ts.map +1 -1
  34. package/core/index.js +2 -1
  35. package/core/index.js.map +1 -1
  36. package/core/manifest/environment-modules.d.ts +2 -2
  37. package/core/manifest/environment-modules.d.ts.map +1 -1
  38. package/core/manifest/environment-modules.js.map +1 -1
  39. package/core/manifest/gateway-url-validator.d.ts +17 -0
  40. package/core/manifest/gateway-url-validator.d.ts.map +1 -0
  41. package/core/manifest/gateway-url-validator.js +56 -0
  42. package/core/manifest/gateway-url-validator.js.map +1 -0
  43. package/core/manifest/manifest-loader.d.ts +47 -11
  44. package/core/manifest/manifest-loader.d.ts.map +1 -1
  45. package/core/manifest/manifest-loader.js +141 -72
  46. package/core/manifest/manifest-loader.js.map +1 -1
  47. package/core/manifest/module-schema.json +1 -1
  48. package/core/performance/performance-tracker.d.ts +2 -0
  49. package/core/performance/performance-tracker.d.ts.map +1 -1
  50. package/core/performance/performance-tracker.js +18 -0
  51. package/core/performance/performance-tracker.js.map +1 -1
  52. package/core/rpc/azure/rpc-azure-model.d.ts +19 -1
  53. package/core/rpc/azure/rpc-azure-model.d.ts.map +1 -1
  54. package/core/rpc/azure/rpc-azure-model.js.map +1 -1
  55. package/core/rpc/forward/rpc-forward-auto-client.d.ts +5 -0
  56. package/core/rpc/forward/rpc-forward-auto-client.d.ts.map +1 -1
  57. package/core/rpc/forward/rpc-forward-auto-client.js +13 -0
  58. package/core/rpc/forward/rpc-forward-auto-client.js.map +1 -1
  59. package/core/rpc/resource-data-changed/rpc-resource-data-changed-client.d.ts +16 -0
  60. package/core/rpc/resource-data-changed/rpc-resource-data-changed-client.d.ts.map +1 -0
  61. package/core/rpc/resource-data-changed/rpc-resource-data-changed-client.js +18 -0
  62. package/core/rpc/resource-data-changed/rpc-resource-data-changed-client.js.map +1 -0
  63. package/core/rpc/resource-data-changed/rpc-resource-data-changed-handler.d.ts +8 -0
  64. package/core/rpc/resource-data-changed/rpc-resource-data-changed-handler.d.ts.map +1 -0
  65. package/core/rpc/resource-data-changed/rpc-resource-data-changed-handler.js +2 -0
  66. package/core/rpc/resource-data-changed/rpc-resource-data-changed-handler.js.map +1 -0
  67. package/core/rpc/resource-data-changed/rpc-resource-data-changed-model.d.ts +18 -0
  68. package/core/rpc/resource-data-changed/rpc-resource-data-changed-model.d.ts.map +1 -0
  69. package/core/rpc/resource-data-changed/rpc-resource-data-changed-model.js +8 -0
  70. package/core/rpc/resource-data-changed/rpc-resource-data-changed-model.js.map +1 -0
  71. package/core/rpc/resource-data-changed/rpc-resource-data-changed-subject-server.d.ts +27 -0
  72. package/core/rpc/resource-data-changed/rpc-resource-data-changed-subject-server.d.ts.map +1 -0
  73. package/core/rpc/resource-data-changed/rpc-resource-data-changed-subject-server.js +23 -0
  74. package/core/rpc/resource-data-changed/rpc-resource-data-changed-subject-server.js.map +1 -0
  75. package/core/rpc/rpc-forwarder.d.ts +16 -2
  76. package/core/rpc/rpc-forwarder.d.ts.map +1 -1
  77. package/core/rpc/rpc-forwarder.js +50 -6
  78. package/core/rpc/rpc-forwarder.js.map +1 -1
  79. package/core/security/authorization-manager.d.ts +126 -4
  80. package/core/security/authorization-manager.d.ts.map +1 -1
  81. package/core/security/authorization-manager.js +381 -49
  82. package/core/security/authorization-manager.js.map +1 -1
  83. package/core/security/connection-manager.d.ts +2 -1
  84. package/core/security/connection-manager.d.ts.map +1 -1
  85. package/core/security/connection-manager.js +10 -6
  86. package/core/security/connection-manager.js.map +1 -1
  87. package/core/security/tool-condition-validator.d.ts +8 -3
  88. package/core/security/tool-condition-validator.d.ts.map +1 -1
  89. package/core/security/tool-condition-validator.js +43 -28
  90. package/core/security/tool-condition-validator.js.map +1 -1
  91. package/core/version.js +1 -1
  92. package/core/version.js.map +1 -1
  93. package/core/virtualization/virtualization-client.d.ts +47 -78
  94. package/core/virtualization/virtualization-client.d.ts.map +1 -1
  95. package/core/virtualization/virtualization-client.js +57 -77
  96. package/core/virtualization/virtualization-client.js.map +1 -1
  97. package/core/virtualization/virtualization-models.d.ts +9 -8
  98. package/core/virtualization/virtualization-models.d.ts.map +1 -1
  99. package/core/virtualization/virtualization-models.js +7 -5
  100. package/core/virtualization/virtualization-models.js.map +1 -1
  101. package/core/virtualization/virtualization-unavailable-error.d.ts +1 -1
  102. package/core/virtualization/virtualization-unavailable-error.js +1 -1
  103. package/core/virtualization/virtualization-unavailable-error.js.map +1 -1
  104. package/package.json +12 -17
  105. package/{react-sdk-core → react}/hooks/useAppContextInit.d.ts +2 -0
  106. package/react/hooks/useAppContextInit.d.ts.map +1 -0
  107. package/{react-sdk-core → react}/hooks/useAppContextInit.js +18 -2
  108. package/{react-sdk-core → react}/index.js +1 -1
  109. package/react/services/BaseNavigationService.d.ts.map +1 -0
  110. package/{react-sdk-core → react}/services/BaseNavigationService.js +4 -0
  111. package/{react-sdk-core → react}/version.d.ts.map +1 -1
  112. package/react/version.js +3 -0
  113. package/{react-sdk-core → react}/vite/smeCoreStringsPlugin.d.ts +1 -1
  114. package/{react-sdk-core → react}/vite/smeCoreStringsPlugin.js +1 -1
  115. package/{react-sdk-core → react}/vite/smeManifestResourcesPlugin.d.ts +1 -1
  116. package/{react-sdk-core → react}/vite/smeManifestResourcesPlugin.js +1 -1
  117. package/{react-sdk-core → react}/vite/smePowerShellPlugin.d.ts +1 -1
  118. package/{react-sdk-core → react}/vite/smePowerShellPlugin.js +1 -1
  119. package/styles/css/main-v2.css +1 -1
  120. package/styles/package.json +1 -1
  121. package/wac-cli/dist/angular20/configs/ci-build-yml-config.js +3 -2
  122. package/wac-cli/dist/angular20/configs/ci-build-yml-config.js.map +1 -1
  123. package/wac-cli/dist/angular20/upgrader/nvmrc-engines-upgrader.js +2 -2
  124. package/wac-cli/dist/create-react/main.js +3 -2
  125. package/wac-cli/dist/create-react/main.js.map +1 -1
  126. package/wac-cli/module-template/build/pipelines/ci-build.yml +2 -2
  127. package/wac-cli/module-template/build/pipelines/pull-request.yml +2 -2
  128. package/wac-cli/templates/internal/main.ts +2 -2
  129. package/wac-cli/templates/internal/package.json +26 -14
  130. package/wac-cli/templates/react-template/package.json +3 -2
  131. package/wac-cli/templates/react-template/src/App.tsx +1 -1
  132. package/wac-cli/templates/react-template/src/hooks/useServicesData.ts +1 -1
  133. package/wac-cli/templates/react-template/vite.config.ts +1 -1
  134. package/wac-cli/templates/wac-template/angular.json +8 -13
  135. package/wac-cli/templates/wac-template/package.json +26 -14
  136. package/wac-cli/templates/wac-template/src/main.ts +2 -2
  137. package/react-sdk-core/hooks/useAppContextInit.d.ts.map +0 -1
  138. package/react-sdk-core/services/BaseNavigationService.d.ts.map +0 -1
  139. package/react-sdk-core/version.js +0 -3
  140. /package/{react-sdk-core → react}/components/SplashScreen.d.ts +0 -0
  141. /package/{react-sdk-core → react}/components/SplashScreen.d.ts.map +0 -0
  142. /package/{react-sdk-core → react}/components/SplashScreen.js +0 -0
  143. /package/{react-sdk-core → react}/context/AppContextProvider.d.ts +0 -0
  144. /package/{react-sdk-core → react}/context/AppContextProvider.d.ts.map +0 -0
  145. /package/{react-sdk-core → react}/context/AppContextProvider.js +0 -0
  146. /package/{react-sdk-core → react}/hooks/useAppContext.d.ts +0 -0
  147. /package/{react-sdk-core → react}/hooks/useAppContext.d.ts.map +0 -0
  148. /package/{react-sdk-core → react}/hooks/useAppContext.js +0 -0
  149. /package/{react-sdk-core → react}/hooks/useSkeletonStyles.d.ts +0 -0
  150. /package/{react-sdk-core → react}/hooks/useSkeletonStyles.d.ts.map +0 -0
  151. /package/{react-sdk-core → react}/hooks/useSkeletonStyles.js +0 -0
  152. /package/{react-sdk-core → react}/hooks/useWacTheme.d.ts +0 -0
  153. /package/{react-sdk-core → react}/hooks/useWacTheme.d.ts.map +0 -0
  154. /package/{react-sdk-core → react}/hooks/useWacTheme.js +0 -0
  155. /package/{react-sdk-core → react}/index.d.ts +0 -0
  156. /package/{react-sdk-core → react}/index.d.ts.map +0 -0
  157. /package/{react-sdk-core → react}/services/AppContextService.d.ts +0 -0
  158. /package/{react-sdk-core → react}/services/AppContextService.d.ts.map +0 -0
  159. /package/{react-sdk-core → react}/services/AppContextService.js +0 -0
  160. /package/{react-sdk-core → react}/services/BaseNavigationService.d.ts +0 -0
  161. /package/{react-sdk-core → react}/services/NavigationService.d.ts +0 -0
  162. /package/{react-sdk-core → react}/services/NavigationService.d.ts.map +0 -0
  163. /package/{react-sdk-core → react}/services/NavigationService.js +0 -0
  164. /package/{react-sdk-core → react}/services/Polyfills.d.ts +0 -0
  165. /package/{react-sdk-core → react}/services/Polyfills.d.ts.map +0 -0
  166. /package/{react-sdk-core → react}/services/Polyfills.js +0 -0
  167. /package/{react-sdk-core → react}/version.d.ts +0 -0
  168. /package/{react-sdk-core → react}/vite/index.d.ts +0 -0
  169. /package/{react-sdk-core → react}/vite/index.d.ts.map +0 -0
  170. /package/{react-sdk-core → react}/vite/index.js +0 -0
  171. /package/{react-sdk-core → react}/vite/log.d.ts +0 -0
  172. /package/{react-sdk-core → react}/vite/log.d.ts.map +0 -0
  173. /package/{react-sdk-core → react}/vite/log.js +0 -0
  174. /package/{react-sdk-core → react}/vite/smeCoreStringsPlugin.d.ts.map +0 -0
  175. /package/{react-sdk-core → react}/vite/smeManifestResourcesPlugin.d.ts.map +0 -0
  176. /package/{react-sdk-core → react}/vite/smePowerShellPlugin.d.ts.map +0 -0
@@ -4,6 +4,7 @@ import { Crypto } from '../data/crypto';
4
4
  import { headerConstants } from '../data/http-constants';
5
5
  import { Net } from '../data/net';
6
6
  import { Logging } from '../diagnostics/logging';
7
+ import { EnvironmentModule } from '../manifest/environment-modules';
7
8
  import { RpcForwarder, RpcServiceForwarder } from '../rpc/rpc-forwarder';
8
9
  import { SignOnManager } from './sign-on-manager';
9
10
  /**
@@ -11,6 +12,7 @@ import { SignOnManager } from './sign-on-manager';
11
12
  */
12
13
  export class AuthorizationManager extends RpcServiceForwarder {
13
14
  authorize;
15
+ options;
14
16
  /**
15
17
  * Cache key for session storage of authorization cache
16
18
  */
@@ -26,6 +28,7 @@ export class AuthorizationManager extends RpcServiceForwarder {
26
28
  setNodeTokenError: 'setNodeTokenError',
27
29
  setJeaContext: 'setJeaContext',
28
30
  clearNodeTokens: 'clearNodeTokens',
31
+ clearScopedTokens: 'clearScopedTokens',
29
32
  getNewToken: 'getNewToken',
30
33
  secureToken: 'secureToken',
31
34
  encrypt: 'encrypt',
@@ -49,6 +52,10 @@ export class AuthorizationManager extends RpcServiceForwarder {
49
52
  * The JSON Web Key. Single string with JSON.stringify format.
50
53
  */
51
54
  jwk;
55
+ /**
56
+ * The canonical origin that owns the cached credentials.
57
+ */
58
+ gatewayOrigin;
52
59
  /**
53
60
  * Credentials expiration time, used to determine the lifetime of new tokens. Value in milliseconds.
54
61
  */
@@ -88,6 +95,21 @@ export class AuthorizationManager extends RpcServiceForwarder {
88
95
  * The token awaiter subject.
89
96
  */
90
97
  tokenAwaiter;
98
+ /** Direct child frames participating in the current shared credential request. */
99
+ sharedTokenRequesters = new Set();
100
+ /**
101
+ * Token awaiters for credential requests from specific child requesters, keyed by requester RPC name.
102
+ * Used only when requester scoping is enabled, so one requester's prompt is never shared with another.
103
+ */
104
+ requesterTokenAwaiters = new Map();
105
+ /**
106
+ * Shell instance only: credentials that apply to a single requester, keyed by requester RPC name.
107
+ */
108
+ scopedTokens = {};
109
+ /** Child-only scoped tokens that currently take precedence over shared tokens in the effective public state. */
110
+ scopedTokenState = null;
111
+ /** Child-only shared tokens retained beneath scoped tokens and restored when scoped state clears. */
112
+ sharedTokenState = null;
91
113
  /**
92
114
  * The subject observable of sign on token awaiter.
93
115
  */
@@ -134,6 +156,20 @@ export class AuthorizationManager extends RpcServiceForwarder {
134
156
  get gatewayJwk() {
135
157
  return this.jwk;
136
158
  }
159
+ /**
160
+ * Binds cached credentials to a canonical gateway origin.
161
+ * Credentials and gateway encryption state are cleared before a changed origin is recorded.
162
+ */
163
+ setGatewayOrigin(origin) {
164
+ if (this.gatewayOrigin === origin) {
165
+ return;
166
+ }
167
+ this.resetAllTokens();
168
+ this.jwk = null;
169
+ this.gatewayJwkChangedSubject.next(null);
170
+ this.gatewayOrigin = origin;
171
+ this.updateCache();
172
+ }
137
173
  /**
138
174
  * Gets an observable the emits when the authorization token is ready
139
175
  */
@@ -149,10 +185,12 @@ export class AuthorizationManager extends RpcServiceForwarder {
149
185
  * Initializes a new instance of the Authorization Manager class
150
186
  * @param authorize An AuthorizationHandler with which to retrieve user credentials
151
187
  * @param rpc The rpc to forward auth requests to a parent window
188
+ * @param options Optional behavior switches
152
189
  */
153
- constructor(authorize, rpc) {
190
+ constructor(authorize, rpc, options = {}) {
154
191
  super('authorization-manager', rpc);
155
192
  this.authorize = authorize;
193
+ this.options = options;
156
194
  this.gatewayJwkChangedSubject = new Subject();
157
195
  this.gatewayJwkChanged = this.gatewayJwkChangedSubject.asObservable();
158
196
  this.credentialExpirationChangedSubject = new Subject();
@@ -166,7 +204,9 @@ export class AuthorizationManager extends RpcServiceForwarder {
166
204
  try {
167
205
  const cachedData = JSON.parse(cachedDataString);
168
206
  // verify data and set properties
207
+ this.gatewayOrigin = cachedData.gatewayOrigin || null;
169
208
  this.jwk = cachedData.jwk || null;
209
+ this.scopedTokens = cachedData.scopedTokens || {};
170
210
  this.manageAsToken = cachedData.manageAsToken || null;
171
211
  this.nodeTokens = cachedData.nodeTokens || {};
172
212
  this.nodePowershellEndpoints = cachedData.nodePowershellEndpoints || {};
@@ -179,6 +219,19 @@ export class AuthorizationManager extends RpcServiceForwarder {
179
219
  }
180
220
  this.signOnManager = new SignOnManager(() => this.refreshSignOnToken());
181
221
  }
222
+ /**
223
+ * Attributes requests to direct children, preserving requester names relayed by hosted shells.
224
+ * Creates requester-only error data when the caller supplies none.
225
+ *
226
+ * @param errorData the error data received from the child, if any.
227
+ * @param sourceName the RPC name of the direct child that sent the request.
228
+ */
229
+ static withRequesterName(errorData, sourceName) {
230
+ if (!sourceName || EnvironmentModule.isShellName(sourceName)) {
231
+ return errorData;
232
+ }
233
+ return { ...(errorData || {}), requesterName: sourceName };
234
+ }
182
235
  /**
183
236
  * defines the conditions under which the AuthorizationManager can handle an ajax error
184
237
  */
@@ -274,20 +327,33 @@ export class AuthorizationManager extends RpcServiceForwarder {
274
327
  this.updateCache();
275
328
  }
276
329
  getNewToken(nodeNames, errorData) {
330
+ return this.requestNewToken(nodeNames, errorData, this.getRequesterAwaiterKey(errorData));
331
+ }
332
+ requestNewToken(nodeNames, errorData, requesterKey, sourceName) {
277
333
  // if we are already awaiting a token, then hook into the current request and try using that token
278
- if (this.tokenAwaiter && !this.tokenAwaiter.closed) {
279
- return this.tokenAwaiter;
334
+ const pendingAwaiter = this.getPendingAwaiter(requesterKey);
335
+ if (!requesterKey) {
336
+ if (!pendingAwaiter) {
337
+ this.sharedTokenRequesters.clear();
338
+ }
339
+ if (sourceName) {
340
+ this.sharedTokenRequesters.add(sourceName);
341
+ }
342
+ }
343
+ if (pendingAwaiter) {
344
+ return pendingAwaiter;
280
345
  }
281
346
  // ensure input is an array only if not null.
282
347
  if (nodeNames && !Array.isArray(nodeNames)) {
283
348
  nodeNames = [nodeNames];
284
349
  }
285
350
  // define a new subject for multiple requests to wait on
286
- this.tokenAwaiter = new Subject();
351
+ const awaiter = new Subject();
352
+ this.setPendingAwaiter(requesterKey, awaiter);
287
353
  // try to forward execute getNewToken from our parent
288
354
  const parentExecuter = this.forwardExecute(0 /* RpcRelationshipType.Parent */, AuthorizationManager.rpcCommands.getNewToken, [nodeNames, errorData]);
289
355
  if (parentExecuter) {
290
- return this.tokenAwaiter;
356
+ return awaiter;
291
357
  }
292
358
  // Need to clear the endpoints before we acquire a new token,
293
359
  // because we must try new credentials in an admin context before any fallback happens.
@@ -297,9 +363,11 @@ export class AuthorizationManager extends RpcServiceForwarder {
297
363
  }
298
364
  }
299
365
  // since we could not forward the request We must ask for the auth token ourselves
300
- this.authorize(nodeNames, errorData)
366
+ const authorizeErrorData = this.isRequesterScopingEnabled() ? { ...(errorData || {}), requesterScope: requesterKey || null } : errorData;
367
+ this.authorize(nodeNames, authorizeErrorData)
301
368
  .pipe(mergeMap(credentials => {
302
- if (credentials.applyToAllNodes) {
369
+ // scoped credentials never replace the credentials shared by every tool
370
+ if (credentials.applyToAllNodes && !this.getCredentialScope(credentials, requesterKey)) {
303
371
  this.nodeTokens = {};
304
372
  this.updateCache();
305
373
  const forward = this.forwardExecute(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.clearNodeTokens, []);
@@ -309,6 +377,7 @@ export class AuthorizationManager extends RpcServiceForwarder {
309
377
  }
310
378
  return of(credentials);
311
379
  }), mergeMap(credentials => {
380
+ const scope = this.getCredentialScope(credentials, requesterKey);
312
381
  const appliesTo = credentials.applyToAllNodes ? null : nodeNames || null;
313
382
  return this.createSecureToken({
314
383
  username: credentials.username,
@@ -316,26 +385,53 @@ export class AuthorizationManager extends RpcServiceForwarder {
316
385
  useLaps: credentials.useLaps,
317
386
  lapsLocalAdminName: credentials.lapsLocalAdminName
318
387
  }).pipe(map(token => {
319
- return {
388
+ const response = {
320
389
  token: token,
321
390
  appliesTo: appliesTo
322
391
  };
392
+ if (scope) {
393
+ response.scope = scope;
394
+ }
395
+ return response;
323
396
  }));
397
+ }), mergeMap(result => {
398
+ if (result.scope && !result.appliesTo) {
399
+ // Older core prefers node tokens over a global token and does not understand scope.
400
+ const forward = this.forwardExecuteToChildren(result.scope, AuthorizationManager.rpcCommands.clearNodeTokens, []);
401
+ if (forward) {
402
+ return forward.pipe(map(() => result));
403
+ }
404
+ }
405
+ return of(result);
324
406
  }),
325
407
  // take the next 1 result and use it for our token.
326
408
  // We can now notify anyone waiting for auth and save the token
327
409
  take(1))
328
410
  .subscribe({
329
411
  next: result => {
330
- this.completeTokenAwaiter(result);
331
- this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeToken, result);
412
+ if (result.scope) {
413
+ this.saveScopedToken(result);
414
+ const awaiter = this.requesterTokenAwaiters.get(requesterKey);
415
+ if (awaiter) {
416
+ this.requesterTokenAwaiters.delete(requesterKey);
417
+ awaiter.next(result);
418
+ awaiter.complete();
419
+ }
420
+ this.forwardNotifyToChildren(result.scope, AuthorizationManager.rpcCommands.setNodeToken, result);
421
+ return;
422
+ }
423
+ this.completeTokenAwaiter(result, null, requesterKey);
424
+ const pendingRequesters = Array.from(this.requesterTokenAwaiters.keys());
425
+ const notification = pendingRequesters.length ? { ...result, pendingRequesters } : result;
426
+ this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeToken, notification);
332
427
  },
333
428
  error: error => {
334
- this.completeTokenAwaiter(null, error);
335
- this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeTokenError, RpcForwarder.ensureSerializable(error));
429
+ const sharedRequesters = Array.from(this.sharedTokenRequesters);
430
+ this.completeTokenAwaiter(null, error, requesterKey);
431
+ this.notifyTokenError(requesterKey, error, sharedRequesters);
336
432
  }
337
433
  });
338
- return this.tokenAwaiter;
434
+ return awaiter;
339
435
  }
340
436
  refreshSignOnToken() {
341
437
  // if we are already awaiting a token, then hook into the current request and try using that token
@@ -433,8 +529,17 @@ export class AuthorizationManager extends RpcServiceForwarder {
433
529
  * @param nodeName The nodeName to get token for.
434
530
  */
435
531
  getSavedNodeToken(nodeName) {
436
- if (nodeName && this.nodeTokens[nodeName.toLocaleLowerCase()]) {
437
- return this.nodeTokens[nodeName.toLocaleLowerCase()];
532
+ const normalizedNodeName = nodeName && nodeName.toLocaleLowerCase();
533
+ if (this.scopedTokenState) {
534
+ if (normalizedNodeName && this.scopedTokenState.nodeTokens[normalizedNodeName]) {
535
+ return this.scopedTokenState.nodeTokens[normalizedNodeName];
536
+ }
537
+ if (this.scopedTokenState.manageAsToken) {
538
+ return this.scopedTokenState.manageAsToken;
539
+ }
540
+ }
541
+ if (normalizedNodeName && this.nodeTokens[normalizedNodeName]) {
542
+ return this.nodeTokens[normalizedNodeName];
438
543
  }
439
544
  return this.manageAsToken;
440
545
  }
@@ -529,6 +634,10 @@ export class AuthorizationManager extends RpcServiceForwarder {
529
634
  }
530
635
  }
531
636
  resetAllTokens() {
637
+ this.scopedTokens = {};
638
+ this.scopedTokenState = null;
639
+ this.sharedTokenState = null;
640
+ this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.clearScopedTokens, null);
532
641
  this.manageAsToken = null;
533
642
  this.nodePowershellEndpoints = {};
534
643
  this.nodeTokens = {};
@@ -559,19 +668,39 @@ export class AuthorizationManager extends RpcServiceForwarder {
559
668
  this.nodeTokens = data.result.nodeTokens;
560
669
  this.nodePowershellEndpoints = data.result.nodePowershellEndpoints;
561
670
  this.signOnManager.signedHttpRequestToken = data.result.signOnToken;
671
+ this.scopedTokenState = data.result.scopedTokens || null;
672
+ const sharedTokens = data.result.sharedTokens || { nodeTokens: {} };
673
+ this.sharedTokenState = this.scopedTokenState
674
+ ? {
675
+ manageAsToken: sharedTokens.manageAsToken,
676
+ nodeTokens: { ...sharedTokens.nodeTokens }
677
+ }
678
+ : null;
679
+ this.recomputeEffectiveTokenState();
562
680
  }
563
681
  /**
564
682
  * Called when a new instance of the service in another window is initialized and needs to synchronize with its parent
565
- * @param from The RpcRelationshipType that this request is from
683
+ * @param sourceName The RPC name of the initializing child, used to include the credentials scoped to it
566
684
  * @returns an observable for the all the values needed to initialize the service
567
685
  */
568
- onForwardInit() {
569
- // authorization manager doesn't pass any properties to child services at this time.
686
+ onForwardInit(sourceName) {
687
+ const sharedTokenState = this.scopedTokenState ? this.sharedTokenState : null;
688
+ const scoped = this.scopedTokenState || (sourceName && this.isRequesterScopingEnabled() ? this.scopedTokens[sourceName] : null);
689
+ const sharedManageAsToken = sharedTokenState ? sharedTokenState.manageAsToken : this.manageAsToken;
690
+ const sharedNodeTokens = sharedTokenState ? sharedTokenState.nodeTokens : this.nodeTokens;
570
691
  return of({
571
- manageAsToken: this.manageAsToken,
572
- nodeTokens: this.nodeTokens,
573
- nodePowershellEndpoints: this.nodePowershellEndpoints,
574
- signOnToken: this.signOnManager.signedHttpRequestToken
692
+ manageAsToken: (scoped && scoped.manageAsToken) || sharedManageAsToken,
693
+ nodeTokens: scoped ? (scoped.manageAsToken ? { ...scoped.nodeTokens } : { ...sharedNodeTokens, ...scoped.nodeTokens }) : this.nodeTokens,
694
+ sharedTokens: scoped
695
+ ? {
696
+ manageAsToken: sharedManageAsToken,
697
+ nodeTokens: sharedNodeTokens
698
+ }
699
+ : undefined,
700
+ // Shared JEA fallbacks may belong to a different identity than the scoped all-connections token.
701
+ nodePowershellEndpoints: scoped && scoped.manageAsToken ? {} : this.nodePowershellEndpoints,
702
+ signOnToken: this.signOnManager.signedHttpRequestToken,
703
+ scopedTokens: scoped || null
575
704
  });
576
705
  }
577
706
  /**
@@ -581,18 +710,19 @@ export class AuthorizationManager extends RpcServiceForwarder {
581
710
  * @param args The arguments of the method
582
711
  * @returns an observable for the result of the method call
583
712
  */
584
- onForwardExecute(from, name, args) {
713
+ onForwardExecute(from, name, args, sourceName) {
585
714
  // command comes from child
586
715
  if (from === 1 /* RpcRelationshipType.Child */) {
587
716
  switch (name) {
588
717
  case AuthorizationManager.rpcCommands.getNewToken: {
718
+ const errorData = AuthorizationManager.withRequesterName(args ? args[1] : null, sourceName);
719
+ // only a direct child can be scoped, because scoped tokens are delivered to the direct child frame by name
720
+ const isDirectRequester = !!errorData && errorData.requesterName === sourceName;
721
+ const requesterKey = isDirectRequester ? this.getRequesterAwaiterKey(errorData) : '';
589
722
  // start getting the new token, but return immediately to avoid timeout
590
- const subscription = this.getNewToken(args ? args[0] : null, args ? args[1] : null).subscribe({
723
+ const subscription = this.requestNewToken(args ? args[0] : null, errorData, requesterKey, sourceName).subscribe({
591
724
  next: () => subscription.unsubscribe(),
592
- error: error => {
593
- subscription.unsubscribe();
594
- this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeTokenError, RpcForwarder.ensureSerializable(error));
595
- }
725
+ error: () => subscription.unsubscribe()
596
726
  });
597
727
  return of(null);
598
728
  }
@@ -633,7 +763,14 @@ export class AuthorizationManager extends RpcServiceForwarder {
633
763
  // command comes from parent
634
764
  if (from === 0 /* RpcRelationshipType.Parent */) {
635
765
  if (name === AuthorizationManager.rpcCommands.clearNodeTokens) {
636
- this.nodeTokens = {};
766
+ // Clear shared node-specific credentials without removing requester-scoped state.
767
+ if (this.scopedTokenState) {
768
+ this.sharedTokenState.nodeTokens = {};
769
+ this.recomputeEffectiveTokenState();
770
+ }
771
+ else {
772
+ this.nodeTokens = {};
773
+ }
637
774
  this.nodePowershellEndpoints = {};
638
775
  // if we also have children, forward the request on
639
776
  const forward = this.forwardExecute(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.clearNodeTokens, args);
@@ -654,17 +791,66 @@ export class AuthorizationManager extends RpcServiceForwarder {
654
791
  // allow our parent to give us a new token.
655
792
  if (from === 0 /* RpcRelationshipType.Parent */) {
656
793
  if (name === AuthorizationManager.rpcCommands.manageAsToken) {
657
- this.manageAsToken = value;
794
+ if (this.scopedTokenState) {
795
+ this.sharedTokenState.manageAsToken = value;
796
+ this.nodePowershellEndpoints = {};
797
+ this.recomputeEffectiveTokenState();
798
+ }
799
+ else {
800
+ this.manageAsToken = value;
801
+ }
658
802
  // if we also have children, forward the notification on
659
803
  const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.manageAsToken, value);
660
804
  return forward || of(null);
661
805
  }
806
+ if (name === AuthorizationManager.rpcCommands.clearScopedTokens) {
807
+ this.clearScopedOverlay();
808
+ const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.clearScopedTokens, value);
809
+ return forward || of(null);
810
+ }
811
+ // resetAllTokens sends clearNodeTokens as a notification; applyToAllNodes sends it as an execute
812
+ if (name === AuthorizationManager.rpcCommands.clearNodeTokens) {
813
+ // Clear shared node-specific credentials without removing requester-scoped state.
814
+ if (this.scopedTokenState) {
815
+ this.sharedTokenState.nodeTokens = {};
816
+ this.recomputeEffectiveTokenState();
817
+ }
818
+ else {
819
+ this.nodeTokens = {};
820
+ }
821
+ const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.clearNodeTokens, value);
822
+ return forward || of(null);
823
+ }
662
824
  if (name === AuthorizationManager.rpcCommands.setNodeToken) {
663
825
  if (!value) {
664
826
  const message = MsftSme.getStrings().MsftSmeShell.Core.Error.InvalidValue.message;
665
827
  return throwError(() => message.format(AuthorizationManager.rpcCommands.setNodeToken));
666
828
  }
667
- this.completeTokenAwaiter(value);
829
+ const response = value;
830
+ if (response.pendingRequesters && response.pendingRequesters.indexOf(MsftSme.self().Environment.name) >= 0) {
831
+ // this requester is still waiting for its own credentials, so only save the shared token
832
+ if (this.scopedTokenState) {
833
+ this.applyChildSharedTokenResponse(response);
834
+ }
835
+ else {
836
+ if (!response.appliesTo) {
837
+ this.manageAsToken = response.token;
838
+ }
839
+ else if (Array.isArray(response.appliesTo)) {
840
+ response.appliesTo.forEach(nodeName => {
841
+ this.nodeTokens[nodeName.toLocaleLowerCase()] = response.token;
842
+ });
843
+ this.updateCache();
844
+ }
845
+ else {
846
+ this.nodeTokens[response.appliesTo.toLocaleLowerCase()] = response.token;
847
+ this.updateCache();
848
+ }
849
+ }
850
+ }
851
+ else {
852
+ this.completeTokenAwaiter(response);
853
+ }
668
854
  const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeToken, value);
669
855
  return forward || of(null);
670
856
  }
@@ -759,36 +945,180 @@ export class AuthorizationManager extends RpcServiceForwarder {
759
945
  /**
760
946
  * Completes the token awaiter
761
947
  */
762
- completeTokenAwaiter(result, error) {
948
+ completeTokenAwaiter(result, error, requesterKey = '') {
949
+ if (!requesterKey) {
950
+ this.sharedTokenRequesters.clear();
951
+ }
763
952
  if (error) {
764
- if (this.tokenAwaiter) {
765
- const awaiter = this.tokenAwaiter;
766
- this.tokenAwaiter = null;
953
+ const awaiter = requesterKey ? this.requesterTokenAwaiters.get(requesterKey) : this.tokenAwaiter;
954
+ if (awaiter) {
955
+ if (requesterKey) {
956
+ this.requesterTokenAwaiters.delete(requesterKey);
957
+ }
958
+ else {
959
+ this.tokenAwaiter = null;
960
+ }
767
961
  awaiter.error(error);
768
962
  }
769
963
  }
770
964
  else {
771
- if (!result.appliesTo) {
772
- this.manageAsToken = result.token;
773
- }
774
- else if (Array.isArray(result.appliesTo)) {
775
- result.appliesTo.forEach(nodeName => {
776
- this.nodeTokens[nodeName.toLocaleLowerCase()] = result.token;
777
- });
778
- this.updateCache();
965
+ if (result.scope || this.scopedTokenState) {
966
+ if (result.scope) {
967
+ if (!this.scopedTokenState) {
968
+ this.sharedTokenState = {
969
+ manageAsToken: this.token,
970
+ nodeTokens: { ...(this.nodeTokens || {}) }
971
+ };
972
+ this.scopedTokenState = { nodeTokens: {} };
973
+ }
974
+ AuthorizationManager.addToScope(this.scopedTokenState, result);
975
+ this.recomputeEffectiveTokenState();
976
+ }
977
+ else {
978
+ this.applyChildSharedTokenResponse(result);
979
+ }
779
980
  }
780
981
  else {
781
- this.nodeTokens[result.appliesTo.toLocaleLowerCase()] = result.token;
782
- this.updateCache();
982
+ if (!result.appliesTo) {
983
+ this.manageAsToken = result.token;
984
+ }
985
+ else if (Array.isArray(result.appliesTo)) {
986
+ result.appliesTo.forEach(nodeName => {
987
+ this.nodeTokens[nodeName.toLocaleLowerCase()] = result.token;
988
+ });
989
+ this.updateCache();
990
+ }
991
+ else {
992
+ this.nodeTokens[result.appliesTo.toLocaleLowerCase()] = result.token;
993
+ this.updateCache();
994
+ }
783
995
  }
784
- if (this.tokenAwaiter) {
785
- const awaiter = this.tokenAwaiter;
786
- this.tokenAwaiter = null;
996
+ const awaiter = requesterKey ? this.requesterTokenAwaiters.get(requesterKey) : this.tokenAwaiter;
997
+ if (awaiter) {
998
+ if (requesterKey) {
999
+ this.requesterTokenAwaiters.delete(requesterKey);
1000
+ }
1001
+ else {
1002
+ this.tokenAwaiter = null;
1003
+ }
787
1004
  awaiter.next(result);
788
1005
  awaiter.complete();
789
1006
  }
790
1007
  }
791
1008
  }
1009
+ /**
1010
+ * Adds a token response to a set of scoped credentials.
1011
+ */
1012
+ static addToScope(scoped, result) {
1013
+ if (!result.appliesTo) {
1014
+ scoped.manageAsToken = result.token;
1015
+ scoped.nodeTokens = {};
1016
+ return;
1017
+ }
1018
+ const nodeNames = Array.isArray(result.appliesTo) ? result.appliesTo : [result.appliesTo];
1019
+ nodeNames.forEach(nodeName => {
1020
+ scoped.nodeTokens[nodeName.toLocaleLowerCase()] = result.token;
1021
+ });
1022
+ }
1023
+ /**
1024
+ * Shell instance: saves a token that applies only to the requester named by result.scope.
1025
+ * The tokens shared by every tool are left unchanged.
1026
+ */
1027
+ saveScopedToken(result) {
1028
+ const scoped = this.scopedTokens[result.scope] || { nodeTokens: {} };
1029
+ AuthorizationManager.addToScope(scoped, result);
1030
+ this.scopedTokens[result.scope] = scoped;
1031
+ this.updateCache();
1032
+ }
1033
+ /**
1034
+ * Child instance: re-applies requester-scoped credentials over the shared ones.
1035
+ */
1036
+ recomputeEffectiveTokenState() {
1037
+ if (!this.scopedTokenState) {
1038
+ return;
1039
+ }
1040
+ const sharedState = this.sharedTokenState;
1041
+ this.token = this.scopedTokenState.manageAsToken || sharedState.manageAsToken;
1042
+ this.nodeTokens = this.scopedTokenState.manageAsToken ? { ...this.scopedTokenState.nodeTokens } : { ...sharedState.nodeTokens, ...this.scopedTokenState.nodeTokens };
1043
+ }
1044
+ /**
1045
+ * Child instance: forgets requester-scoped credentials.
1046
+ */
1047
+ clearScopedOverlay() {
1048
+ const sharedState = this.sharedTokenState;
1049
+ this.scopedTokenState = null;
1050
+ this.sharedTokenState = null;
1051
+ if (!sharedState) {
1052
+ return;
1053
+ }
1054
+ this.token = sharedState.manageAsToken;
1055
+ this.nodeTokens = { ...sharedState.nodeTokens };
1056
+ }
1057
+ /**
1058
+ * Child instance: saves a shared token update without allowing it to replace scoped effective credentials.
1059
+ */
1060
+ applyChildSharedTokenResponse(result) {
1061
+ const sharedState = this.sharedTokenState;
1062
+ if (!result.appliesTo) {
1063
+ sharedState.manageAsToken = result.token;
1064
+ }
1065
+ else if (Array.isArray(result.appliesTo)) {
1066
+ result.appliesTo.forEach(nodeName => {
1067
+ sharedState.nodeTokens[nodeName.toLocaleLowerCase()] = result.token;
1068
+ });
1069
+ }
1070
+ else {
1071
+ sharedState.nodeTokens[result.appliesTo.toLocaleLowerCase()] = result.token;
1072
+ }
1073
+ this.recomputeEffectiveTokenState();
1074
+ }
1075
+ /**
1076
+ * Returns the key of the token awaiter for a request: the requester RPC name when requester scoping is
1077
+ * enabled on this shell instance, otherwise an empty string for the shared awaiter.
1078
+ */
1079
+ getRequesterAwaiterKey(errorData) {
1080
+ if (!errorData || !errorData.requesterName) {
1081
+ return '';
1082
+ }
1083
+ return this.isRequesterScopingEnabled() ? errorData.requesterName : '';
1084
+ }
1085
+ isRequesterScopingEnabled() {
1086
+ const isEnabled = this.options && this.options.isRequesterScopingEnabled;
1087
+ return MsftSme.isShell() && !!isEnabled && isEnabled();
1088
+ }
1089
+ /**
1090
+ * Returns the scope for authorized credentials, or null when they apply to every tool.
1091
+ */
1092
+ getCredentialScope(credentials, requesterKey) {
1093
+ return credentials && credentials.scopeToRequester && requesterKey ? requesterKey : null;
1094
+ }
1095
+ getPendingAwaiter(requesterKey) {
1096
+ const awaiter = requesterKey ? this.requesterTokenAwaiters.get(requesterKey) : this.tokenAwaiter;
1097
+ return awaiter && !awaiter.closed ? awaiter : null;
1098
+ }
1099
+ setPendingAwaiter(requesterKey, awaiter) {
1100
+ if (requesterKey) {
1101
+ this.requesterTokenAwaiters.set(requesterKey, awaiter);
1102
+ }
1103
+ else {
1104
+ this.tokenAwaiter = awaiter;
1105
+ }
1106
+ }
1107
+ /**
1108
+ * Routes failures to the requesting frames. Legacy shells retain shared error broadcasts.
1109
+ */
1110
+ notifyTokenError(requesterKey, error, sharedRequesters) {
1111
+ const value = RpcForwarder.ensureSerializable(error);
1112
+ if (requesterKey) {
1113
+ this.forwardNotifyToChildren(requesterKey, AuthorizationManager.rpcCommands.setNodeTokenError, value);
1114
+ }
1115
+ else if (this.isRequesterScopingEnabled()) {
1116
+ sharedRequesters.forEach(sourceName => this.forwardNotifyToChildren(sourceName, AuthorizationManager.rpcCommands.setNodeTokenError, value));
1117
+ }
1118
+ else {
1119
+ this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeTokenError, value);
1120
+ }
1121
+ }
792
1122
  /**
793
1123
  * Completes the sign on token awaiter
794
1124
  */
@@ -836,10 +1166,12 @@ export class AuthorizationManager extends RpcServiceForwarder {
836
1166
  }
837
1167
  // update cached data
838
1168
  const cacheData = {
1169
+ gatewayOrigin: this.gatewayOrigin,
839
1170
  jwk: this.jwk,
840
1171
  manageAsToken: this.manageAsToken,
841
1172
  nodePowershellEndpoints: this.nodePowershellEndpoints,
842
- nodeTokens: this.nodeTokens
1173
+ nodeTokens: this.nodeTokens,
1174
+ scopedTokens: this.scopedTokens
843
1175
  };
844
1176
  MsftSme.SessionStorageHandler.setItem(AuthorizationManager.cacheKey, JSON.stringify(cacheData));
845
1177
  }